Add computed values.

This commit is contained in:
Simon Sapin 2013-08-29 17:06:33 +01:00
parent 254c522ab4
commit e2ec549da5
2 changed files with 195 additions and 84 deletions

View file

@ -2,7 +2,6 @@
* 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/. */
pub type Float = f64;
pub type Integer = i64;
@ -11,6 +10,7 @@ pub mod specified {
use std::ascii::StrAsciiExt;
use cssparser::*;
use super::{Integer, Float};
pub use CSSColor = cssparser::Color;
pub enum Length {
Au(Integer), // application units
@ -66,51 +66,54 @@ pub mod specified {
}
pub enum LengthOrPercentage {
Length(Length),
Percentage(Float),
LP_Length(Length),
LP_Percentage(Float),
}
impl LengthOrPercentage {
fn parse_internal(input: &ComponentValue, negative_ok: bool)
-> Option<LengthOrPercentage> {
match input {
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0.
=> Length::parse_dimension(value.value, unit.as_slice()).map_move(Length),
=> Length::parse_dimension(value.value, unit.as_slice()).map_move(LP_Length),
&ast::Percentage(ref value) if negative_ok || value.value >= 0.
=> Some(Percentage(value.value)),
&Number(ref value) if value.value == 0. => Some(Length(Au(0))),
=> Some(LP_Percentage(value.value)),
&Number(ref value) if value.value == 0. => Some(LP_Length(Au(0))),
_ => None
}
}
#[inline]
pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentage> {
LengthOrPercentage::parse_internal(input, /* negative_ok = */ true)
}
#[inline]
pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentage> {
LengthOrPercentage::parse_internal(input, /* negative_ok = */ false)
}
}
pub enum LengthOrPercentageOrAuto {
Length_(Length),
Percentage_(Float),
Auto,
LPA_Length(Length),
LPA_Percentage(Float),
LPA_Auto,
}
impl LengthOrPercentageOrAuto {
#[inline]
fn parse_internal(input: &ComponentValue, negative_ok: bool)
-> Option<LengthOrPercentageOrAuto> {
match input {
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0.
=> Length::parse_dimension(value.value, unit.as_slice()).map_move(Length_),
=> Length::parse_dimension(value.value, unit.as_slice()).map_move(LPA_Length),
&ast::Percentage(ref value) if negative_ok || value.value >= 0.
=> Some(Percentage_(value.value)),
&Number(ref value) if value.value == 0. => Some(Length_(Au(0))),
&Ident(ref value) if value.eq_ignore_ascii_case("auto") => Some(Auto),
=> Some(LPA_Percentage(value.value)),
&Number(ref value) if value.value == 0. => Some(LPA_Length(Au(0))),
&Ident(ref value) if value.eq_ignore_ascii_case("auto") => Some(LPA_Auto),
_ => None
}
}
#[inline]
pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> {
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ true)
}
#[inline]
pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> {
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ false)
}
@ -118,22 +121,72 @@ pub mod specified {
}
pub mod computed {
use cssparser;
use super::*;
struct Length(Integer); // in application units
use super::super::longhands::font_weight;
pub struct Context {
current_color: cssparser::RGBA,
has_border_top: bool,
has_border_right: bool,
has_border_bottom: bool,
has_border_left: bool,
font_size: Length,
font_weight: font_weight::ComputedValue,
// TODO, as needed: root font size, viewport size, etc.
}
pub struct Length(Integer); // in application units
impl Length {
fn times(self, factor: Float) -> Length {
pub fn times(self, factor: Float) -> Length {
Length(((*self as Float) * factor) as Integer)
}
}
pub fn compute(parent_font_size: Length, value: specified::Length) -> Length {
pub fn compute_Length(value: specified::Length, context: &Context) -> Length {
match value {
specified::Au(value) => Length(value),
specified::Em(value) => parent_font_size.times(value),
specified::Em(value) => context.font_size.times(value),
specified::Ex(value) => {
let x_height = 0.5; // TODO: find that form the font
parent_font_size.times(value * x_height)
let x_height = 0.5; // TODO: find that from the font
context.font_size.times(value * x_height)
},
}
}
pub enum LengthOrPercentage {
LP_Length(Length),
LP_Percentage(Float),
}
pub fn compute_LengthOrPercentage(value: specified::LengthOrPercentage, context: &Context)
-> LengthOrPercentage {
match value {
specified::LP_Length(value) => LP_Length(compute_Length(value, context)),
specified::LP_Percentage(value) => LP_Percentage(value),
}
}
pub enum LengthOrPercentageOrAuto {
LPA_Length(Length),
LPA_Percentage(Float),
LPA_Auto,
}
pub fn compute_LengthOrPercentageOrAuto(value: specified::LengthOrPercentageOrAuto,
context: &Context) -> LengthOrPercentageOrAuto {
match value {
specified::LPA_Length(value) => LPA_Length(compute_Length(value, context)),
specified::LPA_Percentage(value) => LPA_Percentage(value),
specified::LPA_Auto => LPA_Auto,
}
}
pub struct CSSColor {
rgba: cssparser::RGBA,
is_current_color: bool, // For inheritance
}
pub fn compute_CSSColor(color: specified::CSSColor, context: &Context) -> CSSColor {
match color {
cssparser::RGBA(rgba) => CSSColor { rgba: rgba, is_current_color: false },
cssparser::CurrentColor => CSSColor { rgba: context.current_color,
is_current_color: true },
}
}
}

View file

@ -8,7 +8,6 @@ use std::ascii::StrAsciiExt;
use errors::{ErrorLoggerIterator, log_css_error};
pub use std::iterator;
pub use cssparser::*;
pub use CSSColor = cssparser::Color;
pub use parsing_utils::*;
pub use self::common_types::*;
@ -43,8 +42,9 @@ INHERITED = set()
pub mod longhands {
pub use super::*;
pub use std;
<%def name="longhand(name, inherited=False)">
<%def name="longhand(name, inherited=False, no_super=False)">
<%
property = Longhand(name)
LONGHANDS.append(property)
@ -52,7 +52,9 @@ pub mod longhands {
INHERITED.add(name)
%>
pub mod ${property.ident} {
% if not no_super:
use super::*;
% endif
${caller.body()}
}
</%def>
@ -68,11 +70,14 @@ pub mod longhands {
<%def name="single_keyword(name, values, inherited=False)">
<%self:single_component_value name="${name}" inherited="${inherited}">
// The computed value is the same as the specified value.
pub use to_computed_value = std::util::id;
pub enum SpecifiedValue {
% for value in values.split():
${to_rust_ident(value)},
% endfor
}
pub type ComputedValue = SpecifiedValue;
pub fn from_component_value(v: &ComponentValue) -> Option<SpecifiedValue> {
do get_ident_lower(v).chain |keyword| {
match keyword.as_slice() {
@ -86,30 +91,26 @@ pub mod longhands {
</%self:single_component_value>
</%def>
<%def name="predefined_function(name, result_type, function, inherited=False)">
<%def name="predefined_type(name, type, parse_method='parse', inherited=False)">
<%self:longhand name="${name}" inherited="${inherited}">
pub type SpecifiedValue = ${result_type};
pub use to_computed_value = super::super::common_types::computed::compute_${type};
pub type SpecifiedValue = specified::${type};
pub type ComputedValue = computed::${type};
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
one_component_value(input).chain(${function})
one_component_value(input).chain(specified::${type}::${parse_method})
}
</%self:longhand>
</%def>
<%def name="predefined_type(name, type, inherited=False)">
${predefined_function(name, type, type + "::parse", inherited)}
</%def>
// CSS 2.1, Section 8 - Box model
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("margin-" + side, "specified::LengthOrPercentageOrAuto")}
${predefined_type("margin-" + side, "LengthOrPercentageOrAuto")}
% endfor
% for side in ["top", "right", "bottom", "left"]:
${predefined_function("padding-" + side,
"specified::LengthOrPercentage",
"specified::LengthOrPercentage::parse_non_negative")}
${predefined_type("padding-" + side, "LengthOrPercentage", "parse_non_negative")}
% endfor
% for side in ["top", "right", "bottom", "left"]:
@ -119,9 +120,11 @@ pub mod longhands {
// dotted dashed double groove ridge insed outset hidden
${single_keyword("border-top-style", "none solid")}
% for side in ["right", "bottom", "left"]:
${predefined_function("border-%s-style" % side,
"border_top_style::SpecifiedValue",
"border_top_style::from_component_value")}
<%self:longhand name="border-${side}-style", no_super="True">
pub use super::border_top_style::*;
pub type SpecifiedValue = super::border_top_style::SpecifiedValue;
pub type ComputedValue = super::border_top_style::ComputedValue;
</%self:longhand>
% endfor
pub fn parse_border_width(component_value: &ComponentValue) -> Option<specified::Length> {
@ -136,7 +139,18 @@ pub mod longhands {
}
}
% for side in ["top", "right", "bottom", "left"]:
${predefined_function("border-%s-width" % side, "specified::Length", "parse_border_width")}
<%self:longhand name="border-${side}-width">
pub type SpecifiedValue = specified::Length;
pub type ComputedValue = computed::Length;
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
one_component_value(input).chain(parse_border_width)
}
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> ComputedValue {
if context.has_border_${side} { computed::compute_Length(value, context) }
else { computed::Length(0) }
}
</%self:longhand>
% endfor
// CSS 2.1, Section 9 - Visual formatting model
@ -154,35 +168,44 @@ pub mod longhands {
// CSS 2.1, Section 10 - Visual formatting model details
${predefined_function("width",
"specified::LengthOrPercentageOrAuto",
"specified::LengthOrPercentageOrAuto::parse_non_negative")}
${predefined_function("height",
"specified::LengthOrPercentageOrAuto",
"specified::LengthOrPercentageOrAuto::parse_non_negative")}
${predefined_type("width", "LengthOrPercentageOrAuto", "parse_non_negative")}
${predefined_type("height", "LengthOrPercentageOrAuto", "parse_non_negative")}
<%self:single_component_value name="line-height">
pub enum SpecifiedValue {
Normal,
Length(specified::Length),
Percentage(Float),
Number(Float),
SpecifiedNormal,
SpecifiedLength(specified::Length),
SpecifiedNumber(Float),
// percentage are the same as em.
}
/// normal | <number> | <length> | <percentage>
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
match input {
&ast::Number(ref value) if value.value >= 0.
=> Some(Number(value.value)),
=> Some(SpecifiedNumber(value.value)),
&ast::Percentage(ref value) if value.value >= 0.
=> Some(Percentage(value.value)),
=> Some(SpecifiedLength(specified::Em(value.value))),
&Dimension(ref value, ref unit) if value.value >= 0.
=> specified::Length::parse_dimension(value.value, unit.as_slice())
.map_move(Length),
&Ident(ref value) if value.eq_ignore_ascii_case("auto")
=> Some(Normal),
.map_move(SpecifiedLength),
&Ident(ref value) if value.eq_ignore_ascii_case("normal")
=> Some(SpecifiedNormal),
_ => None,
}
}
pub enum ComputedValue {
Normal,
Length(computed::Length),
Number(Float),
}
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> ComputedValue {
match value {
SpecifiedNormal => Normal,
SpecifiedLength(value) => Length(computed::compute_Length(value, context)),
SpecifiedNumber(value) => Number(value),
}
}
</%self:single_component_value>
// CSS 2.1, Section 11 - Visual effects
@ -199,6 +222,7 @@ pub mod longhands {
// CSS 2.1, Section 15 - Fonts
<%self:longhand name="font-family" inherited="True">
pub use to_computed_value = std::util::id;
enum FontFamily {
FamilyName(~str),
// Generic
@ -209,6 +233,7 @@ pub mod longhands {
// Monospace,
}
pub type SpecifiedValue = ~[FontFamily];
pub type ComputedValue = SpecifiedValue;
/// <familiy-name>#
/// <familiy-name> = <string> | [ <ident>+ ]
/// TODO: <generic-familiy>
@ -275,52 +300,83 @@ pub mod longhands {
pub enum SpecifiedValue {
Bolder,
Lighther,
Weight100,
Weight200,
Weight300,
Weight400,
Weight500,
Weight600,
Weight700,
Weight800,
Weight900,
% for weight in range(100, 901, 100):
SpecifiedWeight${weight},
% endfor
}
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
match input {
&Ident(ref value) => match value.to_ascii_lower().as_slice() {
"bold" => Some(Weight700),
"normal" => Some(Weight400),
"bold" => Some(SpecifiedWeight700),
"normal" => Some(SpecifiedWeight400),
"bolder" => Some(Bolder),
"lighter" => Some(Lighther),
_ => None,
},
&Number(ref value) => match value.int_value {
Some(100) => Some(Weight100),
Some(200) => Some(Weight200),
Some(300) => Some(Weight300),
Some(400) => Some(Weight400),
Some(500) => Some(Weight500),
Some(600) => Some(Weight600),
Some(700) => Some(Weight700),
Some(800) => Some(Weight800),
Some(900) => Some(Weight900),
Some(100) => Some(SpecifiedWeight100),
Some(200) => Some(SpecifiedWeight200),
Some(300) => Some(SpecifiedWeight300),
Some(400) => Some(SpecifiedWeight400),
Some(500) => Some(SpecifiedWeight500),
Some(600) => Some(SpecifiedWeight600),
Some(700) => Some(SpecifiedWeight700),
Some(800) => Some(SpecifiedWeight800),
Some(900) => Some(SpecifiedWeight900),
_ => None,
},
_ => None
}
}
pub enum ComputedValue {
% for weight in range(100, 901, 100):
Weight${weight},
% endfor
}
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> ComputedValue {
match value {
% for weight in range(100, 901, 100):
SpecifiedWeight${weight} => Weight${weight},
% endfor
Bolder => match context.font_weight {
Weight100 => Weight400,
Weight200 => Weight400,
Weight300 => Weight400,
Weight400 => Weight700,
Weight500 => Weight700,
Weight600 => Weight900,
Weight700 => Weight900,
Weight800 => Weight900,
Weight900 => Weight900,
},
Lighther => match context.font_weight {
Weight100 => Weight100,
Weight200 => Weight100,
Weight300 => Weight100,
Weight400 => Weight100,
Weight500 => Weight100,
Weight600 => Weight400,
Weight700 => Weight400,
Weight800 => Weight700,
Weight900 => Weight700,
},
}
}
</%self:single_component_value>
<%self:single_component_value name="font-size" inherited="True">
pub use to_computed_value = super::super::common_types::computed::compute_Length;
pub type SpecifiedValue = specified::Length; // Percentages are the same as em.
pub type ComputedValue = computed::Length;
/// <length> | <percentage>
/// TODO: support <absolute-size> and <relative-size>
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
do specified::LengthOrPercentage::parse_non_negative(input).map_move |value| {
match value {
specified::Length(value) => value,
specified::Percentage(value) => specified::Em(value),
specified::LP_Length(value) => value,
specified::LP_Percentage(value) => specified::Em(value),
}
}
}
@ -331,6 +387,7 @@ pub mod longhands {
${single_keyword("text-align", "left right center justify", inherited=True)}
<%self:longhand name="text-decoration">
pub use to_computed_value = std::util::id;
pub struct SpecifiedValue {
underline: bool,
overline: bool,
@ -338,6 +395,7 @@ pub mod longhands {
// 'blink' is accepted in the parser but ignored.
// Just not blinking the text is a conforming implementation per CSS 2.1.
}
pub type ComputedValue = SpecifiedValue;
/// none | [ underline || overline || line-through || blink ]
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
let mut result = SpecifiedValue {
@ -424,18 +482,18 @@ pub mod shorthands {
// TODO: other background-* properties
<%self:shorthand name="background" sub_properties="background-color">
do one_component_value(input).chain(CSSColor::parse).map_move |color| {
do one_component_value(input).chain(specified::CSSColor::parse).map_move |color| {
Longhands { background_color: Some(color) }
}
</%self:shorthand>
${four_sides_shorthand("border-color", "border-%s-color", "CSSColor::parse")}
${four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse")}
${four_sides_shorthand("border-style", "border-%s-style",
"border_top_style::from_component_value")}
${four_sides_shorthand("border-width", "border-%s-width", "parse_border_width")}
pub fn parse_border(input: &[ComponentValue])
-> Option<(Option<CSSColor>,
-> Option<(Option<specified::CSSColor>,
Option<border_top_style::SpecifiedValue>,
Option<specified::Length>)> {
let mut color = None;
@ -444,7 +502,7 @@ pub mod shorthands {
let mut any = false;
for component_value in input.skip_whitespace() {
if color.is_none() {
match CSSColor::parse(component_value) {
match specified::CSSColor::parse(component_value) {
Some(c) => { color = Some(c); any = true; loop },
None => ()
}