From 5d0f7f10e04fca9feb26a45e8b07f1798b75e654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 28 Jun 2017 09:48:56 -0700 Subject: [PATCH] stylo: Fix font-variant-alternates property --- components/style/gecko/generated/bindings.rs | 11 + components/style/properties/gecko.mako.rs | 51 +++- .../style/properties/longhand/font.mako.rs | 253 +++++++++++------- 3 files changed, 220 insertions(+), 95 deletions(-) diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index 3c0d2d94506..96b152df994 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -873,6 +873,17 @@ extern "C" { extern "C" { pub fn Gecko_nsFont_Destroy(dst: *mut nsFont); } +extern "C" { + pub fn Gecko_ClearAlternateValues(font: *mut nsFont, length: usize); +} +extern "C" { + pub fn Gecko_AppendAlternateValues(font: *mut nsFont, alternate_name: u32, + atom: *mut nsIAtom); +} +extern "C" { + pub fn Gecko_CopyAlternateValuesFrom(dest: *mut nsFont, + src: *const nsFont); +} extern "C" { pub fn Gecko_SetImageOrientation(aVisibility: *mut nsStyleVisibility, aRadians: f64, aFlip: bool); diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 3822acb952a..9fa06283acc 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1684,14 +1684,59 @@ fn static_assert() { <% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %> pub fn set_font_variant_alternates(&mut self, v: longhands::font_variant_alternates::computed_value::T) { - self.gecko.mFont.variantAlternates = v.to_gecko_keyword() + use gecko_bindings::bindings::{Gecko_ClearAlternateValues, Gecko_AppendAlternateValues}; + % for value in "normal swash stylistic ornaments annotation styleset character_variant historical".split(): + use gecko_bindings::structs::NS_FONT_VARIANT_ALTERNATES_${value.upper()}; + % endfor + use self::longhands::font_variant_alternates::VariantAlternates; + + unsafe { + Gecko_ClearAlternateValues(&mut self.gecko.mFont, v.len()); + } + + if v.0.is_empty() { + self.gecko.mFont.variantAlternates = NS_FONT_VARIANT_ALTERNATES_NORMAL as u16; + } + + for val in v.0.iter() { + match *val { + % for value in "Swash Stylistic Ornaments Annotation".split(): + VariantAlternates::${value}(ref ident) => { + self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_${value.upper()} as u16; + unsafe { + Gecko_AppendAlternateValues(&mut self.gecko.mFont, + NS_FONT_VARIANT_ALTERNATES_${value.upper()}, + ident.0.as_ptr()); + } + }, + % endfor + % for value in "styleset character_variant".split(): + VariantAlternates::${to_camel_case(value)}(ref slice) => { + self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_${value.upper()} as u16; + for ident in slice.iter() { + unsafe { + Gecko_AppendAlternateValues(&mut self.gecko.mFont, + NS_FONT_VARIANT_ALTERNATES_${value.upper()}, + ident.0.as_ptr()); + } + } + }, + % endfor + VariantAlternates::HistoricalForms => { + self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_HISTORICAL as u16; + } + } + } } #[allow(non_snake_case)] pub fn copy_font_variant_alternates_from(&mut self, other: &Self) { + use gecko_bindings::bindings::Gecko_CopyAlternateValuesFrom; + self.gecko.mFont.variantAlternates = other.gecko.mFont.variantAlternates; - // FIXME: Copy alternateValues as well. - // self.gecko.mFont.alternateValues = other.gecko.mFont.alternateValues; + unsafe { + Gecko_CopyAlternateValuesFrom(&mut self.gecko.mFont, &other.gecko.mFont); + } } ${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")} diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 6cedca5b874..1202d5fdced 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -1272,19 +1272,123 @@ ${helpers.single_keyword_system("font-kerning", spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning", animation_value_type="discrete")} -/// FIXME: Implement proper handling of each values. -/// https://github.com/servo/servo/issues/15957 <%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="none" spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates"> use properties::longhands::system_font::SystemFont; use std::fmt; use style_traits::ToCss; + use values::CustomIdent; no_viewport_percentage!(SpecifiedValue); + #[derive(PartialEq, Clone, Debug)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub enum VariantAlternates { + Stylistic(CustomIdent), + Styleset(Box<[CustomIdent]>), + CharacterVariant(Box<[CustomIdent]>), + Swash(CustomIdent), + Ornaments(CustomIdent), + Annotation(CustomIdent), + HistoricalForms, + } + + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub struct VariantAlternatesList(pub Box<[VariantAlternates]>); + + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub enum SpecifiedValue { + Value(VariantAlternatesList), + System(SystemFont) + } + + <%self:simple_system_boilerplate name="font_variant_alternates"> + + impl ToCss for VariantAlternates { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + % for value in "swash stylistic ornaments annotation".split(): + VariantAlternates::${to_camel_case(value)}(ref atom) => { + dest.write_str("${value}")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + % endfor + % for value in "styleset character-variant".split(): + VariantAlternates::${to_camel_case(value)}(ref vec) => { + dest.write_str("${value}")?; + dest.write_str("(")?; + let mut iter = vec.iter(); + iter.next().unwrap().to_css(dest)?; + for c in iter { + dest.write_str(", ")?; + c.to_css(dest)?; + } + dest.write_str(")") + }, + % endfor + VariantAlternates::HistoricalForms => { + dest.write_str("historical-forms") + }, + } + } + } + + impl ToCss for VariantAlternatesList { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.0.is_empty() { + return dest.write_str("normal"); + } + + let mut iter = self.0.iter(); + iter.next().unwrap().to_css(dest)?; + for alternate in iter { + dest.write_str(" ")?; + alternate.to_css(dest)?; + } + Ok(()) + } + } + + impl VariantAlternatesList { + /// Returns the length of all variant alternates. + pub fn len(&self) -> usize { + self.0.iter().fold(0, |acc, alternate| { + match *alternate { + % for value in "Swash Stylistic Ornaments Annotation".split(): + VariantAlternates::${value}(_) => { + acc + 1 + }, + % endfor + % for value in "Styleset CharacterVariant".split(): + VariantAlternates::${value}(ref slice) => { + acc + slice.len() + } + % endfor + _ => acc, + } + }) + } + } + + pub mod computed_value { + pub type T = super::VariantAlternatesList; + } + #[inline] + pub fn get_initial_value() -> computed_value::T { + VariantAlternatesList(vec![].into_boxed_slice()) + } + #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + SpecifiedValue::Value(VariantAlternatesList(vec![].into_boxed_slice())) + } + bitflags! { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub flags VariantAlternates: u8 { + pub flags ParsingFlags: u8 { const NORMAL = 0, const HISTORICAL_FORMS = 0x01, const STYLISTIC = 0x02, @@ -1296,69 +1400,6 @@ ${helpers.single_keyword_system("font-kerning", } } - #[derive(Debug, Clone, PartialEq)] - pub enum SpecifiedValue { - Value(VariantAlternates), - System(SystemFont) - } - - <%self:simple_system_boilerplate name="font_variant_alternates"> - - <% font_variant_alternates_map = { "HISTORICAL_FORMS": "HISTORICAL", - "STYLISTIC": "STYLISTIC", - "STYLESET": "STYLESET", - "CHARACTER_VARIANT": "CHARACTER_VARIANT", - "SWASH": "SWASH", - "ORNAMENTS": "ORNAMENTS", - "ANNOTATION": "ANNOTATION" } %> - ${helpers.gecko_bitflags_conversion(font_variant_alternates_map, 'NS_FONT_VARIANT_ALTERNATES_', - 'VariantAlternates', kw_type='u16')} - - impl ToCss for VariantAlternates { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.is_empty() { - return dest.write_str("normal") - } - - let mut has_any = false; - - macro_rules! write_value { - ($ident:ident => $str:expr) => { - if self.intersects($ident) { - if has_any { - dest.write_str(" ")?; - } - has_any = true; - dest.write_str($str)?; - } - } - } - - write_value!(HISTORICAL_FORMS => "historical-forms"); - write_value!(STYLISTIC => "stylistic"); - write_value!(STYLESET => "styleset"); - write_value!(CHARACTER_VARIANT => "character-variant"); - write_value!(SWASH => "swash"); - write_value!(ORNAMENTS => "ornaments"); - write_value!(ANNOTATION => "annotation"); - - debug_assert!(has_any); - Ok(()) - } - } - - pub mod computed_value { - pub type T = super::VariantAlternates; - } - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T::empty() - } - #[inline] - pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue::Value(VariantAlternates::empty()) - } - /// normal | /// [ stylistic() || /// historical-forms || @@ -1369,35 +1410,63 @@ ${helpers.single_keyword_system("font-kerning", /// annotation() ] pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - let mut result = VariantAlternates::empty(); - + let mut alternates = Vec::new(); if input.try(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(SpecifiedValue::Value(result)) + return Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice()))); } - while let Ok(flag) = input.try(|input| { - Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?, - "stylistic" => STYLISTIC, - "historical-forms" => HISTORICAL_FORMS, - "styleset" => STYLESET, - "character-variant" => CHARACTER_VARIANT, - "swash" => SWASH, - "ornaments" => ORNAMENTS, - "annotation" => ANNOTATION, - _ => return Err(()), - }) - }) { - if result.intersects(flag) { - return Err(StyleParseError::UnspecifiedError.into()) + let mut parsed_alternates = ParsingFlags::empty(); + macro_rules! check_if_parsed( + ($flag:ident) => ( + if parsed_alternates.contains($flag) { + return Err(StyleParseError::UnspecifiedError.into()) + } + parsed_alternates |= $flag; + ) + ); + while let Ok(_) = input.try(|input| { + match input.next()? { + Token::Ident(ident) => { + if ident == "historical-forms" { + check_if_parsed!(HISTORICAL_FORMS); + alternates.push(VariantAlternates::HistoricalForms); + Ok(()) + } else { + return Err(StyleParseError::UnspecifiedError.into()); + } + }, + Token::Function(name) => { + input.parse_nested_block(|i| { + match_ignore_ascii_case! { &name, + % for value in "swash stylistic ornaments annotation".split(): + "${value}" => { + check_if_parsed!(${value.upper()}); + let ident = CustomIdent::from_ident(i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::${to_camel_case(value)}(ident)); + Ok(()) + }, + % endfor + % for value in "styleset character-variant".split(): + "${value}" => { + check_if_parsed!(${to_rust_ident(value).upper()}); + let idents = i.parse_comma_separated(|i| + CustomIdent::from_ident(i.expect_ident()?, &[]))?; + alternates.push(VariantAlternates::${to_camel_case(value)}(idents.into_boxed_slice())); + Ok(()) + }, + % endfor + _ => return Err(StyleParseError::UnspecifiedError.into()), + } + }) + }, + _ => Err(StyleParseError::UnspecifiedError.into()), } - result.insert(flag); - } + }) { } - if !result.is_empty() { - Ok(SpecifiedValue::Value(result)) - } else { - Err(StyleParseError::UnspecifiedError.into()) + if parsed_alternates.is_empty() { + return Err(StyleParseError::UnspecifiedError.into()); } + Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice()))) } @@ -2347,9 +2416,8 @@ ${helpers.single_keyword("-moz-math-variant", -moz-info -moz-dialog -moz-button -moz-pull-down-menu -moz-list -moz-field""".split() kw_font_props = """font_style font_variant_caps font_stretch - font_kerning font_variant_position font_variant_alternates - font_variant_ligatures font_variant_east_asian - font_variant_numeric""".split() + font_kerning font_variant_position font_variant_ligatures + font_variant_east_asian font_variant_numeric""".split() kw_cast = """font_style font_variant_caps font_stretch font_kerning font_variant_position""".split() %> @@ -2428,6 +2496,7 @@ ${helpers.single_keyword("-moz-math-variant", font_language_override: longhands::font_language_override::computed_value ::T(system.languageOverride), font_feature_settings: longhands::font_feature_settings::get_initial_value(), + font_variant_alternates: longhands::font_variant_alternates::get_initial_value(), system_font: *self, }; unsafe { bindings::Gecko_nsFont_Destroy(&mut system); }