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 e22f8bcb34f86fab48ac2dfb1b2b804d1d13d485..95f63af327359d9b16fd1002a85874c7ea39ac74 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
@@ -48,6 +48,7 @@ enum CSSSampleId {
     kInternalForcedVisitedColor = 0,
     kInternalOverflowBlock = 0,
     kInternalOverflowInline = 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
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 e189d584f05f2ce6354c03a9b19f56985df8a15e..41b430e8f2416be098494f5c49fb97cae13447aa 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
@@ -312,7 +312,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 7a974e81271e2f0193d8d311e96aa1e4f783d01f..0e8fa29cad7dee14cd38c808a6eeb49cfcc9879f 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -8966,6 +8966,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 2e4d05c1004093e7b694aaa4c61890837c522be5..756360fad130635ab95ab10c31ea044a54914d25 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -352,6 +352,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 5024cbbc9c7030126b3ea79d62a94c3655adbd47..087717498eeb43208f9e430adeb671cf98e650e0 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
@@ -12273,5 +12273,36 @@ const CSSValue* InternalEmptyLineHeight::ParseSingleValue(
                                          CSSValueID::kNone>(stream);
 }
 
+const CSSValue* ElectronCornerSmoothing::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) 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, 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 0731f31a86e413db570c5337fbb69f044b22a68d..34943df6b2d259d70925427b680cba2300491ff5 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
@@ -4021,6 +4021,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 d8608ef73c9c362c2284d34c453ae3394c3bd844..04ec67d5ae60b065f35795e5239e1f66845d199f 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
@@ -434,6 +434,7 @@ class StyleBuilderConverter {
   static PositionTryFallback ConvertSinglePositionTryFallback(
       StyleResolverState&,
       const CSSValue&);
+  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 ec80337fdac36fa1636f5142c3827d3bbc81644c..634db12dfb443acabde79e9faf59cb8415991464 100644
--- a/third_party/blink/renderer/core/paint/contoured_border_geometry.cc
+++ b/third_party/blink/renderer/core/paint/contoured_border_geometry.cc
@@ -50,6 +50,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 =
@@ -148,7 +166,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 67f6aecb0f50bb58266cec93be57c5c86701f33c..adda233b4b9f6639852e21591f11b16873e2f39a 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1656,6 +1656,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 59031b23d3c50aa87db48a5c5a66c5ab04a8103a..1f83cf0dff83d748bf1caafd3685202c14a3aaed 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 7e3d46902fbf736b4240eb3fcb89975a7b222197..57fdc89fc265ad70cb0bff8443cc10268d154ed9 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"
@@ -244,6 +245,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 4573b97c88c9553d880b8f61b29ff43bf16e1beb..de9168878c536e44e7656d7b23f05c1c1a452367 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",
+    },
     {
       name: "Accelerated2dCanvas",
       settable_from_internals: true,
