Fix hit-testing of nested clip-paths with objectBoundingBox units
A nested clip-path should resolve against the original elements
reference box, but it would instead resolve against the referencing
<clipPath>.
Pass the appropriate reference box to SVGLayoutSupport::IntersectsClipPath.
Took the opportunity to export some related tests to WPT.
Bug: 938913
Change-Id: I6d6ad88f0a97028b21b1f1a488afbdd42a96264b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1505949
Reviewed-by: Stephen Chenney <schenney@chromium.org>
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#638293}
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
index c2de62b..fed5ecf 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc
@@ -184,7 +184,8 @@
LocalToSVGParentTransform());
if (!local_location)
return false;
- if (!SVGLayoutSupport::IntersectsClipPath(*this, *local_location))
+ if (!SVGLayoutSupport::IntersectsClipPath(*this, object_bounding_box_,
+ *local_location))
return false;
if (SVGLayoutSupport::HitTestChildren(LastChild(), result, *local_location,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
index 85e417ed..e82c209a 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc
@@ -195,7 +195,8 @@
LocalToSVGParentTransform());
if (!local_location)
return false;
- if (!SVGLayoutSupport::IntersectsClipPath(*this, *local_location))
+ if (!SVGLayoutSupport::IntersectsClipPath(*this, object_bounding_box_,
+ *local_location))
return false;
if (hit_rules.can_hit_fill || hit_rules.can_hit_bounding_box) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
index f1169d2f..f1797c6 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
@@ -238,7 +238,8 @@
bool LayoutSVGResourceClipper::HitTestClipContent(
const FloatRect& object_bounding_box,
const HitTestLocation& location) const {
- if (!SVGLayoutSupport::IntersectsClipPath(*this, location))
+ if (!SVGLayoutSupport::IntersectsClipPath(*this, object_bounding_box,
+ location))
return false;
TransformedHitTestLocation local_location(
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
index 8b35bb8..7244b3e 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
@@ -369,7 +369,8 @@
LocalToSVGParentTransform());
if (!local_location)
return false;
- if (!SVGLayoutSupport::IntersectsClipPath(*this, *local_location))
+ if (!SVGLayoutSupport::IntersectsClipPath(*this, fill_bounding_box_,
+ *local_location))
return false;
if (HitTestShape(result.GetHitTestRequest(), *local_location, hit_rules)) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
index 42eb744..e778c837 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
@@ -326,7 +326,8 @@
LocalToSVGParentTransform());
if (!local_location)
return false;
- if (!SVGLayoutSupport::IntersectsClipPath(*this, *local_location))
+ if (!SVGLayoutSupport::IntersectsClipPath(*this, ObjectBoundingBox(),
+ *local_location))
return false;
if (LayoutBlock::NodeAtPoint(result, *local_location, accumulated_offset,
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
index fdbf27f1..d6a7725 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc
@@ -418,11 +418,11 @@
}
bool SVGLayoutSupport::IntersectsClipPath(const LayoutObject& object,
+ const FloatRect& reference_box,
const HitTestLocation& location) {
ClipPathOperation* clip_path_operation = object.StyleRef().ClipPath();
if (!clip_path_operation)
return true;
- const FloatRect& reference_box = object.ObjectBoundingBox();
if (clip_path_operation->GetType() == ClipPathOperation::SHAPE) {
ShapeClipPathOperation& clip_path =
ToShapeClipPathOperation(*clip_path_operation);
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_support.h b/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
index 7e53866..87ad62c2 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_support.h
@@ -72,8 +72,13 @@
// Determine if the LayoutObject references a filter resource object.
static bool HasFilterResource(const LayoutObject&);
- // Determine whether the passed location intersects the clip path of |object|.
- static bool IntersectsClipPath(const LayoutObject&, const HitTestLocation&);
+ // Determine whether the passed location intersects a clip path referenced by
+ // the passed LayoutObject.
+ // |reference_box| is used to resolve 'objectBoundingBox' units/percentages,
+ // and can differ from the reference box of the passed LayoutObject.
+ static bool IntersectsClipPath(const LayoutObject&,
+ const FloatRect& reference_box,
+ const HitTestLocation&);
// Shared child hit-testing code between LayoutSVGRoot/LayoutSVGContainer.
static bool HitTestChildren(LayoutObject* last_child,
diff --git a/third_party/blink/web_tests/css3/masking/clip-path-hittest.html b/third_party/blink/web_tests/css3/masking/clip-path-hittest.html
deleted file mode 100644
index 10a360b..0000000
--- a/third_party/blink/web_tests/css3/masking/clip-path-hittest.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<title>Hit-test of clip-path polygon on <div> with box-shadow</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<style>
-body {
- margin: 0;
-}
-.box {
- width: 100px;
- height: 100px;
- background-color: blue;
- box-shadow: -100px 0px red;
- -webkit-clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
- clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
-}
-</style>
-<div class=box></div>
-<script>
-function assert_element_at(element, pointlist) {
- for (var point of pointlist) {
- var result = document.elementFromPoint(point[0], point[1]);
- assert_equals(result, element, point.join(','));
- }
-}
-
-test(function() {
- var div = document.querySelector('.box');
-
- // Points inside clip-path.
- assert_element_at(div, [[50, 50], [50, 25], [50, 75], [25, 50], [75, 50]]);
-
- // Points outside clip-path.
- assert_element_at(document.body, [[20, 20], [80, 20], [20, 80], [80, 80]]);
-});
-</script>
diff --git a/third_party/blink/web_tests/css3/masking/clip-path-hittest-reference-obb-w-transform.html b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-objectboundingbox-001.html
similarity index 71%
rename from third_party/blink/web_tests/css3/masking/clip-path-hittest-reference-obb-w-transform.html
rename to third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-objectboundingbox-001.html
index c4d54df8..22d4bc0b0 100644
--- a/third_party/blink/web_tests/css3/masking/clip-path-hittest-reference-obb-w-transform.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-objectboundingbox-001.html
@@ -1,7 +1,8 @@
<!DOCTYPE html>
<title>Hit-test of clip-path objectBoundingBox <clipPath> with additional transform</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.fxtf.org/css-masking/#the-clip-path">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
<style>
body {
margin: 0;
@@ -22,14 +23,14 @@
</svg>
<script>
function assert_element_at(element, pointlist) {
- for (var point of pointlist) {
- var result = document.elementFromPoint(point[0], point[1]);
+ for (let point of pointlist) {
+ let result = document.elementFromPoint(point[0], point[1]);
assert_equals(result, element, point.join(','));
}
}
test(function() {
- var div = document.querySelector('.box');
+ let div = document.querySelector('.box');
// Points inside clip-path.
assert_element_at(div, [[150, 150], [150, 125], [150, 175], [125, 150], [175, 150]]);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-objectboundingbox-002.html b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-objectboundingbox-002.html
new file mode 100644
index 0000000..1932848
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-objectboundingbox-002.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>Hit-test of clip-path nested objectBoundingBox <clipPath></title>
+<link rel="help" href="https://drafts.fxtf.org/css-masking/#the-clip-path">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+body {
+ margin: 0;
+}
+.box {
+ width: 200px;
+ height: 200px;
+ background-color: blue;
+ margin: 100px;
+ clip-path: url(#clip);
+}
+</style>
+<div class="box"></div>
+<svg height="0">
+ <clipPath id="nested" clipPathUnits="objectBoundingBox">
+ <circle cx="0.25" cy="0.25" r="0.25"/>
+ </clipPath>
+ <clipPath id="clip" clipPathUnits="objectBoundingBox" clip-path="url(#nested)">
+ <rect width="0.5" height="0.5"/>
+ </clipPath>
+</svg>
+<script>
+function assert_element_at(element, pointlist) {
+ for (let point of pointlist) {
+ let result = document.elementFromPoint(point[0], point[1]);
+ assert_equals(result, element, point.join(','));
+ }
+}
+
+test(function() {
+ let div = document.querySelector('.box');
+
+ // Points inside clip-path.
+ assert_element_at(div, [[150, 150], [150, 125], [150, 175], [125, 150], [175, 150]]);
+
+ // Points outside clip-path.
+ assert_element_at(document.body, [[110, 110], [190, 110], [110, 190], [190, 190]]);
+});
+</script>
diff --git a/third_party/blink/web_tests/css3/masking/clip-path-hittest-reference-usou.html b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-userspaceonuse-001.html
similarity index 62%
rename from third_party/blink/web_tests/css3/masking/clip-path-hittest-reference-usou.html
rename to third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-userspaceonuse-001.html
index d857c3d..2b8beab 100644
--- a/third_party/blink/web_tests/css3/masking/clip-path-hittest-reference-usou.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-element-userspaceonuse-001.html
@@ -1,7 +1,8 @@
<!DOCTYPE html>
-<title>Hit-test of clip-path userSpaceOnUse <clipPath> on <div></title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<title>Hit-test of clip-path userSpaceOnUse <clipPath></title>
+<link rel="help" href="https://drafts.fxtf.org/css-masking/#the-clip-path">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
<style>
body {
margin: 0;
@@ -11,11 +12,10 @@
height: 100px;
background-color: blue;
margin: 100px;
- -webkit-clip-path: url(#clip);
clip-path: url(#clip);
}
</style>
-<div class=box></div>
+<div class="box"></div>
<svg height="0">
<clipPath id="clip" clipPathUnits="userSpaceOnUse">
<polygon points="50,0 100,50 50,100 0,50"/>
@@ -23,14 +23,14 @@
</svg>
<script>
function assert_element_at(element, pointlist) {
- for (var point of pointlist) {
- var result = document.elementFromPoint(point[0], point[1]);
+ for (let point of pointlist) {
+ let result = document.elementFromPoint(point[0], point[1]);
assert_equals(result, element, point.join(','));
}
}
test(function() {
- var div = document.querySelector('.box');
+ let div = document.querySelector('.box');
// Points inside clip-path.
assert_element_at(div, [[150, 150], [150, 125], [150, 175], [125, 150], [175, 150]]);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-shape-polygon-and-box-shadow.html b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-shape-polygon-and-box-shadow.html
new file mode 100644
index 0000000..23009f76
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masking/hit-test/clip-path-shape-polygon-and-box-shadow.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>Hit-test of clip-path polygon combined with box-shadow</title>
+<link rel="help" href="https://drafts.fxtf.org/css-masking/#the-clip-path">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+body {
+ margin: 0;
+}
+.box {
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ box-shadow: -100px 0px red;
+ clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
+}
+</style>
+<div class="box"></div>
+<script>
+function assert_element_at(element, pointlist) {
+ for (let point of pointlist) {
+ let result = document.elementFromPoint(point[0], point[1]);
+ assert_equals(result, element, point.join(','));
+ }
+}
+
+test(function() {
+ let div = document.querySelector('.box');
+
+ // Points inside clip-path.
+ assert_element_at(div, [[50, 50], [50, 25], [50, 75], [25, 50], [75, 50]]);
+
+ // Points outside clip-path.
+ assert_element_at(document.body, [[20, 20], [80, 20], [20, 80], [80, 80]]);
+});
+</script>