auto merge of #5154 : luniv/servo/viewpoint-percent-lengths, r=SimonSapin

Spec: http://dev.w3.org/csswg/css-values-3/#viewport-relative-lengths
This commit is contained in:
bors-servo 2015-03-05 18:55:02 -07:00
commit a557b51c28
7 changed files with 254 additions and 117 deletions

View file

@ -6,6 +6,7 @@
#![allow(unsafe_blocks)] #![allow(unsafe_blocks)]
use context::SharedLayoutContext;
use css::node_style::StyledNode; use css::node_style::StyledNode;
use incremental::{self, RestyleDamage}; use incremental::{self, RestyleDamage};
use data::{LayoutDataAccess, LayoutDataWrapper}; use data::{LayoutDataAccess, LayoutDataWrapper};
@ -391,6 +392,7 @@ pub trait MatchMethods {
-> StyleSharingResult; -> StyleSharingResult;
unsafe fn cascade_node(&self, unsafe fn cascade_node(&self,
layout_context: &SharedLayoutContext,
parent: Option<LayoutNode>, parent: Option<LayoutNode>,
applicable_declarations: &ApplicableDeclarations, applicable_declarations: &ApplicableDeclarations,
applicable_declarations_cache: &mut ApplicableDeclarationsCache); applicable_declarations_cache: &mut ApplicableDeclarationsCache);
@ -398,6 +400,7 @@ pub trait MatchMethods {
trait PrivateMatchMethods { trait PrivateMatchMethods {
fn cascade_node_pseudo_element(&self, fn cascade_node_pseudo_element(&self,
layout_context: &SharedLayoutContext,
parent_style: Option<&Arc<ComputedValues>>, parent_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[DeclarationBlock], applicable_declarations: &[DeclarationBlock],
style: &mut Option<Arc<ComputedValues>>, style: &mut Option<Arc<ComputedValues>>,
@ -414,6 +417,7 @@ trait PrivateMatchMethods {
impl<'ln> PrivateMatchMethods for LayoutNode<'ln> { impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
fn cascade_node_pseudo_element(&self, fn cascade_node_pseudo_element(&self,
layout_context: &SharedLayoutContext,
parent_style: Option<&Arc<ComputedValues>>, parent_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[DeclarationBlock], applicable_declarations: &[DeclarationBlock],
style: &mut Option<Arc<ComputedValues>>, style: &mut Option<Arc<ComputedValues>>,
@ -430,7 +434,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
None => None, None => None,
Some(ref style) => Some(&**style), 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, shareable,
Some(&***parent_style), Some(&***parent_style),
cached_computed_values); cached_computed_values);
@ -438,7 +443,8 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
this_style = Arc::new(the_style); this_style = Arc::new(the_style);
} }
None => { None => {
let (the_style, is_cacheable) = cascade(applicable_declarations, let (the_style, is_cacheable) = cascade(layout_context.screen_size,
applicable_declarations,
shareable, shareable,
None, None,
None); None);
@ -602,6 +608,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
} }
unsafe fn cascade_node(&self, unsafe fn cascade_node(&self,
layout_context: &SharedLayoutContext,
parent: Option<LayoutNode>, parent: Option<LayoutNode>,
applicable_declarations: &ApplicableDeclarations, applicable_declarations: &ApplicableDeclarations,
applicable_declarations_cache: &mut ApplicableDeclarationsCache) { applicable_declarations_cache: &mut ApplicableDeclarationsCache) {
@ -635,26 +642,29 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
} }
_ => { _ => {
let mut damage = self.cascade_node_pseudo_element( let mut damage = self.cascade_node_pseudo_element(
layout_context,
parent_style, parent_style,
applicable_declarations.normal.as_slice(), applicable_declarations.normal.as_slice(),
&mut layout_data.shared_data.style, &mut layout_data.shared_data.style,
applicable_declarations_cache, applicable_declarations_cache,
applicable_declarations.normal_shareable); applicable_declarations.normal_shareable);
if applicable_declarations.before.len() > 0 { if applicable_declarations.before.len() > 0 {
damage = damage | self.cascade_node_pseudo_element( damage = damage | self.cascade_node_pseudo_element(
Some(layout_data.shared_data.style.as_ref().unwrap()), layout_context,
&*applicable_declarations.before, Some(layout_data.shared_data.style.as_ref().unwrap()),
&mut layout_data.data.before_style, &*applicable_declarations.before,
applicable_declarations_cache, &mut layout_data.data.before_style,
false); applicable_declarations_cache,
false);
} }
if applicable_declarations.after.len() > 0 { if applicable_declarations.after.len() > 0 {
damage = damage | self.cascade_node_pseudo_element( damage = damage | self.cascade_node_pseudo_element(
Some(layout_data.shared_data.style.as_ref().unwrap()), layout_context,
&*applicable_declarations.after, Some(layout_data.shared_data.style.as_ref().unwrap()),
&mut layout_data.data.after_style, &*applicable_declarations.after,
applicable_declarations_cache, &mut layout_data.data.after_style,
false); applicable_declarations_cache,
false);
} }
layout_data.data.restyle_damage = damage; layout_data.data.restyle_damage = damage;
} }

View file

@ -176,7 +176,8 @@ impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> {
// Perform the CSS cascade. // Perform the CSS cascade.
unsafe { unsafe {
node.cascade_node(parent_opt, node.cascade_node(self.layout_context.shared,
parent_opt,
&applicable_declarations, &applicable_declarations,
self.layout_context.applicable_declarations_cache()); self.layout_context.applicable_declarations_cache());
} }

View file

@ -119,7 +119,7 @@ impl PresentationalHintSynthesis for Stylist {
*shareable = false *shareable = false
} }
LengthOrPercentageOrAuto::Length(length) => { 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( matching_rules_list.vec_push(from_declaration(
PropertyDeclaration::Width(SpecifiedValue(width_value)))); PropertyDeclaration::Width(SpecifiedValue(width_value))));
*shareable = false *shareable = false
@ -160,9 +160,9 @@ impl PresentationalHintSynthesis for Stylist {
// FIXME(pcwalton): More use of atoms, please! // FIXME(pcwalton): More use of atoms, please!
let value = match element.get_attr(&ns!(""), &atom!("type")) { let value = match element.get_attr(&ns!(""), &atom!("type")) {
Some("text") | Some("password") => { 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( matching_rules_list.vec_push(from_declaration(
PropertyDeclaration::Width(SpecifiedValue( PropertyDeclaration::Width(SpecifiedValue(
@ -180,7 +180,7 @@ impl PresentationalHintSynthesis for Stylist {
// scrollbar size into consideration (but we don't have a scrollbar yet!) // scrollbar size into consideration (but we don't have a scrollbar yet!)
// //
// https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-width // 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( matching_rules_list.vec_push(from_declaration(
PropertyDeclaration::Width(SpecifiedValue( PropertyDeclaration::Width(SpecifiedValue(
specified::LengthOrPercentageOrAuto::Length(value))))); specified::LengthOrPercentageOrAuto::Length(value)))));
@ -193,7 +193,7 @@ impl PresentationalHintSynthesis for Stylist {
// TODO(mttr) This should take scrollbar size into consideration. // TODO(mttr) This should take scrollbar size into consideration.
// //
// https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-height // 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( matching_rules_list.vec_push(from_declaration(
PropertyDeclaration::Height(SpecifiedValue( PropertyDeclaration::Height(SpecifiedValue(
longhands::height::SpecifiedValue( longhands::height::SpecifiedValue(
@ -241,7 +241,7 @@ impl PresentationalHintSynthesis for Stylist {
match element.get_unsigned_integer_attribute(UnsignedIntegerAttribute::Border) { match element.get_unsigned_integer_attribute(UnsignedIntegerAttribute::Border) {
None => {} None => {}
Some(length) => { 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( matching_rules_list.vec_push(from_declaration(
PropertyDeclaration::BorderTopWidth(SpecifiedValue( PropertyDeclaration::BorderTopWidth(SpecifiedValue(
longhands::border_top_width::SpecifiedValue(width_value))))); longhands::border_top_width::SpecifiedValue(width_value)))));

View file

@ -5,7 +5,7 @@
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use cssparser::{Token, Parser, Delimiter}; use cssparser::{Token, Parser, Delimiter};
use geom::size::TypedSize2D; use geom::size::{Size2D, TypedSize2D};
use properties::longhands; use properties::longhands;
use util::geometry::{Au, ViewportPx}; use util::geometry::{Au, ViewportPx};
use values::specified; use values::specified;
@ -23,6 +23,31 @@ pub enum Range<T> {
//Eq(T), // FIXME: Implement parsing support for equality then re-enable this. //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> { impl<T: Ord> Range<T> {
fn evaluate(&self, value: T) -> bool { fn evaluate(&self, value: T) -> bool {
match *self { match *self {
@ -33,9 +58,9 @@ impl<T: Ord> Range<T> {
} }
} }
#[derive(PartialEq, Eq, Copy, Debug)] #[derive(PartialEq, Copy, Debug)]
pub enum Expression { pub enum Expression {
Width(Range<Au>), Width(Range<specified::Length>),
} }
#[derive(PartialEq, Eq, Copy, Debug)] #[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 { impl Expression {
fn parse(input: &mut Parser) -> Result<Expression, ()> { fn parse(input: &mut Parser) -> Result<Expression, ()> {
try!(input.expect_parenthesis_block()); try!(input.expect_parenthesis_block());
@ -111,10 +125,10 @@ impl Expression {
// TODO: Handle other media features // TODO: Handle other media features
match_ignore_ascii_case! { name, match_ignore_ascii_case! { name,
"min-width" => { "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" => { "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(()) _ => Err(())
} }
@ -186,6 +200,9 @@ pub fn parse_media_query_list(input: &mut Parser) -> MediaQueryList {
impl MediaQueryList { impl MediaQueryList {
pub fn evaluate(&self, device: &Device) -> bool { 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) // Check if any queries match (OR condition)
self.media_queries.iter().any(|mq| { self.media_queries.iter().any(|mq| {
// Check if media matches. Unknown media never matches. // Check if media matches. Unknown media never matches.
@ -198,8 +215,8 @@ impl MediaQueryList {
// Check if all conditions match (AND condition) // Check if all conditions match (AND condition)
let query_match = media_match && mq.expressions.iter().all(|expression| { let query_match = media_match && mq.expressions.iter().all(|expression| {
match expression { match expression {
&Expression::Width(value) => value.evaluate( &Expression::Width(value) =>
Au::from_frac_px(device.viewport_size.to_untyped().width as f64)), value.to_computed_range(viewport_size).evaluate(viewport_size.width),
} }
}); });
@ -220,6 +237,7 @@ mod tests {
use stylesheets::Origin; use stylesheets::Origin;
use super::*; use super::*;
use url::Url; use url::Url;
use values::specified;
use std::borrow::ToOwned; use std::borrow::ToOwned;
fn test_media_rule<F>(css: &str, callback: F) where F: Fn(&MediaQueryList, &str) { 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.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] { 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"), _ => panic!("wrong expression type"),
} }
}); });
@ -397,7 +415,7 @@ mod tests {
assert!(q.media_type == MediaQueryType::All, css.to_owned()); assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] { 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"), _ => panic!("wrong expression type"),
} }
}); });
@ -412,7 +430,7 @@ mod tests {
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned()); assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] { 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"), _ => panic!("wrong expression type"),
} }
}); });
@ -424,7 +442,7 @@ mod tests {
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned()); assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] { 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"), _ => panic!("wrong expression type"),
} }
}); });
@ -436,7 +454,7 @@ mod tests {
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned()); assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned()); assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] { 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"), _ => panic!("wrong expression type"),
} }
}); });
@ -451,11 +469,11 @@ mod tests {
assert!(q.media_type == MediaQueryType::All, css.to_owned()); assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned()); assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] { 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"), _ => panic!("wrong expression type"),
} }
match q.expressions[1] { 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"), _ => panic!("wrong expression type"),
} }
}); });
@ -467,11 +485,11 @@ mod tests {
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned()); assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned()); assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] { 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"), _ => panic!("wrong expression type"),
} }
match q.expressions[1] { 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"), _ => panic!("wrong expression type"),
} }
}); });

View file

@ -19,8 +19,9 @@ use cssparser::{Parser, Color, RGBA, AtRuleParser, DeclarationParser,
DeclarationListParser, parse_important, ToCss}; DeclarationListParser, parse_important, ToCss};
use geom::num::Zero; use geom::num::Zero;
use geom::SideOffsets2D; use geom::SideOffsets2D;
use geom::size::Size2D;
use values::specified::BorderStyle; use values::specified::{Length, BorderStyle};
use values::computed::{self, ToComputedValue}; use values::computed::{self, ToComputedValue};
use selectors::matching::DeclarationBlock; use selectors::matching::DeclarationBlock;
use parser::{ParserContext, log_css_error}; use parser::{ParserContext, log_css_error};
@ -566,7 +567,7 @@ pub mod longhands {
SpecifiedValue::Number(value) => computed_value::T::Number(value), SpecifiedValue::Number(value) => computed_value::T::Number(value),
SpecifiedValue::Percentage(value) => { SpecifiedValue::Percentage(value) => {
computed_value::T::Length( 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) input.try(specified::LengthOrPercentage::parse_non_negative)
.map(|value| match value { .map(|value| match value {
specified::LengthOrPercentage::Length(value) => 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(|()| { .or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()), match_ignore_ascii_case! { try!(input.expect_ident()),
"xx-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 5)), "xx-small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 5)),
"x-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 4)), "x-small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 4)),
"small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 8 / 9)), "small" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 8 / 9)),
"medium" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX))), "medium" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX))),
"large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 6 / 5)), "large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 6 / 5)),
"x-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 2)), "x-large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 3 / 2)),
"xx-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 2)), "xx-large" => Ok(specified::Length::Absolute(Au::from_px(MEDIUM_PX) * 2)),
// https://github.com/servo/servo/issues/3423#issuecomment-56321664 // https://github.com/servo/servo/issues/3423#issuecomment-56321664
"smaller" => Ok(specified::Length::Em(0.85)), "smaller" => Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(0.85))),
"larger" => Ok(specified::Length::Em(1.2)) "larger" => Ok(specified::Length::FontRelative(specified::FontRelativeLength::Em(1.2)))
_ => Err(()) _ => Err(())
} }
@ -2038,7 +2039,7 @@ pub mod longhands {
pub fn parse_one_box_shadow(input: &mut Parser) -> Result<SpecifiedBoxShadow, ()> { pub fn parse_one_box_shadow(input: &mut Parser) -> Result<SpecifiedBoxShadow, ()> {
use util::geometry::Au; 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 lengths_parsed = false;
let mut color = None; let mut color = None;
let mut inset = false; let mut inset = false;
@ -2208,10 +2209,10 @@ pub mod longhands {
})); }));
if sides.len() == 4 { if sides.len() == 4 {
Ok(Some(SpecifiedClipRect { Ok(Some(SpecifiedClipRect {
top: sides[0].unwrap_or(Length::Au(Au(0))), top: sides[0].unwrap_or(Length::Absolute(Au(0))),
right: sides[1], right: sides[1],
bottom: sides[2], bottom: sides[2],
left: sides[3].unwrap_or(Length::Au(Au(0))), left: sides[3].unwrap_or(Length::Absolute(Au(0))),
})) }))
} else { } else {
Err(()) Err(())
@ -2317,7 +2318,7 @@ pub mod longhands {
fn parse_one_text_shadow(input: &mut Parser) -> Result<SpecifiedTextShadow,()> { fn parse_one_text_shadow(input: &mut Parser) -> Result<SpecifiedTextShadow,()> {
use util::geometry::Au; 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 lengths_parsed = false;
let mut color = None; let mut color = None;
@ -2855,7 +2856,7 @@ pub mod shorthands {
fn parse_one_set_of_border_radii(mut input: &mut Parser) fn parse_one_set_of_border_radii(mut input: &mut Parser)
-> Result<[LengthOrPercentage; 4], ()> { -> Result<[LengthOrPercentage; 4], ()> {
let mut count = 0; 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 { while count < 4 {
if let Ok(value) = input.try(LengthOrPercentage::parse) { if let Ok(value) = input.try(LengthOrPercentage::parse) {
values[count] = value; 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 /// Performs the CSS cascade, computing new styles for an element from its parent style and
/// optionally a cached related style. The arguments are: /// 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. /// * `applicable_declarations`: The list of CSS rules that matched.
/// ///
/// * `shareable`: Whether the `ComputedValues` structure to be constructed should be considered /// * `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. /// this is ignored.
/// ///
/// Returns the computed values and a boolean indicating whether the result is cacheable. /// 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, shareable: bool,
parent_style: Option< &ComputedValues >, parent_style: Option< &ComputedValues >,
cached_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(); let inherited_font_style = inherited_style.get_font();
computed::Context { computed::Context {
is_root_element: is_root_element, is_root_element: is_root_element,
viewport_size: viewport_size,
inherited_font_weight: inherited_font_style.font_weight, inherited_font_weight: inherited_font_style.font_weight,
inherited_font_size: inherited_font_style.font_size, inherited_font_size: inherited_font_style.font_size,
inherited_height: inherited_style.get_box().height, inherited_height: inherited_style.get_box().height,
@ -3769,9 +3774,12 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclarati
PropertyDeclaration::FontSize(ref value) => { PropertyDeclaration::FontSize(ref value) => {
context.font_size = match *value { context.font_size = match *value {
DeclaredValue::SpecifiedValue(ref specified_value) => { DeclaredValue::SpecifiedValue(ref specified_value) => {
specified_value.0.to_computed_value_with_font_size( match specified_value.0 {
context.inherited_font_size, context.root_font_size 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::Initial => longhands::font_size::get_initial_value(),
DeclaredValue::Inherit => context.inherited_font_size, DeclaredValue::Inherit => context.inherited_font_size,

View file

@ -52,11 +52,14 @@ pub type CSSFloat = f64;
pub mod specified { pub mod specified {
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::cmp;
use std::f64::consts::PI; use std::f64::consts::PI;
use std::fmt; use std::fmt;
use std::fmt::{Formatter, Debug}; use std::fmt::{Formatter, Debug};
use std::num::{NumCast, ToPrimitive};
use url::Url; use url::Url;
use cssparser::{self, Token, Parser, ToCss, CssStringWriter}; use cssparser::{self, Token, Parser, ToCss, CssStringWriter};
use geom::size::Size2D;
use parser::ParserContext; use parser::ParserContext;
use text_writer::{self, TextWriter}; use text_writer::{self, TextWriter};
use util::geometry::Au; use util::geometry::Au;
@ -114,17 +117,114 @@ pub mod specified {
} }
#[derive(Clone, PartialEq, Copy)] #[derive(Clone, PartialEq, Copy)]
pub enum Length { pub enum FontRelativeLength {
Au(Au), // application units
Em(CSSFloat), Em(CSSFloat),
Ex(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. /// HTML5 "character width", as defined in HTML5 § 14.5.4.
/// ///
/// This cannot be specified by the user directly and is only generated by /// This cannot be specified by the user directly and is only generated by
/// `Stylist::synthesize_rules_for_legacy_attributes()`. /// `Stylist::synthesize_rules_for_legacy_attributes()`.
ServoCharacterWidth(i32), ServoCharacterWidth(CharacterWidth),
} }
impl fmt::Debug for Length { impl fmt::Debug for Length {
@ -134,41 +234,15 @@ pub mod specified {
impl ToCss for Length { impl ToCss for Length {
fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { fn to_css<W>(&self, dest: &mut W) -> text_writer::Result where W: TextWriter {
match self { match self {
&Length::Au(length) => write!(dest, "{}px", length.to_subpx()), &Length::Absolute(length) => write!(dest, "{}px", length.to_subpx()),
&Length::Em(length) => write!(dest, "{}em", length), &Length::FontRelative(length) => length.to_css(dest),
&Length::Ex(length) => write!(dest, "{}ex", length), &Length::ViewportPercentage(length) => length.to_css(dest),
&Length::Rem(length) => write!(dest, "{}rem", length),
&Length::ServoCharacterWidth(_) &Length::ServoCharacterWidth(_)
=> panic!("internal CSS values should never be serialized"), => 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_PX: CSSFloat = 60.;
const AU_PER_IN: CSSFloat = AU_PER_PX * 96.; const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54; 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. => { Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => {
Length::parse_dimension(value.value, unit) 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(()) _ => Err(())
} }
} }
@ -196,20 +270,26 @@ pub mod specified {
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> { pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> {
match_ignore_ascii_case! { unit, match_ignore_ascii_case! { unit,
"px" => Ok(Length::from_px(value)), "px" => Ok(Length::from_px(value)),
"in" => Ok(Length::Au(Au((value * AU_PER_IN) as i32))), "in" => Ok(Length::Absolute(Au((value * AU_PER_IN) as i32))),
"cm" => Ok(Length::Au(Au((value * AU_PER_CM) as i32))), "cm" => Ok(Length::Absolute(Au((value * AU_PER_CM) as i32))),
"mm" => Ok(Length::Au(Au((value * AU_PER_MM) as i32))), "mm" => Ok(Length::Absolute(Au((value * AU_PER_MM) as i32))),
"pt" => Ok(Length::Au(Au((value * AU_PER_PT) as i32))), "pt" => Ok(Length::Absolute(Au((value * AU_PER_PT) as i32))),
"pc" => Ok(Length::Au(Au((value * AU_PER_PC) as i32))), "pc" => Ok(Length::Absolute(Au((value * AU_PER_PC) as i32))),
"em" => Ok(Length::Em(value)), // font-relative
"ex" => Ok(Length::Ex(value)), "em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))),
"rem" => Ok(Length::Rem(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(()) _ => Err(())
} }
} }
#[inline] #[inline]
pub fn from_px(px_value: CSSFloat) -> Length { 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)) Ok(LengthOrPercentage::Percentage(value.unit_value))
} }
Token::Number(ref value) if value.value == 0. => { Token::Number(ref value) if value.value == 0. => {
Ok(LengthOrPercentage::Length(Length::Au(Au(0)))) Ok(LengthOrPercentage::Length(Length::Absolute(Au(0))))
} }
_ => Err(()) _ => Err(())
} }
@ -294,7 +374,7 @@ pub mod specified {
Ok(LengthOrPercentageOrAuto::Percentage(value.unit_value)) Ok(LengthOrPercentageOrAuto::Percentage(value.unit_value))
} }
Token::Number(ref value) if value.value == 0. => { 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") => { Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => {
Ok(LengthOrPercentageOrAuto::Auto) Ok(LengthOrPercentageOrAuto::Auto)
@ -345,7 +425,7 @@ pub mod specified {
Ok(LengthOrPercentageOrNone::Percentage(value.unit_value)) Ok(LengthOrPercentageOrNone::Percentage(value.unit_value))
} }
Token::Number(ref value) if value.value == 0. => { 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") => { Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => {
Ok(LengthOrPercentageOrNone::None) Ok(LengthOrPercentageOrNone::None)
@ -386,7 +466,7 @@ pub mod specified {
Ok(PositionComponent::Percentage(value.unit_value)) Ok(PositionComponent::Percentage(value.unit_value))
} }
Token::Number(ref value) if value.value == 0. => { 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) => { Token::Ident(value) => {
match_ignore_ascii_case! { value, match_ignore_ascii_case! { value,
@ -673,6 +753,7 @@ pub mod computed {
use super::specified::{AngleOrCorner}; use super::specified::{AngleOrCorner};
use super::{specified, CSSFloat}; use super::{specified, CSSFloat};
pub use cssparser::Color as CSSColor; pub use cssparser::Color as CSSColor;
use geom::size::Size2D;
use properties::longhands; use properties::longhands;
use std::fmt; use std::fmt;
use url::Url; use url::Url;
@ -699,6 +780,7 @@ pub mod computed {
pub border_bottom_present: bool, pub border_bottom_present: bool,
pub border_left_present: bool, pub border_left_present: bool,
pub is_root_element: bool, pub is_root_element: bool,
pub viewport_size: Size2D<Au>
// TODO, as needed: viewport size, etc. // TODO, as needed: viewport size, etc.
} }
@ -736,7 +818,15 @@ pub mod computed {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> Au { 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)
}
} }
} }

View file

@ -136,6 +136,7 @@ struct Reftest {
is_flaky: bool, is_flaky: bool,
experimental: bool, experimental: bool,
fragment_identifier: Option<String>, fragment_identifier: Option<String>,
resolution: Option<String>,
} }
struct TestLine<'a> { 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 flakiness = RenderMode::empty();
let mut experimental = false; let mut experimental = false;
let mut fragment_identifier = None; let mut fragment_identifier = None;
let mut resolution = None;
for condition in conditions_list { for condition in conditions_list {
match condition { match condition {
"flaky_cpu" => flakiness.insert(CPU_RENDERING), "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=") { if condition.starts_with("fragment=") {
fragment_identifier = Some(condition.slice_from("fragment=".len()).to_string()); 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 { 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), is_flaky: render_mode.intersects(flakiness),
experimental: experimental, experimental: experimental,
fragment_identifier: fragment_identifier, fragment_identifier: fragment_identifier,
resolution: resolution,
}; };
tests.push(make_test(reftest)); tests.push(make_test(reftest));
@ -265,6 +271,10 @@ fn capture(reftest: &Reftest, side: usize) -> (u32, u32, Vec<u8>) {
if reftest.experimental { if reftest.experimental {
command.arg("--experimental"); command.arg("--experimental");
} }
if let Some(ref resolution) = reftest.resolution {
command.arg("--resolution");
command.arg(resolution);
}
let retval = match command.status() { let retval = match command.status() {
Ok(status) => status, Ok(status) => status,
Err(e) => panic!("failed to execute process: {}", e), Err(e) => panic!("failed to execute process: {}", e),