diff options
-rw-r--r-- | components/layout/css/matching.rs | 38 | ||||
-rw-r--r-- | components/layout/traversal.rs | 3 | ||||
-rw-r--r-- | components/style/legacy.rs | 12 | ||||
-rw-r--r-- | components/style/media_queries.rs | 72 | ||||
-rw-r--r-- | components/style/properties.mako.rs | 50 | ||||
-rw-r--r-- | components/style/values.rs | 186 | ||||
-rw-r--r-- | tests/reftest.rs | 10 |
7 files changed, 254 insertions, 117 deletions
diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs index 8e2002ca06f..f688be2cda5 100644 --- a/components/layout/css/matching.rs +++ b/components/layout/css/matching.rs @@ -6,6 +6,7 @@ #![allow(unsafe_blocks)] +use context::SharedLayoutContext; use css::node_style::StyledNode; use incremental::{self, RestyleDamage}; use data::{LayoutDataAccess, LayoutDataWrapper}; @@ -391,6 +392,7 @@ pub trait MatchMethods { -> StyleSharingResult; unsafe fn cascade_node(&self, + layout_context: &SharedLayoutContext, parent: Option<LayoutNode>, applicable_declarations: &ApplicableDeclarations, applicable_declarations_cache: &mut ApplicableDeclarationsCache); @@ -398,6 +400,7 @@ pub trait MatchMethods { trait PrivateMatchMethods { fn cascade_node_pseudo_element(&self, + layout_context: &SharedLayoutContext, parent_style: Option<&Arc<ComputedValues>>, applicable_declarations: &[DeclarationBlock], style: &mut Option<Arc<ComputedValues>>, @@ -414,6 +417,7 @@ trait PrivateMatchMethods { impl<'ln> PrivateMatchMethods for LayoutNode<'ln> { fn cascade_node_pseudo_element(&self, + layout_context: &SharedLayoutContext, parent_style: Option<&Arc<ComputedValues>>, applicable_declarations: &[DeclarationBlock], style: &mut Option<Arc<ComputedValues>>, @@ -430,7 +434,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> { None => None, Some(ref style) => Some(&**style), }; - let (the_style, is_cacheable) = cascade(applicable_declarations, + let (the_style, is_cacheable) = cascade(layout_context.screen_size, + applicable_declarations, shareable, Some(&***parent_style), cached_computed_values); @@ -438,7 +443,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> { this_style = Arc::new(the_style); } None => { - let (the_style, is_cacheable) = cascade(applicable_declarations, + let (the_style, is_cacheable) = cascade(layout_context.screen_size, + applicable_declarations, shareable, None, None); @@ -602,6 +608,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> { } unsafe fn cascade_node(&self, + layout_context: &SharedLayoutContext, parent: Option<LayoutNode>, applicable_declarations: &ApplicableDeclarations, applicable_declarations_cache: &mut ApplicableDeclarationsCache) { @@ -635,26 +642,29 @@ impl<'ln> MatchMethods for LayoutNode<'ln> { } _ => { let mut damage = self.cascade_node_pseudo_element( + layout_context, parent_style, applicable_declarations.normal.as_slice(), &mut layout_data.shared_data.style, applicable_declarations_cache, applicable_declarations.normal_shareable); if applicable_declarations.before.len() > 0 { - damage = damage | self.cascade_node_pseudo_element( - Some(layout_data.shared_data.style.as_ref().unwrap()), - &*applicable_declarations.before, - &mut layout_data.data.before_style, - applicable_declarations_cache, - false); + damage = damage | self.cascade_node_pseudo_element( + layout_context, + Some(layout_data.shared_data.style.as_ref().unwrap()), + &*applicable_declarations.before, + &mut layout_data.data.before_style, + applicable_declarations_cache, + false); } if applicable_declarations.after.len() > 0 { - damage = damage | self.cascade_node_pseudo_element( - Some(layout_data.shared_data.style.as_ref().unwrap()), - &*applicable_declarations.after, - &mut layout_data.data.after_style, - applicable_declarations_cache, - false); + damage = damage | self.cascade_node_pseudo_element( + layout_context, + Some(layout_data.shared_data.style.as_ref().unwrap()), + &*applicable_declarations.after, + &mut layout_data.data.after_style, + applicable_declarations_cache, + false); } layout_data.data.restyle_damage = damage; } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index ceb7bf78679..a31f6c9d3b6 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -176,7 +176,8 @@ impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> { // Perform the CSS cascade. unsafe { - node.cascade_node(parent_opt, + node.cascade_node(self.layout_context.shared, + parent_opt, &applicable_declarations, self.layout_context.applicable_declarations_cache()); } diff --git a/components/style/legacy.rs b/components/style/legacy.rs index c7d974d1094..d1451ae6bc4 100644 --- a/components/style/legacy.rs +++ b/components/style/legacy.rs @@ -119,7 +119,7 @@ impl PresentationalHintSynthesis for Stylist { *shareable = false } LengthOrPercentageOrAuto::Length(length) => { - let width_value = specified::LengthOrPercentageOrAuto::Length(specified::Length::Au(length)); + let width_value = specified::LengthOrPercentageOrAuto::Length(specified::Length::Absolute(length)); matching_rules_list.vec_push(from_declaration( PropertyDeclaration::Width(SpecifiedValue(width_value)))); *shareable = false @@ -160,9 +160,9 @@ impl PresentationalHintSynthesis for Stylist { // FIXME(pcwalton): More use of atoms, please! let value = match element.get_attr(&ns!(""), &atom!("type")) { Some("text") | Some("password") => { - specified::Length::ServoCharacterWidth(value) + specified::Length::ServoCharacterWidth(specified::CharacterWidth(value)) } - _ => specified::Length::Au(Au::from_px(value as int)), + _ => specified::Length::Absolute(Au::from_px(value as int)), }; matching_rules_list.vec_push(from_declaration( PropertyDeclaration::Width(SpecifiedValue( @@ -180,7 +180,7 @@ impl PresentationalHintSynthesis for Stylist { // scrollbar size into consideration (but we don't have a scrollbar yet!) // // https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-width - let value = specified::Length::ServoCharacterWidth(value); + let value = specified::Length::ServoCharacterWidth(specified::CharacterWidth(value)); matching_rules_list.vec_push(from_declaration( PropertyDeclaration::Width(SpecifiedValue( specified::LengthOrPercentageOrAuto::Length(value))))); @@ -193,7 +193,7 @@ impl PresentationalHintSynthesis for Stylist { // TODO(mttr) This should take scrollbar size into consideration. // // https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-height - let value = specified::Length::Em(value as CSSFloat); + let value = specified::Length::FontRelative(specified::FontRelativeLength::Em(value as CSSFloat)); matching_rules_list.vec_push(from_declaration( PropertyDeclaration::Height(SpecifiedValue( longhands::height::SpecifiedValue( @@ -241,7 +241,7 @@ impl PresentationalHintSynthesis for Stylist { match element.get_unsigned_integer_attribute(UnsignedIntegerAttribute::Border) { None => {} Some(length) => { - let width_value = specified::Length::Au(Au::from_px(length as int)); + let width_value = specified::Length::Absolute(Au::from_px(length as int)); matching_rules_list.vec_push(from_declaration( PropertyDeclaration::BorderTopWidth(SpecifiedValue( longhands::border_top_width::SpecifiedValue(width_value))))); diff --git a/components/style/media_queries.rs b/components/style/media_queries.rs index 1aca08922e5..97f75104c40 100644 --- a/components/style/media_queries.rs +++ b/components/style/media_queries.rs @@ -5,7 +5,7 @@ use std::ascii::AsciiExt; use cssparser::{Token, Parser, Delimiter}; -use geom::size::TypedSize2D; +use geom::size::{Size2D, TypedSize2D}; use properties::longhands; use util::geometry::{Au, ViewportPx}; use values::specified; @@ -23,6 +23,31 @@ pub enum Range<T> { //Eq(T), // FIXME: Implement parsing support for equality then re-enable this. } +impl Range<specified::Length> { + fn to_computed_range(&self, viewport_size: Size2D<Au>) -> Range<Au> { + let compute_width = |width| { + match width { + &specified::Length::Absolute(value) => value, + &specified::Length::FontRelative(value) => { + // http://dev.w3.org/csswg/mediaqueries3/ - Section 6 + // em units are relative to the initial font-size. + let initial_font_size = longhands::font_size::get_initial_value(); + value.to_computed_value(initial_font_size, initial_font_size) + } + &specified::Length::ViewportPercentage(value) => + value.to_computed_value(viewport_size), + _ => unreachable!() + } + }; + + match *self { + Range::Min(ref width) => Range::Min(compute_width(width)), + Range::Max(ref width) => Range::Max(compute_width(width)), + //Range::Eq(ref width) => Range::Eq(compute_width(width)) + } + } +} + impl<T: Ord> Range<T> { fn evaluate(&self, value: T) -> bool { match *self { @@ -33,9 +58,9 @@ impl<T: Ord> Range<T> { } } -#[derive(PartialEq, Eq, Copy, Debug)] +#[derive(PartialEq, Copy, Debug)] pub enum Expression { - Width(Range<Au>), + Width(Range<specified::Length>), } #[derive(PartialEq, Eq, Copy, Debug)] @@ -91,17 +116,6 @@ impl Device { } } - -fn parse_non_negative_length(input: &mut Parser) -> Result<Au, ()> { - let length = try!(specified::Length::parse_non_negative(input)); - - // http://dev.w3.org/csswg/mediaqueries3/ - Section 6 - // em units are relative to the initial font-size. - let initial_font_size = longhands::font_size::get_initial_value(); - Ok(length.to_computed_value_with_font_size(initial_font_size, initial_font_size)) -} - - impl Expression { fn parse(input: &mut Parser) -> Result<Expression, ()> { try!(input.expect_parenthesis_block()); @@ -111,10 +125,10 @@ impl Expression { // TODO: Handle other media features match_ignore_ascii_case! { name, "min-width" => { - Ok(Expression::Width(Range::Min(try!(parse_non_negative_length(input))))) + Ok(Expression::Width(Range::Min(try!(specified::Length::parse_non_negative(input))))) }, "max-width" => { - Ok(Expression::Width(Range::Max(try!(parse_non_negative_length(input))))) + Ok(Expression::Width(Range::Max(try!(specified::Length::parse_non_negative(input))))) } _ => Err(()) } @@ -186,6 +200,9 @@ pub fn parse_media_query_list(input: &mut Parser) -> MediaQueryList { impl MediaQueryList { pub fn evaluate(&self, device: &Device) -> bool { + let viewport_size = Size2D(Au::from_frac32_px(device.viewport_size.width.get()), + Au::from_frac32_px(device.viewport_size.height.get())); + // Check if any queries match (OR condition) self.media_queries.iter().any(|mq| { // Check if media matches. Unknown media never matches. @@ -198,8 +215,8 @@ impl MediaQueryList { // Check if all conditions match (AND condition) let query_match = media_match && mq.expressions.iter().all(|expression| { match expression { - &Expression::Width(value) => value.evaluate( - Au::from_frac_px(device.viewport_size.to_untyped().width as f64)), + &Expression::Width(value) => + value.to_computed_range(viewport_size).evaluate(viewport_size.width), } }); @@ -220,6 +237,7 @@ mod tests { use stylesheets::Origin; use super::*; use url::Url; + use values::specified; use std::borrow::ToOwned; fn test_media_rule<F>(css: &str, callback: F) where F: Fn(&MediaQueryList, &str) { @@ -385,7 +403,7 @@ mod tests { assert!(q.media_type == MediaQueryType::All, css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned()); match q.expressions[0] { - Expression::Width(Range::Min(w)) => assert!(w == Au::from_px(100)), + Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))), _ => panic!("wrong expression type"), } }); @@ -397,7 +415,7 @@ mod tests { assert!(q.media_type == MediaQueryType::All, css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned()); match q.expressions[0] { - Expression::Width(Range::Max(w)) => assert!(w == Au::from_px(43)), + Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))), _ => panic!("wrong expression type"), } }); @@ -412,7 +430,7 @@ mod tests { assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned()); match q.expressions[0] { - Expression::Width(Range::Min(w)) => assert!(w == Au::from_px(100)), + Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))), _ => panic!("wrong expression type"), } }); @@ -424,7 +442,7 @@ mod tests { assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned()); match q.expressions[0] { - Expression::Width(Range::Max(w)) => assert!(w == Au::from_px(43)), + Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))), _ => panic!("wrong expression type"), } }); @@ -436,7 +454,7 @@ mod tests { assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned()); match q.expressions[0] { - Expression::Width(Range::Max(w)) => assert!(w == Au::from_px(52)), + Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(52))), _ => panic!("wrong expression type"), } }); @@ -451,11 +469,11 @@ mod tests { assert!(q.media_type == MediaQueryType::All, css.to_owned()); assert!(q.expressions.len() == 2, css.to_owned()); match q.expressions[0] { - Expression::Width(Range::Min(w)) => assert!(w == Au::from_px(100)), + Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))), _ => panic!("wrong expression type"), } match q.expressions[1] { - Expression::Width(Range::Max(w)) => assert!(w == Au::from_px(200)), + Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))), _ => panic!("wrong expression type"), } }); @@ -467,11 +485,11 @@ mod tests { assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned()); assert!(q.expressions.len() == 2, css.to_owned()); match q.expressions[0] { - Expression::Width(Range::Min(w)) => assert!(w == Au::from_px(100)), + Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))), _ => panic!("wrong expression type"), } match q.expressions[1] { - Expression::Width(Range::Max(w)) => assert!(w == Au::from_px(200)), + Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))), _ => panic!("wrong expression type"), } }); diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 11089b165ce..5e57d9a68ad 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -19,8 +19,9 @@ use cssparser::{Parser, Color, RGBA, AtRuleParser, DeclarationParser, DeclarationListParser, parse_important, ToCss}; use geom::num::Zero; use geom::SideOffsets2D; +use geom::size::Size2D; -use values::specified::BorderStyle; +use values::specified::{Length, BorderStyle}; use values::computed::{self, ToComputedValue}; use selectors::matching::DeclarationBlock; use parser::{ParserContext, log_css_error}; @@ -566,7 +567,7 @@ pub mod longhands { SpecifiedValue::Number(value) => computed_value::T::Number(value), SpecifiedValue::Percentage(value) => { computed_value::T::Length( - specified::Length::Em(value).to_computed_value(context)) + specified::Length::FontRelative(specified::FontRelativeLength::Em(value)).to_computed_value(context)) } } } @@ -1475,21 +1476,21 @@ pub mod longhands { input.try(specified::LengthOrPercentage::parse_non_negative) .map(|value| match value { specified::LengthOrPercentage::Length(value) => value, - specified::LengthOrPercentage::Percentage(value) => specified::Length::Em(value) + specified::LengthOrPercentage::Percentage(value) => specified::Length::FontRelative(specified::FontRelativeLength::Em(value)) }) .or_else(|()| { match_ignore_ascii_case! { try!(input.expect_ident()), - "xx-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 5)), - "x-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 4)), - "small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 8 / 9)), - "medium" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX))), - "large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 6 / 5)), - "x-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 2)), - "xx-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 2)), + "xx-small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 5)), + "x-small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 4)), + "small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 8 / 9)), + "medium" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX))), + "large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 6 / 5)), + "x-large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 2)), + "xx-large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 2)), // https://github.com/servo/servo/issues/3423#issuecomment-56321664 - "smaller" => Ok(specified::Length::Em(0.85)), - "larger" => Ok(specified::Length::Em(1.2)) + "smaller" => Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(0.85))), + "larger" => Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(1.2))) _ => Err(()) } @@ -2038,7 +2039,7 @@ pub mod longhands { pub fn parse_one_box_shadow(input: &mut Parser) -> Result<SpecifiedBoxShadow, ()> { use util::geometry::Au; - let mut lengths = [specified::Length::Au(Au(0)); 4]; + let mut lengths = [specified::Length::Absolute(Au(0)); 4]; let mut lengths_parsed = false; let mut color = None; let mut inset = false; @@ -2208,10 +2209,10 @@ pub mod longhands { })); if sides.len() == 4 { Ok(Some(SpecifiedClipRect { - top: sides[0].unwrap_or(Length::Au(Au(0))), + top: sides[0].unwrap_or(Length::Absolute(Au(0))), right: sides[1], bottom: sides[2], - left: sides[3].unwrap_or(Length::Au(Au(0))), + left: sides[3].unwrap_or(Length::Absolute(Au(0))), })) } else { Err(()) @@ -2317,7 +2318,7 @@ pub mod longhands { fn parse_one_text_shadow(input: &mut Parser) -> Result<SpecifiedTextShadow,()> { use util::geometry::Au; - let mut lengths = [specified::Length::Au(Au(0)); 3]; + let mut lengths = [specified::Length::Absolute(Au(0)); 3]; let mut lengths_parsed = false; let mut color = None; @@ -2855,7 +2856,7 @@ pub mod shorthands { fn parse_one_set_of_border_radii(mut input: &mut Parser) -> Result<[LengthOrPercentage; 4], ()> { let mut count = 0; - let mut values = [LengthOrPercentage::Length(Length::Au(Au(0))); 4]; + let mut values = [LengthOrPercentage::Length(Length::Absolute(Au(0))); 4]; while count < 4 { if let Ok(value) = input.try(LengthOrPercentage::parse) { values[count] = value; @@ -3699,6 +3700,8 @@ fn cascade_with_cached_declarations(applicable_declarations: &[DeclarationBlock< /// Performs the CSS cascade, computing new styles for an element from its parent style and /// optionally a cached related style. The arguments are: /// +/// * `viewport_size`: The size of the initial viewport. +/// /// * `applicable_declarations`: The list of CSS rules that matched. /// /// * `shareable`: Whether the `ComputedValues` structure to be constructed should be considered @@ -3712,7 +3715,8 @@ fn cascade_with_cached_declarations(applicable_declarations: &[DeclarationBlock< /// this is ignored. /// /// Returns the computed values and a boolean indicating whether the result is cacheable. -pub fn cascade(applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>], +pub fn cascade(viewport_size: Size2D<Au>, + applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>], shareable: bool, parent_style: Option< &ComputedValues >, cached_style: Option< &ComputedValues >) @@ -3727,6 +3731,7 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclarati let inherited_font_style = inherited_style.get_font(); computed::Context { is_root_element: is_root_element, + viewport_size: viewport_size, inherited_font_weight: inherited_font_style.font_weight, inherited_font_size: inherited_font_style.font_size, inherited_height: inherited_style.get_box().height, @@ -3769,9 +3774,12 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclarati PropertyDeclaration::FontSize(ref value) => { context.font_size = match *value { DeclaredValue::SpecifiedValue(ref specified_value) => { - specified_value.0.to_computed_value_with_font_size( - context.inherited_font_size, context.root_font_size - ) + match specified_value.0 { + Length::FontRelative(value) => value.to_computed_value(context.inherited_font_size, + context.root_font_size), + Length::ServoCharacterWidth(value) => value.to_computed_value(context.inherited_font_size), + _ => specified_value.0.to_computed_value(&context) + } } DeclaredValue::Initial => longhands::font_size::get_initial_value(), DeclaredValue::Inherit => context.inherited_font_size, diff --git a/components/style/values.rs b/components/style/values.rs index ae1e4bc2600..8c57af3884c 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -52,11 +52,14 @@ pub type CSSFloat = f64; pub mod specified { use std::ascii::AsciiExt; + use std::cmp; use std::f64::consts::PI; use std::fmt; use std::fmt::{Formatter, Debug}; + use std::num::{NumCast, ToPrimitive}; use url::Url; use cssparser::{self, Token, Parser, ToCss, CssStringWriter}; + use geom::size::Size2D; use parser::ParserContext; use text_writer::{self, TextWriter}; use util::geometry::Au; @@ -114,17 +117,114 @@ pub mod specified { } #[derive(Clone, PartialEq, Copy)] - pub enum Length { - Au(Au), // application units + pub enum FontRelativeLength { Em(CSSFloat), Ex(CSSFloat), - Rem(CSSFloat), + Rem(CSSFloat) + } + + impl fmt::Debug for FontRelativeLength { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) } + } + + impl ToCss for FontRelativeLength { + fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match self { + &FontRelativeLength::Em(length) => write!(dest, "{}em", length), + &FontRelativeLength::Ex(length) => write!(dest, "{}ex", length), + &FontRelativeLength::Rem(length) => write!(dest, "{}rem", length) + } + } + } + + impl FontRelativeLength { + pub fn to_computed_value(&self, + reference_font_size: Au, + root_font_size: Au) + -> Au + { + match self { + &FontRelativeLength::Em(length) => reference_font_size.scale_by(length), + &FontRelativeLength::Ex(length) => { + let x_height = 0.5; // TODO: find that from the font + reference_font_size.scale_by(length * x_height) + }, + &FontRelativeLength::Rem(length) => root_font_size.scale_by(length) + } + } + } + + #[derive(Clone, PartialEq, Copy)] + pub enum ViewportPercentageLength { + Vw(CSSFloat), + Vh(CSSFloat), + Vmin(CSSFloat), + Vmax(CSSFloat) + } + + impl fmt::Debug for ViewportPercentageLength { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_to_css(f) } + } + + impl ToCss for ViewportPercentageLength { + fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match self { + &ViewportPercentageLength::Vw(length) => write!(dest, "{}vw", length), + &ViewportPercentageLength::Vh(length) => write!(dest, "{}vh", length), + &ViewportPercentageLength::Vmin(length) => write!(dest, "{}vmin", length), + &ViewportPercentageLength::Vmax(length) => write!(dest, "{}vmax", length) + } + } + } + + impl ViewportPercentageLength { + pub fn to_computed_value(&self, viewport_size: Size2D<Au>) -> Au { + macro_rules! to_unit { + ($viewport_dimension:expr) => { + $viewport_dimension.to_f64().unwrap() / 100.0 + } + } + + let value = match self { + &ViewportPercentageLength::Vw(length) => + length * to_unit!(viewport_size.width), + &ViewportPercentageLength::Vh(length) => + length * to_unit!(viewport_size.height), + &ViewportPercentageLength::Vmin(length) => + length * to_unit!(cmp::min(viewport_size.width, viewport_size.height)), + &ViewportPercentageLength::Vmax(length) => + length * to_unit!(cmp::max(viewport_size.width, viewport_size.height)), + }; + NumCast::from(value).unwrap() + } + } + + #[derive(Clone, PartialEq, Copy)] + pub struct CharacterWidth(pub i32); + + impl CharacterWidth { + pub fn to_computed_value(&self, reference_font_size: Au) -> Au { + // This applies the *converting a character width to pixels* algorithm as specified + // in HTML5 § 14.5.4. + // + // TODO(pcwalton): Find these from the font. + let average_advance = reference_font_size.scale_by(0.5); + let max_advance = reference_font_size; + average_advance.scale_by(self.0 as CSSFloat - 1.0) + max_advance + } + } + + #[derive(Clone, PartialEq, Copy)] + pub enum Length { + Absolute(Au), // application units + FontRelative(FontRelativeLength), + ViewportPercentage(ViewportPercentageLength), /// HTML5 "character width", as defined in HTML5 § 14.5.4. /// /// This cannot be specified by the user directly and is only generated by /// `Stylist::synthesize_rules_for_legacy_attributes()`. - ServoCharacterWidth(i32), + ServoCharacterWidth(CharacterWidth), } impl fmt::Debug for Length { @@ -134,41 +234,15 @@ pub mod specified { impl ToCss for Length { fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { match self { - &Length::Au(length) => write!(dest, "{}px", length.to_subpx()), - &Length::Em(length) => write!(dest, "{}em", length), - &Length::Ex(length) => write!(dest, "{}ex", length), - &Length::Rem(length) => write!(dest, "{}rem", length), + &Length::Absolute(length) => write!(dest, "{}px", length.to_subpx()), + &Length::FontRelative(length) => length.to_css(dest), + &Length::ViewportPercentage(length) => length.to_css(dest), &Length::ServoCharacterWidth(_) => panic!("internal CSS values should never be serialized"), } } } - impl Length { - pub fn to_computed_value_with_font_size(&self, reference_font_size: Au, root_font_size: Au) - -> Au { - match *self { - Length::Au(value) => value, - Length::Em(value) => reference_font_size.scale_by(value), - Length::Ex(value) => { - let x_height = 0.5; // TODO: find that from the font - reference_font_size.scale_by(value * x_height) - }, - Length::Rem(value) => root_font_size.scale_by(value), - Length::ServoCharacterWidth(value) => { - // This applies the *converting a character width to pixels* algorithm as specified - // in HTML5 § 14.5.4. - // - // TODO(pcwalton): Find these from the font. - let average_advance = reference_font_size.scale_by(0.5); - let max_advance = reference_font_size; - average_advance.scale_by(value as CSSFloat - 1.0) + max_advance - } - } - } - } - - const AU_PER_PX: CSSFloat = 60.; const AU_PER_IN: CSSFloat = AU_PER_PX * 96.; const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54; @@ -182,7 +256,7 @@ pub mod specified { Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => { Length::parse_dimension(value.value, unit) } - Token::Number(ref value) if value.value == 0. => Ok(Length::Au(Au(0))), + Token::Number(ref value) if value.value == 0. => Ok(Length::Absolute(Au(0))), _ => Err(()) } } @@ -196,20 +270,26 @@ pub mod specified { pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> { match_ignore_ascii_case! { unit, "px" => Ok(Length::from_px(value)), - "in" => Ok(Length::Au(Au((value * AU_PER_IN) as i32))), - "cm" => Ok(Length::Au(Au((value * AU_PER_CM) as i32))), - "mm" => Ok(Length::Au(Au((value * AU_PER_MM) as i32))), - "pt" => Ok(Length::Au(Au((value * AU_PER_PT) as i32))), - "pc" => Ok(Length::Au(Au((value * AU_PER_PC) as i32))), - "em" => Ok(Length::Em(value)), - "ex" => Ok(Length::Ex(value)), - "rem" => Ok(Length::Rem(value)) + "in" => Ok(Length::Absolute(Au((value * AU_PER_IN) as i32))), + "cm" => Ok(Length::Absolute(Au((value * AU_PER_CM) as i32))), + "mm" => Ok(Length::Absolute(Au((value * AU_PER_MM) as i32))), + "pt" => Ok(Length::Absolute(Au((value * AU_PER_PT) as i32))), + "pc" => Ok(Length::Absolute(Au((value * AU_PER_PC) as i32))), + // font-relative + "em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))), + "ex" => Ok(Length::FontRelative(FontRelativeLength::Ex(value))), + "rem" => Ok(Length::FontRelative(FontRelativeLength::Rem(value))), + // viewport percentages + "vw" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vw(value))), + "vh" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vh(value))), + "vmin" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmin(value))), + "vmax" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmax(value))) _ => Err(()) } } #[inline] pub fn from_px(px_value: CSSFloat) -> Length { - Length::Au(Au((px_value * AU_PER_PX) as i32)) + Length::Absolute(Au((px_value * AU_PER_PX) as i32)) } } @@ -245,7 +325,7 @@ pub mod specified { Ok(LengthOrPercentage::Percentage(value.unit_value)) } Token::Number(ref value) if value.value == 0. => { - Ok(LengthOrPercentage::Length(Length::Au(Au(0)))) + Ok(LengthOrPercentage::Length(Length::Absolute(Au(0)))) } _ => Err(()) } @@ -294,7 +374,7 @@ pub mod specified { Ok(LengthOrPercentageOrAuto::Percentage(value.unit_value)) } Token::Number(ref value) if value.value == 0. => { - Ok(LengthOrPercentageOrAuto::Length(Length::Au(Au(0)))) + Ok(LengthOrPercentageOrAuto::Length(Length::Absolute(Au(0)))) } Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => { Ok(LengthOrPercentageOrAuto::Auto) @@ -345,7 +425,7 @@ pub mod specified { Ok(LengthOrPercentageOrNone::Percentage(value.unit_value)) } Token::Number(ref value) if value.value == 0. => { - Ok(LengthOrPercentageOrNone::Length(Length::Au(Au(0)))) + Ok(LengthOrPercentageOrNone::Length(Length::Absolute(Au(0)))) } Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => { Ok(LengthOrPercentageOrNone::None) @@ -386,7 +466,7 @@ pub mod specified { Ok(PositionComponent::Percentage(value.unit_value)) } Token::Number(ref value) if value.value == 0. => { - Ok(PositionComponent::Length(Length::Au(Au(0)))) + Ok(PositionComponent::Length(Length::Absolute(Au(0)))) } Token::Ident(value) => { match_ignore_ascii_case! { value, @@ -673,6 +753,7 @@ pub mod computed { use super::specified::{AngleOrCorner}; use super::{specified, CSSFloat}; pub use cssparser::Color as CSSColor; + use geom::size::Size2D; use properties::longhands; use std::fmt; use url::Url; @@ -699,6 +780,7 @@ pub mod computed { pub border_bottom_present: bool, pub border_left_present: bool, pub is_root_element: bool, + pub viewport_size: Size2D<Au> // TODO, as needed: viewport size, etc. } @@ -736,7 +818,15 @@ pub mod computed { #[inline] fn to_computed_value(&self, context: &Context) -> Au { - self.to_computed_value_with_font_size(context.font_size, context.root_font_size) + match self { + &specified::Length::Absolute(length) => length, + &specified::Length::FontRelative(length) => + length.to_computed_value(context.font_size, context.root_font_size), + &specified::Length::ViewportPercentage(length) => + length.to_computed_value(context.viewport_size), + &specified::Length::ServoCharacterWidth(length) => + length.to_computed_value(context.font_size) + } } } diff --git a/tests/reftest.rs b/tests/reftest.rs index 3908217c556..f4fe8e51037 100644 --- a/tests/reftest.rs +++ b/tests/reftest.rs @@ -136,6 +136,7 @@ struct Reftest { is_flaky: bool, experimental: bool, fragment_identifier: Option<String>, + resolution: Option<String>, } struct TestLine<'a> { @@ -195,6 +196,7 @@ fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_o let mut flakiness = RenderMode::empty(); let mut experimental = false; let mut fragment_identifier = None; + let mut resolution = None; for condition in conditions_list { match condition { "flaky_cpu" => flakiness.insert(CPU_RENDERING), @@ -207,6 +209,9 @@ fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_o if condition.starts_with("fragment=") { fragment_identifier = Some(condition.slice_from("fragment=".len()).to_string()); } + if condition.starts_with("resolution=") { + resolution = Some(condition.slice_from("resolution=".len()).to_string()); + } } let reftest = Reftest { @@ -219,6 +224,7 @@ fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_o is_flaky: render_mode.intersects(flakiness), experimental: experimental, fragment_identifier: fragment_identifier, + resolution: resolution, }; tests.push(make_test(reftest)); @@ -265,6 +271,10 @@ fn capture(reftest: &Reftest, side: usize) -> (u32, u32, Vec<u8>) { if reftest.experimental { command.arg("--experimental"); } + if let Some(ref resolution) = reftest.resolution { + command.arg("--resolution"); + command.arg(resolution); + } let retval = match command.status() { Ok(status) => status, Err(e) => panic!("failed to execute process: {}", e), |