Title: [284336] trunk
Revision
284336
Author
simon.fra...@apple.com
Date
2021-10-17 09:54:24 -0700 (Sun, 17 Oct 2021)

Log Message

REGRESSION (r270850): Reference clip path clips in the wrong place when inside non-visible overflow
https://bugs.webkit.org/show_bug.cgi?id=231852
<rdar://83186229>

Reviewed by Tim Horton.
Source/WebCore:

When clipping CSS boxes with reference clip paths we need to give RenderSVGResourceClipper two
different rectangles; the objectBoundingBox that is used to compute clip path geometry,
and the bounds of the clipped content, so that the buffer-based clipping code path
can create an ImageBuffer of the appropriate size.

Previously, we confounded these two rectangles, resulting in various bugs.

Tests: css3/masking/reference-clip-path-bounds.html
       css3/masking/reference-clip-path-objectBoundingBox-buffer-clip.html
       css3/masking/reference-clip-path-objectBoundingBox-path-clip.html

* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::setupClipPath):
* rendering/svg/RenderSVGResourceClipper.cpp:
(WebCore::RenderSVGResourceClipper::applyResource):
(WebCore::RenderSVGResourceClipper::applyClippingToContext):
* rendering/svg/RenderSVGResourceClipper.h:

LayoutTests:

Tests for reference clip-path with box-shadows and enclosing oveflow:hidden.

* css3/masking/reference-clip-path-bounds-expected.html: Added.
* css3/masking/reference-clip-path-bounds.html: Added.
* css3/masking/reference-clip-path-objectBoundingBox-buffer-clip-expected.html: Added.
* css3/masking/reference-clip-path-objectBoundingBox-buffer-clip.html: Added.
* css3/masking/reference-clip-path-objectBoundingBox-path-clip-expected.html: Added.
* css3/masking/reference-clip-path-objectBoundingBox-path-clip.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (284335 => 284336)


--- trunk/LayoutTests/ChangeLog	2021-10-17 16:38:27 UTC (rev 284335)
+++ trunk/LayoutTests/ChangeLog	2021-10-17 16:54:24 UTC (rev 284336)
@@ -1,3 +1,20 @@
+2021-10-17  Simon Fraser  <simon.fra...@apple.com>
+
+        REGRESSION (r270850): Reference clip path clips in the wrong place when inside non-visible overflow
+        https://bugs.webkit.org/show_bug.cgi?id=231852
+        <rdar://83186229>
+
+        Reviewed by Tim Horton.
+        
+        Tests for reference clip-path with box-shadows and enclosing oveflow:hidden.
+
+        * css3/masking/reference-clip-path-bounds-expected.html: Added.
+        * css3/masking/reference-clip-path-bounds.html: Added.
+        * css3/masking/reference-clip-path-objectBoundingBox-buffer-clip-expected.html: Added.
+        * css3/masking/reference-clip-path-objectBoundingBox-buffer-clip.html: Added.
+        * css3/masking/reference-clip-path-objectBoundingBox-path-clip-expected.html: Added.
+        * css3/masking/reference-clip-path-objectBoundingBox-path-clip.html: Added.
+
 2021-10-17  Tyler Wilcock  <tyle...@apple.com>
 
         AX: WebKit should not expose redundant AXGroups with missing role when the label is the same as the contents

Added: trunk/LayoutTests/css3/masking/reference-clip-path-bounds-expected.html (0 => 284336)


--- trunk/LayoutTests/css3/masking/reference-clip-path-bounds-expected.html	                        (rev 0)
+++ trunk/LayoutTests/css3/masking/reference-clip-path-bounds-expected.html	2021-10-17 16:54:24 UTC (rev 284336)
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+    .container {
+        position: absolute;
+        top: 20px;
+        border: 2px solid black;
+        width: 300px;
+        height: 300px;
+        overflow: hidden;
+    }
+
+    .clipped {
+        margin-left: 50px;
+        width: 200px;
+        height: 200px;
+        clip-path: inset(10px 40px 40px 10px);
+        background-color: green;
+    }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <div class="clipped"></div> 
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/css3/masking/reference-clip-path-bounds.html (0 => 284336)


--- trunk/LayoutTests/css3/masking/reference-clip-path-bounds.html	                        (rev 0)
+++ trunk/LayoutTests/css3/masking/reference-clip-path-bounds.html	2021-10-17 16:54:24 UTC (rev 284336)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+    .container {
+        position: absolute;
+        top: 20px;
+        border: 2px solid black;
+        width: 300px;
+        height: 300px;
+        overflow: hidden;
+    }
+
+    .clipped {
+        margin-left: 50px;
+        width: 200px;
+        height: 200px;
+        clip-path: url(#rect-clip);
+        background-color: green;
+    }
+    </style>
+</head>
+<body>
+    <svg width="0" height="0">
+        <defs>
+            <clippath id="rect-clip">
+                <rect x="10" y="10" width="150" height="150"/>
+            </clippath>
+        </defs>
+    </svg>
+    <div class="container">
+        <div class="clipped"></div> 
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-buffer-clip-expected.html (0 => 284336)


--- trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-buffer-clip-expected.html	                        (rev 0)
+++ trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-buffer-clip-expected.html	2021-10-17 16:54:24 UTC (rev 284336)
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .clipper {
+            position: absolute;
+            top: 150px;
+            left: 150px;
+            width: 100px;
+            height: 150px;
+            background: lime;
+        }
+        
+        .clipper > div {
+            position: absolute;
+            width: 50px;
+            height: 200px;
+            background-color: blue;
+        }
+        
+        .clipper .above {
+            top: -100px;
+            left: 0px;
+            height: 150px;
+        }
+
+        .clipper .below {
+            top: 50px;
+            left: 50px;
+        }
+    </style>
+</head>
+<body>
+  <div class="clipper">
+    <div class="below"></div>
+    <div class="above"></div>
+  </div>
+    <svg height="0" width="0">
+    <clipPath id="clipper" clipPathUnits="objectBoundingBox">
+        <!--Two rects triggers the buffer-based clipping in RenderSVGResourceClipper::applyClippingToContext() -->
+      <rect x="0.25" y="-0.25" width="0.25" height="1"/>
+      <rect x="0.25" y="0.25" width="0.5" height="1.5"/>
+    </clipPath>
+  </svg>
+</body>
+</html>

Added: trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-buffer-clip.html (0 => 284336)


--- trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-buffer-clip.html	                        (rev 0)
+++ trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-buffer-clip.html	2021-10-17 16:54:24 UTC (rev 284336)
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .clipper {
+            position: absolute;
+            top: 100px;
+            left: 100px;
+            width: 200px;
+            height: 200px;
+            clip-path: url(#clipper);
+            background: lime;
+        }
+        
+        .clipper > div {
+            position: absolute;
+            width: 200px;
+            height: 200px;
+            background-color: blue;
+        }
+        
+        .above {
+            top: -100px;
+            left: -100px;
+        }
+
+        .below {
+            top: 100px;
+            left: 100px;
+        }
+    </style>
+</head>
+<body>
+  <div class="clipper">
+    <div class="below"></div>
+    <div class="above"></div>
+  </div>
+    <svg height="0" width="0">
+    <clipPath id="clipper" clipPathUnits="objectBoundingBox">
+        <!--Two rects triggers the buffer-based clipping in RenderSVGResourceClipper::applyClippingToContext() -->
+      <rect x="0.25" y="-0.25" width="0.25" height="1"/>
+      <rect x="0.25" y="0.25" width="0.5" height="1.5"/>
+    </clipPath>
+  </svg>
+</body>
+</html>

Added: trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-path-clip-expected.html (0 => 284336)


--- trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-path-clip-expected.html	                        (rev 0)
+++ trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-path-clip-expected.html	2021-10-17 16:54:24 UTC (rev 284336)
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .clipper {
+            position: absolute;
+            top: 120px;
+            left: 170px;
+            width: 100px;
+            height: 200px;
+            background: lime;
+        }
+        
+        .clipper > div {
+            position: absolute;
+            top: 100px;
+            left: 50px;
+            width: 50px;
+            height: 200px;
+            background-color: blue;
+        }
+    </style>
+</head>
+<body>
+  <div class="clipper">
+    <div></div>
+  </div>
+    <svg height="0" width="0">
+    <clipPath id="clipper" clipPathUnits="objectBoundingBox">
+      <rect x="0.25" y="-0.25" width="0.5" height="1.5"/>
+    </clipPath>
+  </svg>
+</body>
+</html>

Added: trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-path-clip.html (0 => 284336)


--- trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-path-clip.html	                        (rev 0)
+++ trunk/LayoutTests/css3/masking/reference-clip-path-objectBoundingBox-path-clip.html	2021-10-17 16:54:24 UTC (rev 284336)
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .clipper {
+            position: absolute;
+            top: 100px;
+            left: 100px;
+            width: 200px;
+            height: 200px;
+            clip-path: url(#clipper);
+            background: lime;
+            margin: 20px;
+        }
+        
+        .clipper > div {
+            position: absolute;
+            top: 100px;
+            left: 100px;
+            width: 200px;
+            height: 200px;
+            background-color: blue;
+        }
+    </style>
+</head>
+<body>
+  <div class="clipper">
+    <div></div>
+  </div>
+    <svg height="0" width="0">
+    <clipPath id="clipper" clipPathUnits="objectBoundingBox">
+      <rect x="0.25" y="0" width="0.5" height="1.5"/>
+    </clipPath>
+  </svg>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (284335 => 284336)


--- trunk/Source/WebCore/ChangeLog	2021-10-17 16:38:27 UTC (rev 284335)
+++ trunk/Source/WebCore/ChangeLog	2021-10-17 16:54:24 UTC (rev 284336)
@@ -1,3 +1,29 @@
+2021-10-17  Simon Fraser  <simon.fra...@apple.com>
+
+        REGRESSION (r270850): Reference clip path clips in the wrong place when inside non-visible overflow
+        https://bugs.webkit.org/show_bug.cgi?id=231852
+        <rdar://83186229>
+
+        Reviewed by Tim Horton.
+
+        When clipping CSS boxes with reference clip paths we need to give RenderSVGResourceClipper two
+        different rectangles; the objectBoundingBox that is used to compute clip path geometry,
+        and the bounds of the clipped content, so that the buffer-based clipping code path
+        can create an ImageBuffer of the appropriate size.
+
+        Previously, we confounded these two rectangles, resulting in various bugs.
+
+        Tests: css3/masking/reference-clip-path-bounds.html
+               css3/masking/reference-clip-path-objectBoundingBox-buffer-clip.html
+               css3/masking/reference-clip-path-objectBoundingBox-path-clip.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::setupClipPath):
+        * rendering/svg/RenderSVGResourceClipper.cpp:
+        (WebCore::RenderSVGResourceClipper::applyResource):
+        (WebCore::RenderSVGResourceClipper::applyClippingToContext):
+        * rendering/svg/RenderSVGResourceClipper.h:
+
 2021-10-17  Tyler Wilcock  <tyle...@apple.com>
 
         AX: WebKit should not expose redundant AXGroups with missing role when the label is the same as the contents

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (284335 => 284336)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2021-10-17 16:38:27 UTC (rev 284335)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2021-10-17 16:54:24 UTC (rev 284336)
@@ -3182,8 +3182,11 @@
     if (is<RenderSVGRoot>(renderer()))
         return false;
 
+    if (!is<RenderBox>(renderer()))
+        return false;
+
     // It's not clear that this geometry is correct: https://github.com/w3c/csswg-drafts/issues/5786
-    auto rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, { UseLocalClipRectIfPossible });
+    auto clipPathObjectBoundingBox = computeReferenceRectFromBox(downcast<RenderBox>(renderer()), CSSBoxType::BorderBox, offsetFromRoot);
 
     auto& style = renderer().style();
     LayoutSize paintingOffsetFromRoot = LayoutSize(snapSizeToDevicePixel(offsetFromRoot + paintingInfo.subpixelOffset, LayoutPoint(), renderer().document().deviceScaleFactor()));
@@ -3190,7 +3193,8 @@
     ASSERT(style.clipPath());
     if (is<ShapeClipPathOperation>(*style.clipPath()) || (is<BoxClipPathOperation>(*style.clipPath()) && is<RenderBox>(renderer()))) {
         WindRule windRule;
-        Path path = computeClipPath(paintingOffsetFromRoot, rootRelativeBounds, windRule);
+        // FIXME: Should probably pixel-snap clipPathObjectBoundingBox here.
+        Path path = computeClipPath(paintingOffsetFromRoot, clipPathObjectBoundingBox, windRule);
         context.save();
         context.clipPath(path, windRule);
         return true;
@@ -3200,11 +3204,15 @@
         auto& referenceClipPathOperation = downcast<ReferenceClipPathOperation>(*style.clipPath());
         if (auto* clipperRenderer = renderer().ensureReferencedSVGResources().referencedClipperRenderer(renderer().document(), referenceClipPathOperation)) {
             context.save();
-            auto referenceBox = snapRectToDevicePixels(rootRelativeBounds, renderer().document().deviceScaleFactor());
+            auto referenceBox = snapRectToDevicePixels(clipPathObjectBoundingBox, renderer().document().deviceScaleFactor());
             auto offset = referenceBox.location();
+
+            auto clippedContentBounds = FloatRect(calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, { UseLocalClipRectIfPossible }));
+            clippedContentBounds.moveBy(-offset);
+
             context.translate(offset);
-            FloatRect svgReferenceBox { {}, referenceBox.size() };
-            clipperRenderer->applyClippingToContext(context, renderer(), svgReferenceBox, renderer().style().effectiveZoom());
+            FloatRect clipPathReferenceBox { { }, referenceBox.size() };
+            clipperRenderer->applyClippingToContext(context, renderer(), clipPathReferenceBox, clippedContentBounds, renderer().style().effectiveZoom());
             context.translate(-offset);
             return true;
         }

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp (284335 => 284336)


--- trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp	2021-10-17 16:38:27 UTC (rev 284335)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp	2021-10-17 16:54:24 UTC (rev 284336)
@@ -79,7 +79,8 @@
     if (repaintRect.isEmpty())
         return true;
 
-    return applyClippingToContext(*context, renderer, renderer.objectBoundingBox());
+    auto boundingBox = renderer.objectBoundingBox();
+    return applyClippingToContext(*context, renderer, boundingBox, boundingBox);
 }
 
 bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext& context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox, float effectiveZoom)
@@ -139,11 +140,11 @@
     return true;
 }
 
-bool RenderSVGResourceClipper::applyClippingToContext(GraphicsContext& context, RenderElement& renderer, const FloatRect& objectBoundingBox, float effectiveZoom)
+bool RenderSVGResourceClipper::applyClippingToContext(GraphicsContext& context, RenderElement& renderer, const FloatRect& objectBoundingBox, const FloatRect& clippedContentBounds, float effectiveZoom)
 {
     ClipperData& clipperData = addRendererToClipper(renderer);
     
-    LOG_WITH_STREAM(SVG, stream << "RenderSVGResourceClipper " << this << " applyClippingToContext: renderer " << &renderer << " objectBoundingBox " << objectBoundingBox << " (existing image buffer " << clipperData.imageBuffer.get() << ")");
+    LOG_WITH_STREAM(SVG, stream << "RenderSVGResourceClipper " << this << " applyClippingToContext: renderer " << &renderer << " objectBoundingBox " << objectBoundingBox << " clippedContentBounds " << clippedContentBounds << " (existing image buffer " << clipperData.imageBuffer.get() << ")");
 
     AffineTransform animatedLocalTransform = clipPathElement().animatedLocalTransform();
 
@@ -151,13 +152,13 @@
         return true;
 
     AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer);
-    if (!clipperData.isValidForGeometry(objectBoundingBox, absoluteTransform)) {
+    if (!clipperData.isValidForGeometry(objectBoundingBox, clippedContentBounds, absoluteTransform)) {
         // FIXME (149469): This image buffer should not be unconditionally unaccelerated. Making it match the context breaks nested clipping, though.
-        auto maskImage = SVGRenderingContext::createImageBuffer(objectBoundingBox, absoluteTransform, DestinationColorSpace::SRGB(), RenderingMode::Unaccelerated, &context);
+        auto maskImage = SVGRenderingContext::createImageBuffer(clippedContentBounds, absoluteTransform, DestinationColorSpace::SRGB(), RenderingMode::Unaccelerated, &context);
         if (!maskImage)
             return false;
 
-        clipperData = { WTFMove(maskImage), objectBoundingBox, absoluteTransform };
+        clipperData = { WTFMove(maskImage), objectBoundingBox, clippedContentBounds, absoluteTransform };
 
         GraphicsContext& maskContext = clipperData.imageBuffer->context();
         maskContext.concatCTM(animatedLocalTransform);
@@ -169,7 +170,7 @@
         if (resources && (clipper = resources->clipper())) {
             GraphicsContextStateSaver stateSaver(maskContext);
 
-            if (!clipper->applyClippingToContext(maskContext, *this, objectBoundingBox))
+            if (!clipper->applyClippingToContext(maskContext, *this, objectBoundingBox, clippedContentBounds))
                 return false;
 
             succeeded = drawContentIntoMaskImage(*clipperData.imageBuffer, objectBoundingBox, effectiveZoom);
@@ -184,7 +185,7 @@
     if (!clipperData.imageBuffer)
         return false;
 
-    SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, objectBoundingBox, clipperData.imageBuffer, true);
+    SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, clippedContentBounds, clipperData.imageBuffer, true);
     return true;
 }
 

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.h (284335 => 284336)


--- trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.h	2021-10-17 16:38:27 UTC (rev 284335)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGResourceClipper.h	2021-10-17 16:54:24 UTC (rev 284336)
@@ -45,7 +45,9 @@
     // clipPath can be clipped too, but don't have a boundingBox or repaintRect. So we can't call
     // applyResource directly and use the rects from the object, since they are empty for RenderSVGResources
     // FIXME: We made applyClippingToContext public because we cannot call applyResource on HTML elements (it asserts on RenderObject::objectBoundingBox)
-    bool applyClippingToContext(GraphicsContext&, RenderElement&, const FloatRect&, float effectiveZoom = 1);
+    // objectBoundingBox ia used to compute clip path geometry when clipPathUnits="objectBoundingBox".
+    // clippedContentBounds is the bounds of the content to which clipping is being applied.
+    bool applyClippingToContext(GraphicsContext&, RenderElement&, const FloatRect& objectBoundingBox, const FloatRect& clippedContentBounds, float effectiveZoom = 1);
     FloatRect resourceBoundingBox(const RenderObject&) override;
 
     RenderSVGResourceType resourceType() const override { return ClipperResourceType; }
@@ -59,20 +61,22 @@
 
     struct ClipperData {
         FloatRect objectBoundingBox;
+        FloatRect clippedContentBounds;
         AffineTransform absoluteTransform;
         RefPtr<ImageBuffer> imageBuffer;
         
         ClipperData() = default;
-        ClipperData(RefPtr<ImageBuffer>&& buffer, const FloatRect& boundingBox, const AffineTransform& transform)
+        ClipperData(RefPtr<ImageBuffer>&& buffer, const FloatRect& boundingBox, const FloatRect& clippedBounds, const AffineTransform& transform)
             : objectBoundingBox(boundingBox)
+            , clippedContentBounds(clippedBounds)
             , absoluteTransform(transform)
             , imageBuffer(WTFMove(buffer))
         {
         }
 
-        bool isValidForGeometry(const FloatRect& boundingBox, const AffineTransform& transform) const
+        bool isValidForGeometry(const FloatRect& boundingBox, const FloatRect& clippedBounds, const AffineTransform& transform) const
         {
-            return imageBuffer && objectBoundingBox == boundingBox && absoluteTransform == transform;
+            return imageBuffer && objectBoundingBox == boundingBox && clippedContentBounds == clippedBounds && absoluteTransform == transform;
         }
     };
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to