[web] Remap cupertino fonts to -apple-system (#18388)

* Remap cupertino fonts to -apple-system
* Update text tests for fallback checks
diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart
index c410c8b..d0301e1 100644
--- a/lib/web_ui/lib/src/engine/util.dart
+++ b/lib/web_ui/lib/src/engine/util.dart
@@ -413,10 +413,18 @@
 
 /// A default fallback font family in case an unloaded font has been requested.
 ///
-/// For iOS, default to Helvetica, where it should be available, otherwise
-/// default to Arial.
-final String _fallbackFontFamily =
-    operatingSystem == OperatingSystem.iOs ? 'Helvetica' : 'Arial';
+/// -apple-system targets San Francisco in Safari (on Mac OS X and iOS),
+/// and it targets Neue Helvetica and Lucida Grande on older versions of
+/// Mac OS X. It properly selects between San Francisco Text and
+/// San Francisco Display depending on the text’s size.
+///
+/// For iOS, default to -apple-system, where it should be available, otherwise
+/// default to Arial. BlinkMacSystemFont is used for Chrome on iOS.
+final String _fallbackFontFamily = _isMacOrIOS ?
+    '-apple-system, BlinkMacSystemFont' : 'Arial';
+
+bool get _isMacOrIOS => operatingSystem == OperatingSystem.iOs ||
+    operatingSystem == OperatingSystem.macOs;
 
 /// Create a font-family string appropriate for CSS.
 ///
@@ -426,6 +434,16 @@
   if (_genericFontFamilies.contains(fontFamily)) {
     return fontFamily;
   }
+  if (_isMacOrIOS) {
+    // Unlike Safari, Chrome on iOS does not correctly fallback to cupertino
+    // on sans-serif.
+    // Map to San Francisco Text/Display fonts, use -apple-system,
+    // BlinkMacSystemFont.
+    if (fontFamily == '.SF Pro Text' || fontFamily == '.SF Pro Display' ||
+        fontFamily == '.SF UI Text' || fontFamily == '.SF UI Display') {
+      return _fallbackFontFamily;
+    }
+  }
   return '"$fontFamily", $_fallbackFontFamily, sans-serif';
 }
 
diff --git a/lib/web_ui/test/text_test.dart b/lib/web_ui/test/text_test.dart
index 0c9a5c6..266d88d 100644
--- a/lib/web_ui/test/text_test.dart
+++ b/lib/web_ui/test/text_test.dart
@@ -17,6 +17,16 @@
 
   await webOnlyInitializeTestDomRenderer();
 
+  String fallback;
+  setUp(() {
+    if (operatingSystem == OperatingSystem.macOs ||
+        operatingSystem == OperatingSystem.iOs) {
+      fallback = '-apple-system, BlinkMacSystemFont';
+    } else {
+      fallback = 'Arial';
+    }
+  });
+
   test('predictably lays out a single-line paragraph', () {
     for (double fontSize in <double>[10.0, 20.0, 30.0, 40.0]) {
       final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
@@ -361,9 +371,9 @@
     expect(paragraph.plainText, isNull);
     final List<SpanElement> spans =
         paragraph.paragraphElement.querySelectorAll('span');
-    expect(spans[0].style.fontFamily, 'Ahem, Arial, sans-serif');
+    expect(spans[0].style.fontFamily, 'Ahem, $fallback, sans-serif');
     // The nested span here should not set it's family to default sans-serif.
-    expect(spans[1].style.fontFamily, 'Ahem, Arial, sans-serif');
+    expect(spans[1].style.fontFamily, 'Ahem, $fallback, sans-serif');
   },
       // TODO(nurhan): https://github.com/flutter/flutter/issues/50771
       // TODO(nurhan): https://github.com/flutter/flutter/issues/46638
@@ -383,7 +393,7 @@
 
     final EngineParagraph paragraph = builder.build();
     expect(paragraph.paragraphElement.style.fontFamily,
-        'SomeFont, Arial, sans-serif');
+        'SomeFont, $fallback, sans-serif');
 
     debugEmulateFlutterTesterEnvironment = true;
   },
@@ -422,7 +432,7 @@
 
     final EngineParagraph paragraph = builder.build();
     expect(paragraph.paragraphElement.style.fontFamily,
-        '"MyFont 2000", Arial, sans-serif');
+        '"MyFont 2000", $fallback, sans-serif');
 
     debugEmulateFlutterTesterEnvironment = true;
   },