From d5c001e64dc3d1925938b195a7ca332bb6d727d6 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 24 Mar 2017 12:30:10 -0700 Subject: [PATCH] servo: Merge #16122 - stylo: Handle font-size keywords correctly (from Manishearth:stylo-font-size); r=heycam r=heycam https://bugzilla.mozilla.org/show_bug.cgi?id=1341775 Source-Repo: https://github.com/servo/servo Source-Revision: 1584bbd3ecc2a86339f8fe2ccb88bd30fc462b1c --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 2047c506f34dd7a2ea6e36b24008384212f8f737 --- servo/components/style/build_gecko.rs | 1 + .../style/gecko_bindings/bindings.rs | 6 ++ .../style/gecko_bindings/structs_debug.rs | 8 ++ .../style/gecko_bindings/structs_release.rs | 8 ++ .../components/style/properties/gecko.mako.rs | 43 +++++++-- .../style/properties/helpers.mako.rs | 33 +++++-- .../style/properties/longhand/font.mako.rs | 94 +++++++++++++++---- .../style/properties/properties.mako.rs | 70 ++++++++++++++ .../style/values/specified/length.rs | 8 ++ 9 files changed, 238 insertions(+), 33 deletions(-) diff --git a/servo/components/style/build_gecko.rs b/servo/components/style/build_gecko.rs index 7ca5530e9a34..b485efa48ee6 100644 --- a/servo/components/style/build_gecko.rs +++ b/servo/components/style/build_gecko.rs @@ -293,6 +293,7 @@ mod bindings { "BORDER_STYLE_.*", "mozilla::SERVO_PREF_.*", "kNameSpaceID_.*", + "kGenericFont_.*", ]; let whitelist = [ "RawGecko.*", diff --git a/servo/components/style/gecko_bindings/bindings.rs b/servo/components/style/gecko_bindings/bindings.rs index 360766befcc5..7efc2252a004 100644 --- a/servo/components/style/gecko_bindings/bindings.rs +++ b/servo/components/style/gecko_bindings/bindings.rs @@ -1012,6 +1012,12 @@ extern "C" { pub fn Gecko_nsStyleFont_CopyLangFrom(aFont: *mut nsStyleFont, aSource: *const nsStyleFont); } +extern "C" { + pub fn Gecko_nsStyleFont_GetBaseSize(font: *const nsStyleFont, + pres_context: + RawGeckoPresContextBorrowed) + -> nscoord; +} extern "C" { pub fn Gecko_GetMediaFeatures() -> *const nsMediaFeature; } diff --git a/servo/components/style/gecko_bindings/structs_debug.rs b/servo/components/style/gecko_bindings/structs_debug.rs index d6be67cdf8ad..4dfd92f3dfe9 100644 --- a/servo/components/style/gecko_bindings/structs_debug.rs +++ b/servo/components/style/gecko_bindings/structs_debug.rs @@ -19097,6 +19097,14 @@ pub mod root { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct gfxFontStyle([u8; 0]); + pub const kGenericFont_NONE: u8 = 0; + pub const kGenericFont_moz_variable: u8 = 0; + pub const kGenericFont_moz_fixed: u8 = 1; + pub const kGenericFont_serif: u8 = 2; + pub const kGenericFont_sans_serif: u8 = 4; + pub const kGenericFont_monospace: u8 = 8; + pub const kGenericFont_cursive: u8 = 16; + pub const kGenericFont_fantasy: u8 = 32; #[repr(C)] #[derive(Debug)] pub struct nsFont { diff --git a/servo/components/style/gecko_bindings/structs_release.rs b/servo/components/style/gecko_bindings/structs_release.rs index 53d42fdde005..661b28a18f92 100644 --- a/servo/components/style/gecko_bindings/structs_release.rs +++ b/servo/components/style/gecko_bindings/structs_release.rs @@ -18546,6 +18546,14 @@ pub mod root { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct gfxFontStyle([u8; 0]); + pub const kGenericFont_NONE: u8 = 0; + pub const kGenericFont_moz_variable: u8 = 0; + pub const kGenericFont_moz_fixed: u8 = 1; + pub const kGenericFont_serif: u8 = 2; + pub const kGenericFont_sans_serif: u8 = 4; + pub const kGenericFont_monospace: u8 = 8; + pub const kGenericFont_cursive: u8 = 16; + pub const kGenericFont_fantasy: u8 = 32; #[repr(C)] #[derive(Debug)] pub struct nsFont { diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 82203d96e193..3bd5e6b31207 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -80,6 +80,7 @@ pub struct ComputedValues { shareable: bool, pub writing_mode: WritingMode, pub root_font_size: Au, + pub font_size_keyword: Option, } impl ComputedValues { @@ -89,6 +90,7 @@ impl ComputedValues { shareable: parent.shareable, writing_mode: parent.writing_mode, root_font_size: parent.root_font_size, + font_size_keyword: parent.font_size_keyword, % for style_struct in data.style_structs: % if style_struct.inherited: ${style_struct.ident}: parent.${style_struct.ident}.clone(), @@ -103,6 +105,7 @@ impl ComputedValues { shareable: bool, writing_mode: WritingMode, root_font_size: Au, + font_size_keyword: Option, % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor @@ -112,6 +115,7 @@ impl ComputedValues { shareable: shareable, writing_mode: writing_mode, root_font_size: root_font_size, + font_size_keyword: font_size_keyword, % for style_struct in data.style_structs: ${style_struct.ident}: ${style_struct.ident}, % endfor @@ -124,6 +128,7 @@ impl ComputedValues { shareable: true, 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()), % for style_struct in data.style_structs: ${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context), % endfor @@ -194,6 +199,11 @@ impl ComputedValues { pub struct ${style_struct.gecko_struct_name} { gecko: ${style_struct.gecko_ffi_name}, } +impl ${style_struct.gecko_struct_name} { + pub fn gecko(&self) -> &${style_struct.gecko_ffi_name} { + &self.gecko + } +} <%def name="impl_simple_setter(ident, gecko_ffi_name)"> @@ -1191,14 +1201,31 @@ fn static_assert() { unsafe { Gecko_FontFamilyList_AppendNamed(list, name.0.as_ptr()); } } FontFamily::Generic(ref name) => { - let family_type = - if name == &atom!("serif") { FontFamilyType::eFamily_serif } - else if name == &atom!("sans-serif") { FontFamilyType::eFamily_sans_serif } - else if name == &atom!("cursive") { FontFamilyType::eFamily_cursive } - else if name == &atom!("fantasy") { FontFamilyType::eFamily_fantasy } - else if name == &atom!("monospace") { FontFamilyType::eFamily_monospace } - else if name == &atom!("-moz-fixed") { FontFamilyType::eFamily_moz_fixed } - else { panic!("Unknown generic font family") }; + let (family_type, generic) = + if name == &atom!("serif") { + (FontFamilyType::eFamily_serif, + structs::kGenericFont_serif) + } else if name == &atom!("sans-serif") { + (FontFamilyType::eFamily_sans_serif, + structs::kGenericFont_sans_serif) + } else if name == &atom!("cursive") { + (FontFamilyType::eFamily_cursive, + structs::kGenericFont_cursive) + } else if name == &atom!("fantasy") { + (FontFamilyType::eFamily_fantasy, + structs::kGenericFont_fantasy) + } else if name == &atom!("monospace") { + (FontFamilyType::eFamily_monospace, + structs::kGenericFont_monospace) + } else if name == &atom!("-moz-fixed") { + (FontFamilyType::eFamily_moz_fixed, + structs::kGenericFont_moz_fixed) + } else { + panic!("Unknown generic font family") + }; + if v.0.len() == 1 { + self.gecko.mGenericID = generic; + } unsafe { Gecko_FontFamilyList_AppendGeneric(list, family_type); } } } diff --git a/servo/components/style/properties/helpers.mako.rs b/servo/components/style/properties/helpers.mako.rs index f6c5dd299946..37f8b8f1184e 100644 --- a/servo/components/style/properties/helpers.mako.rs +++ b/servo/components/style/properties/helpers.mako.rs @@ -266,6 +266,13 @@ match *value { DeclaredValue::Value(ref specified_value) => { let computed = specified_value.to_computed_value(context); + % if property.ident == "font_size": + if let longhands::font_size::SpecifiedValue::Keyword(kw) = **specified_value { + context.mutate_style().font_size_keyword = Some(kw); + } else { + context.mutate_style().font_size_keyword = None; + } + % endif % if property.has_uncacheable_values: context.mutate_style().mutate_${data.current_style_struct.name_lower}() .set_${property.ident}(computed, cacheable ${maybe_wm}); @@ -280,12 +287,22 @@ CSSWideKeyword::Unset | % endif CSSWideKeyword::Initial => { - // We assume that it's faster to use copy_*_from rather than - // set_*(get_initial_value()); - let initial_struct = default_style - .get_${data.current_style_struct.name_lower}(); - context.mutate_style().mutate_${data.current_style_struct.name_lower}() - .copy_${property.ident}_from(initial_struct ${maybe_wm}); + % if property.ident == "font_size": + // font-size's default ("medium") does not always + // compute to the same value and depends on the font + let computed = longhands::font_size::get_initial_specified_value() + .to_computed_value(context); + context.mutate_style().mutate_${data.current_style_struct.name_lower}() + .set_font_size(computed); + context.mutate_style().font_size_keyword = Some(Default::default()); + % else: + // We assume that it's faster to use copy_*_from rather than + // set_*(get_initial_value()); + let initial_struct = default_style + .get_${data.current_style_struct.name_lower}(); + context.mutate_style().mutate_${data.current_style_struct.name_lower}() + .copy_${property.ident}_from(initial_struct ${maybe_wm}); + % endif }, % if data.current_style_struct.inherited: CSSWideKeyword::Unset | @@ -300,6 +317,10 @@ inherited_style.get_${data.current_style_struct.name_lower}(); context.mutate_style().mutate_${data.current_style_struct.name_lower}() .copy_${property.ident}_from(inherited_struct ${maybe_wm}); + % if property.ident == "font_size": + context.mutate_style().font_size_keyword = + context.inherited_style.font_size_keyword; + % endif } } } diff --git a/servo/components/style/properties/longhand/font.mako.rs b/servo/components/style/properties/longhand/font.mako.rs index 49b3557c8cb1..55550f2d7d82 100644 --- a/servo/components/style/properties/longhand/font.mako.rs +++ b/servo/components/style/properties/longhand/font.mako.rs @@ -465,6 +465,12 @@ ${helpers.single_keyword("font-variant-caps", } } + impl Default for KeywordSize { + fn default() -> Self { + Medium + } + } + impl ToCss for KeywordSize { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { dest.write_str(match *self { @@ -482,29 +488,79 @@ ${helpers.single_keyword("font-variant-caps", } } - impl ToComputedValue for KeywordSize { - type ComputedValue = Au; - #[inline] - fn to_computed_value(&self, _: &Context) -> computed_value::T { - // https://drafts.csswg.org/css-fonts-3/#font-size-prop - use values::FONT_MEDIUM_PX; - match *self { - XXSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 5, - XSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 4, - Small => Au::from_px(FONT_MEDIUM_PX) * 8 / 9, - Medium => Au::from_px(FONT_MEDIUM_PX), - Large => Au::from_px(FONT_MEDIUM_PX) * 6 / 5, - XLarge => Au::from_px(FONT_MEDIUM_PX) * 3 / 2, - XXLarge => Au::from_px(FONT_MEDIUM_PX) * 2, - XXXLarge => Au::from_px(FONT_MEDIUM_PX) * 3, + % if product == "servo": + impl ToComputedValue for KeywordSize { + type ComputedValue = Au; + #[inline] + fn to_computed_value(&self, _: &Context) -> computed_value::T { + // https://drafts.csswg.org/css-fonts-3/#font-size-prop + use values::FONT_MEDIUM_PX; + match *self { + XXSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 5, + XSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 4, + Small => Au::from_px(FONT_MEDIUM_PX) * 8 / 9, + Medium => Au::from_px(FONT_MEDIUM_PX), + Large => Au::from_px(FONT_MEDIUM_PX) * 6 / 5, + XLarge => Au::from_px(FONT_MEDIUM_PX) * 3 / 2, + XXLarge => Au::from_px(FONT_MEDIUM_PX) * 2, + XXXLarge => Au::from_px(FONT_MEDIUM_PX) * 3, + } + } + + #[inline] + fn from_computed_value(_: &computed_value::T) -> Self { + unreachable!() } } + % else: + impl ToComputedValue for KeywordSize { + type ComputedValue = Au; + #[inline] + fn to_computed_value(&self, cx: &Context) -> computed_value::T { + use gecko_bindings::bindings::Gecko_nsStyleFont_GetBaseSize; + use values::specified::length::au_to_int_px; + // Data from nsRuleNode.cpp in Gecko + // Mapping from base size and HTML size to pixels + // The first index is (base_size - 9), the second is the + // HTML size. "0" is CSS keyword xx-small, not HTML size 0, + // since HTML size 0 is the same as 1. + // + // xxs xs s m l xl xxl - + // - 0/1 2 3 4 5 6 7 + static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [ + [9, 9, 9, 9, 11, 14, 18, 27], + [9, 9, 9, 10, 12, 15, 20, 30], + [9, 9, 10, 11, 13, 17, 22, 33], + [9, 9, 10, 12, 14, 18, 24, 36], + [9, 10, 12, 13, 16, 20, 26, 39], + [9, 10, 12, 14, 17, 21, 28, 42], + [9, 10, 13, 15, 18, 23, 30, 45], + [9, 10, 13, 16, 18, 24, 32, 48] + ]; - #[inline] - fn from_computed_value(_: &computed_value::T) -> Self { - unreachable!() + static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300]; + + // XXXManishearth handle quirks mode + + let base_size = unsafe { + Gecko_nsStyleFont_GetBaseSize(cx.style().get_font().gecko(), + &*cx.device.pres_context) + }; + let base_size_px = au_to_int_px(base_size as f32); + let html_size = *self as usize; + if base_size_px >= 9 && base_size_px <= 16 { + Au::from_px(FONT_SIZE_MAPPING[(base_size_px - 9) as usize][html_size]) + } else { + Au(FONT_SIZE_FACTORS[html_size] * base_size / 100) + } + } + + #[inline] + fn from_computed_value(_: &computed_value::T) -> Self { + unreachable!() + } } - } + % endif impl SpecifiedValue { /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size diff --git a/servo/components/style/properties/properties.mako.rs b/servo/components/style/properties/properties.mako.rs index 9fde5fb40b05..49f88fd31aa0 100644 --- a/servo/components/style/properties/properties.mako.rs +++ b/servo/components/style/properties/properties.mako.rs @@ -1502,6 +1502,8 @@ pub struct ComputedValues { pub writing_mode: WritingMode, /// The root element's computed font size. pub root_font_size: Au, + /// The keyword behind the current font-size property, if any + pub font_size_keyword: Option, } #[cfg(feature = "servo")] @@ -1511,6 +1513,7 @@ impl ComputedValues { shareable: bool, writing_mode: WritingMode, root_font_size: Au, + font_size_keyword: Option, % for style_struct in data.active_style_structs(): ${style_struct.ident}: Arc, % endfor @@ -1520,6 +1523,7 @@ impl ComputedValues { shareable: shareable, writing_mode: writing_mode, root_font_size: root_font_size, + font_size_keyword: font_size_keyword, % for style_struct in data.active_style_structs(): ${style_struct.ident}: ${style_struct.ident}, % endfor @@ -1843,6 +1847,7 @@ mod lazy_static_module { shareable: true, writing_mode: WritingMode::empty(), root_font_size: longhands::font_size::get_initial_value(), + font_size_keyword: Some(Default::default()), }; } } @@ -1985,6 +1990,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device, flags.contains(SHAREABLE), WritingMode::empty(), inherited_style.root_font_size, + inherited_style.font_size_keyword, % for style_struct in data.active_style_structs(): % if style_struct.inherited: inherited_style.clone_${style_struct.name_lower}(), @@ -1998,6 +2004,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device, flags.contains(SHAREABLE), WritingMode::empty(), inherited_style.root_font_size, + inherited_style.font_size_keyword, % for style_struct in data.active_style_structs(): inherited_style.clone_${style_struct.name_lower}(), % endfor @@ -2030,6 +2037,13 @@ pub fn apply_declarations<'a, F, I>(device: &Device, // To improve i-cache behavior, we outline the individual functions and use // virtual dispatch instead. % for category_to_cascade_now in ["early", "other"]: + % if category_to_cascade_now == "early": + // Pull these out so that we can + // compute them in a specific order without + // introducing more iterations + let mut font_size = None; + let mut font_family = None; + % endif for declaration in iter_declarations() { let longhand_id = match declaration.id() { PropertyDeclarationId::Longhand(id) => id, @@ -2076,6 +2090,17 @@ pub fn apply_declarations<'a, F, I>(device: &Device, } seen.insert(physical_longhand_id); + % if category_to_cascade_now == "early": + if LonghandId::FontSize == longhand_id { + font_size = Some(declaration); + continue; + } + if LonghandId::FontFamily == longhand_id { + font_family = Some(declaration); + continue; + } + % endif + let discriminant = longhand_id as usize; (CASCADE_PROPERTY[discriminant])(declaration, inherited_style, @@ -2088,6 +2113,51 @@ pub fn apply_declarations<'a, F, I>(device: &Device, % if category_to_cascade_now == "early": let writing_mode = get_writing_mode(context.style.get_inheritedbox()); context.style.writing_mode = writing_mode; + // It is important that font_size is computed before + // the late properties (for em units), but after font-family + // (for the base-font-size dependence for default and keyword font-sizes) + // Additionally, when we support system fonts they will have to be + // computed early, and *before* font_family, so I'm including + // font_family here preemptively instead of keeping it within + // the early properties. + // + // To avoid an extra iteration, we just pull out the property + // during the early iteration and cascade them in order + // after it. + if let Some(declaration) = font_family { + let discriminant = LonghandId::FontFamily as usize; + (CASCADE_PROPERTY[discriminant])(declaration, + inherited_style, + default_style, + &mut context, + &mut cacheable, + &mut cascade_info, + error_reporter); + } + if let Some(declaration) = font_size { + let discriminant = LonghandId::FontSize as usize; + (CASCADE_PROPERTY[discriminant])(declaration, + inherited_style, + default_style, + &mut context, + &mut cacheable, + &mut cascade_info, + error_reporter); + } else if let Some(kw) = inherited_style.font_size_keyword { + // Font size keywords will inherit as keywords and be recomputed + // each time. + let discriminant = LonghandId::FontSize as usize; + let size = PropertyDeclaration::FontSize( + longhands::font_size::SpecifiedValue::Keyword(kw) + ); + (CASCADE_PROPERTY[discriminant])(&size, + inherited_style, + default_style, + &mut context, + &mut cacheable, + &mut cascade_info, + error_reporter); + } % endif % endfor diff --git a/servo/components/style/values/specified/length.rs b/servo/components/style/values/specified/length.rs index bffae92c4ff4..7a16ce41d8fe 100644 --- a/servo/components/style/values/specified/length.rs +++ b/servo/components/style/values/specified/length.rs @@ -33,6 +33,14 @@ const AU_PER_Q: CSSFloat = AU_PER_MM / 4.; const AU_PER_PT: CSSFloat = AU_PER_IN / 72.; const AU_PER_PC: CSSFloat = AU_PER_PT * 12.; +/// Same as Gecko's AppUnitsToIntCSSPixels +/// +/// Converts app units to integer pixel values, +/// rounding during the conversion +pub fn au_to_int_px(au: f32) -> i32 { + (au / AU_PER_PX).round() as i32 +} + #[derive(Clone, PartialEq, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] /// A font relative length.