--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -1030,74 +1030,176 @@ public:
const nscoord aBEnd) const override;
nscoord BStart() const override { return mRect.y; }
nscoord BEnd() const override { return mRect.YMost(); }
bool IsEmpty() const override { return mRect.IsEmpty(); }
void Translate(nscoord aLineLeft, nscoord aBlockStart) override
{
mRect.MoveBy(aLineLeft, aBlockStart);
+
+ if (mShapeMargin > 0) {
+ MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalTopRightCorner &&
+ mLogicalBottomLeftCorner && mLogicalBottomRightCorner,
+ "If we have positive shape-margin, we should have corners.");
+ mLogicalTopLeftCorner->Translate(aLineLeft, aBlockStart);
+ mLogicalTopRightCorner->Translate(aLineLeft, aBlockStart);
+ mLogicalBottomLeftCorner->Translate(aLineLeft, aBlockStart);
+ mLogicalBottomRightCorner->Translate(aLineLeft, aBlockStart);
+ }
+ }
+
+ static bool EachCornerHasBalancedRadii(const nscoord* aRadii) {
+ return (aRadii[eCornerTopLeftX] == aRadii[eCornerTopLeftY] &&
+ aRadii[eCornerTopRightX] == aRadii[eCornerTopRightY] &&
+ aRadii[eCornerBottomLeftX] == aRadii[eCornerBottomLeftY] &&
+ aRadii[eCornerBottomRightX] == aRadii[eCornerBottomRightY]);
}
private:
// The rect of the rounded box shape in the float manager's coordinate
// space.
nsRect mRect;
// The half corner radii of the reference box. It's an nscoord[8] array
// in the float manager's coordinate space. If there are no radii, it's
// nullptr.
const UniquePtr<nscoord[]> mRadii;
- // A shape-margin value extends the boundaries of the float area.
+ // A shape-margin value extends the boundaries of the float area. When our
+ // first constructor is used, it is for the creation of rounded boxes that
+ // can ignore shape-margin -- either because it was specified as zero or
+ // because the box shape and radii can be inflated to account for it. When
+ // our second constructor is used, we store the shape-margin value here.
const nscoord mShapeMargin;
+
+ // If our second constructor is called (which implies mShapeMargin > 0),
+ // we will construct EllipseShapeInfo objects for each corner. We use the
+ // float logical naming here, where LogicalTopLeftCorner means the BStart
+ // LineLeft corner, and similarly for the other corners.
+ UniquePtr<EllipseShapeInfo> mLogicalTopLeftCorner;
+ UniquePtr<EllipseShapeInfo> mLogicalTopRightCorner;
+ UniquePtr<EllipseShapeInfo> mLogicalBottomLeftCorner;
+ UniquePtr<EllipseShapeInfo> mLogicalBottomRightCorner;
};
nsFloatManager::RoundedBoxShapeInfo::RoundedBoxShapeInfo(const nsRect& aRect,
UniquePtr<nscoord[]> aRadii,
nscoord aShapeMargin,
int32_t aAppUnitsPerDevPixel)
: mRect(aRect)
, mRadii(Move(aRadii))
, mShapeMargin(aShapeMargin)
{
+ MOZ_ASSERT(mShapeMargin > 0 && !EachCornerHasBalancedRadii(mRadii.get()),
+ "Slow constructor should only be used for for shape-margin > 0 "
+ "and radii with elliptical corners.");
+ // Before we inflate mRect by mShapeMargin, construct each of our corners.
+ // If we do it in this order, it's a bit simpler to calculate the center
+ // of each of the corners.
+ mLogicalTopLeftCorner = MakeUnique<EllipseShapeInfo>(
+ nsPoint(mRect.X() + mRadii[eCornerTopLeftX],
+ mRect.Y() + mRadii[eCornerTopLeftY]),
+ nsSize(mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY]),
+ mShapeMargin, aAppUnitsPerDevPixel);
+
+ mLogicalTopRightCorner = MakeUnique<EllipseShapeInfo>(
+ nsPoint(mRect.XMost() - mRadii[eCornerTopRightX],
+ mRect.Y() + mRadii[eCornerTopRightY]),
+ nsSize(mRadii[eCornerTopRightX], mRadii[eCornerTopRightY]),
+ mShapeMargin, aAppUnitsPerDevPixel);
+
+ mLogicalBottomLeftCorner = MakeUnique<EllipseShapeInfo>(
+ nsPoint(mRect.X() + mRadii[eCornerBottomLeftX],
+ mRect.YMost() - mRadii[eCornerBottomLeftY]),
+ nsSize(mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY]),
+ mShapeMargin, aAppUnitsPerDevPixel);
+
+ mLogicalBottomRightCorner = MakeUnique<EllipseShapeInfo>(
+ nsPoint(mRect.XMost() - mRadii[eCornerBottomRightX],
+ mRect.YMost() - mRadii[eCornerBottomRightY]),
+ nsSize(mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY]),
+ mShapeMargin, aAppUnitsPerDevPixel);
+
+ // Now we inflate our mRect by mShapeMargin.
+ mRect.Inflate(mShapeMargin);
}
nscoord
nsFloatManager::RoundedBoxShapeInfo::LineLeft(const nscoord aBStart,
const nscoord aBEnd) const
{
- if (!mRadii) {
- return mRect.x;
+ if (mShapeMargin == 0) {
+ if (!mRadii) {
+ return mRect.x;
+ }
+
+ nscoord lineLeftDiff =
+ ComputeEllipseLineInterceptDiff(
+ mRect.y, mRect.YMost(),
+ mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
+ mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
+ aBStart, aBEnd);
+ return mRect.x + lineLeftDiff;
}
- nscoord lineLeftDiff =
- ComputeEllipseLineInterceptDiff(
- mRect.y, mRect.YMost(),
- mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
- mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
- aBStart, aBEnd);
- return mRect.x + lineLeftDiff;
+ MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalBottomLeftCorner,
+ "If we have positive shape-margin, we should have corners.");
+
+ // Determine if aBEnd is within our top corner.
+ if (aBEnd < mLogicalTopLeftCorner->BEnd()) {
+ return mLogicalTopLeftCorner->LineLeft(aBStart, aBEnd);
+ }
+
+ // Determine if aBStart is within our bottom corner.
+ if (aBStart >= mLogicalBottomLeftCorner->BStart()) {
+ return mLogicalBottomLeftCorner->LineLeft(aBStart, aBEnd);
+ }
+
+ // Either aBStart or aBEnd or both are within the flat part of our left
+ // edge. Because we've already inflated our mRect to encompass our
+ // mShapeMargin, we can just return the edge.
+ return mRect.X();
}
nscoord
nsFloatManager::RoundedBoxShapeInfo::LineRight(const nscoord aBStart,
const nscoord aBEnd) const
{
- if (!mRadii) {
- return mRect.XMost();
+ if (mShapeMargin == 0) {
+ if (!mRadii) {
+ return mRect.XMost();
+ }
+
+ nscoord lineRightDiff =
+ ComputeEllipseLineInterceptDiff(
+ mRect.y, mRect.YMost(),
+ mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
+ mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
+ aBStart, aBEnd);
+ return mRect.XMost() - lineRightDiff;
}
- nscoord lineRightDiff =
- ComputeEllipseLineInterceptDiff(
- mRect.y, mRect.YMost(),
- mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
- mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
- aBStart, aBEnd);
- return mRect.XMost() - lineRightDiff;
+ MOZ_ASSERT(mLogicalTopRightCorner && mLogicalBottomRightCorner,
+ "If we have positive shape-margin, we should have corners.");
+
+ // Determine if aBEnd is within our top corner.
+ if (aBEnd < mLogicalTopRightCorner->BEnd()) {
+ return mLogicalTopRightCorner->LineRight(aBStart, aBEnd);
+ }
+
+ // Determine if aBStart is within our bottom corner.
+ if (aBStart >= mLogicalBottomRightCorner->BStart()) {
+ return mLogicalBottomRightCorner->LineRight(aBStart, aBEnd);
+ }
+
+ // Either aBStart or aBEnd or both are within the flat part of our right
+ // edge. Because we've already inflated our mRect to encompass our
+ // mShapeMargin, we can just return the edge.
+ return mRect.XMost();
}
/////////////////////////////////////////////////////////////////////////////
// PolygonShapeInfo
//
// Implements shape-outside: polygon().
//
class nsFloatManager::PolygonShapeInfo final : public nsFloatManager::ShapeInfo
@@ -2136,32 +2238,29 @@ nsFloatManager::ShapeInfo::CreateInset(
auto logicalRadii = MakeUnique<nscoord[]>(8);
for (int32_t i = 0; i < 8; ++i) {
logicalRadii[i] = aShapeMargin;
}
return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
Move(logicalRadii));
}
- // If we have radii, and each pair is equal, we can inflate both
- // logicalInsetRect and all the radii and use the fast constructor.
- if (physicalRadii[0] == physicalRadii[1] &&
- physicalRadii[2] == physicalRadii[3] &&
- physicalRadii[4] == physicalRadii[5] &&
- physicalRadii[6] == physicalRadii[7]) {
+ // If we have radii, and they have balanced/equal corners, we can inflate
+ // both logicalInsetRect and all the radii and use the fast constructor.
+ if (RoundedBoxShapeInfo::EachCornerHasBalancedRadii(physicalRadii)) {
logicalInsetRect.Inflate(aShapeMargin);
for (nscoord& r : physicalRadii) {
r += aShapeMargin;
}
return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
ConvertToFloatLogical(physicalRadii,
aWM));
}
- // With positive shape-margin and unequal radii pairs, we have to use the
+ // With positive shape-margin and elliptical radii, we have to use the
// slow constructor.
nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
ConvertToFloatLogical(physicalRadii,
aWM),
aShapeMargin, appUnitsPerDevPixel);
}