From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Calvin Watford <watfordcalvin@gmail.com>
Date: Mon, 9 Dec 2024 16:58:15 -0700
Subject: feat: Corner Smoothing CSS rule and Blink painting

This patch implements the `-electron-corner-smoothing` CSS rule by
making three primary changes to Blink:

1. Adds the `-electron-corner-smoothing` CSS rule:
    * Metadata in `blink/renderer/core/css/css_properties.json5`
    * Parsing in `blink/renderer/core/css/properties/longhands/longhands_custom.cc`
    * Other required definitions for all CSS rules (`css_property_id.mojom`, `css_property_equality.cc`)

2. Modifies how Blink paints rounded rectangles:
    * Augments `blink::ContouredRect` to add smoothness.
    * Modifies graphics to handle smooth `ContouredRect`s, delegating to
      `//electron/shell/renderer/electron_smooth_round_rect`.

3. Adds a renderer feature:
    * Controls whether the CSS rule is available.

diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
index 3d92fdce159a52728f572481f23d27758beafb9a..cae9673c37f65ca9a98e43f544d1c1651f6593c2 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
@@ -50,7 +50,7 @@ enum CSSSampleId {
     kInternalOverflowInline = 0,
     kInternalOverscrollArea = 0,
     kInternalOverscrollPosition = 0,
-
+    kElectronCornerSmoothing = 0,
     // This CSSSampleId represents page load for CSS histograms. It is recorded once
     // per page visit for each CSS histogram being logged on the blink side and the
     // browser side.
diff --git a/third_party/blink/renderer/build/scripts/core/css/css_properties.py b/third_party/blink/renderer/build/scripts/core/css/css_properties.py
index 6e60de1319c5506d7180719fa230ab9cf537b832..e570e335fbd413340ddedeee423eca71275b4c1c 100755
--- a/third_party/blink/renderer/build/scripts/core/css/css_properties.py
+++ b/third_party/blink/renderer/build/scripts/core/css/css_properties.py
@@ -346,7 +346,7 @@ class CSSProperties(object):
                 name_without_leading_dash = name_without_leading_dash[1:]
             internal_visited_order = 1
             if name_without_leading_dash.startswith(
-                    'internal-visited-'
+                    'internal-'
             ) or name_without_leading_dash.startswith(
                     'internal-forced-visited-'):
                 internal_visited_order = 0
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index f0c10ae70102e5ada433a6469c6a2dd66e25578e..a8f7bded24ac7791ed91f933c0e5da5a2ff4df51 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -9433,6 +9433,26 @@
       property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"],
     },
 
+    {
+      name: "-electron-corner-smoothing",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
+      interpolable: true,
+      field_group: "*",
+      field_template: "external",
+      // To keep this patch small, Length is used instead of a more descriptive
+      // custom type.
+      // - `system-ui` = `Length::Auto()`
+      // - percent     = `Length::Percent`
+      type_name: "Length",
+      default_value: "Length::None()",
+      keywords: ["system-ui"],
+      converter: "ConvertCornerSmoothing",
+      runtime_flag: "ElectronCSSCornerSmoothing",
+      valid_for_permission_element: true,
+      valid_for_page_context: true,
+      invalidate: ["border-radius", "paint", "corner-shape"],
+    },
+
     // Visited properties.
     {
       name: "-internal-visited-color",
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index add3cfe363fcd6e0bf05429f21290ede31059936..ddf26f82b131d3f017115484b14307b0fd29bc58 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -402,6 +402,8 @@ bool CSSPropertyEquality::PropertiesEqual(const PropertyHandle& property,
       return a.DominantBaseline() == b.DominantBaseline();
     case CSSPropertyID::kDynamicRangeLimit:
       return a.GetDynamicRangeLimit() == b.GetDynamicRangeLimit();
+    case CSSPropertyID::kElectronCornerSmoothing:
+      return a.ElectronCornerSmoothing() == b.ElectronCornerSmoothing();
     case CSSPropertyID::kEmptyCells:
       return a.EmptyCells() == b.EmptyCells();
     case CSSPropertyID::kFill:
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 50886998a3f57ebe6e10dd629fdfc4a5e4fc0d67..38bebdf0d013d6d5be9545b1da222fa8aea0715d 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -12996,5 +12996,36 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue(
                                          CSSValueID::kNone>(stream);
 }
 
+const CSSValue* ElectronCornerSmoothing::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    CSSParserLocalContext& local_context) const {
+  // Try to parse `system-ui` keyword first.
+  if (auto* ident =
+          css_parsing_utils::ConsumeIdent<CSSValueID::kSystemUi>(stream)) {
+    return ident;
+  }
+  // Try to parse as percent.
+  return css_parsing_utils::ConsumePercent(
+      stream, context, local_context, CSSPrimitiveValue::ValueRange::kNonNegative);
+}
+
+const CSSValue* ElectronCornerSmoothing::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  const Length& length = style.ElectronCornerSmoothing();
+  switch (length.GetType()) {
+    case Length::kAuto:
+      return CSSIdentifierValue::Create(CSSValueID::kSystemUi);
+    case Length::kPercent:
+      return CSSNumericLiteralValue::Create(
+          length.Percent(), CSSPrimitiveValue::UnitType::kPercentage);
+    default:
+      return CSSIdentifierValue::Create(CSSValueID::kNone);
+  }
+}
+
 }  // namespace css_longhand
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index c75e87a27ba45c6e1c80039158ef4a635b796497..2e477e4cd16214442274d85fbc7fd268204d22ad 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -4173,6 +4173,15 @@ PositionTryFallback StyleBuilderConverter::ConvertSinglePositionTryFallback(
   return PositionTryFallback(scoped_name, tactic_list);
 }
 
+Length StyleBuilderConverter::ConvertCornerSmoothing(StyleResolverState& state,
+                                                     const CSSValue& value) {
+  auto* ident = DynamicTo<CSSIdentifierValue>(value);
+  if (ident && ident->GetValueID() == CSSValueID::kSystemUi) {
+    return Length::Auto();
+  }
+  return ConvertLength(state, value);
+}
+
 FitText StyleBuilderConverter::ConvertFitText(StyleResolverState& state,
                                               const CSSValue& value) {
   const auto& list = To<CSSValueList>(value);
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
index beca76bbbe2d31fde75652a73f5bf2dcfaf17731..bc42166596fdab9f0143b851b27ecfcdf6b716b8 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
@@ -457,6 +457,7 @@ class StyleBuilderConverter {
       StyleResolverState&,
       const CSSValue&,
       bool allow_any_keyword_in_position_area = false);
+  static Length ConvertCornerSmoothing(StyleResolverState&, const CSSValue&);
   static FitText ConvertFitText(StyleResolverState&, const CSSValue&);
   static TextOverflowData ConvertTextOverflow(StyleResolverState&,
                                               const CSSValue&);
diff --git a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc
index 19cda703154dab9397827ab6ea66c2ca446c644d..dd5943c511886f4e39b2e7f10e67e60fa9a67290 100644
--- a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc
+++ b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc
@@ -51,6 +51,24 @@ float EffectiveCurvature(Superellipse superellipse, const gfx::SizeF& radius) {
                           : superellipse.Exponent();
 }
 
+float SmoothnessFromLength(const Length& length) {
+  // `none` = 0%
+  if (length.IsNone()) {
+    return 0.0f;
+  }
+
+  // `system-ui` keyword, represented internally as "auto" length
+  if (length.HasAuto()) {
+#if BUILDFLAG(IS_MAC)
+    return 0.6f;
+#else
+    return 0.0f;
+#endif  // BUILDFLAG(IS_MAC)
+  }
+
+  return length.Percent() / 100.0f;
+}
+
 gfx::QuadF ComputeHullQuad(const ContouredRect::Corner& corner) {
   const gfx::PointF half_corner = corner.HalfCorner();
   const gfx::PointF perpendicular_line =
@@ -150,7 +168,8 @@ ContouredRect ComputeContouredBorderFromStyle(
       EffectiveCurvature(style.CornerTopLeftShape(), radii.TopLeft()),
       EffectiveCurvature(style.CornerTopRightShape(), radii.TopRight()),
       EffectiveCurvature(style.CornerBottomRightShape(), radii.BottomRight()),
-      EffectiveCurvature(style.CornerBottomLeftShape(), radii.BottomLeft()));
+      EffectiveCurvature(style.CornerBottomLeftShape(), radii.BottomLeft()),
+      SmoothnessFromLength(style.ElectronCornerSmoothing()));
   if (curvature.IsRound()) {
     return result;
   }
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 83ebf4a21a9d42b307a8a2108e118174d6d62f0e..160b3828d38f55734740cc07d6f851099922f70b 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1669,6 +1669,8 @@ component("platform") {
     "widget/widget_base.h",
     "widget/widget_base_client.h",
     "windows_keyboard_codes.h",
+    "//electron/shell/renderer/electron_smooth_round_rect.h",
+    "//electron/shell/renderer/electron_smooth_round_rect.cc",
   ]
 
   sources -= blink_platform_avx_files
diff --git a/third_party/blink/renderer/platform/geometry/contoured_rect.h b/third_party/blink/renderer/platform/geometry/contoured_rect.h
index 17c97038dc0fdb97021ee4b1aa3c8a4ca459fb2b..71d697d738f6aee8cbbb700f1e6ad9acca980b7b 100644
--- a/third_party/blink/renderer/platform/geometry/contoured_rect.h
+++ b/third_party/blink/renderer/platform/geometry/contoured_rect.h
@@ -52,19 +52,29 @@ class PLATFORM_EXPORT ContouredRect {
     constexpr CornerCurvature(float top_left,
                               float top_right,
                               float bottom_right,
-                              float bottom_left)
+                              float bottom_left,
+                              float smoothness)
         : top_left_(top_left),
           top_right_(top_right),
           bottom_right_(bottom_right),
-          bottom_left_(bottom_left) {
+          bottom_left_(bottom_left),
+          smoothness_(smoothness) {
       DCHECK_GE(top_left, 0);
       DCHECK_GE(top_right, 0);
       DCHECK_GE(bottom_right, 0);
       DCHECK_GE(bottom_left, 0);
+      DCHECK_GE(smoothness, 0);
     }
+    constexpr CornerCurvature(float top_left,
+                              float top_right,
+                              float bottom_right,
+                              float bottom_left)
+        : CornerCurvature(top_left, top_right, bottom_right, bottom_left, 0) {}
+
+    constexpr bool IsSmooth() const { return smoothness_ > 0.0f; }
 
     constexpr bool IsRound() const {
-      return (top_left_ == kRound) && IsUniform();
+      return (top_left_ == kRound) && IsUniform() && !IsSmooth();
     }
 
     constexpr bool IsConvex() const {
@@ -86,6 +96,7 @@ class PLATFORM_EXPORT ContouredRect {
     constexpr float TopRight() const { return top_right_; }
     constexpr float BottomRight() const { return bottom_right_; }
     constexpr float BottomLeft() const { return bottom_left_; }
+    constexpr float Smoothness() const { return smoothness_; }
 
     constexpr bool operator==(const CornerCurvature&) const = default;
 
@@ -96,6 +107,7 @@ class PLATFORM_EXPORT ContouredRect {
     float top_right_ = kRound;
     float bottom_right_ = kRound;
     float bottom_left_ = kRound;
+    float smoothness_ = 0.0f;
   };
 
   // A Corner is a axis-aligned quad, with the points ordered (start, outer,
diff --git a/third_party/blink/renderer/platform/geometry/path_builder.cc b/third_party/blink/renderer/platform/geometry/path_builder.cc
index 18f283e625101318ee14b50e6e765dfd1c9a1a44..44a3a55974c9e4b9e715574075f256615f45f38e 100644
--- a/third_party/blink/renderer/platform/geometry/path_builder.cc
+++ b/third_party/blink/renderer/platform/geometry/path_builder.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/geometry/path_builder.h"
 
+#include "electron/shell/renderer/electron_smooth_round_rect.h"
 #include "third_party/blink/renderer/platform/geometry/contoured_rect.h"
 #include "third_party/blink/renderer/platform/geometry/infinite_int_rect.h"
 #include "third_party/blink/renderer/platform/geometry/path.h"
@@ -250,6 +251,32 @@ PathBuilder& PathBuilder::AddContouredRect(
     AddRoundedRect(target_rect);
     return *this;
   }
+
+  // TODO(clavin): decompose `electron::DrawSmoothRoundRect` into corners
+  if (contoured_rect.GetCornerCurvature().IsSmooth()) {
+    const gfx::RectF& box = contoured_rect.Rect();
+
+    // Constrain the radii (on a copy) to ensure they do not exceed the box.
+    FloatRoundedRect round_rect_copy = contoured_rect.AsRoundedRect();
+    round_rect_copy.ConstrainRadii();
+    const FloatRoundedRect::Radii& radii = round_rect_copy.GetRadii();
+    float smoothness = std::clamp(
+        contoured_rect.GetCornerCurvature().Smoothness(), 0.0f, 1.0f);
+
+    // Since the Electron implementation of DrawSmoothRoundRect uses one radius
+    // for both dimensions, we need to use the minimum of the two supplied.
+    auto min_radius = [](const gfx::SizeF& radius) -> float {
+      return std::min(radius.width(), radius.height());
+    };
+
+    builder_.addPath(electron::DrawSmoothRoundRect(
+        box.x(), box.y(), box.width(), box.height(), smoothness,
+        min_radius(radii.TopLeft()), min_radius(radii.TopRight()),
+        min_radius(radii.BottomRight()), min_radius(radii.BottomLeft())));
+
+    return *this;
+  }
+
   const FloatRoundedRect& origin_rect = contoured_rect.GetOriginRect();
 
   auto DrawAsSinglePath = [&]() {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 640a50a1af53f0771da02de73d70a94c973aa624..f981c8dc7492872f296e01cd64692859671be5d5 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -214,6 +214,10 @@
   },
 
   data: [
+    {
+      name: "ElectronCSSCornerSmoothing",
+      status: "stable",
+    },
     {
       // This flag changes about:blank to dark in dark mode on user action.
       name: "AboutBlankPageRespectsDarkModeOnUserAction",
