This commit is contained in:
Simon Sapin 2013-09-30 19:21:27 +01:00
commit 226ccf7e72
7 changed files with 7066 additions and 43 deletions

View file

@ -9,3 +9,6 @@ pub mod properties;
pub mod namespaces; pub mod namespaces;
pub mod media_queries; pub mod media_queries;
pub mod parsing_utils; pub mod parsing_utils;
#[cfg(test)]
mod tests;

View file

@ -12,6 +12,7 @@ pub mod specified {
use super::{Integer, Float}; use super::{Integer, Float};
pub use CSSColor = cssparser::Color; pub use CSSColor = cssparser::Color;
#[deriving(Clone)]
pub enum Length { pub enum Length {
Au(Integer), // application units Au(Integer), // application units
Em(Float), Em(Float),
@ -65,6 +66,7 @@ pub mod specified {
} }
} }
#[deriving(Clone)]
pub enum LengthOrPercentage { pub enum LengthOrPercentage {
LP_Length(Length), LP_Length(Length),
LP_Percentage(Float), LP_Percentage(Float),
@ -91,6 +93,7 @@ pub mod specified {
} }
} }
#[deriving(Clone)]
pub enum LengthOrPercentageOrAuto { pub enum LengthOrPercentageOrAuto {
LPA_Length(Length), LPA_Length(Length),
LPA_Percentage(Float), LPA_Percentage(Float),
@ -123,19 +126,23 @@ pub mod specified {
pub mod computed { pub mod computed {
use cssparser; use cssparser;
pub use CSSColor = cssparser::Color; pub use CSSColor = cssparser::Color;
pub use compute_CSSColor = std::util::id; pub use compute_CSSColor = super::super::longhands::computed_as_specified;
use super::*; use super::*;
use super::super::longhands::font_weight; use super::super::longhands;
pub struct Context { pub struct Context {
current_color: cssparser::RGBA, current_color: cssparser::RGBA,
font_size: Length,
font_weight: longhands::font_weight::ComputedValue,
position: longhands::position::SpecifiedValue,
float: longhands::float::SpecifiedValue,
is_root_element: bool,
has_border_top: bool, has_border_top: bool,
has_border_right: bool, has_border_right: bool,
has_border_bottom: bool, has_border_bottom: bool,
has_border_left: bool, has_border_left: bool,
font_size: Length,
font_weight: font_weight::ComputedValue,
// TODO, as needed: root font size, viewport size, etc. // TODO, as needed: root font size, viewport size, etc.
} }
#[deriving(Clone)]
pub struct Length(Integer); // in application units pub struct Length(Integer); // in application units
impl Length { impl Length {
pub fn times(self, factor: Float) -> Length { pub fn times(self, factor: Float) -> Length {
@ -154,6 +161,7 @@ pub mod computed {
} }
} }
#[deriving(Clone)]
pub enum LengthOrPercentage { pub enum LengthOrPercentage {
LP_Length(Length), LP_Length(Length),
LP_Percentage(Float), LP_Percentage(Float),
@ -166,6 +174,7 @@ pub mod computed {
} }
} }
#[deriving(Clone)]
pub enum LengthOrPercentageOrAuto { pub enum LengthOrPercentageOrAuto {
LPA_Length(Length), LPA_Length(Length),
LPA_Percentage(Float), LPA_Percentage(Float),

View file

@ -23,20 +23,30 @@ def to_rust_ident(name):
return name return name
class Longhand(object): class Longhand(object):
def __init__(self, name): def __init__(self, name, is_inherited):
self.name = name self.name = name
self.ident = to_rust_ident(name) self.ident = to_rust_ident(name)
self.is_inherited = is_inherited
class Shorthand(object): class Shorthand(object):
def __init__(self, name, sub_properties): def __init__(self, name, sub_properties):
self.name = name self.name = name
self.ident = to_rust_ident(name) self.ident = to_rust_ident(name)
self.sub_properties = [Longhand(s) for s in sub_properties] self.sub_properties = [LONGHANDS_BY_NAME[s] for s in sub_properties]
LONGHANDS_PER_STYLE_STRUCT = []
THIS_STYLE_STRUCT_LONGHANDS = None
LONGHANDS = [] LONGHANDS = []
LONGHANDS_BY_NAME = {}
SHORTHANDS = [] SHORTHANDS = []
INHERITED = set()
def new_style_struct(name):
longhands = []
LONGHANDS_PER_STYLE_STRUCT.append((name, longhands))
global THIS_STYLE_STRUCT_LONGHANDS
THIS_STYLE_STRUCT_LONGHANDS = longhands
return ""
%> %>
@ -44,21 +54,42 @@ pub mod longhands {
pub use super::*; pub use super::*;
pub use std; pub use std;
<%def name="longhand(name, inherited=False, no_super=False)"> pub fn computed_as_specified<T>(value: T, _context: &computed::Context) -> T { value }
<%def name="raw_longhand(name, inherited=False, no_super=False)">
<% <%
property = Longhand(name) property = Longhand(name, inherited)
THIS_STYLE_STRUCT_LONGHANDS.append(property)
LONGHANDS.append(property) LONGHANDS.append(property)
if inherited: LONGHANDS_BY_NAME[name] = property
INHERITED.add(name)
%> %>
pub mod ${property.ident} { pub mod ${property.ident} {
% if not no_super: % if not no_super:
use super::*; use super::*;
% endif % endif
${caller.body()} ${caller.body()}
pub fn parse_declared(input: &[ComponentValue])
-> Option<DeclaredValue<SpecifiedValue>> {
match CSSWideKeyword::parse(input) {
Some(Left(keyword)) => Some(CSSWideKeyword(keyword)),
Some(Right(Unset)) => Some(CSSWideKeyword(${
"Inherit" if inherited else "Initial"})),
None => parse_specified(input),
}
}
} }
</%def> </%def>
<%def name="longhand(name, inherited=False, no_super=False)">
<%self:raw_longhand name="${name}" inherited="${inherited}">
${caller.body()}
pub fn parse_specified(input: &[ComponentValue])
-> Option<DeclaredValue<SpecifiedValue>> {
parse(input).map_move(super::SpecifiedValue)
}
</%self:raw_longhand>
</%def>
<%def name="single_component_value(name, inherited=False)"> <%def name="single_component_value(name, inherited=False)">
<%self:longhand name="${name}" inherited="${inherited}"> <%self:longhand name="${name}" inherited="${inherited}">
${caller.body()} ${caller.body()}
@ -71,7 +102,8 @@ pub mod longhands {
<%def name="single_keyword(name, values, inherited=False)"> <%def name="single_keyword(name, values, inherited=False)">
<%self:single_component_value name="${name}" inherited="${inherited}"> <%self:single_component_value name="${name}" inherited="${inherited}">
// The computed value is the same as the specified value. // The computed value is the same as the specified value.
pub use to_computed_value = std::util::id; pub use to_computed_value = super::computed_as_specified;
#[deriving(Clone)]
pub enum SpecifiedValue { pub enum SpecifiedValue {
% for value in values.split(): % for value in values.split():
${to_rust_ident(value)}, ${to_rust_ident(value)},
@ -109,23 +141,29 @@ pub mod longhands {
// CSS 2.1, Section 8 - Box model // CSS 2.1, Section 8 - Box model
${new_style_struct("Margin")}
% for side in ["top", "right", "bottom", "left"]: % for side in ["top", "right", "bottom", "left"]:
${predefined_type("margin-" + side, "LengthOrPercentageOrAuto", ${predefined_type("margin-" + side, "LengthOrPercentageOrAuto",
"computed::LPA_Length(computed::Length(0))")} "computed::LPA_Length(computed::Length(0))")}
% endfor % endfor
${new_style_struct("Padding")}
% for side in ["top", "right", "bottom", "left"]: % for side in ["top", "right", "bottom", "left"]:
${predefined_type("padding-" + side, "LengthOrPercentage", ${predefined_type("padding-" + side, "LengthOrPercentage",
"computed::LP_Length(computed::Length(0))", "computed::LP_Length(computed::Length(0))",
"parse_non_negative")} "parse_non_negative")}
% endfor % endfor
${new_style_struct("Border")}
% for side in ["top", "right", "bottom", "left"]: % for side in ["top", "right", "bottom", "left"]:
${predefined_type("border-%s-color" % side, "CSSColor", "CurrentColor")} ${predefined_type("border-%s-color" % side, "CSSColor", "CurrentColor")}
% endfor % endfor
// dotted dashed double groove ridge insed outset hidden // dotted dashed double groove ridge insed outset
${single_keyword("border-top-style", "none solid")} ${single_keyword("border-top-style", "none solid hidden")}
% for side in ["right", "bottom", "left"]: % for side in ["right", "bottom", "left"]:
<%self:longhand name="border-${side}-style", no_super="True"> <%self:longhand name="border-${side}-style", no_super="True">
pub use super::border_top_style::*; pub use super::border_top_style::*;
@ -165,6 +203,8 @@ pub mod longhands {
// CSS 2.1, Section 9 - Visual formatting model // CSS 2.1, Section 9 - Visual formatting model
${new_style_struct("Box")}
// TODO: don't parse values we don't support // TODO: don't parse values we don't support
${single_keyword("display", ${single_keyword("display",
"inline block list-item inline-block none " "inline block list-item inline-block none "
@ -186,6 +226,7 @@ pub mod longhands {
"parse_non_negative")} "parse_non_negative")}
<%self:single_component_value name="line-height"> <%self:single_component_value name="line-height">
#[deriving(Clone)]
pub enum SpecifiedValue { pub enum SpecifiedValue {
SpecifiedNormal, SpecifiedNormal,
SpecifiedLength(specified::Length), SpecifiedLength(specified::Length),
@ -207,6 +248,7 @@ pub mod longhands {
_ => None, _ => None,
} }
} }
#[deriving(Clone)]
pub enum ComputedValue { pub enum ComputedValue {
Normal, Normal,
Length(computed::Length), Length(computed::Length),
@ -231,16 +273,37 @@ pub mod longhands {
// CSS 2.1, Section 14 - Colors and Backgrounds // CSS 2.1, Section 14 - Colors and Backgrounds
${new_style_struct("Background")}
${predefined_type("background-color", "CSSColor", ${predefined_type("background-color", "CSSColor",
"RGBA(RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")} "RGBA(RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")}
${predefined_type("color", "CSSColor",
"RGBA(RGBA { red: 0., green: 0., blue: 0., alpha: 1. }) /* black */",
inherited=True)} ${new_style_struct("Color")}
<%self:raw_longhand name="color" inherited="True">
pub use to_computed_value = super::computed_as_specified;
pub type SpecifiedValue = RGBA;
pub type ComputedValue = SpecifiedValue;
#[inline] pub fn get_initial_value() -> ComputedValue {
RGBA { red: 0., green: 0., blue: 0., alpha: 1. } /* black */
}
pub fn parse_specified(input: &[ComponentValue]) -> Option<DeclaredValue<SpecifiedValue>> {
match one_component_value(input).chain(Color::parse) {
Some(RGBA(rgba)) => Some(SpecifiedValue(rgba)),
Some(CurrentColor) => Some(CSSWideKeyword(Inherit)),
None => None,
}
}
</%self:raw_longhand>
// CSS 2.1, Section 15 - Fonts // CSS 2.1, Section 15 - Fonts
${new_style_struct("Font")}
<%self:longhand name="font-family" inherited="True"> <%self:longhand name="font-family" inherited="True">
pub use to_computed_value = std::util::id; pub use to_computed_value = super::computed_as_specified;
#[deriving(Clone)]
enum FontFamily { enum FontFamily {
FamilyName(~str), FamilyName(~str),
// Generic // Generic
@ -316,6 +379,7 @@ pub mod longhands {
${single_keyword("font-variant", "normal", inherited=True)} // Add small-caps when supported ${single_keyword("font-variant", "normal", inherited=True)} // Add small-caps when supported
<%self:single_component_value name="font-weight" inherited="True"> <%self:single_component_value name="font-weight" inherited="True">
#[deriving(Clone)]
pub enum SpecifiedValue { pub enum SpecifiedValue {
Bolder, Bolder,
Lighther, Lighther,
@ -348,6 +412,7 @@ pub mod longhands {
_ => None _ => None
} }
} }
#[deriving(Clone)]
pub enum ComputedValue { pub enum ComputedValue {
% for weight in range(100, 901, 100): % for weight in range(100, 901, 100):
Weight${weight}, Weight${weight},
@ -407,11 +472,14 @@ pub mod longhands {
// CSS 2.1, Section 16 - Text // CSS 2.1, Section 16 - Text
${new_style_struct("Text")}
// TODO: initial value should be 'start' (CSS Text Level 3, direction-dependent.) // TODO: initial value should be 'start' (CSS Text Level 3, direction-dependent.)
${single_keyword("text-align", "left right center justify", inherited=True)} ${single_keyword("text-align", "left right center justify", inherited=True)}
<%self:longhand name="text-decoration"> <%self:longhand name="text-decoration">
pub use to_computed_value = std::util::id; pub use to_computed_value = super::computed_as_specified;
#[deriving(Clone)]
pub struct SpecifiedValue { pub struct SpecifiedValue {
underline: bool, underline: bool,
overline: bool, overline: bool,
@ -611,25 +679,29 @@ pub fn parse_property_declaration_list(input: ~[Node]) -> PropertyDeclarationBlo
} }
#[deriving(Clone)]
pub enum CSSWideKeyword { pub enum CSSWideKeyword {
Initial, Initial,
Inherit, Inherit,
Unset,
} }
struct Unset;
impl CSSWideKeyword { impl CSSWideKeyword {
pub fn parse(input: &[ComponentValue]) -> Option<CSSWideKeyword> { pub fn parse(input: &[ComponentValue]) -> Option<Either<CSSWideKeyword, Unset>> {
do one_component_value(input).chain(get_ident_lower).chain |keyword| { do one_component_value(input).chain(get_ident_lower).chain |keyword| {
match keyword.as_slice() { match keyword.as_slice() {
"initial" => Some(Initial), "initial" => Some(Left(Initial)),
"inherit" => Some(Inherit), "inherit" => Some(Left(Inherit)),
"unset" => Some(Unset), "unset" => Some(Right(Unset)),
_ => None _ => None
} }
} }
} }
} }
#[deriving(Clone)]
pub enum DeclaredValue<T> { pub enum DeclaredValue<T> {
SpecifiedValue(T), SpecifiedValue(T),
CSSWideKeyword(CSSWideKeyword), CSSWideKeyword(CSSWideKeyword),
@ -647,24 +719,29 @@ impl PropertyDeclaration {
match name.to_ascii_lower().as_slice() { match name.to_ascii_lower().as_slice() {
% for property in LONGHANDS: % for property in LONGHANDS:
"${property.name}" => result_list.push(${property.ident}_declaration( "${property.name}" => result_list.push(${property.ident}_declaration(
match CSSWideKeyword::parse(value) { match longhands::${property.ident}::parse_declared(value) {
Some(keyword) => CSSWideKeyword(keyword), Some(value) => value,
None => match longhands::${property.ident}::parse(value) { None => return false,
Some(value) => SpecifiedValue(value),
None => return false,
}
} }
)), )),
% endfor % endfor
% for shorthand in SHORTHANDS: % for shorthand in SHORTHANDS:
"${shorthand.name}" => match CSSWideKeyword::parse(value) { "${shorthand.name}" => match CSSWideKeyword::parse(value) {
Some(keyword) => { Some(Left(keyword)) => {
% for sub_property in shorthand.sub_properties: % for sub_property in shorthand.sub_properties:
result_list.push(${sub_property.ident}_declaration( result_list.push(${sub_property.ident}_declaration(
CSSWideKeyword(keyword) CSSWideKeyword(keyword)
)); ));
% endfor % endfor
}, },
Some(Right(Unset)) => {
% for sub_property in shorthand.sub_properties:
result_list.push(${sub_property.ident}_declaration(
CSSWideKeyword(${
"Inherit" if sub_property.is_inherited else "Initial"})
));
% endfor
},
None => match shorthands::${shorthand.ident}::parse(value) { None => match shorthands::${shorthand.ident}::parse(value) {
Some(result) => { Some(result) => {
% for sub_property in shorthand.sub_properties: % for sub_property in shorthand.sub_properties:
@ -685,3 +762,121 @@ impl PropertyDeclaration {
true true
} }
} }
pub mod style_structs {
use super::longhands;
% for name, longhands in LONGHANDS_PER_STYLE_STRUCT:
pub struct ${name} {
% for longhand in longhands:
${longhand.ident}: longhands::${longhand.ident}::ComputedValue,
% endfor
}
% endfor
}
pub struct ComputedValues {
% for name, longhands in LONGHANDS_PER_STYLE_STRUCT:
${name}: style_structs::${name},
% endfor
}
#[inline]
fn get_initial_values() -> ComputedValues {
ComputedValues {
% for style_struct, longhands in LONGHANDS_PER_STYLE_STRUCT:
${style_struct}: style_structs::${style_struct} {
% for longhand in longhands:
${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
% endfor
},
% endfor
}
}
pub fn cascade(applicable_declarations: &[PropertyDeclaration],
parent_style: Option< &ComputedValues>)
-> ComputedValues {
let initial_keep_alive;
let (parent_style, is_root_element) = match parent_style {
Some(s) => (s, false),
None => {
initial_keep_alive = ~get_initial_values();
(&*initial_keep_alive, true)
}
};
struct AllDeclaredValues {
% for property in LONGHANDS:
${property.ident}: DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
% endfor
}
let mut specified = AllDeclaredValues {
% for property in LONGHANDS:
${property.ident}: CSSWideKeyword(${
"Inherit" if property.is_inherited else "Initial"}),
% endfor
};
for declaration in applicable_declarations.iter() {
match declaration {
% for property in LONGHANDS:
&${property.ident}_declaration(ref value) => {
// Overwrite earlier declarations.
specified.${property.ident} = (*value).clone() // TODO: can we avoid a copy?
}
% endfor
}
}
// This assumes that the computed and specified values have the same Rust type.
macro_rules! get_specified(
($style_struct: ident, $property: ident) => {
match specified.$property {
SpecifiedValue(value) => value,
CSSWideKeyword(Initial) => longhands::$property::get_initial_value(),
CSSWideKeyword(Inherit) => parent_style.$style_struct.$property.clone(),
}
};
)
macro_rules! has_border(
($property: ident) => {
match get_specified!(Border, $property) {
longhands::border_top_style::none
| longhands::border_top_style::hidden => false,
_ => true,
}
};
)
let context = &mut computed::Context {
current_color: get_specified!(Color, color),
font_size: parent_style.Font.font_size,
font_weight: parent_style.Font.font_weight,
position: get_specified!(Box, position),
float: get_specified!(Box, float),
is_root_element: is_root_element,
has_border_top: has_border!(border_top_style),
has_border_right: has_border!(border_right_style),
has_border_bottom: has_border!(border_bottom_style),
has_border_left: has_border!(border_left_style),
};
macro_rules! get_computed(
($style_struct: ident, $property: ident) => {
match specified.$property {
SpecifiedValue(ref value)
// TODO: avoid a copy?
=> longhands::$property::to_computed_value(value.clone(), context),
CSSWideKeyword(Initial) => longhands::$property::get_initial_value(),
CSSWideKeyword(Inherit) => parent_style.$style_struct.$property.clone(),
}
};
)
context.font_size = get_computed!(Font, font_size);
ComputedValues {
% for style_struct, longhands in LONGHANDS_PER_STYLE_STRUCT:
${style_struct}: style_structs::${style_struct} {
% for longhand in longhands:
${longhand.ident}: get_computed!(${style_struct}, ${longhand.ident}),
% endfor
},
% endfor
}
}

View file

@ -214,8 +214,8 @@ fn parse_simple_selectors(iter: &mut Iter, namespaces: &NamespaceMap)
// None means invalid selector // None means invalid selector
// Some(None) means no type selector // Some(None) means no type selector.
// Some(Some([...])) is a type selector. Might be empty for *|* // Some(Some(~[...])) is a type selector. Might be empty for *|*
fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap) fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap)
-> Option<Option<~[SimpleSelector]>> { -> Option<Option<~[SimpleSelector]>> {
skip_whitespace(iter); skip_whitespace(iter);
@ -242,6 +242,10 @@ fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap)
// Parse a simple selector other than a type selector // Parse a simple selector other than a type selector
// None means invalid selector
// Some(None) means not a simple name
// Some(Some(Left(s)) is a simple selector
// Some(Some(Right(p)) is a pseudo-element
fn parse_one_simple_selector(iter: &mut Iter, namespaces: &NamespaceMap, inside_negation: bool) fn parse_one_simple_selector(iter: &mut Iter, namespaces: &NamespaceMap, inside_negation: bool)
-> Option<Option<Either<SimpleSelector, PseudoElement>>> { -> Option<Option<Either<SimpleSelector, PseudoElement>>> {
match iter.peek() { match iter.peek() {
@ -292,6 +296,7 @@ fn parse_one_simple_selector(iter: &mut Iter, namespaces: &NamespaceMap, inside_
} }
} }
// None means invalid selector // None means invalid selector
// Some(None) means not a qualified name // Some(None) means not a qualified name
// Some(Some((None, None)) means *|* // Some(Some((None, None)) means *|*
@ -304,16 +309,14 @@ fn parse_qualified_name(iter: &mut Iter, allow_universal: bool, namespaces: &Nam
#[inline] #[inline]
fn default_namespace(namespaces: &NamespaceMap, local_name: Option<~str>) fn default_namespace(namespaces: &NamespaceMap, local_name: Option<~str>)
-> Option<Option<(Option<~str>, Option<~str>)>> { -> Option<Option<(Option<~str>, Option<~str>)>> {
match namespaces.default { Some(Some((namespaces.default.map(|url| url.to_owned()), local_name)))
None => Some(Some((None, local_name))),
Some(ref url) => Some(Some((Some(url.to_owned()), local_name))),
}
} }
#[inline] #[inline]
fn explicit_namespace(iter: &mut Iter, allow_universal: bool, namespace_url: Option<~str>) fn explicit_namespace(iter: &mut Iter, allow_universal: bool, namespace_url: Option<~str>)
-> Option<Option<(Option<~str>, Option<~str>)>> { -> Option<Option<(Option<~str>, Option<~str>)>> {
assert!(iter.next() == Some(Delim('|'))); assert!(iter.next() == Some(Delim('|')),
"Implementation error, this should not happen.");
match iter.peek() { match iter.peek() {
Some(&Delim('*')) if allow_universal => { Some(&Delim('*')) if allow_universal => {
iter.next(); iter.next();
@ -331,24 +334,24 @@ fn parse_qualified_name(iter: &mut Iter, allow_universal: bool, namespaces: &Nam
Some(&Ident(_)) => { Some(&Ident(_)) => {
let value = get_next_ident(iter); let value = get_next_ident(iter);
match iter.peek() { match iter.peek() {
Some(&Delim('|')) => default_namespace(namespaces, Some(value)), Some(&Delim('|')) => {
_ => {
let namespace_url = match namespaces.prefix_map.find(&value) { let namespace_url = match namespaces.prefix_map.find(&value) {
None => return None, // Undeclared namespace prefix: invalid selector None => return None, // Undeclared namespace prefix: invalid selector
Some(ref url) => url.to_owned(), Some(ref url) => url.to_owned(),
}; };
explicit_namespace(iter, allow_universal, Some(namespace_url)) explicit_namespace(iter, allow_universal, Some(namespace_url))
}, },
_ => default_namespace(namespaces, Some(value)),
} }
}, },
Some(&Delim('*')) => { Some(&Delim('*')) => {
iter.next(); // Consume '*' iter.next(); // Consume '*'
match iter.peek() { match iter.peek() {
Some(&Delim('|')) => { Some(&Delim('|')) => explicit_namespace(iter, allow_universal, None),
_ => {
if allow_universal { default_namespace(namespaces, None) } if allow_universal { default_namespace(namespaces, None) }
else { None } else { None }
}, },
_ => explicit_namespace(iter, allow_universal, None),
} }
}, },
Some(&Delim('|')) => explicit_namespace(iter, allow_universal, Some(~"")), Some(&Delim('|')) => explicit_namespace(iter, allow_universal, Some(~"")),

View file

@ -31,7 +31,7 @@ pub struct StyleRule {
} }
fn parse_stylesheet(css: &str) -> Stylesheet { pub fn parse_stylesheet(css: &str) -> Stylesheet {
static STATE_CHARSET: uint = 1; static STATE_CHARSET: uint = 1;
static STATE_IMPORTS: uint = 2; static STATE_IMPORTS: uint = 2;
static STATE_NAMESPACES: uint = 3; static STATE_NAMESPACES: uint = 3;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
use super::stylesheets::parse_stylesheet;
#[test]
fn test_bootstrap() {
// Test that parsing bootstrap does not trigger an assertion or otherwise fail.
let stylesheet = parse_stylesheet(include_str!("bootstrap-v3.0.0.css"));
assert!(stylesheet.rules.len() > 100); // This depends on whet selectors are supported.
}