border-radius fails to clip iframe contents
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Apr 2020 14:39:02 +0000 (14:39 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Apr 2020 14:39:02 +0000 (14:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=211199
Source/WebCore:

<rdar://problem/61945671>

Reviewed by Zalan Bujtas.

iframes need to use the same composited clipping strategy that we use for other
replaced elements with composited contents, like video and WebGL. To achieve this,
change GraphicsLayer to allow child GraphicsLayers to be parented in the contents
clipping layer, just like content layers are. (We don't want to do this unconditionally,
because it will change behavior for video with controls.)

Add GraphicsLayer::contentsRectClipsDescendants(), and used it to run code that
creates the contents clipping (and optional shape) layers even when no contents
layer is present. Fix up the sublayer list building to parent layers from
children in the contents clipping layer.

Tests: compositing/iframes/border-radius-composited-frame.html
       compositing/iframes/border-uneven-radius-composited-frame.html

* platform/graphics/GraphicsLayer.cpp:
(WebCore::GraphicsLayer::GraphicsLayer):
* platform/graphics/GraphicsLayer.h:
(WebCore::GraphicsLayer::contentsRectClipsDescendants const):
(WebCore::GraphicsLayer::setContentsRectClipsDescendants):
* platform/graphics/ca/GraphicsLayerCA.cpp:
(WebCore::GraphicsLayerCA::setContentsRectClipsDescendants):
(WebCore::GraphicsLayerCA::updateSublayerList):
(WebCore::GraphicsLayerCA::updateContentsRects):
(WebCore::GraphicsLayerCA::createTransformAnimationsFromKeyframes):
* platform/graphics/ca/GraphicsLayerCA.h:
* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateConfiguration):
* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::isCompositedSubframeRenderer):
* rendering/RenderLayerCompositor.h:

LayoutTests:

Reviewed by Zalan Bujtas.

* compositing/iframes/border-radius-composited-frame-expected.html: Added.
* compositing/iframes/border-radius-composited-frame.html: Added.
* compositing/iframes/border-uneven-radius-composited-frame-expected.html: Added.
* compositing/iframes/border-uneven-radius-composited-frame.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@260950 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/compositing/iframes/border-radius-composited-frame-expected.html [new file with mode: 0644]
LayoutTests/compositing/iframes/border-radius-composited-frame.html [new file with mode: 0644]
LayoutTests/compositing/iframes/border-uneven-radius-composited-frame-expected.html [new file with mode: 0644]
LayoutTests/compositing/iframes/border-uneven-radius-composited-frame.html [new file with mode: 0644]
LayoutTests/platform/mac-wk1/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/GraphicsLayer.cpp
Source/WebCore/platform/graphics/GraphicsLayer.h
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h
Source/WebCore/rendering/RenderLayerBacking.cpp
Source/WebCore/rendering/RenderLayerCompositor.cpp
Source/WebCore/rendering/RenderLayerCompositor.h

index bb4113b..e844fcf 100644 (file)
@@ -1,3 +1,15 @@
+2020-04-30  Simon Fraser  <simon.fraser@apple.com>
+
+        border-radius fails to clip iframe contents
+        https://bugs.webkit.org/show_bug.cgi?id=211199
+
+        Reviewed by Zalan Bujtas.
+
+        * compositing/iframes/border-radius-composited-frame-expected.html: Added.
+        * compositing/iframes/border-radius-composited-frame.html: Added.
+        * compositing/iframes/border-uneven-radius-composited-frame-expected.html: Added.
+        * compositing/iframes/border-uneven-radius-composited-frame.html: Added.
+
 2020-04-30  Philippe Normand  <pnormand@igalia.com>
 
         [SOUP] http/tests/media/video-accept-encoding.html fails
diff --git a/LayoutTests/compositing/iframes/border-radius-composited-frame-expected.html b/LayoutTests/compositing/iframes/border-radius-composited-frame-expected.html
new file mode 100644 (file)
index 0000000..361e451
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .wrapper {
+            height: 400px;
+            width: 400px;
+            border: 10px solid black;
+            box-sizing: border-box;
+            border-radius: 50%;
+            overflow: hidden;
+            transform: translateZ(0);
+        }
+        
+        .composited {
+            transform: translateZ(0);
+        }
+        
+        .box {
+            width: 100%;
+            height: 100%;
+            background-color: silver;
+        }
+    </style>
+</head>
+<body>
+    <div class="wrapper">
+        <div class="composited box">asd
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/compositing/iframes/border-radius-composited-frame.html b/LayoutTests/compositing/iframes/border-radius-composited-frame.html
new file mode 100644 (file)
index 0000000..fbd74d3
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        iframe {
+            height: 400px;
+            width: 400px;
+            border: 10px solid black;
+            box-sizing: border-box;
+            border-radius: 50%;
+        }
+    </style>
+</head>
+<body>
+    <iframe srcdoc="
+       <style>
+        body {
+            background-color: silver;
+            margin: 0;
+                   transform: translateZ(0);
+        }
+       </style>
+    "></iframe>
+</body>
+</html>
diff --git a/LayoutTests/compositing/iframes/border-uneven-radius-composited-frame-expected.html b/LayoutTests/compositing/iframes/border-uneven-radius-composited-frame-expected.html
new file mode 100644 (file)
index 0000000..74afd83
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        .wrapper {
+            height: 400px;
+            width: 400px;
+            border: 10px solid black;
+            box-sizing: border-box;
+            border-top-left-radius: 30%;
+            overflow: hidden;
+            transform: translateZ(0);
+        }
+        
+        .composited {
+            transform: translateZ(0);
+        }
+        
+        .box {
+            width: 100%;
+            height: 100%;
+            background-color: silver;
+        }
+    </style>
+</head>
+<body>
+    <div class="wrapper">
+        <div class="composited box">asd
+        </div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/compositing/iframes/border-uneven-radius-composited-frame.html b/LayoutTests/compositing/iframes/border-uneven-radius-composited-frame.html
new file mode 100644 (file)
index 0000000..549496e
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        iframe {
+            height: 400px;
+            width: 400px;
+            border: 10px solid black;
+            box-sizing: border-box;
+            border-top-left-radius: 30%;
+        }
+    </style>
+</head>
+<body>
+    <iframe srcdoc="
+       <style>
+        body {
+            background-color: silver;
+            margin: 0;
+                   transform: translateZ(0);
+        }
+       </style>
+    "></iframe>
+</body>
+</html>
index f35c7ea..86de91e 100644 (file)
@@ -389,6 +389,7 @@ fast/scrolling/iframe-scrollable-after-back.html [ Skip ]
 fast/scrolling/overflow-scrollable-after-back.html [ Skip ]
 
 compositing/rtl/rtl-fixed-overflow-scrolled.html [ Failure ]
+compositing/iframes/border-uneven-radius-composited-frame.html [ ImageOnlyFailure ]
 compositing/iframes/overlapped-nested-iframes.html [ Pass Failure ]
 compositing/clipping/border-radius-async-overflow-non-stacking.html [ ImageOnlyFailure ]
 compositing/visibility/visibility-change-in-subframe.html [ Failure ]
index edbda7a..f8bf7a1 100644 (file)
@@ -1,3 +1,42 @@
+2020-04-30  Simon Fraser  <simon.fraser@apple.com>
+
+        border-radius fails to clip iframe contents
+        https://bugs.webkit.org/show_bug.cgi?id=211199
+        <rdar://problem/61945671>
+
+        Reviewed by Zalan Bujtas.
+
+        iframes need to use the same composited clipping strategy that we use for other
+        replaced elements with composited contents, like video and WebGL. To achieve this,
+        change GraphicsLayer to allow child GraphicsLayers to be parented in the contents
+        clipping layer, just like content layers are. (We don't want to do this unconditionally,
+        because it will change behavior for video with controls.)
+
+        Add GraphicsLayer::contentsRectClipsDescendants(), and used it to run code that
+        creates the contents clipping (and optional shape) layers even when no contents
+        layer is present. Fix up the sublayer list building to parent layers from
+        children in the contents clipping layer.
+
+        Tests: compositing/iframes/border-radius-composited-frame.html
+               compositing/iframes/border-uneven-radius-composited-frame.html
+
+        * platform/graphics/GraphicsLayer.cpp:
+        (WebCore::GraphicsLayer::GraphicsLayer):
+        * platform/graphics/GraphicsLayer.h:
+        (WebCore::GraphicsLayer::contentsRectClipsDescendants const):
+        (WebCore::GraphicsLayer::setContentsRectClipsDescendants):
+        * platform/graphics/ca/GraphicsLayerCA.cpp:
+        (WebCore::GraphicsLayerCA::setContentsRectClipsDescendants):
+        (WebCore::GraphicsLayerCA::updateSublayerList):
+        (WebCore::GraphicsLayerCA::updateContentsRects):
+        (WebCore::GraphicsLayerCA::createTransformAnimationsFromKeyframes):
+        * platform/graphics/ca/GraphicsLayerCA.h:
+        * rendering/RenderLayerBacking.cpp:
+        (WebCore::RenderLayerBacking::updateConfiguration):
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::RenderLayerCompositor::isCompositedSubframeRenderer):
+        * rendering/RenderLayerCompositor.h:
+
 2020-04-30  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][TFC] Adjust the available vertical space with the row span for cell layout
index 0285b14..af87c18 100644 (file)
@@ -135,6 +135,7 @@ GraphicsLayer::GraphicsLayer(Type type, GraphicsLayerClient& layerClient)
     , m_masksToBounds(false)
     , m_drawsContent(false)
     , m_contentsVisible(true)
+    , m_contentsRectClipsDescendants(false)
     , m_acceleratesDrawing(false)
     , m_usesDisplayListDrawing(false)
     , m_appliesPageScale(false)
index f0adeae..3bb5051 100644 (file)
@@ -451,6 +451,10 @@ public:
     // Set a rounded rect that will be used to clip the layer contents.
     FloatRoundedRect contentsClippingRect() const { return m_contentsClippingRect; }
     virtual void setContentsClippingRect(const FloatRoundedRect& roundedRect) { m_contentsClippingRect = roundedRect; }
+    
+    // If true, contentsClippingRect is used to clip child GraphicsLayers.
+    bool contentsRectClipsDescendants() const { return m_contentsRectClipsDescendants; }
+    virtual void setContentsRectClipsDescendants(bool b) { m_contentsRectClipsDescendants = b; }
 
     // Set a rounded rect that is used to clip this layer and its descendants (implies setting masksToBounds).
     // Returns false if the platform can't support this rounded clip, and we should fall back to painting a mask.
@@ -711,6 +715,7 @@ protected:
     bool m_masksToBounds : 1;
     bool m_drawsContent : 1;
     bool m_contentsVisible : 1;
+    bool m_contentsRectClipsDescendants : 1;
     bool m_acceleratesDrawing : 1;
     bool m_usesDisplayListDrawing : 1;
     bool m_appliesPageScale : 1; // Set for the layer which has the page scale applied to it.
index 060e762..0683a3f 100644 (file)
@@ -967,6 +967,15 @@ void GraphicsLayerCA::setContentsClippingRect(const FloatRoundedRect& rect)
     noteLayerPropertyChanged(ContentsRectsChanged);
 }
 
+void GraphicsLayerCA::setContentsRectClipsDescendants(bool contentsRectClipsDescendants)
+{
+    if (contentsRectClipsDescendants == m_contentsRectClipsDescendants)
+        return;
+
+    GraphicsLayer::setContentsRectClipsDescendants(contentsRectClipsDescendants);
+    noteLayerPropertyChanged(ChildrenChanged | ContentsRectsChanged);
+}
+
 bool GraphicsLayerCA::setMasksToBoundsRect(const FloatRoundedRect& roundedRect)
 {
     if (roundedRect == m_masksToBoundsRect)
@@ -2019,19 +2028,31 @@ void GraphicsLayerCA::updateSublayerList(bool maxLayerDepthReached)
 #endif
     };
 
+    auto buildChildLayerList = [&](PlatformCALayerList& list) {
+        appendLayersFromChildren(list);
+        appendDebugLayers(list);
+    };
+
     PlatformCALayerList primaryLayerChildren;
     appendCustomAndClippingLayers(primaryLayerChildren);
 
+    bool clippingLayerHostsChildren = m_contentsRectClipsDescendants && m_contentsClippingLayer;
+    if (clippingLayerHostsChildren) {
+        PlatformCALayerList clippingChildren;
+        buildChildLayerList(clippingChildren);
+        m_contentsClippingLayer->setSublayers(clippingChildren);
+    }
+
     if (m_structuralLayer) {
         PlatformCALayerList layerList;
         appendStructuralLayerChildren(layerList);
-        appendLayersFromChildren(layerList);
-        appendDebugLayers(layerList);
+
+        if (!clippingLayerHostsChildren)
+            buildChildLayerList(layerList);
+
         m_structuralLayer->setSublayers(layerList);
-    } else {
-        appendLayersFromChildren(primaryLayerChildren);
-        appendDebugLayers(primaryLayerChildren);
-    }
+    } else if (!clippingLayerHostsChildren)
+        buildChildLayerList(primaryLayerChildren);
 
     m_layer->setSublayers(primaryLayerChildren);
 }
@@ -2677,7 +2698,7 @@ void GraphicsLayerCA::updateClippingStrategy(PlatformCALayer& clippingLayer, Ref
 
 void GraphicsLayerCA::updateContentsRects()
 {
-    if (!m_contentsLayer)
+    if (!m_contentsLayer && !m_contentsRectClipsDescendants)
         return;
 
     auto contentBounds = FloatRect { { }, m_contentsRect.size() };
@@ -2700,13 +2721,14 @@ void GraphicsLayerCA::updateContentsRects()
         
         updateClippingStrategy(*m_contentsClippingLayer, m_contentsShapeMaskLayer, m_contentsClippingRect);
 
-        if (gainedOrLostClippingLayer) {
+        if (m_contentsLayer && gainedOrLostClippingLayer) {
             m_contentsLayer->removeFromSuperlayer();
             m_contentsClippingLayer->appendSublayer(*m_contentsLayer);
         }
     } else {
         if (m_contentsClippingLayer) {
-            m_contentsLayer->removeFromSuperlayer();
+            if (m_contentsLayer)
+                m_contentsLayer->removeFromSuperlayer();
 
             m_contentsClippingLayer->removeFromSuperlayer();
             m_contentsClippingLayer->setOwner(nullptr);
@@ -2724,8 +2746,10 @@ void GraphicsLayerCA::updateContentsRects()
     if (gainedOrLostClippingLayer)
         noteSublayersChanged(DontScheduleFlush);
 
-    m_contentsLayer->setPosition(m_contentsRect.location());
-    m_contentsLayer->setBounds(contentBounds);
+    if (m_contentsLayer) {
+        m_contentsLayer->setPosition(m_contentsRect.location());
+        m_contentsLayer->setBounds(contentBounds);
+    }
 
     if (m_layerClones) {
         for (auto& layer : m_layerClones->contentsLayerClones.values()) {
@@ -3615,7 +3639,13 @@ void GraphicsLayerCA::resumeAnimations()
 
 PlatformCALayer* GraphicsLayerCA::hostLayerForSublayers() const
 {
-    return m_structuralLayer.get() ? m_structuralLayer.get() : m_layer.get(); 
+    if (contentsRectClipsDescendants() && m_contentsClippingLayer)
+        return m_contentsClippingLayer.get();
+
+    if (m_structuralLayer)
+        return m_structuralLayer.get();
+
+    return m_layer.get();
 }
 
 PlatformCALayer* GraphicsLayerCA::layerForSuperlayer() const
index 724345e..0d0c46c 100644 (file)
@@ -120,6 +120,7 @@ public:
     
     WEBCORE_EXPORT void setContentsRect(const FloatRect&) override;
     WEBCORE_EXPORT void setContentsClippingRect(const FloatRoundedRect&) override;
+    WEBCORE_EXPORT void setContentsRectClipsDescendants(bool) override;
     WEBCORE_EXPORT bool setMasksToBoundsRect(const FloatRoundedRect&) override;
 
     WEBCORE_EXPORT void setShapeLayerPath(const Path&) override;
index 0183011..d2c3d91 100644 (file)
@@ -967,6 +967,11 @@ bool RenderLayerBacking::updateConfiguration(const RenderLayer* compositingAnces
         layerConfigChanged = true;
     }
 
+    if (RenderLayerCompositor::isCompositedSubframeRenderer(renderer())) {
+        m_graphicsLayer->setContentsRectClipsDescendants(true);
+        updateContentsRects();
+    }
+
     if (is<RenderImage>(renderer()) && downcast<RenderImage>(renderer()).isEditableImage()) {
         auto element = renderer().element();
         if (is<HTMLImageElement>(element)) {
index bf0512f..102e29f 100644 (file)
@@ -2823,6 +2823,14 @@ ScrollingNodeID RenderLayerCompositor::asyncScrollableContainerNodeID(const Rend
     return containerScrollingNodeID;
 }
 
+bool RenderLayerCompositor::isCompositedSubframeRenderer(const RenderObject& renderer)
+{
+    if (!is<RenderWidget>(renderer))
+        return false;
+
+    return downcast<RenderWidget>(renderer).requiresAcceleratedCompositing();
+}
+
 // Return true if the given layer is a stacking context and has compositing child
 // layers that it needs to clip. In this case we insert a clipping GraphicsLayer
 // into the hierarchy between this layer and its children in the z-order hierarchy.
index 200113a..7b2bbb5 100644 (file)
@@ -295,6 +295,7 @@ public:
     // to know if there is non-affine content, e.g. for drawing into an image.
     bool has3DContent() const;
     
+    static bool isCompositedSubframeRenderer(const RenderObject&);
     static RenderLayerCompositor* frameContentsCompositor(RenderWidget&);
     // Return true if the layers changed.
     bool parentFrameContentLayers(RenderWidget&);