mirror of
https://github.com/servo/servo.git
synced 2025-06-23 00:24:35 +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
|
||||
*.dll
|
||||
*.dummy
|
||||
*.pyc
|
||||
*-test
|
||||
Makefile
|
||||
properties/mod.rs
|
||||
|
|
|
@ -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())" > $@
|
||||
|
|
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/. */
|
||||
|
||||
|
||||
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<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