Diff
Modified: trunk/LayoutTests/ChangeLog (278342 => 278343)
--- trunk/LayoutTests/ChangeLog 2021-06-02 08:11:14 UTC (rev 278342)
+++ trunk/LayoutTests/ChangeLog 2021-06-02 08:28:32 UTC (rev 278343)
@@ -1,3 +1,19 @@
+2021-05-28 Antoine Quint <[email protected]>
+
+ Hit-testing does not account for clip-path on <iframe>
+ https://bugs.webkit.org/show_bug.cgi?id=226380
+ <rdar://problem/78621486>
+
+ Reviewed by Antti Koivisto.
+
+ Add a new test which checks that we hit-test correctly in part of an
+ <iframe> or <img> clipped by the clip-path property.
+
+ * css3/masking/clip-path-hit-test-iframe-expected.txt: Added.
+ * css3/masking/clip-path-hit-test-iframe.html: Added.
+ * css3/masking/clip-path-hit-test-img-expected.txt: Added.
+ * css3/masking/clip-path-hit-test-img.html: Added.
+
2021-06-01 Lauro Moura <[email protected]>
[WPE] Garden imported/w3c/web-platform-tests/html/semantics/embedded-content/the-img-element/source-media-outside-doc.html flakiness
Added: trunk/LayoutTests/css3/masking/clip-path-hit-test-iframe-expected.txt (0 => 278343)
--- trunk/LayoutTests/css3/masking/clip-path-hit-test-iframe-expected.txt (rev 0)
+++ trunk/LayoutTests/css3/masking/clip-path-hit-test-iframe-expected.txt 2021-06-02 08:28:32 UTC (rev 278343)
@@ -0,0 +1,10 @@
+Test clip-path is accounted for during hit-testing with iframe.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.elementFromPoint(50, 50).localName is "body"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/css3/masking/clip-path-hit-test-iframe.html (0 => 278343)
--- trunk/LayoutTests/css3/masking/clip-path-hit-test-iframe.html (rev 0)
+++ trunk/LayoutTests/css3/masking/clip-path-hit-test-iframe.html 2021-06-02 08:28:32 UTC (rev 278343)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Hit testing of iframe with clip-path</title>
+<script src=""
+<script type="text/_javascript_">
+
+function runTest() {
+ description('Test clip-path is accounted for during hit-testing with iframe.');
+ shouldBeEqualToString("document.elementFromPoint(50, 50).localName", "body");
+}
+
+</script>
+<style>
+
+p {
+ pointer-events: none;
+}
+
+iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 400px;
+ height: 300px;
+ border: 0;
+ clip-path: inset(100px 0 0 0);
+}
+
+</style>
+</head>
+<body>
+ <iframe srcdoc="<body style='margin: 0; background: white'></body>" _onload_="runTest()"></iframe>
+</body>
+</html>
Added: trunk/LayoutTests/css3/masking/clip-path-hit-test-img-expected.txt (0 => 278343)
--- trunk/LayoutTests/css3/masking/clip-path-hit-test-img-expected.txt (rev 0)
+++ trunk/LayoutTests/css3/masking/clip-path-hit-test-img-expected.txt 2021-06-02 08:28:32 UTC (rev 278343)
@@ -0,0 +1,10 @@
+Test clip-path is accounted for during hit-testing with replaced elements.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.elementFromPoint(50, 50).localName is "body"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/css3/masking/clip-path-hit-test-img.html (0 => 278343)
--- trunk/LayoutTests/css3/masking/clip-path-hit-test-img.html (rev 0)
+++ trunk/LayoutTests/css3/masking/clip-path-hit-test-img.html 2021-06-02 08:28:32 UTC (rev 278343)
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Hit testing of iframe with clip-path</title>
+<script src=""
+<style>
+
+p {
+ pointer-events: none;
+}
+
+img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 400px;
+ height: 300px;
+ clip-path: inset(100px 0 0 0);
+}
+
+</style>
+</head>
+<body>
+<img>
+<script type="text/_javascript_">
+
+description('Test clip-path is accounted for during hit-testing with replaced elements.');
+shouldBeEqualToString("document.elementFromPoint(50, 50).localName", "body");
+
+</script>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (278342 => 278343)
--- trunk/Source/WebCore/ChangeLog 2021-06-02 08:11:14 UTC (rev 278342)
+++ trunk/Source/WebCore/ChangeLog 2021-06-02 08:28:32 UTC (rev 278343)
@@ -1,3 +1,28 @@
+2021-05-28 Antoine Quint <[email protected]>
+
+ Hit-testing does not account for clip-path on <iframe>
+ https://bugs.webkit.org/show_bug.cgi?id=226380
+ <rdar://problem/78621486>
+
+ Reviewed by Antti Koivisto.
+
+ The logic to account for the clip-path property during hit-testing was only found in RenderBlock::nodeAtPoint()
+ although other types of RenderBox objects may need this, such as RenderIFrame. So we move some of the logic
+ from RenderBlock::nodeAtPoint() to dedicated methods on RenderBox such that RenderBox::nodeAtPoint() may call them
+ but also allow for RenderBlock::nodeAtPoint() to call them.
+
+ Test: css3/masking/clip-path-hit-test-iframe.html
+ css3/masking/clip-path-hit-test-img.html
+
+ * rendering/RenderBlock.cpp:
+ (WebCore::RenderBlock::nodeAtPoint):
+ * rendering/RenderBox.cpp:
+ (WebCore::RenderBox::hitTestVisualOverflow const):
+ (WebCore::RenderBox::hitTestClipPath const):
+ (WebCore::RenderBox::hitTestBorderRadius const):
+ (WebCore::RenderBox::nodeAtPoint):
+ * rendering/RenderBox.h:
+
2021-06-02 Youenn Fablet <[email protected]>
Improve AudioMediaStreamTrackRendererUnit logging to know which source is being played
Modified: trunk/Source/WebCore/rendering/RenderBlock.cpp (278342 => 278343)
--- trunk/Source/WebCore/rendering/RenderBlock.cpp 2021-06-02 08:11:14 UTC (rev 278342)
+++ trunk/Source/WebCore/rendering/RenderBlock.cpp 2021-06-02 08:28:32 UTC (rev 278343)
@@ -63,7 +63,6 @@
#include "RenderLayoutState.h"
#include "RenderListMarker.h"
#include "RenderMenuList.h"
-#include "RenderSVGResourceClipper.h"
#include "RenderSVGRoot.h"
#include "RenderTableCell.h"
#include "RenderTextControl.h"
@@ -2062,14 +2061,9 @@
const LayoutPoint adjustedLocation(accumulatedOffset + location());
const LayoutSize localOffset = toLayoutSize(adjustedLocation);
- if (!isRenderView()) {
- // Check if we need to do anything at all.
- LayoutRect overflowBox = visualOverflowRect();
- flipForWritingMode(overflowBox);
- overflowBox.moveBy(adjustedLocation);
- if (!locationInContainer.intersects(overflowBox))
- return false;
- }
+ // Check if we need to do anything at all.
+ if (!hitTestVisualOverflow(locationInContainer, accumulatedOffset))
+ return false;
if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) {
updateHitTestResult(result, locationInContainer.point() - localOffset);
@@ -2078,31 +2072,8 @@
return true;
}
- if (style().clipPath()) {
- switch (style().clipPath()->type()) {
- case ClipPathOperation::Shape: {
- auto& clipPath = downcast<ShapeClipPathOperation>(*style().clipPath());
- auto referenceBoxRect = referenceBox(clipPath.referenceBox());
- if (!clipPath.pathForReferenceRect(referenceBoxRect).contains(locationInContainer.point() - localOffset, clipPath.windRule()))
- return false;
- break;
- }
- case ClipPathOperation::Reference: {
- const auto& referenceClipPathOperation = downcast<ReferenceClipPathOperation>(*style().clipPath());
- auto* element = document().getElementById(referenceClipPathOperation.fragment());
- if (!element || !element->renderer())
- break;
- if (!is<SVGClipPathElement>(*element))
- break;
- auto& clipper = downcast<RenderSVGResourceClipper>(*element->renderer());
- if (!clipper.hitTestClipContent(FloatRect(borderBoxRect()), FloatPoint(locationInContainer.point() - localOffset)))
- return false;
- break;
- }
- case ClipPathOperation::Box:
- break;
- }
- }
+ if (!hitTestClipPath(locationInContainer, accumulatedOffset))
+ return false;
// If we have clipping, then we can't have any spillout.
bool useClip = (hasControlClip() || hasOverflowClip());
@@ -2113,14 +2084,8 @@
if (!checkChildren && hitTestExcludedChildrenInBorder(request, result, locationInContainer, adjustedLocation, hitTestAction))
return true;
- // Check if the point is outside radii.
- if (!isRenderView() && style().hasBorderRadius()) {
- LayoutRect borderRect = borderBoxRect();
- borderRect.moveBy(adjustedLocation);
- RoundedRect border = style().getRoundedBorderFor(borderRect);
- if (!locationInContainer.intersects(border))
- return false;
- }
+ if (!hitTestBorderRadius(locationInContainer, accumulatedOffset))
+ return false;
// Now hit test our background
if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) {
Modified: trunk/Source/WebCore/rendering/RenderBox.cpp (278342 => 278343)
--- trunk/Source/WebCore/rendering/RenderBox.cpp 2021-06-02 08:11:14 UTC (rev 278342)
+++ trunk/Source/WebCore/rendering/RenderBox.cpp 2021-06-02 08:28:32 UTC (rev 278343)
@@ -26,6 +26,7 @@
#include "RenderBox.h"
#include "CSSFontSelector.h"
+#include "ClipPathOperation.h"
#include "ControlStates.h"
#include "Document.h"
#include "Editing.h"
@@ -66,10 +67,12 @@
#include "RenderLayerScrollableArea.h"
#include "RenderLayoutState.h"
#include "RenderMultiColumnFlow.h"
+#include "RenderSVGResourceClipper.h"
#include "RenderTableCell.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "RuntimeApplicationChecks.h"
+#include "SVGClipPathElement.h"
#include "ScrollAnimator.h"
#include "ScrollbarTheme.h"
#include "Settings.h"
@@ -1339,6 +1342,65 @@
}
// Hit Testing
+bool RenderBox::hitTestVisualOverflow(const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const
+{
+ if (isRenderView())
+ return true;
+
+ LayoutPoint adjustedLocation = accumulatedOffset + location();
+ LayoutRect overflowBox = visualOverflowRect();
+ flipForWritingMode(overflowBox);
+ overflowBox.moveBy(adjustedLocation);
+ return locationInContainer.intersects(overflowBox);
+}
+
+bool RenderBox::hitTestClipPath(const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const
+{
+ if (!style().clipPath())
+ return true;
+
+ LayoutPoint adjustedLocation = accumulatedOffset + location();
+ const LayoutSize localOffset = toLayoutSize(adjustedLocation);
+
+ switch (style().clipPath()->type()) {
+ case ClipPathOperation::Shape: {
+ auto& clipPath = downcast<ShapeClipPathOperation>(*style().clipPath());
+ auto referenceBoxRect = referenceBox(clipPath.referenceBox());
+ if (!clipPath.pathForReferenceRect(referenceBoxRect).contains(locationInContainer.point() - localOffset, clipPath.windRule()))
+ return false;
+ break;
+ }
+ case ClipPathOperation::Reference: {
+ const auto& referenceClipPathOperation = downcast<ReferenceClipPathOperation>(*style().clipPath());
+ auto* element = document().getElementById(referenceClipPathOperation.fragment());
+ if (!element || !element->renderer())
+ break;
+ if (!is<SVGClipPathElement>(*element))
+ break;
+ auto& clipper = downcast<RenderSVGResourceClipper>(*element->renderer());
+ if (!clipper.hitTestClipContent(FloatRect(borderBoxRect()), FloatPoint(locationInContainer.point() - localOffset)))
+ return false;
+ break;
+ }
+ case ClipPathOperation::Box:
+ break;
+ }
+
+ return true;
+}
+
+bool RenderBox::hitTestBorderRadius(const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const
+{
+ if (isRenderView() || !style().hasBorderRadius())
+ return true;
+
+ LayoutPoint adjustedLocation = accumulatedOffset + location();
+ LayoutRect borderRect = borderBoxRect();
+ borderRect.moveBy(adjustedLocation);
+ RoundedRect border = style().getRoundedBorderFor(borderRect);
+ return locationInContainer.intersects(border);
+}
+
bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
{
LayoutPoint adjustedLocation = accumulatedOffset + location();
@@ -1356,12 +1418,21 @@
LayoutRect boundsRect = borderBoxRectInFragment(nullptr);
boundsRect.moveBy(adjustedLocation);
if (visibleToHitTesting(request) && action == HitTestForeground && locationInContainer.intersects(boundsRect)) {
+ if (!hitTestVisualOverflow(locationInContainer, accumulatedOffset))
+ return false;
+
+ if (!hitTestClipPath(locationInContainer, accumulatedOffset))
+ return false;
+
+ if (!hitTestBorderRadius(locationInContainer, accumulatedOffset))
+ return false;
+
updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
if (result.addNodeToListBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
return true;
}
- return false;
+ return RenderBoxModelObject::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
}
// --------------------- painting stuff -------------------------------
Modified: trunk/Source/WebCore/rendering/RenderBox.h (278342 => 278343)
--- trunk/Source/WebCore/rendering/RenderBox.h 2021-06-02 08:11:14 UTC (rev 278342)
+++ trunk/Source/WebCore/rendering/RenderBox.h 2021-06-02 08:28:32 UTC (rev 278343)
@@ -306,6 +306,9 @@
void layout() override;
bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) override;
+ bool hitTestVisualOverflow(const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const;
+ bool hitTestClipPath(const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const;
+ bool hitTestBorderRadius(const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const;
LayoutUnit minPreferredLogicalWidth() const override;
LayoutUnit maxPreferredLogicalWidth() const override;