diff --git a/servo/Cargo.lock b/servo/Cargo.lock index 76b094fb706b..a9c9a8e6c9ab 100644 --- a/servo/Cargo.lock +++ b/servo/Cargo.lock @@ -2746,6 +2746,7 @@ dependencies = [ "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bindgen 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/servo/components/style/Cargo.toml b/servo/components/style/Cargo.toml index b5ec4e4a31da..42e33e1c1b5a 100644 --- a/servo/components/style/Cargo.toml +++ b/servo/components/style/Cargo.toml @@ -25,6 +25,7 @@ testing = [] app_units = "0.4" atomic_refcell = "0.1" bitflags = "0.7" +byteorder = "1.0" cfg-if = "0.1.0" cssparser = "0.12" encoding = {version = "0.2", optional = true} diff --git a/servo/components/style/lib.rs b/servo/components/style/lib.rs index ddb585388025..059e26c03ec1 100644 --- a/servo/components/style/lib.rs +++ b/servo/components/style/lib.rs @@ -41,6 +41,7 @@ extern crate app_units; extern crate atomic_refcell; #[macro_use] extern crate bitflags; +#[allow(unused_extern_crates)] extern crate byteorder; #[cfg(feature = "gecko")] #[macro_use] #[no_link] extern crate cfg_if; #[macro_use] extern crate cssparser; extern crate euclid; diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 790ca4211f29..ebfdac88afb6 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -1177,7 +1177,7 @@ fn static_assert() { <%self:impl_trait style_struct_name="Font" - skip_longhands="font-family font-size font-size-adjust font-weight font-synthesis -x-lang" + skip_longhands="font-family font-size font-size-adjust font-weight font-synthesis -x-lang font-language-override" skip_additionals="*"> pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) { @@ -1315,6 +1315,11 @@ fn static_assert() { Gecko_nsStyleFont_CopyLangFrom(&mut self.gecko, &other.gecko); } } + + pub fn set_font_language_override(&mut self, v: longhands::font_language_override::computed_value::T) { + self.gecko.mFont.languageOverride = v.0; + } + ${impl_simple_copy('font_language_override', 'mFont.languageOverride')} <%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)"> diff --git a/servo/components/style/properties/longhand/font.mako.rs b/servo/components/style/properties/longhand/font.mako.rs index 4d9d97fc8685..0640a853a1b6 100644 --- a/servo/components/style/properties/longhand/font.mako.rs +++ b/servo/components/style/properties/longhand/font.mako.rs @@ -954,40 +954,67 @@ ${helpers.single_keyword("font-variant-position", } -// https://www.w3.org/TR/css-fonts-3/#propdef-font-language-override -<%helpers:longhand name="font-language-override" products="none" animatable="False" extra_prefixes="moz" +<%helpers:longhand name="font-language-override" products="gecko" animatable="False" extra_prefixes="moz" spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override"> + use std::fmt; + use style_traits::ToCss; + use byteorder::{BigEndian, ByteOrder}; use values::HasViewportPercentage; - use values::computed::ComputedValueAsSpecified; - pub use self::computed_value::T as SpecifiedValue; - - impl ComputedValueAsSpecified for SpecifiedValue {} no_viewport_percentage!(SpecifiedValue); + #[derive(Debug, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub enum SpecifiedValue { + Normal, + Override(String), + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + use cssparser; + match *self { + SpecifiedValue::Normal => dest.write_str("normal"), + SpecifiedValue::Override(ref lang) => + cssparser::serialize_string(lang, dest), + } + } + } + pub mod computed_value { - use std::fmt; + use std::{fmt, str}; use style_traits::ToCss; + use byteorder::{BigEndian, ByteOrder}; + use cssparser; impl ToCss for T { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - T::Normal => dest.write_str("normal"), - T::Override(ref lang) => write!(dest, "\"{}\"", lang), + if self.0 == 0 { + return dest.write_str("normal") } + let mut buf = [0; 4]; + BigEndian::write_u32(&mut buf, self.0); + // Safe because we ensure it's ASCII during computing + let slice = if cfg!(debug_assertions) { + str::from_utf8(&buf).unwrap() + } else { + unsafe { str::from_utf8_unchecked(&buf) } + }; + cssparser::serialize_string(slice.trim_right(), dest) } } - #[derive(Clone, Debug, PartialEq)] + // 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 + // (see http://www.microsoft.com/typography/otspec/languagetags.htm). + #[derive(PartialEq, Clone, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub enum T { - Normal, - Override(String), - } + pub struct T(pub u32); } #[inline] pub fn get_initial_value() -> computed_value::T { - computed_value::T::Normal + computed_value::T(0) } #[inline] @@ -995,6 +1022,45 @@ ${helpers.single_keyword("font-variant-position", SpecifiedValue::Normal } + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, _: &Context) -> computed_value::T { + use std::ascii::AsciiExt; + match *self { + SpecifiedValue::Normal => computed_value::T(0), + SpecifiedValue::Override(ref lang) => { + if lang.is_empty() || lang.len() > 4 || !lang.is_ascii() { + return computed_value::T(0) + } + let mut computed_lang = lang.clone(); + while computed_lang.len() < 4 { + computed_lang.push(' '); + } + let bytes = computed_lang.into_bytes(); + computed_value::T(BigEndian::read_u32(&bytes)) + } + } + } + #[inline] + fn from_computed_value(computed: &computed_value::T) -> Self { + if computed.0 == 0 { + return SpecifiedValue::Normal + } + let mut buf = [0; 4]; + BigEndian::write_u32(&mut buf, computed.0); + SpecifiedValue::Override( + if cfg!(debug_assertions) { + String::from_utf8(buf.to_vec()).unwrap() + } else { + unsafe { String::from_utf8_unchecked(buf.to_vec()) } + } + ) + } + } + + /// normal | pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { if input.try(|input| input.expect_ident_matching("normal")).is_ok() { Ok(SpecifiedValue::Normal) diff --git a/servo/components/style/properties/shorthand/font.mako.rs b/servo/components/style/properties/shorthand/font.mako.rs index 19ec505ee0c9..1b68ea6afe12 100644 --- a/servo/components/style/properties/shorthand/font.mako.rs +++ b/servo/components/style/properties/shorthand/font.mako.rs @@ -4,21 +4,20 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> -<%helpers:shorthand name="font" sub_properties="font-style font-variant font-weight font-stretch - font-size line-height font-family - ${'font-size-adjust' if product == 'gecko' or data.testing else ''} - ${'font-kerning' if product == 'gecko' or data.testing else ''} - ${'font-variant-caps' if product == 'gecko' or data.testing else ''} - ${'font-variant-position' if product == 'gecko' or data.testing else ''} - ${'font-language-override' if data.testing else ''}" +<%helpers:shorthand name="font" + sub_properties="font-style font-variant font-weight font-stretch + font-size line-height font-family + ${'font-size-adjust' if product == 'gecko' or data.testing else ''} + ${'font-kerning' if product == 'gecko' or data.testing else ''} + ${'font-variant-caps' if product == 'gecko' or data.testing else ''} + ${'font-variant-position' if product == 'gecko' or data.testing else ''} + ${'font-language-override' if product == 'gecko' or data.testing else ''}" spec="https://drafts.csswg.org/css-fonts-3/#propdef-font"> use properties::longhands::{font_style, font_variant, font_weight, font_stretch}; use properties::longhands::{font_size, line_height}; % if product == "gecko" or data.testing: - use properties::longhands::{font_size_adjust, font_kerning, font_variant_caps, font_variant_position}; - % endif - % if data.testing: - use properties::longhands::font_language_override; + use properties::longhands::{font_size_adjust, font_kerning, font_variant_caps, font_variant_position, + font_language_override}; % endif use properties::longhands::font_family::SpecifiedValue as FontFamily; @@ -84,13 +83,10 @@ line_height: unwrap_or_initial!(line_height), font_family: family, % if product == "gecko" or data.testing: - % for name in "size_adjust kerning variant_caps variant_position".split(): + % for name in "size_adjust kerning variant_caps variant_position language_override".split(): font_${name}: font_${name}::get_initial_specified_value(), % endfor % endif - % if data.testing: - font_language_override: font_language_override::get_initial_specified_value(), - % endif }) } @@ -99,19 +95,13 @@ fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { % if product == "gecko" or data.testing: - % for name in "size_adjust kerning variant_caps variant_position".split(): + % for name in "size_adjust kerning variant_caps variant_position language_override".split(): if self.font_${name} != &font_${name}::get_initial_specified_value() { return Ok(()); } % endfor % endif - % if data.testing: - if self.font_language_override != &font_language_override::get_initial_specified_value() { - return Ok(()); - } - % endif - % for name in "style variant weight stretch".split(): self.font_${name}.to_css(dest)?; dest.write_str(" ")?;