diff --git a/.gitignore b/.gitignore index 504bab8100a..55369470c0c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,7 @@ *.dylib *.dll *.dummy +*.pyc *-test Makefile +properties/mod.rs diff --git a/Makefile.in b/Makefile.in index f19332bac2d..585705efd53 100644 --- a/Makefile.in +++ b/Makefile.in @@ -9,7 +9,8 @@ RUSTC ?= rustc RUSTFLAGS ?= -L $(CSSPARSER_DIR) -RUST_SRC=$(shell find $(VPATH)/. -type f -name '*.rs') $(CSSPARSER_DIR)/libcssparser.dummy +PROPERTIES_RS=$(VPATH)/properties/mod.rs +RUST_SRC=$(shell find $(VPATH)/. -type f -name '*.rs') $(CSSPARSER_DIR)/libcssparser.dummy $(PROPERTIES_RS) .PHONY: all all: libservo-style.dummy @@ -32,3 +33,7 @@ check-debug: servo-style-tests .PHONY: clean clean: rm -f *.o *.a *.so *.dylib *.dll *.dummy *-test + + +$(PROPERTIES_RS): $(PROPERTIES_RS).mako + PYTHONPATH=$(VPATH)/Mako-0.8.1.zip python -c "from mako.template import Template; print(Template(filename='$<').render())" > $@ diff --git a/Mako-0.8.1.zip b/Mako-0.8.1.zip new file mode 100644 index 00000000000..c324c72ef8a Binary files /dev/null and b/Mako-0.8.1.zip differ diff --git a/properties/common_types.rs b/properties/common_types.rs index 80304ff7fe0..1b39f46ea55 100644 --- a/properties/common_types.rs +++ b/properties/common_types.rs @@ -3,9 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use cssparser::*; -use parsing_utils::*; - pub type Float = f64; pub type Integer = i64; @@ -140,30 +137,3 @@ pub mod computed { } } } - - -pub enum BorderStyle { - BorderStyleSolid, - // Uncomment when supported -// BorderStyleDotted, -// BorderStyleDashed, -// BorderStyleDouble, -// BorderStyleGroove, -// BorderStyleRidge, -// BorderStyleInset, -// BorderStyleOutset, -// BorderStyleHidden, - BorderStyleNone, -} -impl BorderStyle { - pub fn parse(input: &ComponentValue) -> Option { - do get_ident_lower(input).chain |keyword| { - match keyword.as_slice() { - "solid" => Some(BorderStyleSolid), - "none" => Some(BorderStyleNone), - _ => None, - } - } - } -} - diff --git a/properties/longhands.rs b/properties/longhands.rs deleted file mode 100644 index e0bc102f8f2..00000000000 --- a/properties/longhands.rs +++ /dev/null @@ -1,362 +0,0 @@ -/* 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::StrAsciiExt; -pub use std::iterator; -pub use std::option; -pub use cssparser::*; -pub use CSSColor = cssparser::Color; -pub use parsing_utils::*; -pub use super::common_types::*; - - -macro_rules! single_keyword( - ($property_name: ident, $( $lower_case_keyword_string: pat => $variant: ident ),+ ) => { - pub mod $property_name { - use super::*; - pub enum SpecifiedValue { - $( $variant ),+ - } - pub fn parse(input: &[ComponentValue]) -> option::Option { - one_component_value(input).chain(from_component_value) - } - pub fn from_component_value(v: &ComponentValue) -> option::Option { - do get_ident_lower(v).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) => { - pub mod $property_name { - use super::*; - pub type SpecifiedValue = $type_; - pub fn parse(input: &[ComponentValue]) -> Option { - one_component_value(input).chain($parse_function) - } - } - }; -) - - - -// CSS 2.1, Section 8 - Box model - -single_type!(margin_top, specified::LengthOrPercentageOrAuto, - specified::LengthOrPercentageOrAuto::parse) -single_type!(margin_right, specified::LengthOrPercentageOrAuto, - specified::LengthOrPercentageOrAuto::parse) -single_type!(margin_bottom, specified::LengthOrPercentageOrAuto, - specified::LengthOrPercentageOrAuto::parse) -single_type!(margin_left, specified::LengthOrPercentageOrAuto, - specified::LengthOrPercentageOrAuto::parse) - -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) - -single_type!(border_top_style, BorderStyle) -single_type!(border_right_style, BorderStyle) -single_type!(border_bottom_style, BorderStyle) -single_type!(border_left_style, BorderStyle) - -pub fn parse_border_width(component_value: &ComponentValue) -> Option { - match component_value { - &Ident(ref value) => match value.to_ascii_lower().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: don’t parse values we don’t 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::LengthOrPercentageOrAuto, - specified::LengthOrPercentageOrAuto::parse_non_negative) -single_type!(height, specified::LengthOrPercentageOrAuto, - specified::LengthOrPercentageOrAuto::parse_non_negative) - -pub mod line_height { - use super::*; - pub enum SpecifiedValue { - Normal, - Length(specified::Length), - Percentage(Float), - Number(Float), - } - /// normal | | | - pub fn parse(input: &[ComponentValue]) -> Option { - one_component_value(input).chain(from_component_value) - } - pub fn from_component_value(input: &ComponentValue) -> Option { - match input { - &ast::Number(ref value) if value.value >= 0. - => Some(Number(value.value)), - &ast::Percentage(ref value) if value.value >= 0. - => Some(Percentage(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), - _ => 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 - -pub mod font_family { - use super::*; - enum FontFamily { - FamilyName(~str), - // Generic - Serif, - SansSerif, - Cursive, - Fantasy, - Monospace, - } - pub type SpecifiedValue = ~[FontFamily]; - /// # - /// = | [ + ] - /// TODO: - pub fn parse(input: &[ComponentValue]) -> Option { - from_iter(input.skip_whitespace()) - } - pub fn from_iter<'a>(mut iter: SkipWhitespaceIterator<'a>) -> Option { - let mut result = ~[]; - 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 value.to_ascii_lower().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, - // Uncomment when supported - //"small-caps" => SmallCaps, - "normal" => Normal -) - -pub mod font_weight { - use super::*; - pub 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 - pub fn parse(input: &[ComponentValue]) -> Option { - one_component_value(input).chain(from_component_value) - } - pub fn from_component_value(input: &ComponentValue) -> Option { - match input { - &Ident(ref value) => match value.to_ascii_lower().as_slice() { - "bold" => Some(Weight700), - "normal" => Some(Weight400), - "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), - _ => None, - }, - _ => None - } - } -} - -pub mod font_size { - use super::*; - pub type SpecifiedValue = specified::Length; // Percentages are the same as em. - /// | - /// TODO: support and - pub fn parse(input: &[ComponentValue]) -> Option { - one_component_value(input).chain(from_component_value) - } - pub fn from_component_value(input: &ComponentValue) -> Option { - do specified::LengthOrPercentage::parse_non_negative(input).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) - -pub mod text_decoration { - use super::*; - pub 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 { - let mut result = SpecifiedValue { - underline: false, overline: false, line_through: false, - }; - let mut blink = false; - let mut empty = 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 diff --git a/properties/mod.rs b/properties/mod.rs deleted file mode 100644 index fc1bd46d7f2..00000000000 --- a/properties/mod.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* 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::StrAsciiExt; -use cssparser::*; -use errors::{ErrorLoggerIterator, log_css_error}; - -pub mod common_types; -pub mod longhands; -pub mod shorthands; - - -pub struct PropertyDeclarationBlock { - important: ~[PropertyDeclaration], - normal: ~[PropertyDeclaration], -} - -pub struct PropertyDeclaration; // TODO - - -pub fn parse_property_declaration_list(input: ~[Node]) -> PropertyDeclarationBlock { - let mut important = ~[]; - let mut normal = ~[]; - for item in ErrorLoggerIterator(parse_declaration_list(input.move_iter())) { - match item { - Decl_AtRule(rule) => log_css_error( - rule.location, fmt!("Unsupported at-rule in declaration list: @%s", rule.name)), - Declaration(Declaration{ location: l, name: n, value: v, important: i}) => { - let list = if i { &mut important } else { &mut normal }; - if !parse_one_property_declaration(n.to_ascii_lower(), v, list) { - log_css_error(l, "Invalid property declaration") - } - } - } - } - PropertyDeclarationBlock { important: important, normal: normal } -} - - -fn parse_one_property_declaration(name: &str, value: ~[ComponentValue], - result_list: &mut ~[PropertyDeclaration]) -> bool { - - let _ = name; - let _ = value; - let _ = result_list; - false -} diff --git a/properties/mod.rs.mako b/properties/mod.rs.mako new file mode 100644 index 00000000000..7bcd3bd6636 --- /dev/null +++ b/properties/mod.rs.mako @@ -0,0 +1,639 @@ +/* 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/. */ + +// This file is a Mako template: http://www.makotemplates.org/ + +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::*; + +pub mod common_types; + + +<%! + +def to_rust_ident(name): + name = name.replace("-", "_") + if name in ["static"]: # Rust keywords + name += "_" + return name + +class Longhand(object): + def __init__(self, name): + self.name = name + self.ident = to_rust_ident(name) + + +class Shorthand(object): + def __init__(self, name, sub_properties): + self.name = name + self.ident = to_rust_ident(name) + self.sub_properties = [Longhand(s) for s in sub_properties] + +longhands = [] +shorthands = [] + +%> + +pub mod longhands { + pub use super::*; + + <%def name="longhand(name)"> + <% + property = Longhand(name) + longhands.append(property) + %> + pub mod ${property.ident} { + use super::*; + ${caller.body()} + } + + + <%def name="single_component_value(name)"> + <%self:longhand name="${name}"> + ${caller.body()} + pub fn parse(input: &[ComponentValue]) -> Option { + one_component_value(input).chain(from_component_value) + } + + + + <%def name="single_keyword(name, values)"> + <%self:single_component_value name="${name}"> + pub enum SpecifiedValue { + % for value in values.split(): + ${to_rust_ident(value)}, + % endfor + } + pub fn from_component_value(v: &ComponentValue) -> Option { + do get_ident_lower(v).chain |keyword| { + match keyword.as_slice() { + % for value in values.split(): + "${value}" => Some(${to_rust_ident(value)}), + % endfor + _ => None, + } + } + } + + + + <%def name="predefined_function(name, result_type, function)"> + <%self:longhand name="${name}"> + pub type SpecifiedValue = ${result_type}; + pub fn parse(input: &[ComponentValue]) -> Option { + one_component_value(input).chain(${function}) + } + + + + <%def name="predefined_type(name, type)"> + ${predefined_function(name, type, type + "::parse")} + + + + // CSS 2.1, Section 8 - Box model + + ${predefined_function("margin-top", "specified::LengthOrPercentageOrAuto", + "specified::LengthOrPercentageOrAuto::parse")} + ${predefined_function("margin-right", "specified::LengthOrPercentageOrAuto", + "specified::LengthOrPercentageOrAuto::parse")} + ${predefined_function("margin-bottom", "specified::LengthOrPercentageOrAuto", + "specified::LengthOrPercentageOrAuto::parse")} + ${predefined_function("margin-left", "specified::LengthOrPercentageOrAuto", + "specified::LengthOrPercentageOrAuto::parse")} + + ${predefined_function("padding-top", + "specified::LengthOrPercentage", + "specified::LengthOrPercentage::parse_non_negative")} + ${predefined_function("padding-right", + "specified::LengthOrPercentage", + "specified::LengthOrPercentage::parse_non_negative")} + ${predefined_function("padding-bottom", + "specified::LengthOrPercentage", + "specified::LengthOrPercentage::parse_non_negative")} + ${predefined_function("padding-left", + "specified::LengthOrPercentage", + "specified::LengthOrPercentage::parse_non_negative")} + + ${predefined_type("border-top-color", "CSSColor")} + ${predefined_type("border-right-color", "CSSColor")} + ${predefined_type("border-bottom-color", "CSSColor")} + ${predefined_type("border-left-color", "CSSColor")} + + pub enum BorderStyle { + BorderStyleSolid, + // Uncomment when supported +// BorderStyleDotted, +// BorderStyleDashed, +// BorderStyleDouble, +// BorderStyleGroove, +// BorderStyleRidge, +// BorderStyleInset, +// BorderStyleOutset, +// BorderStyleHidden, + BorderStyleNone, + } + impl BorderStyle { + pub fn parse(input: &ComponentValue) -> Option { + do get_ident_lower(input).chain |keyword| { + match keyword.as_slice() { + "solid" => Some(BorderStyleSolid), + "none" => Some(BorderStyleNone), + _ => None, + } + } + } + } + ${predefined_type("border-top-style", "BorderStyle")} + ${predefined_type("border-right-style", "BorderStyle")} + ${predefined_type("border-bottom-style", "BorderStyle")} + ${predefined_type("border-left-style", "BorderStyle")} + + pub fn parse_border_width(component_value: &ComponentValue) -> Option { + match component_value { + &Ident(ref value) => match value.to_ascii_lower().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) + } + } + ${predefined_function("border-top-width", "specified::Length", "parse_border_width")} + ${predefined_function("border-right-width", "specified::Length", "parse_border_width")} + ${predefined_function("border-bottom-width", "specified::Length", "parse_border_width")} + ${predefined_function("border-left-width", "specified::Length", "parse_border_width")} + + // CSS 2.1, Section 9 - Visual formatting model + + // TODO: don't parse values we don't support + ${single_keyword("display", + "inline block list-item inline-block none " + )} +// "table inline-table table-row-group table-header-group table-footer-group " +// "table-row table-column-group table-column table-cell table-caption" + + ${single_keyword("position", "static absolute relative fixed")} + ${single_keyword("float", "left right none")} + ${single_keyword("clear", "left right none both")} + + // 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")} + + <%self:single_component_value name="line-height"> + pub enum SpecifiedValue { + Normal, + Length(specified::Length), + Percentage(Float), + Number(Float), + } + /// normal | | | + pub fn from_component_value(input: &ComponentValue) -> Option { + match input { + &ast::Number(ref value) if value.value >= 0. + => Some(Number(value.value)), + &ast::Percentage(ref value) if value.value >= 0. + => Some(Percentage(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), + _ => 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 + + ${predefined_type("background-color", "CSSColor")} + ${predefined_type("color", "CSSColor")} + + // CSS 2.1, Section 15 - Fonts + + <%self:longhand name="font-family"> + enum FontFamily { + FamilyName(~str), + // Generic +// Serif, +// SansSerif, +// Cursive, +// Fantasy, +// Monospace, + } + pub type SpecifiedValue = ~[FontFamily]; + /// # + /// = | [ + ] + /// TODO: + pub fn parse(input: &[ComponentValue]) -> Option { + from_iter(input.skip_whitespace()) + } + pub fn from_iter<'a>(mut iter: SkipWhitespaceIterator<'a>) -> Option { + let mut result = ~[]; + 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 value.to_ascii_lower().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 italic oblique")} + ${single_keyword("font-variant", "normal")} // Add small-caps when supported + + <%self:single_component_value name="font-weight"> + pub 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 + pub fn from_component_value(input: &ComponentValue) -> Option { + match input { + &Ident(ref value) => match value.to_ascii_lower().as_slice() { + "bold" => Some(Weight700), + "normal" => Some(Weight400), + "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), + _ => None, + }, + _ => None + } + } + + + <%self:single_component_value name="font-size"> + pub type SpecifiedValue = specified::Length; // Percentages are the same as em. + /// | + /// TODO: support and + pub fn from_component_value(input: &ComponentValue) -> Option { + do specified::LengthOrPercentage::parse_non_negative(input).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 right center justify")} + + <%self:longhand name="text-decoration"> + pub 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 ] + pub fn parse(input: &[ComponentValue]) -> Option { + let mut result = SpecifiedValue { + underline: false, overline: false, line_through: false, + }; + let mut blink = false; + let mut empty = 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 +} + + +pub mod shorthands { + pub use super::*; + pub use super::longhands::*; + + <%def name="shorthand(name, sub_properties)"> + <% + shorthand = Shorthand(name, sub_properties.split()) + shorthands.append(shorthand) + %> + pub mod ${shorthand.ident} { + use super::*; + struct Longhands { + % for sub_property in shorthand.sub_properties: + ${sub_property.ident}: Option<${sub_property.ident}::SpecifiedValue>, + % endfor + } + pub fn parse(input: &[ComponentValue]) -> Option { + ${caller.body()} + } + } + + + <%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)"> + <%self:shorthand name="${name}" sub_properties="${ + ' '.join(sub_property_pattern % side + for side in ['top', 'right', 'bottom', 'left'])}"> + let mut iter = input.skip_whitespace().map(${parser_function}); + // zero or more than four values is invalid. + // one value sets them all + // two values set (top, bottom) and (left, right) + // three values set top, (left, right) and bottom + // four values set them in order + let top = iter.next().unwrap_or_default(None); + let right = iter.next().unwrap_or_default(top); + let bottom = iter.next().unwrap_or_default(top); + let left = iter.next().unwrap_or_default(right); + if top.is_some() && right.is_some() && bottom.is_some() && left.is_some() + && iter.next().is_none() { + Some(Longhands { + % for side in ["top", "right", "bottom", "left"]: + ${to_rust_ident(sub_property_pattern % side)}: ${side}, + % endfor + }) + } else { + None + } + + + + + // TODO: other background-* properties + <%self:shorthand name="background" sub_properties="background-color"> + do one_component_value(input).chain(CSSColor::parse).map_move |color| { + Longhands { background_color: Some(color) } + } + + + ${four_sides_shorthand("border-color", "border-%s-color", "CSSColor::parse")} + ${four_sides_shorthand("border-style", "border-%s-style", "BorderStyle::parse")} + ${four_sides_shorthand("border-width", "border-%s-width", "parse_border_width")} + + pub fn parse_border(input: &[ComponentValue]) -> Option<(Option, Option, + Option)> { + let mut color = None; + let mut style = None; + let mut width = None; + let mut any = false; + for component_value in input.skip_whitespace() { + if color.is_none() { + match CSSColor::parse(component_value) { + Some(c) => { color = Some(c); any = true; loop }, + None => () + } + } + if style.is_none() { + match BorderStyle::parse(component_value) { + Some(s) => { style = Some(s); any = true; loop }, + None => () + } + } + if width.is_none() { + match parse_border_width(component_value) { + Some(w) => { width = Some(w); any = true; loop }, + None => () + } + } + return None + } + if any { Some((color, style, width)) } else { None } + } + + <%def name="border_side(side)"> + <%self:shorthand name="border-${side}" sub_properties="border-${side}-color + border-${side}-style + border-${side}-width"> + do parse_border(input).map_move |(color, style, width)| { + Longhands { border_${side}_color: color, border_${side}_style: style, + border_${side}_width: width } + } + + + + ${border_side("top")} + ${border_side("right")} + ${border_side("bottom")} + ${border_side("left")} + + <%self:shorthand name="border" sub_properties=" + border-top-color + border-top-width + border-top-style + border-right-color + border-right-width + border-right-style + border-bottom-color + border-bottom-width + border-bottom-style + border-left-color + border-left-width + border-left-style + "> + do parse_border(input).map_move |(color, style, width)| { + Longhands { + border_top_color: color, border_top_style: style, border_top_width: width, + border_right_color: color, border_right_style: style, border_right_width: width, + border_bottom_color: color, border_bottom_style: style, border_bottom_width: width, + border_left_color: color, border_left_style: style, border_left_width: width, + } + } + + +} + + +pub struct PropertyDeclarationBlock { + important: ~[PropertyDeclaration], + normal: ~[PropertyDeclaration], +} + + +pub fn parse_property_declaration_list(input: ~[Node]) -> PropertyDeclarationBlock { + let mut important = ~[]; + let mut normal = ~[]; + for item in ErrorLoggerIterator(parse_declaration_list(input.move_iter())) { + match item { + Decl_AtRule(rule) => log_css_error( + rule.location, fmt!("Unsupported at-rule in declaration list: @%s", rule.name)), + Declaration(Declaration{ location: l, name: n, value: v, important: i}) => { + let list = if i { &mut important } else { &mut normal }; + if !PropertyDeclaration::parse(n, v, list) { + log_css_error(l, "Invalid property declaration") + } + } + } + } + PropertyDeclarationBlock { important: important, normal: normal } +} + + +pub enum CSSWideKeyword { + Initial, + Inherit, + Unset, +} + +impl CSSWideKeyword { + pub fn parse(input: &[ComponentValue]) -> Option { + do one_component_value(input).chain(get_ident_lower).chain |keyword| { + match keyword.as_slice() { + "initial" => Some(Initial), + "inherit" => Some(Inherit), + "unset" => Some(Unset), + _ => None + } + } + } +} + +pub enum DeclaredValue { + SpecifiedValue(T), + CSSWideKeyword(CSSWideKeyword), +} + +pub enum PropertyDeclaration { + % for property in longhands: + ${property.ident}_declaration(DeclaredValue), + % endfor +} + +impl PropertyDeclaration { + pub fn parse(name: &str, value: &[ComponentValue], + result_list: &mut ~[PropertyDeclaration]) -> bool { + match name.to_ascii_lower().as_slice() { + % for property in longhands: + "${property.name}" => result_list.push(${property.ident}_declaration( + match CSSWideKeyword::parse(value) { + Some(keyword) => CSSWideKeyword(keyword), + None => match longhands::${property.ident}::parse(value) { + Some(value) => SpecifiedValue(value), + None => return false, + } + } + )), + % endfor + % for shorthand in shorthands: + "${shorthand.name}" => match CSSWideKeyword::parse(value) { + Some(keyword) => { + % for sub_property in shorthand.sub_properties: + result_list.push(${sub_property.ident}_declaration( + CSSWideKeyword(keyword) + )); + % endfor + }, + None => match shorthands::${shorthand.ident}::parse(value) { + Some(result) => { + % for sub_property in shorthand.sub_properties: + result_list.push(${sub_property.ident}_declaration( + match result.${sub_property.ident} { + Some(value) => SpecifiedValue(value), + None => CSSWideKeyword(Initial), + } + )); + % endfor + }, + None => return false, + } + }, + % endfor + _ => return false, // Unknown property + } + true + } +} diff --git a/properties/shorthands.rs b/properties/shorthands.rs deleted file mode 100644 index 178d6bfbe8b..00000000000 --- a/properties/shorthands.rs +++ /dev/null @@ -1,286 +0,0 @@ -/* 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 super::longhands::*; - - -macro_rules! shorthand( - ($name: ident[$($longhand: ident),+] |input| $parser: expr) => { - pub mod $name { - use super::*; - struct Longhands { - $( $longhand: Option<$longhand::SpecifiedValue> ),+ - } - fn parse(input: &[ComponentValue]) -> Option { $parser } - } - }; -) - - -// The value of each longhand is the same as the value of the shorthand -macro_rules! duplicating_shorthand( - ($name: ident, $parser_function: expr, $($longhand: ident),+) => { - shorthand!($name [$($longhand),+] |input| { - do $parser_function(input).map_move |value| { - Longhands { $( $longhand: Some(value) ),+ } - } - }) - }; -) - - -macro_rules! four_side_shorthand( - ($name: ident, $parser_function: expr, - $top: ident, $right: ident, $bottom: ident, $left: ident) => { - shorthand!($name [$top, $right, $bottom, $left] |input| { - let mut iter = input.skip_whitespace().map($parser_function); - // zero or more than four values is invalid. - // one value sets them all - // two values set (top, bottom) and (left, right) - // three values set top, (left, right) and bottom - // four values set them in order - let top = iter.next().unwrap_or_default(None); - let right = iter.next().unwrap_or_default(top); - let bottom = iter.next().unwrap_or_default(top); - let left = iter.next().unwrap_or_default(right); - if top.is_some() && right.is_some() && bottom.is_some() && left.is_some() - && iter.next().is_none() { - Some(Longhands { $top: top, $right: right, $bottom: bottom, $left: left }) - } else { - None - } - }) - }; -) - - -// TODO: other background-* properties -shorthand!(background [ - background_color -] |input| { - do one_component_value(input).chain(CSSColor::parse).map_move |color| { - Longhands { background_color: Some(color) } - } -}) - - -duplicating_shorthand!(border_color, border_top_color::parse, - border_top_color, - border_right_color, - border_bottom_color, - border_left_color -) - -duplicating_shorthand!(border_width, border_top_width::parse, - border_top_width, - border_right_width, - border_bottom_width, - border_left_width -) - -duplicating_shorthand!(border_style, border_top_style::parse, - border_top_style, - border_right_style, - border_bottom_style, - border_left_style -) - - -pub fn parse_border(input: &[ComponentValue]) -> Option<(Option, Option, - Option)> { - let mut color = None; - let mut style = None; - let mut width = None; - let mut any = false; - for component_value in input.skip_whitespace() { - if color.is_none() { - match CSSColor::parse(component_value) { - Some(c) => { color = Some(c); any = true; loop }, - None => () - } - } - if style.is_none() { - match BorderStyle::parse(component_value) { - Some(s) => { style = Some(s); any = true; loop }, - None => () - } - } - if width.is_none() { - match parse_border_width(component_value) { - Some(w) => { width = Some(w); any = true; loop }, - None => () - } - } - return None - } - if any { Some((color, style, width)) } else { None } -} - - -shorthand!(border_top [ - border_top_color, - border_top_width, - border_top_style -] |input| { - do parse_border(input).map_move |(color, style, width)| { - Longhands { border_top_color: color, border_top_style: style, - border_top_width: width } - } -}) - -shorthand!(border_right [ - border_right_color, - border_right_width, - border_right_style -] |input| { - do parse_border(input).map_move |(color, style, width)| { - Longhands { border_right_color: color, border_right_style: style, - border_right_width: width } - } -}) - -shorthand!(border_bottom [ - border_bottom_color, - border_bottom_width, - border_bottom_style -] |input| { - do parse_border(input).map_move |(color, style, width)| { - Longhands { border_bottom_color: color, border_bottom_style: style, - border_bottom_width: width } - } -}) - -shorthand!(border_left [ - border_left_color, - border_left_width, - border_left_style -] |input| { - do parse_border(input).map_move |(color, style, width)| { - Longhands { border_left_color: color, border_left_style: style, - border_left_width: width } - } -}) - -shorthand!(border [ - border_top_color, - border_top_width, - border_top_style, - border_right_color, - border_right_width, - border_right_style, - border_bottom_color, - border_bottom_width, - border_bottom_style, - border_left_color, - border_left_width, - border_left_style -] |input| { - do parse_border(input).map_move |(color, style, width)| { - Longhands { - border_top_color: color, border_top_style: style, border_top_width: width, - border_right_color: color, border_right_style: style, border_right_width: width, - border_bottom_color: color, border_bottom_style: style, border_bottom_width: width, - border_left_color: color, border_left_style: style, border_left_width: width, - } - } -}) - - -// TODO: system fonts -shorthand!(font [ - font_style, - font_variant, - font_weight, - font_size, - line_height, - font_family -] |input| { - let mut iter = input.skip_whitespace(); - let mut nb_normals = 0u; - let mut style = None; - let mut variant = None; - let mut weight = None; - let mut size = None; - let mut line_height = None; - for component_value in iter { - // Special-case 'normal' because it is valid in each of - // font-style, font-weight and font-variant. - // Leaves the values to None, 'normal' is the initial value for each of them. - if get_ident_lower(component_value).filtered( - |v| v.eq_ignore_ascii_case("normal")).is_some() { - nb_normals += 1; - loop; - } - if style.is_none() { - match font_style::from_component_value(component_value) { - Some(s) => { style = Some(s); loop }, - None => () - } - } - if weight.is_none() { - match font_weight::from_component_value(component_value) { - Some(w) => { weight = Some(w); loop }, - None => () - } - } - if variant.is_none() { - match font_variant::from_component_value(component_value) { - Some(v) => { variant = Some(v); loop }, - None => () - } - } - match font_size::from_component_value(component_value) { - Some(s) => { size = Some(s); break }, - None => return None - } - } - #[inline] - fn count(opt: &Option) -> uint { - match opt { - &Some(_) => 1, - &None => 0, - } - } - if size.is_none() || (count(&style) + count(&weight) + count(&variant) + nb_normals) > 3 { - return None - } - let mut copied_iter = iter.clone(); - match copied_iter.next() { - Some(&Delim('/')) => { - iter = copied_iter; - line_height = match iter.next() { - Some(v) => line_height::from_component_value(v), - _ => return None, - }; - if line_height.is_none() { return None } - } - _ => () - } - let family = font_family::from_iter(iter); - if family.is_none() { return None } - Some(Longhands{ - font_style: style, - font_variant: variant, - font_weight: weight, - font_size: size, - line_height: line_height, - font_family: family - }) -}) - - -four_side_shorthand!(margin, specified::LengthOrPercentageOrAuto::parse, - margin_top, - margin_right, - margin_bottom, - margin_left -) - - -four_side_shorthand!(padding, specified::LengthOrPercentage::parse, - padding_top, - padding_right, - padding_bottom, - padding_left -)