Parse all longhand properties that Servo seems to support.

This commit is contained in:
Simon Sapin 2013-08-13 20:17:08 +01:00
parent c1b7e157b7
commit 9ea09f9d0d
5 changed files with 513 additions and 0 deletions

21
parsing_utils.rs Normal file
View file

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::ascii::to_ascii_lower;
use cssparser::*;
pub fn one_component_value<'a>(input: &'a [ComponentValue]) -> Option<&'a ComponentValue> {
let mut iter = input.skip_whitespace();
iter.next().filtered(|_| iter.next().is_none())
}
pub fn get_ident_lower(component_value: &ComponentValue) -> Option<~str> {
match component_value {
&Ident(ref value) => Some(to_ascii_lower(value.as_slice())),
_ => None,
}
}

138
properties/common_types.rs Normal file
View file

@ -0,0 +1,138 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub type Float = f64;
pub type Integer = i64;
pub mod specified {
use std::ascii::{to_ascii_lower, eq_ignore_ascii_case};
use cssparser::*;
use super::{Integer, Float};
pub enum Length {
Au(Integer), // application units
Em(Float),
Ex(Float),
// Ch(Float),
// Rem(Float),
// Vw(Float),
// Vh(Float),
// Vmin(Float),
// Vmax(Float),
}
static AU_PER_PX: Float = 60.;
static AU_PER_IN: Float = AU_PER_PX * 96.;
static AU_PER_CM: Float = AU_PER_IN / 2.54;
static AU_PER_MM: Float = AU_PER_IN / 25.4;
static AU_PER_PT: Float = AU_PER_IN / 72.;
static AU_PER_PC: Float = AU_PER_PT * 12.;
impl Length {
#[inline]
fn parse_internal(input: &ComponentValue, negative_ok: bool) -> Option<Length> {
match input {
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0.
=> Length::parse_dimension(value.value, unit.as_slice()),
&Number(ref value) if value.value == 0. => Some(Au(0)),
_ => None
}
}
pub fn parse(input: &ComponentValue) -> Option<Length> {
Length::parse_internal(input, /* negative_ok = */ true)
}
pub fn parse_non_negative(input: &ComponentValue) -> Option<Length> {
Length::parse_internal(input, /* negative_ok = */ false)
}
pub fn parse_dimension(value: Float, unit: &str) -> Option<Length> {
match to_ascii_lower(unit).as_slice() {
"px" => Some(Length::from_px(value)),
"in" => Some(Au((value * AU_PER_IN) as Integer)),
"cm" => Some(Au((value * AU_PER_CM) as Integer)),
"mm" => Some(Au((value * AU_PER_MM) as Integer)),
"pt" => Some(Au((value * AU_PER_PT) as Integer)),
"pc" => Some(Au((value * AU_PER_PC) as Integer)),
"em" => Some(Em(value)),
"ex" => Some(Ex(value)),
_ => None
}
}
#[inline]
pub fn from_px(px_value: Float) -> Length {
Au((px_value * AU_PER_PX) as Integer)
}
}
pub enum LengthOrPercentage {
Length(Length),
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),
&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))),
_ => None
}
}
pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentage> {
LengthOrPercentage::parse_internal(input, /* negative_ok = */ true)
}
pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentage> {
LengthOrPercentage::parse_internal(input, /* negative_ok = */ false)
}
}
pub enum LengthOrPercentageOrAuto {
Length_(Length),
Percentage_(Float),
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_),
&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 eq_ignore_ascii_case(value.as_slice(), "auto") => Some(Auto),
_ => None
}
}
pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> {
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ true)
}
pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> {
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ false)
}
}
}
pub mod computed {
use super::*;
struct Length(Integer); // in application units
impl Length {
fn times(self, factor: Float) -> Length {
Length(((*self as Float) * factor) as Integer)
}
pub fn compute(parent_font_size: Length, value: specified::Length) -> Length {
match value {
specified::Au(value) => Length(value),
specified::Em(value) => parent_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)
},
}
}
}
}

346
properties/longhands.rs Normal file
View file

@ -0,0 +1,346 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub use std::ascii::{to_ascii_lower, eq_ignore_ascii_case};
pub use std::option;
pub use cssparser::*;
pub use CSSColor = cssparser::Color;
pub use parsing_utils::*;
pub use super::common_types::specified;
pub use super::common_types;
macro_rules! single_keyword(
($property_name: ident, $( $lower_case_keyword_string: pat => $variant: ident ),+ ) => {
mod $property_name {
use super::*;
enum SpecifiedValue {
$( $variant ),+
}
fn parse(input: &[ComponentValue]) -> option::Option<SpecifiedValue> {
do one_component_value(input).chain(get_ident_lower).chain |keyword| {
match keyword.as_slice() {
$( $lower_case_keyword_string => option::Some($variant) ),+ ,
_ => option::None,
}
}
}
}
};
)
macro_rules! single_type(
($property_name: ident, $type_: ident) => {
single_type!($property_name, $type_, $type_::parse)
};
($property_name: ident, $type_: ty, $parse_function: expr) => {
mod $property_name {
use super::*;
type SpecifiedValue = $type_;
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
one_component_value(input).chain($parse_function)
}
}
};
)
// CSS 2.1, Section 8 - Box model
single_type!(margin_top, specified::LengthOrPercentageOrAuto,
specified::LengthOrPercentageOrAuto::parse_non_negative)
single_type!(margin_right, specified::LengthOrPercentageOrAuto,
specified::LengthOrPercentageOrAuto::parse_non_negative)
single_type!(margin_bottom, specified::LengthOrPercentageOrAuto,
specified::LengthOrPercentageOrAuto::parse_non_negative)
single_type!(margin_left, specified::LengthOrPercentageOrAuto,
specified::LengthOrPercentageOrAuto::parse_non_negative)
single_type!(padding_top, specified::LengthOrPercentage,
specified::LengthOrPercentage::parse_non_negative)
single_type!(padding_right, specified::LengthOrPercentage,
specified::LengthOrPercentage::parse_non_negative)
single_type!(padding_bottom, specified::LengthOrPercentage,
specified::LengthOrPercentage::parse_non_negative)
single_type!(padding_left, specified::LengthOrPercentage,
specified::LengthOrPercentage::parse_non_negative)
single_type!(border_top_color, CSSColor)
single_type!(border_right_color, CSSColor)
single_type!(border_bottom_color, CSSColor)
single_type!(border_left_color, CSSColor)
pub fn parse_border_width(component_value: &ComponentValue) -> Option<specified::Length> {
match component_value {
&Ident(ref value) => match to_ascii_lower(value.as_slice()).as_slice() {
"thin" => Some(specified::Length::from_px(1.)),
"medium" => Some(specified::Length::from_px(3.)),
"thick" => Some(specified::Length::from_px(5.)),
_ => None
},
_ => specified::Length::parse_non_negative(component_value)
}
}
single_type!(border_top_width, specified::Length, parse_border_width)
single_type!(border_right_width, specified::Length, parse_border_width)
single_type!(border_bottom_width, specified::Length, parse_border_width)
single_type!(border_left_width, specified::Length, parse_border_width)
// CSS 2.1, Section 9 - Visual formatting model
// TODO: dont parse values we dont support
single_keyword!(display,
"inline" => Inline,
"block" => Block,
"list-item" => ListItem,
"inline-block" => InlineBlock,
"table" => Table,
"inline-table" => InlineTable,
"table-row-group" => TableRowGroup,
"table-header-group" => TableHeaderGroup,
"table-footer-group" => TableFooterGroup,
"table-row" => TableRow,
"table-column-group" => TableColumnGroup,
"table-column" => TableColumn,
"table-cell" => TableCell,
"table-caption" => TableCaption,
"none" => None
)
single_keyword!(position,
"static" => Static, "absolute" => Absolute, "relative" => Relative, "fixed" => Fixed)
single_keyword!(float, "left" => Left, "right" => Right, "none" => None)
single_keyword!(clear, "left" => Left, "right" => Right, "none" => None, "both" => Both)
// CSS 2.1, Section 10 - Visual formatting model details
single_type!(width, specified::Length, specified::Length::parse_non_negative)
single_type!(height, specified::Length, specified::Length::parse_non_negative)
mod line_height {
use super::*;
enum SpecifiedValue {
Normal,
Length(specified::Length),
Percentage(common_types::Float),
Number(common_types::Float),
}
/// normal | <number> | <length> | <percentage>
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
match one_component_value(input) {
Some(&ast::Number(ref value)) if value.value >= 0.
=> Some(Number(value.value)),
Some(&ast::Percentage(ref value)) if value.value >= 0.
=> Some(Percentage(value.value)),
Some(&Dimension(ref value, ref unit)) if value.value >= 0.
=> specified::Length::parse_dimension(value.value, unit.as_slice()).map_move(Length),
Some(&Ident(ref value)) if eq_ignore_ascii_case(value.as_slice(), "auto")
=> Some(Normal),
_ => None,
}
}
}
// CSS 2.1, Section 11 - Visual effects
// CSS 2.1, Section 12 - Generated content, automatic numbering, and lists
// CSS 2.1, Section 13 - Paged media
// CSS 2.1, Section 14 - Colors and Backgrounds
single_type!(background_color, CSSColor)
single_type!(color, CSSColor)
// CSS 2.1, Section 15 - Fonts
mod font_family {
use super::*;
enum FontFamily {
FamilyName(~str),
// Generic
Serif,
SansSerif,
Cursive,
Fantasy,
Monospace,
}
type SpecifiedValue = ~[FontFamily];
/// <familiy-name>#
/// <familiy-name> = <string> | [ <ident>+ ]
/// TODO: <generic-familiy>
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
let mut result = ~[];
let mut iter = input.skip_whitespace();
macro_rules! add(
($value: expr) => {
{
result.push($value);
match iter.next() {
Some(&Comma) => (),
None => break 'outer,
_ => return None,
}
}
}
)
'outer: loop {
match iter.next() {
// TODO: avoid copying strings?
Some(&String(ref value)) => add!(FamilyName(value.to_owned())),
Some(&Ident(ref value)) => {
let value = value.as_slice();
match to_ascii_lower(value).as_slice() {
"serif" => add!(Serif),
"sans-serif" => add!(SansSerif),
"cursive" => add!(Cursive),
"fantasy" => add!(Fantasy),
"monospace" => add!(Monospace),
_ => {
let mut idents = ~[value];
loop {
match iter.next() {
Some(&Ident(ref value)) => idents.push(value.as_slice()),
Some(&Comma) => {
result.push(FamilyName(idents.connect(" ")));
break
},
None => {
result.push(FamilyName(idents.connect(" ")));
break 'outer
},
_ => return None,
}
}
}
}
}
_ => return None,
}
}
Some(result)
}
}
single_keyword!(font_style, "normal" => Normal, "italic" => Italic, "oblique" => Oblique)
single_keyword!(font_variant, "normal" => Normal, "small-caps" => SmallCaps)
mod font_weight {
use super::*;
enum SpecifiedValue {
Bolder,
Lighther,
Weight100,
Weight200,
Weight300,
Weight400,
Weight500,
Weight600,
Weight700,
Weight800,
Weight900,
}
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
match one_component_value(input) {
Some(&Ident(ref value)) => match to_ascii_lower(value.as_slice()).as_slice() {
"bold" => Some(Weight700),
"normal" => Some(Weight400),
"bolder" => Some(Bolder),
"lighter" => Some(Lighther),
_ => None,
},
Some(&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(Weight800),
_ => None,
},
_ => None
}
}
}
mod font_size {
use super::*;
type SpecifiedValue = specified::Length; // Percentages are the same as em.
/// <length> | <percentage>
/// TODO: support <absolute-size> and <relative-size>
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
do one_component_value(input).chain(specified::LengthOrPercentage::parse_non_negative)
.map_move |value| {
match value {
specified::Length(value) => value,
specified::Percentage(value) => specified::Em(value),
}
}
}
}
// CSS 2.1, Section 16 - Text
single_keyword!(text_align, "left" => Left, "right" => Right,
"center" => Center, "justify" => Justify)
mod text_decoration {
use super::*;
struct SpecifiedValue {
underline: bool,
overline: bool,
line_through: bool,
// 'blink' is accepted in the parser but ignored.
// Just not blinking the text is a conforming implementation per CSS 2.1.
}
/// none | [ underline || overline || line-through || blink ]
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
let mut result = SpecifiedValue {
underline: false, overline: false, line_through: false,
};
let mut blink = false;
let mut empty = true;
macro_rules! found(
($flag: expr) => (
{
if $flag { return None }
empty = false;
$flag = true;
}
);
)
for component_value in input.skip_whitespace() {
match get_ident_lower(component_value) {
None => return None,
Some(keyword) => match keyword.as_slice() {
"underline" => if result.underline { return None }
else { empty = false; result.underline = true },
"overline" => if result.overline { return None }
else { empty = false; result.overline = true },
"line-through" => if result.line_through { return None }
else { empty = false; result.line_through = true },
"blink" => if blink { return None }
else { empty = false; blink = true },
"none" => return if empty { Some(result) } else { None },
_ => return None,
}
}
}
if !empty { Some(result) } else { None }
}
}
// CSS 2.1, Section 17 - Tables
// CSS 2.1, Section 18 - User interface

View file

@ -6,6 +6,9 @@ use std::ascii::to_ascii_lower;
use cssparser::*;
use errors::{ErrorLoggerIterator, log_css_error};
pub mod longhands;
pub mod common_types;
pub struct PropertyDeclarationBlock {
important: ~[PropertyDeclaration],
@ -36,5 +39,9 @@ pub fn parse_property_declaration_list(input: ~[Node]) -> PropertyDeclarationBlo
fn parse_one_property_declaration(name: &str, value: ~[ComponentValue],
result_list: &mut ~[PropertyDeclaration]) -> bool {
let _ = name;
let _ = value;
let _ = result_list;
false
}

View file

@ -14,3 +14,4 @@ pub mod selectors;
pub mod properties;
pub mod namespaces;
pub mod media_queries;
pub mod parsing_utils;