Bug 1706080 - [css-fonts] Implement 'font-synthesis: small-caps'. r=jfkthame
authorMats Palmgren <mats@mozilla.com>
Mon, 09 Aug 2021 21:41:34 +0000
changeset 588283 ccd03e2f466b4623583fa4ad711a7a56a493d96f
parent 588282 429bfcda963d647ba494d10d8a5c28b61588dcc1
child 588284 a1a730897df8caf06d617c02468bb8f246bf0a59
push id38691
push usernbeleuzu@mozilla.com
push dateTue, 10 Aug 2021 09:49:08 +0000
treeherdermozilla-central@efefbf74d3fc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1706080
milestone93.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1706080 - [css-fonts] Implement 'font-synthesis: small-caps'. r=jfkthame Differential Revision: https://phabricator.services.mozilla.com/D114313
devtools/shared/css/generated/properties-db.js
gfx/src/nsFont.h
gfx/src/nsFontMetrics.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontConstants.h
gfx/thebes/gfxTextRun.cpp
layout/style/test/property_database.js
modules/libpref/init/StaticPrefList.yaml
servo/components/style/values/specified/font.rs
testing/web-platform/tests/css/css-fonts/font-synthesis-06-ref.html
testing/web-platform/tests/css/css-fonts/font-synthesis-06.html
testing/web-platform/tests/css/css-fonts/font-synthesis-07-ref.html
testing/web-platform/tests/css/css-fonts/font-synthesis-07.html
testing/web-platform/tests/css/css-fonts/inheritance.html
testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-computed.html
testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-invalid.html
testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-valid.html
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -6388,17 +6388,19 @@ exports.CSS_PROPERTIES = {
     "isInherited": true,
     "subproperties": [
       "font-synthesis"
     ],
     "supports": [],
     "values": [
       "inherit",
       "initial",
-      "revert",
+      "none",
+      "revert",
+      "small-caps",
       "style",
       "unset",
       "weight"
     ]
   },
   "font-variant": {
     "isInherited": true,
     "subproperties": [
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -77,18 +77,19 @@ struct nsFont final {
 
   // Kerning
   uint8_t kerning = NS_FONT_KERNING_AUTO;
 
   // Whether automatic optical sizing should be applied to variation fonts
   // that include an 'opsz' axis
   uint8_t opticalSizing = NS_FONT_OPTICAL_SIZING_AUTO;
 
-  // Synthesis setting, controls use of fake bolding/italics
-  uint8_t synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE;
+  // Synthesis setting, controls use of fake bolding/italics/small-caps
+  uint8_t synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE |
+                      NS_FONT_SYNTHESIS_SMALL_CAPS;
 
   // initialize the font with a fontlist
   nsFont(const mozilla::StyleFontFamily&, mozilla::Length aSize);
 
   // initialize the font with a single generic
   nsFont(mozilla::StyleGenericFontFamily, mozilla::Length aSize);
 
   // Make a copy of the given font
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -118,17 +118,18 @@ nsFontMetrics::nsFontMetrics(const nsFon
       mTextRunRTL(false),
       mVertical(false),
       mTextOrientation(mozilla::StyleTextOrientation::Mixed) {
   gfxFontStyle style(
       aFont.style, aFont.weight, aFont.stretch,
       gfxFloat(aFont.size.ToAppUnits()) / mP2A, aFont.sizeAdjust,
       aFont.family.is_system_font, mDeviceContext->IsPrinterContext(),
       aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
-      aFont.synthesis & NS_FONT_SYNTHESIS_STYLE, aFont.languageOverride);
+      aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
+      aFont.synthesis & NS_FONT_SYNTHESIS_SMALL_CAPS, aFont.languageOverride);
 
   aFont.AddFontFeaturesToStyle(&style, mOrientation == eVertical);
   style.featureValueLookup = aParams.featureValueLookup;
 
   aFont.AddFontVariationsToStyle(&style);
 
   gfxFloat devToCssSize = gfxFloat(mP2A) / gfxFloat(AppUnitsPerCSSPixel());
   mFontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -4027,38 +4027,41 @@ gfxFontStyle::gfxFontStyle()
       variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
       variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
       sizeAdjustBasis(uint8_t(FontSizeAdjust::Tag::None)),
       systemFont(true),
       printerFont(false),
       useGrayscaleAntialiasing(false),
       allowSyntheticWeight(true),
       allowSyntheticStyle(true),
+      allowSyntheticSmallCaps(true),
       noFallbackVariantFeatures(true) {}
 
 gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
                            FontStretch aStretch, gfxFloat aSize,
                            const FontSizeAdjust& aSizeAdjust, bool aSystemFont,
                            bool aPrinterFont, bool aAllowWeightSynthesis,
                            bool aAllowStyleSynthesis,
+                           bool aAllowSmallCapsSynthesis,
                            uint32_t aLanguageOverride)
     : size(aSize),
       baselineOffset(0.0f),
       languageOverride(aLanguageOverride),
       fontSmoothingBackgroundColor(NS_RGBA(0, 0, 0, 0)),
       weight(aWeight),
       stretch(aStretch),
       style(aStyle),
       variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
       variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
       systemFont(aSystemFont),
       printerFont(aPrinterFont),
       useGrayscaleAntialiasing(false),
       allowSyntheticWeight(aAllowWeightSynthesis),
       allowSyntheticStyle(aAllowStyleSynthesis),
+      allowSyntheticSmallCaps(aAllowSmallCapsSynthesis),
       noFallbackVariantFeatures(true) {
   MOZ_ASSERT(!mozilla::IsNaN(size));
 
   switch (aSizeAdjust.tag) {
     case FontSizeAdjust::Tag::None:
       sizeAdjust = 0.0f;
       break;
     case FontSizeAdjust::Tag::ExHeight:
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -98,17 +98,18 @@ struct gfxFontStyle {
   using FontSlantStyle = mozilla::FontSlantStyle;
   using FontWeight = mozilla::FontWeight;
   using FontSizeAdjust = mozilla::StyleFontSizeAdjust;
 
   gfxFontStyle();
   gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch,
                gfxFloat aSize, const FontSizeAdjust& aSizeAdjust,
                bool aSystemFont, bool aPrinterFont, bool aWeightSynthesis,
-               bool aStyleSynthesis, uint32_t aLanguageOverride);
+               bool aStyleSynthesis, bool aSmallCapsSynthesis,
+               uint32_t aLanguageOverride);
   // Features are composed of (1) features from style rules (2) features
   // from feature settings rules and (3) family-specific features.  (1) and
   // (3) are guaranteed to be mutually exclusive
 
   // custom opentype feature settings
   CopyableTArray<gfxFontFeature> featureSettings;
 
   // Some font-variant property values require font-specific settings
@@ -193,16 +194,17 @@ struct gfxFontStyle {
   bool printerFont : 1;
 
   // Used to imitate -webkit-font-smoothing: antialiased
   bool useGrayscaleAntialiasing : 1;
 
   // Whether synthetic styles are allowed
   bool allowSyntheticWeight : 1;
   bool allowSyntheticStyle : 1;
+  bool allowSyntheticSmallCaps : 1;
 
   // some variant features require fallback which complicates the shaping
   // code, so set up a bool to indicate when shaping with fallback is needed
   bool noFallbackVariantFeatures : 1;
 
   // Return the final adjusted font size for the given aspect ratio.
   // Not meant to be called when sizeAdjustBasis is NONE.
   gfxFloat GetAdjustedSize(gfxFloat aspect) const {
--- a/gfx/thebes/gfxFontConstants.h
+++ b/gfx/thebes/gfxFontConstants.h
@@ -35,16 +35,17 @@
 #define NS_FONT_SMOOTHING_GRAYSCALE 1
 
 #define NS_FONT_KERNING_AUTO 0
 #define NS_FONT_KERNING_NONE 1
 #define NS_FONT_KERNING_NORMAL 2
 
 #define NS_FONT_SYNTHESIS_WEIGHT 0x1
 #define NS_FONT_SYNTHESIS_STYLE 0x2
+#define NS_FONT_SYNTHESIS_SMALL_CAPS 0x4
 
 #define NS_FONT_OPTICAL_SIZING_AUTO 0
 #define NS_FONT_OPTICAL_SIZING_NONE 1
 
 #define NS_FONT_VARIANT_ALTERNATES_NORMAL 0
 // alternates - simple enumerated values
 #define NS_FONT_VARIANT_ALTERNATES_HISTORICAL (1 << 0)
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -2753,16 +2753,17 @@ void gfxFontGroup::InitScriptRun(DrawTar
                               (matchedLength > 0), range.orientation, isCJK);
         if (!subSuperFont->SplitAndInitTextRun(
                 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
                 matchedLength, aRunScript, mLanguage, range.orientation)) {
           // glyph layout failed! treat as missing glyphs
           matchedFont = nullptr;
         }
       } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
+                 mStyle.allowSyntheticSmallCaps &&
                  !matchedFont->SupportsVariantCaps(
                      aRunScript, mStyle.variantCaps, petiteToSmallCaps,
                      syntheticLower, syntheticUpper)) {
         // fallback for small-caps variant glyphs
         if (!matchedFont->InitFakeSmallCapsRun(
                 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
                 matchedLength, range.matchType, range.orientation, aRunScript,
                 mExplicitLanguage ? mLanguage.get() : nullptr, syntheticLower,
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -5756,25 +5756,27 @@ var gCSSProperties = {
     domProp: "fontSynthesis",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     applies_to_first_line: true,
     applies_to_marker: true,
     applies_to_placeholder: true,
     applies_to_cue: true,
-    initial_values: ["weight style"],
-    other_values: ["none", "weight", "style"],
+    initial_values: ["weight style small-caps"],
+    other_values: ["none", "weight", "style", "small-caps"],
     invalid_values: [
       "weight none",
       "style none",
       "none style",
       "weight 10px",
       "weight weight",
       "style style",
+      "small-caps none",
+      "small-caps small-caps",
     ],
   },
   "font-variant": {
     domProp: "fontVariant",
     inherited: true,
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [
       "font-variant-alternates",
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -7613,16 +7613,23 @@
 
 # Is 'content:none' supported on (non-pseudo) elements?
 - name: layout.css.element-content-none.enabled
   type: RelaxedAtomicBool
   value: false
   mirror: always
   rust: true
 
+# Is 'font-synthesis: small-caps' supported?
+- name: layout.css.font-synthesis-small-caps.enabled
+  type: RelaxedAtomicBool
+  value: true
+  mirror: always
+  rust: true
+
 # Whether frame visibility tracking is enabled globally.
 - name: layout.framevisibility.enabled
   type: bool
   value: true
   mirror: always
 
 # The fraction of the scrollport we allow to horizontally scroll by before we
 # schedule an update of frame visibility.
--- a/servo/components/style/values/specified/font.rs
+++ b/servo/components/style/values/specified/font.rs
@@ -1945,124 +1945,160 @@ impl Parse for FontFeatureSettings {
 }
 
 #[derive(
     Clone,
     Copy,
     Debug,
     MallocSizeOf,
     PartialEq,
-    SpecifiedValueInfo,
     ToComputedValue,
     ToResolvedValue,
     ToShmem,
 )]
 /// Whether user agents are allowed to synthesize bold or oblique font faces
-/// when a font family lacks bold or italic faces
+/// when a font family lacks those faces, or a small-caps variant when this is
+/// not supported by the face.
 pub struct FontSynthesis {
     /// If a `font-weight` is requested that the font family does not contain,
     /// the user agent may synthesize the requested weight from the weights
     /// that do exist in the font family.
-    #[css(represents_keyword)]
     pub weight: bool,
     /// If a font-style is requested that the font family does not contain,
     /// the user agent may synthesize the requested style from the normal face in the font family.
-    #[css(represents_keyword)]
     pub style: bool,
+    /// This bit controls whether the user agent is allowed to synthesize small caps variant
+    /// when a font face lacks it.
+    pub small_caps: bool,
 }
 
 impl FontSynthesis {
     #[inline]
     /// Get the default value of font-synthesis
     pub fn get_initial_value() -> Self {
         FontSynthesis {
             weight: true,
             style: true,
+            small_caps: true,
         }
     }
     #[inline]
     /// Get the 'none' value of font-synthesis
     pub fn none() -> Self {
         FontSynthesis {
             weight: false,
             style: false,
+            small_caps: false,
         }
     }
+    #[inline]
+    /// Return true if this is the 'none' value
+    pub fn is_none(&self) -> bool {
+        *self == Self::none()
+    }
 }
 
 impl Parse for FontSynthesis {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<FontSynthesis, ParseError<'i>> {
-        let mut result = FontSynthesis {
-            weight: false,
-            style: false,
-        };
-        try_match_ident_ignore_ascii_case! { input,
-            "none" => Ok(result),
-            "weight" => {
-                result.weight = true;
-                if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() {
-                    result.style = true;
-                }
-                Ok(result)
-            },
-            "style" => {
-                result.style = true;
-                if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() {
-                    result.weight = true;
-                }
-                Ok(result)
-            },
+        use crate::values::SelectorParseErrorKind;
+        let mut result = Self::none();
+        while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
+            match_ignore_ascii_case! { &ident,
+                "none" if result.is_none() => return Ok(result),
+                "weight" if !result.weight => result.weight = true,
+                "style" if !result.style => result.style = true,
+                "small-caps" if !result.small_caps &&
+                                static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled")
+                                    => result.small_caps = true,
+                _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident))),
+            }
+        }
+        if !result.is_none() {
+            Ok(result)
+        } else {
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
 impl ToCss for FontSynthesis {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
-        if self.weight && self.style {
-            dest.write_str("weight style")
-        } else if self.style {
-            dest.write_str("style")
-        } else if self.weight {
-            dest.write_str("weight")
-        } else {
-            dest.write_str("none")
+        if self.is_none() {
+            return dest.write_str("none");
+        }
+
+        let mut need_space = false;
+        if self.weight {
+            dest.write_str("weight")?;
+            need_space = true;
+        }
+        if self.style {
+            if need_space {
+                dest.write_str(" ")?;
+            }
+            dest.write_str("style")?;
+            need_space = true;
+        }
+        if self.small_caps {
+            if need_space {
+                dest.write_str(" ")?;
+            }
+            dest.write_str("small-caps")?;
+        }
+        Ok(())
+    }
+}
+
+impl SpecifiedValueInfo for FontSynthesis {
+    fn collect_completion_keywords(f: KeywordsCollectFn) {
+        f(&[
+            "none",
+            "weight",
+            "style",
+        ]);
+        if static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled") {
+            f(&["small-caps"]);
         }
     }
 }
 
 #[cfg(feature = "gecko")]
 impl From<u8> for FontSynthesis {
     fn from(bits: u8) -> FontSynthesis {
         use crate::gecko_bindings::structs;
 
         FontSynthesis {
             weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
             style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0,
+            small_caps: bits & structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8 != 0,
         }
     }
 }
 
 #[cfg(feature = "gecko")]
 impl From<FontSynthesis> for u8 {
     fn from(v: FontSynthesis) -> u8 {
         use crate::gecko_bindings::structs;
 
         let mut bits: u8 = 0;
         if v.weight {
             bits |= structs::NS_FONT_SYNTHESIS_WEIGHT as u8;
         }
         if v.style {
             bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
         }
+        if v.small_caps {
+            bits |= structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8;
+        }
         bits
     }
 }
 
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
 /// Allows authors to explicitly specify the language system of the font,
 /// overriding the language system implied by the content language
 pub enum FontLanguageOverride {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-fonts/font-synthesis-06-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Reference: 'font-synthesis:none' with a font that support small-caps</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+  @font-face {
+    font-family: fwf;
+    src: url(support/fonts/FontWithFancyFeatures.otf);
+  }
+  .test {
+     font: 2em/1 fwf;
+  }
+</style>
+
+<p>Test passes if there are four check marks (✓), and zero crosses (✗). </p>
+<p class="test">CCC C</p>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-fonts/font-synthesis-06.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>CSS Test: 'font-synthesis:none' with a font that support small-caps</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
+<link rel="match" href="font-synthesis-06-ref.html">
+<meta name="assert" content="font-synthesis:none should not affect a font that supports small-caps">
+<style>
+  @font-face {
+    font-family: fwf;
+    src: url(support/fonts/FontWithFancyFeatures.otf);
+  }
+  .test {
+     font: 2em/1 fwf;
+     font-variant: small-caps;
+     font-synthesis: none;
+  }
+</style>
+
+<p>Test passes if there are four check marks (✓), and zero crosses (✗). </p>
+<p class="test">JJJ J</p>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-fonts/font-synthesis-07-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Reference: 'font-synthesis:none' with a font that doesn't support small-caps</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+  @font-face {
+    font-family: Lato;
+    src: url(support/fonts/Lato-Medium.ttf);
+  }
+  .test {
+     font: 2em/1 Lato;
+  }
+</style>
+
+<p>Test passes if "A" is upper-case and "bcd e" is lower-case.</p>
+<p class="test">Abcd e</p>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-fonts/font-synthesis-07.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>CSS Test: 'font-synthesis:none' with a font that doesn't support small-caps</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
+<link rel="match" href="font-synthesis-07-ref.html">
+<meta name="assert" content="font-synthesis:none inhibits synthesising small-caps">
+<style>
+  @font-face {
+    font-family: Lato;
+    src: url(support/fonts/Lato-Medium.ttf);
+  }
+  .test {
+     font: 2em/1 Lato;
+     font-variant: small-caps;
+     font-synthesis: none;
+  }
+</style>
+
+<p>Test passes if "A" is upper-case and "bcd e" is lower-case.</p>
+<p class="test">Abcd e</p>
--- a/testing/web-platform/tests/css/css-fonts/inheritance.html
+++ b/testing/web-platform/tests/css/css-fonts/inheritance.html
@@ -30,17 +30,17 @@ assert_inherited('font-family', null, '"
 assert_inherited('font-feature-settings', 'normal', '"smcp", "swsh" 2');
 assert_inherited('font-kerning', 'auto', 'none');
 assert_inherited('font-language-override', 'normal' , '"ksw"');
 assert_inherited('font-optical-sizing', 'auto' , 'none');
 assert_inherited('font-size', mediumFontSize /* medium */, '123px');
 assert_inherited('font-size-adjust', 'none', '1.5');
 assert_inherited('font-stretch', '100%' /* normal */, '75%');
 assert_inherited('font-style', 'normal', 'italic');
-assert_inherited('font-synthesis', 'weight style', 'none');
+assert_inherited('font-synthesis', 'weight style small-caps', 'none');
 assert_inherited('font-variant', 'normal', 'none');
 assert_inherited('font-variant-alternates', 'normal', 'historical-forms');
 assert_inherited('font-variant-caps', 'normal', 'small-caps');
 assert_inherited('font-variant-east-asian', 'normal', 'ruby');
 assert_inherited('font-variant-ligatures', 'normal', 'none');
 assert_inherited('font-variant-numeric', 'normal', 'ordinal');
 assert_inherited('font-variant-position', 'normal', 'super');
 assert_inherited('font-variation-settings', 'normal', '"wght" 700');
--- a/testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-computed.html
+++ b/testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-computed.html
@@ -1,21 +1,25 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
-<title>CSS Fonts Module Level 3: getComputedStyle().fontSynthesis</title>
-<link rel="help" href="https://www.w3.org/TR/css-fonts-3/#font-synthesis-prop">
+<title>CSS Fonts Module: getComputedStyle().fontSynthesis</title>
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
 <meta name="assert" content="font-synthesis computed value is as specified.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/computed-testcommon.js"></script>
 </head>
 <body>
 <div id="target"></div>
 <script>
 test_computed_value('font-synthesis', 'none');
 test_computed_value('font-synthesis', 'weight');
 test_computed_value('font-synthesis', 'style');
+test_computed_value('font-synthesis', 'small-caps');
+test_computed_value('font-synthesis', 'style small-caps');
+test_computed_value('font-synthesis', 'weight small-caps');
 test_computed_value('font-synthesis', 'weight style');
+test_computed_value('font-synthesis', 'weight style small-caps');
 </script>
 </body>
 </html>
--- a/testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-invalid.html
+++ b/testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-invalid.html
@@ -1,18 +1,22 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
-<title>CSS Fonts Module Level 3: parsing font-synthesis with invalid values</title>
-<link rel="help" href="https://www.w3.org/TR/css-fonts-3/#font-synthesis-prop">
-<meta name="assert" content="font-synthesis supports only the grammar 'none | [ weight || style ]'.">
+<title>CSS Fonts Module: parsing font-synthesis with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
+<meta name="assert" content="font-synthesis supports only the grammar 'none | [ weight || style || small-caps ]'.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
 </head>
 <body>
 <script>
 test_invalid_value('font-synthesis', 'auto');
 test_invalid_value('font-synthesis', 'none weight');
+test_invalid_value('font-synthesis', 'none style');
+test_invalid_value('font-synthesis', 'style none');
+test_invalid_value('font-synthesis', 'none small-caps');
+test_invalid_value('font-synthesis', 'small-caps none');
 </script>
 </body>
 </html>
--- a/testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-valid.html
+++ b/testing/web-platform/tests/css/css-fonts/parsing/font-synthesis-valid.html
@@ -1,20 +1,26 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
-<title>CSS Fonts Module Level 3: parsing font-synthesis with valid values</title>
-<link rel="help" href="https://www.w3.org/TR/css-fonts-3/#font-synthesis-prop">
-<meta name="assert" content="font-synthesis supports the full grammar 'none | [ weight || style ]'.">
+<title>CSS Fonts Module: parsing font-synthesis with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-synthesis">
+<meta name="assert" content="font-synthesis supports the full grammar 'none | [ weight || style || small-caps ]'.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
 </head>
 <body>
 <script>
 test_valid_value('font-synthesis', 'none');
 test_valid_value('font-synthesis', 'weight');
 test_valid_value('font-synthesis', 'style');
+test_valid_value('font-synthesis', 'small-caps');
 test_valid_value('font-synthesis', 'style weight', 'weight style');
+test_valid_value('font-synthesis', 'small-caps weight', 'weight small-caps');
+test_valid_value('font-synthesis', 'small-caps style', 'style small-caps');
+test_valid_value('font-synthesis', 'style weight small-caps', 'weight style small-caps');
+test_valid_value('font-synthesis', 'style small-caps weight ', 'weight style small-caps');
+test_valid_value('font-synthesis', 'small-caps style weight ', 'weight style small-caps');
 </script>
 </body>
 </html>