mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Parse all longhand properties that Servo seems to support.
This commit is contained in:
parent
c1b7e157b7
commit
9ea09f9d0d
5 changed files with 513 additions and 0 deletions
21
parsing_utils.rs
Normal file
21
parsing_utils.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* 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::to_ascii_lower;
|
||||
use cssparser::*;
|
||||
|
||||
|
||||
pub fn one_component_value<'a>(input: &'a [ComponentValue]) -> Option<&'a ComponentValue> {
|
||||
let mut iter = input.skip_whitespace();
|
||||
iter.next().filtered(|_| iter.next().is_none())
|
||||
}
|
||||
|
||||
|
||||
pub fn get_ident_lower(component_value: &ComponentValue) -> Option<~str> {
|
||||
match component_value {
|
||||
&Ident(ref value) => Some(to_ascii_lower(value.as_slice())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
138
properties/common_types.rs
Normal file
138
properties/common_types.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
/* 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 type Float = f64;
|
||||
pub type Integer = i64;
|
||||
|
||||
|
||||
pub mod specified {
|
||||
use std::ascii::{to_ascii_lower, eq_ignore_ascii_case};
|
||||
use cssparser::*;
|
||||
use super::{Integer, Float};
|
||||
|
||||
pub enum Length {
|
||||
Au(Integer), // application units
|
||||
Em(Float),
|
||||
Ex(Float),
|
||||
// Ch(Float),
|
||||
// Rem(Float),
|
||||
// Vw(Float),
|
||||
// Vh(Float),
|
||||
// Vmin(Float),
|
||||
// Vmax(Float),
|
||||
}
|
||||
static AU_PER_PX: Float = 60.;
|
||||
static AU_PER_IN: Float = AU_PER_PX * 96.;
|
||||
static AU_PER_CM: Float = AU_PER_IN / 2.54;
|
||||
static AU_PER_MM: Float = AU_PER_IN / 25.4;
|
||||
static AU_PER_PT: Float = AU_PER_IN / 72.;
|
||||
static AU_PER_PC: Float = AU_PER_PT * 12.;
|
||||
impl Length {
|
||||
#[inline]
|
||||
fn parse_internal(input: &ComponentValue, negative_ok: bool) -> Option<Length> {
|
||||
match input {
|
||||
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0.
|
||||
=> Length::parse_dimension(value.value, unit.as_slice()),
|
||||
&Number(ref value) if value.value == 0. => Some(Au(0)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn parse(input: &ComponentValue) -> Option<Length> {
|
||||
Length::parse_internal(input, /* negative_ok = */ true)
|
||||
}
|
||||
pub fn parse_non_negative(input: &ComponentValue) -> Option<Length> {
|
||||
Length::parse_internal(input, /* negative_ok = */ false)
|
||||
}
|
||||
pub fn parse_dimension(value: Float, unit: &str) -> Option<Length> {
|
||||
match to_ascii_lower(unit).as_slice() {
|
||||
"px" => Some(Length::from_px(value)),
|
||||
"in" => Some(Au((value * AU_PER_IN) as Integer)),
|
||||
"cm" => Some(Au((value * AU_PER_CM) as Integer)),
|
||||
"mm" => Some(Au((value * AU_PER_MM) as Integer)),
|
||||
"pt" => Some(Au((value * AU_PER_PT) as Integer)),
|
||||
"pc" => Some(Au((value * AU_PER_PC) as Integer)),
|
||||
"em" => Some(Em(value)),
|
||||
"ex" => Some(Ex(value)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn from_px(px_value: Float) -> Length {
|
||||
Au((px_value * AU_PER_PX) as Integer)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LengthOrPercentage {
|
||||
Length(Length),
|
||||
Percentage(Float),
|
||||
}
|
||||
impl LengthOrPercentage {
|
||||
fn parse_internal(input: &ComponentValue, negative_ok: bool)
|
||||
-> Option<LengthOrPercentage> {
|
||||
match input {
|
||||
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0.
|
||||
=> Length::parse_dimension(value.value, unit.as_slice()).map_move(Length),
|
||||
&ast::Percentage(ref value) if negative_ok || value.value >= 0.
|
||||
=> Some(Percentage(value.value)),
|
||||
&Number(ref value) if value.value == 0. => Some(Length(Au(0))),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentage> {
|
||||
LengthOrPercentage::parse_internal(input, /* negative_ok = */ true)
|
||||
}
|
||||
pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentage> {
|
||||
LengthOrPercentage::parse_internal(input, /* negative_ok = */ false)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LengthOrPercentageOrAuto {
|
||||
Length_(Length),
|
||||
Percentage_(Float),
|
||||
Auto,
|
||||
}
|
||||
impl LengthOrPercentageOrAuto {
|
||||
#[inline]
|
||||
fn parse_internal(input: &ComponentValue, negative_ok: bool)
|
||||
-> Option<LengthOrPercentageOrAuto> {
|
||||
match input {
|
||||
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0.
|
||||
=> Length::parse_dimension(value.value, unit.as_slice()).map_move(Length_),
|
||||
&ast::Percentage(ref value) if negative_ok || value.value >= 0.
|
||||
=> Some(Percentage_(value.value)),
|
||||
&Number(ref value) if value.value == 0. => Some(Length_(Au(0))),
|
||||
&Ident(ref value) if eq_ignore_ascii_case(value.as_slice(), "auto") => Some(Auto),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn parse(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> {
|
||||
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ true)
|
||||
}
|
||||
pub fn parse_non_negative(input: &ComponentValue) -> Option<LengthOrPercentageOrAuto> {
|
||||
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod computed {
|
||||
use super::*;
|
||||
struct Length(Integer); // in application units
|
||||
impl Length {
|
||||
fn times(self, factor: Float) -> Length {
|
||||
Length(((*self as Float) * factor) as Integer)
|
||||
}
|
||||
|
||||
pub fn compute(parent_font_size: Length, value: specified::Length) -> Length {
|
||||
match value {
|
||||
specified::Au(value) => Length(value),
|
||||
specified::Em(value) => parent_font_size.times(value),
|
||||
specified::Ex(value) => {
|
||||
let x_height = 0.5; // TODO: find that form the font
|
||||
parent_font_size.times(value * x_height)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
346
properties/longhands.rs
Normal file
346
properties/longhands.rs
Normal file
|
@ -0,0 +1,346 @@
|
|||
/* 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 std::ascii::{to_ascii_lower, eq_ignore_ascii_case};
|
||||
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;
|
||||
|
||||
|
||||
macro_rules! single_keyword(
|
||||
($property_name: ident, $( $lower_case_keyword_string: pat => $variant: ident ),+ ) => {
|
||||
mod $property_name {
|
||||
use super::*;
|
||||
enum SpecifiedValue {
|
||||
$( $variant ),+
|
||||
}
|
||||
fn parse(input: &[ComponentValue]) -> option::Option<SpecifiedValue> {
|
||||
do one_component_value(input).chain(get_ident_lower).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) => {
|
||||
mod $property_name {
|
||||
use super::*;
|
||||
type SpecifiedValue = $type_;
|
||||
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_non_negative)
|
||||
single_type!(margin_right, specified::LengthOrPercentageOrAuto,
|
||||
specified::LengthOrPercentageOrAuto::parse_non_negative)
|
||||
single_type!(margin_bottom, specified::LengthOrPercentageOrAuto,
|
||||
specified::LengthOrPercentageOrAuto::parse_non_negative)
|
||||
single_type!(margin_left, specified::LengthOrPercentageOrAuto,
|
||||
specified::LengthOrPercentageOrAuto::parse_non_negative)
|
||||
|
||||
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)
|
||||
|
||||
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() {
|
||||
"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::Length, specified::Length::parse_non_negative)
|
||||
single_type!(height, specified::Length, specified::Length::parse_non_negative)
|
||||
|
||||
mod line_height {
|
||||
use super::*;
|
||||
enum SpecifiedValue {
|
||||
Normal,
|
||||
Length(specified::Length),
|
||||
Percentage(common_types::Float),
|
||||
Number(common_types::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.
|
||||
=> Some(Number(value.value)),
|
||||
Some(&ast::Percentage(ref value)) if value.value >= 0.
|
||||
=> Some(Percentage(value.value)),
|
||||
Some(&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")
|
||||
=> 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
|
||||
|
||||
mod font_family {
|
||||
use super::*;
|
||||
enum FontFamily {
|
||||
FamilyName(~str),
|
||||
// Generic
|
||||
Serif,
|
||||
SansSerif,
|
||||
Cursive,
|
||||
Fantasy,
|
||||
Monospace,
|
||||
}
|
||||
type SpecifiedValue = ~[FontFamily];
|
||||
/// <familiy-name>#
|
||||
/// <familiy-name> = <string> | [ <ident>+ ]
|
||||
/// TODO: <generic-familiy>
|
||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||
let mut result = ~[];
|
||||
let mut iter = input.skip_whitespace();
|
||||
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 to_ascii_lower(value).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, "normal" => Normal, "small-caps" => SmallCaps)
|
||||
|
||||
mod font_weight {
|
||||
use super::*;
|
||||
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
|
||||
fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
|
||||
match one_component_value(input) {
|
||||
Some(&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 {
|
||||
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(Weight800),
|
||||
_ => None,
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod font_size {
|
||||
use super::*;
|
||||
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| {
|
||||
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)
|
||||
|
||||
mod text_decoration {
|
||||
use super::*;
|
||||
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;
|
||||
macro_rules! found(
|
||||
($flag: expr) => (
|
||||
{
|
||||
if $flag { return None }
|
||||
empty = false;
|
||||
$flag = 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
|
|
@ -6,6 +6,9 @@ use std::ascii::to_ascii_lower;
|
|||
use cssparser::*;
|
||||
use errors::{ErrorLoggerIterator, log_css_error};
|
||||
|
||||
pub mod longhands;
|
||||
pub mod common_types;
|
||||
|
||||
|
||||
pub struct PropertyDeclarationBlock {
|
||||
important: ~[PropertyDeclaration],
|
||||
|
@ -36,5 +39,9 @@ pub fn parse_property_declaration_list(input: ~[Node]) -> PropertyDeclarationBlo
|
|||
|
||||
fn parse_one_property_declaration(name: &str, value: ~[ComponentValue],
|
||||
result_list: &mut ~[PropertyDeclaration]) -> bool {
|
||||
|
||||
let _ = name;
|
||||
let _ = value;
|
||||
let _ = result_list;
|
||||
false
|
||||
}
|
|
@ -14,3 +14,4 @@ pub mod selectors;
|
|||
pub mod properties;
|
||||
pub mod namespaces;
|
||||
pub mod media_queries;
|
||||
pub mod parsing_utils;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue