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 &lt;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 &lt;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 &lt;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 &lt;clipPath> on &lt;div></title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
+<title>Hit-test of clip-path userSpaceOnUse &lt;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>