diff --git a/layout/generic/nsFloatManager.cpp b/layout/generic/nsFloatManager.cpp index cc647db8a747..69ea18eaf461 100644 --- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -527,6 +527,91 @@ nsFloatManager::ClearContinues(StyleClear aBreakType) const aBreakType == StyleClear::Right)); } +///////////////////////////////////////////////////////////////////////////// +// BoxShapeInfo + +nscoord +nsFloatManager::BoxShapeInfo::LineLeft(WritingMode aWM, + const nscoord aBStart, + const nscoord aBEnd) const +{ + nscoord radii[8]; + bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii); + + if (!hasRadii) { + return mShapeBoxRect.x; + } + + // Get the physical side for line-left since border-radii are in + // the physical axis. + mozilla::Side lineLeftSide = + aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft)); + nscoord blockStartCornerRadiusL = + radii[SideToHalfCorner(lineLeftSide, true, false)]; + nscoord blockStartCornerRadiusB = + radii[SideToHalfCorner(lineLeftSide, true, true)]; + nscoord blockEndCornerRadiusL = + radii[SideToHalfCorner(lineLeftSide, false, false)]; + nscoord blockEndCornerRadiusB = + radii[SideToHalfCorner(lineLeftSide, false, true)]; + + if (aWM.IsLineInverted()) { + // This happens only when aWM is vertical-lr. Need to swap blockStart + // and blockEnd corners. + std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL); + std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB); + } + + nscoord lineLeftDiff = + ComputeEllipseLineInterceptDiff( + mShapeBoxRect.y, mShapeBoxRect.YMost(), + blockStartCornerRadiusL, blockStartCornerRadiusB, + blockEndCornerRadiusL, blockEndCornerRadiusB, + aBStart, aBEnd); + return mShapeBoxRect.x + lineLeftDiff; +} + +nscoord +nsFloatManager::BoxShapeInfo::LineRight(WritingMode aWM, + const nscoord aBStart, + const nscoord aBEnd) const +{ + nscoord radii[8]; + bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii); + + if (!hasRadii) { + return mShapeBoxRect.XMost(); + } + + // Get the physical side for line-right since border-radii are in + // the physical axis. + mozilla::Side lineRightSide = + aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight)); + nscoord blockStartCornerRadiusL = + radii[SideToHalfCorner(lineRightSide, false, false)]; + nscoord blockStartCornerRadiusB = + radii[SideToHalfCorner(lineRightSide, false, true)]; + nscoord blockEndCornerRadiusL = + radii[SideToHalfCorner(lineRightSide, true, false)]; + nscoord blockEndCornerRadiusB = + radii[SideToHalfCorner(lineRightSide, true, true)]; + + if (aWM.IsLineInverted()) { + // This happens only when aWM is vertical-lr. Need to swap blockStart + // and blockEnd corners. + std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL); + std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB); + } + + nscoord lineRightDiff = + ComputeEllipseLineInterceptDiff( + mShapeBoxRect.y, mShapeBoxRect.YMost(), + blockStartCornerRadiusL, blockStartCornerRadiusB, + blockEndCornerRadiusL, blockEndCornerRadiusB, + aBStart, aBEnd); + return mShapeBoxRect.XMost() - lineRightDiff; +} + ///////////////////////////////////////////////////////////////////////////// // FloatInfo @@ -545,31 +630,36 @@ nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside; + if (shapeOutside.GetType() == StyleShapeSourceType::None) { + return; + } + + // Initialize 's reference rect. + LogicalRect rect = aMarginRect; + + switch (shapeOutside.GetReferenceBox()) { + case StyleShapeOutsideShapeBox::Content: + rect.Deflate(aWM, mFrame->GetLogicalUsedPadding(aWM)); + MOZ_FALLTHROUGH; + case StyleShapeOutsideShapeBox::Padding: + rect.Deflate(aWM, mFrame->GetLogicalUsedBorder(aWM)); + MOZ_FALLTHROUGH; + case StyleShapeOutsideShapeBox::Border: + rect.Deflate(aWM, mFrame->GetLogicalUsedMargin(aWM)); + break; + case StyleShapeOutsideShapeBox::Margin: + // Do nothing. rect is already a margin rect. + break; + case StyleShapeOutsideShapeBox::NoBox: + MOZ_ASSERT_UNREACHABLE("Why don't we have a shape-box?"); + break; + } + if (shapeOutside.GetType() == StyleShapeSourceType::Box) { - // Initialize shape-box reference rect. - LogicalRect rect = aMarginRect; - - switch (shapeOutside.GetReferenceBox()) { - case StyleShapeOutsideShapeBox::Content: - rect.Deflate(aWM, mFrame->GetLogicalUsedPadding(aWM)); - MOZ_FALLTHROUGH; - case StyleShapeOutsideShapeBox::Padding: - rect.Deflate(aWM, mFrame->GetLogicalUsedBorder(aWM)); - MOZ_FALLTHROUGH; - case StyleShapeOutsideShapeBox::Border: - rect.Deflate(aWM, mFrame->GetLogicalUsedMargin(aWM)); - break; - case StyleShapeOutsideShapeBox::Margin: - // Do nothing. rect is already a margin rect. - break; - case StyleShapeOutsideShapeBox::NoBox: - MOZ_ASSERT_UNREACHABLE("Why don't we have a shape-box?"); - break; - } - - mShapeBoxRect.emplace(rect.LineLeft(aWM, aContainerSize) + aLineLeft, - rect.BStart(aWM) + aBlockStart, - rect.ISize(aWM), rect.BSize(aWM)); + nsRect shapeBoxRect(rect.LineLeft(aWM, aContainerSize) + aLineLeft, + rect.BStart(aWM) + aBlockStart, + rect.ISize(aWM), rect.BSize(aWM)); + mShapeInfo = MakeUnique(shapeBoxRect, mFrame); } } @@ -579,7 +669,7 @@ nsFloatManager::FloatInfo::FloatInfo(FloatInfo&& aOther) , mLeftBEnd(Move(aOther.mLeftBEnd)) , mRightBEnd(Move(aOther.mRightBEnd)) , mRect(Move(aOther.mRect)) - , mShapeBoxRect(Move(aOther.mShapeBoxRect)) + , mShapeInfo(Move(aOther.mShapeInfo)) { MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); } @@ -601,51 +691,10 @@ nsFloatManager::FloatInfo::LineLeft(WritingMode aWM, } MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside); - const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside; - if (shapeOutside.GetType() == StyleShapeSourceType::None) { + if (!mShapeInfo) { return LineLeft(); } - - if (shapeOutside.GetType() == StyleShapeSourceType::Box) { - nscoord radii[8]; - bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii); - - if (!hasRadii) { - return ShapeBoxRect().x; - } - - // Get the physical side for line-left since border-radii are in - // the physical axis. - mozilla::Side lineLeftSide = - aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft)); - nscoord blockStartCornerRadiusL = - radii[SideToHalfCorner(lineLeftSide, true, false)]; - nscoord blockStartCornerRadiusB = - radii[SideToHalfCorner(lineLeftSide, true, true)]; - nscoord blockEndCornerRadiusL = - radii[SideToHalfCorner(lineLeftSide, false, false)]; - nscoord blockEndCornerRadiusB = - radii[SideToHalfCorner(lineLeftSide, false, true)]; - - if (aWM.IsLineInverted()) { - // This happens only when aWM is vertical-lr. Need to swap blockStart - // and blockEnd corners. - std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL); - std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB); - } - - nscoord lineLeftDiff = - ComputeEllipseLineInterceptDiff( - ShapeBoxRect().y, ShapeBoxRect().YMost(), - blockStartCornerRadiusL, blockStartCornerRadiusB, - blockEndCornerRadiusL, blockEndCornerRadiusB, - aBStart, aBEnd); - return ShapeBoxRect().x + lineLeftDiff; - } - - // XXX: Other shape source types are not implemented yet. - - return LineLeft(); + return mShapeInfo->LineLeft(aWM, aBStart, aBEnd); } nscoord @@ -659,55 +708,42 @@ nsFloatManager::FloatInfo::LineRight(WritingMode aWM, } MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside); - const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside; - if (shapeOutside.GetType() == StyleShapeSourceType::None) { + if (!mShapeInfo) { return LineRight(); } + return mShapeInfo->LineRight(aWM, aBStart, aBEnd); +} - if (shapeOutside.GetType() == StyleShapeSourceType::Box) { - nscoord radii[8]; - bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii); - - if (!hasRadii) { - return ShapeBoxRect().XMost(); - } - - // Get the physical side for line-right since border-radii are in - // the physical axis. - mozilla::Side lineRightSide = - aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight)); - nscoord blockStartCornerRadiusL = - radii[SideToHalfCorner(lineRightSide, false, false)]; - nscoord blockStartCornerRadiusB = - radii[SideToHalfCorner(lineRightSide, false, true)]; - nscoord blockEndCornerRadiusL = - radii[SideToHalfCorner(lineRightSide, true, false)]; - nscoord blockEndCornerRadiusB = - radii[SideToHalfCorner(lineRightSide, true, true)]; - - if (aWM.IsLineInverted()) { - // This happens only when aWM is vertical-lr. Need to swap blockStart - // and blockEnd corners. - std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL); - std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB); - } - - nscoord lineRightDiff = - ComputeEllipseLineInterceptDiff( - ShapeBoxRect().y, ShapeBoxRect().YMost(), - blockStartCornerRadiusL, blockStartCornerRadiusB, - blockEndCornerRadiusL, blockEndCornerRadiusB, - aBStart, aBEnd); - return ShapeBoxRect().XMost() - lineRightDiff; +nscoord +nsFloatManager::FloatInfo::BStart(ShapeType aShapeType) const +{ + if (aShapeType == ShapeType::Margin) { + return BStart(); } - // XXX: Other shape source types are not implemented yet. + MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside); + if (!mShapeInfo) { + return BStart(); + } + return mShapeInfo->BStart(); +} - return LineRight(); +nscoord +nsFloatManager::FloatInfo::BEnd(ShapeType aShapeType) const +{ + if (aShapeType == ShapeType::Margin) { + return BEnd(); + } + + MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside); + if (!mShapeInfo) { + return BEnd(); + } + return mShapeInfo->BEnd(); } /* static */ nscoord -nsFloatManager::FloatInfo::ComputeEllipseLineInterceptDiff( +nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff( const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd, const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB, const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB, @@ -777,7 +813,7 @@ nsFloatManager::FloatInfo::ComputeEllipseLineInterceptDiff( } /* static */ nscoord -nsFloatManager::FloatInfo::XInterceptAtY(const nscoord aY, +nsFloatManager::ShapeInfo::XInterceptAtY(const nscoord aY, const nscoord aRadiusX, const nscoord aRadiusY) { diff --git a/layout/generic/nsFloatManager.h b/layout/generic/nsFloatManager.h index c2a7f0afc42c..4faa3bb10786 100644 --- a/layout/generic/nsFloatManager.h +++ b/layout/generic/nsFloatManager.h @@ -10,7 +10,7 @@ #define nsFloatManager_h_ #include "mozilla/Attributes.h" -#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" #include "mozilla/WritingModes.h" #include "nsCoord.h" #include "nsFrameList.h" // for DEBUG_FRAME_DUMP @@ -332,6 +332,70 @@ public: private: + // ShapeInfo is an abstract class for implementing all the shapes in CSS + // Shapes Module. A subclass needs to override all the methods to adjust + // the flow area with respect to its shape. + class ShapeInfo + { + public: + virtual ~ShapeInfo() {} + + virtual nscoord LineLeft(mozilla::WritingMode aWM, + const nscoord aBStart, + const nscoord aBEnd) const = 0; + virtual nscoord LineRight(mozilla::WritingMode aWM, + const nscoord aBStart, + const nscoord aBEnd) const = 0; + virtual nscoord BStart() const = 0; + virtual nscoord BEnd() const = 0; + + protected: + // Compute the minimum line-axis difference between the bounding shape + // box and its rounded corner within the given band (block-axis region). + // This is used as a helper function to compute the LineRight() and + // LineLeft(). See the picture in the implementation for an example. + // RadiusL and RadiusB stand for radius on the line-axis and block-axis. + // + // Returns radius-x diff on the line-axis, or 0 if there's no rounded + // corner within the given band. + static nscoord ComputeEllipseLineInterceptDiff( + const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd, + const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB, + const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB, + const nscoord aBandBStart, const nscoord aBandBEnd); + + static nscoord XInterceptAtY(const nscoord aY, const nscoord aRadiusX, + const nscoord aRadiusY); + }; + + // Implements shape-outside: . + class BoxShapeInfo final : public ShapeInfo + { + public: + BoxShapeInfo(const nsRect& aShapeBoxRect, nsIFrame* const aFrame) + : mShapeBoxRect(aShapeBoxRect) + , mFrame(aFrame) + { + } + + nscoord LineLeft(mozilla::WritingMode aWM, + const nscoord aBStart, + const nscoord aBEnd) const override; + nscoord LineRight(mozilla::WritingMode aWM, + const nscoord aBStart, + const nscoord aBEnd) const override; + nscoord BStart() const override { return mShapeBoxRect.y; } + nscoord BEnd() const override { return mShapeBoxRect.YMost(); } + + private: + // This is the reference box of css shape-outside if specified, which + // implements the value in the CSS Shapes Module Level 1. + // The coordinate space is the same as FloatInfo::mRect. + const nsRect mShapeBoxRect; + // The frame of the float. + nsIFrame* const mFrame; + }; + struct FloatInfo { nsIFrame *const mFrame; // The lowest block-ends of left/right floats up to and including @@ -350,8 +414,6 @@ private: nscoord BSize() const { return mRect.height; } bool IsEmpty() const { return mRect.IsEmpty(); } - nsRect ShapeBoxRect() const { return mShapeBoxRect.valueOr(mRect); } - // aBStart and aBEnd are the starting and ending coordinate of a band. // LineLeft() and LineRight() return the innermost line-left extent and // line-right extent within the given band, respectively. @@ -359,32 +421,8 @@ private: const nscoord aBStart, const nscoord aBEnd) const; nscoord LineRight(mozilla::WritingMode aWM, ShapeType aShapeType, const nscoord aBStart, const nscoord aBEnd) const; - - nscoord BStart(ShapeType aShapeType) const - { - return aShapeType == ShapeType::Margin ? BStart() : ShapeBoxRect().y; - } - nscoord BEnd(ShapeType aShapeType) const - { - return aShapeType == ShapeType::Margin ? BEnd() : ShapeBoxRect().YMost(); - } - - // Compute the minimum line-axis difference between the bounding shape - // box and its rounded corner within the given band (block-axis region). - // This is used as a helper function to compute the LineRight() and - // LineLeft(). See the picture in the implementation for an example. - // RadiusL and RadiusB stand for radius on the line-axis and block-axis. - // - // Returns radius-x diff on the line-axis, or 0 if there's no rounded - // corner within the given band. - static nscoord ComputeEllipseLineInterceptDiff( - const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd, - const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB, - const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB, - const nscoord aBandBStart, const nscoord aBandBEnd); - - static nscoord XInterceptAtY(const nscoord aY, const nscoord aRadiusX, - const nscoord aRadiusY); + nscoord BStart(ShapeType aShapeType) const; + nscoord BEnd(ShapeType aShapeType) const; #ifdef NS_BUILD_REFCNT_LOGGING FloatInfo(FloatInfo&& aOther); @@ -398,10 +436,9 @@ private: // the line-relative axis of the frame manager and its block // coordinates are in the frame manager's real block direction. nsRect mRect; - // This is the reference box of css shape-outside if specified, which - // implements the value in the CSS Shapes Module Level 1. - // The coordinate setup is the same as mRect. - mozilla::Maybe mShapeBoxRect; + // Pointer to a concrete subclass of ShapeInfo or null, which means that + // there is no shape-outside. + mozilla::UniquePtr mShapeInfo; }; #ifdef DEBUG