Skip to content

Commit

Permalink
Add separate specified value for keyword font sizes
Browse files Browse the repository at this point in the history
In Gecko, these keywords compute to different values depending on the
font.

See https://bugzilla.mozilla.org/show_bug.cgi?id=1341775
  • Loading branch information
Manishearth committed Mar 20, 2017
1 parent bb54f0a commit c9198d9
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 109 deletions.
5 changes: 4 additions & 1 deletion components/script/dom/element.rs
Expand Up @@ -478,7 +478,10 @@ impl LayoutElementHelpers for LayoutJS<Element> {
if let Some(font_size) = font_size {
hints.push(from_declaration(
shared_lock,
PropertyDeclaration::FontSize(font_size::SpecifiedValue(font_size.into()))))
PropertyDeclaration::FontSize(
font_size::SpecifiedValue::from_html_size(font_size as u8)
)
))
}

let cellspacing = if let Some(this) = self.downcast::<HTMLTableElement>() {
Expand Down
30 changes: 14 additions & 16 deletions components/script/dom/htmlfontelement.rs
Expand Up @@ -18,7 +18,6 @@ use html5ever_atoms::LocalName;
use servo_atoms::Atom;
use style::attr::AttrValue;
use style::str::{HTML_SPACE_CHARACTERS, read_numbers};
use style::values::specified;

#[dom_struct]
pub struct HTMLFontElement {
Expand Down Expand Up @@ -62,8 +61,7 @@ impl HTMLFontElementMethods for HTMLFontElement {
// https://html.spec.whatwg.org/multipage/#dom-font-size
fn SetSize(&self, value: DOMString) {
let element = self.upcast::<Element>();
let length = parse_length(&value);
element.set_attribute(&local_name!("size"), AttrValue::Length(value.into(), length));
element.set_attribute(&local_name!("size"), parse_size(&value));
}
}

Expand All @@ -76,10 +74,7 @@ impl VirtualMethods for HTMLFontElement {
match name {
&local_name!("face") => AttrValue::from_atomic(value.into()),
&local_name!("color") => AttrValue::from_legacy_color(value.into()),
&local_name!("size") => {
let length = parse_length(&value);
AttrValue::Length(value.into(), length)
},
&local_name!("size") => parse_size(&value),
_ => self.super_type().unwrap().parse_plain_attribute(name, value),
}
}
Expand All @@ -88,7 +83,7 @@ impl VirtualMethods for HTMLFontElement {
pub trait HTMLFontElementLayoutHelpers {
fn get_color(&self) -> Option<RGBA>;
fn get_face(&self) -> Option<Atom>;
fn get_size(&self) -> Option<specified::Length>;
fn get_size(&self) -> Option<u32>;
}

impl HTMLFontElementLayoutHelpers for LayoutJS<HTMLFontElement> {
Expand All @@ -113,18 +108,21 @@ impl HTMLFontElementLayoutHelpers for LayoutJS<HTMLFontElement> {
}

#[allow(unsafe_code)]
fn get_size(&self) -> Option<specified::Length> {
unsafe {
fn get_size(&self) -> Option<u32> {
let size = unsafe {
(*self.upcast::<Element>().unsafe_get())
.get_attr_for_layout(&ns!(), &local_name!("size"))
.and_then(AttrValue::as_length)
.cloned()
};
match size {
Some(&AttrValue::UInt(_, s)) => Some(s),
_ => None,
}
}
}

/// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size
fn parse_length(mut input: &str) -> Option<specified::Length> {
fn parse_size(mut input: &str) -> AttrValue {
let original_input = input;
// Steps 1 & 2 are not relevant

// Step 3
Expand All @@ -138,7 +136,7 @@ fn parse_length(mut input: &str) -> Option<specified::Length> {
let mut input_chars = input.chars().peekable();
let parse_mode = match input_chars.peek() {
// Step 4
None => return None,
None => return AttrValue::String(original_input.into()),

// Step 5
Some(&'+') => {
Expand All @@ -155,7 +153,7 @@ fn parse_length(mut input: &str) -> Option<specified::Length> {
// Steps 6, 7, 8
let mut value = match read_numbers(input_chars) {
(Some(v), _) if v >= 0 => v,
_ => return None,
_ => return AttrValue::String(original_input.into()),
};

// Step 9
Expand All @@ -166,5 +164,5 @@ fn parse_length(mut input: &str) -> Option<specified::Length> {
}

// Steps 10, 11, 12
Some(specified::Length::from_font_size_int(value as u8))
AttrValue::UInt(original_input.into(), value as u32)
}
163 changes: 142 additions & 21 deletions components/style/properties/longhand/font.mako.rs
Expand Up @@ -400,24 +400,129 @@ ${helpers.single_keyword("font-variant-caps",

impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
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"),
}
}
}

impl HasViewportPercentage for SpecifiedValue {
fn has_viewport_percentage(&self) -> bool {
return self.0.has_viewport_percentage()
match *self {
SpecifiedValue::Length(ref lop) => lop.has_viewport_percentage(),
_ => false
}
}
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SpecifiedValue(pub specified::LengthOrPercentage);
pub enum SpecifiedValue {
Length(specified::LengthOrPercentage),
Keyword(KeywordSize),
Smaller,
Larger,
}

pub mod computed_value {
use app_units::Au;
pub type T = Au;
}

/// CSS font keywords
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeywordSize {
XXSmall = 0,
XSmall = 1,
Small = 2,
Medium = 3,
Large = 4,
XLarge = 5,
XXLarge = 6,
// This is not a real font keyword and will not parse
// HTML font-size 7 corresponds to this value
XXXLarge = 7,
}

pub use self::KeywordSize::*;

impl KeywordSize {
pub fn parse(input: &mut Parser) -> Result<Self, ()> {
Ok(match_ignore_ascii_case! {&*input.expect_ident()?,
"xx-small" => XXSmall,
"x-small" => XSmall,
"small" => Small,
"medium" => Medium,
"large" => Large,
"x-large" => XLarge,
"xx-large" => XXLarge,
_ => return Err(())
})
}
}

impl ToCss for KeywordSize {
fn to_css<W>(&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"),
})
}
}

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!()
}
}

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,
})
}
}

#[inline]
#[allow(missing_docs)]
pub fn get_initial_value() -> computed_value::T {
Expand All @@ -426,53 +531,69 @@ ${helpers.single_keyword("font-variant-caps",

#[inline]
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue(specified::LengthOrPercentage::Length(NoCalcLength::medium()))
SpecifiedValue::Keyword(Medium)
}

impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;

#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match self.0 {
LengthOrPercentage::Length(NoCalcLength::FontRelative(value)) => {
use values::specified::length::FontRelativeLength;
match *self {
SpecifiedValue::Length(LengthOrPercentage::Length(
NoCalcLength::FontRelative(value))) => {
value.to_computed_value(context, /* use inherited */ true)
}
LengthOrPercentage::Length(NoCalcLength::ServoCharacterWidth(value)) => {
SpecifiedValue::Length(LengthOrPercentage::Length(
NoCalcLength::ServoCharacterWidth(value))) => {
value.to_computed_value(context.inherited_style().get_font().clone_font_size())
}
LengthOrPercentage::Length(ref l) => {
SpecifiedValue::Length(LengthOrPercentage::Length(ref l)) => {
l.to_computed_value(context)
}
LengthOrPercentage::Percentage(Percentage(value)) => {
SpecifiedValue::Length(LengthOrPercentage::Percentage(Percentage(value))) => {
context.inherited_style().get_font().clone_font_size().scale_by(value)
}
LengthOrPercentage::Calc(ref calc) => {
SpecifiedValue::Length(LengthOrPercentage::Calc(ref calc)) => {
let calc = calc.to_computed_value(context);
calc.length() + context.inherited_style().get_font().clone_font_size()
.scale_by(calc.percentage())
}
SpecifiedValue::Keyword(ref key) => {
key.to_computed_value(context)
}
SpecifiedValue::Smaller => {
FontRelativeLength::Em(0.85).to_computed_value(context,
/* use_inherited */ true)
}
SpecifiedValue::Larger => {
FontRelativeLength::Em(1.2).to_computed_value(context,
/* use_inherited */ true)
}
}
}

#[inline]
fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue(LengthOrPercentage::Length(
SpecifiedValue::Length(LengthOrPercentage::Length(
ToComputedValue::from_computed_value(computed)
))
}
}
/// <length> | <percentage> | <absolute-size> | <relative-size>
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
use values::specified::{Length, LengthOrPercentage};

input.try(specified::LengthOrPercentage::parse_non_negative)
.or_else(|()| {
let ident = try!(input.expect_ident());
NoCalcLength::from_str(&ident as &str)
.ok_or(())
.map(specified::LengthOrPercentage::Length)
}).map(SpecifiedValue)
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if let Ok(lop) = input.try(specified::LengthOrPercentage::parse_non_negative) {
Ok(SpecifiedValue::Length(lop))
} else if let Ok(kw) = input.try(KeywordSize::parse) {
Ok(SpecifiedValue::Keyword(kw))
} else {
match_ignore_ascii_case! {&*input.expect_ident()?,
"smaller" => Ok(SpecifiedValue::Smaller),
"larger" => Ok(SpecifiedValue::Larger),
_ => Err(())
}
}
}
</%helpers:longhand>

Expand Down
42 changes: 0 additions & 42 deletions components/style/values/specified/length.rs
Expand Up @@ -276,38 +276,6 @@ impl Mul<CSSFloat> for NoCalcLength {
}

impl NoCalcLength {
/// https://drafts.csswg.org/css-fonts-3/#font-size-prop
pub fn from_str(s: &str) -> Option<NoCalcLength> {
Some(match_ignore_ascii_case! { s,
"xx-small" => NoCalcLength::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 5),
"x-small" => NoCalcLength::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 4),
"small" => NoCalcLength::Absolute(Au::from_px(FONT_MEDIUM_PX) * 8 / 9),
"medium" => NoCalcLength::Absolute(Au::from_px(FONT_MEDIUM_PX)),
"large" => NoCalcLength::Absolute(Au::from_px(FONT_MEDIUM_PX) * 6 / 5),
"x-large" => NoCalcLength::Absolute(Au::from_px(FONT_MEDIUM_PX) * 3 / 2),
"xx-large" => NoCalcLength::Absolute(Au::from_px(FONT_MEDIUM_PX) * 2),

// https://github.com/servo/servo/issues/3423#issuecomment-56321664
"smaller" => NoCalcLength::FontRelative(FontRelativeLength::Em(0.85)),
"larger" => NoCalcLength::FontRelative(FontRelativeLength::Em(1.2)),
_ => return None
})
}

/// https://drafts.csswg.org/css-fonts-3/#font-size-prop
pub fn from_font_size_int(i: u8) -> Self {
let au = match i {
0 | 1 => Au::from_px(FONT_MEDIUM_PX) * 3 / 4,
2 => Au::from_px(FONT_MEDIUM_PX) * 8 / 9,
3 => Au::from_px(FONT_MEDIUM_PX),
4 => Au::from_px(FONT_MEDIUM_PX) * 6 / 5,
5 => Au::from_px(FONT_MEDIUM_PX) * 3 / 2,
6 => Au::from_px(FONT_MEDIUM_PX) * 2,
_ => Au::from_px(FONT_MEDIUM_PX) * 3,
};
NoCalcLength::Absolute(au)
}

/// Parse a given absolute or relative dimension.
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<NoCalcLength, ()> {
match_ignore_ascii_case! { unit,
Expand Down Expand Up @@ -444,21 +412,11 @@ impl Length {
Length::NoCalc(NoCalcLength::zero())
}

/// https://drafts.csswg.org/css-fonts-3/#font-size-prop
pub fn from_str(s: &str) -> Option<Length> {
NoCalcLength::from_str(s).map(Length::NoCalc)
}

/// Parse a given absolute or relative dimension.
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> {
NoCalcLength::parse_dimension(value, unit).map(Length::NoCalc)
}

/// https://drafts.csswg.org/css-fonts-3/#font-size-prop
pub fn from_font_size_int(i: u8) -> Self {
Length::NoCalc(NoCalcLength::from_font_size_int(i))
}

#[inline]
fn parse_internal(input: &mut Parser, context: AllowedNumericType) -> Result<Length, ()> {
match try!(input.next()) {
Expand Down

0 comments on commit c9198d9

Please sign in to comment.