diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 03d7cc469156..022507fdce0d 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -2402,7 +2402,7 @@ 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, + v: values::computed::font::FontVariantAlternates, device: &Device) { use gecko_bindings::bindings::{Gecko_ClearAlternateValues, Gecko_AppendAlternateValues}; use gecko_bindings::bindings::Gecko_nsFont_ResetFontFeatureValuesLookup; @@ -2410,7 +2410,7 @@ fn static_assert() { % 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; + use values::specified::font::VariantAlternates; unsafe { Gecko_ClearAlternateValues(&mut self.gecko.mFont, v.len()); @@ -2471,13 +2471,13 @@ fn static_assert() { self.copy_font_variant_alternates_from(other) } - pub fn clone_font_variant_alternates(&self) -> longhands::font_variant_alternates::computed_value::T { + pub fn clone_font_variant_alternates(&self) -> values::computed::font::FontVariantAlternates { use Atom; % 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 properties::longhands::font_variant_alternates::VariantAlternates; - use properties::longhands::font_variant_alternates::VariantAlternatesList; + use values::specified::font::VariantAlternates; + use values::specified::font::VariantAlternatesList; use values::CustomIdent; if self.gecko.mFont.variantAlternates == NS_FONT_VARIANT_ALTERNATES_NORMAL as u16 { diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index acf9cf334605..08310021cdc8 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -670,204 +670,14 @@ ${helpers.single_keyword_system("font-kerning", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", animation_value_type="discrete")} -<%helpers:longhand name="font-variant-alternates" 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-alternates"> - use properties::longhands::system_font::SystemFont; - use std::fmt; - use style_traits::ToCss; - use values::CustomIdent; - - - #[derive(Clone, Debug, MallocSizeOf, PartialEq)] - pub enum VariantAlternates { - Stylistic(CustomIdent), - Styleset(Box<[CustomIdent]>), - CharacterVariant(Box<[CustomIdent]>), - Swash(CustomIdent), - Ornaments(CustomIdent), - Annotation(CustomIdent), - HistoricalForms, - } - - #[derive(Clone, Debug, MallocSizeOf, PartialEq)] - pub struct VariantAlternatesList(pub Box<[VariantAlternates]>); - - #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)] - 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(MallocSizeOf))] - pub struct ParsingFlags: u8 { - const NORMAL = 0; - const HISTORICAL_FORMS = 0x01; - const STYLISTIC = 0x02; - const STYLESET = 0x04; - const CHARACTER_VARIANT = 0x08; - const SWASH = 0x10; - const ORNAMENTS = 0x20; - const ANNOTATION = 0x40; - } - } - - /// normal | - /// [ stylistic() || - /// historical-forms || - /// styleset( #) || - /// character-variant( #) || - /// swash() || - /// ornaments() || - /// annotation() ] - pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result> { - let mut alternates = Vec::new(); - if input.try(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice()))); - } - - let mut parsed_alternates = ParsingFlags::empty(); - macro_rules! check_if_parsed( - ($input:expr, $flag:path) => ( - if parsed_alternates.contains($flag) { - return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - parsed_alternates |= $flag; - ) - ); - while let Ok(_) = input.try(|input| { - // FIXME: remove clone() when lifetimes are non-lexical - match input.next()?.clone() { - Token::Ident(ref ident) => { - if *ident == "historical-forms" { - check_if_parsed!(input, ParsingFlags::HISTORICAL_FORMS); - alternates.push(VariantAlternates::HistoricalForms); - Ok(()) - } else { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - }, - Token::Function(ref name) => { - input.parse_nested_block(|i| { - match_ignore_ascii_case! { &name, - % for value in "swash stylistic ornaments annotation".split(): - "${value}" => { - check_if_parsed!(i, ParsingFlags::${value.upper()}); - let location = i.current_source_location(); - let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; - alternates.push(VariantAlternates::${to_camel_case(value)}(ident)); - Ok(()) - }, - % endfor - % for value in "styleset character-variant".split(): - "${value}" => { - check_if_parsed!(i, ParsingFlags:: ${to_rust_ident(value).upper()}); - let idents = i.parse_comma_separated(|i| { - let location = i.current_source_location(); - CustomIdent::from_ident(location, i.expect_ident()?, &[]) - })?; - alternates.push(VariantAlternates::${to_camel_case(value)}(idents.into_boxed_slice())); - Ok(()) - }, - % endfor - _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)), - } - }) - }, - _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), - } - }) { } - - if parsed_alternates.is_empty() { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice()))) - } - +${helpers.predefined_type("font-variant-alternates", + "FontVariantAlternates", + products="gecko", + initial_value="computed::FontVariantAlternates::get_initial_value()", + initial_specified_value="specified::FontVariantAlternates::get_initial_specified_value()", + 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-alternates")} #[cfg(feature = "gecko")] macro_rules! exclusive_value { diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index a14d6e19cbae..98c3e014c4b2 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -265,6 +265,17 @@ impl ToAnimatedValue for FontSizeAdjust { } } +/// Use VariantAlternatesList as computed type of FontVariantAlternates +pub type FontVariantAlternates = specified::VariantAlternatesList; + +impl FontVariantAlternates { + #[inline] + /// Get initial value with VariantAlternatesList + pub fn get_initial_value() -> Self { + specified::VariantAlternatesList(vec![].into_boxed_slice()) + } +} + impl ToComputedValue for specified::MozScriptMinSize { type ComputedValue = MozScriptMinSize; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 51fe7f209878..7c6f54f58edb 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -36,7 +36,8 @@ pub use self::angle::Angle; pub use self::background::{BackgroundSize, BackgroundRepeat}; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderCornerRadius, BorderSpacing}; -pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom}; +pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; +pub use self::font::{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 67c9956d4b89..da2fa9432e8d 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -10,8 +10,11 @@ use app_units::Au; use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; use properties::longhands::system_font::SystemFont; +#[allow(unused_imports)] +use std::ascii::AsciiExt; use std::fmt; use style_traits::{ToCss, StyleParseErrorKind, ParseError}; +use values::CustomIdent; use values::computed::{font as computed, Context, Length, NonNegativeLength, ToComputedValue}; use values::specified::{AllowQuirks, LengthOrPercentage, NoCalcLength, Number}; use values::specified::length::{AU_PER_PT, AU_PER_PX, FontBaseSize}; @@ -610,6 +613,292 @@ impl Parse for FontSize { } } +bitflags! { + #[cfg_attr(feature = "servo", derive(MallocSizeOf))] + /// Flags of variant alternates in bit + struct VariantAlternatesParsingFlags: u8 { + /// None of variant alternates enabled + const NORMAL = 0; + /// Historical forms + const HISTORICAL_FORMS = 0x01; + /// Stylistic Alternates + const STYLISTIC = 0x02; + /// Stylistic Sets + const STYLESET = 0x04; + /// Character Variant + const CHARACTER_VARIANT = 0x08; + /// Swash glyphs + const SWASH = 0x10; + /// Ornaments glyphs + const ORNAMENTS = 0x20; + /// Annotation forms + const ANNOTATION = 0x40; + } +} + +#[derive(Clone, Debug, MallocSizeOf, PartialEq)] +/// Set of variant alternates +pub enum VariantAlternates { + /// Enables display of stylistic alternates + Stylistic(CustomIdent), + /// Enables display with stylistic sets + Styleset(Box<[CustomIdent]>), + /// Enables display of specific character variants + CharacterVariant(Box<[CustomIdent]>), + /// Enables display of swash glyphs + Swash(CustomIdent), + /// Enables replacement of default glyphs with ornaments + Ornaments(CustomIdent), + /// Enables display of alternate annotation forms + Annotation(CustomIdent), + /// Enables display of historical forms + HistoricalForms, +} + +impl ToCss for VariantAlternates { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + VariantAlternates::Swash(ref atom) => { + dest.write_str("swash")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Stylistic(ref atom) => { + dest.write_str("stylistic")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Ornaments(ref atom) => { + dest.write_str("ornaments")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Annotation(ref atom) => { + dest.write_str("annotation")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Styleset(ref vec) => { + dest.write_str("styleset")?; + 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(")") + }, + VariantAlternates::CharacterVariant(ref vec) => { + dest.write_str("character-variant")?; + 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(")") + }, + VariantAlternates::HistoricalForms => { + dest.write_str("historical-forms") + }, + } + } +} + +#[derive(Clone, Debug, MallocSizeOf, PartialEq)] +/// List of Variant Alternates +pub struct VariantAlternatesList(pub Box<[VariantAlternates]>); + +impl VariantAlternatesList { + /// Returns the length of all variant alternates. + pub fn len(&self) -> usize { + self.0.iter().fold(0, |acc, alternate| { + match *alternate { + VariantAlternates::Swash(_) | VariantAlternates::Stylistic(_) | + VariantAlternates::Ornaments(_) | VariantAlternates::Annotation(_) => { + acc + 1 + }, + VariantAlternates::Styleset(ref slice) | + VariantAlternates::CharacterVariant(ref slice) => { + acc + slice.len() + }, + _ => acc, + } + }) + } +} + +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(()) + } +} + +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)] +/// Control over the selection of these alternate glyphs +pub enum FontVariantAlternates { + /// Use alternative glyph from value + Value(VariantAlternatesList), + /// Use system font glyph + System(SystemFont) +} + +impl FontVariantAlternates { + #[inline] + /// Get initial specified value with VariantAlternatesList + pub fn get_initial_specified_value() -> Self { + FontVariantAlternates::Value(VariantAlternatesList(vec![].into_boxed_slice())) + } + + /// Get FontVariantAlternates with system font + pub fn system_font(f: SystemFont) -> Self { + FontVariantAlternates::System(f) + } + + /// Get SystemFont of FontVariantAlternates + pub fn get_system(&self) -> Option { + if let FontVariantAlternates::System(s) = *self { + Some(s) + } else { + None + } + } +} + +impl ToComputedValue for FontVariantAlternates { + type ComputedValue = computed::FontVariantAlternates; + + fn to_computed_value(&self, _context: &Context) -> computed::FontVariantAlternates { + match *self { + FontVariantAlternates::Value(ref v) => v.clone(), + FontVariantAlternates::System(_) => { + #[cfg(feature = "gecko")] { + _context.cached_system_font.as_ref().unwrap().font_variant_alternates.clone() + } + #[cfg(feature = "servo")] { + unreachable!() + } + } + } + } + + fn from_computed_value(other: &computed::FontVariantAlternates) -> Self { + FontVariantAlternates::Value(other.clone()) + } +} + +impl Parse for FontVariantAlternates { + /// normal | + /// [ stylistic() || + /// historical-forms || + /// styleset( #) || + /// character-variant( #) || + /// swash() || + /// ornaments() || + /// annotation() ] + fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { + let mut alternates = Vec::new(); + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + return Ok(FontVariantAlternates::Value(VariantAlternatesList(alternates.into_boxed_slice()))); + } + + let mut parsed_alternates = VariantAlternatesParsingFlags::empty(); + macro_rules! check_if_parsed( + ($input:expr, $flag:path) => ( + if parsed_alternates.contains($flag) { + return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + parsed_alternates |= $flag; + ) + ); + while let Ok(_) = input.try(|input| { + // FIXME: remove clone() when lifetimes are non-lexical + match input.next()?.clone() { + Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => { + check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS); + alternates.push(VariantAlternates::HistoricalForms); + Ok(()) + }, + Token::Function(ref name) => { + input.parse_nested_block(|i| { + match_ignore_ascii_case! { &name, + "swash" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Swash(ident)); + Ok(()) + }, + "stylistic" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Stylistic(ident)); + Ok(()) + }, + "ornaments" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Ornaments(ident)); + Ok(()) + }, + "annotation" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Annotation(ident)); + Ok(()) + }, + "styleset" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET); + let idents = i.parse_comma_separated(|i| { + let location = i.current_source_location(); + CustomIdent::from_ident(location, i.expect_ident()?, &[]) + })?; + alternates.push(VariantAlternates::Styleset(idents.into_boxed_slice())); + Ok(()) + }, + "character-variant" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT); + let idents = i.parse_comma_separated(|i| { + let location = i.current_source_location(); + CustomIdent::from_ident(location, i.expect_ident()?, &[]) + })?; + alternates.push(VariantAlternates::CharacterVariant(idents.into_boxed_slice())); + Ok(()) + }, + _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + } + }) + }, + _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + } + }) { } + + if parsed_alternates.is_empty() { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + Ok(FontVariantAlternates::Value(VariantAlternatesList(alternates.into_boxed_slice()))) + } +} + #[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 5d89190e229d..9a33019d98a9 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -30,7 +30,8 @@ pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, Justify pub use self::background::{BackgroundRepeat, BackgroundSize}; pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth, BorderSpacing}; -pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom}; +pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; +pub use self::font::{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};