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/. */
|
||||
|
||||
|
||||
use cssparser::*;
|
||||
use parsing_utils::*;
|
||||
|
||||
pub type Float = f64;
|
||||
pub type Integer = i64;
|
||||
|
||||
|
@ -16,6 +19,7 @@ pub mod specified {
|
|||
Au(Integer), // application units
|
||||
Em(Float),
|
||||
Ex(Float),
|
||||
// XXX uncomment when supported:
|
||||
// Ch(Float),
|
||||
// Rem(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::iterator;
|
||||
pub use std::option;
|
||||
pub use cssparser::*;
|
||||
pub use CSSColor = cssparser::Color;
|
||||
pub use parsing_utils::*;
|
||||
pub use super::common_types::specified;
|
||||
pub use super::common_types;
|
||||
pub use super::common_types::*;
|
||||
|
||||
|
||||
macro_rules! single_keyword(
|
||||
($property_name: ident, $( $lower_case_keyword_string: pat => $variant: ident ),+ ) => {
|
||||
mod $property_name {
|
||||
pub mod $property_name {
|
||||
use super::*;
|
||||
enum SpecifiedValue {
|
||||
pub enum SpecifiedValue {
|
||||
$( $variant ),+
|
||||
}
|
||||
fn parse(input: &[ComponentValue]) -> option::Option<SpecifiedValue> {
|
||||
do one_component_value(input).chain(get_ident_lower).chain |keyword| {
|
||||
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,
|
||||
|
@ -37,10 +40,10 @@ macro_rules! single_type(
|
|||
single_type!($property_name, $type_, $type_::parse)
|
||||
};
|
||||
($property_name: ident, $type_: ty, $parse_function: expr) => {
|
||||
mod $property_name {
|
||||
pub mod $property_name {
|
||||
use super::*;
|
||||
type SpecifiedValue = $type_;
|
||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||
pub type SpecifiedValue = $type_;
|
||||
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||
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_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 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_left_width, specified::Length, parse_border_width)
|
||||
|
||||
|
||||
// CSS 2.1, Section 9 - Visual formatting model
|
||||
|
||||
// TODO: don’t parse values we don’t support
|
||||
|
@ -125,24 +134,27 @@ single_type!(width, specified::LengthOrPercentageOrAuto,
|
|||
single_type!(height, specified::LengthOrPercentageOrAuto,
|
||||
specified::LengthOrPercentageOrAuto::parse_non_negative)
|
||||
|
||||
mod line_height {
|
||||
pub mod line_height {
|
||||
use super::*;
|
||||
enum SpecifiedValue {
|
||||
pub enum SpecifiedValue {
|
||||
Normal,
|
||||
Length(specified::Length),
|
||||
Percentage(common_types::Float),
|
||||
Number(common_types::Float),
|
||||
Percentage(Float),
|
||||
Number(Float),
|
||||
}
|
||||
/// normal | <number> | <length> | <percentage>
|
||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||
match one_component_value(input) {
|
||||
Some(&ast::Number(ref value)) if value.value >= 0.
|
||||
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)),
|
||||
Some(&ast::Percentage(ref value)) if value.value >= 0.
|
||||
&ast::Percentage(ref value) if value.value >= 0.
|
||||
=> 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),
|
||||
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),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -163,7 +175,7 @@ single_type!(color, CSSColor)
|
|||
|
||||
// CSS 2.1, Section 15 - Fonts
|
||||
|
||||
mod font_family {
|
||||
pub mod font_family {
|
||||
use super::*;
|
||||
enum FontFamily {
|
||||
FamilyName(~str),
|
||||
|
@ -174,13 +186,17 @@ mod font_family {
|
|||
Fantasy,
|
||||
Monospace,
|
||||
}
|
||||
type SpecifiedValue = ~[FontFamily];
|
||||
pub type SpecifiedValue = ~[FontFamily];
|
||||
/// <familiy-name>#
|
||||
/// <familiy-name> = <string> | [ <ident>+ ]
|
||||
/// 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 iter = input.skip_whitespace();
|
||||
macro_rules! add(
|
||||
($value: expr) => {
|
||||
{
|
||||
|
@ -232,11 +248,15 @@ mod font_family {
|
|||
}
|
||||
|
||||
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::*;
|
||||
enum SpecifiedValue {
|
||||
pub enum SpecifiedValue {
|
||||
Bolder,
|
||||
Lighther,
|
||||
Weight100,
|
||||
|
@ -250,16 +270,19 @@ mod font_weight {
|
|||
Weight900,
|
||||
}
|
||||
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
|
||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||
match one_component_value(input) {
|
||||
Some(&Ident(ref value)) => match to_ascii_lower(value.as_slice()).as_slice() {
|
||||
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 to_ascii_lower(value.as_slice()).as_slice() {
|
||||
"bold" => Some(Weight700),
|
||||
"normal" => Some(Weight400),
|
||||
"bolder" => Some(Bolder),
|
||||
"lighter" => Some(Lighther),
|
||||
_ => None,
|
||||
},
|
||||
Some(&Number(ref value)) => match value.int_value {
|
||||
&Number(ref value) => match value.int_value {
|
||||
Some(100) => Some(Weight100),
|
||||
Some(200) => Some(Weight200),
|
||||
Some(300) => Some(Weight300),
|
||||
|
@ -276,14 +299,16 @@ mod font_weight {
|
|||
}
|
||||
}
|
||||
|
||||
mod font_size {
|
||||
pub mod font_size {
|
||||
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>
|
||||
/// TODO: support <absolute-size> and <relative-size>
|
||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||
do one_component_value(input).chain(specified::LengthOrPercentage::parse_non_negative)
|
||||
.map_move |value| {
|
||||
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),
|
||||
|
@ -297,9 +322,9 @@ mod font_size {
|
|||
single_keyword!(text_align, "left" => Left, "right" => Right,
|
||||
"center" => Center, "justify" => Justify)
|
||||
|
||||
mod text_decoration {
|
||||
pub mod text_decoration {
|
||||
use super::*;
|
||||
struct SpecifiedValue {
|
||||
pub struct SpecifiedValue {
|
||||
underline: bool,
|
||||
overline: bool,
|
||||
line_through: bool,
|
||||
|
|
|
@ -6,8 +6,9 @@ use std::ascii::to_ascii_lower;
|
|||
use cssparser::*;
|
||||
use errors::{ErrorLoggerIterator, log_css_error};
|
||||
|
||||
pub mod longhands;
|
||||
pub mod common_types;
|
||||
pub mod longhands;
|
||||
pub mod shorthands;
|
||||
|
||||
|
||||
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