From 3ff497aaff54f975cbe62f559e1184b38e3708fd Mon Sep 17 00:00:00 2001 From: CYBAI Date: Fri, 17 Nov 2017 02:04:44 +0800 Subject: [PATCH] style: Move font-variant-ligatures outside of mako --- .../style/properties/longhand/font.mako.rs | 169 +----------- .../style/properties/shorthand/font.mako.rs | 6 +- components/style/values/computed/font.rs | 3 + components/style/values/computed/mod.rs | 2 +- components/style/values/specified/font.rs | 255 ++++++++++++++++++ components/style/values/specified/mod.rs | 2 +- ports/geckolib/glue.rs | 1 + 7 files changed, 273 insertions(+), 165 deletions(-) diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 428d9d8c1c1..2d05a02ce90 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -699,167 +699,14 @@ ${helpers.predefined_type("font-variant-east-asian", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian")} -<%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="discrete" - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" - spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures"> - use properties::longhands::system_font::SystemFont; - use std::fmt; - use style_traits::ToCss; - - - bitflags! { - #[derive(MallocSizeOf)] - pub struct VariantLigatures: u16 { - const NORMAL = 0; - const NONE = 0x01; - const COMMON_LIGATURES = 0x02; - const NO_COMMON_LIGATURES = 0x04; - const DISCRETIONARY_LIGATURES = 0x08; - const NO_DISCRETIONARY_LIGATURES = 0x10; - const HISTORICAL_LIGATURES = 0x20; - const NO_HISTORICAL_LIGATURES = 0x40; - const CONTEXTUAL = 0x80; - const NO_CONTEXTUAL = 0x100; - } - } - - #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] - #[derive(Clone, Debug, PartialEq, ToCss)] - pub enum SpecifiedValue { - Value(VariantLigatures), - System(SystemFont) - } - - <%self:simple_system_boilerplate name="font_variant_ligatures"> - - // servo_bit: gecko_bit - <% font_variant_ligatures_map = { "VariantLigatures::NONE": "NONE", - "VariantLigatures::COMMON_LIGATURES": "COMMON", - "VariantLigatures::NO_COMMON_LIGATURES": "NO_COMMON", - "VariantLigatures::DISCRETIONARY_LIGATURES": "DISCRETIONARY", - "VariantLigatures::NO_DISCRETIONARY_LIGATURES": "NO_DISCRETIONARY", - "VariantLigatures::HISTORICAL_LIGATURES": "HISTORICAL", - "VariantLigatures::NO_HISTORICAL_LIGATURES": "NO_HISTORICAL", - "VariantLigatures::CONTEXTUAL": "CONTEXTUAL", - "VariantLigatures::NO_CONTEXTUAL": "NO_CONTEXTUAL" } %> - - ${helpers.gecko_bitflags_conversion(font_variant_ligatures_map, 'NS_FONT_VARIANT_LIGATURES_', - 'VariantLigatures', kw_type='u16')} - - impl ToCss for VariantLigatures { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.is_empty() { - return dest.write_str("normal") - } - if self.contains(VariantLigatures::NONE) { - return dest.write_str("none") - } - - let mut has_any = false; - - macro_rules! write_value { - ($ident:path => $str:expr) => { - if self.intersects($ident) { - if has_any { - dest.write_str(" ")?; - } - has_any = true; - dest.write_str($str)?; - } - } - } - - write_value!(VariantLigatures::COMMON_LIGATURES => "common-ligatures"); - write_value!(VariantLigatures::NO_COMMON_LIGATURES => "no-common-ligatures"); - write_value!(VariantLigatures::DISCRETIONARY_LIGATURES => "discretionary-ligatures"); - write_value!(VariantLigatures::NO_DISCRETIONARY_LIGATURES => "no-discretionary-ligatures"); - write_value!(VariantLigatures::HISTORICAL_LIGATURES => "historical-ligatures"); - write_value!(VariantLigatures::NO_HISTORICAL_LIGATURES => "no-historical-ligatures"); - write_value!(VariantLigatures::CONTEXTUAL => "contextual"); - write_value!(VariantLigatures::NO_CONTEXTUAL => "no-contextual"); - - debug_assert!(has_any); - Ok(()) - } - } - - pub mod computed_value { - pub type T = super::VariantLigatures; - } - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T::empty() - } - #[inline] - pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue::Value(VariantLigatures::empty()) - } - #[inline] - pub fn get_none_specified_value() -> SpecifiedValue { - SpecifiedValue::Value(VariantLigatures::NONE) - } - - /// normal | none | - /// [ || - /// || - /// || - /// ] - /// = [ common-ligatures | no-common-ligatures ] - /// = [ discretionary-ligatures | no-discretionary-ligatures ] - /// = [ historical-ligatures | no-historical-ligatures ] - /// = [ contextual | no-contextual ] - <% common_lig_values = "VariantLigatures::COMMON_LIGATURES | VariantLigatures::NO_COMMON_LIGATURES" %> - <% discretionary_lig_values = """VariantLigatures::DISCRETIONARY_LIGATURES | - VariantLigatures::NO_DISCRETIONARY_LIGATURES""" %> - <% historical_lig_values = "VariantLigatures::HISTORICAL_LIGATURES | VariantLigatures::NO_HISTORICAL_LIGATURES" %> - <% contextual_alt_values = "VariantLigatures::CONTEXTUAL | VariantLigatures::NO_CONTEXTUAL" %> - pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result> { - let mut result = VariantLigatures::empty(); - - if input.try(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(SpecifiedValue::Value(result)) - } - if input.try(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(SpecifiedValue::Value(VariantLigatures::NONE)) - } - - while let Ok(flag) = input.try(|input| { - Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?, - "common-ligatures" => - exclusive_value!((result, ${common_lig_values}) => VariantLigatures::COMMON_LIGATURES), - "no-common-ligatures" => - exclusive_value!((result, ${common_lig_values}) => VariantLigatures::NO_COMMON_LIGATURES), - "discretionary-ligatures" => - exclusive_value!((result, ${discretionary_lig_values}) => - VariantLigatures::DISCRETIONARY_LIGATURES), - "no-discretionary-ligatures" => - exclusive_value!((result, ${discretionary_lig_values}) => - VariantLigatures::NO_DISCRETIONARY_LIGATURES), - "historical-ligatures" => - exclusive_value!((result, ${historical_lig_values}) => VariantLigatures::HISTORICAL_LIGATURES), - "no-historical-ligatures" => - exclusive_value!((result, ${historical_lig_values}) => VariantLigatures::NO_HISTORICAL_LIGATURES), - "contextual" => - exclusive_value!((result, ${contextual_alt_values}) => VariantLigatures::CONTEXTUAL), - "no-contextual" => - exclusive_value!((result, ${contextual_alt_values}) => VariantLigatures::NO_CONTEXTUAL), - _ => return Err(()), - }) - }) { - result.insert(flag); - } - - if !result.is_empty() { - Ok(SpecifiedValue::Value(result)) - } else { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - } - - #[cfg(feature = "gecko")] - impl_gecko_keyword_conversions!(VariantLigatures, u16); - +${helpers.predefined_type("font-variant-ligatures", + "FontVariantLigatures", + products="gecko", + initial_value="computed::FontVariantLigatures::empty()", + initial_specified_value="specified::FontVariantLigatures::empty()", + animation_value_type="discrete", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures")} <%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="discrete" flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" diff --git a/components/style/properties/shorthand/font.mako.rs b/components/style/properties/shorthand/font.mako.rs index 33eded1e5a0..716dad9dce0 100644 --- a/components/style/properties/shorthand/font.mako.rs +++ b/components/style/properties/shorthand/font.mako.rs @@ -258,6 +258,8 @@ % for prop in sub_properties: use properties::longhands::font_variant_${prop}; % endfor + #[allow(unused_imports)] + use values::specified::FontVariantLigatures; pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { @@ -271,7 +273,7 @@ // The 'none' value sets 'font-variant-ligatures' to 'none' and resets all other sub properties // to their initial value. % if product == "gecko": - ligatures = Some(font_variant_ligatures::get_none_specified_value()); + ligatures = Some(FontVariantLigatures::none()); % endif } else { let mut has_custom_value: bool = false; @@ -311,7 +313,7 @@ let has_none_ligatures = % if product == "gecko": - self.font_variant_ligatures == &font_variant_ligatures::get_none_specified_value(); + self.font_variant_ligatures == &FontVariantLigatures::none(); % else: false; % endif diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 70981d6bf74..e0e7a6e50f6 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -280,6 +280,9 @@ impl FontVariantAlternates { /// Use VariantEastAsian as computed type of FontVariantEastAsian pub type FontVariantEastAsian = specified::VariantEastAsian; +/// Use VariantLigatures as computed type of FontVariantLigatures +pub type FontVariantLigatures = specified::VariantLigatures; + /// font-language-override can only have a single three-letter /// OpenType "language system" tag, so we should be able to compute /// it and store it as a 32-bit integer diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index d5dda639464..2ddd5af7ce9 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -38,7 +38,7 @@ pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth} pub use self::border::{BorderRadius, BorderCornerRadius, BorderSpacing}; pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; pub use self::font::{FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; -pub use self::font::{MozScriptLevel, MozScriptMinSize, XTextZoom}; +pub use self::font::{FontVariantLigatures, MozScriptLevel, MozScriptMinSize, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, ScrollSnapType, VerticalAlign}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 839978e1222..37230ad15ae 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -1095,6 +1095,261 @@ impl Parse for FontVariantEastAsian { } } +bitflags! { + #[derive(MallocSizeOf)] + /// Variants of ligatures + pub struct VariantLigatures: u16 { + /// Specifies that common default features are enabled + const NORMAL = 0; + /// Specifies that all types of ligatures and contextual forms + /// covered by this property are explicitly disabled + const NONE = 0x01; + /// Enables display of common ligatures + const COMMON_LIGATURES = 0x02; + /// Disables display of common ligatures + const NO_COMMON_LIGATURES = 0x04; + /// Enables display of discretionary ligatures + const DISCRETIONARY_LIGATURES = 0x08; + /// Disables display of discretionary ligatures + const NO_DISCRETIONARY_LIGATURES = 0x10; + /// Enables display of historical ligatures + const HISTORICAL_LIGATURES = 0x20; + /// Disables display of historical ligatures + const NO_HISTORICAL_LIGATURES = 0x40; + /// Enables display of contextual alternates + const CONTEXTUAL = 0x80; + /// Disables display of contextual alternates + const NO_CONTEXTUAL = 0x100; + } +} + +#[cfg(feature = "gecko")] +impl VariantLigatures { + /// Obtain a specified value from a Gecko keyword value + /// + /// Intended for use with presentation attributes, not style structs + pub fn from_gecko_keyword(kw: u16) -> Self { + Self::from_bits_truncate(kw) + } + + /// Transform into gecko keyword + pub fn to_gecko_keyword(self) -> u16 { + self.bits() + } +} + +impl ToCss for VariantLigatures { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.is_empty() { + return dest.write_str("normal") + } + if self.contains(VariantLigatures::NONE) { + return dest.write_str("none") + } + + let mut has_any = false; + + macro_rules! write_value { + ($ident:path => $str:expr) => { + if self.intersects($ident) { + if has_any { + dest.write_str(" ")?; + } + has_any = true; + dest.write_str($str)?; + } + } + } + + write_value!(VariantLigatures::COMMON_LIGATURES => "common-ligatures"); + write_value!(VariantLigatures::NO_COMMON_LIGATURES => "no-common-ligatures"); + write_value!(VariantLigatures::DISCRETIONARY_LIGATURES => "discretionary-ligatures"); + write_value!(VariantLigatures::NO_DISCRETIONARY_LIGATURES => "no-discretionary-ligatures"); + write_value!(VariantLigatures::HISTORICAL_LIGATURES => "historical-ligatures"); + write_value!(VariantLigatures::NO_HISTORICAL_LIGATURES => "no-historical-ligatures"); + write_value!(VariantLigatures::CONTEXTUAL => "contextual"); + write_value!(VariantLigatures::NO_CONTEXTUAL => "no-contextual"); + + debug_assert!(has_any); + Ok(()) + } +} + +#[cfg(feature = "gecko")] +impl From for VariantLigatures { + fn from(bits: u16) -> VariantLigatures { + VariantLigatures::from_gecko_keyword(bits) + } +} + +#[cfg(feature = "gecko")] +impl From for u16 { + fn from(v: VariantLigatures) -> u16 { + v.to_gecko_keyword() + } +} + +/// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value. +#[cfg(feature = "gecko")] +#[inline] +pub fn assert_variant_ligatures_matches() { + use gecko_bindings::structs; + + macro_rules! check_variant_ligatures { + ( $( $a:ident => $b:path),*, ) => { + if cfg!(debug_assertions) { + $( + assert_eq!(structs::$a as u16, $b.bits()); + )* + } + } + } + + check_variant_ligatures! { + NS_FONT_VARIANT_LIGATURES_NONE => VariantLigatures::NONE, + NS_FONT_VARIANT_LIGATURES_COMMON => VariantLigatures::COMMON_LIGATURES, + NS_FONT_VARIANT_LIGATURES_NO_COMMON => VariantLigatures::NO_COMMON_LIGATURES, + NS_FONT_VARIANT_LIGATURES_DISCRETIONARY => VariantLigatures::DISCRETIONARY_LIGATURES, + NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY => VariantLigatures::NO_DISCRETIONARY_LIGATURES, + NS_FONT_VARIANT_LIGATURES_HISTORICAL => VariantLigatures::HISTORICAL_LIGATURES, + NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL => VariantLigatures::NO_HISTORICAL_LIGATURES, + NS_FONT_VARIANT_LIGATURES_CONTEXTUAL => VariantLigatures::CONTEXTUAL, + NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL => VariantLigatures::NO_CONTEXTUAL, + } +} + +#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] +#[derive(Clone, Debug, PartialEq, ToCss)] +/// Ligatures and contextual forms are ways of combining glyphs +/// to produce more harmonized forms +pub enum FontVariantLigatures { + /// Value variant with `variant-ligatures` + Value(VariantLigatures), + /// System font variant + System(SystemFont) +} + +impl FontVariantLigatures { + /// Get `font-variant-ligatures` with system font + pub fn system_font(f: SystemFont) -> Self { + FontVariantLigatures::System(f) + } + + /// Get system font + pub fn get_system(&self) -> Option { + if let FontVariantLigatures::System(s) = *self { + Some(s) + } else { + None + } + } + + #[inline] + /// Default value of `font-variant-ligatures` as `empty` + pub fn empty() -> FontVariantLigatures { + FontVariantLigatures::Value(VariantLigatures::empty()) + } + + #[inline] + /// Get `none` variant of `font-variant-ligatures` + pub fn none() -> FontVariantLigatures { + FontVariantLigatures::Value(VariantLigatures::NONE) + } +} + +impl ToComputedValue for FontVariantLigatures { + type ComputedValue = computed::FontVariantLigatures; + + fn to_computed_value(&self, _context: &Context) -> computed::FontVariantLigatures { + match *self { + FontVariantLigatures::Value(ref v) => v.clone(), + FontVariantLigatures::System(_) => { + #[cfg(feature = "gecko")] { + _context.cached_system_font.as_ref().unwrap().font_variant_ligatures.clone() + } + #[cfg(feature = "servo")] { + unreachable!() + } + } + } + } + + fn from_computed_value(other: &computed::FontVariantLigatures) -> Self { + FontVariantLigatures::Value(other.clone()) + } +} + +impl Parse for FontVariantLigatures { + /// normal | none | + /// [ || + /// || + /// || + /// ] + /// = [ common-ligatures | no-common-ligatures ] + /// = [ discretionary-ligatures | no-discretionary-ligatures ] + /// = [ historical-ligatures | no-historical-ligatures ] + /// = [ contextual | no-contextual ] + fn parse<'i, 't> ( + _context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + let mut result = VariantLigatures::empty(); + + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + return Ok(FontVariantLigatures::Value(result)) + } + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(FontVariantLigatures::Value(VariantLigatures::NONE)) + } + + while let Ok(flag) = input.try(|input| { + Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?, + "common-ligatures" => + exclusive_value!((result, VariantLigatures::COMMON_LIGATURES | + VariantLigatures::NO_COMMON_LIGATURES + ) => VariantLigatures::COMMON_LIGATURES), + "no-common-ligatures" => + exclusive_value!((result, VariantLigatures::COMMON_LIGATURES | + VariantLigatures::NO_COMMON_LIGATURES + ) => VariantLigatures::NO_COMMON_LIGATURES), + "discretionary-ligatures" => + exclusive_value!((result, VariantLigatures::DISCRETIONARY_LIGATURES | + VariantLigatures::NO_DISCRETIONARY_LIGATURES + ) => VariantLigatures::DISCRETIONARY_LIGATURES), + "no-discretionary-ligatures" => + exclusive_value!((result, VariantLigatures::DISCRETIONARY_LIGATURES | + VariantLigatures::NO_DISCRETIONARY_LIGATURES + ) => VariantLigatures::NO_DISCRETIONARY_LIGATURES), + "historical-ligatures" => + exclusive_value!((result, VariantLigatures::HISTORICAL_LIGATURES | + VariantLigatures::NO_HISTORICAL_LIGATURES + ) => VariantLigatures::HISTORICAL_LIGATURES), + "no-historical-ligatures" => + exclusive_value!((result, VariantLigatures::HISTORICAL_LIGATURES | + VariantLigatures::NO_HISTORICAL_LIGATURES + ) => VariantLigatures::NO_HISTORICAL_LIGATURES), + "contextual" => + exclusive_value!((result, VariantLigatures::CONTEXTUAL | + VariantLigatures::NO_CONTEXTUAL + ) => VariantLigatures::CONTEXTUAL), + "no-contextual" => + exclusive_value!((result, VariantLigatures::CONTEXTUAL | + VariantLigatures::NO_CONTEXTUAL + ) => VariantLigatures::NO_CONTEXTUAL), + _ => return Err(()), + }) + }) { + result.insert(flag); + } + + if !result.is_empty() { + Ok(FontVariantLigatures::Value(result)) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + } +} + #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)] /// Whether user agents are allowed to synthesize bold or oblique font faces /// when a font family lacks bold or italic faces diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index d85ecb7d435..7bd01722e9d 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -32,7 +32,7 @@ pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth, BorderSpacing}; pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; pub use self::font::{FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; -pub use self::font::{MozScriptLevel, MozScriptMinSize, XTextZoom}; +pub use self::font::{FontVariantLigatures, MozScriptLevel, MozScriptMinSize, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, ScrollSnapType, VerticalAlign}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index f002d440185..415aff19f68 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -187,6 +187,7 @@ pub extern "C" fn Servo_Initialize(dummy_url_data: *mut URLExtraData) { parser::assert_parsing_mode_match(); traversal_flags::assert_traversal_flags_match(); specified::font::assert_variant_east_asian_matches(); + specified::font::assert_variant_ligatures_matches(); // Initialize the dummy url data unsafe { DUMMY_URL_DATA = dummy_url_data; }