diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 8baba9bb54dc..921083fb6ddb 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -468,7 +468,7 @@ impl LayoutElementHelpers for LayoutJS { hints.push(from_declaration( shared_lock, PropertyDeclaration::FontFamily( - font_family::computed_value::T(vec![ + font_family::SpecifiedValue::Values(vec![ font_family::computed_value::FontFamily::from_atom( font_family)])))); } diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index 2ea1f286db54..2a122f482e9c 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -695,6 +695,9 @@ extern "C" { pub fn Gecko_Atomize(aString: *const ::std::os::raw::c_char, aLength: u32) -> *mut nsIAtom; } +extern "C" { + pub fn Gecko_Atomize16(aString: *const nsAString) -> *mut nsIAtom; +} extern "C" { pub fn Gecko_AddRefAtom(aAtom: *mut nsIAtom); } @@ -731,6 +734,14 @@ extern "C" { extern "C" { pub fn Gecko_CopyFontFamilyFrom(dst: *mut nsFont, src: *const nsFont); } +extern "C" { + pub fn Gecko_nsFont_InitSystem(dst: *mut nsFont, font_id: i32, + font: *const nsStyleFont, + pres_context: RawGeckoPresContextBorrowed); +} +extern "C" { + pub fn Gecko_nsFont_Destroy(dst: *mut nsFont); +} extern "C" { pub fn Gecko_SetImageOrientation(aVisibility: *mut nsStyleVisibility, aRadians: f64, aFlip: bool); diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs index ca2c37ec2131..9db8374f9a55 100644 --- a/components/style/gecko_string_cache/mod.rs +++ b/components/style/gecko_string_cache/mod.rs @@ -8,9 +8,11 @@ use gecko_bindings::bindings::Gecko_AddRefAtom; use gecko_bindings::bindings::Gecko_Atomize; +use gecko_bindings::bindings::Gecko_Atomize16; use gecko_bindings::bindings::Gecko_ReleaseAtom; use gecko_bindings::structs::nsIAtom; use precomputed_hash::PrecomputedHash; +use nsstring::nsAString; use std::borrow::{Cow, Borrow}; use std::char::{self, DecodeUtf16}; use std::fmt::{self, Write}; @@ -281,6 +283,17 @@ impl<'a> From<&'a str> for Atom { } } +impl<'a> From<&'a nsAString> for Atom { + #[inline] + fn from(string: &nsAString) -> Atom { + unsafe { + Atom(WeakAtom::new( + Gecko_Atomize16(string) + )) + } + } +} + impl<'a> From> for Atom { #[inline] fn from(string: Cow<'a, str>) -> Atom { diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 23daca26c863..2a75da38d9fb 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -95,6 +95,7 @@ pub struct ComputedValues { /// When this is Some, we compute font sizes by computing the keyword against /// the generic font, and then multiplying it by the ratio. pub font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>, + pub cached_system_font: Option, } impl ComputedValues { @@ -104,6 +105,7 @@ impl ComputedValues { writing_mode: parent.writing_mode, root_font_size: parent.root_font_size, font_size_keyword: parent.font_size_keyword, + cached_system_font: None, % for style_struct in data.style_structs: % if style_struct.inherited: ${style_struct.ident}: parent.${style_struct.ident}.clone(), @@ -126,6 +128,7 @@ impl ComputedValues { custom_properties: custom_properties, writing_mode: writing_mode, root_font_size: root_font_size, + cached_system_font: None, font_size_keyword: font_size_keyword, % for style_struct in data.style_structs: ${style_struct.ident}: ${style_struct.ident}, @@ -139,6 +142,7 @@ impl ComputedValues { writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious root_font_size: longhands::font_size::get_initial_value(), // FIXME(bz): Also seems dubious? font_size_keyword: Some((Default::default(), 1.)), + cached_system_font: None, % for style_struct in data.style_structs: ${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context), % endfor diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 49a751bd59f4..2dfcc7a837cd 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -265,6 +265,11 @@ <% maybe_wm = ", wm" if property.logical else "" %> match *value { DeclaredValue::Value(ref specified_value) => { + % if property.ident in "font_size font_family".split() and product == "gecko": + if let Some(sf) = specified_value.get_system() { + longhands::system_font::resolve_system_font(sf, context); + } + % endif let computed = specified_value.to_computed_value(context); % if property.ident == "font_size": if let longhands::font_size::SpecifiedValue::Keyword(kw, fraction) diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 7c6031ca09cb..68e5ce6cb90b 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -358,7 +358,7 @@ impl AnimationValue { } /// Construct an AnimationValue from a property declaration - pub fn from_declaration(decl: &PropertyDeclaration, context: &Context, initial: &ComputedValues) -> Option { + pub fn from_declaration(decl: &PropertyDeclaration, context: &mut Context, initial: &ComputedValues) -> Option { use error_reporting::StdoutErrorReporter; use properties::LonghandId; use properties::DeclaredValue; @@ -367,6 +367,11 @@ impl AnimationValue { % for prop in data.longhands: % if prop.animatable: PropertyDeclaration::${prop.camel_case}(ref val) => { + % if prop.ident in "font_size font_family".split() and product == "gecko": + if let Some(sf) = val.get_system() { + longhands::system_font::resolve_system_font(sf, context); + } + % endif Some(AnimationValue::${prop.camel_case}(val.to_computed_value(context))) }, % endif diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index df95d9f464cb..09870f66edc6 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -3,22 +3,33 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> -<% from data import Method %> +<% from data import Method, to_camel_case, to_rust_ident %> <% data.new_style_struct("Font", inherited=True) %> + +<%def name="nongecko_unreachable()"> + %if product == "gecko": + ${caller.body()} + %else: + unreachable!() + %endif + + <%helpers:longhand name="font-family" animation_type="none" need_index="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-family"> + use properties::longhands::system_font::SystemFont; use self::computed_value::{FontFamily, FamilyName}; + use std::fmt; + use style_traits::ToCss; use values::HasViewportPercentage; use values::computed::ComputedValueAsSpecified; - pub use self::computed_value::T as SpecifiedValue; - impl ComputedValueAsSpecified for SpecifiedValue {} no_viewport_percentage!(SpecifiedValue); pub mod computed_value { use cssparser::{CssStringWriter, Parser, serialize_identifier}; + use properties::longhands::system_font::SystemFont; use std::fmt::{self, Write}; use Atom; use style_traits::ToCss; @@ -200,9 +211,61 @@ SpecifiedValue::parse(input) } + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub enum SpecifiedValue { + Values(Vec), + System(SystemFont), + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + fn to_computed_value(&self, _cx: &Context) -> Self::ComputedValue { + match *self { + SpecifiedValue::Values(ref v) => computed_value::T(v.clone()), + SpecifiedValue::System(_) => { + <%self:nongecko_unreachable> + _cx.style.cached_system_font.as_ref().unwrap().font_family.clone() + + } + } + } + fn from_computed_value(other: &computed_value::T) -> Self { + SpecifiedValue::Values(other.0.clone()) + } + } + impl SpecifiedValue { + pub fn system_font(f: SystemFont) -> Self { + SpecifiedValue::System(f) + } + pub fn get_system(&self) -> Option { + if let SpecifiedValue::System(s) = *self { + Some(s) + } else { + None + } + } + pub fn parse(input: &mut Parser) -> Result { - input.parse_comma_separated(|input| FontFamily::parse(input)).map(SpecifiedValue) + input.parse_comma_separated(|input| FontFamily::parse(input)).map(SpecifiedValue::Values) + } + } + + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SpecifiedValue::Values(ref v) => { + let mut iter = v.iter(); + iter.next().unwrap().to_css(dest)?; + for family in iter { + dest.write_str(", ")?; + family.to_css(dest)?; + } + Ok(()) + } + _ => Ok(()) + } } } @@ -418,6 +481,7 @@ ${helpers.single_keyword("font-variant-caps", use values::{FONT_MEDIUM_PX, HasViewportPercentage}; use values::specified::{FontRelativeLength, LengthOrPercentage, Length}; use values::specified::{NoCalcLength, Percentage}; + use properties::longhands::system_font::SystemFont; impl ToCss for SpecifiedValue { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { @@ -426,6 +490,7 @@ ${helpers.single_keyword("font-variant-caps", SpecifiedValue::Keyword(kw, _) => kw.to_css(dest), SpecifiedValue::Smaller => dest.write_str("smaller"), SpecifiedValue::Larger => dest.write_str("larger"), + SpecifiedValue::System(_) => Ok(()), } } } @@ -452,6 +517,7 @@ ${helpers.single_keyword("font-variant-caps", Keyword(KeywordSize, f32), Smaller, Larger, + System(SystemFont) } impl From for SpecifiedValue { @@ -677,6 +743,12 @@ ${helpers.single_keyword("font-variant-caps", FontRelativeLength::Em(1.2).to_computed_value(context, /* use_inherited */ true) } + + SpecifiedValue::System(_) => { + <%self:nongecko_unreachable> + context.style.cached_system_font.as_ref().unwrap().font_size + + } } } @@ -703,6 +775,18 @@ ${helpers.single_keyword("font-variant-caps", _ => Err(()) } } + impl SpecifiedValue { + pub fn system_font(f: SystemFont) -> Self { + SpecifiedValue::System(f) + } + pub fn get_system(&self) -> Option { + if let SpecifiedValue::System(s) = *self { + Some(s) + } else { + None + } + } + } <%helpers:longhand products="gecko" name="font-size-adjust" animation_type="normal" @@ -1801,3 +1885,116 @@ ${helpers.single_keyword("-moz-math-variant", } + +% if product == "gecko": + pub mod system_font { + use app_units::Au; + use cssparser::Parser; + use properties::longhands; + use values::computed::{ToComputedValue, Context}; + <% + system_fonts = """caption icon menu message-box small-caption status-bar + -moz-window -moz-document -moz-workspace -moz-desktop + -moz-info -moz-dialog -moz-button -moz-pull-down-menu + -moz-list -moz-field""".split() + %> + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + pub enum SystemFont { + % for font in system_fonts: + ${to_camel_case(font)}, + % endfor + } + + impl ToComputedValue for SystemFont { + type ComputedValue = ComputedSystemFont; + + fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue { + use gecko_bindings::bindings; + use gecko_bindings::structs::{LookAndFeel_FontID, nsFont}; + use std::mem; + + let id = match *self { + % for font in system_fonts: + SystemFont::${to_camel_case(font)} => { + LookAndFeel_FontID::eFont_${to_camel_case(font.replace("-moz-", ""))} + } + % endfor + }; + + let mut system: nsFont = unsafe { mem::uninitialized() }; + unsafe { + bindings::Gecko_nsFont_InitSystem(&mut system, id as i32, + cx.style.get_font().gecko(), + &*cx.device.pres_context) + } + let family = system.fontlist.mFontlist.iter().map(|font| { + use properties::longhands::font_family::computed_value::*; + FontFamily::FamilyName(FamilyName { + name: (&*font.mName).into(), + quoted: true + }) + }).collect::>(); + let ret = ComputedSystemFont { + font_family: longhands::font_family::computed_value::T(family), + font_size: Au(system.size), + system_font: *self, + }; + unsafe { bindings::Gecko_nsFont_Destroy(&mut system); } + ret + } + + fn from_computed_value(_: &ComputedSystemFont) -> Self { + unreachable!() + } + } + + #[inline] + /// Compute and cache a system font + /// + /// Must be called before attempting to compute a system font + /// specified value + pub fn resolve_system_font(system: SystemFont, context: &mut Context) { + if context.style.cached_system_font.is_none() { + let computed = system.to_computed_value(context); + context.style.cached_system_font = Some(computed); + } + debug_assert!(system == context.style.cached_system_font.as_ref().unwrap().system_font) + } + + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + pub struct ComputedSystemFont { + pub font_family: longhands::font_family::computed_value::T, + pub font_size: longhands::font_size::computed_value::T, + pub system_font: SystemFont, + } + + impl SystemFont { + pub fn parse(input: &mut Parser) -> Result { + Ok(match_ignore_ascii_case! { &*input.expect_ident()?, + % for font in system_fonts: + "${font}" => SystemFont::${to_camel_case(font)}, + % endfor + _ => return Err(()) + }) + } + } + } +% else: + pub mod system_font { + use cssparser::Parser; + + // We don't parse system fonts, but in the interest of not littering + // a lot of code with `if product == gecko` conditionals, we have a + // dummy system font module that does nothing + + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + /// void enum for system font, can never exist + pub enum SystemFont {} + impl SystemFont { + pub fn parse(_: &mut Parser) -> Result { + Err(()) + } + } + } +% endif diff --git a/components/style/properties/shorthand/font.mako.rs b/components/style/properties/shorthand/font.mako.rs index 6459d2c68c96..a4fd93b843a6 100644 --- a/components/style/properties/shorthand/font.mako.rs +++ b/components/style/properties/shorthand/font.mako.rs @@ -16,8 +16,9 @@ ${'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_caps, font_weight, font_stretch}; - use properties::longhands::{font_size, line_height}; + use properties::longhands::{font_family, font_style, font_weight, font_stretch}; + use properties::longhands::{font_size, line_height, font_variant_caps}; + use properties::longhands::system_font::SystemFont; <% gecko_sub_properties = "kerning language_override size_adjust \ variant_alternates variant_east_asian \ @@ -38,6 +39,19 @@ let mut weight = None; let mut stretch = None; let size; + % if product == "gecko": + if let Ok(sys) = input.try(SystemFont::parse) { + return Ok(Longhands { + % for name in "family size".split(): + font_${name}: font_${name}::SpecifiedValue::system_font(sys), + % endfor + % for name in "style weight stretch variant_caps".split() + gecko_sub_properties: + font_${name}: font_${name}::get_initial_specified_value(), + % endfor + line_height: line_height::get_initial_specified_value(), + }) + } + % endif loop { // Special-case 'normal' because it is valid in each of // font-style, font-weight, font-variant and font-stretch. @@ -88,7 +102,7 @@ }; let family = FontFamily::parse(input)?; Ok(Longhands { - % for name in "style variant_caps weight stretch size".split(): + % for name in "style weight stretch size variant_caps".split(): font_${name}: unwrap_or_initial!(font_${name}, ${name}), % endfor line_height: unwrap_or_initial!(line_height), diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 8c7d2175a164..ac8a4a394503 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -1928,7 +1928,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis let default_values = data.default_computed_values(); let metrics = get_metrics_provider_for_product(); - let context = Context { + let mut context = Context { is_root_element: false, device: &data.stylist.device, inherited_style: parent_style.unwrap_or(default_values), @@ -1956,7 +1956,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis .filter_map(|&(ref decl, imp)| { if imp == Importance::Normal { let property = TransitionProperty::from_declaration(decl); - let animation = AnimationValue::from_declaration(decl, &context, default_values); + let animation = AnimationValue::from_declaration(decl, &mut context, default_values); debug_assert!(property.is_none() == animation.is_none(), "The failure condition of TransitionProperty::from_declaration \ and AnimationValue::from_declaration should be the same");