Skip to content
Permalink
Browse files
WebKit fails to render extreme border-radius
https://bugs.webkit.org/show_bug.cgi?id=244638
rdar://99668793

Reviewed by Antti Koivisto.

* LayoutTests/imported/w3c/web-platform-tests/css/css-backgrounds/inner-border-non-renderable-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-backgrounds/inner-border-non-renderable-ref.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-backgrounds/inner-border-non-renderable.html: Added.
Testing that child-background doesn't bleed through its parent border for a inner-border-radius that is larger than the content rect.

* Source/WebCore/rendering/BorderPainter.cpp:
(WebCore::BorderPainter::paintOneBorderSide):
Improving readability.

* Source/WebCore/rendering/RenderBox.cpp:
(WebCore::RenderBox::clipContentForBorderRadius):
(WebCore::RenderBox::pushContentsClip):
The bug can be fixed by adjusting the inner-border radius, if not renderable, here. However, with we adjust it just here the bug can still be triggered in
other situations, for example, when applying a clip-path in an element with non-renderable border. Therefore, we are adjusting the non-renderable inner-border
directly in RenderStyle::getRoundedInnerBorderFor. Here, we are adding a helper function that still calls getRoundedInnerBorderFor for improving readability.

* Source/WebCore/rendering/RenderBox.h:
* Source/WebCore/rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::getRoundedInnerBorderFor):
Adjusting the inner-border-radii for non-renderable inner-border.

Canonical link: https://commits.webkit.org/256943@main
  • Loading branch information
vitorroriz authored and nt1m committed Nov 22, 2022
1 parent 8996a7f commit 8a3db1af14c197a24ab0425903d026408ff569f5
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 5 deletions.
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Testing that child-background doesn't bleed through its parent border for a inner-border-radius that is larger than the content rect</title>
<link rel="help" href="https://w3c.github.io/csswg-drafts/css-backgrounds/#border-radius">
<link rel="assert" content="Testing that child-background doesn't bleed through its parent border for a inner-border-radius that is larger than the content rect">
<style>
body {
font-size: 24px;
color: black;
margin: 8px;
}
</style>
</head>
<body>
<div> Test passes if no blue square is shown:</div>
</body>
</html>
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Testing that child-background doesn't bleed through its parent border for a inner-border-radius that is larger than the content rect</title>
<link rel="help" href="https://w3c.github.io/csswg-drafts/css-backgrounds/#border-radius">
<link rel="assert" content="Testing that child-background doesn't bleed through its parent border for a inner-border-radius that is larger than the content rect">
<style>
body {
font-size: 24px;
color: black;
margin: 8px;
}
</style>
</head>
<body>
<div> Test passes if no blue square is shown:</div>
</body>
</html>
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>Testing that child-background doesn't bleed through its parent border for a inner-border-radius that is larger than the content rect</title>
<link rel="match" href="inner-border-non-renderable-ref.html">
<link rel="help" href="https://w3c.github.io/csswg-drafts/css-backgrounds/#border-radius">
<link rel="assert" content="Testing that child-background doesn't bleed through its parent border for a inner-border-radius that is larger than the content rect">
<style>
body {
font-size: 24px;
color: black;
margin: 8px;
}
.clipping {
width: 300px;
height: 200px;
overflow: hidden;
border: 30px solid green;
border-top-color: gold;
border-top-right-radius: 150px 267px;
background-color: blue;
}
.composited {
width: 100%;
height: 100%;
background-color: blue;
}
.clip-test {
clip-path: inset(60px 10px 190px 320px);
}
</style>
</head>
<body>
<div> Test passes if no blue square is shown:</div>
<div class="clipping clip-test">
<div class="composited"></div>
</div>
</body>
</html>
@@ -829,8 +829,10 @@ void BorderPainter::paintOneBorderSide(const RoundedRect& outerBorder, const Rou

clipBorderSidePolygon(outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);

if (!innerBorder.isRenderable())
graphicsContext.clipOutRoundedRect(FloatRoundedRect(calculateAdjustedInnerBorder(innerBorder, side)));
if (!innerBorder.isRenderable()) {
auto adjustedInnerBorder = FloatRoundedRect(calculateAdjustedInnerBorder(innerBorder, side));
graphicsContext.clipOutRoundedRect(adjustedInnerBorder);
}

float thickness = std::max(std::max(edgeToRender.widthForPainting(), adjacentEdge1.widthForPainting()), adjacentEdge2.widthForPainting());
drawBoxSideFromPath(outerBorder.rect(), *path, edges, radii, edgeToRender.widthForPainting(), thickness, side,
@@ -2000,17 +2000,23 @@ bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer
return false;
}

void RenderBox::clipContentForBorderRadius(GraphicsContext& context, const LayoutPoint& accumulatedOffset, float deviceScaleFactor)
{
auto innerBorder = style().getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size()));
context.clipRoundedRect(innerBorder.pixelSnappedRoundedRectForPainting(deviceScaleFactor));
}

bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset)
{
if (paintInfo.phase == PaintPhase::BlockBackground || paintInfo.phase == PaintPhase::SelfOutline || paintInfo.phase == PaintPhase::Mask)
return false;

bool isControlClip = hasControlClip();
bool isOverflowClip = hasNonVisibleOverflow() && !layer()->isSelfPaintingLayer();

if (!isControlClip && !isOverflowClip)
return false;

if (paintInfo.phase == PaintPhase::Outline)
paintInfo.phase = PaintPhase::ChildOutlines;
else if (paintInfo.phase == PaintPhase::ChildBlockBackground) {
@@ -2022,7 +2028,7 @@ bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumu
FloatRect clipRect = snapRectToDevicePixels((isControlClip ? controlClipRect(accumulatedOffset) : overflowClipRect(accumulatedOffset, nullptr, IgnoreOverlayScrollbarSize, paintInfo.phase)), deviceScaleFactor);
paintInfo.context().save();
if (style().hasBorderRadius())
paintInfo.context().clipRoundedRect(style().getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size())).pixelSnappedRoundedRectForPainting(deviceScaleFactor));
clipContentForBorderRadius(paintInfo.context(), accumulatedOffset, deviceScaleFactor);
paintInfo.context().clip(clipRect);

if (paintInfo.phase == PaintPhase::EventRegion)
@@ -807,6 +807,8 @@ override;

LayoutPoint topLeftLocationWithFlipping() const;

void clipContentForBorderRadius(GraphicsContext&, const LayoutPoint&, float);

private:
// The width/height of the contents + borders + padding. The x/y location is relative to our container (which is not always our parent).
LayoutRect m_frameRect;
@@ -1780,6 +1780,8 @@ RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect,
adjustedRadii.shrink(topWidth, bottomWidth, leftWidth, rightWidth);
roundedRect.includeLogicalEdges(adjustedRadii, isHorizontalWritingMode, includeLogicalLeftEdge, includeLogicalRightEdge);
}
if (!roundedRect.isRenderable())
roundedRect.adjustRadii();
return roundedRect;
}

0 comments on commit 8a3db1a

Please sign in to comment.