diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 95d8f309934..7de0d61a252 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -809,33 +809,26 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND); } - if self - .author_specified - .contains(LonghandId::FontFamily) - { + if self.author_specified.contains(LonghandId::FontFamily) { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY); } - if self - .author_specified - .contains(LonghandId::LetterSpacing) - { + if self.author_specified.contains(LonghandId::LetterSpacing) { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING); } - if self - .author_specified - .contains(LonghandId::WordSpacing) - { + if self.author_specified.contains(LonghandId::WordSpacing) { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING); } #[cfg(feature = "gecko")] - if self - .author_specified - .contains(LonghandId::FontSynthesis) - { - builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS); + if self.author_specified.contains(LonghandId::FontSynthesisWeight) { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT); + } + + #[cfg(feature = "gecko")] + if self.author_specified.contains(LonghandId::FontSynthesisStyle) { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE); } #[cfg(feature = "servo")] diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index 056245f35a1..9b834efc222 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -90,28 +90,34 @@ bitflags! { /// Whether there are author-specified rules for `font-family`. const HAS_AUTHOR_SPECIFIED_FONT_FAMILY = 1 << 16; - /// Whether there are author-specified rules for `font-synthesis`. - const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS = 1 << 17; + /// Whether there are author-specified rules for `font-synthesis-weight`. + const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT = 1 << 17; + + /// Whether there are author-specified rules for `font-synthesis-style`. + const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE = 1 << 18; + + // (There's also font-synthesis-small-caps, but we don't currently need to + // keep track of that.) /// Whether there are author-specified rules for `letter-spacing`. - const HAS_AUTHOR_SPECIFIED_LETTER_SPACING = 1 << 18; + const HAS_AUTHOR_SPECIFIED_LETTER_SPACING = 1 << 19; /// Whether there are author-specified rules for `word-spacing`. - const HAS_AUTHOR_SPECIFIED_WORD_SPACING = 1 << 19; + const HAS_AUTHOR_SPECIFIED_WORD_SPACING = 1 << 20; /// Whether the style depends on viewport units. - const USES_VIEWPORT_UNITS = 1 << 20; + const USES_VIEWPORT_UNITS = 1 << 21; /// Whether the style depends on viewport units on container queries. /// /// This needs to be a separate flag from `USES_VIEWPORT_UNITS` because /// it causes us to re-match the style (rather than re-cascascading it, /// which is enough for other uses of viewport units). - const USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES = 1 << 21; + const USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES = 1 << 22; /// A flag used to mark styles which have `container-type` of `size` or /// `inline-size`, or under one. - const SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE = 1 << 22; + const SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE = 1 << 23; } } diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 642e1b4ebcc..29d217bfb5b 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -884,7 +884,6 @@ class PropertyRestrictions: "text-combine-upright", "ruby-position", # XXX Should these really apply to cue? - "font-synthesis", "-moz-osx-font-smoothing", # FIXME(emilio): background-blend-mode should be part of the # background shorthand, and get reset, per @@ -895,6 +894,7 @@ class PropertyRestrictions: + PropertyRestrictions.shorthand(data, "background") + PropertyRestrictions.shorthand(data, "outline") + PropertyRestrictions.shorthand(data, "font") + + PropertyRestrictions.shorthand(data, "font-synthesis") ) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 1f4fb6fd82d..116ed004b7b 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -837,7 +837,7 @@ fn static_assert() { <% skip_font_longhands = """font-family font-size font-size-adjust font-weight - font-style font-stretch font-synthesis -x-lang + font-style font-stretch -x-lang font-variant-alternates font-variant-east-asian font-variant-ligatures font-variant-numeric font-language-override font-feature-settings @@ -909,8 +909,6 @@ fn static_assert() { ${impl_simple('font_stretch', 'mFont.stretch')} ${impl_simple('font_style', 'mFont.style')} - ${impl_simple_type_with_conversion("font_synthesis", "mFont.synthesis")} - ${impl_simple("font_variant_alternates", "mFont.variantAlternates")} ${impl_simple("font_size_adjust", "mFont.sizeAdjust")} diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs index e987a42b652..43744c078a1 100644 --- a/components/style/properties/longhands/font.mako.rs +++ b/components/style/properties/longhands/font.mako.rs @@ -81,12 +81,36 @@ ${helpers.predefined_type( )} ${helpers.predefined_type( - "font-synthesis", + "font-synthesis-weight", "FontSynthesis", engines="gecko", - initial_value="specified::FontSynthesis::get_initial_value()", + initial_value="computed::FontSynthesis::Auto", + initial_specified_value="specified::FontSynthesis::Auto", + gecko_ffi_name="mFont.synthesisWeight", animation_value_type="discrete", - spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis", + spec="https://drafts.csswg.org/css-fonts-4/#font-synthesis-weight", +)} + +${helpers.predefined_type( + "font-synthesis-style", + "FontSynthesis", + engines="gecko", + initial_value="computed::FontSynthesis::Auto", + initial_specified_value="specified::FontSynthesis::Auto", + gecko_ffi_name="mFont.synthesisStyle", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-fonts-4/#font-synthesis-style", +)} + +${helpers.predefined_type( + "font-synthesis-small-caps", + "FontSynthesis", + engines="gecko", + initial_value="computed::FontSynthesis::Auto", + initial_specified_value="specified::FontSynthesis::Auto", + gecko_ffi_name="mFont.synthesisSmallCaps", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-fonts-4/#font-synthesis-small-caps", )} ${helpers.predefined_type( diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs index 5a174d63f64..7b87125d065 100644 --- a/components/style/properties/shorthands/font.mako.rs +++ b/components/style/properties/shorthands/font.mako.rs @@ -456,3 +456,84 @@ } } + +<%helpers:shorthand name="font-synthesis" + engines="gecko" + flags="SHORTHAND_IN_GETCS" + sub_properties="font-synthesis-weight font-synthesis-style font-synthesis-small-caps" + derive_value_info="False" + spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant"> + <% sub_properties = ["weight", "style", "small_caps"] %> + + use crate::values::specified::FontSynthesis; + + pub fn parse_value<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + % for prop in sub_properties: + let mut ${prop} = FontSynthesis::None; + % endfor + + if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { + // Leave all the individual values as None + } else { + let mut has_custom_value = false; + while !input.is_exhausted() { + try_match_ident_ignore_ascii_case! { input, + % for prop in sub_properties: + "${prop.replace('_', '-')}" if ${prop} == FontSynthesis::None => { + has_custom_value = true; + ${prop} = FontSynthesis::Auto; + continue; + }, + % endfor + } + } + if !has_custom_value { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + } + + Ok(expanded! { + % for prop in sub_properties: + font_synthesis_${prop}: ${prop}, + % endfor + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + let mut has_any = false; + + % for prop in sub_properties: + if self.font_synthesis_${prop} == &FontSynthesis::Auto { + if has_any { + dest.write_char(' ')?; + } + has_any = true; + dest.write_str("${prop.replace('_', '-')}")?; + } + % endfor + + if !has_any { + dest.write_str("none")?; + } + + Ok(()) + } + } + + // The shorthand takes the sub-property names of the longhands, and not the + // 'auto' keyword like they do, so we can't automatically derive this. + impl SpecifiedValueInfo for Longhands { + fn collect_completion_keywords(f: KeywordsCollectFn) { + f(&[ + "none", + % for prop in sub_properties: + "${prop.replace('_', '-')}", + % endfor + ]); + } + } + diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index 946c41705eb..e6ace1864bf 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -869,12 +869,8 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { if !is_legacy_marker { return; } - if !self - .style - .flags - .get() - .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) - { + let flags = self.style.flags.get(); + if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) { self.style .mutate_font() .set_font_family(FontFamily::moz_bullet().clone()); @@ -882,33 +878,23 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { // FIXME(mats): We can remove this if support for font-synthesis is added to @font-face rules. // Then we can add it to the @font-face rule in html.css instead. // https://github.com/w3c/csswg-drafts/issues/6081 - if !self - .style - .flags - .get() - .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS) - { + if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT) { self.style .mutate_font() - .set_font_synthesis(FontSynthesis::none()); + .set_font_synthesis_weight(FontSynthesis::None); + } + if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE) { + self.style + .mutate_font() + .set_font_synthesis_style(FontSynthesis::None); } } - if !self - .style - .flags - .get() - .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) - { + if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) { self.style .mutate_inherited_text() .set_letter_spacing(LetterSpacing::normal()); } - if !self - .style - .flags - .get() - .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) - { + if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) { self.style .mutate_inherited_text() .set_word_spacing(WordSpacing::normal()); diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 543d640551b..c093b4091c8 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -26,8 +26,8 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, ToCss}; pub use crate::values::computed::Length as MozScriptMinSize; -pub use crate::values::specified::font::FontPalette; -pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier}; +pub use crate::values::specified::font::{FontPalette, FontSynthesis}; +pub use crate::values::specified::font::MozScriptSizeMultiplier; pub use crate::values::specified::font::{ FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric, XLang, XTextZoom, diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index f61f1c5abaa..79988675c09 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -1717,155 +1717,6 @@ impl Parse for FontVariantNumeric { /// This property provides low-level control over OpenType or TrueType font features. pub type FontFeatureSettings = FontSettings>; -#[derive( - Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, -)] -/// Whether user agents are allowed to synthesize bold or oblique font 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. - 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. - 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() - } -} - -#[inline] -fn allow_font_synthesis_small_caps() -> bool { - #[cfg(feature = "gecko")] - return static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled"); - #[cfg(feature = "servo")] - return false; -} - -impl Parse for FontSynthesis { - fn parse<'i, 't>( - _: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - 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 && allow_font_synthesis_small_caps() - => 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(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - 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 allow_font_synthesis_small_caps() { - f(&["small-caps"]); - } - } -} - -#[cfg(feature = "gecko")] -impl From 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 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 @@ -1936,6 +1787,29 @@ impl Parse for FontLanguageOverride { } } +/// A value for any of the font-synthesis-{weight,style,small-caps} properties. +#[repr(u8)] +#[derive( + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +pub enum FontSynthesis { + /// This attribute may be synthesized if not supported by a face. + Auto, + /// Do not attempt to synthesis this style attribute. + None, +} + #[derive( Clone, Debug, @@ -1993,7 +1867,6 @@ impl ToCss for FontPalette { /// variations. pub type FontVariationSettings = FontSettings>; - fn parse_one_feature_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>,