Compositing layer that renders two positioned elements should not hit test
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Mar 2019 14:45:31 +0000 (14:45 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Mar 2019 14:45:31 +0000 (14:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195371
<rdar://problem/48649586>

Reviewed by Simon Fraser.

Source/WebCore:

Compute and pass an event region for layers if it differs from layer bounds.

This patch fixes various block overflow and layer expansion cases. It does not handle
overflowing line boxes yet (it adds tests for those too).

Test: fast/scrolling/ios/overflow-scroll-overlap-2.html

* platform/graphics/GraphicsLayer.cpp:
(WebCore::GraphicsLayer::setEventRegion):
* platform/graphics/GraphicsLayer.h:
(WebCore::GraphicsLayer::eventRegion):
* platform/graphics/Region.h:
* platform/graphics/ca/GraphicsLayerCA.cpp:
(WebCore::GraphicsLayerCA::setEventRegion):
(WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers):
(WebCore::GraphicsLayerCA::updateEventRegion):

Pass the region via the main platform layer of the graphics layer.

* platform/graphics/ca/GraphicsLayerCA.h:
* platform/graphics/ca/PlatformCALayer.h:
* platform/graphics/ca/cocoa/PlatformCALayerCocoa.h:
* rendering/PaintInfo.h:
* rendering/PaintPhase.h:

Add EventRegion paint phase that computes the region instead of painting anything.

* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::paintObject):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::paintForegroundForFragments):
(WebCore::RenderLayer::paintForegroundForFragmentsWithPhase):

Invoke EventRegion paint phase.

* rendering/RenderLayer.h:
* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::paintIntoLayer):

Request event region when pointing a layer.

Source/WebKit:

* Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm:
(WebKit::RemoteLayerTreePropertyApplier::applyProperties):
* Shared/RemoteLayerTree/RemoteLayerTreeTransaction.h:
* Shared/RemoteLayerTree/RemoteLayerTreeTransaction.mm:
(WebKit::RemoteLayerTreeTransaction::LayerProperties::LayerProperties):
(WebKit::RemoteLayerTreeTransaction::LayerProperties::encode const):
(WebKit::RemoteLayerTreeTransaction::LayerProperties::decode):

Pass event region to UI process.

* UIProcess/RemoteLayerTree/RemoteLayerTreeNode.h:
(WebKit::RemoteLayerTreeNode::layerID const):
(WebKit::RemoteLayerTreeNode::eventRegion const):
* UIProcess/RemoteLayerTree/RemoteLayerTreeNode.mm:
(WebKit::RemoteLayerTreeNode::RemoteLayerTreeNode):
(WebKit::RemoteLayerTreeNode::~RemoteLayerTreeNode):
(WebKit::RemoteLayerTreeNode::setEventRegion):

Maintain event region in RemoteLayerTreeNodes.

(WebKit::RemoteLayerTreeNode::initializeLayer):
(WebKit::RemoteLayerTreeNode::layerID):
(WebKit::RemoteLayerTreeNode::forCALayer):

Move layerID to RemoteLayerTreeNode and store RemoteLayerTreeNode pointer to CALayers instead.
This makes it easy to find the matching RemoteLayerTreeNode from a layer, globally.

(WebKit::RemoteLayerTreeNode::setLayerID): Deleted.
* UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
(WebKit::collectDescendantViewsAtPoint):

If we have event region, use it for hit testing.

(-[UIView _web_findDescendantViewAtPoint:withEvent:]):
(collectDescendantViewsAtPoint): Deleted.
* WebProcess/WebPage/RemoteLayerTree/PlatformCALayerRemote.cpp:
(WebKit::PlatformCALayerRemote::eventRegion const):
(WebKit::PlatformCALayerRemote::setEventRegion):
* WebProcess/WebPage/RemoteLayerTree/PlatformCALayerRemote.h:

LayoutTests:

* fast/scrolling/ios/overflow-scroll-overlap-2-expected.txt: Added.
* fast/scrolling/ios/overflow-scroll-overlap-2.html: Added.

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/scrolling/ios/overflow-scroll-overlap-2-expected.txt [new file with mode: 0644]
LayoutTests/fast/scrolling/ios/overflow-scroll-overlap-2.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/GraphicsLayer.cpp
Source/WebCore/platform/graphics/GraphicsLayer.h
Source/WebCore/platform/graphics/Region.h
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h
Source/WebCore/platform/graphics/ca/PlatformCALayer.h
Source/WebCore/platform/graphics/ca/cocoa/PlatformCALayerCocoa.h
Source/WebCore/platform/graphics/ca/win/PlatformCALayerWin.h
Source/WebCore/rendering/PaintInfo.h
Source/WebCore/rendering/PaintPhase.h
Source/WebCore/rendering/RenderBlock.cpp
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/RenderLayer.h
Source/WebCore/rendering/RenderLayerBacking.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm
Source/WebKit/Shared/RemoteLayerTree/RemoteLayerTreeTransaction.h
Source/WebKit/Shared/RemoteLayerTree/RemoteLayerTreeTransaction.mm
Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeNode.h
Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeNode.mm
Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm
Source/WebKit/WebProcess/WebPage/RemoteLayerTree/PlatformCALayerRemote.cpp
Source/WebKit/WebProcess/WebPage/RemoteLayerTree/PlatformCALayerRemote.h

index c110764..0c8e59e 100644 (file)
@@ -1,3 +1,14 @@
+2019-03-12  Antti Koivisto  <antti@apple.com>
+
+        Compositing layer that renders two positioned elements should not hit test
+        https://bugs.webkit.org/show_bug.cgi?id=195371
+        <rdar://problem/48649586>
+
+        Reviewed by Simon Fraser.
+
+        * fast/scrolling/ios/overflow-scroll-overlap-2-expected.txt: Added.
+        * fast/scrolling/ios/overflow-scroll-overlap-2.html: Added.
+
 2019-03-12  Enrique Ocaña González  <eocanha@igalia.com>
 
         [Media][MSE] Don't emit timeUpdate after play() if currentTime hasn't changed
diff --git a/LayoutTests/fast/scrolling/ios/overflow-scroll-overlap-2-expected.txt b/LayoutTests/fast/scrolling/ios/overflow-scroll-overlap-2-expected.txt
new file mode 100644 (file)
index 0000000..8f11fe5
--- /dev/null
@@ -0,0 +1,9 @@
+Test that scrollable areas with non-trivial overlap are correctly targeted.
+
+case 1: 
+case 2: Scrollable 2 
+case 3: Scrollable 3 
+case 4: Scrollable 4 
+case 5: Scrollable 5 
+case 6: 
+
diff --git a/LayoutTests/fast/scrolling/ios/overflow-scroll-overlap-2.html b/LayoutTests/fast/scrolling/ios/overflow-scroll-overlap-2.html
new file mode 100644 (file)
index 0000000..0c90611
--- /dev/null
@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+body {
+    touch-action: none;
+}
+.case {
+    width: 200px;
+    height: 200px;
+    display: inline-block;
+    position: relative;
+}
+.scrollcontent {
+    width: 500px;
+    height: 500px;
+    background: green;
+}
+
+.overflowscroll {
+    overflow: scroll;
+    height: 100px;
+    width: 100px;
+    position: absolute;
+    border: 2px solid black;
+}
+.overlapping {
+    position:absolute;
+    left: 25px;
+    top: 25px;
+    width: 100px;
+    height: 100px;
+    background: red;
+}
+.clip {
+    position:absolute;
+    width: 100px;
+    height: 100px;
+    overflow:hidden;
+}
+.large {
+    width: 3000px;
+    height: 150px;
+}
+#log {
+    position:relative;
+    white-space: pre;
+}
+</style>
+<script src="../../../resources/basic-gestures.js"></script>
+<script type="text/javascript">
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+    internals.settings.setAsyncFrameScrollingEnabled(true);
+    internals.settings.setAsyncOverflowScrollingEnabled(true);
+}
+
+function sleep(delay)
+{
+    return new Promise((resolve) => { setTimeout(resolve, delay); });
+}
+
+async function runTest() {
+    for (const scrollable of document.querySelectorAll('.overflowscroll')) {
+        scrollable.addEventListener('scroll', function(e) {
+            logScroll(e.target);
+        });
+    }
+
+    {
+        let i = 0;
+        for (const scrollcontent of document.querySelectorAll('.scrollcontent'))
+            scrollcontent.innerText = "Scrollable " + ++i;
+    }
+    {
+        let i = 0;
+        for (const overlapping of document.querySelectorAll('.overlapping'))
+            overlapping.insertBefore(document.createTextNode("Overlapping " + ++i), overlapping.firstChild);
+    }
+
+
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+
+    for (const testcase of document.querySelectorAll('.case'))
+        testcase.style.display = 'none';
+
+    {
+        let i = 0;
+        for (const testcase of document.querySelectorAll('.case')) {
+            ++i;
+            testcase.style.display = 'inline-block';
+            
+            const target = testcase.querySelector('.target');
+            const rect = target.getBoundingClientRect();
+            const centerX = (rect.left + rect.right) / 2;
+            const centerY = (rect.top + rect.bottom) / 2;
+            await touchAndDragFromPointToPoint(centerX, centerY, centerX, centerY - 30);
+            await liftUpAtPoint(centerX, centerY - 30);
+            await sleep(500);
+
+            testcase.style.display = 'none';
+            outputCase(i);
+        }
+   }
+
+    for (const testcase of document.querySelectorAll('.case'))
+        testcase.style.display = 'none';
+
+    testRunner.notifyDone();
+}
+
+const scrolledElements = new Set();
+
+function logScroll(element) {
+    if (scrolledElements.has(element))
+        return;
+    scrolledElements.add(element);
+}
+
+function outputCase(i) {
+    log.innerText += "case " + i + ": ";
+    for (const scrolled of scrolledElements)
+        log.innerText += scrolled.getElementsByClassName("scrollcontent")[0].innerText + " ";
+    log.innerText += "\n";
+    scrolledElements.clear();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>
+Test that scrollable areas with non-trivial overlap are correctly targeted.
+</p>
+<div class="case">
+    <div class="overflowscroll target">
+        <div class="scrollcontent"></div>
+    </div>
+    <div class="overlapping" style="left: 80px">
+        <div class="overlapping" style="left: -40px; top: -30px"></div>
+    </div>
+</div>
+
+<div class="case">
+    <div class="overflowscroll target">
+        <div class="scrollcontent"></div>
+    </div>
+    <div class="overlapping" style="box-shadow: 0px 0px 32px 32px blue; left: 60px; top: 60px">
+    </div>
+</div>
+
+<div class="case">
+    <div class="overflowscroll target">
+        <div class="scrollcontent"></div>
+    </div>
+    <div class="overlapping" style="pointer-events:none">
+    </div>
+</div>
+
+<div class="case">
+    <div class="overflowscroll target">
+        <div class="scrollcontent"></div>
+    </div>
+    <div class="overlapping" style="top:-60px">
+    Text text text text text text text text text text text text text text text text text text text text text text
+    </div>
+</div>
+
+<div class="case">
+    <div class="overflowscroll target">
+        <div class="scrollcontent"></div>
+    </div>
+    <div class="overlapping" style="top:-60px; padding-left:40px">
+    Text text text text text text text text text text text text text text text text text text text text text text
+    </div>
+</div>
+
+<div class="case">
+    <div class="overflowscroll target">
+        <div class="scrollcontent"></div>
+    </div>
+    <div class="overlapping" style="top:40px; left:40px; border-radius:50px; line-height:100px">
+    </div>
+</div>
+
+<div id=log></div>
+
+</body>
+</html>
index a94a359..9d3a500 100644 (file)
@@ -1,3 +1,52 @@
+2019-03-12  Antti Koivisto  <antti@apple.com>
+
+        Compositing layer that renders two positioned elements should not hit test
+        https://bugs.webkit.org/show_bug.cgi?id=195371
+        <rdar://problem/48649586>
+
+        Reviewed by Simon Fraser.
+
+        Compute and pass an event region for layers if it differs from layer bounds.
+
+        This patch fixes various block overflow and layer expansion cases. It does not handle
+        overflowing line boxes yet (it adds tests for those too).
+
+        Test: fast/scrolling/ios/overflow-scroll-overlap-2.html
+
+        * platform/graphics/GraphicsLayer.cpp:
+        (WebCore::GraphicsLayer::setEventRegion):
+        * platform/graphics/GraphicsLayer.h:
+        (WebCore::GraphicsLayer::eventRegion):
+        * platform/graphics/Region.h:
+        * platform/graphics/ca/GraphicsLayerCA.cpp:
+        (WebCore::GraphicsLayerCA::setEventRegion):
+        (WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers):
+        (WebCore::GraphicsLayerCA::updateEventRegion):
+
+        Pass the region via the main platform layer of the graphics layer.
+
+        * platform/graphics/ca/GraphicsLayerCA.h:
+        * platform/graphics/ca/PlatformCALayer.h:
+        * platform/graphics/ca/cocoa/PlatformCALayerCocoa.h:
+        * rendering/PaintInfo.h:
+        * rendering/PaintPhase.h:
+
+        Add EventRegion paint phase that computes the region instead of painting anything.
+
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::paintObject):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::paintForegroundForFragments):
+        (WebCore::RenderLayer::paintForegroundForFragmentsWithPhase):
+
+        Invoke EventRegion paint phase.
+
+        * rendering/RenderLayer.h:
+        * rendering/RenderLayerBacking.cpp:
+        (WebCore::RenderLayerBacking::paintIntoLayer):
+
+        Request event region when pointing a layer.
+
 2019-03-12  Philippe Normand  <pnormand@igalia.com>
 
         [GStreamer][v4l2] Synchronous video texture flushing support
index 6b5cf2d..f39f371 100644 (file)
@@ -31,6 +31,7 @@
 #include "FloatRect.h"
 #include "GraphicsContext.h"
 #include "LayoutRect.h"
+#include "Region.h"
 #include "RotateTransformOperation.h"
 #include <wtf/HashMap.h>
 #include <wtf/NeverDestroyed.h>
@@ -411,6 +412,11 @@ void GraphicsLayer::setShapeLayerWindRule(WindRule windRule)
 #endif
 }
 
+void GraphicsLayer::setEventRegion(std::unique_ptr<Region>&& eventRegion)
+{
+    m_eventRegion = WTFMove(eventRegion);
+}
+
 void GraphicsLayer::noteDeviceOrPageScaleFactorChangedIncludingDescendants()
 {
     deviceOrPageScaleFactorChanged();
index e8e241a..94a1956 100644 (file)
@@ -54,6 +54,7 @@ namespace WebCore {
 class GraphicsContext;
 class GraphicsLayerFactory;
 class Image;
+class Region;
 class TiledBacking;
 class TimingFunction;
 class TransformationMatrix;
@@ -452,6 +453,10 @@ public:
     WindRule shapeLayerWindRule() const;
     virtual void setShapeLayerWindRule(WindRule);
 
+    // Non-null if the event sensitive region of the layer differs from the layer bounds.
+    const Region* eventRegion() const { return m_eventRegion.get(); }
+    virtual void setEventRegion(std::unique_ptr<Region>&&);
+
     // Transitions are identified by a special animation name that cannot clash with a keyframe identifier.
     static String animationNameForTransition(AnimatedPropertyID);
 
@@ -726,6 +731,8 @@ protected:
     FloatRoundedRect m_backdropFiltersRect;
     Optional<FloatRect> m_animationExtent;
 
+    std::unique_ptr<Region> m_eventRegion;
+
 #if USE(CA)
     WindRule m_shapeLayerWindRule { WindRule::NonZero };
     Path m_shapeLayerPath;
index d72ec4b..b5b2db2 100644 (file)
@@ -53,10 +53,10 @@ public:
     // Returns true if the query region is a subset of this region.
     WEBCORE_EXPORT bool contains(const Region&) const;
 
-    bool contains(const IntPoint&) const;
+    WEBCORE_EXPORT bool contains(const IntPoint&) const;
 
     // Returns true if the query region intersects any part of this region.
-    bool intersects(const Region&) const;
+    WEBCORE_EXPORT bool intersects(const Region&) const;
 
     WEBCORE_EXPORT unsigned totalArea() const;
 
index c593900..a91ef00 100644 (file)
@@ -39,6 +39,7 @@
 #include "PlatformCAFilters.h"
 #include "PlatformCALayer.h"
 #include "PlatformScreen.h"
+#include "Region.h"
 #include "RotateTransformOperation.h"
 #include "ScaleTransformOperation.h"
 #include "TiledBacking.h"
@@ -51,6 +52,7 @@
 #include <wtf/HexNumber.h>
 #include <wtf/MathExtras.h>
 #include <wtf/NeverDestroyed.h>
+#include <wtf/PointerComparison.h>
 #include <wtf/SetForScope.h>
 #include <wtf/SystemTracing.h>
 #include <wtf/text/StringConcatenateNumbers.h>
@@ -982,6 +984,15 @@ void GraphicsLayerCA::setShapeLayerWindRule(WindRule windRule)
     noteLayerPropertyChanged(WindRuleChanged);
 }
 
+void GraphicsLayerCA::setEventRegion(std::unique_ptr<Region>&& eventRegion)
+{
+    if (arePointingToEqualData(eventRegion, m_eventRegion))
+        return;
+
+    GraphicsLayer::setEventRegion(WTFMove(eventRegion));
+    noteLayerPropertyChanged(EventRegionChanged, DontScheduleFlush);
+}
+
 bool GraphicsLayerCA::shouldRepaintOnSizeChange() const
 {
     return drawsContent() && !tiledBacking();
@@ -1844,6 +1855,9 @@ void GraphicsLayerCA::commitLayerChangesBeforeSublayers(CommitState& commitState
     if (m_uncommittedChanges & MasksToBoundsRectChanged) // Needs to happen before ChildrenChanged
         updateMasksToBoundsRect();
 
+    if (m_uncommittedChanges & EventRegionChanged)
+        updateEventRegion();
+
     if (m_uncommittedChanges & MaskLayerChanged) {
         updateMaskLayer();
         // If the mask layer becomes tiled it can set this flag again. Clear the flag so that
@@ -1853,7 +1867,7 @@ void GraphicsLayerCA::commitLayerChangesBeforeSublayers(CommitState& commitState
 
     if (m_uncommittedChanges & ContentsNeedsDisplay)
         updateContentsNeedsDisplay();
-    
+
     if (m_uncommittedChanges & SupportsSubpixelAntialiasedTextChanged)
         updateSupportsSubpixelAntialiasedText();
 
@@ -2755,6 +2769,11 @@ void GraphicsLayerCA::updateMasksToBoundsRect()
     }
 }
 
+void GraphicsLayerCA::updateEventRegion()
+{
+    m_layer->setEventRegion(eventRegion());
+}
+
 void GraphicsLayerCA::updateMaskLayer()
 {
     PlatformCALayer* maskCALayer = m_maskLayer ? downcast<GraphicsLayerCA>(*m_maskLayer).primaryLayer() : nullptr;
index cc92f58..8886b37 100644 (file)
@@ -124,6 +124,8 @@ public:
     WEBCORE_EXPORT void setShapeLayerPath(const Path&) override;
     WEBCORE_EXPORT void setShapeLayerWindRule(WindRule) override;
 
+    WEBCORE_EXPORT void setEventRegion(std::unique_ptr<Region>&&) override;
+
     WEBCORE_EXPORT void suspendAnimations(MonotonicTime) override;
     WEBCORE_EXPORT void resumeAnimations() override;
 
@@ -413,6 +415,7 @@ private:
     void updateContentsColorLayer();
     void updateContentsRects();
     void updateMasksToBoundsRect();
+    void updateEventRegion();
     void updateMaskLayer();
     void updateReplicatedLayers();
 
@@ -531,6 +534,7 @@ private:
         WindRuleChanged                         = 1LLU << 37,
         UserInteractionEnabledChanged           = 1LLU << 38,
         NeedsComputeVisibleAndCoverageRect      = 1LLU << 39,
+        EventRegionChanged                      = 1LLU << 40,
     };
     typedef uint64_t LayerChangeFlags;
     void addUncommittedChanges(LayerChangeFlags);
index 2b06149..3486c1d 100644 (file)
@@ -233,6 +233,9 @@ public:
 
     virtual WindRule shapeWindRule() const = 0;
     virtual void setShapeWindRule(WindRule) = 0;
+
+    virtual const Region* eventRegion() const = 0;
+    virtual void setEventRegion(const Region*) = 0;
     
     virtual GraphicsLayer::CustomAppearance customAppearance() const = 0;
     virtual void updateCustomAppearance(GraphicsLayer::CustomAppearance) = 0;
index 9f65ec3..fd2dded 100644 (file)
@@ -168,6 +168,9 @@ public:
     GraphicsLayer::CustomAppearance customAppearance() const override { return m_customAppearance; }
     void updateCustomAppearance(GraphicsLayer::CustomAppearance) override;
 
+    const Region* eventRegion() const override { return nullptr; }
+    void setEventRegion(const Region*) override { }
+
     GraphicsLayer::EmbeddedViewID embeddedViewID() const override;
 
     TiledBacking* tiledBacking() override;
index eab577f..f09f7a9 100644 (file)
@@ -157,6 +157,9 @@ public:
     GraphicsLayer::CustomAppearance customAppearance() const override { return m_customAppearance; }
     void updateCustomAppearance(GraphicsLayer::CustomAppearance customAppearance) override { m_customAppearance = customAppearance; }
 
+    const Region* eventRegion() const override { return nullptr; }
+    void setEventRegion(const Region*) override { }
+
     GraphicsLayer::EmbeddedViewID embeddedViewID() const override;
 
     TiledBacking* tiledBacking() override;
index 4e1cfee..52f4165 100644 (file)
@@ -38,6 +38,7 @@
 namespace WebCore {
 
 class OverlapTestRequestClient;
+class Region;
 class RenderInline;
 class RenderLayer;
 class RenderLayerModelObject;
@@ -129,6 +130,7 @@ struct PaintInfo {
     const RenderLayerModelObject* paintContainer; // the layer object that originates the current painting
     bool requireSecurityOriginAccessForWidgets { false };
     const RenderLayer* m_enclosingSelfPaintingLayer { nullptr };
+    Region* eventRegion { nullptr }; // For PaintPhase::EventRegion.
 
 private:
     GraphicsContext* m_context;
index eb22d32..2e3c023 100644 (file)
@@ -49,7 +49,8 @@ enum class PaintPhase : uint8_t {
     CollapsedTableBorders,
     TextClip,
     Mask,
-    ClippingMask
+    ClippingMask,
+    EventRegion,
 };
 
 enum class PaintBehavior : uint16_t {
index 5d35202..aacf9a6 100644 (file)
@@ -1240,6 +1240,17 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffs
     if (paintInfo.paintRootBackgroundOnly())
         return;
 
+    if (paintPhase == PaintPhase::EventRegion) {
+        // FIXME: Handle inlines, lineboxes, SVG too.
+        // FIXME: Transforms?
+        if (style().pointerEvents() != PointerEvents::None)
+            paintInfo.eventRegion->unite(enclosingIntRect(LayoutRect(paintOffset, size())));
+
+        // No need to check descendants if we don't have overflow.
+        if (!hasVisualOverflow())
+            return;
+    }
+
     // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div).
     LayoutPoint scrolledOffset = paintOffset;
     scrolledOffset.moveBy(-scrollPosition());
index 4d1811c..a7e711a 100644 (file)
@@ -4746,6 +4746,8 @@ void RenderLayer::paintForegroundForFragments(const LayerFragments& layerFragmen
         paintForegroundForFragmentsWithPhase(PaintPhase::Float, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
         paintForegroundForFragmentsWithPhase(PaintPhase::Foreground, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
         paintForegroundForFragmentsWithPhase(PaintPhase::ChildOutlines, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
+        if (localPaintingInfo.eventRegion)
+            paintForegroundForFragmentsWithPhase(PaintPhase::EventRegion, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
     }
     
     if (shouldClip)
@@ -4767,6 +4769,8 @@ void RenderLayer::paintForegroundForFragmentsWithPhase(PaintPhase phase, const L
         PaintInfo paintInfo(context, fragment.foregroundRect.rect(), phase, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), this, localPaintingInfo.requireSecurityOriginAccessForWidgets);
         if (phase == PaintPhase::Foreground)
             paintInfo.overlapTestRequests = localPaintingInfo.overlapTestRequests;
+        if (phase == PaintPhase::EventRegion)
+            paintInfo.eventRegion = localPaintingInfo.eventRegion;
         renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
         
         if (shouldClip)
index 4580ce8..839569e 100644 (file)
@@ -66,6 +66,7 @@ class ClipRectsCache;
 class HitTestRequest;
 class HitTestResult;
 class HitTestingTransformState;
+class Region;
 class RenderFragmentedFlow;
 class RenderGeometryMap;
 class RenderLayerBacking;
@@ -905,6 +906,7 @@ private:
         OptionSet<PaintBehavior> paintBehavior;
         bool requireSecurityOriginAccessForWidgets;
         bool clipToDirtyRect { true };
+        Region* eventRegion { nullptr };
     };
 
     // Compute, cache and return clip rects computed with the given layer as the root.
index 778fda5..cf9426a 100644 (file)
@@ -51,6 +51,7 @@
 #include "PerformanceLoggingClient.h"
 #include "PluginViewBase.h"
 #include "ProgressTracker.h"
+#include "Region.h"
 #include "RenderFragmentContainer.h"
 #include "RenderFragmentedFlow.h"
 #include "RenderHTMLCanvas.h"
@@ -2573,10 +2574,22 @@ void RenderLayerBacking::paintIntoLayer(const GraphicsLayer* graphicsLayer, Grap
     if (m_owningLayer.isRenderViewLayer())
         renderer().view().frameView().willPaintContents(context, paintDirtyRect, paintingState);
 
-    // FIXME: GraphicsLayers need a way to split for RenderFragmentContainers.
     RenderLayer::LayerPaintingInfo paintingInfo(&m_owningLayer, paintDirtyRect, paintBehavior, -m_subpixelOffsetFromRenderer);
+
+    auto eventRegion = std::make_unique<Region>();
+    paintingInfo.eventRegion = eventRegion.get();
+
     m_owningLayer.paintLayerContents(context, paintingInfo, paintFlags);
 
+    paintingInfo.eventRegion = nullptr;
+    // Use null event region to indicate the entire layer is sensitive to events (the common case).
+    // FIXME: We could optimize Region so it doesn't use lots of memory if it contains a single rect only.
+    if (eventRegion->contains(roundedIntRect(compositedBounds())))
+        eventRegion = nullptr;
+    else
+        eventRegion->translate(roundedIntSize(contentOffsetInCompositingLayer()));
+    m_graphicsLayer->setEventRegion(WTFMove(eventRegion));
+
     if (m_owningLayer.containsDirtyOverlayScrollbars())
         m_owningLayer.paintLayerContents(context, paintingInfo, paintFlags | RenderLayer::PaintLayerPaintingOverlayScrollbars);
 
index 0e1dc4e..377a395 100644 (file)
@@ -1,3 +1,51 @@
+2019-03-12  Antti Koivisto  <antti@apple.com>
+
+        Compositing layer that renders two positioned elements should not hit test
+        https://bugs.webkit.org/show_bug.cgi?id=195371
+        <rdar://problem/48649586>
+
+        Reviewed by Simon Fraser.
+
+        * Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm:
+        (WebKit::RemoteLayerTreePropertyApplier::applyProperties):
+        * Shared/RemoteLayerTree/RemoteLayerTreeTransaction.h:
+        * Shared/RemoteLayerTree/RemoteLayerTreeTransaction.mm:
+        (WebKit::RemoteLayerTreeTransaction::LayerProperties::LayerProperties):
+        (WebKit::RemoteLayerTreeTransaction::LayerProperties::encode const):
+        (WebKit::RemoteLayerTreeTransaction::LayerProperties::decode):
+
+        Pass event region to UI process.
+
+        * UIProcess/RemoteLayerTree/RemoteLayerTreeNode.h:
+        (WebKit::RemoteLayerTreeNode::layerID const):
+        (WebKit::RemoteLayerTreeNode::eventRegion const):
+        * UIProcess/RemoteLayerTree/RemoteLayerTreeNode.mm:
+        (WebKit::RemoteLayerTreeNode::RemoteLayerTreeNode):
+        (WebKit::RemoteLayerTreeNode::~RemoteLayerTreeNode):
+        (WebKit::RemoteLayerTreeNode::setEventRegion):
+
+        Maintain event region in RemoteLayerTreeNodes.
+
+        (WebKit::RemoteLayerTreeNode::initializeLayer):
+        (WebKit::RemoteLayerTreeNode::layerID):
+        (WebKit::RemoteLayerTreeNode::forCALayer):
+
+        Move layerID to RemoteLayerTreeNode and store RemoteLayerTreeNode pointer to CALayers instead.
+        This makes it easy to find the matching RemoteLayerTreeNode from a layer, globally.
+
+        (WebKit::RemoteLayerTreeNode::setLayerID): Deleted.
+        * UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
+        (WebKit::collectDescendantViewsAtPoint):
+
+        If we have event region, use it for hit testing.
+
+        (-[UIView _web_findDescendantViewAtPoint:withEvent:]):
+        (collectDescendantViewsAtPoint): Deleted.
+        * WebProcess/WebPage/RemoteLayerTree/PlatformCALayerRemote.cpp:
+        (WebKit::PlatformCALayerRemote::eventRegion const):
+        (WebKit::PlatformCALayerRemote::setEventRegion):
+        * WebProcess/WebPage/RemoteLayerTree/PlatformCALayerRemote.h:
+
 2019-03-12  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [WPE][GTK] Load events may occur in unexpected order when JS redirects page before subresource load finishes
index cddbc00..0a79977 100644 (file)
@@ -261,6 +261,9 @@ void RemoteLayerTreePropertyApplier::applyProperties(RemoteLayerTreeNode& node,
     updateChildren(node, properties, relatedLayers);
     updateMask(node, properties, relatedLayers);
 
+    if (properties.changedProperties & RemoteLayerTreeTransaction::EventRegionChanged)
+        node.setEventRegion(properties.eventRegion ? std::make_unique<WebCore::Region>(*properties.eventRegion) : nullptr);
+
 #if PLATFORM(IOS_FAMILY)
     applyPropertiesToUIView(node.uiView(), properties, relatedLayers);
 #endif
index 54d99ca..2d37ecc 100644 (file)
@@ -92,6 +92,7 @@ public:
         EdgeAntialiasingMaskChanged     = 1LLU << 35,
         CustomAppearanceChanged         = 1LLU << 36,
         UserInteractionEnabledChanged   = 1LLU << 37,
+        EventRegionChanged              = 1LLU << 38,
     };
 
     struct LayerCreationProperties {
@@ -171,6 +172,7 @@ public:
         bool opaque;
         bool contentsHidden;
         bool userInteractionEnabled;
+        std::unique_ptr<WebCore::Region> eventRegion;
     };
 
     explicit RemoteLayerTreeTransaction();
index 6688a03..d99dddc 100644 (file)
@@ -32,6 +32,7 @@
 #import "WebCoreArgumentCoders.h"
 #import <QuartzCore/QuartzCore.h>
 #import <WebCore/LengthFunctions.h>
+#import <WebCore/Region.h>
 #import <WebCore/TimingFunction.h>
 #import <wtf/text/CString.h>
 #import <wtf/text/TextStream.h>
@@ -154,6 +155,10 @@ RemoteLayerTreeTransaction::LayerProperties::LayerProperties(const LayerProperti
 
     if (other.filters)
         filters = std::make_unique<WebCore::FilterOperations>(*other.filters);
+
+    if (other.eventRegion)
+        eventRegion = std::make_unique<WebCore::Region>(*other.eventRegion);
+
 }
 
 void RemoteLayerTreeTransaction::LayerProperties::encode(IPC::Encoder& encoder) const
@@ -276,6 +281,12 @@ void RemoteLayerTreeTransaction::LayerProperties::encode(IPC::Encoder& encoder)
 
     if (changedProperties & UserInteractionEnabledChanged)
         encoder << userInteractionEnabled;
+
+    if (changedProperties & EventRegionChanged) {
+        encoder << !!eventRegion;
+        if (eventRegion)
+            encoder << *eventRegion;
+    }
 }
 
 bool RemoteLayerTreeTransaction::LayerProperties::decode(IPC::Decoder& decoder, LayerProperties& result)
@@ -499,6 +510,18 @@ bool RemoteLayerTreeTransaction::LayerProperties::decode(IPC::Decoder& decoder,
             return false;
     }
 
+    if (result.changedProperties & EventRegionChanged) {
+        bool hasEventRegion = false;
+        if (!decoder.decode(hasEventRegion))
+            return false;
+        if (hasEventRegion) {
+            auto eventRegion = std::make_unique<WebCore::Region>();
+            if (!decoder.decode(*eventRegion))
+                return false;
+            result.eventRegion = WTFMove(eventRegion);
+        }
+    }
+
     return true;
 }
 
index 00ae5c6..c705aac 100644 (file)
@@ -53,18 +53,29 @@ public:
     UIView *uiView() const { return m_uiView.get(); }
 #endif
 
+    WebCore::GraphicsLayer::PlatformLayerID layerID() const { return m_layerID; }
+
+    const WebCore::Region* eventRegion() const { return m_eventRegion.get(); }
+    void setEventRegion(std::unique_ptr<WebCore::Region>&&);
+
     void detachFromParent();
 
     static WebCore::GraphicsLayer::PlatformLayerID layerID(CALayer *);
+    static RemoteLayerTreeNode* forCALayer(CALayer *);
+
     static NSString *appendLayerDescription(NSString *description, CALayer *);
 
 private:
-    void setLayerID(WebCore::GraphicsLayer::PlatformLayerID);
+    void initializeLayer();
+
+    WebCore::GraphicsLayer::PlatformLayerID m_layerID;
 
     RetainPtr<CALayer> m_layer;
 #if PLATFORM(IOS_FAMILY)
     RetainPtr<UIView> m_uiView;
 #endif
+
+    std::unique_ptr<WebCore::Region> m_eventRegion;
 };
 
 }
index 79ebe2d..22cdaa1 100644 (file)
 
 namespace WebKit {
 
+static NSString *const WKRemoteLayerTreeNodePropertyKey = @"WKRemoteLayerTreeNode";
+
 RemoteLayerTreeNode::RemoteLayerTreeNode(WebCore::GraphicsLayer::PlatformLayerID layerID, RetainPtr<CALayer> layer)
-    : m_layer(WTFMove(layer))
+    : m_layerID(layerID)
+    , m_layer(WTFMove(layer))
 {
-    setLayerID(layerID);
+    initializeLayer();
     [m_layer setDelegate:[WebActionDisablingCALayerDelegate shared]];
 }
 
 #if PLATFORM(IOS_FAMILY)
 RemoteLayerTreeNode::RemoteLayerTreeNode(WebCore::GraphicsLayer::PlatformLayerID layerID, RetainPtr<UIView> uiView)
-    : m_layer([uiView.get() layer])
+    : m_layerID(layerID)
+    , m_layer([uiView.get() layer])
     , m_uiView(WTFMove(uiView))
 {
-    setLayerID(layerID);
+    initializeLayer();
 }
 #endif
 
-RemoteLayerTreeNode::~RemoteLayerTreeNode() = default;
+RemoteLayerTreeNode::~RemoteLayerTreeNode()
+{
+    [layer() setValue:nil forKey:WKRemoteLayerTreeNodePropertyKey];
+}
 
 std::unique_ptr<RemoteLayerTreeNode> RemoteLayerTreeNode::createWithPlainLayer(WebCore::GraphicsLayer::PlatformLayerID layerID)
 {
@@ -80,16 +87,25 @@ void RemoteLayerTreeNode::detachFromParent()
     [layer() removeFromSuperlayer];
 }
 
-static NSString *const WKLayerIDPropertyKey = @"WKLayerID";
+void RemoteLayerTreeNode::setEventRegion(std::unique_ptr<WebCore::Region>&& eventRegion)
+{
+    m_eventRegion = WTFMove(eventRegion);
+}
 
-void RemoteLayerTreeNode::setLayerID(WebCore::GraphicsLayer::PlatformLayerID layerID)
+void RemoteLayerTreeNode::initializeLayer()
 {
-    [layer() setValue:@(layerID) forKey:WKLayerIDPropertyKey];
+    [layer() setValue:[NSValue valueWithPointer:this] forKey:WKRemoteLayerTreeNodePropertyKey];
 }
 
 WebCore::GraphicsLayer::PlatformLayerID RemoteLayerTreeNode::layerID(CALayer *layer)
 {
-    return [[layer valueForKey:WKLayerIDPropertyKey] unsignedLongLongValue];
+    auto* node = forCALayer(layer);
+    return node ? node->layerID() : 0;
+}
+
+RemoteLayerTreeNode* RemoteLayerTreeNode::forCALayer(CALayer *layer)
+{
+    return static_cast<RemoteLayerTreeNode*>([[layer valueForKey:WKRemoteLayerTreeNodePropertyKey] pointerValue]);
 }
 
 NSString *RemoteLayerTreeNode::appendLayerDescription(NSString *description, CALayer *layer)
index 606350c..20d3691 100644 (file)
 #if PLATFORM(IOS_FAMILY)
 
 #import "RemoteLayerTreeHost.h"
+#import "RemoteLayerTreeNode.h"
 #import "UIKitSPI.h"
 #import "WKDrawingView.h"
+#import <WebCore/Region.h>
 #import <pal/spi/cocoa/QuartzCoreSPI.h>
 #import <wtf/SoftLinking.h>
 
+namespace WebKit {
+
 static void collectDescendantViewsAtPoint(Vector<UIView *, 16>& viewsAtPoint, UIView *parent, CGPoint point, UIEvent *event)
 {
     if (parent.clipsToBounds && ![parent pointInside:point withEvent:event])
@@ -53,8 +57,12 @@ static void collectDescendantViewsAtPoint(Vector<UIView *, 16>& viewsAtPoint, UI
             return true;
         }();
 
-        if (!isTransparent && [view pointInside:subviewPoint withEvent:event])
-            viewsAtPoint.append(view);
+        if (!isTransparent && [view pointInside:subviewPoint withEvent:event]) {
+            auto* node = RemoteLayerTreeNode::forCALayer(view.layer);
+            auto* eventRegion = node ? node->eventRegion() : nullptr;
+            if (!eventRegion || eventRegion->contains(WebCore::IntPoint(subviewPoint)))
+                viewsAtPoint.append(view);
+        }
 
         if (![view subviews])
             return;
@@ -63,6 +71,8 @@ static void collectDescendantViewsAtPoint(Vector<UIView *, 16>& viewsAtPoint, UI
     };
 }
 
+}
+
 @interface UIView (WKHitTesting)
 - (UIView *)_web_findDescendantViewAtPoint:(CGPoint)point withEvent:(UIEvent *)event;
 @end
@@ -72,7 +82,7 @@ static void collectDescendantViewsAtPoint(Vector<UIView *, 16>& viewsAtPoint, UI
 - (UIView *)_web_findDescendantViewAtPoint:(CGPoint)point withEvent:(UIEvent *)event
 {
     Vector<UIView *, 16> viewsAtPoint;
-    collectDescendantViewsAtPoint(viewsAtPoint, self, point, event);
+    WebKit::collectDescendantViewsAtPoint(viewsAtPoint, self, point, event);
 
     for (auto i = viewsAtPoint.size(); i--;) {
         auto *view = viewsAtPoint[i];
index d3688ff..90e1f87 100644 (file)
@@ -38,6 +38,7 @@
 #import <WebCore/PlatformCAFilters.h>
 #import <WebCore/PlatformCALayerCocoa.h>
 #import <WebCore/TiledBacking.h>
+#import <wtf/PointerComparison.h>
 
 namespace WebKit {
 using namespace WebCore;
@@ -870,6 +871,21 @@ void PlatformCALayerRemote::updateCustomAppearance(GraphicsLayer::CustomAppearan
     m_properties.notePropertiesChanged(RemoteLayerTreeTransaction::CustomAppearanceChanged);
 }
 
+const Region* PlatformCALayerRemote::eventRegion() const
+{
+    return m_properties.eventRegion.get();
+}
+
+void PlatformCALayerRemote::setEventRegion(const WebCore::Region* eventRegion)
+{
+    const auto* oldEventRegion = m_properties.eventRegion.get();
+    if (arePointingToEqualData(oldEventRegion, eventRegion))
+        return;
+
+    m_properties.eventRegion = eventRegion ? std::make_unique<WebCore::Region>(*eventRegion) : nullptr;
+    m_properties.notePropertiesChanged(RemoteLayerTreeTransaction::EventRegionChanged);
+}
+
 GraphicsLayer::EmbeddedViewID PlatformCALayerRemote::embeddedViewID() const
 {
     return m_embeddedViewID;
index 6ddd1ed..2172f0d 100644 (file)
@@ -174,6 +174,9 @@ public:
     WebCore::GraphicsLayer::CustomAppearance customAppearance() const override;
     void updateCustomAppearance(WebCore::GraphicsLayer::CustomAppearance) override;
 
+    const WebCore::Region* eventRegion() const override;
+    void setEventRegion(const WebCore::Region*) override;
+
     WebCore::GraphicsLayer::EmbeddedViewID embeddedViewID() const override;
 
     WebCore::TiledBacking* tiledBacking() override { return nullptr; }