diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 6114267e2364..a0490db4e1d2 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -2161,7 +2161,7 @@ fn static_assert() { } pub fn set_font_size(&mut self, v: longhands::font_size::computed_value::T) { - use self::longhands::font_size::KeywordSize; + use values::specified::font::KeywordSize; self.gecko.mSize = v.size().0; self.gecko.mScriptUnconstrainedSize = v.size().0; if let Some(info) = v.info { @@ -2370,7 +2370,8 @@ fn static_assert() { } pub fn clone_font_size(&self) -> longhands::font_size::computed_value::T { - use self::longhands::font_size::KeywordSize; + use values::computed::font::KeywordInfo; + use values::specified::font::KeywordSize; let size = Au(self.gecko.mSize).into(); let kw = match self.gecko.mFontSizeKeyword as u32 { structs::NS_STYLE_FONT_SIZE_XXSMALL => KeywordSize::XXSmall, @@ -2391,7 +2392,7 @@ fn static_assert() { }; longhands::font_size::computed_value::T { size: size, - info: Some(longhands::font_size::computed_value::KeywordInfo { + info: Some(KeywordInfo { kw: kw, factor: self.gecko.mFontSizeFactor, offset: Au(self.gecko.mFontSizeOffset).into() diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 9d3ceba8d740..455ea5dd14b4 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -600,363 +600,23 @@ ${helpers.single_keyword_system("font-variant-caps", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size"> use app_units::Au; - use properties::longhands::system_font::SystemFont; - use std::fmt; - use style_traits::ToCss; - use values::FONT_MEDIUM_PX; - use values::computed::NonNegativeLength; - use values::specified::{AllowQuirks, LengthOrPercentage, NoCalcLength}; + use values::specified::AllowQuirks; use values::specified::length::FontBaseSize; - - impl ToCss for SpecifiedValue { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - SpecifiedValue::Length(ref lop) => lop.to_css(dest), - SpecifiedValue::Keyword(kw, _, _) => kw.to_css(dest), - SpecifiedValue::Smaller => dest.write_str("smaller"), - SpecifiedValue::Larger => dest.write_str("larger"), - SpecifiedValue::System(sys) => sys.to_css(dest), - } - } - } - - #[derive(Clone, Debug, PartialEq)] - #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub enum SpecifiedValue { - Length(specified::LengthOrPercentage), - /// A keyword value, along with a ratio and absolute offset. - /// The ratio in any specified keyword value - /// will be 1 (with offset 0), but we cascade keywordness even - /// after font-relative (percent and em) values - /// have been applied, which is where the ratio - /// comes in. The offset comes in if we cascaded a calc value, - /// where the font-relative portion (em and percentage) will - /// go into the ratio, and the remaining units all computed together - /// will go into the offset. - /// See bug 1355707. - Keyword(KeywordSize, f32, NonNegativeLength), - Smaller, - Larger, - System(SystemFont) - } - - impl From for SpecifiedValue { - fn from(other: specified::LengthOrPercentage) -> Self { - SpecifiedValue::Length(other) - } - } + use values::specified::font::{FONT_MEDIUM_PX, KeywordSize}; pub mod computed_value { - use app_units::Au; - use std::fmt; - use style_traits::ToCss; - use values::computed::NonNegativeLength; - - #[derive(Copy, Clone, PartialEq, Debug)] - #[derive(ToAnimatedValue, Animate, ToAnimatedZero, ComputeSquaredDistance)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct T { - pub size: NonNegativeLength, - pub info: Option, - } - - #[derive(Copy, Clone, PartialEq, Debug)] - #[derive(ToAnimatedValue, Animate, ToAnimatedZero, ComputeSquaredDistance)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct KeywordInfo { - pub kw: super::KeywordSize, - pub factor: f32, - pub offset: NonNegativeLength, - } - - impl KeywordInfo { - /// Given a parent keyword info (self), apply an additional factor/offset to it - pub fn compose(self, factor: f32, offset: NonNegativeLength) -> Self { - KeywordInfo { - kw: self.kw, - factor: self.factor * factor, - offset: self.offset.scale_by(factor) + offset, - } - } - } - - impl T { - pub fn size(self) -> Au { - self.size.into() - } - } - - impl ToCss for T { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - self.size.to_css(dest) - } - } + use values::computed::font; + pub type T = font::FontSize; } - /// CSS font keywords - #[derive(Clone, Copy, Debug, PartialEq)] - #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] - #[derive(ToAnimatedValue, Animate, ToAnimatedZero, ComputeSquaredDistance)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub enum KeywordSize { - XXSmall = 1, // This is to enable the NonZero optimization - // which simplifies the representation of Option - // in bindgen - XSmall, - Small, - Medium, - Large, - XLarge, - XXLarge, - // This is not a real font keyword and will not parse - // HTML font-size 7 corresponds to this value - XXXLarge, - } - - pub use self::KeywordSize::*; - - impl KeywordSize { - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - try_match_ident_ignore_ascii_case! { input.expect_ident()?, - "xx-small" => Ok(XXSmall), - "x-small" => Ok(XSmall), - "small" => Ok(Small), - "medium" => Ok(Medium), - "large" => Ok(Large), - "x-large" => Ok(XLarge), - "xx-large" => Ok(XXLarge), - } - } - - pub fn html_size(&self) -> u8 { - match *self { - KeywordSize::XXSmall => 0, - KeywordSize::XSmall => 1, - KeywordSize::Small => 2, - KeywordSize::Medium => 3, - KeywordSize::Large => 4, - KeywordSize::XLarge => 5, - KeywordSize::XXLarge => 6, - KeywordSize::XXXLarge => 7, - } - } - } - - 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 { - XXSmall => "xx-small", - XSmall => "x-small", - Small => "small", - Medium => "medium", - Large => "large", - XLarge => "x-large", - XXLarge => "xx-large", - XXXLarge => unreachable!("We should never serialize \ - specified values set via - HTML presentation attributes"), - }) - } - } - - % if product == "servo": - impl ToComputedValue for KeywordSize { - type ComputedValue = NonNegativeLength; - #[inline] - fn to_computed_value(&self, _: &Context) -> NonNegativeLength { - // 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, - }.into() - } - - #[inline] - fn from_computed_value(_: &NonNegativeLength) -> Self { - unreachable!() - } - } - % else: - impl ToComputedValue for KeywordSize { - type ComputedValue = NonNegativeLength; - #[inline] - fn to_computed_value(&self, cx: &Context) -> NonNegativeLength { - use gecko_bindings::structs::nsIAtom; - 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] - ]; - - static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300]; - - // XXXManishearth handle quirks mode - - let ref gecko_font = cx.style().get_font().gecko(); - let base_size = unsafe { Atom::with(gecko_font.mLanguage.raw::(), |atom| { - cx.font_metrics_provider.get_size(atom, gecko_font.mGenericID).0 - }) }; - - let base_size_px = au_to_int_px(base_size as f32); - let html_size = self.html_size() as usize; - if base_size_px >= 9 && base_size_px <= 16 { - NonNegativeLength::new(FONT_SIZE_MAPPING[(base_size_px - 9) as usize][html_size] as f32) - } else { - Au(FONT_SIZE_FACTORS[html_size] * base_size / 100).into() - } - } - - #[inline] - fn from_computed_value(_: &NonNegativeLength) -> Self { - unreachable!() - } - } - % endif - - /// This is the ratio applied for font-size: larger - /// and smaller by both Firefox and Chrome - const LARGER_FONT_SIZE_RATIO: f32 = 1.2; - - impl SpecifiedValue { - /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size - pub fn from_html_size(size: u8) -> Self { - SpecifiedValue::Keyword(match size { - // If value is less than 1, let it be 1. - 0 | 1 => XSmall, - 2 => Small, - 3 => Medium, - 4 => Large, - 5 => XLarge, - 6 => XXLarge, - // If value is greater than 7, let it be 7. - _ => XXXLarge, - }, 1., Au(0).into()) - } - - /// Compute it against a given base font size - pub fn to_computed_value_against( - &self, - context: &Context, - base_size: FontBaseSize, - ) -> computed_value::T { - use values::specified::length::FontRelativeLength; - let compose_keyword = |factor| { - context.style().get_parent_font() - .clone_font_size().info - .map(|i| i.compose(factor, Au(0).into())) - }; - let mut info = None; - let size = match *self { - SpecifiedValue::Length(LengthOrPercentage::Length( - NoCalcLength::FontRelative(value))) => { - if let FontRelativeLength::Em(em) = value { - // If the parent font was keyword-derived, this is too. - // Tack the em unit onto the factor - info = compose_keyword(em); - } - value.to_computed_value(context, base_size).into() - } - SpecifiedValue::Length(LengthOrPercentage::Length( - NoCalcLength::ServoCharacterWidth(value))) => { - value.to_computed_value(base_size.resolve(context)).into() - } - SpecifiedValue::Length(LengthOrPercentage::Length( - NoCalcLength::Absolute(ref l))) => { - context.maybe_zoom_text(l.to_computed_value(context).into()) - } - SpecifiedValue::Length(LengthOrPercentage::Length(ref l)) => { - l.to_computed_value(context).into() - } - SpecifiedValue::Length(LengthOrPercentage::Percentage(pc)) => { - // If the parent font was keyword-derived, this is too. - // Tack the % onto the factor - info = compose_keyword(pc.0); - base_size.resolve(context).scale_by(pc.0).into() - } - SpecifiedValue::Length(LengthOrPercentage::Calc(ref calc)) => { - let parent = context.style().get_parent_font().clone_font_size(); - // if we contain em/% units and the parent was keyword derived, this is too - // Extract the ratio/offset and compose it - if (calc.em.is_some() || calc.percentage.is_some()) && parent.info.is_some() { - let ratio = calc.em.unwrap_or(0.) + calc.percentage.map_or(0., |pc| pc.0); - // Compute it, but shave off the font-relative part (em, %) - // This will mean that other font-relative units like ex and ch will be computed against - // the old font even when the font changes. There's no particular "right answer" for what - // to do here -- Gecko recascades as if the font had changed, we instead track the changes - // and reapply, which means that we carry over old computed ex/ch values whilst Gecko - // recomputes new ones. This is enough of an edge case to not really matter. - let abs = calc.to_computed_value_zoomed(context, FontBaseSize::Custom(Au(0).into())) - .length_component().into(); - info = parent.info.map(|i| i.compose(ratio, abs)); - } - let calc = calc.to_computed_value_zoomed(context, base_size); - calc.to_used_value(Some(base_size.resolve(context))).unwrap().into() - } - SpecifiedValue::Keyword(key, fraction, offset) => { - // As a specified keyword, this is keyword derived - info = Some(computed_value::KeywordInfo { - kw: key, - factor: fraction, - offset: offset, - }); - let key_len = key.to_computed_value(context).scale_by(fraction) + offset; - context.maybe_zoom_text(key_len).into() - } - SpecifiedValue::Smaller => { - info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO); - FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO) - .to_computed_value(context, base_size).into() - } - SpecifiedValue::Larger => { - info = compose_keyword(LARGER_FONT_SIZE_RATIO); - FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO) - .to_computed_value(context, base_size).into() - } - - SpecifiedValue::System(_) => { - <%self:nongecko_unreachable> - context.cached_system_font.as_ref().unwrap().font_size.size - - } - }; - computed_value::T { size, info } - } - } + pub use values::specified::font::FontSize as SpecifiedValue; #[inline] #[allow(missing_docs)] pub fn get_initial_value() -> computed_value::T { computed_value::T { size: Au::from_px(FONT_MEDIUM_PX).into(), - info: Some(computed_value::KeywordInfo { + info: Some(::values::computed::font::KeywordInfo { kw: KeywordSize::Medium, factor: 1., offset: Au(0).into(), @@ -966,26 +626,10 @@ ${helpers.single_keyword_system("font-variant-caps", #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue::Keyword(Medium, 1., Au(0).into()) + SpecifiedValue::Keyword(KeywordSize::Medium, 1., Au(0).into()) } - impl ToComputedValue for SpecifiedValue { - type ComputedValue = computed_value::T; - - #[inline] - fn to_computed_value(&self, context: &Context) -> computed_value::T { - self.to_computed_value_against(context, FontBaseSize::InheritedStyle) - } - - #[inline] - fn from_computed_value(computed: &computed_value::T) -> Self { - SpecifiedValue::Length(LengthOrPercentage::Length( - ToComputedValue::from_computed_value(&computed.size.0) - )) - } - } - /// | | | pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { @@ -1012,19 +656,6 @@ ${helpers.single_keyword_system("font-variant-caps", } } - 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 - } - } - } - #[allow(unused_mut)] pub fn cascade_specified_font_size(context: &mut Context, specified_value: &SpecifiedValue, diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs new file mode 100644 index 000000000000..62110779d617 --- /dev/null +++ b/components/style/values/computed/font.rs @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Computed values for font properties + +use app_units::Au; +use std::fmt; +use style_traits::ToCss; +use values::computed::NonNegativeLength; +use values::specified::font as specified; + +#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(ToAnimatedValue, Animate, ToAnimatedZero, ComputeSquaredDistance)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] +/// The computed value of font-size +pub struct FontSize { + /// The size. + pub size: NonNegativeLength, + /// If derived from a keyword, the keyword and additional transformations applied to it + pub info: Option, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(ToAnimatedValue, Animate, ToAnimatedZero, ComputeSquaredDistance)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] +/// Additional information for keyword-derived font sizes. +pub struct KeywordInfo { + /// The keyword used + pub kw: specified::KeywordSize, + /// A factor to be multiplied by the computed size of the keyword + pub factor: f32, + /// An additional Au offset to add to the kw*factor in the case of calcs + pub offset: NonNegativeLength, +} + +impl KeywordInfo { + /// Given a parent keyword info (self), apply an additional factor/offset to it + pub fn compose(self, factor: f32, offset: NonNegativeLength) -> Self { + KeywordInfo { + kw: self.kw, + factor: self.factor * factor, + offset: self.offset.scale_by(factor) + offset, + } + } +} + +impl FontSize { + /// The actual computed font size. + pub fn size(self) -> Au { + self.size.into() + } +} + +impl ToCss for FontSize { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.size.to_css(dest) + } +} diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 1c8a478f2050..d7f6ddc5af9b 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -67,6 +67,7 @@ pub mod box_; pub mod color; pub mod effects; pub mod flex; +pub mod font; pub mod image; #[cfg(feature = "gecko")] pub mod gecko; diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs index 2a0483ca630f..15978aa04a3f 100644 --- a/components/style/values/mod.rs +++ b/components/style/values/mod.rs @@ -29,9 +29,6 @@ pub type CSSFloat = f32; /// A CSS integer value. pub type CSSInteger = i32; -/// The default font size. -pub const FONT_MEDIUM_PX: i32 = 16; - define_keyword_type!(None_, "none"); define_keyword_type!(Auto, "auto"); define_keyword_type!(Normal, "normal"); diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs new file mode 100644 index 000000000000..af0d5df149b6 --- /dev/null +++ b/components/style/values/specified/font.rs @@ -0,0 +1,355 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Specified values for font properties + +#[cfg(feature = "gecko")] +use Atom; +use app_units::Au; +use cssparser::Parser; +use properties::longhands::system_font::SystemFont; +use std::fmt; +use style_traits::{ToCss, ParseError}; +use values::computed::{font as computed, Context, NonNegativeLength, ToComputedValue}; +use values::specified::{LengthOrPercentage, NoCalcLength}; +use values::specified::length::FontBaseSize; + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] +/// A specified font-size value +pub enum FontSize { + /// A length; e.g. 10px. + Length(LengthOrPercentage), + /// A keyword value, along with a ratio and absolute offset. + /// The ratio in any specified keyword value + /// will be 1 (with offset 0), but we cascade keywordness even + /// after font-relative (percent and em) values + /// have been applied, which is where the ratio + /// comes in. The offset comes in if we cascaded a calc value, + /// where the font-relative portion (em and percentage) will + /// go into the ratio, and the remaining units all computed together + /// will go into the offset. + /// See bug 1355707. + Keyword(KeywordSize, f32, NonNegativeLength), + /// font-size: smaller + Smaller, + /// font-size: larger + Larger, + /// Derived from a specified system font. + System(SystemFont) +} + +impl ToCss for FontSize { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + FontSize::Length(ref lop) => lop.to_css(dest), + FontSize::Keyword(kw, _, _) => kw.to_css(dest), + FontSize::Smaller => dest.write_str("smaller"), + FontSize::Larger => dest.write_str("larger"), + FontSize::System(sys) => sys.to_css(dest), + } + } +} + +impl From for FontSize { + fn from(other: LengthOrPercentage) -> Self { + FontSize::Length(other) + } +} + +/// CSS font keywords +#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(ToAnimatedValue, Animate, ToAnimatedZero, ComputeSquaredDistance)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] +#[allow(missing_docs)] +pub enum KeywordSize { + XXSmall = 1, // This is to enable the NonZero optimization + // which simplifies the representation of Option + // in bindgen + XSmall, + Small, + Medium, + Large, + XLarge, + XXLarge, + // This is not a real font keyword and will not parse + // HTML font-size 7 corresponds to this value + XXXLarge, +} + +impl KeywordSize { + /// Parse a keyword size + pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { + try_match_ident_ignore_ascii_case! { input.expect_ident()?, + "xx-small" => Ok(KeywordSize::XXSmall), + "x-small" => Ok(KeywordSize::XSmall), + "small" => Ok(KeywordSize::Small), + "medium" => Ok(KeywordSize::Medium), + "large" => Ok(KeywordSize::Large), + "x-large" => Ok(KeywordSize::XLarge), + "xx-large" => Ok(KeywordSize::XXLarge), + } + } + + /// Convert to an HTML value + pub fn html_size(&self) -> u8 { + match *self { + KeywordSize::XXSmall => 0, + KeywordSize::XSmall => 1, + KeywordSize::Small => 2, + KeywordSize::Medium => 3, + KeywordSize::Large => 4, + KeywordSize::XLarge => 5, + KeywordSize::XXLarge => 6, + KeywordSize::XXXLarge => 7, + } + } +} + +impl Default for KeywordSize { + fn default() -> Self { + KeywordSize::Medium + } +} + +impl ToCss for KeywordSize { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str(match *self { + KeywordSize::XXSmall => "xx-small", + KeywordSize::XSmall => "x-small", + KeywordSize::Small => "small", + KeywordSize::Medium => "medium", + KeywordSize::Large => "large", + KeywordSize::XLarge => "x-large", + KeywordSize::XXLarge => "xx-large", + KeywordSize::XXXLarge => unreachable!("We should never serialize \ + specified values set via + HTML presentation attributes"), + }) + } +} + +/// This is the ratio applied for font-size: larger +/// and smaller by both Firefox and Chrome +const LARGER_FONT_SIZE_RATIO: f32 = 1.2; + +/// The default font size. +pub const FONT_MEDIUM_PX: i32 = 16; + +#[cfg(feature = "servo")] +impl ToComputedValue for KeywordSize { + type ComputedValue = NonNegativeLength; + #[inline] + fn to_computed_value(&self, _: &Context) -> NonNegativeLength { + // https://drafts.csswg.org/css-fonts-3/#font-size-prop + match *self { + KeywordSize::XXSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 5, + KeywordSize::XSmall => Au::from_px(FONT_MEDIUM_PX) * 3 / 4, + KeywordSize::Small => Au::from_px(FONT_MEDIUM_PX) * 8 / 9, + KeywordSize::Medium => Au::from_px(FONT_MEDIUM_PX), + KeywordSize::Large => Au::from_px(FONT_MEDIUM_PX) * 6 / 5, + KeywordSize::XLarge => Au::from_px(FONT_MEDIUM_PX) * 3 / 2, + KeywordSize::XXLarge => Au::from_px(FONT_MEDIUM_PX) * 2, + KeywordSize::XXXLarge => Au::from_px(FONT_MEDIUM_PX) * 3, + }.into() + } + + #[inline] + fn from_computed_value(_: &NonNegativeLength) -> Self { + unreachable!() + } +} + +#[cfg(feature = "gecko")] +impl ToComputedValue for KeywordSize { + type ComputedValue = NonNegativeLength; + #[inline] + fn to_computed_value(&self, cx: &Context) -> NonNegativeLength { + use gecko_bindings::structs::nsIAtom; + 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] + ]; + + static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300]; + + // XXXManishearth handle quirks mode (bug 1401322) + + let ref gecko_font = cx.style().get_font().gecko(); + let base_size = unsafe { Atom::with(gecko_font.mLanguage.raw::(), |atom| { + cx.font_metrics_provider.get_size(atom, gecko_font.mGenericID).0 + }) }; + + let base_size_px = au_to_int_px(base_size as f32); + let html_size = self.html_size() 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]).into() + } else { + Au(FONT_SIZE_FACTORS[html_size] * base_size / 100).into() + } + } + + #[inline] + fn from_computed_value(_: &NonNegativeLength) -> Self { + unreachable!() + } +} + +impl FontSize { + /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size + pub fn from_html_size(size: u8) -> Self { + FontSize::Keyword(match size { + // If value is less than 1, let it be 1. + 0 | 1 => KeywordSize::XSmall, + 2 => KeywordSize::Small, + 3 => KeywordSize::Medium, + 4 => KeywordSize::Large, + 5 => KeywordSize::XLarge, + 6 => KeywordSize::XXLarge, + // If value is greater than 7, let it be 7. + _ => KeywordSize::XXXLarge, + }, 1., Au(0).into()) + } + + /// Compute it against a given base font size + pub fn to_computed_value_against( + &self, + context: &Context, + base_size: FontBaseSize, + ) -> computed::FontSize { + use values::specified::length::FontRelativeLength; + + let compose_keyword = |factor| { + context.style().get_parent_font() + .clone_font_size().info + .map(|i| i.compose(factor, Au(0).into())) + }; + let mut info = None; + let size = match *self { + FontSize::Length(LengthOrPercentage::Length( + NoCalcLength::FontRelative(value))) => { + if let FontRelativeLength::Em(em) = value { + // If the parent font was keyword-derived, this is too. + // Tack the em unit onto the factor + info = compose_keyword(em); + } + value.to_computed_value(context, base_size).into() + } + FontSize::Length(LengthOrPercentage::Length( + NoCalcLength::ServoCharacterWidth(value))) => { + value.to_computed_value(base_size.resolve(context)).into() + } + FontSize::Length(LengthOrPercentage::Length( + NoCalcLength::Absolute(ref l))) => { + context.maybe_zoom_text(l.to_computed_value(context).into()) + } + FontSize::Length(LengthOrPercentage::Length(ref l)) => { + l.to_computed_value(context).into() + } + FontSize::Length(LengthOrPercentage::Percentage(pc)) => { + // If the parent font was keyword-derived, this is too. + // Tack the % onto the factor + info = compose_keyword(pc.0); + base_size.resolve(context).scale_by(pc.0).into() + } + FontSize::Length(LengthOrPercentage::Calc(ref calc)) => { + let parent = context.style().get_parent_font().clone_font_size(); + // if we contain em/% units and the parent was keyword derived, this is too + // Extract the ratio/offset and compose it + if (calc.em.is_some() || calc.percentage.is_some()) && parent.info.is_some() { + let ratio = calc.em.unwrap_or(0.) + calc.percentage.map_or(0., |pc| pc.0); + // Compute it, but shave off the font-relative part (em, %) + // This will mean that other font-relative units like ex and ch will be computed against + // the old font even when the font changes. There's no particular "right answer" for what + // to do here -- Gecko recascades as if the font had changed, we instead track the changes + // and reapply, which means that we carry over old computed ex/ch values whilst Gecko + // recomputes new ones. This is enough of an edge case to not really matter. + let abs = calc.to_computed_value_zoomed(context, FontBaseSize::Custom(Au(0).into())) + .length_component().into(); + info = parent.info.map(|i| i.compose(ratio, abs)); + } + let calc = calc.to_computed_value_zoomed(context, base_size); + calc.to_used_value(Some(base_size.resolve(context))).unwrap().into() + } + FontSize::Keyword(key, fraction, offset) => { + // As a specified keyword, this is keyword derived + info = Some(computed::KeywordInfo { + kw: key, + factor: fraction, + offset: offset, + }); + context.maybe_zoom_text(key.to_computed_value(context).scale_by(fraction) + offset) + } + FontSize::Smaller => { + info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO); + FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO) + .to_computed_value(context, base_size).into() + } + FontSize::Larger => { + info = compose_keyword(LARGER_FONT_SIZE_RATIO); + FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO) + .to_computed_value(context, base_size).into() + } + + FontSize::System(_) => { + #[cfg(feature = "servo")] { + unreachable!() + } + #[cfg(feature = "gecko")] { + context.cached_system_font.as_ref().unwrap().font_size.size + } + } + }; + computed::FontSize { size, info } + } +} + +impl ToComputedValue for FontSize { + type ComputedValue = computed::FontSize; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed::FontSize { + self.to_computed_value_against(context, FontBaseSize::InheritedStyle) + } + + #[inline] + fn from_computed_value(computed: &computed::FontSize) -> Self { + FontSize::Length(LengthOrPercentage::Length( + ToComputedValue::from_computed_value(&computed.size.0) + )) + } +} + +impl FontSize { + /// Construct a system font value. + pub fn system_font(f: SystemFont) -> Self { + FontSize::System(f) + } + + /// Obtain the system font, if any + pub fn get_system(&self) -> Option { + if let FontSize::System(s) = *self { + Some(s) + } else { + None + } + } +} diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index 257781f62aeb..7d1ff581a79a 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -18,7 +18,7 @@ use style_traits::{ToCss, ParseError, StyleParseError}; use style_traits::values::specified::AllowedNumericType; use stylesheets::CssRuleType; use super::{AllowQuirks, Number, ToComputedValue, Percentage}; -use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, None_, Normal}; +use values::{Auto, CSSFloat, Either, None_, Normal}; use values::{ExtremumLength, serialize_dimension}; use values::computed::{self, CSSPixelLength, Context}; use values::generics::NonNegative; @@ -571,12 +571,6 @@ impl NoCalcLength { } } - #[inline] - /// Returns a `medium` length. - pub fn medium() -> NoCalcLength { - NoCalcLength::Absolute(AbsoluteLength::Px(FONT_MEDIUM_PX as f32)) - } - /// Get an absolute length from a px value. #[inline] pub fn from_px(px_value: CSSFloat) -> NoCalcLength { diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 856aecca90ac..1add4928007e 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -65,6 +65,7 @@ pub mod calc; pub mod color; pub mod effects; pub mod flex; +pub mod font; #[cfg(feature = "gecko")] pub mod gecko; pub mod grid;