LCOV - code coverage report
Current view: top level - layout/generic - nsFloatManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 5 965 0.5 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* class that manages rules for positioning floats */
       8             : 
       9             : #include "nsFloatManager.h"
      10             : 
      11             : #include <algorithm>
      12             : #include <initializer_list>
      13             : 
      14             : #include "gfxContext.h"
      15             : #include "mozilla/ReflowInput.h"
      16             : #include "mozilla/ShapeUtils.h"
      17             : #include "nsBlockFrame.h"
      18             : #include "nsDeviceContext.h"
      19             : #include "nsError.h"
      20             : #include "nsImageRenderer.h"
      21             : #include "nsIPresShell.h"
      22             : #include "nsMemory.h"
      23             : 
      24             : using namespace mozilla;
      25             : using namespace mozilla::image;
      26             : using namespace mozilla::gfx;
      27             : 
      28             : int32_t nsFloatManager::sCachedFloatManagerCount = 0;
      29             : void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
      30             : 
      31           0 : /////////////////////////////////////////////////////////////////////////////
      32           0 : // nsFloatManager
      33             : 
      34             : nsFloatManager::nsFloatManager(nsIPresShell* aPresShell,
      35             :                                WritingMode aWM)
      36             :   :
      37             : #ifdef DEBUG
      38             :     mWritingMode(aWM),
      39             : #endif
      40             :     mLineLeft(0), mBlockStart(0),
      41             :     mFloatDamage(aPresShell),
      42           0 :     mPushedLeftFloatPastBreak(false),
      43             :     mPushedRightFloatPastBreak(false),
      44           0 :     mSplitLeftFloatAcrossBreak(false),
      45           0 :     mSplitRightFloatAcrossBreak(false)
      46             : {
      47           0 :   MOZ_COUNT_CTOR(nsFloatManager);
      48             : }
      49           0 : 
      50           0 : nsFloatManager::~nsFloatManager()
      51             : {
      52             :   MOZ_COUNT_DTOR(nsFloatManager);
      53           0 : }
      54             : 
      55           0 : // static
      56             : void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW
      57             : {
      58           0 :   if (sCachedFloatManagerCount > 0) {
      59             :     // We have cached unused instances of this class, return a cached
      60             :     // instance in stead of always creating a new one.
      61             :     return sCachedFloatManagers[--sCachedFloatManagerCount];
      62             :   }
      63           0 : 
      64             :   // The cache is empty, this means we have to create a new instance using
      65             :   // the global |operator new|.
      66             :   return moz_xmalloc(aSize);
      67           0 : }
      68             : 
      69           0 : void
      70             : nsFloatManager::operator delete(void* aPtr, size_t aSize)
      71             : {
      72             :   if (!aPtr)
      73             :     return;
      74             :   // This float manager is no longer used, if there's still room in
      75           0 :   // the cache we'll cache this float manager, unless the layout
      76             :   // module was already shut down.
      77             : 
      78             :   if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE &&
      79             :       sCachedFloatManagerCount >= 0) {
      80           0 :     // There's still space in the cache for more instances, put this
      81           0 :     // instance in the cache in stead of deleting it.
      82             : 
      83             :     sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr;
      84             :     return;
      85             :   }
      86           0 : 
      87             :   // The cache is full, or the layout module has been shut down,
      88             :   // delete this float manager.
      89             :   free(aPtr);
      90             : }
      91           0 : 
      92             : 
      93             : /* static */
      94             : void nsFloatManager::Shutdown()
      95             : {
      96             :   // The layout module is being shut down, clean up the cache and
      97             :   // disable further caching.
      98           0 : 
      99           0 :   int32_t i;
     100           0 : 
     101           0 :   for (i = 0; i < sCachedFloatManagerCount; i++) {
     102             :     void* floatManager = sCachedFloatManagers[i];
     103             :     if (floatManager)
     104             :       free(floatManager);
     105           0 :   }
     106           0 : 
     107             :   // Disable further caching.
     108             :   sCachedFloatManagerCount = -1;
     109             : }
     110             : 
     111             : #define CHECK_BLOCK_AND_LINE_DIR(aWM) \
     112             :   NS_ASSERTION((aWM).GetBlockDir() == mWritingMode.GetBlockDir() &&     \
     113             :                (aWM).IsLineInverted() == mWritingMode.IsLineInverted(), \
     114           0 :                "incompatible writing modes")
     115             : 
     116             : nsFlowAreaRect
     117             : nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBCoord, nscoord aBSize,
     118             :                             BandInfoType aBandInfoType, ShapeType aShapeType,
     119           0 :                             LogicalRect aContentArea, SavedState* aState,
     120           0 :                             const nsSize& aContainerSize) const
     121           0 : {
     122             :   CHECK_BLOCK_AND_LINE_DIR(aWM);
     123             :   NS_ASSERTION(aBSize >= 0, "unexpected max block size");
     124           0 :   NS_ASSERTION(aContentArea.ISize(aWM) >= 0,
     125           0 :                "unexpected content area inline size");
     126           0 : 
     127           0 :   nscoord blockStart = aBCoord + mBlockStart;
     128             :   if (blockStart < nscoord_MIN) {
     129             :     NS_WARNING("bad value");
     130             :     blockStart = nscoord_MIN;
     131             :   }
     132           0 : 
     133             :   // Determine the last float that we should consider.
     134           0 :   uint32_t floatCount;
     135           0 :   if (aState) {
     136             :     // Use the provided state.
     137             :     floatCount = aState->mFloatInfoCount;
     138           0 :     MOZ_ASSERT(floatCount <= mFloats.Length(), "bad state");
     139             :   } else {
     140             :     // Use our current state.
     141             :     floatCount = mFloats.Length();
     142             :   }
     143           0 : 
     144           0 :   // If there are no floats at all, or we're below the last one, return
     145           0 :   // quickly.
     146           0 :   if (floatCount == 0 ||
     147           0 :       (mFloats[floatCount-1].mLeftBEnd <= blockStart &&
     148         148 :        mFloats[floatCount-1].mRightBEnd <= blockStart)) {
     149             :     return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBCoord,
     150             :                           aContentArea.ISize(aWM), aBSize,
     151             :                           nsFlowAreaRectFlags::NO_FLAGS);
     152           0 :   }
     153             : 
     154             :   nscoord blockEnd;
     155           0 :   if (aBSize == nscoord_MAX) {
     156             :     // This warning (and the two below) are possible to hit on pages
     157             :     // with really large objects.
     158           0 :     NS_WARNING_ASSERTION(aBandInfoType == BandInfoType::BandFromPoint, "bad height");
     159           0 :     blockEnd = nscoord_MAX;
     160           0 :   } else {
     161           0 :     blockEnd = blockStart + aBSize;
     162             :     if (blockEnd < blockStart || blockEnd > nscoord_MAX) {
     163             :       NS_WARNING("bad value");
     164           0 :       blockEnd = nscoord_MAX;
     165           0 :     }
     166           0 :   }
     167           0 :   nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize);
     168           0 :   nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize);
     169             :   if (lineRight < lineLeft) {
     170             :     NS_WARNING("bad value");
     171             :     lineRight = lineLeft;
     172             :   }
     173             : 
     174             :   // Walk backwards through the floats until we either hit the front of
     175           0 :   // the list or we're above |blockStart|.
     176           0 :   bool haveFloats = false;
     177           0 :   bool mayWiden = false;
     178             :   for (uint32_t i = floatCount; i > 0; --i) {
     179             :     const FloatInfo &fi = mFloats[i-1];
     180             :     if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) {
     181           0 :       // There aren't any more floats that could intersect this band.
     182             :       break;
     183             :     }
     184             :     if (fi.IsEmpty(aShapeType)) {
     185             :       // Ignore empty float areas.
     186             :       // https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior
     187           0 :       continue;
     188           0 :     }
     189           0 : 
     190             :     nscoord floatBStart = fi.BStart(aShapeType);
     191           0 :     nscoord floatBEnd = fi.BEnd(aShapeType);
     192           0 :     if (blockStart < floatBStart && aBandInfoType == BandInfoType::BandFromPoint) {
     193             :       // This float is below our band.  Shrink our band's height if needed.
     194             :       if (floatBStart < blockEnd) {
     195             :         blockEnd = floatBStart;
     196             :       }
     197             :     }
     198             :     // If blockStart == blockEnd (which happens only with WidthWithinHeight),
     199             :     // we include floats that begin at our 0-height vertical area.  We
     200           0 :     // need to do this to satisfy the invariant that a
     201           0 :     // WidthWithinHeight call is at least as narrow on both sides as a
     202           0 :     // BandFromPoint call beginning at its blockStart.
     203             :     else if (blockStart < floatBEnd &&
     204             :              (floatBStart < blockEnd ||
     205             :               (floatBStart == blockEnd && blockStart == blockEnd))) {
     206           0 :       // This float is in our band.
     207             : 
     208             :       // Shrink our band's width if needed.
     209             :       StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM);
     210             : 
     211           0 :       // When aBandInfoType is BandFromPoint, we're only intended to
     212           0 :       // consider a point along the y axis rather than a band.
     213             :       const nscoord bandBlockEnd =
     214             :         aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd;
     215           0 :       if (floatStyle == StyleFloat::Left) {
     216           0 :         // A left float
     217           0 :         nscoord lineRightEdge =
     218             :           fi.LineRight(aShapeType, blockStart, bandBlockEnd);
     219             :         if (lineRightEdge > lineLeft) {
     220             :           lineLeft = lineRightEdge;
     221             :           // Only set haveFloats to true if the float is inside our
     222           0 :           // containing block.  This matches the spec for what some
     223             :           // callers want and disagrees for other callers, so we should
     224             :           // probably provide better information at some point.
     225             :           haveFloats = true;
     226           0 : 
     227             :           // Our area may widen in the block direction if this float may
     228             :           // narrow in the block direction.
     229             :           mayWiden = mayWiden || fi.MayNarrowInBlockDirection(aShapeType);
     230             :         }
     231           0 :       } else {
     232           0 :         // A right float
     233           0 :         nscoord lineLeftEdge =
     234             :           fi.LineLeft(aShapeType, blockStart, bandBlockEnd);
     235           0 :         if (lineLeftEdge < lineRight) {
     236           0 :           lineRight = lineLeftEdge;
     237             :           // See above.
     238             :           haveFloats = true;
     239             :           mayWiden = mayWiden || fi.MayNarrowInBlockDirection(aShapeType);
     240             :         }
     241           0 :       }
     242           0 : 
     243             :       // Shrink our band's height if needed.
     244             :       if (floatBEnd < blockEnd && aBandInfoType == BandInfoType::BandFromPoint) {
     245             :         blockEnd = floatBEnd;
     246             :       }
     247           0 :     }
     248           0 :   }
     249             : 
     250           0 :   nscoord blockSize = (blockEnd == nscoord_MAX) ?
     251           0 :                        nscoord_MAX : (blockEnd - blockStart);
     252           0 :   // convert back from LineLeft/Right to IStart
     253           0 :   nscoord inlineStart = aWM.IsBidiLTR()
     254             :                         ? lineLeft - mLineLeft
     255             :                         : mLineLeft - lineRight +
     256           0 :                           LogicalSize(aWM, aContainerSize).ISize(aWM);
     257           0 : 
     258             :   nsFlowAreaRectFlags flags =
     259           0 :     (haveFloats ? nsFlowAreaRectFlags::HAS_FLOATS : nsFlowAreaRectFlags::NO_FLAGS) |
     260           0 :     (mayWiden ? nsFlowAreaRectFlags::MAY_WIDEN : nsFlowAreaRectFlags::NO_FLAGS);
     261             : 
     262             :   return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart,
     263             :                         lineRight - lineLeft, blockSize, flags);
     264           0 : }
     265             : 
     266             : void
     267           0 : nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect,
     268           0 :                          WritingMode aWM, const nsSize& aContainerSize)
     269           0 : {
     270             :   CHECK_BLOCK_AND_LINE_DIR(aWM);
     271             :   NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
     272           0 :   NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!");
     273             : 
     274             :   FloatInfo info(aFloatFrame, mLineLeft, mBlockStart, aMarginRect, aWM,
     275           0 :                  aContainerSize);
     276           0 : 
     277           0 :   // Set mLeftBEnd and mRightBEnd.
     278           0 :   if (HasAnyFloats()) {
     279             :     FloatInfo &tail = mFloats[mFloats.Length() - 1];
     280           0 :     info.mLeftBEnd = tail.mLeftBEnd;
     281           0 :     info.mRightBEnd = tail.mRightBEnd;
     282             :   } else {
     283           0 :     info.mLeftBEnd = nscoord_MIN;
     284           0 :     info.mRightBEnd = nscoord_MIN;
     285             :   }
     286             :   StyleFloat floatStyle = aFloatFrame->StyleDisplay()->PhysicalFloats(aWM);
     287           0 :   MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right,
     288           0 :              "Unexpected float style!");
     289           0 :   nscoord& sideBEnd =
     290           0 :     floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd;
     291             :   nscoord thisBEnd = info.BEnd();
     292           0 :   if (thisBEnd > sideBEnd)
     293           0 :     sideBEnd = thisBEnd;
     294             : 
     295             :   mFloats.AppendElement(std::move(info));
     296             : }
     297           0 : 
     298             : // static
     299             : LogicalRect
     300             : nsFloatManager::CalculateRegionFor(WritingMode          aWM,
     301             :                                    nsIFrame*            aFloat,
     302             :                                    const LogicalMargin& aMargin,
     303           0 :                                    const nsSize&        aContainerSize)
     304           0 : {
     305           0 :   // We consider relatively positioned frames at their original position.
     306             :   LogicalRect region(aWM, nsRect(aFloat->GetNormalPosition(),
     307             :                                  aFloat->GetSize()),
     308           0 :                      aContainerSize);
     309             : 
     310             :   // Float region includes its margin
     311             :   region.Inflate(aWM, aMargin);
     312           0 : 
     313             :   // Don't store rectangles with negative margin-box width or height in
     314             :   // the float manager; it can't deal with them.
     315           0 :   if (region.ISize(aWM) < 0) {
     316           0 :     // Preserve the right margin-edge for left floats and the left
     317           0 :     // margin-edge for right floats
     318           0 :     const nsStyleDisplay* display = aFloat->StyleDisplay();
     319             :     StyleFloat floatStyle = display->PhysicalFloats(aWM);
     320           0 :     if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) {
     321             :       region.IStart(aWM) = region.IEnd(aWM);
     322           0 :     }
     323           0 :     region.ISize(aWM) = 0;
     324             :   }
     325           0 :   if (region.BSize(aWM) < 0) {
     326             :     region.BSize(aWM) = 0;
     327             :   }
     328             :   return region;
     329             : }
     330             : 
     331           0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(FloatRegionProperty, nsMargin)
     332             : 
     333             : LogicalRect
     334           0 : nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat,
     335           0 :                              const nsSize& aContainerSize)
     336           0 : {
     337           0 :   LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize);
     338           0 :   void* storedRegion = aFloat->GetProperty(FloatRegionProperty());
     339             :   if (storedRegion) {
     340           0 :     nsMargin margin = *static_cast<nsMargin*>(storedRegion);
     341             :     region.Inflate(aWM, LogicalMargin(aWM, margin));
     342             :   }
     343             :   return region;
     344           0 : }
     345             : 
     346             : void
     347             : nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat,
     348           0 :                                const LogicalRect& aRegion,
     349           0 :                                const nsSize& aContainerSize)
     350           0 : {
     351           0 :   nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize);
     352             :   nsRect rect = aFloat->GetRect();
     353             :   if (region.IsEqualEdges(rect)) {
     354           0 :     aFloat->DeleteProperty(FloatRegionProperty());
     355           0 :   }
     356           0 :   else {
     357           0 :     nsMargin* storedMargin = aFloat->GetProperty(FloatRegionProperty());
     358             :     if (!storedMargin) {
     359           0 :       storedMargin = new nsMargin();
     360             :       aFloat->SetProperty(FloatRegionProperty(), storedMargin);
     361           0 :     }
     362             :     *storedMargin = region - rect;
     363             :   }
     364           0 : }
     365             : 
     366           0 : nsresult
     367             : nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList)
     368             : {
     369             :   if (!aFrameList) {
     370             :     return NS_OK;
     371             :   }
     372             :   // This could be a good bit simpler if we could guarantee that the
     373           0 :   // floats given were at the end of our list, so we could just search
     374             :   // for the head of aFrameList.  (But we can't;
     375           0 :   // layout/reftests/bugs/421710-1.html crashes.)
     376           0 :   nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1);
     377             : 
     378             :   for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) {
     379           0 :     frameSet.PutEntry(f);
     380           0 :   }
     381           0 : 
     382             :   uint32_t newLength = mFloats.Length();
     383             :   while (newLength > 0) {
     384             :     if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) {
     385             :       break;
     386           0 :     }
     387             :     --newLength;
     388             :   }
     389           0 :   mFloats.TruncateLength(newLength);
     390           0 : 
     391             : #ifdef DEBUG
     392             :   for (uint32_t i = 0; i < mFloats.Length(); ++i) {
     393             :     NS_ASSERTION(!frameSet.Contains(mFloats[i].mFrame),
     394             :                  "Frame region deletion was requested but we couldn't delete it");
     395           0 :   }
     396             : #endif
     397             : 
     398             :   return NS_OK;
     399          72 : }
     400             : 
     401          72 : void
     402             : nsFloatManager::PushState(SavedState* aState)
     403             : {
     404             :   MOZ_ASSERT(aState, "Need a place to save state");
     405             : 
     406             :   // This is a cheap push implementation, which
     407             :   // only saves the (x,y) and last frame in the mFrameInfoMap
     408             :   // which is enough info to get us back to where we should be
     409             :   // when pop is called.
     410             :   //
     411             :   // This push/pop mechanism is used to undo any
     412             :   // floats that were added during the unconstrained reflow
     413             :   // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
     414             :   //
     415             :   // It should also be noted that the state for mFloatDamage is
     416             :   // intentionally not saved or restored in PushState() and PopState(),
     417             :   // since that could lead to bugs where damage is missed/dropped when
     418             :   // we move from position A to B (during the intermediate incremental
     419             :   // reflow mentioned above) and then from B to C during the subsequent
     420          72 :   // reflow. In the typical case A and C will be the same, but not always.
     421           0 :   // Allowing mFloatDamage to accumulate the damage incurred during both
     422          72 :   // reflows ensures that nothing gets missed.
     423           0 :   aState->mLineLeft = mLineLeft;
     424           0 :   aState->mBlockStart = mBlockStart;
     425           0 :   aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak;
     426           0 :   aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak;
     427           0 :   aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak;
     428             :   aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak;
     429             :   aState->mFloatInfoCount = mFloats.Length();
     430           0 : }
     431             : 
     432           0 : void
     433             : nsFloatManager::PopState(SavedState* aState)
     434           0 : {
     435           0 :   MOZ_ASSERT(aState, "No state to restore?");
     436           0 : 
     437           0 :   mLineLeft = aState->mLineLeft;
     438           0 :   mBlockStart = aState->mBlockStart;
     439           0 :   mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak;
     440             :   mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak;
     441           0 :   mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak;
     442             :   mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak;
     443           0 : 
     444           0 :   NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(),
     445             :                "somebody misused PushState/PopState");
     446             :   mFloats.TruncateLength(aState->mFloatInfoCount);
     447           0 : }
     448             : 
     449           0 : nscoord
     450             : nsFloatManager::GetLowestFloatTop() const
     451             : {
     452           0 :   if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) {
     453             :     return nscoord_MAX;
     454             :   }
     455           0 :   if (!HasAnyFloats()) {
     456             :     return nscoord_MIN;
     457             :   }
     458             :   return mFloats[mFloats.Length() -1].BStart() - mBlockStart;
     459             : }
     460           0 : 
     461             : #ifdef DEBUG_FRAME_DUMP
     462           0 : void
     463           0 : DebugListFloatManager(const nsFloatManager *aFloatManager)
     464             : {
     465             :   aFloatManager->List(stdout);
     466           0 : }
     467             : 
     468           0 : nsresult
     469             : nsFloatManager::List(FILE* out) const
     470             : {
     471           0 :   if (!HasAnyFloats())
     472           0 :     return NS_OK;
     473             : 
     474           0 :   for (uint32_t i = 0; i < mFloats.Length(); ++i) {
     475             :     const FloatInfo &fi = mFloats[i];
     476           0 :     fprintf_stderr(out, "Float %u: frame=%p rect={%d,%d,%d,%d} BEnd={l:%d, r:%d}\n",
     477             :                    i, static_cast<void*>(fi.mFrame),
     478             :                    fi.LineLeft(), fi.BStart(), fi.ISize(), fi.BSize(),
     479             :                    fi.mLeftBEnd, fi.mRightBEnd);
     480             :   }
     481             :   return NS_OK;
     482             : }
     483           0 : #endif
     484             : 
     485             : nscoord
     486           0 : nsFloatManager::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
     487             :                             uint32_t aFlags) const
     488             : {
     489           0 :   if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) {
     490             :     return nscoord_MAX;
     491             :   }
     492             :   if (!HasAnyFloats()) {
     493           0 :     return aBCoord;
     494             :   }
     495           0 : 
     496           0 :   nscoord blockEnd = aBCoord + mBlockStart;
     497             : 
     498           0 :   const FloatInfo &tail = mFloats[mFloats.Length() - 1];
     499           0 :   switch (aBreakType) {
     500           0 :     case StyleClear::Both:
     501             :       blockEnd = std::max(blockEnd, tail.mLeftBEnd);
     502           0 :       blockEnd = std::max(blockEnd, tail.mRightBEnd);
     503           0 :       break;
     504             :     case StyleClear::Left:
     505           0 :       blockEnd = std::max(blockEnd, tail.mLeftBEnd);
     506           0 :       break;
     507             :     case StyleClear::Right:
     508             :       blockEnd = std::max(blockEnd, tail.mRightBEnd);
     509             :       break;
     510             :     default:
     511             :       // Do nothing
     512           0 :       break;
     513             :   }
     514           0 : 
     515             :   blockEnd -= mBlockStart;
     516             : 
     517             :   return blockEnd;
     518           0 : }
     519             : 
     520           0 : bool
     521           0 : nsFloatManager::ClearContinues(StyleClear aBreakType) const
     522           0 : {
     523           0 :   return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) &&
     524           0 :           (aBreakType == StyleClear::Both ||
     525           0 :            aBreakType == StyleClear::Left)) ||
     526             :          ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) &&
     527             :           (aBreakType == StyleClear::Both ||
     528             :            aBreakType == StyleClear::Right));
     529             : }
     530             : 
     531             : /////////////////////////////////////////////////////////////////////////////
     532             : // ShapeInfo is an abstract class for implementing all the shapes in CSS
     533             : // Shapes Module. A subclass needs to override all the methods to adjust
     534             : // the flow area with respect to its shape.
     535             : //
     536             : class nsFloatManager::ShapeInfo
     537             : {
     538             : public:
     539             :   virtual ~ShapeInfo() {}
     540             : 
     541             :   virtual nscoord LineLeft(const nscoord aBStart,
     542             :                            const nscoord aBEnd) const = 0;
     543             :   virtual nscoord LineRight(const nscoord aBStart,
     544             :                             const nscoord aBEnd) const = 0;
     545             :   virtual nscoord BStart() const = 0;
     546             :   virtual nscoord BEnd() const = 0;
     547             :   virtual bool IsEmpty() const = 0;
     548             : 
     549             :   // Does this shape possibly get inline narrower in the BStart() to BEnd()
     550             :   // span when proceeding in the block direction? This is false for unrounded
     551             :   // rectangles that span all the way to BEnd(), but could be true for other
     552             :   // shapes. Note that we don't care if the BEnd() falls short of the margin
     553             :   // rect -- the ShapeInfo can only affect float behavior in the span between
     554             :   // BStart() and BEnd().
     555             :   virtual bool MayNarrowInBlockDirection() const = 0;
     556             : 
     557             :   // Translate the current origin by the specified offsets.
     558             :   virtual void Translate(nscoord aLineLeft, nscoord aBlockStart) = 0;
     559             : 
     560             :   static LogicalRect ComputeShapeBoxRect(
     561             :     const StyleShapeSource& aShapeOutside,
     562             :     nsIFrame* const aFrame,
     563             :     const LogicalRect& aMarginRect,
     564             :     WritingMode aWM);
     565           0 : 
     566             :   // Convert the LogicalRect to the special logical coordinate space used
     567             :   // in float manager.
     568             :   static nsRect ConvertToFloatLogical(const LogicalRect& aRect,
     569             :                                       WritingMode aWM,
     570           0 :                                       const nsSize& aContainerSize)
     571             :   {
     572             :     return nsRect(aRect.LineLeft(aWM, aContainerSize), aRect.BStart(aWM),
     573             :                   aRect.ISize(aWM), aRect.BSize(aWM));
     574             :   }
     575             : 
     576             :   static UniquePtr<ShapeInfo> CreateShapeBox(
     577             :     nsIFrame* const aFrame,
     578             :     nscoord aShapeMargin,
     579             :     const LogicalRect& aShapeBoxRect,
     580             :     WritingMode aWM,
     581             :     const nsSize& aContainerSize);
     582             : 
     583             :   static UniquePtr<ShapeInfo> CreateBasicShape(
     584             :     const UniquePtr<StyleBasicShape>& aBasicShape,
     585             :     nscoord aShapeMargin,
     586             :     nsIFrame* const aFrame,
     587             :     const LogicalRect& aShapeBoxRect,
     588             :     const LogicalRect& aMarginRect,
     589             :     WritingMode aWM,
     590             :     const nsSize& aContainerSize);
     591             : 
     592             :   static UniquePtr<ShapeInfo> CreateInset(
     593             :     const UniquePtr<StyleBasicShape>& aBasicShape,
     594             :     nscoord aShapeMargin,
     595             :     nsIFrame* aFrame,
     596             :     const LogicalRect& aShapeBoxRect,
     597             :     WritingMode aWM,
     598             :     const nsSize& aContainerSize);
     599             : 
     600             :   static UniquePtr<ShapeInfo> CreateCircleOrEllipse(
     601             :     const UniquePtr<StyleBasicShape>& aBasicShape,
     602             :     nscoord aShapeMargin,
     603             :     nsIFrame* const aFrame,
     604             :     const LogicalRect& aShapeBoxRect,
     605             :     WritingMode aWM,
     606             :     const nsSize& aContainerSize);
     607             : 
     608             :   static UniquePtr<ShapeInfo> CreatePolygon(
     609             :     const UniquePtr<StyleBasicShape>& aBasicShape,
     610             :     nscoord aShapeMargin,
     611             :     nsIFrame* const aFrame,
     612             :     const LogicalRect& aShapeBoxRect,
     613             :     const LogicalRect& aMarginRect,
     614             :     WritingMode aWM,
     615             :     const nsSize& aContainerSize);
     616             : 
     617             :   static UniquePtr<ShapeInfo> CreateImageShape(
     618             :     const UniquePtr<nsStyleImage>& aShapeImage,
     619             :     float aShapeImageThreshold,
     620             :     nscoord aShapeMargin,
     621             :     nsIFrame* const aFrame,
     622             :     const LogicalRect& aMarginRect,
     623             :     WritingMode aWM,
     624             :     const nsSize& aContainerSize);
     625             : 
     626             : protected:
     627             :   // Compute the minimum line-axis difference between the bounding shape
     628             :   // box and its rounded corner within the given band (block-axis region).
     629             :   // This is used as a helper function to compute the LineRight() and
     630             :   // LineLeft(). See the picture in the implementation for an example.
     631             :   // RadiusL and RadiusB stand for radius on the line-axis and block-axis.
     632             :   //
     633             :   // Returns radius-x diff on the line-axis, or 0 if there's no rounded
     634             :   // corner within the given band.
     635             :   static nscoord ComputeEllipseLineInterceptDiff(
     636             :     const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
     637             :     const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
     638             :     const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
     639             :     const nscoord aBandBStart, const nscoord aBandBEnd);
     640             : 
     641             :   static nscoord XInterceptAtY(const nscoord aY, const nscoord aRadiusX,
     642             :                                const nscoord aRadiusY);
     643             : 
     644             :   // Convert the physical point to the special logical coordinate space
     645             :   // used in float manager.
     646             :   static nsPoint ConvertToFloatLogical(const nsPoint& aPoint,
     647             :                                        WritingMode aWM,
     648             :                                        const nsSize& aContainerSize);
     649             : 
     650             :   // Convert the half corner radii (nscoord[8]) to the special logical
     651             :   // coordinate space used in float manager.
     652             :   static UniquePtr<nscoord[]> ConvertToFloatLogical(
     653             :     const nscoord aRadii[8],
     654             :     WritingMode aWM);
     655             : 
     656             :   // Some ShapeInfo subclasses may define their float areas in intervals.
     657             :   // Each interval is a rectangle that is one device pixel deep in the block
     658             :   // axis. The values are stored as block edges in the y coordinates,
     659             :   // and inline edges as the x coordinates. Interval arrays should be sorted
     660             :   // on increasing y values. This function uses a binary search to find the
     661             :   // first interval that contains aTargetY. If no such interval exists, this
     662             :   // function returns aIntervals.Length().
     663             :   static size_t MinIntervalIndexContainingY(const nsTArray<nsRect>& aIntervals,
     664             :                                             const nscoord aTargetY);
     665             : 
     666             :   // This interval function is designed to handle the arguments to ::LineLeft()
     667             :   // and LineRight() and interpret them for the supplied aIntervals.
     668             :   static nscoord LineEdge(const nsTArray<nsRect>& aIntervals,
     669             :                           const nscoord aBStart,
     670             :                           const nscoord aBEnd,
     671             :                           bool aIsLineLeft);
     672             : 
     673             :   // These types, constants, and functions are useful for ShapeInfos that
     674             :   // allocate a distance field. Efficient distance field calculations use
     675             :   // integer values that are 5X the Euclidean distance. MAX_MARGIN_5X is the
     676             :   // largest possible margin that we can calculate (in 5X integer dev pixels),
     677             :   // given these constraints.
     678             :   typedef uint16_t dfType;
     679             :   static const dfType MAX_CHAMFER_VALUE;
     680             :   static const dfType MAX_MARGIN;
     681             :   static const dfType MAX_MARGIN_5X;
     682             : 
     683             :   // This function returns a typed, overflow-safe value of aShapeMargin in
     684             :   // 5X integer dev pixels.
     685             :   static dfType CalcUsedShapeMargin5X(nscoord aShapeMargin,
     686             :                                       int32_t aAppUnitsPerDevPixel);
     687             : };
     688             : 
     689             : const nsFloatManager::ShapeInfo::dfType
     690             : nsFloatManager::ShapeInfo::MAX_CHAMFER_VALUE = 11;
     691             : 
     692             : const nsFloatManager::ShapeInfo::dfType
     693             : nsFloatManager::ShapeInfo::MAX_MARGIN = (std::numeric_limits<dfType>::max() -
     694             :                                          MAX_CHAMFER_VALUE) / 5;
     695             : 
     696             : const nsFloatManager::ShapeInfo::dfType
     697             : nsFloatManager::ShapeInfo::MAX_MARGIN_5X = MAX_MARGIN * 5;
     698             : 
     699             : /////////////////////////////////////////////////////////////////////////////
     700             : // EllipseShapeInfo
     701           0 : //
     702             : // Implements shape-outside: circle() and shape-outside: ellipse().
     703             : //
     704             : class nsFloatManager::EllipseShapeInfo final : public nsFloatManager::ShapeInfo
     705             : {
     706             : public:
     707             :   // Construct the float area using math to calculate the shape boundary.
     708             :   // This is the fast path and should be used when shape-margin is negligible,
     709             :   // or when the two values of aRadii are roughly equal. Those two conditions
     710             :   // are defined by ShapeMarginIsNegligible() and RadiiAreRoughlyEqual(). In
     711             :   // those cases, we can conveniently represent the entire float area using
     712             :   // an ellipse.
     713             :   EllipseShapeInfo(const nsPoint& aCenter,
     714             :                    const nsSize& aRadii,
     715             :                    nscoord aShapeMargin);
     716             : 
     717             :   // Construct the float area using rasterization to calculate the shape
     718             :   // boundary. This constructor accounts for the fact that applying
     719             :   // 'shape-margin' to an ellipse produces a shape that is not mathematically
     720             :   // representable as an ellipse.
     721             :   EllipseShapeInfo(const nsPoint& aCenter,
     722             :                    const nsSize& aRadii,
     723             :                    nscoord aShapeMargin,
     724             :                    int32_t aAppUnitsPerDevPixel);
     725             : 
     726             :   static bool ShapeMarginIsNegligible(nscoord aShapeMargin) {
     727             :     // For now, only return true for a shape-margin of 0. In the future, if
     728             :     // we want to enable use of the fast-path constructor more often, this
     729             :     // limit could be increased;
     730             :     static const nscoord SHAPE_MARGIN_NEGLIGIBLE_MAX(0);
     731             :     return aShapeMargin <= SHAPE_MARGIN_NEGLIGIBLE_MAX;
     732             :   }
     733             : 
     734             :   static bool RadiiAreRoughlyEqual(const nsSize& aRadii) {
     735             :     // For now, only return true when we are exactly equal. In the future, if
     736           0 :     // we want to enable use of the fast-path constructor more often, this
     737             :     // could be generalized to allow radii that are in some close proportion
     738             :     // to each other.
     739             :     return aRadii.width == aRadii.height;
     740             :   }
     741             :   nscoord LineEdge(const nscoord aBStart,
     742             :                    const nscoord aBEnd,
     743             :                    bool aLeft) const;
     744             :   nscoord LineLeft(const nscoord aBStart,
     745           0 :                    const nscoord aBEnd) const override;
     746           0 :   nscoord LineRight(const nscoord aBStart,
     747             :                     const nscoord aBEnd) const override;
     748           0 :   nscoord BStart() const override {
     749           0 :     return mCenter.y - mRadii.height - mShapeMargin;
     750             :   }
     751           0 :   nscoord BEnd() const override {
     752             :     return mCenter.y + mRadii.height + mShapeMargin;
     753             :   }
     754             :   bool IsEmpty() const override {
     755           0 :     // An EllipseShapeInfo is never empty, because an ellipse or circle with
     756             :     // a zero radius acts like a point, and an ellipse with one zero radius
     757           0 :     // acts like a line.
     758           0 :     return false;
     759             :   }
     760             :   bool MayNarrowInBlockDirection() const override {
     761           0 :     return true;
     762             :   }
     763           0 : 
     764             :   void Translate(nscoord aLineLeft, nscoord aBlockStart) override
     765           0 :   {
     766           0 :     mCenter.MoveBy(aLineLeft, aBlockStart);
     767             : 
     768           0 :     for (nsRect& interval : mIntervals) {
     769             :       interval.MoveBy(aLineLeft, aBlockStart);
     770             :     }
     771             :   }
     772             : 
     773             : private:
     774             :   // The position of the center of the ellipse. The coordinate space is the
     775             :   // same as FloatInfo::mRect.
     776             :   nsPoint mCenter;
     777             :   // The radii of the ellipse in app units. The width and height represent
     778             :   // the line-axis and block-axis radii of the ellipse.
     779             :   nsSize mRadii;
     780             :   // The shape-margin of the ellipse in app units. If this value is greater
     781             :   // than zero, then we calculate the bounds of the ellipse + margin using
     782             :   // numerical methods and store the values in mIntervals.
     783             :   nscoord mShapeMargin;
     784             : 
     785             :   // An interval is slice of the float area defined by this EllipseShapeInfo.
     786             :   // Each interval is a rectangle that is one pixel deep in the block
     787             :   // axis. The values are stored as block edges in the y coordinates,
     788             :   // and inline edges as the x coordinates.
     789             : 
     790             :   // The intervals are stored in ascending order on y.
     791           0 :   nsTArray<nsRect> mIntervals;
     792             : };
     793           0 : 
     794             : nsFloatManager::EllipseShapeInfo::EllipseShapeInfo(const nsPoint& aCenter,
     795             :                                                    const nsSize& aRadii,
     796           0 :                                                    nscoord aShapeMargin)
     797             :   : mCenter(aCenter)
     798           0 :   , mRadii(aRadii)
     799             :   , mShapeMargin(0) // We intentionally ignore the value of aShapeMargin here.
     800             : {
     801             :   MOZ_ASSERT(RadiiAreRoughlyEqual(aRadii) ||
     802             :              ShapeMarginIsNegligible(aShapeMargin),
     803             :              "This constructor should only be called when margin is "
     804             :              "negligible or radii are roughly equal.");
     805           0 : 
     806           0 :   // We add aShapeMargin into the radii, and we earlier stored a mShapeMargin
     807           0 :   // of zero.
     808             :   mRadii.width += aShapeMargin;
     809           0 :   mRadii.height += aShapeMargin;
     810             : }
     811             : 
     812           0 : nsFloatManager::EllipseShapeInfo::EllipseShapeInfo(const nsPoint& aCenter,
     813             :                                                    const nsSize& aRadii,
     814             :                                                    nscoord aShapeMargin,
     815           0 :                                                    int32_t aAppUnitsPerDevPixel)
     816             :   : mCenter(aCenter)
     817           0 :   , mRadii(aRadii)
     818             :   , mShapeMargin(aShapeMargin)
     819             : {
     820           0 :   if (RadiiAreRoughlyEqual(aRadii) || ShapeMarginIsNegligible(aShapeMargin)) {
     821           0 :     // Mimic the behavior of the simple constructor, by adding aShapeMargin
     822           0 :     // into the radii, and then storing mShapeMargin of zero.
     823           0 :     mRadii.width += mShapeMargin;
     824             :     mRadii.height += mShapeMargin;
     825             :     mShapeMargin = 0;
     826             :     return;
     827             :   }
     828             : 
     829             :   // We have to calculate a distance field from the ellipse edge, then build
     830             :   // intervals based on pixels with less than aShapeMargin distance to an
     831             :   // edge pixel.
     832             : 
     833             :   // mCenter and mRadii have already been translated into logical coordinates.
     834             :   // x = inline, y = block. Due to symmetry, we only need to calculate the
     835             :   // distance field for one quadrant of the ellipse. We choose the positive-x,
     836             :   // positive-y quadrant (the lower right quadrant in horizontal-tb writing
     837             :   // mode). We choose this quadrant because it allows us to traverse our
     838             :   // distance field in memory order, which is more cache efficient.
     839             :   // When we apply these intervals in LineLeft() and LineRight(), we
     840             :   // account for block ranges that hit other quadrants, or hit multiple
     841             :   // quadrants.
     842             : 
     843             :   // Given this setup, computing the distance field is a one-pass O(n)
     844             :   // operation that runs from block top-to-bottom, inline left-to-right. We
     845             :   // use a chamfer 5-7-11 5x5 matrix to compute minimum distance to an edge
     846             :   // pixel. This integer math computation is reasonably close to the true
     847             :   // Euclidean distance. The distances will be approximately 5x the true
     848           0 :   // distance, quantized in integer units. The 5x is factored away in the
     849             :   // comparison which builds the intervals.
     850             :   dfType usedMargin5X = CalcUsedShapeMargin5X(aShapeMargin,
     851             :                                               aAppUnitsPerDevPixel);
     852             : 
     853             :   // Calculate the bounds of one quadrant of the ellipse, in integer device
     854           0 :   // pixels. These bounds are equal to the rectangle defined by the radii,
     855           0 :   // plus the shape-margin value in both dimensions.
     856             :   const LayoutDeviceIntSize bounds =
     857             :     LayoutDevicePixel::FromAppUnitsRounded(mRadii, aAppUnitsPerDevPixel) +
     858             :     LayoutDeviceIntSize(usedMargin5X / 5, usedMargin5X / 5);
     859             : 
     860             :   // Since our distance field is computed with a 5x5 neighborhood, but only
     861             :   // looks in the negative block and negative inline directions, it is
     862             :   // effectively a 3x3 neighborhood. We need to expand our distance field
     863             :   // outwards by a further 2 pixels in both axes (on the minimum block edge
     864             :   // and the minimum inline edge). We call this edge area the expanded region.
     865             : 
     866             :   static const uint32_t iExpand = 2;
     867             :   static const uint32_t bExpand = 2;
     868             : 
     869             :   // Clamp the size of our distance field sizes to prevent multiplication
     870           0 :   // overflow.
     871           0 :   static const uint32_t DF_SIDE_MAX =
     872           0 :     floor(sqrt((double)(std::numeric_limits<int32_t>::max())));
     873           0 :   const uint32_t iSize = std::min(bounds.width + iExpand, DF_SIDE_MAX);
     874             :   const uint32_t bSize = std::min(bounds.height + bExpand, DF_SIDE_MAX);
     875             :   auto df = MakeUniqueFallible<dfType[]>(iSize * bSize);
     876             :   if (!df) {
     877             :     // Without a distance field, we can't reason about the float area.
     878             :     return;
     879             :   }
     880             : 
     881             :   // Single pass setting distance field, in positive block direction, three
     882             :   // cases:
     883             :   // 1) Expanded region pixel: set to MAX_MARGIN_5X.
     884             :   // 2) Pixel within the ellipse: set to 0.
     885           0 :   // 3) Other pixel: set to minimum neighborhood distance value, computed
     886           0 :   //                 with 5-7-11 chamfer.
     887           0 : 
     888           0 :   for (uint32_t b = 0; b < bSize; ++b) {
     889             :     bool bIsInExpandedRegion(b < bExpand);
     890             :     nscoord bInAppUnits = (b - bExpand) * aAppUnitsPerDevPixel;
     891             :     bool bIsMoreThanEllipseBEnd(bInAppUnits > mRadii.height);
     892             : 
     893             :     // Find the i intercept of the ellipse edge for this block row, and
     894             :     // adjust it to compensate for the expansion of the inline dimension.
     895             :     // If we're in the expanded region, or if we're using a b that's more
     896             :     // than the bEnd of the ellipse, the intercept is nscoord_MIN.
     897             :     // We have one other special case to consider: when the ellipse has no
     898             :     // height. In that case we treat the bInAppUnits == 0 case as
     899           0 :     // intercepting at the width of the ellipse. All other cases solve
     900           0 :     // the intersection mathematically.
     901           0 :     const int32_t iIntercept =
     902           0 :       (bIsInExpandedRegion || bIsMoreThanEllipseBEnd) ? nscoord_MIN :
     903             :       iExpand + NSAppUnitsToIntPixels(
     904           0 :         (!!mRadii.height || bInAppUnits) ?
     905             :         XInterceptAtY(bInAppUnits, mRadii.width, mRadii.height) :
     906             :         mRadii.width,
     907           0 :         aAppUnitsPerDevPixel);
     908             : 
     909           0 :     // Set iMax in preparation for this block row.
     910           0 :     int32_t iMax = iIntercept;
     911           0 : 
     912             :     for (uint32_t i = 0; i < iSize; ++i) {
     913             :       const uint32_t index = i + b * iSize;
     914             :       MOZ_ASSERT(index < (iSize * bSize),
     915           0 :                  "Our distance field index should be in-bounds.");
     916             : 
     917             :       // Handle our three cases, in order.
     918           0 :       if (i < iExpand ||
     919           0 :           bIsInExpandedRegion) {
     920             :         // Case 1: Expanded reqion pixel.
     921             :         df[index] = MAX_MARGIN_5X;
     922             :       } else if ((int32_t)i <= iIntercept) {
     923           0 :         // Case 2: Pixel within the ellipse, or just outside the edge of it.
     924             :         // Having a positive height indicates that there's an area we can
     925             :         // be inside of.
     926             :         df[index] = (!!mRadii.height) ? 0 : 5;
     927             :       } else {
     928             :         // Case 3: Other pixel.
     929             : 
     930             :         // Backward-looking neighborhood distance from target pixel X
     931             :         // with chamfer 5-7-11 looks like:
     932             :         //
     933             :         // +--+--+--+
     934             :         // |  |11|  |
     935             :         // +--+--+--+
     936             :         // |11| 7| 5|
     937             :         // +--+--+--+
     938             :         // |  | 5| X|
     939             :         // +--+--+--+
     940           0 :         //
     941             :         // X should be set to the minimum of the values of all of the numbered
     942             :         // neighbors summed with the value in that chamfer cell.
     943             :         MOZ_ASSERT(index - iSize - 2 < (iSize * bSize) &&
     944             :                    index - (iSize * 2) - 1 < (iSize * bSize),
     945           0 :                    "Our distance field most extreme indices should be "
     946           0 :                    "in-bounds.");
     947           0 : 
     948           0 :         df[index] = std::min<dfType>(df[index - 1] + 5,
     949           0 :                     std::min<dfType>(df[index - iSize] + 5,
     950             :                     std::min<dfType>(df[index - iSize - 1] + 7,
     951             :                     std::min<dfType>(df[index - iSize - 2] + 11,
     952             :                                      df[index - (iSize * 2) - 1] + 11))));
     953           0 : 
     954           0 :         // Check the df value and see if it's less than or equal to the
     955             :         // usedMargin5X value.
     956             :         if (df[index] <= usedMargin5X) {
     957             :           MOZ_ASSERT(iMax < (int32_t)i);
     958             :           iMax = i;
     959             :         } else {
     960             :           // Since we're computing the bottom-right quadrant, there's no way
     961             :           // for a later i value in this row to be within the usedMargin5X
     962             :           // value. Likewise, every row beyond us will encounter this
     963             :           // condition with an i value less than or equal to our i value now.
     964             :           // Since our chamfer only looks upward and leftward, we can stop
     965             :           // calculating for the rest of the row, because the distance field
     966             :           // values there will never be looked at in a later row's chamfer
     967             :           // calculation.
     968             :           break;
     969             :         }
     970             :       }
     971             :     }
     972             : 
     973           0 :     // It's very likely, though not guaranteed that we will find an pixel
     974             :     // within the shape-margin distance for each block row. This may not
     975             :     // always be true due to rounding errors.
     976           0 :     if (iMax > nscoord_MIN) {
     977             :       // Origin for this interval is at the center of the ellipse, adjusted
     978             :       // in the positive block direction by bInAppUnits.
     979           0 :       nsPoint origin(aCenter.x, aCenter.y + bInAppUnits);
     980           0 :       // Size is an inline iMax plus 1 (to account for the whole pixel) dev
     981           0 :       // pixels, by 1 block dev pixel. We convert this to app units.
     982             :       nsSize size((iMax - iExpand + 1) * aAppUnitsPerDevPixel,
     983             :                   aAppUnitsPerDevPixel);
     984             :       mIntervals.AppendElement(nsRect(origin, size));
     985             :     }
     986             :   }
     987           0 : }
     988             : 
     989             : nscoord
     990             : nsFloatManager::EllipseShapeInfo::LineEdge(const nscoord aBStart,
     991             :                                            const nscoord aBEnd,
     992           0 :                                            bool aIsLineLeft) const
     993             : {
     994           0 :   // If no mShapeMargin, just compute the edge using math.
     995             :   if (mShapeMargin == 0) {
     996           0 :     nscoord lineDiff =
     997           0 :       ComputeEllipseLineInterceptDiff(BStart(), BEnd(),
     998           0 :                                       mRadii.width, mRadii.height,
     999           0 :                                       mRadii.width, mRadii.height,
    1000             :                                       aBStart, aBEnd);
    1001             :     return mCenter.x + (aIsLineLeft ? (-mRadii.width + lineDiff) :
    1002             :                                       (mRadii.width - lineDiff));
    1003           0 :   }
    1004           0 : 
    1005           0 :   // We are checking against our intervals. Make sure we have some.
    1006             :   if (mIntervals.IsEmpty()) {
    1007             :     NS_WARNING("With mShapeMargin > 0, we can't proceed without intervals.");
    1008             :     return aIsLineLeft ? nscoord_MAX : nscoord_MIN;
    1009             :   }
    1010             : 
    1011             :   // Map aBStart and aBEnd into our intervals. Our intervals are calculated
    1012           0 :   // for the lower-right quadrant (in terms of horizontal-tb writing mode).
    1013           0 :   // If aBStart and aBEnd span the center of the ellipse, then we know we
    1014           0 :   // are at the maximum displacement from the center.
    1015           0 :   bool bStartIsAboveCenter = (aBStart < mCenter.y);
    1016           0 :   bool bEndIsBelowOrAtCenter = (aBEnd >= mCenter.y);
    1017             :   if (bStartIsAboveCenter && bEndIsBelowOrAtCenter) {
    1018             :     return mCenter.x + (aIsLineLeft ? (-mRadii.width - mShapeMargin) :
    1019             :                                       (mRadii.width + mShapeMargin));
    1020             :   }
    1021             : 
    1022             :   // aBStart and aBEnd don't span the center. Since the intervals are
    1023             :   // strictly wider approaching the center (the start of the mIntervals
    1024             :   // array), we only need to find the interval at the block value closest to
    1025             :   // the center. We find the min of aBStart, aBEnd, and their reflections --
    1026             :   // whichever two of them are within the lower-right quadrant. When we
    1027             :   // reflect from the upper-right quadrant to the lower-right, we have to
    1028             :   // subtract 1 from the reflection, to account that block values are always
    1029             :   // addressed from the leading block edge.
    1030             : 
    1031             :   // The key example is when we check with aBStart == aBEnd at the top of the
    1032             :   // intervals. That block line would be considered contained in the
    1033           0 :   // intervals (though it has no height), but its reflection would not be
    1034           0 :   // within the intervals unless we subtract 1.
    1035             :   nscoord bSmallestWithinIntervals = std::min(
    1036           0 :     bStartIsAboveCenter ? aBStart + (mCenter.y - aBStart) * 2 - 1 : aBStart,
    1037             :     bEndIsBelowOrAtCenter ? aBEnd : aBEnd + (mCenter.y - aBEnd) * 2 - 1);
    1038             : 
    1039             :   MOZ_ASSERT(bSmallestWithinIntervals >= mCenter.y &&
    1040           0 :              bSmallestWithinIntervals < BEnd(),
    1041           0 :              "We should have a block value within the float area.");
    1042           0 : 
    1043             :   size_t index = MinIntervalIndexContainingY(mIntervals,
    1044             :                                              bSmallestWithinIntervals);
    1045             :   if (index >= mIntervals.Length()) {
    1046             :     // This indicates that our intervals don't cover the block value
    1047             :     // bSmallestWithinIntervals. This can happen when rounding error in the
    1048             :     // distance field calculation resulted in the last block pixel row not
    1049             :     // contributing to the float area. As long as we're within one block pixel
    1050           0 :     // past the last interval, this is an expected outcome.
    1051           0 : #ifdef DEBUG
    1052           0 :     nscoord onePixelPastLastInterval =
    1053             :       mIntervals[mIntervals.Length() - 1].YMost() +
    1054             :       mIntervals[mIntervals.Length() - 1].Height();
    1055             :     NS_WARNING_ASSERTION(bSmallestWithinIntervals < onePixelPastLastInterval,
    1056           0 :                          "We should have found a matching interval for this "
    1057             :                          "block value.");
    1058             : #endif
    1059             :     return aIsLineLeft ? nscoord_MAX : nscoord_MIN;
    1060             :   }
    1061             : 
    1062             :   // The interval is storing the line right value. If aIsLineLeft is true,
    1063             :   // return the line right value reflected about the center. Since this is
    1064           0 :   // an inline measurement, it's just checking the distance to an edge, and
    1065           0 :   // not a collision with a specific pixel. For that reason, we don't need
    1066             :   // to subtract 1 from the reflection, as we did with the block reflection.
    1067             :   nscoord iLineRight = mIntervals[index].XMost();
    1068             :   return aIsLineLeft ? iLineRight - (iLineRight - mCenter.x) * 2
    1069             :                      : iLineRight;
    1070           0 : }
    1071             : 
    1072             : nscoord
    1073           0 : nsFloatManager::EllipseShapeInfo::LineLeft(const nscoord aBStart,
    1074             :                                            const nscoord aBEnd) const
    1075             : {
    1076             :   return LineEdge(aBStart, aBEnd, true);
    1077           0 : }
    1078             : 
    1079             : nscoord
    1080           0 : nsFloatManager::EllipseShapeInfo::LineRight(const nscoord aBStart,
    1081             :                                             const nscoord aBEnd) const
    1082             : {
    1083             :   return LineEdge(aBStart, aBEnd, false);
    1084             : }
    1085             : 
    1086             : /////////////////////////////////////////////////////////////////////////////
    1087             : // RoundedBoxShapeInfo
    1088           0 : //
    1089             : // Implements shape-outside: <shape-box> and shape-outside: inset().
    1090             : //
    1091           0 : class nsFloatManager::RoundedBoxShapeInfo final : public nsFloatManager::ShapeInfo
    1092             : {
    1093           0 : public:
    1094           0 :   RoundedBoxShapeInfo(const nsRect& aRect,
    1095           0 :                       UniquePtr<nscoord[]> aRadii)
    1096           0 :     : mRect(aRect)
    1097             :     , mRadii(std::move(aRadii))
    1098             :     , mShapeMargin(0)
    1099             :   {}
    1100             : 
    1101             :   RoundedBoxShapeInfo(const nsRect& aRect,
    1102             :                       UniquePtr<nscoord[]> aRadii,
    1103             :                       nscoord aShapeMargin,
    1104             :                       int32_t aAppUnitsPerDevPixel);
    1105             : 
    1106             :   nscoord LineLeft(const nscoord aBStart,
    1107           0 :                    const nscoord aBEnd) const override;
    1108           0 :   nscoord LineRight(const nscoord aBStart,
    1109           0 :                     const nscoord aBEnd) const override;
    1110             :   nscoord BStart() const override { return mRect.y; }
    1111             :   nscoord BEnd() const override { return mRect.YMost(); }
    1112             :   bool IsEmpty() const override {
    1113             :     // A RoundedBoxShapeInfo is never empty, because if it is collapsed to
    1114           0 :     // zero area, it acts like a point. If it is collapsed further, to become
    1115             :     // inside-out, it acts like a rect in the same shape as the inside-out
    1116           0 :     // rect.
    1117             :     return false;
    1118           0 :   }
    1119             :   bool MayNarrowInBlockDirection() const override {
    1120             :     // Only possible to narrow if there are non-null mRadii.
    1121           0 :     return !!mRadii;
    1122             :   }
    1123           0 : 
    1124             :   void Translate(nscoord aLineLeft, nscoord aBlockStart) override
    1125           0 :   {
    1126           0 :     mRect.MoveBy(aLineLeft, aBlockStart);
    1127             : 
    1128             :     if (mShapeMargin > 0) {
    1129           0 :       MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalTopRightCorner &&
    1130           0 :                  mLogicalBottomLeftCorner && mLogicalBottomRightCorner,
    1131           0 :                  "If we have positive shape-margin, we should have corners.");
    1132           0 :       mLogicalTopLeftCorner->Translate(aLineLeft, aBlockStart);
    1133             :       mLogicalTopRightCorner->Translate(aLineLeft, aBlockStart);
    1134           0 :       mLogicalBottomLeftCorner->Translate(aLineLeft, aBlockStart);
    1135             :       mLogicalBottomRightCorner->Translate(aLineLeft, aBlockStart);
    1136           0 :     }
    1137           0 :   }
    1138           0 : 
    1139           0 :   static bool EachCornerHasBalancedRadii(const nscoord* aRadii) {
    1140           0 :     return (aRadii[eCornerTopLeftX] == aRadii[eCornerTopLeftY] &&
    1141             :             aRadii[eCornerTopRightX] == aRadii[eCornerTopRightY] &&
    1142             :             aRadii[eCornerBottomLeftX] == aRadii[eCornerBottomLeftY] &&
    1143             :             aRadii[eCornerBottomRightX] == aRadii[eCornerBottomRightY]);
    1144             :   }
    1145             : 
    1146             : private:
    1147             :   // The rect of the rounded box shape in the float manager's coordinate
    1148             :   // space.
    1149             :   nsRect mRect;
    1150             :   // The half corner radii of the reference box. It's an nscoord[8] array
    1151             :   // in the float manager's coordinate space. If there are no radii, it's
    1152             :   // nullptr.
    1153             :   const UniquePtr<nscoord[]> mRadii;
    1154             : 
    1155             :   // A shape-margin value extends the boundaries of the float area. When our
    1156             :   // first constructor is used, it is for the creation of rounded boxes that
    1157             :   // can ignore shape-margin -- either because it was specified as zero or
    1158             :   // because the box shape and radii can be inflated to account for it. When
    1159             :   // our second constructor is used, we store the shape-margin value here.
    1160             :   const nscoord mShapeMargin;
    1161             : 
    1162             :   // If our second constructor is called (which implies mShapeMargin > 0),
    1163             :   // we will construct EllipseShapeInfo objects for each corner. We use the
    1164             :   // float logical naming here, where LogicalTopLeftCorner means the BStart
    1165             :   // LineLeft corner, and similarly for the other corners.
    1166             :   UniquePtr<EllipseShapeInfo> mLogicalTopLeftCorner;
    1167             :   UniquePtr<EllipseShapeInfo> mLogicalTopRightCorner;
    1168             :   UniquePtr<EllipseShapeInfo> mLogicalBottomLeftCorner;
    1169           0 :   UniquePtr<EllipseShapeInfo> mLogicalBottomRightCorner;
    1170             : };
    1171             : 
    1172           0 : nsFloatManager::RoundedBoxShapeInfo::RoundedBoxShapeInfo(const nsRect& aRect,
    1173             :   UniquePtr<nscoord[]> aRadii,
    1174           0 :   nscoord aShapeMargin,
    1175           0 :   int32_t aAppUnitsPerDevPixel)
    1176             :   : mRect(aRect)
    1177           0 :   , mRadii(std::move(aRadii))
    1178             :   , mShapeMargin(aShapeMargin)
    1179             : {
    1180             :   MOZ_ASSERT(mShapeMargin > 0 && !EachCornerHasBalancedRadii(mRadii.get()),
    1181             :              "Slow constructor should only be used for for shape-margin > 0 "
    1182             :              "and radii with elliptical corners.");
    1183             : 
    1184           0 :   // Before we inflate mRect by mShapeMargin, construct each of our corners.
    1185           0 :   // If we do it in this order, it's a bit simpler to calculate the center
    1186           0 :   // of each of the corners.
    1187           0 :   mLogicalTopLeftCorner = MakeUnique<EllipseShapeInfo>(
    1188           0 :     nsPoint(mRect.X() + mRadii[eCornerTopLeftX],
    1189             :             mRect.Y() + mRadii[eCornerTopLeftY]),
    1190           0 :     nsSize(mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY]),
    1191           0 :     mShapeMargin, aAppUnitsPerDevPixel);
    1192           0 : 
    1193           0 :   mLogicalTopRightCorner = MakeUnique<EllipseShapeInfo>(
    1194           0 :     nsPoint(mRect.XMost() - mRadii[eCornerTopRightX],
    1195             :             mRect.Y() + mRadii[eCornerTopRightY]),
    1196           0 :     nsSize(mRadii[eCornerTopRightX], mRadii[eCornerTopRightY]),
    1197           0 :     mShapeMargin, aAppUnitsPerDevPixel);
    1198           0 : 
    1199           0 :   mLogicalBottomLeftCorner = MakeUnique<EllipseShapeInfo>(
    1200           0 :     nsPoint(mRect.X() + mRadii[eCornerBottomLeftX],
    1201             :             mRect.YMost() - mRadii[eCornerBottomLeftY]),
    1202           0 :     nsSize(mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY]),
    1203           0 :     mShapeMargin, aAppUnitsPerDevPixel);
    1204           0 : 
    1205           0 :   mLogicalBottomRightCorner = MakeUnique<EllipseShapeInfo>(
    1206           0 :     nsPoint(mRect.XMost() - mRadii[eCornerBottomRightX],
    1207             :             mRect.YMost() - mRadii[eCornerBottomRightY]),
    1208             :     nsSize(mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY]),
    1209           0 :     mShapeMargin, aAppUnitsPerDevPixel);
    1210           0 : 
    1211             :   // Now we inflate our mRect by mShapeMargin.
    1212             :   mRect.Inflate(mShapeMargin);
    1213           0 : }
    1214             : 
    1215             : nscoord
    1216           0 : nsFloatManager::RoundedBoxShapeInfo::LineLeft(const nscoord aBStart,
    1217           0 :                                               const nscoord aBEnd) const
    1218           0 : {
    1219             :   if (mShapeMargin == 0) {
    1220             :     if (!mRadii) {
    1221             :       return mRect.x;
    1222           0 :     }
    1223             : 
    1224           0 :     nscoord lineLeftDiff =
    1225           0 :       ComputeEllipseLineInterceptDiff(
    1226           0 :         mRect.y, mRect.YMost(),
    1227           0 :         mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
    1228             :         mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
    1229             :         aBStart, aBEnd);
    1230           0 :     return mRect.x + lineLeftDiff;
    1231             :   }
    1232             : 
    1233             :   MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalBottomLeftCorner,
    1234           0 :              "If we have positive shape-margin, we should have corners.");
    1235           0 : 
    1236             :   // Determine if aBEnd is within our top corner.
    1237             :   if (aBEnd < mLogicalTopLeftCorner->BEnd()) {
    1238             :     return mLogicalTopLeftCorner->LineLeft(aBStart, aBEnd);
    1239           0 :   }
    1240           0 : 
    1241             :   // Determine if aBStart is within our bottom corner.
    1242             :   if (aBStart >= mLogicalBottomLeftCorner->BStart()) {
    1243             :     return mLogicalBottomLeftCorner->LineLeft(aBStart, aBEnd);
    1244             :   }
    1245             : 
    1246           0 :   // Either aBStart or aBEnd or both are within the flat part of our left
    1247             :   // edge. Because we've already inflated our mRect to encompass our
    1248             :   // mShapeMargin, we can just return the edge.
    1249             :   return mRect.X();
    1250           0 : }
    1251             : 
    1252             : nscoord
    1253           0 : nsFloatManager::RoundedBoxShapeInfo::LineRight(const nscoord aBStart,
    1254           0 :                                                const nscoord aBEnd) const
    1255           0 : {
    1256             :   if (mShapeMargin == 0) {
    1257             :     if (!mRadii) {
    1258             :       return mRect.XMost();
    1259           0 :     }
    1260             : 
    1261           0 :     nscoord lineRightDiff =
    1262           0 :       ComputeEllipseLineInterceptDiff(
    1263           0 :         mRect.y, mRect.YMost(),
    1264           0 :         mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
    1265             :         mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
    1266             :         aBStart, aBEnd);
    1267           0 :     return mRect.XMost() - lineRightDiff;
    1268             :   }
    1269             : 
    1270             :   MOZ_ASSERT(mLogicalTopRightCorner && mLogicalBottomRightCorner,
    1271           0 :              "If we have positive shape-margin, we should have corners.");
    1272           0 : 
    1273             :   // Determine if aBEnd is within our top corner.
    1274             :   if (aBEnd < mLogicalTopRightCorner->BEnd()) {
    1275             :     return mLogicalTopRightCorner->LineRight(aBStart, aBEnd);
    1276           0 :   }
    1277           0 : 
    1278             :   // Determine if aBStart is within our bottom corner.
    1279             :   if (aBStart >= mLogicalBottomRightCorner->BStart()) {
    1280             :     return mLogicalBottomRightCorner->LineRight(aBStart, aBEnd);
    1281             :   }
    1282             : 
    1283           0 :   // Either aBStart or aBEnd or both are within the flat part of our right
    1284             :   // edge. Because we've already inflated our mRect to encompass our
    1285             :   // mShapeMargin, we can just return the edge.
    1286             :   return mRect.XMost();
    1287             : }
    1288             : 
    1289             : /////////////////////////////////////////////////////////////////////////////
    1290             : // PolygonShapeInfo
    1291           0 : //
    1292             : // Implements shape-outside: polygon().
    1293             : //
    1294             : class nsFloatManager::PolygonShapeInfo final : public nsFloatManager::ShapeInfo
    1295             : {
    1296             : public:
    1297             :   explicit PolygonShapeInfo(nsTArray<nsPoint>&& aVertices);
    1298             :   PolygonShapeInfo(nsTArray<nsPoint>&& aVertices,
    1299             :                    nscoord aShapeMargin,
    1300             :                    int32_t aAppUnitsPerDevPixel,
    1301             :                    const nsRect& aMarginRect);
    1302             : 
    1303             :   nscoord LineLeft(const nscoord aBStart,
    1304           0 :                    const nscoord aBEnd) const override;
    1305           0 :   nscoord LineRight(const nscoord aBStart,
    1306           0 :                     const nscoord aBEnd) const override;
    1307             :   nscoord BStart() const override { return mBStart; }
    1308             :   nscoord BEnd() const override { return mBEnd; }
    1309             :   bool IsEmpty() const override {
    1310             :     // A PolygonShapeInfo is never empty, because the parser prevents us from
    1311           0 :     // creating a shape with no vertices. If we only have 1 vertex, the
    1312             :     // shape acts like a point. With 2 non-coincident vertices, the shape
    1313           0 :     // acts like a line.
    1314             :     return false;
    1315             :   }
    1316             :   bool MayNarrowInBlockDirection() const override { return true; }
    1317             : 
    1318             :   void Translate(nscoord aLineLeft, nscoord aBlockStart) override;
    1319             : 
    1320             : private:
    1321             :   // Helper method for determining the mBStart and mBEnd based on the
    1322             :   // vertices' y extent.
    1323             :   void ComputeExtent();
    1324             : 
    1325             :   // Helper method for implementing LineLeft() and LineRight().
    1326             :   nscoord ComputeLineIntercept(
    1327             :     const nscoord aBStart,
    1328             :     const nscoord aBEnd,
    1329             :     nscoord (*aCompareOp) (std::initializer_list<nscoord>),
    1330             :     const nscoord aLineInterceptInitialValue) const;
    1331             : 
    1332             :   // Given a horizontal line y, and two points p1 and p2 forming a line
    1333             :   // segment L. Solve x for the intersection of y and L. This method
    1334             :   // assumes y and L do intersect, and L is *not* horizontal.
    1335             :   static nscoord XInterceptAtY(const nscoord aY,
    1336             :                                const nsPoint& aP1,
    1337             :                                const nsPoint& aP2);
    1338             : 
    1339             :   // The vertices of the polygon in the float manager's coordinate space.
    1340             :   nsTArray<nsPoint> mVertices;
    1341             : 
    1342             :   // An interval is slice of the float area defined by this PolygonShapeInfo.
    1343             :   // These are only generated and used in float area calculations for
    1344             :   // shape-margin > 0. Each interval is a rectangle that is one device pixel
    1345             :   // deep in the block axis. The values are stored as block edges in the y
    1346             :   // coordinates, and inline edges as the x coordinates.
    1347             : 
    1348             :   // The intervals are stored in ascending order on y.
    1349             :   nsTArray<nsRect> mIntervals;
    1350             : 
    1351             :   // Computed block start and block end value of the polygon shape. These
    1352             :   // initial values are set to correct values in ComputeExtent(), which is
    1353             :   // called from all constructors. Afterwards, mBStart is guaranteed to be
    1354             :   // less than or equal to mBEnd.
    1355             :   nscoord mBStart = nscoord_MAX;
    1356           0 :   nscoord mBEnd = nscoord_MIN;
    1357           0 : };
    1358             : 
    1359           0 : nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(nsTArray<nsPoint>&& aVertices)
    1360           0 :   : mVertices(aVertices)
    1361             : {
    1362           0 :   ComputeExtent();
    1363             : }
    1364             : 
    1365             : nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(
    1366           0 :   nsTArray<nsPoint>&& aVertices,
    1367           0 :   nscoord aShapeMargin,
    1368             :   int32_t aAppUnitsPerDevPixel,
    1369           0 :   const nsRect& aMarginRect)
    1370             :   : mVertices(aVertices)
    1371             : {
    1372           0 :   MOZ_ASSERT(aShapeMargin > 0, "This constructor should only be used for a "
    1373             :                                "polygon with a positive shape-margin.");
    1374             : 
    1375             :   ComputeExtent();
    1376             : 
    1377             :   // With a positive aShapeMargin, we have to calculate a distance
    1378             :   // field from the opaque pixels, then build intervals based on
    1379             :   // them being within aShapeMargin distance to an opaque pixel.
    1380             : 
    1381             :   // Roughly: for each pixel in the margin box, we need to determine the
    1382             :   // distance to the nearest opaque image-pixel.  If that distance is less
    1383             :   // than aShapeMargin, we consider this margin-box pixel as being part of
    1384             :   // the float area.
    1385             : 
    1386             :   // Computing the distance field is a two-pass O(n) operation.
    1387             :   // We use a chamfer 5-7-11 5x5 matrix to compute minimum distance
    1388             :   // to an opaque pixel. This integer math computation is reasonably
    1389             :   // close to the true Euclidean distance. The distances will be
    1390             :   // approximately 5x the true distance, quantized in integer units.
    1391           0 :   // The 5x is factored away in the comparison used in the final
    1392             :   // pass which builds the intervals.
    1393             :   dfType usedMargin5X = CalcUsedShapeMargin5X(aShapeMargin,
    1394             :                                               aAppUnitsPerDevPixel);
    1395             : 
    1396             :   // Allocate our distance field.  The distance field has to cover
    1397             :   // the entire aMarginRect, since aShapeMargin could bleed into it.
    1398           0 :   // Conveniently, our vertices have been converted into this same space,
    1399           0 :   // so if we cover the aMarginRect, we cover all the vertices.
    1400             :   const LayoutDeviceIntSize marginRectDevPixels =
    1401             :     LayoutDevicePixel::FromAppUnitsRounded(aMarginRect.Size(),
    1402             :                                            aAppUnitsPerDevPixel);
    1403             : 
    1404             :   // Since our distance field is computed with a 5x5 neighborhood,
    1405             :   // we need to expand our distance field by a further 4 pixels in
    1406             :   // both axes, 2 on the leading edge and 2 on the trailing edge.
    1407             :   // We call this edge area the "expanded region".
    1408             :   static const uint32_t kiExpansionPerSide = 2;
    1409             :   static const uint32_t kbExpansionPerSide = 2;
    1410             : 
    1411             :   // Clamp the size of our distance field sizes to prevent multiplication
    1412             :   // overflow.
    1413             :   static const uint32_t DF_SIDE_MAX =
    1414             :     floor(sqrt((double)(std::numeric_limits<int32_t>::max())));
    1415             : 
    1416             :   // Clamp the margin plus 2X the expansion values between expansion + 1 and
    1417           0 :   // DF_SIDE_MAX. This ensures that the distance field allocation doesn't
    1418             :   // overflow during multiplication, and the reverse iteration doesn't
    1419             :   // underflow.
    1420           0 :   const uint32_t iSize = std::max(std::min(marginRectDevPixels.width +
    1421           0 :                                            (kiExpansionPerSide * 2),
    1422             :                                            DF_SIDE_MAX),
    1423             :                                   kiExpansionPerSide + 1);
    1424           0 :   const uint32_t bSize = std::max(std::min(marginRectDevPixels.height +
    1425             :                                            (kbExpansionPerSide * 2),
    1426             :                                            DF_SIDE_MAX),
    1427             :                                   kbExpansionPerSide + 1);
    1428             : 
    1429             :   // Since the margin-box size is CSS controlled, and large values will
    1430           0 :   // generate large iSize and bSize values, we do a fallible allocation for
    1431           0 :   // the distance field. If allocation fails, we early exit and layout will
    1432             :   // be wrong, but we'll avoid aborting from OOM.
    1433           0 :   auto df = MakeUniqueFallible<dfType[]>(iSize * bSize);
    1434             :   if (!df) {
    1435             :     // Without a distance field, we can't reason about the float area.
    1436             :     return;
    1437             :   }
    1438             : 
    1439             :   // First pass setting distance field, starting at top-left, three cases:
    1440             :   // 1) Expanded region pixel: set to MAX_MARGIN_5X.
    1441             :   // 2) Pixel within the polygon: set to 0.
    1442           0 :   // 3) Other pixel: set to minimum backward-looking neighborhood
    1443             :   //                 distance value, computed with 5-7-11 chamfer.
    1444             : 
    1445             :   for (uint32_t b = 0; b < bSize; ++b) {
    1446             :     // Find the left and right i intercepts of the polygon edge for this
    1447             :     // block row, and adjust them to compensate for the expansion of the
    1448           0 :     // inline dimension. If we're in the expanded region, or if we're using
    1449           0 :     // a b that's less than the bStart of the polygon, the intercepts are
    1450           0 :     // the nscoord min and max limits.
    1451             :     nscoord bInAppUnits = (b - kbExpansionPerSide) * aAppUnitsPerDevPixel;
    1452             :     bool bIsInExpandedRegion(b < kbExpansionPerSide ||
    1453             :                              b >= bSize - kbExpansionPerSide);
    1454             : 
    1455             :     // We now figure out the i values that correspond to the left edge and
    1456             :     // the right edge of the polygon at one-dev-pixel-thick strip of b. We
    1457             :     // have a ComputeLineIntercept function that takes and returns app unit
    1458             :     // coordinates in the space of aMarginRect. So to pass in b values, we
    1459           0 :     // first have to add the aMarginRect.y value. And for the values that we
    1460           0 :     // get out, we have to subtract away the aMarginRect.x value before
    1461           0 :     // converting the app units to dev pixels.
    1462             :     nscoord bInAppUnitsMarginRect = bInAppUnits + aMarginRect.y;
    1463           0 :     bool bIsLessThanPolygonBStart(bInAppUnitsMarginRect < mBStart);
    1464           0 :     bool bIsMoreThanPolygonBEnd(bInAppUnitsMarginRect > mBEnd);
    1465           0 : 
    1466           0 :     const int32_t iLeftEdge = (bIsInExpandedRegion ||
    1467           0 :                                bIsLessThanPolygonBStart ||
    1468             :                                bIsMoreThanPolygonBEnd) ? nscoord_MAX :
    1469           0 :       kiExpansionPerSide + NSAppUnitsToIntPixels(
    1470           0 :         ComputeLineIntercept(bInAppUnitsMarginRect,
    1471             :                              bInAppUnitsMarginRect + aAppUnitsPerDevPixel,
    1472             :                              std::min<nscoord>, nscoord_MAX) - aMarginRect.x,
    1473           0 :         aAppUnitsPerDevPixel);
    1474           0 : 
    1475           0 :     const int32_t iRightEdge = (bIsInExpandedRegion ||
    1476           0 :                                 bIsLessThanPolygonBStart ||
    1477             :                                 bIsMoreThanPolygonBEnd) ? nscoord_MIN :
    1478           0 :       kiExpansionPerSide + NSAppUnitsToIntPixels(
    1479           0 :         ComputeLineIntercept(bInAppUnitsMarginRect,
    1480             :                              bInAppUnitsMarginRect + aAppUnitsPerDevPixel,
    1481           0 :                              std::max<nscoord>, nscoord_MIN) - aMarginRect.x,
    1482           0 :         aAppUnitsPerDevPixel);
    1483           0 : 
    1484             :     for (uint32_t i = 0; i < iSize; ++i) {
    1485             :       const uint32_t index = i + b * iSize;
    1486             :       MOZ_ASSERT(index < (iSize * bSize),
    1487           0 :                  "Our distance field index should be in-bounds.");
    1488           0 : 
    1489             :       // Handle our three cases, in order.
    1490             :       if (i < kiExpansionPerSide ||
    1491           0 :           i >= iSize - kiExpansionPerSide ||
    1492           0 :           bIsInExpandedRegion) {
    1493             :         // Case 1: Expanded pixel.
    1494             :         df[index] = MAX_MARGIN_5X;
    1495             :       } else if ((int32_t)i >= iLeftEdge && (int32_t)i <= iRightEdge) {
    1496           0 :         // Case 2: Polygon pixel, either inside or just adjacent to the right
    1497             :         // edge. We need this special distinction to detect a space between
    1498             :         // edges that is less than one dev pixel.
    1499             :         df[index] = (int32_t)i < iRightEdge ? 0 : 5;
    1500             :       } else {
    1501             :         // Case 3: Other pixel.
    1502             : 
    1503             :         // Backward-looking neighborhood distance from target pixel X
    1504             :         // with chamfer 5-7-11 looks like:
    1505             :         //
    1506             :         // +--+--+--+--+--+
    1507             :         // |  |11|  |11|  |
    1508             :         // +--+--+--+--+--+
    1509             :         // |11| 7| 5| 7|11|
    1510             :         // +--+--+--+--+--+
    1511             :         // |  | 5| X|  |  |
    1512             :         // +--+--+--+--+--+
    1513             :         //
    1514           0 :         // X should be set to the minimum of MAX_MARGIN_5X and the
    1515             :         // values of all of the numbered neighbors summed with the
    1516             :         // value in that chamfer cell.
    1517             :         MOZ_ASSERT(index - (iSize * 2) - 1 < (iSize * bSize) &&
    1518             :                    index - iSize - 2 < (iSize * bSize),
    1519           0 :                    "Our distance field most extreme indices should be "
    1520           0 :                    "in-bounds.");
    1521           0 : 
    1522           0 :         df[index] = std::min<dfType>(MAX_MARGIN_5X,
    1523           0 :                     std::min<dfType>(df[index - (iSize * 2) - 1] + 11,
    1524           0 :                     std::min<dfType>(df[index - (iSize * 2) + 1] + 11,
    1525           0 :                     std::min<dfType>(df[index - iSize - 2] + 11,
    1526           0 :                     std::min<dfType>(df[index - iSize - 1] + 7,
    1527           0 :                     std::min<dfType>(df[index - iSize] + 5,
    1528             :                     std::min<dfType>(df[index - iSize + 1] + 7,
    1529             :                     std::min<dfType>(df[index - iSize + 2] + 11,
    1530             :                                      df[index - 1] + 5))))))));
    1531             :       }
    1532             :     }
    1533             :   }
    1534             : 
    1535             :   // Okay, time for the second pass. This pass is in reverse order from
    1536             :   // the first pass. All of our opaque pixels have been set to 0, and all
    1537             :   // of our expanded region pixels have been set to MAX_MARGIN_5X. Other
    1538             :   // pixels have been set to some value between those two (inclusive) but
    1539             :   // this hasn't yet taken into account the neighbors that were processed
    1540             :   // after them in the first pass. This time we reverse iterate so we can
    1541             :   // apply the forward-looking chamfer.
    1542             : 
    1543             :   // This time, we constrain our outer and inner loop to ignore the
    1544             :   // expanded region pixels. For each pixel we iterate, we set the df value
    1545             :   // to the minimum forward-looking neighborhood distance value, computed
    1546             :   // with a 5-7-11 chamfer. We also check each df value against the
    1547             :   // usedMargin5X threshold, and use that to set the iMin and iMax values
    1548             :   // for the interval we'll create for that block axis value (b).
    1549           0 : 
    1550           0 :   // At the end of each row, if any of the other pixels had a value less
    1551             :   // than usedMargin5X, we create an interval.
    1552             :   for (uint32_t b = bSize - kbExpansionPerSide - 1;
    1553             :        b >= kbExpansionPerSide; --b) {
    1554           0 :     // iMin tracks the first df pixel and iMax the last df pixel whose
    1555           0 :     // df[] value is less than usedMargin5X. Set iMin and iMax in
    1556             :     // preparation for this row or column.
    1557           0 :     int32_t iMin = iSize;
    1558           0 :     int32_t iMax = -1;
    1559           0 : 
    1560           0 :     for (uint32_t i = iSize - kiExpansionPerSide - 1;
    1561             :          i >= kiExpansionPerSide; --i) {
    1562             :       const uint32_t index = i + b * iSize;
    1563             :       MOZ_ASSERT(index < (iSize * bSize),
    1564             :                  "Our distance field index should be in-bounds.");
    1565           0 : 
    1566             :       // Only apply the chamfer calculation if the df value is not
    1567             :       // already 0, since the chamfer can only reduce the value.
    1568             :       if (df[index]) {
    1569             :         // Forward-looking neighborhood distance from target pixel X
    1570             :         // with chamfer 5-7-11 looks like:
    1571             :         //
    1572             :         // +--+--+--+--+--+
    1573             :         // |  |  | X| 5|  |
    1574             :         // +--+--+--+--+--+
    1575             :         // |11| 7| 5| 7|11|
    1576             :         // +--+--+--+--+--+
    1577             :         // |  |11|  |11|  |
    1578             :         // +--+--+--+--+--+
    1579             :         //
    1580           0 :         // X should be set to the minimum of its current value and
    1581             :         // the values of all of the numbered neighbors summed with
    1582             :         // the value in that chamfer cell.
    1583             :         MOZ_ASSERT(index + (iSize * 2) + 1 < (iSize * bSize) &&
    1584             :                    index + iSize + 2 < (iSize * bSize),
    1585           0 :                    "Our distance field most extreme indices should be "
    1586           0 :                    "in-bounds.");
    1587           0 : 
    1588           0 :         df[index] = std::min<dfType>(df[index],
    1589           0 :                     std::min<dfType>(df[index + (iSize * 2) + 1] + 11,
    1590           0 :                     std::min<dfType>(df[index + (iSize * 2) - 1] + 11,
    1591           0 :                     std::min<dfType>(df[index + iSize + 2] + 11,
    1592           0 :                     std::min<dfType>(df[index + iSize + 1] + 7,
    1593           0 :                     std::min<dfType>(df[index + iSize] + 5,
    1594             :                     std::min<dfType>(df[index + iSize - 1] + 7,
    1595             :                     std::min<dfType>(df[index + iSize - 2] + 11,
    1596             :                                      df[index + 1] + 5))))))));
    1597             :       }
    1598           0 : 
    1599           0 :       // Finally, we can check the df value and see if it's less than
    1600           0 :       // or equal to the usedMargin5X value.
    1601             :       if (df[index] <= usedMargin5X) {
    1602           0 :         if (iMax == -1) {
    1603             :           iMax = i;
    1604             :         }
    1605             :         MOZ_ASSERT(iMin > (int32_t)i);
    1606             :         iMin = i;
    1607           0 :       }
    1608             :     }
    1609             : 
    1610             :     if (iMax != -1) {
    1611             :       // Our interval values, iMin, iMax, and b are all calculated from
    1612             :       // the expanded region, which is based on the margin rect. To create
    1613             :       // our interval, we have to subtract kiExpansionPerSide from iMin and
    1614             :       // iMax, and subtract kbExpansionPerSide from b to account for the
    1615             :       // expanded region edges.  This produces coords that are relative to
    1616             :       // our margin-rect.
    1617             : 
    1618           0 :       // Origin for this interval is at the aMarginRect origin, adjusted in
    1619           0 :       // the block direction by b in app units, and in the inline direction
    1620           0 :       // by iMin in app units.
    1621           0 :       nsPoint origin(aMarginRect.x +
    1622             :                      (iMin - kiExpansionPerSide) * aAppUnitsPerDevPixel,
    1623             :                      aMarginRect.y +
    1624             :                      (b - kbExpansionPerSide) * aAppUnitsPerDevPixel);
    1625             : 
    1626             :       // Size is the difference in iMax and iMin, plus 1 (to account for the
    1627             :       // whole pixel) dev pixels, by 1 block dev pixel. We don't bother
    1628           0 :       // subtracting kiExpansionPerSide from iMin and iMax in this case
    1629           0 :       // because we only care about the distance between them. We convert
    1630             :       // everything to app units.
    1631           0 :       nsSize size((iMax - iMin + 1) * aAppUnitsPerDevPixel,
    1632             :                   aAppUnitsPerDevPixel);
    1633             : 
    1634             :       mIntervals.AppendElement(nsRect(origin, size));
    1635             :     }
    1636           0 :   }
    1637             : 
    1638             :   // Reverse the intervals keep the array sorted on the block direction.
    1639             :   mIntervals.Reverse();
    1640             : 
    1641           0 :   // Adjust our extents by aShapeMargin. This may cause overflow of some
    1642           0 :   // kind if aShapeMargin is large, so we do some clamping to maintain the
    1643             :   // invariant mBStart <= mBEnd.
    1644             :   mBStart = std::min(mBStart, mBStart - aShapeMargin);
    1645             :   mBEnd = std::max(mBEnd, mBEnd + aShapeMargin);
    1646           0 : }
    1647             : 
    1648             : nscoord
    1649             : nsFloatManager::PolygonShapeInfo::LineLeft(const nscoord aBStart,
    1650           0 :                                            const nscoord aBEnd) const
    1651           0 : {
    1652             :   // Use intervals if we have them.
    1653             :   if (!mIntervals.IsEmpty()) {
    1654             :     return LineEdge(mIntervals, aBStart, aBEnd, true);
    1655             :   }
    1656             : 
    1657             :   // We want the line-left-most inline-axis coordinate where the
    1658             :   // (block-axis) aBStart/aBEnd band crosses a line segment of the polygon.
    1659             :   // To get that, we start as line-right as possible (at nscoord_MAX). Then
    1660             :   // we iterate each line segment to compute its intersection point with the
    1661             :   // band (if any) and using std::min() successively to get the smallest
    1662             :   // inline-coordinates among those intersection points.
    1663           0 :   //
    1664             :   // Note: std::min<nscoord> means the function std::min() with template
    1665             :   // parameter nscoord, not the minimum value of nscoord.
    1666             :   return ComputeLineIntercept(aBStart, aBEnd, std::min<nscoord>, nscoord_MAX);
    1667           0 : }
    1668             : 
    1669             : nscoord
    1670             : nsFloatManager::PolygonShapeInfo::LineRight(const nscoord aBStart,
    1671           0 :                                             const nscoord aBEnd) const
    1672           0 : {
    1673             :   // Use intervals if we have them.
    1674             :   if (!mIntervals.IsEmpty()) {
    1675             :     return LineEdge(mIntervals, aBStart, aBEnd, false);
    1676             :   }
    1677             : 
    1678             :   // Similar to LineLeft(). Though here, we want the line-right-most
    1679           0 :   // inline-axis coordinate, so we instead start at nscoord_MIN and use
    1680             :   // std::max() to get the biggest inline-coordinate among those
    1681             :   // intersection points.
    1682             :   return ComputeLineIntercept(aBStart, aBEnd, std::max<nscoord>, nscoord_MIN);
    1683           0 : }
    1684             : 
    1685             : void
    1686             : nsFloatManager::PolygonShapeInfo::ComputeExtent()
    1687             : {
    1688           0 :   // mBStart and mBEnd are the lower and the upper bounds of all the
    1689           0 :   // vertex.y, respectively. The vertex.y is actually on the block-axis of
    1690           0 :   // the float manager's writing mode.
    1691             :   for (const nsPoint& vertex : mVertices) {
    1692             :     mBStart = std::min(mBStart, vertex.y);
    1693           0 :     mBEnd = std::max(mBEnd, vertex.y);
    1694             :   }
    1695           0 : 
    1696             :   MOZ_ASSERT(mBStart <= mBEnd, "Start of float area should be less than "
    1697             :                                "or equal to the end.");
    1698           0 : }
    1699             : 
    1700             : nscoord
    1701             : nsFloatManager::PolygonShapeInfo::ComputeLineIntercept(
    1702             :   const nscoord aBStart,
    1703             :   const nscoord aBEnd,
    1704           0 :   nscoord (*aCompareOp) (std::initializer_list<nscoord>),
    1705             :   const nscoord aLineInterceptInitialValue) const
    1706             : {
    1707           0 :   MOZ_ASSERT(aBStart <= aBEnd,
    1708           0 :              "The band's block start is greater than its block end?");
    1709             : 
    1710             :   const size_t len = mVertices.Length();
    1711             :   nscoord lineIntercept = aLineInterceptInitialValue;
    1712             : 
    1713             :   // We have some special treatment of horizontal lines between vertices.
    1714             :   // Generally, we can ignore the impact of the horizontal lines since their
    1715             :   // endpoints will be included in the lines preceeding or following them.
    1716             :   // However, it's possible the polygon is entirely a horizontal line,
    1717             :   // possibly built from more than one horizontal segment. In such a case,
    1718           0 :   // we need to have the horizontal line(s) contribute to the line intercepts.
    1719             :   // We do this by accepting horizontal lines until we find a non-horizontal
    1720             :   // line, after which all further horizontal lines are ignored.
    1721           0 :   bool canIgnoreHorizontalLines = false;
    1722           0 : 
    1723           0 :   // Iterate each line segment {p0, p1}, {p1, p2}, ..., {pn, p0}.
    1724             :   for (size_t i = 0; i < len; ++i) {
    1725             :     const nsPoint* smallYVertex = &mVertices[i];
    1726             :     const nsPoint* bigYVertex = &mVertices[(i + 1) % len];
    1727           0 : 
    1728             :     // Swap the two points to satisfy the requirement for calling
    1729             :     // XInterceptAtY.
    1730             :     if (smallYVertex->y > bigYVertex->y) {
    1731             :       std::swap(smallYVertex, bigYVertex);
    1732             :     }
    1733             : 
    1734             :     // Generally, we need to ignore line segments that either don't intersect
    1735           0 :     // the band, or merely touch it. However, if the polygon has no block extent
    1736           0 :     // (it is a point, or a horizontal line), and the band touches the line
    1737             :     // segment, we let that line segment through.
    1738             :     if ((aBStart >= bigYVertex->y || aBEnd <= smallYVertex->y) &&
    1739             :         !(mBStart == mBEnd && aBStart == bigYVertex->y)) {
    1740             :       // Skip computing the intercept if the band doesn't intersect the
    1741             :       // line segment.
    1742             :       continue;
    1743             :     }
    1744             : 
    1745           0 :     nscoord bStartLineIntercept;
    1746             :     nscoord bEndLineIntercept;
    1747           0 : 
    1748             :     if (smallYVertex->y == bigYVertex->y) {
    1749             :       // The line is horizontal; see if we can ignore it.
    1750             :       if (canIgnoreHorizontalLines) {
    1751             :         continue;
    1752             :       }
    1753             : 
    1754             :       // For a horizontal line that we can't ignore, we treat the two x value
    1755           0 :       // ends as the bStartLineIntercept and bEndLineIntercept. It doesn't
    1756           0 :       // matter which is applied to which, because they'll both be applied
    1757             :       // to aCompareOp.
    1758             :       bStartLineIntercept = smallYVertex->x;
    1759             :       bEndLineIntercept = bigYVertex->x;
    1760           0 :     } else {
    1761             :       // This is not a horizontal line. We can now ignore all future
    1762           0 :       // horizontal lines.
    1763             :       canIgnoreHorizontalLines = true;
    1764           0 : 
    1765             :       bStartLineIntercept =
    1766             :         aBStart <= smallYVertex->y
    1767           0 :           ? smallYVertex->x
    1768           0 :           : XInterceptAtY(aBStart, *smallYVertex, *bigYVertex);
    1769             :       bEndLineIntercept =
    1770             :         aBEnd >= bigYVertex->y
    1771             :           ? bigYVertex->x
    1772             :           : XInterceptAtY(aBEnd, *smallYVertex, *bigYVertex);
    1773             :     }
    1774             : 
    1775           0 :     // If either new intercept is more extreme than lineIntercept (per
    1776             :     // aCompareOp), then update lineIntercept to that value.
    1777             :     lineIntercept =
    1778           0 :       aCompareOp({lineIntercept, bStartLineIntercept, bEndLineIntercept});
    1779             :   }
    1780             : 
    1781             :   return lineIntercept;
    1782           0 : }
    1783             : 
    1784             : void
    1785           0 : nsFloatManager::PolygonShapeInfo::Translate(nscoord aLineLeft,
    1786           0 :                                             nscoord aBlockStart)
    1787             : {
    1788           0 :   for (nsPoint& vertex : mVertices) {
    1789           0 :     vertex.MoveBy(aLineLeft, aBlockStart);
    1790             :   }
    1791           0 :   for (nsRect& interval : mIntervals) {
    1792           0 :     interval.MoveBy(aLineLeft, aBlockStart);
    1793           0 :   }
    1794             :   mBStart += aBlockStart;
    1795             :   mBEnd += aBlockStart;
    1796           0 : }
    1797             : 
    1798             : /* static */ nscoord
    1799             : nsFloatManager::PolygonShapeInfo::XInterceptAtY(const nscoord aY,
    1800             :                                                 const nsPoint& aP1,
    1801             :                                                 const nsPoint& aP2)
    1802             : {
    1803           0 :   // Solve for x in the linear equation: x = x1 + (y-y1) * (x2-x1) / (y2-y1),
    1804             :   // where aP1 = (x1, y1) and aP2 = (x2, y2).
    1805             : 
    1806             :   MOZ_ASSERT(aP1.y <= aY && aY <= aP2.y,
    1807           0 :              "This function won't work if the horizontal line at aY and "
    1808             :              "the line segment (aP1, aP2) do not intersect!");
    1809             : 
    1810           0 :   MOZ_ASSERT(aP1.y != aP2.y,
    1811             :              "A horizontal line segment results in dividing by zero error!");
    1812             : 
    1813             :   return aP1.x + (aY - aP1.y) * (aP2.x - aP1.x) / (aP2.y - aP1.y);
    1814             : }
    1815             : 
    1816             : /////////////////////////////////////////////////////////////////////////////
    1817             : // ImageShapeInfo
    1818           0 : //
    1819             : // Implements shape-outside: <image>
    1820             : //
    1821             : class nsFloatManager::ImageShapeInfo final : public nsFloatManager::ShapeInfo
    1822             : {
    1823             : public:
    1824             :   ImageShapeInfo(uint8_t* aAlphaPixels,
    1825             :                  int32_t aStride,
    1826             :                  const LayoutDeviceIntSize& aImageSize,
    1827             :                  int32_t aAppUnitsPerDevPixel,
    1828             :                  float aShapeImageThreshold,
    1829             :                  nscoord aShapeMargin,
    1830             :                  const nsRect& aContentRect,
    1831             :                  const nsRect& aMarginRect,
    1832             :                  WritingMode aWM,
    1833             :                  const nsSize& aContainerSize);
    1834             : 
    1835             :   nscoord LineLeft(const nscoord aBStart,
    1836           0 :                    const nscoord aBEnd) const override;
    1837           0 :   nscoord LineRight(const nscoord aBStart,
    1838           0 :                     const nscoord aBEnd) const override;
    1839           0 :   nscoord BStart() const override { return mBStart; }
    1840             :   nscoord BEnd() const override { return mBEnd; }
    1841             :   bool IsEmpty() const override { return mIntervals.IsEmpty(); }
    1842             :   bool MayNarrowInBlockDirection() const override { return true; }
    1843             : 
    1844             :   void Translate(nscoord aLineLeft, nscoord aBlockStart) override;
    1845             : 
    1846             : private:
    1847             :   // An interval is slice of the float area defined by this ImageShapeInfo.
    1848             :   // Each interval is a rectangle that is one pixel deep in the block
    1849             :   // axis. The values are stored as block edges in the y coordinates,
    1850             :   // and inline edges as the x coordinates.
    1851             : 
    1852             :   // The intervals are stored in ascending order on y.
    1853             :   nsTArray<nsRect> mIntervals;
    1854             : 
    1855             :   nscoord mBStart = nscoord_MAX;
    1856             :   nscoord mBEnd = nscoord_MIN;
    1857             : 
    1858             :   // CreateInterval transforms the supplied aIMin and aIMax and aB
    1859             :   // values into an interval that respects the writing mode. An
    1860             :   // aOffsetFromContainer can be provided if the aIMin, aIMax, aB
    1861             :   // values were generated relative to something other than the container
    1862             :   // rect (such as the content rect or margin rect).
    1863             :   void CreateInterval(int32_t aIMin,
    1864             :                       int32_t aIMax,
    1865             :                       int32_t aB,
    1866             :                       int32_t aAppUnitsPerDevPixel,
    1867             :                       const nsPoint& aOffsetFromContainer,
    1868             :                       WritingMode aWM,
    1869           0 :                       const nsSize& aContainerSize);
    1870             : };
    1871             : 
    1872             : nsFloatManager::ImageShapeInfo::ImageShapeInfo(
    1873             :   uint8_t* aAlphaPixels,
    1874             :   int32_t aStride,
    1875             :   const LayoutDeviceIntSize& aImageSize,
    1876             :   int32_t aAppUnitsPerDevPixel,
    1877             :   float aShapeImageThreshold,
    1878             :   nscoord aShapeMargin,
    1879           0 :   const nsRect& aContentRect,
    1880             :   const nsRect& aMarginRect,
    1881           0 :   WritingMode aWM,
    1882             :   const nsSize& aContainerSize)
    1883             : {
    1884           0 :   MOZ_ASSERT(aShapeImageThreshold >=0.0 && aShapeImageThreshold <=1.0,
    1885             :              "The computed value of shape-image-threshold is wrong!");
    1886           0 : 
    1887             :   const uint8_t threshold = NSToIntFloor(aShapeImageThreshold * 255);
    1888           0 : 
    1889           0 :   MOZ_ASSERT(aImageSize.width >= 0 && aImageSize.height >= 0,
    1890             :              "Image size must be non-negative for our math to work.");
    1891           0 :   const uint32_t w = aImageSize.width;
    1892             :   const uint32_t h = aImageSize.height;
    1893             : 
    1894             :   if (aShapeMargin <= 0) {
    1895             :     // Without a positive aShapeMargin, all we have to do is a
    1896             :     // direct threshold comparison of the alpha pixels.
    1897             :     // https://drafts.csswg.org/css-shapes-1/#valdef-shape-image-threshold-number
    1898             : 
    1899             :     // Scan the pixels in a double loop. For horizontal writing modes, we do
    1900           0 :     // this row by row, from top to bottom. For vertical writing modes, we do
    1901           0 :     // column by column, from left to right. We define the two loops
    1902           0 :     // generically, then figure out the rows and cols within the inner loop.
    1903             :     const uint32_t bSize = aWM.IsVertical() ? w : h;
    1904             :     const uint32_t iSize = aWM.IsVertical() ? h : w;
    1905             :     for (uint32_t b = 0; b < bSize; ++b) {
    1906             :       // iMin and max store the start and end of the float area for the row
    1907             :       // or column represented by this iteration of the outer loop.
    1908           0 :       int32_t iMin = -1;
    1909           0 :       int32_t iMax = -1;
    1910           0 : 
    1911           0 :       for (uint32_t i = 0; i < iSize; ++i) {
    1912             :         const uint32_t col = aWM.IsVertical() ? b : i;
    1913             :         const uint32_t row = aWM.IsVertical() ? i : b;
    1914             :         const uint32_t index = col + row * aStride;
    1915             : 
    1916             :         // Determine if the alpha pixel at this row and column has a value
    1917           0 :         // greater than the threshold. If it does, update our iMin and iMax
    1918           0 :         // values to track the edges of the float area for this row or column.
    1919           0 :         // https://drafts.csswg.org/css-shapes-1/#valdef-shape-image-threshold-number
    1920           0 :         const uint8_t alpha = aAlphaPixels[index];
    1921             :         if (alpha > threshold) {
    1922           0 :           if (iMin == -1) {
    1923             :             iMin = i;
    1924             :           }
    1925             :           MOZ_ASSERT(iMax < (int32_t)i);
    1926             :           iMax = i;
    1927             :         }
    1928           0 :       }
    1929             : 
    1930             :       // At the end of a row or column; did we find something?
    1931             :       if (iMin != -1) {
    1932           0 :         // We need to supply an offset of the content rect top left, since
    1933           0 :         // our col and row have been calculated from the content rect,
    1934             :         // instead of the margin rect (against which floats are applied).
    1935             :         CreateInterval(iMin, iMax, b, aAppUnitsPerDevPixel,
    1936             :                        aContentRect.TopLeft(), aWM, aContainerSize);
    1937           0 :       }
    1938             :     }
    1939             : 
    1940             :     if (aWM.IsVerticalRL()) {
    1941             :       // vertical-rl or sideways-rl.
    1942           0 :       // Because we scan the columns from left to right, we need to reverse
    1943             :       // the array so that it's sorted (in ascending order) on the block
    1944             :       // direction.
    1945             :       mIntervals.Reverse();
    1946             :     }
    1947             :   } else {
    1948             :     // With a positive aShapeMargin, we have to calculate a distance
    1949             :     // field from the opaque pixels, then build intervals based on
    1950             :     // them being within aShapeMargin distance to an opaque pixel.
    1951             : 
    1952             :     // Roughly: for each pixel in the margin box, we need to determine the
    1953             :     // distance to the nearest opaque image-pixel.  If that distance is less
    1954             :     // than aShapeMargin, we consider this margin-box pixel as being part of
    1955             :     // the float area.
    1956             : 
    1957             :     // Computing the distance field is a two-pass O(n) operation.
    1958             :     // We use a chamfer 5-7-11 5x5 matrix to compute minimum distance
    1959             :     // to an opaque pixel. This integer math computation is reasonably
    1960             :     // close to the true Euclidean distance. The distances will be
    1961             :     // approximately 5x the true distance, quantized in integer units.
    1962           0 :     // The 5x is factored away in the comparison used in the final
    1963             :     // pass which builds the intervals.
    1964             :     dfType usedMargin5X = CalcUsedShapeMargin5X(aShapeMargin,
    1965             :                                                 aAppUnitsPerDevPixel);
    1966             : 
    1967             :     // Allocate our distance field.  The distance field has to cover
    1968             :     // the entire aMarginRect, since aShapeMargin could bleed into it,
    1969           0 :     // beyond the content rect covered by aAlphaPixels. To make this work,
    1970             :     // we calculate a dfOffset value which is the top left of the content
    1971             :     // rect relative to the margin rect.
    1972           0 :     nsPoint offsetPoint = aContentRect.TopLeft() - aMarginRect.TopLeft();
    1973             :     LayoutDeviceIntPoint dfOffset =
    1974             :       LayoutDevicePixel::FromAppUnitsRounded(offsetPoint,
    1975             :                                              aAppUnitsPerDevPixel);
    1976             : 
    1977             :     // Since our distance field is computed with a 5x5 neighborhood,
    1978             :     // we need to expand our distance field by a further 4 pixels in
    1979             :     // both axes, 2 on the leading edge and 2 on the trailing edge.
    1980             :     // We call this edge area the "expanded region".
    1981             : 
    1982             :     // Our expansion amounts need to be the same for our math to work.
    1983             :     static uint32_t kExpansionPerSide = 2;
    1984           0 :     // Since dfOffset will be used in comparisons against expanded region
    1985           0 :     // pixel values, it's convenient to add expansion amounts to dfOffset in
    1986             :     // both axes, to simplify comparison math later.
    1987             :     dfOffset.x += kExpansionPerSide;
    1988             :     dfOffset.y += kExpansionPerSide;
    1989             : 
    1990             :     // In all these calculations, we purposely ignore aStride, because
    1991             :     // we don't have to replicate the packing that we received in
    1992           0 :     // aAlphaPixels. When we need to convert from df coordinates to
    1993           0 :     // alpha coordinates, we do that with math based on row and col.
    1994             :     const LayoutDeviceIntSize marginRectDevPixels =
    1995             :       LayoutDevicePixel::FromAppUnitsRounded(aMarginRect.Size(),
    1996             :                                              aAppUnitsPerDevPixel);
    1997             : 
    1998             :     // Clamp the size of our distance field sizes to prevent multiplication
    1999             :     // overflow.
    2000             :     static const uint32_t DF_SIDE_MAX =
    2001             :       floor(sqrt((double)(std::numeric_limits<int32_t>::max())));
    2002             : 
    2003             :     // Clamp the margin plus 2X the expansion values between expansion + 1
    2004           0 :     // and DF_SIDE_MAX. This ensures that the distance field allocation
    2005           0 :     // doesn't overflow during multiplication, and the reverse iteration
    2006             :     // doesn't underflow.
    2007           0 :     const uint32_t wEx = std::max(std::min(marginRectDevPixels.width +
    2008           0 :                                            (kExpansionPerSide * 2),
    2009             :                                            DF_SIDE_MAX),
    2010             :                                   kExpansionPerSide + 1);
    2011           0 :     const uint32_t hEx = std::max(std::min(marginRectDevPixels.height +
    2012             :                                            (kExpansionPerSide * 2),
    2013             :                                            DF_SIDE_MAX),
    2014             :                                   kExpansionPerSide + 1);
    2015             : 
    2016             :     // Since the margin-box size is CSS controlled, and large values will
    2017           0 :     // generate large wEx and hEx values, we do a falliable allocation for
    2018           0 :     // the distance field. If allocation fails, we early exit and layout will
    2019             :     // be wrong, but we'll avoid aborting from OOM.
    2020           0 :     auto df = MakeUniqueFallible<dfType[]>(wEx * hEx);
    2021             :     if (!df) {
    2022             :       // Without a distance field, we can't reason about the float area.
    2023           0 :       return;
    2024           0 :     }
    2025             : 
    2026             :     const uint32_t bSize = aWM.IsVertical() ? wEx : hEx;
    2027             :     const uint32_t iSize = aWM.IsVertical() ? hEx : wEx;
    2028             : 
    2029             :     // First pass setting distance field, starting at top-left, three cases:
    2030             :     // 1) Expanded region pixel: set to MAX_MARGIN_5X.
    2031             :     // 2) Image pixel with alpha greater than threshold: set to 0.
    2032             :     // 3) Other pixel: set to minimum backward-looking neighborhood
    2033             :     //                 distance value, computed with 5-7-11 chamfer.
    2034             : 
    2035             :     // Scan the pixels in a double loop. For horizontal writing modes, we do
    2036           0 :     // this row by row, from top to bottom. For vertical writing modes, we do
    2037           0 :     // column by column, from left to right. We define the two loops
    2038           0 :     // generically, then figure out the rows and cols within the inner loop.
    2039           0 :     for (uint32_t b = 0; b < bSize; ++b) {
    2040           0 :       for (uint32_t i = 0; i < iSize; ++i) {
    2041           0 :         const uint32_t col = aWM.IsVertical() ? b : i;
    2042             :         const uint32_t row = aWM.IsVertical() ? i : b;
    2043             :         const uint32_t index = col + row * wEx;
    2044             :         MOZ_ASSERT(index < (wEx * hEx),
    2045           0 :                    "Our distance field index should be in-bounds.");
    2046           0 : 
    2047           0 :         // Handle our three cases, in order.
    2048           0 :         if (col < kExpansionPerSide ||
    2049             :             col >= wEx - kExpansionPerSide ||
    2050           0 :             row < kExpansionPerSide ||
    2051           0 :             row >= hEx - kExpansionPerSide) {
    2052           0 :           // Case 1: Expanded pixel.
    2053           0 :           df[index] = MAX_MARGIN_5X;
    2054           0 :         } else if ((int32_t)col >= dfOffset.x &&
    2055           0 :                    (int32_t)col < (dfOffset.x + aImageSize.width) &&
    2056           0 :                    (int32_t)row >= dfOffset.y &&
    2057             :                    (int32_t)row < (dfOffset.y + aImageSize.height) &&
    2058           0 :                    aAlphaPixels[col - dfOffset.x +
    2059           0 :                                 (row - dfOffset.y) * aStride] > threshold) {
    2060           0 :           // Case 2: Image pixel that is opaque.
    2061             :           DebugOnly<uint32_t> alphaIndex = col - dfOffset.x +
    2062             :                                            (row - dfOffset.y) * aStride;
    2063           0 :           MOZ_ASSERT(alphaIndex < (aStride * h),
    2064             :             "Our aAlphaPixels index should be in-bounds.");
    2065             : 
    2066           0 :           df[index] = 0;
    2067             :         } else {
    2068             :           // Case 3: Other pixel.
    2069             :           if (aWM.IsVertical()) {
    2070             :             // Column-by-column, starting at the left, each column
    2071             :             // top-to-bottom.
    2072             :             // Backward-looking neighborhood distance from target pixel X
    2073             :             // with chamfer 5-7-11 looks like:
    2074             :             //
    2075             :             // +--+--+--+
    2076             :             // |  |11|  |   |    +
    2077             :             // +--+--+--+   |   /|
    2078             :             // |11| 7| 5|   |  / |
    2079             :             // +--+--+--+   | /  V
    2080             :             // |  | 5| X|   |/
    2081             :             // +--+--+--+   +
    2082             :             // |11| 7|  |
    2083             :             // +--+--+--+
    2084             :             // |  |11|  |
    2085             :             // +--+--+--+
    2086             :             //
    2087           0 :             // X should be set to the minimum of MAX_MARGIN_5X and the
    2088             :             // values of all of the numbered neighbors summed with the
    2089             :             // value in that chamfer cell.
    2090             :             MOZ_ASSERT(index - wEx - 2 < (iSize * bSize) &&
    2091             :                        index + wEx - 2 < (iSize * bSize) &&
    2092             :                        index - (wEx * 2) - 1 < (iSize * bSize),
    2093           0 :                        "Our distance field most extreme indices should be "
    2094           0 :                        "in-bounds.");
    2095           0 : 
    2096           0 :             df[index] = std::min<dfType>(MAX_MARGIN_5X,
    2097           0 :                         std::min<dfType>(df[index - wEx - 2] + 11,
    2098           0 :                         std::min<dfType>(df[index + wEx - 2] + 11,
    2099           0 :                         std::min<dfType>(df[index - (wEx * 2) - 1] + 11,
    2100           0 :                         std::min<dfType>(df[index - wEx - 1] + 7,
    2101           0 :                         std::min<dfType>(df[index - 1] + 5,
    2102             :                         std::min<dfType>(df[index + wEx - 1] + 7,
    2103             :                         std::min<dfType>(df[index + (wEx * 2) - 1] + 11,
    2104             :                                          df[index - wEx] + 5))))))));
    2105             :           } else {
    2106             :             // Row-by-row, starting at the top, each row left-to-right.
    2107             :             // Backward-looking neighborhood distance from target pixel X
    2108             :             // with chamfer 5-7-11 looks like:
    2109             :             //
    2110             :             // +--+--+--+--+--+
    2111             :             // |  |11|  |11|  |   ----+
    2112             :             // +--+--+--+--+--+      /
    2113             :             // |11| 7| 5| 7|11|     /
    2114             :             // +--+--+--+--+--+    /
    2115             :             // |  | 5| X|  |  |   +-->
    2116             :             // +--+--+--+--+--+
    2117             :             //
    2118           0 :             // X should be set to the minimum of MAX_MARGIN_5X and the
    2119             :             // values of all of the numbered neighbors summed with the
    2120             :             // value in that chamfer cell.
    2121             :             MOZ_ASSERT(index - (wEx * 2) - 1 < (iSize * bSize) &&
    2122             :                        index - wEx - 2 < (iSize * bSize),
    2123           0 :                        "Our distance field most extreme indices should be "
    2124           0 :                        "in-bounds.");
    2125           0 : 
    2126           0 :             df[index] = std::min<dfType>(MAX_MARGIN_5X,
    2127           0 :                         std::min<dfType>(df[index - (wEx * 2) - 1] + 11,
    2128           0 :                         std::min<dfType>(df[index - (wEx * 2) + 1] + 11,
    2129           0 :                         std::min<dfType>(df[index - wEx - 2] + 11,
    2130           0 :                         std::min<dfType>(df[index - wEx - 1] + 7,
    2131           0 :                         std::min<dfType>(df[index - wEx] + 5,
    2132             :                         std::min<dfType>(df[index - wEx + 1] + 7,
    2133             :                         std::min<dfType>(df[index - wEx + 2] + 11,
    2134             :                                          df[index - 1] + 5))))))));
    2135             :           }
    2136             :         }
    2137             :       }
    2138             :     }
    2139             : 
    2140             :     // Okay, time for the second pass. This pass is in reverse order from
    2141             :     // the first pass. All of our opaque pixels have been set to 0, and all
    2142             :     // of our expanded region pixels have been set to MAX_MARGIN_5X. Other
    2143             :     // pixels have been set to some value between those two (inclusive) but
    2144             :     // this hasn't yet taken into account the neighbors that were processed
    2145             :     // after them in the first pass. This time we reverse iterate so we can
    2146             :     // apply the forward-looking chamfer.
    2147             : 
    2148             :     // This time, we constrain our outer and inner loop to ignore the
    2149             :     // expanded region pixels. For each pixel we iterate, we set the df value
    2150             :     // to the minimum forward-looking neighborhood distance value, computed
    2151             :     // with a 5-7-11 chamfer. We also check each df value against the
    2152             :     // usedMargin5X threshold, and use that to set the iMin and iMax values
    2153             :     // for the interval we'll create for that block axis value (b).
    2154             : 
    2155             :     // At the end of each row (or column in vertical writing modes),
    2156           0 :     // if any of the other pixels had a value less than usedMargin5X,
    2157           0 :     // we create an interval. Note: "bSize - kExpansionPerSide - 1" is the
    2158             :     // index of the final row of pixels before the trailing expanded region.
    2159             :     for (uint32_t b = bSize - kExpansionPerSide - 1;
    2160             :          b >= kExpansionPerSide; --b) {
    2161           0 :       // iMin tracks the first df pixel and iMax the last df pixel whose
    2162           0 :       // df[] value is less than usedMargin5X. Set iMin and iMax in
    2163             :       // preparation for this row or column.
    2164             :       int32_t iMin = iSize;
    2165             :       int32_t iMax = -1;
    2166           0 : 
    2167           0 :       // Note: "iSize - kExpansionPerSide - 1" is the index of the final row
    2168           0 :       // of pixels before the trailing expanded region.
    2169           0 :       for (uint32_t i = iSize - kExpansionPerSide - 1;
    2170           0 :            i >= kExpansionPerSide; --i) {
    2171           0 :         const uint32_t col = aWM.IsVertical() ? b : i;
    2172             :         const uint32_t row = aWM.IsVertical() ? i : b;
    2173             :         const uint32_t index = col + row * wEx;
    2174             :         MOZ_ASSERT(index < (wEx * hEx),
    2175             :                    "Our distance field index should be in-bounds.");
    2176           0 : 
    2177           0 :         // Only apply the chamfer calculation if the df value is not
    2178             :         // already 0, since the chamfer can only reduce the value.
    2179             :         if (df[index]) {
    2180             :           if (aWM.IsVertical()) {
    2181             :             // Column-by-column, starting at the right, each column
    2182             :             // bottom-to-top.
    2183             :             // Forward-looking neighborhood distance from target pixel X
    2184             :             // with chamfer 5-7-11 looks like:
    2185             :             //
    2186             :             // +--+--+--+
    2187             :             // |  |11|  |        +
    2188             :             // +--+--+--+       /|
    2189             :             // |  | 7|11|   A  / |
    2190             :             // +--+--+--+   | /  |
    2191             :             // | X| 5|  |   |/   |
    2192             :             // +--+--+--+   +    |
    2193             :             // | 5| 7|11|
    2194             :             // +--+--+--+
    2195             :             // |  |11|  |
    2196             :             // +--+--+--+
    2197             :             //
    2198           0 :             // X should be set to the minimum of its current value and
    2199             :             // the values of all of the numbered neighbors summed with
    2200             :             // the value in that chamfer cell.
    2201             :             MOZ_ASSERT(index + wEx + 2 < (wEx * hEx) &&
    2202             :                        index + (wEx * 2) + 1 < (wEx * hEx) &&
    2203             :                        index - (wEx * 2) + 1 < (wEx * hEx),
    2204           0 :                        "Our distance field most extreme indices should be "
    2205           0 :                        "in-bounds.");
    2206           0 : 
    2207           0 :             df[index] = std::min<dfType>(df[index],
    2208           0 :                         std::min<dfType>(df[index + wEx + 2] + 11,
    2209           0 :                         std::min<dfType>(df[index - wEx + 2] + 11,
    2210           0 :                         std::min<dfType>(df[index + (wEx * 2) + 1] + 11,
    2211           0 :                         std::min<dfType>(df[index + wEx + 1] + 7,
    2212           0 :                         std::min<dfType>(df[index + 1] + 5,
    2213             :                         std::min<dfType>(df[index - wEx + 1] + 7,
    2214             :                         std::min<dfType>(df[index - (wEx * 2) + 1] + 11,
    2215             :                                          df[index + wEx] + 5))))))));
    2216             :           } else {
    2217             :             // Row-by-row, starting at the bottom, each row right-to-left.
    2218             :             // Forward-looking neighborhood distance from target pixel X
    2219             :             // with chamfer 5-7-11 looks like:
    2220             :             //
    2221             :             // +--+--+--+--+--+
    2222             :             // |  |  | X| 5|  |    <--+
    2223             :             // +--+--+--+--+--+      /
    2224             :             // |11| 7| 5| 7|11|     /
    2225             :             // +--+--+--+--+--+    /
    2226             :             // |  |11|  |11|  |   +----
    2227             :             // +--+--+--+--+--+
    2228             :             //
    2229           0 :             // X should be set to the minimum of its current value and
    2230             :             // the values of all of the numbered neighbors summed with
    2231             :             // the value in that chamfer cell.
    2232             :             MOZ_ASSERT(index + (wEx * 2) + 1 < (wEx * hEx) &&
    2233             :                        index + wEx + 2 < (wEx * hEx),
    2234           0 :                        "Our distance field most extreme indices should be "
    2235           0 :                        "in-bounds.");
    2236           0 : 
    2237           0 :             df[index] = std::min<dfType>(df[index],
    2238           0 :                         std::min<dfType>(df[index + (wEx * 2) + 1] + 11,
    2239           0 :                         std::min<dfType>(df[index + (wEx * 2) - 1] + 11,
    2240           0 :                         std::min<dfType>(df[index + wEx + 2] + 11,
    2241           0 :                         std::min<dfType>(df[index + wEx + 1] + 7,
    2242           0 :                         std::min<dfType>(df[index + wEx] + 5,
    2243             :                         std::min<dfType>(df[index + wEx - 1] + 7,
    2244             :                         std::min<dfType>(df[index + wEx - 2] + 11,
    2245             :                                          df[index + 1] + 5))))))));
    2246             :           }
    2247             :         }
    2248           0 : 
    2249           0 :         // Finally, we can check the df value and see if it's less than
    2250           0 :         // or equal to the usedMargin5X value.
    2251             :         if (df[index] <= usedMargin5X) {
    2252           0 :           if (iMax == -1) {
    2253             :             iMax = i;
    2254             :           }
    2255             :           MOZ_ASSERT(iMin > (int32_t)i);
    2256             :           iMin = i;
    2257           0 :         }
    2258             :       }
    2259             : 
    2260             :       if (iMax != -1) {
    2261             :         // Our interval values, iMin, iMax, and b are all calculated from
    2262             :         // the expanded region, which is based on the margin rect. To create
    2263             :         // our interval, we have to subtract kExpansionPerSide from (iMin,
    2264             :         // iMax, and b) to account for the expanded region edges. This
    2265           0 :         // produces coords that are relative to our margin-rect, so we pass
    2266           0 :         // in aMarginRect.TopLeft() to make CreateInterval convert to our
    2267           0 :         // container's coordinate space.
    2268             :         CreateInterval(iMin - kExpansionPerSide, iMax - kExpansionPerSide,
    2269             :                        b - kExpansionPerSide, aAppUnitsPerDevPixel,
    2270             :                        aMarginRect.TopLeft(), aWM, aContainerSize);
    2271           0 :       }
    2272             :     }
    2273             : 
    2274             :     if (!aWM.IsVerticalRL()) {
    2275             :       // Anything other than vertical-rl or sideways-rl.
    2276           0 :       // Because we assembled our intervals on the bottom-up pass,
    2277             :       // they are reversed for most writing modes. Reverse them to
    2278             :       // keep the array sorted on the block direction.
    2279             :       mIntervals.Reverse();
    2280           0 :     }
    2281           0 :   }
    2282           0 : 
    2283             :   if (!mIntervals.IsEmpty()) {
    2284             :     mBStart = mIntervals[0].Y();
    2285             :     mBEnd = mIntervals.LastElement().YMost();
    2286             :   }
    2287           0 : }
    2288             : 
    2289             : void
    2290             : nsFloatManager::ImageShapeInfo::CreateInterval(
    2291             :   int32_t aIMin,
    2292             :   int32_t aIMax,
    2293             :   int32_t aB,
    2294             :   int32_t aAppUnitsPerDevPixel,
    2295             :   const nsPoint& aOffsetFromContainer,
    2296             :   WritingMode aWM,
    2297             :   const nsSize& aContainerSize)
    2298             : {
    2299             :   // Store an interval as an nsRect with our inline axis values stored in x
    2300             :   // and our block axis values stored in y. The position is dependent on
    2301             :   // the writing mode, but the size is the same for all writing modes.
    2302             : 
    2303           0 :   // Size is the difference in inline axis edges stored as x, and one
    2304           0 :   // block axis pixel stored as y. For the inline axis, we add 1 to aIMax
    2305             :   // because we want to capture the far edge of the last pixel.
    2306             :   nsSize size(((aIMax + 1) - aIMin) * aAppUnitsPerDevPixel,
    2307             :   aAppUnitsPerDevPixel);
    2308             : 
    2309             :   // Since we started our scanning of the image pixels from the top left,
    2310           0 :   // the interval position starts from the origin of the content rect,
    2311             :   // converted to logical coordinates.
    2312             :   nsPoint origin = ConvertToFloatLogical(aOffsetFromContainer, aWM,
    2313           0 :                                          aContainerSize);
    2314             : 
    2315             :   // Depending on the writing mode, we now move the origin.
    2316             :   if (aWM.IsVerticalRL()) {
    2317             :     // vertical-rl or sideways-rl.
    2318             :     // These writing modes proceed from the top right, and each interval
    2319           0 :     // moves in a positive inline direction and negative block direction.
    2320           0 :     // That means that the intervals will be reversed after all have been
    2321             :     // constructed. We add 1 to aB to capture the end of the block axis pixel.
    2322             :     origin.MoveBy(aIMin * aAppUnitsPerDevPixel, (aB + 1) * -aAppUnitsPerDevPixel);
    2323             :   } else if (aWM.IsVerticalLR() && !aWM.IsLineInverted()) {
    2324             :     // sideways-lr.
    2325             :     // Checking IsLineInverted is the only reliable way to distinguish
    2326             :     // vertical-lr from sideways-lr. IsSideways and IsInlineReversed are both
    2327             :     // affected by bidi and text-direction, and so complicate detection.
    2328           0 :     // These writing modes proceed from the bottom left, and each interval
    2329             :     // moves in a negative inline direction and a positive block direction.
    2330             :     // We add 1 to aIMax to capture the end of the inline axis pixel.
    2331             :     origin.MoveBy((aIMax + 1) * -aAppUnitsPerDevPixel, aB * aAppUnitsPerDevPixel);
    2332             :   } else {
    2333           0 :     // horizontal-tb or vertical-lr.
    2334             :     // These writing modes proceed from the top left and each interval
    2335             :     // moves in a positive step in both inline and block directions.
    2336           0 :     origin.MoveBy(aIMin * aAppUnitsPerDevPixel, aB * aAppUnitsPerDevPixel);
    2337           0 :   }
    2338             : 
    2339             :   mIntervals.AppendElement(nsRect(origin, size));
    2340           0 : }
    2341             : 
    2342             : nscoord
    2343           0 : nsFloatManager::ImageShapeInfo::LineLeft(const nscoord aBStart,
    2344             :                                          const nscoord aBEnd) const
    2345             : {
    2346             :   return LineEdge(mIntervals, aBStart, aBEnd, true);
    2347           0 : }
    2348             : 
    2349             : nscoord
    2350           0 : nsFloatManager::ImageShapeInfo::LineRight(const nscoord aBStart,
    2351             :                                           const nscoord aBEnd) const
    2352             : {
    2353             :   return LineEdge(mIntervals, aBStart, aBEnd, false);
    2354           0 : }
    2355             : 
    2356             : void
    2357           0 : nsFloatManager::ImageShapeInfo::Translate(nscoord aLineLeft,
    2358           0 :                                           nscoord aBlockStart)
    2359             : {
    2360             :   for (nsRect& interval : mIntervals) {
    2361           0 :     interval.MoveBy(aLineLeft, aBlockStart);
    2362           0 :   }
    2363           0 : 
    2364             :   mBStart += aBlockStart;
    2365             :   mBEnd += aBlockStart;
    2366             : }
    2367             : 
    2368           0 : /////////////////////////////////////////////////////////////////////////////
    2369             : // FloatInfo
    2370             : 
    2371             : nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame,
    2372           0 :                                      nscoord aLineLeft, nscoord aBlockStart,
    2373             :                                      const LogicalRect& aMarginRect,
    2374           0 :                                      WritingMode aWM,
    2375           0 :                                      const nsSize& aContainerSize)
    2376             :   : mFrame(aFrame)
    2377           0 :   , mRect(ShapeInfo::ConvertToFloatLogical(aMarginRect, aWM, aContainerSize) +
    2378             :           nsPoint(aLineLeft, aBlockStart))
    2379           0 : {
    2380             :   MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
    2381             : 
    2382             :   if (IsEmpty()) {
    2383             :     // Per spec, a float area defined by a shape is clipped to the float’s
    2384             :     // margin box. Therefore, no need to create a shape info if the float's
    2385             :     // margin box is empty, since a float area can only be smaller than the
    2386             :     // margin box.
    2387             : 
    2388             :     // https://drafts.csswg.org/css-shapes/#relation-to-box-model-and-float-behavior
    2389           0 :     return;
    2390             :   }
    2391           0 : 
    2392           0 :   const nsStyleDisplay* styleDisplay = mFrame->StyleDisplay();
    2393           0 :   const StyleShapeSource& shapeOutside = styleDisplay->mShapeOutside;
    2394           0 : 
    2395           0 :   nscoord shapeMargin = (shapeOutside.GetType() == StyleShapeSourceType::None)
    2396             :    ? 0
    2397           0 :    : nsLayoutUtils::ResolveToLength<true>(
    2398             :        styleDisplay->mShapeMargin,
    2399             :        LogicalSize(aWM, aContainerSize).ISize(aWM));
    2400             : 
    2401             :   switch (shapeOutside.GetType()) {
    2402             :     case StyleShapeSourceType::None:
    2403           0 :       // No need to create shape info.
    2404             :       return;
    2405             : 
    2406             :     case StyleShapeSourceType::URL:
    2407           0 :       MOZ_ASSERT_UNREACHABLE("shape-outside doesn't have URL source type!");
    2408           0 :       return;
    2409             : 
    2410             :     case StyleShapeSourceType::Image: {
    2411           0 :       float shapeImageThreshold = styleDisplay->mShapeImageThreshold;
    2412             :       mShapeInfo = ShapeInfo::CreateImageShape(shapeOutside.GetShapeImage(),
    2413             :                                                shapeImageThreshold,
    2414           0 :                                                shapeMargin,
    2415           0 :                                                mFrame,
    2416             :                                                aMarginRect,
    2417             :                                                aWM,
    2418             :                                                aContainerSize);
    2419             :       if (!mShapeInfo) {
    2420             :         // Image is not ready, or fails to load, etc.
    2421             :         return;
    2422             :       }
    2423             : 
    2424             :       break;
    2425             :     }
    2426           0 : 
    2427           0 :     case StyleShapeSourceType::Box: {
    2428             :       // Initialize <shape-box>'s reference rect.
    2429           0 :       LogicalRect shapeBoxRect =
    2430             :         ShapeInfo::ComputeShapeBoxRect(shapeOutside, mFrame, aMarginRect, aWM);
    2431             :       mShapeInfo = ShapeInfo::CreateShapeBox(mFrame, shapeMargin,
    2432             :                                              shapeBoxRect, aWM,
    2433             :                                              aContainerSize);
    2434           0 :       break;
    2435             :     }
    2436             : 
    2437           0 :     case StyleShapeSourceType::Shape: {
    2438           0 :       const UniquePtr<StyleBasicShape>& basicShape = shapeOutside.GetBasicShape();
    2439             :       // Initialize <shape-box>'s reference rect.
    2440           0 :       LogicalRect shapeBoxRect =
    2441             :         ShapeInfo::ComputeShapeBoxRect(shapeOutside, mFrame, aMarginRect, aWM);
    2442             :       mShapeInfo = ShapeInfo::CreateBasicShape(basicShape, shapeMargin, mFrame,
    2443             :                                                shapeBoxRect, aMarginRect, aWM,
    2444             :                                                aContainerSize);
    2445           0 :       break;
    2446             :     }
    2447             :   }
    2448             : 
    2449           0 :   MOZ_ASSERT(mShapeInfo,
    2450             :              "All shape-outside values except none should have mShapeInfo!");
    2451             : 
    2452             :   // Translate the shape to the same origin as nsFloatManager.
    2453           0 :   mShapeInfo->Translate(aLineLeft, aBlockStart);
    2454           0 : }
    2455           0 : 
    2456           0 : #ifdef NS_BUILD_REFCNT_LOGGING
    2457           0 : nsFloatManager::FloatInfo::FloatInfo(FloatInfo&& aOther)
    2458           0 :   : mFrame(std::move(aOther.mFrame))
    2459             :   , mLeftBEnd(std::move(aOther.mLeftBEnd))
    2460           0 :   , mRightBEnd(std::move(aOther.mRightBEnd))
    2461           0 :   , mRect(std::move(aOther.mRect))
    2462             :   , mShapeInfo(std::move(aOther.mShapeInfo))
    2463           0 : {
    2464             :   MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
    2465           0 : }
    2466           0 : 
    2467             : nsFloatManager::FloatInfo::~FloatInfo()
    2468             : {
    2469             :   MOZ_COUNT_DTOR(nsFloatManager::FloatInfo);
    2470           0 : }
    2471             : #endif
    2472             : 
    2473             : nscoord
    2474           0 : nsFloatManager::FloatInfo::LineLeft(ShapeType aShapeType,
    2475           0 :                                     const nscoord aBStart,
    2476             :                                     const nscoord aBEnd) const
    2477             : {
    2478           0 :   if (aShapeType == ShapeType::Margin) {
    2479           0 :     return LineLeft();
    2480           0 :   }
    2481             : 
    2482             :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
    2483             :   if (!mShapeInfo) {
    2484             :     return LineLeft();
    2485             :   }
    2486           0 :   // Clip the flow area to the margin-box because
    2487             :   // https://drafts.csswg.org/css-shapes-1/#relation-to-box-model-and-float-behavior
    2488             :   // says "When a shape is used to define a float area, the shape is clipped
    2489             :   // to the float’s margin box."
    2490           0 :   return std::max(LineLeft(), mShapeInfo->LineLeft(aBStart, aBEnd));
    2491             : }
    2492             : 
    2493             : nscoord
    2494           0 : nsFloatManager::FloatInfo::LineRight(ShapeType aShapeType,
    2495           0 :                                      const nscoord aBStart,
    2496             :                                      const nscoord aBEnd) const
    2497             : {
    2498           0 :   if (aShapeType == ShapeType::Margin) {
    2499           0 :     return LineRight();
    2500           0 :   }
    2501             : 
    2502             :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
    2503           0 :   if (!mShapeInfo) {
    2504             :     return LineRight();
    2505             :   }
    2506             :   // Clip the flow area to the margin-box. See LineLeft().
    2507           0 :   return std::min(LineRight(), mShapeInfo->LineRight(aBStart, aBEnd));
    2508             : }
    2509           0 : 
    2510           0 : nscoord
    2511             : nsFloatManager::FloatInfo::BStart(ShapeType aShapeType) const
    2512             : {
    2513           0 :   if (aShapeType == ShapeType::Margin) {
    2514           0 :     return BStart();
    2515           0 :   }
    2516             : 
    2517             :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
    2518           0 :   if (!mShapeInfo) {
    2519             :     return BStart();
    2520             :   }
    2521             :   // Clip the flow area to the margin-box. See LineLeft().
    2522           0 :   return std::max(BStart(), mShapeInfo->BStart());
    2523             : }
    2524           0 : 
    2525           0 : nscoord
    2526             : nsFloatManager::FloatInfo::BEnd(ShapeType aShapeType) const
    2527             : {
    2528           0 :   if (aShapeType == ShapeType::Margin) {
    2529           0 :     return BEnd();
    2530           0 :   }
    2531             : 
    2532             :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
    2533           0 :   if (!mShapeInfo) {
    2534             :     return BEnd();
    2535             :   }
    2536             :   // Clip the flow area to the margin-box. See LineLeft().
    2537           0 :   return std::min(BEnd(), mShapeInfo->BEnd());
    2538             : }
    2539           0 : 
    2540           0 : bool
    2541             : nsFloatManager::FloatInfo::IsEmpty(ShapeType aShapeType) const
    2542             : {
    2543           0 :   if (aShapeType == ShapeType::Margin) {
    2544           0 :     return IsEmpty();
    2545           0 :   }
    2546             : 
    2547           0 :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
    2548             :   if (!mShapeInfo) {
    2549             :     return IsEmpty();
    2550             :   }
    2551           0 :   return mShapeInfo->IsEmpty();
    2552             : }
    2553             : 
    2554             : bool
    2555             : nsFloatManager::FloatInfo::MayNarrowInBlockDirection(ShapeType aShapeType) const
    2556             : {
    2557             :   // This function mirrors the cases of the three argument versions of
    2558           0 :   // LineLeft() and LineRight(). This function returns true if and only if
    2559             :   // either of those functions could possibly return "narrower" values with
    2560             :   // increasing aBStart values. "Narrower" means closer to the far end of
    2561             :   // the float shape.
    2562           0 :   if (aShapeType == ShapeType::Margin) {
    2563           0 :     return false;
    2564             :   }
    2565             : 
    2566             :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
    2567           0 :   if (!mShapeInfo) {
    2568             :     return false;
    2569             :   }
    2570             : 
    2571             :   return mShapeInfo->MayNarrowInBlockDirection();
    2572             : }
    2573             : 
    2574           0 : /////////////////////////////////////////////////////////////////////////////
    2575             : // ShapeInfo
    2576             : 
    2577             : /* static */ LogicalRect
    2578             : nsFloatManager::ShapeInfo::ComputeShapeBoxRect(
    2579             :   const StyleShapeSource& aShapeOutside,
    2580           0 :   nsIFrame* const aFrame,
    2581             :   const LogicalRect& aMarginRect,
    2582           0 :   WritingMode aWM)
    2583             : {
    2584           0 :   LogicalRect rect = aMarginRect;
    2585             : 
    2586             :   switch (aShapeOutside.GetReferenceBox()) {
    2587           0 :     case StyleGeometryBox::ContentBox:
    2588             :       rect.Deflate(aWM, aFrame->GetLogicalUsedPadding(aWM));
    2589             :       MOZ_FALLTHROUGH;
    2590           0 :     case StyleGeometryBox::PaddingBox:
    2591           0 :       rect.Deflate(aWM, aFrame->GetLogicalUsedBorder(aWM));
    2592             :       MOZ_FALLTHROUGH;
    2593             :     case StyleGeometryBox::BorderBox:
    2594             :       rect.Deflate(aWM, aFrame->GetLogicalUsedMargin(aWM));
    2595             :       break;
    2596             :     case StyleGeometryBox::MarginBox:
    2597           0 :       // Do nothing. rect is already a margin rect.
    2598             :       break;
    2599             :     case StyleGeometryBox::NoBox:
    2600             :     default:
    2601             :       MOZ_ASSERT(aShapeOutside.GetType() != StyleShapeSourceType::Box,
    2602           0 :                  "Box source type must have <shape-box> specified!");
    2603             :       break;
    2604             :   }
    2605             : 
    2606           0 :   return rect;
    2607             : }
    2608             : 
    2609             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
    2610             : nsFloatManager::ShapeInfo::CreateShapeBox(
    2611             :   nsIFrame* const aFrame,
    2612             :   nscoord aShapeMargin,
    2613             :   const LogicalRect& aShapeBoxRect,
    2614           0 :   WritingMode aWM,
    2615             :   const nsSize& aContainerSize)
    2616             : {
    2617           0 :   nsRect logicalShapeBoxRect
    2618             :     = ConvertToFloatLogical(aShapeBoxRect, aWM, aContainerSize);
    2619             : 
    2620           0 :   // Inflate logicalShapeBoxRect by aShapeMargin.
    2621           0 :   logicalShapeBoxRect.Inflate(aShapeMargin);
    2622           0 : 
    2623           0 :   nscoord physicalRadii[8];
    2624             :   bool hasRadii = aFrame->GetShapeBoxBorderRadii(physicalRadii);
    2625             :   if (!hasRadii) {
    2626             :     return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
    2627           0 :                                            UniquePtr<nscoord[]>());
    2628           0 :   }
    2629             : 
    2630             :   // Add aShapeMargin to each of the radii.
    2631           0 :   for (nscoord& r : physicalRadii) {
    2632           0 :     r += aShapeMargin;
    2633           0 :   }
    2634             : 
    2635             :   return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
    2636             :                                          ConvertToFloatLogical(physicalRadii,
    2637           0 :                                                                aWM));
    2638             : }
    2639             : 
    2640             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
    2641             : nsFloatManager::ShapeInfo::CreateBasicShape(
    2642             :   const UniquePtr<StyleBasicShape>& aBasicShape,
    2643             :   nscoord aShapeMargin,
    2644             :   nsIFrame* const aFrame,
    2645             :   const LogicalRect& aShapeBoxRect,
    2646           0 :   const LogicalRect& aMarginRect,
    2647             :   WritingMode aWM,
    2648             :   const nsSize& aContainerSize)
    2649           0 : {
    2650             :   switch (aBasicShape->GetShapeType()) {
    2651             :     case StyleBasicShapeType::Polygon:
    2652             :       return CreatePolygon(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect,
    2653             :                            aMarginRect, aWM, aContainerSize);
    2654           0 :     case StyleBasicShapeType::Circle:
    2655             :     case StyleBasicShapeType::Ellipse:
    2656             :       return CreateCircleOrEllipse(aBasicShape, aShapeMargin, aFrame,
    2657           0 :                                    aShapeBoxRect, aWM,
    2658             :                                    aContainerSize);
    2659             :     case StyleBasicShapeType::Inset:
    2660             :       return CreateInset(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect,
    2661             :                          aWM, aContainerSize);
    2662             :   }
    2663           0 :   return nullptr;
    2664             : }
    2665             : 
    2666             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
    2667             : nsFloatManager::ShapeInfo::CreateInset(
    2668             :   const UniquePtr<StyleBasicShape>& aBasicShape,
    2669             :   nscoord aShapeMargin,
    2670             :   nsIFrame* aFrame,
    2671             :   const LogicalRect& aShapeBoxRect,
    2672             :   WritingMode aWM,
    2673             :   const nsSize& aContainerSize)
    2674             : {
    2675           0 :   // Use physical coordinates to compute inset() because the top, right,
    2676             :   // bottom and left offsets are physical.
    2677           0 :   // https://drafts.csswg.org/css-shapes-1/#funcdef-inset
    2678             :   nsRect physicalShapeBoxRect =
    2679             :     aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
    2680           0 :   nsRect insetRect =
    2681           0 :     ShapeUtils::ComputeInsetRect(aBasicShape, physicalShapeBoxRect);
    2682             : 
    2683             :   nsRect logicalInsetRect =
    2684             :     ConvertToFloatLogical(LogicalRect(aWM, insetRect, aContainerSize),
    2685           0 :                           aWM, aContainerSize);
    2686             :   nscoord physicalRadii[8];
    2687             :   bool hasRadii =
    2688           0 :     ShapeUtils::ComputeInsetRadii(aBasicShape, insetRect, physicalShapeBoxRect,
    2689           0 :                                   physicalRadii);
    2690           0 : 
    2691           0 :   // With a zero shape-margin, we will be able to use the fast constructor.
    2692             :   if (aShapeMargin == 0) {
    2693           0 :     if (!hasRadii) {
    2694           0 :       return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
    2695           0 :                                              UniquePtr<nscoord[]>());
    2696             :     }
    2697             :     return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
    2698             :                                            ConvertToFloatLogical(physicalRadii,
    2699             :                                                                  aWM));
    2700             :   }
    2701             : 
    2702           0 :   // With a positive shape-margin, we might still be able to use the fast
    2703           0 :   // constructor. With no radii, we can build a rounded box by inflating
    2704           0 :   // logicalInsetRect, and supplying aShapeMargin as the radius for all
    2705           0 :   // corners.
    2706           0 :   if (!hasRadii) {
    2707             :     logicalInsetRect.Inflate(aShapeMargin);
    2708           0 :     auto logicalRadii = MakeUnique<nscoord[]>(8);
    2709           0 :     for (int32_t i = 0; i < 8; ++i) {
    2710             :       logicalRadii[i] = aShapeMargin;
    2711             :     }
    2712             :     return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
    2713             :                                            std::move(logicalRadii));
    2714           0 :   }
    2715           0 : 
    2716           0 :   // If we have radii, and they have balanced/equal corners, we can inflate
    2717           0 :   // both logicalInsetRect and all the radii and use the fast constructor.
    2718             :   if (RoundedBoxShapeInfo::EachCornerHasBalancedRadii(physicalRadii)) {
    2719           0 :     logicalInsetRect.Inflate(aShapeMargin);
    2720           0 :     for (nscoord& r : physicalRadii) {
    2721           0 :       r += aShapeMargin;
    2722             :     }
    2723             :     return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
    2724             :                                            ConvertToFloatLogical(physicalRadii,
    2725             :                                                                  aWM));
    2726           0 :   }
    2727           0 : 
    2728           0 :   // With positive shape-margin and elliptical radii, we have to use the
    2729           0 :   // slow constructor.
    2730             :   nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
    2731           0 :   int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
    2732             :   return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
    2733             :                                          ConvertToFloatLogical(physicalRadii,
    2734             :                                                                aWM),
    2735           0 :                                          aShapeMargin, appUnitsPerDevPixel);
    2736             : }
    2737             : 
    2738             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
    2739             : nsFloatManager::ShapeInfo::CreateCircleOrEllipse(
    2740             :   const UniquePtr<StyleBasicShape>& aBasicShape,
    2741             :   nscoord aShapeMargin,
    2742             :   nsIFrame* const aFrame,
    2743             :   const LogicalRect& aShapeBoxRect,
    2744             :   WritingMode aWM,
    2745             :   const nsSize& aContainerSize)
    2746             : {
    2747           0 :   // Use physical coordinates to compute the center of circle() or ellipse()
    2748             :   // since the <position> keywords such as 'left', 'top', etc. are physical.
    2749           0 :   // https://drafts.csswg.org/css-shapes-1/#funcdef-ellipse
    2750             :   nsRect physicalShapeBoxRect =
    2751           0 :     aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
    2752             :   nsPoint physicalCenter =
    2753             :     ShapeUtils::ComputeCircleOrEllipseCenter(aBasicShape, physicalShapeBoxRect);
    2754           0 :   nsPoint logicalCenter =
    2755           0 :     ConvertToFloatLogical(physicalCenter, aWM, aContainerSize);
    2756           0 : 
    2757             :   // Compute the circle or ellipse radii.
    2758           0 :   nsSize radii;
    2759             :   StyleBasicShapeType type = aBasicShape->GetShapeType();
    2760             :   if (type == StyleBasicShapeType::Circle) {
    2761           0 :     nscoord radius = ShapeUtils::ComputeCircleRadius(aBasicShape, physicalCenter,
    2762           0 :                                                      physicalShapeBoxRect);
    2763             :     // Circles can use the three argument, math constructor for
    2764             :     // EllipseShapeInfo.
    2765           0 :     radii = nsSize(radius, radius);
    2766             :     return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin);
    2767             :   }
    2768           0 : 
    2769           0 :   MOZ_ASSERT(type == StyleBasicShapeType::Ellipse);
    2770           0 :   nsSize physicalRadii =
    2771             :     ShapeUtils::ComputeEllipseRadii(aBasicShape, physicalCenter,
    2772             :                                     physicalShapeBoxRect);
    2773             :   LogicalSize logicalRadii(aWM, physicalRadii);
    2774             :   radii = nsSize(logicalRadii.ISize(aWM), logicalRadii.BSize(aWM));
    2775             : 
    2776           0 :   // If radii are close to the same value, or if aShapeMargin is small
    2777           0 :   // enough (as specified in css pixels), then we can use the three argument
    2778           0 :   // constructor for EllipseShapeInfo, which uses math for a more efficient
    2779             :   // method of float area computation.
    2780             :   if (EllipseShapeInfo::ShapeMarginIsNegligible(aShapeMargin) ||
    2781             :       EllipseShapeInfo::RadiiAreRoughlyEqual(radii)) {
    2782             :     return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin);
    2783           0 :   }
    2784           0 : 
    2785           0 :   // We have to use the full constructor for EllipseShapeInfo. This
    2786           0 :   // computes the float area using a rasterization method.
    2787             :   nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
    2788             :   int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
    2789             :   return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin,
    2790           0 :                                       appUnitsPerDevPixel);
    2791             : }
    2792             : 
    2793             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
    2794             : nsFloatManager::ShapeInfo::CreatePolygon(
    2795             :   const UniquePtr<StyleBasicShape>& aBasicShape,
    2796             :   nscoord aShapeMargin,
    2797             :   nsIFrame* const aFrame,
    2798             :   const LogicalRect& aShapeBoxRect,
    2799             :   const LogicalRect& aMarginRect,
    2800             :   WritingMode aWM,
    2801             :   const nsSize& aContainerSize)
    2802             : {
    2803           0 :   // Use physical coordinates to compute each (xi, yi) vertex because CSS
    2804             :   // represents them using physical coordinates.
    2805             :   // https://drafts.csswg.org/css-shapes-1/#funcdef-polygon
    2806             :   nsRect physicalShapeBoxRect =
    2807           0 :     aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
    2808             : 
    2809             :   // Get physical vertices.
    2810           0 :   nsTArray<nsPoint> vertices =
    2811           0 :     ShapeUtils::ComputePolygonVertices(aBasicShape, physicalShapeBoxRect);
    2812             : 
    2813             :   // Convert all the physical vertices to logical.
    2814           0 :   for (nsPoint& vertex : vertices) {
    2815           0 :     vertex = ConvertToFloatLogical(vertex, aWM, aContainerSize);
    2816             :   }
    2817             : 
    2818           0 :   if (aShapeMargin == 0) {
    2819             :     return MakeUnique<PolygonShapeInfo>(std::move(vertices));
    2820             :   }
    2821             : 
    2822           0 :   nsRect marginRect = ConvertToFloatLogical(aMarginRect, aWM, aContainerSize);
    2823           0 : 
    2824           0 :   // We have to use the full constructor for PolygonShapeInfo. This
    2825             :   // computes the float area using a rasterization method.
    2826             :   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
    2827             :   return MakeUnique<PolygonShapeInfo>(std::move(vertices), aShapeMargin,
    2828           0 :                                       appUnitsPerDevPixel, marginRect);
    2829             : }
    2830             : 
    2831             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
    2832             : nsFloatManager::ShapeInfo::CreateImageShape(
    2833             :   const UniquePtr<nsStyleImage>& aShapeImage,
    2834             :   float aShapeImageThreshold,
    2835             :   nscoord aShapeMargin,
    2836             :   nsIFrame* const aFrame,
    2837           0 :   const LogicalRect& aMarginRect,
    2838             :   WritingMode aWM,
    2839             :   const nsSize& aContainerSize)
    2840             : {
    2841           0 :   MOZ_ASSERT(aShapeImage ==
    2842           0 :              aFrame->StyleDisplay()->mShapeOutside.GetShapeImage(),
    2843             :              "aFrame should be the frame that we got aShapeImage from");
    2844           0 : 
    2845             :   nsImageRenderer imageRenderer(aFrame, aShapeImage.get(),
    2846             :                                 nsImageRenderer::FLAG_SYNC_DECODE_IMAGES);
    2847             : 
    2848             :   if (!imageRenderer.PrepareImage()) {
    2849           0 :     // The image is not ready yet.
    2850             :     return nullptr;
    2851             :   }
    2852           0 : 
    2853           0 :   nsRect contentRect = aFrame->GetContentRect();
    2854             : 
    2855           0 :   // Create a draw target and draw shape image on it.
    2856           0 :   nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
    2857             :   int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
    2858             :   LayoutDeviceIntSize contentSizeInDevPixels =
    2859             :     LayoutDeviceIntSize::FromAppUnitsRounded(contentRect.Size(),
    2860           0 :                                              appUnitsPerDevPixel);
    2861             : 
    2862             :   // Use empty CSSSizeOrRatio to force set the preferred size as the frame's
    2863           0 :   // content box size.
    2864           0 :   imageRenderer.SetPreferredSize(CSSSizeOrRatio(), contentRect.Size());
    2865           0 : 
    2866           0 :   RefPtr<gfx::DrawTarget> drawTarget =
    2867             :     gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(
    2868             :       contentSizeInDevPixels.ToUnknownSize(),
    2869             :       gfx::SurfaceFormat::A8);
    2870           0 :   if (!drawTarget) {
    2871           0 :     return nullptr;
    2872             :   }
    2873             : 
    2874           0 :   RefPtr<gfxContext> context = gfxContext::CreateOrNull(drawTarget);
    2875             :   MOZ_ASSERT(context); // already checked the target above
    2876           0 : 
    2877             :   ImgDrawResult result =
    2878             :     imageRenderer.DrawShapeImage(aFrame->PresContext(), *context);
    2879             : 
    2880             :   if (result != ImgDrawResult::SUCCESS) {
    2881           0 :     return nullptr;
    2882           0 :   }
    2883           0 : 
    2884             :   // Retrieve the pixel image buffer to create the image shape info.
    2885           0 :   RefPtr<SourceSurface> sourceSurface = drawTarget->Snapshot();
    2886             :   RefPtr<DataSourceSurface> dataSourceSurface = sourceSurface->GetDataSurface();
    2887             :   DataSourceSurface::ScopedMap map(dataSourceSurface, DataSourceSurface::READ);
    2888             : 
    2889           0 :   if (!map.IsMapped()) {
    2890             :     return nullptr;
    2891             :   }
    2892           0 : 
    2893             :   MOZ_ASSERT(sourceSurface->GetSize() == contentSizeInDevPixels.ToUnknownSize(),
    2894           0 :              "Who changes the size?");
    2895           0 : 
    2896             :   nsRect marginRect = aMarginRect.GetPhysicalRect(aWM, aContainerSize);
    2897             : 
    2898             :   uint8_t* alphaPixels = map.GetData();
    2899           0 :   int32_t stride = map.GetStride();
    2900             : 
    2901             :   // NOTE: ImageShapeInfo constructor does not keep a persistent copy of
    2902             :   // alphaPixels; it's only used during the constructor to compute pixel ranges.
    2903             :   return MakeUnique<ImageShapeInfo>(alphaPixels,
    2904             :                                     stride,
    2905             :                                     contentSizeInDevPixels,
    2906             :                                     appUnitsPerDevPixel,
    2907             :                                     aShapeImageThreshold,
    2908           0 :                                     aShapeMargin,
    2909             :                                     contentRect,
    2910             :                                     marginRect,
    2911             :                                     aWM,
    2912           0 :                                     aContainerSize);
    2913             : }
    2914             : 
    2915             : /* static */ nscoord
    2916             : nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff(
    2917             :   const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
    2918             :   const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
    2919             :   const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
    2920             :   const nscoord aBandBStart, const nscoord aBandBEnd)
    2921             : {
    2922             :   // An example for the band intersecting with the top right corner of an
    2923             :   // ellipse with writing-mode horizontal-tb.
    2924             :   //
    2925             :   //                             lineIntercept lineDiff
    2926             :   //                                    |       |
    2927             :   //  +---------------------------------|-------|-+---- aShapeBoxBStart
    2928             :   //  |                ##########^      |       | |
    2929             :   //  |            ##############|####  |       | |
    2930             :   //  +---------#################|######|-------|-+---- aBandBStart
    2931             :   //  |       ###################|######|##     | |
    2932             :   //  |     aBStartCornerRadiusB |######|###    | |
    2933             :   //  |    ######################|######|#####  | |
    2934             :   //  +---#######################|<-----------><->^---- aBandBEnd
    2935             :   //  |  ########################|##############  |
    2936             :   //  |  ########################|##############  |---- b
    2937             :   //  | #########################|############### |
    2938             :   //  | ######################## v<-------------->v
    2939             :   //  |###################### aBStartCornerRadiusL|
    2940             :   //  |###########################################|
    2941             :   //  |###########################################|
    2942             :   //  |###########################################|
    2943             :   //  |###########################################|
    2944             :   //  | ######################################### |
    2945             :   //  | ######################################### |
    2946             :   //  |  #######################################  |
    2947             :   //  |  #######################################  |
    2948             :   //  |   #####################################   |
    2949             :   //  |    ###################################    |
    2950             :   //  |      ###############################      |
    2951             :   //  |       #############################       |
    2952             :   //  |         #########################         |
    2953           0 :   //  |            ###################            |
    2954           0 :   //  |                ###########                |
    2955             :   //  +-------------------------------------------+----- aShapeBoxBEnd
    2956           0 : 
    2957             :   NS_ASSERTION(aShapeBoxBStart <= aShapeBoxBEnd, "Bad shape box coordinates!");
    2958             :   NS_ASSERTION(aBandBStart <= aBandBEnd, "Bad band coordinates!");
    2959             : 
    2960           0 :   nscoord lineDiff = 0;
    2961           0 : 
    2962           0 :   // If the band intersects both the block-start and block-end corners, we
    2963             :   // don't need to enter either branch because the correct lineDiff is 0.
    2964           0 :   if (aBStartCornerRadiusB > 0 &&
    2965             :       aBandBEnd >= aShapeBoxBStart &&
    2966           0 :       aBandBEnd <= aShapeBoxBStart + aBStartCornerRadiusB) {
    2967           0 :     // The band intersects only the block-start corner.
    2968           0 :     nscoord b = aBStartCornerRadiusB - (aBandBEnd - aShapeBoxBStart);
    2969           0 :     nscoord lineIntercept =
    2970             :       XInterceptAtY(b, aBStartCornerRadiusL, aBStartCornerRadiusB);
    2971             :     lineDiff = aBStartCornerRadiusL - lineIntercept;
    2972           0 :   } else if (aBEndCornerRadiusB > 0 &&
    2973             :              aBandBStart >= aShapeBoxBEnd - aBEndCornerRadiusB &&
    2974           0 :              aBandBStart <= aShapeBoxBEnd) {
    2975           0 :     // The band intersects only the block-end corner.
    2976             :     nscoord b = aBEndCornerRadiusB - (aShapeBoxBEnd - aBandBStart);
    2977             :     nscoord lineIntercept =
    2978           0 :       XInterceptAtY(b, aBEndCornerRadiusL, aBEndCornerRadiusB);
    2979             :     lineDiff = aBEndCornerRadiusL - lineIntercept;
    2980             :   }
    2981             : 
    2982           0 :   return lineDiff;
    2983             : }
    2984             : 
    2985             : /* static */ nscoord
    2986             : nsFloatManager::ShapeInfo::XInterceptAtY(const nscoord aY,
    2987           0 :                                          const nscoord aRadiusX,
    2988           0 :                                          const nscoord aRadiusY)
    2989             : {
    2990             :   // Solve for x in the ellipse equation (x/radiusX)^2 + (y/radiusY)^2 = 1.
    2991             :   MOZ_ASSERT(aRadiusY > 0);
    2992           0 :   return aRadiusX * std::sqrt(1 - (aY * aY) / double(aRadiusY * aRadiusY));
    2993             : }
    2994             : 
    2995             : /* static */ nsPoint
    2996             : nsFloatManager::ShapeInfo::ConvertToFloatLogical(
    2997           0 :   const nsPoint& aPoint,
    2998             :   WritingMode aWM,
    2999           0 :   const nsSize& aContainerSize)
    3000             : {
    3001             :   LogicalPoint logicalPoint(aWM, aPoint, aContainerSize);
    3002             :   return nsPoint(logicalPoint.LineRelative(aWM, aContainerSize),
    3003           0 :                  logicalPoint.B(aWM));
    3004             : }
    3005             : 
    3006           0 : /* static */ UniquePtr<nscoord[]>
    3007             : nsFloatManager::ShapeInfo::ConvertToFloatLogical(const nscoord aRadii[8],
    3008             :                                                  WritingMode aWM)
    3009             : {
    3010             :   UniquePtr<nscoord[]> logicalRadii(new nscoord[8]);
    3011           0 : 
    3012           0 :   // Get the physical side for line-left and line-right since border radii
    3013           0 :   // are on the physical axis.
    3014           0 :   Side lineLeftSide =
    3015           0 :     aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft));
    3016           0 :   logicalRadii[eCornerTopLeftX] =
    3017           0 :     aRadii[SideToHalfCorner(lineLeftSide, true, false)];
    3018           0 :   logicalRadii[eCornerTopLeftY] =
    3019           0 :     aRadii[SideToHalfCorner(lineLeftSide, true, true)];
    3020             :   logicalRadii[eCornerBottomLeftX] =
    3021             :     aRadii[SideToHalfCorner(lineLeftSide, false, false)];
    3022           0 :   logicalRadii[eCornerBottomLeftY] =
    3023           0 :     aRadii[SideToHalfCorner(lineLeftSide, false, true)];
    3024           0 : 
    3025           0 :   Side lineRightSide =
    3026           0 :     aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight));
    3027           0 :   logicalRadii[eCornerTopRightX] =
    3028           0 :     aRadii[SideToHalfCorner(lineRightSide, false, false)];
    3029           0 :   logicalRadii[eCornerTopRightY] =
    3030           0 :     aRadii[SideToHalfCorner(lineRightSide, false, true)];
    3031             :   logicalRadii[eCornerBottomRightX] =
    3032           0 :     aRadii[SideToHalfCorner(lineRightSide, true, false)];
    3033             :   logicalRadii[eCornerBottomRightY] =
    3034             :     aRadii[SideToHalfCorner(lineRightSide, true, true)];
    3035             : 
    3036             :   if (aWM.IsLineInverted()) {
    3037             :     // When IsLineInverted() is true, i.e. aWM is vertical-lr,
    3038           0 :     // line-over/line-under are inverted from block-start/block-end. So the
    3039           0 :     // relationship reverses between which corner comes first going
    3040           0 :     // clockwise, and which corner is block-start versus block-end. We need
    3041           0 :     // to swap the values stored in top and bottom corners.
    3042             :     std::swap(logicalRadii[eCornerTopLeftX], logicalRadii[eCornerBottomLeftX]);
    3043             :     std::swap(logicalRadii[eCornerTopLeftY], logicalRadii[eCornerBottomLeftY]);
    3044           0 :     std::swap(logicalRadii[eCornerTopRightX], logicalRadii[eCornerBottomRightX]);
    3045             :     std::swap(logicalRadii[eCornerTopRightY], logicalRadii[eCornerBottomRightY]);
    3046             :   }
    3047             : 
    3048           0 :   return logicalRadii;
    3049             : }
    3050             : 
    3051             : /* static */ size_t
    3052             : nsFloatManager::ShapeInfo::MinIntervalIndexContainingY(
    3053             :   const nsTArray<nsRect>& aIntervals,
    3054             :   const nscoord aTargetY)
    3055           0 : {
    3056           0 :   // Perform a binary search to find the minimum index of an interval
    3057           0 :   // that contains aTargetY. If no such interval exists, return a value
    3058           0 :   // equal to the number of intervals.
    3059           0 :   size_t startIdx = 0;
    3060             :   size_t endIdx = aIntervals.Length();
    3061             :   while (startIdx < endIdx) {
    3062           0 :     size_t midIdx = startIdx + (endIdx - startIdx) / 2;
    3063           0 :     if (aIntervals[midIdx].ContainsY(aTargetY)) {
    3064           0 :       return midIdx;
    3065             :     }
    3066             :     nscoord midY = aIntervals[midIdx].Y();
    3067             :     if (midY < aTargetY) {
    3068             :       startIdx = midIdx + 1;
    3069             :     } else {
    3070             :       endIdx = midIdx;
    3071             :     }
    3072             :   }
    3073             : 
    3074           0 :   return endIdx;
    3075             : }
    3076             : 
    3077             : /* static */ nscoord
    3078             : nsFloatManager::ShapeInfo::LineEdge(const nsTArray<nsRect>& aIntervals,
    3079           0 :                                     const nscoord aBStart,
    3080             :                                     const nscoord aBEnd,
    3081             :                                     bool aIsLineLeft)
    3082             : {
    3083             :   MOZ_ASSERT(aBStart <= aBEnd,
    3084             :              "The band's block start is greater than its block end?");
    3085             : 
    3086             :   // Find all the intervals whose rects overlap the aBStart to
    3087             :   // aBEnd range, and find the most constraining inline edge
    3088             :   // depending on the value of aLeft.
    3089             : 
    3090           0 :   // Since the intervals are stored in block-axis order, we need
    3091             :   // to find the first interval that overlaps aBStart and check
    3092           0 :   // succeeding intervals until we get past aBEnd.
    3093           0 : 
    3094           0 :   nscoord lineEdge = aIsLineLeft ? nscoord_MAX : nscoord_MIN;
    3095             : 
    3096             :   size_t intervalCount = aIntervals.Length();
    3097             :   for (size_t i = MinIntervalIndexContainingY(aIntervals, aBStart);
    3098           0 :        i < intervalCount; ++i) {
    3099           0 :     // We can always get the bCoord from the intervals' mLineLeft,
    3100           0 :     // since the y() coordinate is duplicated in both points in the
    3101             :     // interval.
    3102             :     auto& interval = aIntervals[i];
    3103             :     nscoord bCoord = interval.Y();
    3104           0 :     if (bCoord >= aBEnd) {
    3105           0 :       break;
    3106             :     }
    3107           0 :     // Get the edge from the interval point indicated by aLeft.
    3108             :     if (aIsLineLeft) {
    3109             :       lineEdge = std::min(lineEdge, interval.X());
    3110             :     } else {
    3111           0 :       lineEdge = std::max(lineEdge, interval.XMost());
    3112             :     }
    3113             :   }
    3114             : 
    3115           0 :   return lineEdge;
    3116             : }
    3117             : 
    3118             : /* static */ nsFloatManager::ShapeInfo::dfType
    3119             : nsFloatManager::ShapeInfo::CalcUsedShapeMargin5X(
    3120             :   nscoord aShapeMargin,
    3121             :   int32_t aAppUnitsPerDevPixel)
    3122             : {
    3123             :   // Our distance field has to be able to hold values equal to the
    3124             :   // maximum shape-margin value that we care about faithfully rendering,
    3125             :   // times 5. A 16-bit unsigned int can represent up to ~ 65K which means
    3126             :   // we can handle a margin up to ~ 13K device pixels. That's good enough
    3127             :   // for practical usage. Any supplied shape-margin value higher than this
    3128             :   // maximum will be clamped.
    3129           0 :   static const float MAX_MARGIN_5X_FLOAT = (float)MAX_MARGIN_5X;
    3130           0 : 
    3131           0 :   // Convert aShapeMargin to dev pixels, convert that into 5x-dev-pixel
    3132             :   // space, then clamp to MAX_MARGIN_5X_FLOAT.
    3133             :   float shapeMarginDevPixels5X = 5.0f *
    3134             :     NSAppUnitsToFloatPixels(aShapeMargin, aAppUnitsPerDevPixel);
    3135             :   NS_WARNING_ASSERTION(shapeMarginDevPixels5X <= MAX_MARGIN_5X_FLOAT,
    3136             :                        "shape-margin is too large and is being clamped.");
    3137             : 
    3138             :   // We calculate a minimum in float space, which takes care of any overflow
    3139             :   // or infinity that may have occurred earlier from multiplication of
    3140             :   // too-large aShapeMargin values.
    3141             :   float usedMargin5XFloat = std::min(shapeMarginDevPixels5X,
    3142             :                                      MAX_MARGIN_5X_FLOAT);
    3143             :   return (dfType)NSToIntRound(usedMargin5XFloat);
    3144             : }
    3145             : 
    3146             : //----------------------------------------------------------------------
    3147             : 
    3148             : nsAutoFloatManager::~nsAutoFloatManager()
    3149             : {
    3150             :   // Restore the old float manager in the reflow input if necessary.
    3151             :   if (mNew) {
    3152             : #ifdef DEBUG
    3153             :     if (nsBlockFrame::gNoisyFloatManager) {
    3154             :       printf("restoring old float manager %p\n", mOld);
    3155             :     }
    3156             : #endif
    3157             : 
    3158             :     mReflowInput.mFloatManager = mOld;
    3159             : 
    3160             : #ifdef DEBUG
    3161             :     if (nsBlockFrame::gNoisyFloatManager) {
    3162             :       if (mOld) {
    3163             :         mReflowInput.mFrame->ListTag(stdout);
    3164             :         printf(": float manager %p after reflow\n", mOld);
    3165             :         mOld->List(stdout);
    3166             :       }
    3167             :     }
    3168             : #endif
    3169             :   }
    3170             : }
    3171             : 
    3172             : void
    3173             : nsAutoFloatManager::CreateFloatManager(nsPresContext *aPresContext)
    3174             : {
    3175             :   MOZ_ASSERT(!mNew, "Redundant call to CreateFloatManager!");
    3176             : 
    3177             :   // Create a new float manager and install it in the reflow
    3178             :   // input. `Remember' the old float manager so we can restore it
    3179             :   // later.
    3180             :   mNew = MakeUnique<nsFloatManager>(aPresContext->PresShell(),
    3181             :                                     mReflowInput.GetWritingMode());
    3182             : 
    3183             : #ifdef DEBUG
    3184             :   if (nsBlockFrame::gNoisyFloatManager) {
    3185             :     printf("constructed new float manager %p (replacing %p)\n",
    3186             :            mNew.get(), mReflowInput.mFloatManager);
    3187             :   }
    3188             : #endif
    3189             : 
    3190             :   // Set the float manager in the existing reflow input.
    3191             :   mOld = mReflowInput.mFloatManager;
    3192             :   mReflowInput.mFloatManager = mNew.get();
    3193             : }

Generated by: LCOV version 1.13-14-ga5dd952