mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Add parsing of supported shorthand properties.
This commit is contained in:
parent
3343de55e6
commit
449683565f
4 changed files with 381 additions and 38 deletions
|
@ -3,6 +3,9 @@
|
||||||
* 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;
|
||||||
|
|
||||||
|
@ -16,6 +19,7 @@ pub mod specified {
|
||||||
Au(Integer), // application units
|
Au(Integer), // application units
|
||||||
Em(Float),
|
Em(Float),
|
||||||
Ex(Float),
|
Ex(Float),
|
||||||
|
// XXX uncomment when supported:
|
||||||
// Ch(Float),
|
// Ch(Float),
|
||||||
// Rem(Float),
|
// Rem(Float),
|
||||||
// Vw(Float),
|
// Vw(Float),
|
||||||
|
@ -136,3 +140,30 @@ 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,23 +4,26 @@
|
||||||
|
|
||||||
|
|
||||||
pub use std::ascii::{to_ascii_lower, eq_ignore_ascii_case};
|
pub use std::ascii::{to_ascii_lower, eq_ignore_ascii_case};
|
||||||
|
pub use std::iterator;
|
||||||
pub use std::option;
|
pub use std::option;
|
||||||
pub use cssparser::*;
|
pub use cssparser::*;
|
||||||
pub use CSSColor = cssparser::Color;
|
pub use CSSColor = cssparser::Color;
|
||||||
pub use parsing_utils::*;
|
pub use parsing_utils::*;
|
||||||
pub use super::common_types::specified;
|
pub use super::common_types::*;
|
||||||
pub use super::common_types;
|
|
||||||
|
|
||||||
|
|
||||||
macro_rules! single_keyword(
|
macro_rules! single_keyword(
|
||||||
($property_name: ident, $( $lower_case_keyword_string: pat => $variant: ident ),+ ) => {
|
($property_name: ident, $( $lower_case_keyword_string: pat => $variant: ident ),+ ) => {
|
||||||
mod $property_name {
|
pub mod $property_name {
|
||||||
use super::*;
|
use super::*;
|
||||||
enum SpecifiedValue {
|
pub enum SpecifiedValue {
|
||||||
$( $variant ),+
|
$( $variant ),+
|
||||||
}
|
}
|
||||||
fn parse(input: &[ComponentValue]) -> option::Option<SpecifiedValue> {
|
pub fn parse(input: &[ComponentValue]) -> option::Option<SpecifiedValue> {
|
||||||
do one_component_value(input).chain(get_ident_lower).chain |keyword| {
|
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() {
|
match keyword.as_slice() {
|
||||||
$( $lower_case_keyword_string => option::Some($variant) ),+ ,
|
$( $lower_case_keyword_string => option::Some($variant) ),+ ,
|
||||||
_ => option::None,
|
_ => option::None,
|
||||||
|
@ -37,10 +40,10 @@ macro_rules! single_type(
|
||||||
single_type!($property_name, $type_, $type_::parse)
|
single_type!($property_name, $type_, $type_::parse)
|
||||||
};
|
};
|
||||||
($property_name: ident, $type_: ty, $parse_function: expr) => {
|
($property_name: ident, $type_: ty, $parse_function: expr) => {
|
||||||
mod $property_name {
|
pub mod $property_name {
|
||||||
use super::*;
|
use super::*;
|
||||||
type SpecifiedValue = $type_;
|
pub type SpecifiedValue = $type_;
|
||||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
one_component_value(input).chain($parse_function)
|
one_component_value(input).chain($parse_function)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +77,11 @@ single_type!(border_right_color, CSSColor)
|
||||||
single_type!(border_bottom_color, CSSColor)
|
single_type!(border_bottom_color, CSSColor)
|
||||||
single_type!(border_left_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> {
|
pub fn parse_border_width(component_value: &ComponentValue) -> Option<specified::Length> {
|
||||||
match component_value {
|
match component_value {
|
||||||
&Ident(ref value) => match to_ascii_lower(value.as_slice()).as_slice() {
|
&Ident(ref value) => match to_ascii_lower(value.as_slice()).as_slice() {
|
||||||
|
@ -91,6 +99,7 @@ single_type!(border_right_width, specified::Length, parse_border_width)
|
||||||
single_type!(border_bottom_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)
|
single_type!(border_left_width, specified::Length, parse_border_width)
|
||||||
|
|
||||||
|
|
||||||
// CSS 2.1, Section 9 - Visual formatting model
|
// CSS 2.1, Section 9 - Visual formatting model
|
||||||
|
|
||||||
// TODO: don’t parse values we don’t support
|
// TODO: don’t parse values we don’t support
|
||||||
|
@ -125,24 +134,27 @@ single_type!(width, specified::LengthOrPercentageOrAuto,
|
||||||
single_type!(height, specified::LengthOrPercentageOrAuto,
|
single_type!(height, specified::LengthOrPercentageOrAuto,
|
||||||
specified::LengthOrPercentageOrAuto::parse_non_negative)
|
specified::LengthOrPercentageOrAuto::parse_non_negative)
|
||||||
|
|
||||||
mod line_height {
|
pub mod line_height {
|
||||||
use super::*;
|
use super::*;
|
||||||
enum SpecifiedValue {
|
pub enum SpecifiedValue {
|
||||||
Normal,
|
Normal,
|
||||||
Length(specified::Length),
|
Length(specified::Length),
|
||||||
Percentage(common_types::Float),
|
Percentage(Float),
|
||||||
Number(common_types::Float),
|
Number(Float),
|
||||||
}
|
}
|
||||||
/// normal | <number> | <length> | <percentage>
|
/// normal | <number> | <length> | <percentage>
|
||||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
match one_component_value(input) {
|
one_component_value(input).chain(from_component_value)
|
||||||
Some(&ast::Number(ref value)) if value.value >= 0.
|
}
|
||||||
|
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
|
||||||
|
match input {
|
||||||
|
&ast::Number(ref value) if value.value >= 0.
|
||||||
=> Some(Number(value.value)),
|
=> Some(Number(value.value)),
|
||||||
Some(&ast::Percentage(ref value)) if value.value >= 0.
|
&ast::Percentage(ref value) if value.value >= 0.
|
||||||
=> Some(Percentage(value.value)),
|
=> Some(Percentage(value.value)),
|
||||||
Some(&Dimension(ref value, ref unit)) if value.value >= 0.
|
&Dimension(ref value, ref unit) if value.value >= 0.
|
||||||
=> specified::Length::parse_dimension(value.value, unit.as_slice()).map_move(Length),
|
=> specified::Length::parse_dimension(value.value, unit.as_slice()).map_move(Length),
|
||||||
Some(&Ident(ref value)) if eq_ignore_ascii_case(value.as_slice(), "auto")
|
&Ident(ref value) if eq_ignore_ascii_case(value.as_slice(), "auto")
|
||||||
=> Some(Normal),
|
=> Some(Normal),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -163,7 +175,7 @@ single_type!(color, CSSColor)
|
||||||
|
|
||||||
// CSS 2.1, Section 15 - Fonts
|
// CSS 2.1, Section 15 - Fonts
|
||||||
|
|
||||||
mod font_family {
|
pub mod font_family {
|
||||||
use super::*;
|
use super::*;
|
||||||
enum FontFamily {
|
enum FontFamily {
|
||||||
FamilyName(~str),
|
FamilyName(~str),
|
||||||
|
@ -174,13 +186,17 @@ mod font_family {
|
||||||
Fantasy,
|
Fantasy,
|
||||||
Monospace,
|
Monospace,
|
||||||
}
|
}
|
||||||
type SpecifiedValue = ~[FontFamily];
|
pub type SpecifiedValue = ~[FontFamily];
|
||||||
/// <familiy-name>#
|
/// <familiy-name>#
|
||||||
/// <familiy-name> = <string> | [ <ident>+ ]
|
/// <familiy-name> = <string> | [ <ident>+ ]
|
||||||
/// TODO: <generic-familiy>
|
/// TODO: <generic-familiy>
|
||||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
|
// XXX Using peekable() for compat with parsing of the 'font' shorthand.
|
||||||
|
from_iter(input.skip_whitespace().peekable())
|
||||||
|
}
|
||||||
|
type Iter<'self> = iterator::Peekable<&'self ComponentValue, SkipWhitespaceIterator<'self>>;
|
||||||
|
pub fn from_iter(mut iter: Iter) -> Option<SpecifiedValue> {
|
||||||
let mut result = ~[];
|
let mut result = ~[];
|
||||||
let mut iter = input.skip_whitespace();
|
|
||||||
macro_rules! add(
|
macro_rules! add(
|
||||||
($value: expr) => {
|
($value: expr) => {
|
||||||
{
|
{
|
||||||
|
@ -232,11 +248,15 @@ mod font_family {
|
||||||
}
|
}
|
||||||
|
|
||||||
single_keyword!(font_style, "normal" => Normal, "italic" => Italic, "oblique" => Oblique)
|
single_keyword!(font_style, "normal" => Normal, "italic" => Italic, "oblique" => Oblique)
|
||||||
single_keyword!(font_variant, "normal" => Normal, "small-caps" => SmallCaps)
|
single_keyword!(font_variant,
|
||||||
|
// Uncomment when supported
|
||||||
|
//"small-caps" => SmallCaps,
|
||||||
|
"normal" => Normal
|
||||||
|
)
|
||||||
|
|
||||||
mod font_weight {
|
pub mod font_weight {
|
||||||
use super::*;
|
use super::*;
|
||||||
enum SpecifiedValue {
|
pub enum SpecifiedValue {
|
||||||
Bolder,
|
Bolder,
|
||||||
Lighther,
|
Lighther,
|
||||||
Weight100,
|
Weight100,
|
||||||
|
@ -250,16 +270,19 @@ mod font_weight {
|
||||||
Weight900,
|
Weight900,
|
||||||
}
|
}
|
||||||
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
|
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
|
||||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
match one_component_value(input) {
|
one_component_value(input).chain(from_component_value)
|
||||||
Some(&Ident(ref value)) => match to_ascii_lower(value.as_slice()).as_slice() {
|
}
|
||||||
|
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
|
||||||
|
match input {
|
||||||
|
&Ident(ref value) => match to_ascii_lower(value.as_slice()).as_slice() {
|
||||||
"bold" => Some(Weight700),
|
"bold" => Some(Weight700),
|
||||||
"normal" => Some(Weight400),
|
"normal" => Some(Weight400),
|
||||||
"bolder" => Some(Bolder),
|
"bolder" => Some(Bolder),
|
||||||
"lighter" => Some(Lighther),
|
"lighter" => Some(Lighther),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
Some(&Number(ref value)) => match value.int_value {
|
&Number(ref value) => match value.int_value {
|
||||||
Some(100) => Some(Weight100),
|
Some(100) => Some(Weight100),
|
||||||
Some(200) => Some(Weight200),
|
Some(200) => Some(Weight200),
|
||||||
Some(300) => Some(Weight300),
|
Some(300) => Some(Weight300),
|
||||||
|
@ -276,14 +299,16 @@ mod font_weight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod font_size {
|
pub mod font_size {
|
||||||
use super::*;
|
use super::*;
|
||||||
type SpecifiedValue = specified::Length; // Percentages are the same as em.
|
pub type SpecifiedValue = specified::Length; // Percentages are the same as em.
|
||||||
/// <length> | <percentage>
|
/// <length> | <percentage>
|
||||||
/// TODO: support <absolute-size> and <relative-size>
|
/// TODO: support <absolute-size> and <relative-size>
|
||||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||||
do one_component_value(input).chain(specified::LengthOrPercentage::parse_non_negative)
|
one_component_value(input).chain(from_component_value)
|
||||||
.map_move |value| {
|
}
|
||||||
|
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
|
||||||
|
do specified::LengthOrPercentage::parse_non_negative(input).map_move |value| {
|
||||||
match value {
|
match value {
|
||||||
specified::Length(value) => value,
|
specified::Length(value) => value,
|
||||||
specified::Percentage(value) => specified::Em(value),
|
specified::Percentage(value) => specified::Em(value),
|
||||||
|
@ -297,9 +322,9 @@ mod font_size {
|
||||||
single_keyword!(text_align, "left" => Left, "right" => Right,
|
single_keyword!(text_align, "left" => Left, "right" => Right,
|
||||||
"center" => Center, "justify" => Justify)
|
"center" => Center, "justify" => Justify)
|
||||||
|
|
||||||
mod text_decoration {
|
pub mod text_decoration {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct SpecifiedValue {
|
pub struct SpecifiedValue {
|
||||||
underline: bool,
|
underline: bool,
|
||||||
overline: bool,
|
overline: bool,
|
||||||
line_through: bool,
|
line_through: bool,
|
||||||
|
|
|
@ -6,8 +6,9 @@ use std::ascii::to_ascii_lower;
|
||||||
use cssparser::*;
|
use cssparser::*;
|
||||||
use errors::{ErrorLoggerIterator, log_css_error};
|
use errors::{ErrorLoggerIterator, log_css_error};
|
||||||
|
|
||||||
pub mod longhands;
|
|
||||||
pub mod common_types;
|
pub mod common_types;
|
||||||
|
pub mod longhands;
|
||||||
|
pub mod shorthands;
|
||||||
|
|
||||||
|
|
||||||
pub struct PropertyDeclarationBlock {
|
pub struct PropertyDeclarationBlock {
|
||||||
|
|
286
properties/shorthands.rs
Normal file
286
properties/shorthands.rs
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
/* 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| eq_ignore_ascii_case(v.as_slice(), "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 iter = iter.peekable();
|
||||||
|
match iter.peek() {
|
||||||
|
Some(& &Delim('/')) => {
|
||||||
|
iter.next();
|
||||||
|
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