mirror of
https://github.com/servo/servo.git
synced 2025-06-23 08:34:42 +01:00
Add PropertyDeclaration, refactor property parsing to use a Mako template.
This commit is contained in:
parent
8fec26174b
commit
622bc5705c
8 changed files with 647 additions and 727 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,5 +4,7 @@
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
*.dummy
|
*.dummy
|
||||||
|
*.pyc
|
||||||
*-test
|
*-test
|
||||||
Makefile
|
Makefile
|
||||||
|
properties/mod.rs
|
||||||
|
|
|
@ -9,7 +9,8 @@ RUSTC ?= rustc
|
||||||
RUSTFLAGS ?= -L $(CSSPARSER_DIR)
|
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
|
.PHONY: all
|
||||||
all: libservo-style.dummy
|
all: libservo-style.dummy
|
||||||
|
@ -32,3 +33,7 @@ check-debug: servo-style-tests
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.a *.so *.dylib *.dll *.dummy *-test
|
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())" > $@
|
||||||
|
|
BIN
Mako-0.8.1.zip
Normal file
BIN
Mako-0.8.1.zip
Normal file
Binary file not shown.
|
@ -3,9 +3,6 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|
||||||
use cssparser::*;
|
|
||||||
use parsing_utils::*;
|
|
||||||
|
|
||||||
pub type Float = f64;
|
pub type Float = f64;
|
||||||
pub type Integer = i64;
|
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<BorderStyle> {
|
|
||||||
do get_ident_lower(input).chain |keyword| {
|
|
||||||
match keyword.as_slice() {
|
|
||||||
"solid" => Some(BorderStyleSolid),
|
|
||||||
"none" => Some(BorderStyleNone),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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<SpecifiedValue> {
|
|
||||||
one_component_value(input).chain(from_component_value)
|
|
||||||
}
|
|
||||||
pub fn from_component_value(v: &ComponentValue) -> option::Option<SpecifiedValue> {
|
|
||||||
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<SpecifiedValue> {
|
|
||||||
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<specified::Length> {
|
|
||||||
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 | <number> | <length> | <percentage>
|
|
||||||
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
|
||||||
one_component_value(input).chain(from_component_value)
|
|
||||||
}
|
|
||||||
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
|
|
||||||
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];
|
|
||||||
/// <familiy-name>#
|
|
||||||
/// <familiy-name> = <string> | [ <ident>+ ]
|
|
||||||
/// TODO: <generic-familiy>
|
|
||||||
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
|
||||||
from_iter(input.skip_whitespace())
|
|
||||||
}
|
|
||||||
pub fn from_iter<'a>(mut iter: SkipWhitespaceIterator<'a>) -> Option<SpecifiedValue> {
|
|
||||||
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<SpecifiedValue> {
|
|
||||||
one_component_value(input).chain(from_component_value)
|
|
||||||
}
|
|
||||||
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),
|
|
||||||
"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.
|
|
||||||
/// <length> | <percentage>
|
|
||||||
/// TODO: support <absolute-size> and <relative-size>
|
|
||||||
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
|
||||||
one_component_value(input).chain(from_component_value)
|
|
||||||
}
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<SpecifiedValue> {
|
|
||||||
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
|
|
|
@ -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
|
|
||||||
}
|
|
639
properties/mod.rs.mako
Normal file
639
properties/mod.rs.mako
Normal file
|
@ -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>
|
||||||
|
|
||||||
|
<%def name="single_component_value(name)">
|
||||||
|
<%self:longhand name="${name}">
|
||||||
|
${caller.body()}
|
||||||
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
|
one_component_value(input).chain(from_component_value)
|
||||||
|
}
|
||||||
|
</%self:longhand>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%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<SpecifiedValue> {
|
||||||
|
do get_ident_lower(v).chain |keyword| {
|
||||||
|
match keyword.as_slice() {
|
||||||
|
% for value in values.split():
|
||||||
|
"${value}" => Some(${to_rust_ident(value)}),
|
||||||
|
% endfor
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%self:single_component_value>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="predefined_function(name, result_type, function)">
|
||||||
|
<%self:longhand name="${name}">
|
||||||
|
pub type SpecifiedValue = ${result_type};
|
||||||
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
|
one_component_value(input).chain(${function})
|
||||||
|
}
|
||||||
|
</%self:longhand>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="predefined_type(name, type)">
|
||||||
|
${predefined_function(name, type, type + "::parse")}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
// 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<BorderStyle> {
|
||||||
|
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<specified::Length> {
|
||||||
|
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 | <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)),
|
||||||
|
&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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%self:single_component_value>
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
/// <familiy-name>#
|
||||||
|
/// <familiy-name> = <string> | [ <ident>+ ]
|
||||||
|
/// TODO: <generic-familiy>
|
||||||
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
|
from_iter(input.skip_whitespace())
|
||||||
|
}
|
||||||
|
pub fn from_iter<'a>(mut iter: SkipWhitespaceIterator<'a>) -> Option<SpecifiedValue> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
</%self:longhand>
|
||||||
|
|
||||||
|
|
||||||
|
${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<SpecifiedValue> {
|
||||||
|
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>
|
||||||
|
|
||||||
|
<%self:single_component_value name="font-size">
|
||||||
|
pub type SpecifiedValue = specified::Length; // Percentages are the same as em.
|
||||||
|
/// <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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%self:single_component_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<SpecifiedValue> {
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
</%self:longhand>
|
||||||
|
|
||||||
|
// 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<Longhands> {
|
||||||
|
${caller.body()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%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
|
||||||
|
}
|
||||||
|
</%self:shorthand>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
// 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) }
|
||||||
|
}
|
||||||
|
</%self:shorthand>
|
||||||
|
|
||||||
|
${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<CSSColor>, Option<BorderStyle>,
|
||||||
|
Option<specified::Length>)> {
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
</%self:shorthand>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%self:shorthand>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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<CSSWideKeyword> {
|
||||||
|
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<T> {
|
||||||
|
SpecifiedValue(T),
|
||||||
|
CSSWideKeyword(CSSWideKeyword),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PropertyDeclaration {
|
||||||
|
% for property in longhands:
|
||||||
|
${property.ident}_declaration(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
|
||||||
|
% 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Longhands> { $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<CSSColor>, Option<BorderStyle>,
|
|
||||||
Option<specified::Length>)> {
|
|
||||||
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<T>(opt: &Option<T>) -> 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
|
|
||||||
)
|
|
Loading…
Add table
Add a link
Reference in a new issue