Make Transform::TransformRRectF tolerate values within kEpsilon of 0.

This changes Transform::TransformRRectF so that it doesn't fail due to
lack of axis-alignment in cases where
Transform::Preserves2dAxisAlignment is true, by adjusting relevant
values that are within kEpsilon of 0.

This change prevents us from (a) deciding that we can take the
border-radius fast path, and then (b) having that fast path fail.

Fixed: 1207151
Change-Id: I2ae31ec86f4f4a32ae02fc1bc6b4815b253cda2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3353877
Reviewed-by: danakj chromium <danakj@chromium.org>
Commit-Queue: David Baron <dbaron@chromium.org>
Cr-Commit-Position: refs/heads/main@{#953613}
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 2b8ad609..e8a6155a 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -769,8 +769,10 @@
   auto result =
       std::make_pair(node->mask_filter_info, node->is_fast_rounded_corner);
 
-  if (!result.first.Transform(to_target))
+  if (!result.first.Transform(to_target)) {
+    DCHECK(result.first.IsEmpty());
     return kEmptyMaskFilterInfoPair;
+  }
 
   return result;
 }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-radius-clipping-with-transform-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-radius-clipping-with-transform-001-ref.html
new file mode 100644
index 0000000..db3ae40
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-radius-clipping-with-transform-001-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>CSS Test (Backgrounds): border-radius clipping on overflow:hidden with transforms</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<style>
+
+#outer {
+  border: 10px solid #000;
+  border-radius: 60px;
+  width: 200px;
+  height: 200px;
+  background: blue;
+  position: absolute;
+  top: 10px;
+  left: 10px;
+}
+
+#coverinner, #coverouter {
+  position: absolute;
+  border: 4px solid fuchsia;
+  filter: grayscale(30%);
+}
+
+#coverinner {
+  width: 196px;
+  height: 196px;
+  top: 18px;
+  left: 18px;
+  border-radius: 52px;
+}
+
+#coverouter {
+  width: 216px;
+  height: 216px;
+  top: 8px;
+  left: 8px;
+  border-radius: 62px;
+}
+
+</style>
+
+<div id="outer">
+</div>
+<div id="coverinner">
+</div>
+<div id="coverouter">
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-radius-clipping-with-transform-001.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-radius-clipping-with-transform-001.html
new file mode 100644
index 0000000..e7d173b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/border-radius-clipping-with-transform-001.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<title>CSS Test (Backgrounds): border-radius clipping on overflow:hidden with transforms</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1207151">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#corner-clipping">
+<link rel="match" href="border-radius-clipping-with-transform-001-ref.html">
+<meta name="assert" content="The content should be clipped correctly, despite the interesting transforms.">
+<style>
+
+#outer {
+  border: 10px solid #000;
+  border-radius: 60px;
+  width: 200px;
+  height: 200px;
+  overflow: hidden;
+  transform: rotate(90deg);
+  position: absolute;
+  top: 10px;
+  left: 10px;
+}
+#inner {
+  width: 100%;
+  height: 100%;
+  background: blue;
+  transform: translateZ(0);
+}
+
+#coverinner, #coverouter {
+  position: absolute;
+  border: 4px solid fuchsia;
+  filter: grayscale(30%);
+}
+
+#coverinner {
+  width: 196px;
+  height: 196px;
+  top: 18px;
+  left: 18px;
+  border-radius: 52px;
+}
+
+#coverouter {
+  width: 216px;
+  height: 216px;
+  top: 8px;
+  left: 8px;
+  border-radius: 62px;
+}
+
+</style>
+
+<div id="outer">
+  <div id="inner">
+  </div>
+</div>
+<div id="coverinner">
+</div>
+<div id="coverouter">
+</div>
diff --git a/ui/gfx/geometry/transform.cc b/ui/gfx/geometry/transform.cc
index eee5af7..ef9e268 100644
--- a/ui/gfx/geometry/transform.cc
+++ b/ui/gfx/geometry/transform.cc
@@ -485,8 +485,23 @@
 }
 
 bool Transform::TransformRRectF(RRectF* rrect) const {
+  // We want this to fail only in cases where our
+  // Transform::Preserves2dAxisAlignment returns false.  However,
+  // SkMatrix::preservesAxisAlignment is stricter (it lacks the kEpsilon
+  // test).  So after converting our skia::Matrix44 to SkMatrix, round
+  // relevant values less than kEpsilon to zero.
+  SkMatrix rounded_matrix(matrix_);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMScaleX)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMScaleX, 0.0f);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMSkewX)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMSkewX, 0.0f);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMSkewY)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMSkewY, 0.0f);
+  if (std::abs(rounded_matrix.get(SkMatrix::kMScaleY)) < kEpsilon)
+    rounded_matrix.set(SkMatrix::kMScaleY, 0.0f);
+
   SkRRect result;
-  if (!SkRRect(*rrect).transform(SkMatrix(matrix_), &result))
+  if (!SkRRect(*rrect).transform(rounded_matrix, &result))
     return false;
   *rrect = gfx::RRectF(result);
   return true;
diff --git a/ui/gfx/geometry/transform_unittest.cc b/ui/gfx/geometry/transform_unittest.cc
index 127c203..fb57d8a 100644
--- a/ui/gfx/geometry/transform_unittest.cc
+++ b/ui/gfx/geometry/transform_unittest.cc
@@ -2645,6 +2645,14 @@
   EXPECT_TRUE(rotation_90_Clock.TransformRRectF(&rrect));
   EXPECT_EQ(expected.ToString(), rrect.ToString());
 
+  Transform rotation_90_unrounded;
+  rotation_90_unrounded.Rotate(90.0);
+  rrect = RRectF(gfx::RectF(0, 0, 20.f, 25.f),
+                 gfx::RoundedCornersF(1.f, 2.f, 3.f, 4.f));
+  EXPECT_TRUE(rotation_90_unrounded.Preserves2dAxisAlignment());
+  EXPECT_TRUE(rotation_90_unrounded.TransformRRectF(&rrect));
+  EXPECT_EQ(expected.ToString(), rrect.ToString());
+
   Transform scale;
   scale.Scale(2.f, 2.f);
   rrect = RRectF(gfx::RectF(0, 0, 20.f, 25.f),