Move style crate unit tests into the unit_tests crate.

This commit is contained in:
Simon Sapin 2015-04-07 18:13:50 +02:00
parent e27c967f81
commit e5466a34c5
9 changed files with 593 additions and 556 deletions

View file

@ -926,11 +926,16 @@ name = "unit_tests"
version = "0.0.1"
dependencies = [
"cookie 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.2.0 (git+https://github.com/servo/rust-cssparser)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gfx 0.0.1",
"net 0.0.1",
"net_traits 0.0.1",
"profile 0.0.1",
"selectors 0.1.0 (git+https://github.com/servo/rust-selectors)",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)",
"string_cache_plugin 0.0.0 (git+https://github.com/servo/string-cache)",
"style 0.0.1",
"url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1",
]

View file

@ -13,7 +13,7 @@ use values::specified;
#[derive(Debug, PartialEq)]
pub struct MediaQueryList {
media_queries: Vec<MediaQuery>
pub media_queries: Vec<MediaQuery>
}
#[derive(PartialEq, Eq, Copy, Debug)]
@ -71,9 +71,9 @@ pub enum Qualifier {
#[derive(Debug, PartialEq)]
pub struct MediaQuery {
qualifier: Option<Qualifier>,
media_type: MediaQueryType,
expressions: Vec<Expression>,
pub qualifier: Option<Qualifier>,
pub media_type: MediaQueryType,
pub expressions: Vec<Expression>,
}
impl MediaQuery {
@ -230,415 +230,5 @@ impl MediaQueryList {
#[cfg(test)]
mod tests {
use geom::size::TypedSize2D;
use util::geometry::Au;
use stylesheets::{iter_stylesheet_media_rules, iter_stylesheet_style_rules, Stylesheet};
use stylesheets::Origin;
use super::*;
use url::Url;
use values::specified;
use std::borrow::ToOwned;
fn test_media_rule<F>(css: &str, callback: F) where F: Fn(&MediaQueryList, &str) {
let url = Url::parse("http://localhost").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::Author);
let mut rule_count: int = 0;
iter_stylesheet_media_rules(&stylesheet, |rule| {
rule_count += 1;
callback(&rule.media_queries, css);
});
assert!(rule_count > 0);
}
fn media_query_test(device: &Device, css: &str, expected_rule_count: int) {
let url = Url::parse("http://localhost").unwrap();
let ss = Stylesheet::from_str(css, url, Origin::Author);
let mut rule_count: int = 0;
iter_stylesheet_style_rules(&ss, device, |_| rule_count += 1);
assert!(rule_count == expected_rule_count, css.to_owned());
}
#[test]
fn test_mq_empty() {
test_media_rule("@media { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_screen() {
test_media_rule("@media screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_print() {
test_media_rule("@media print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_unknown() {
test_media_rule("@media fridge { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only glass { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not wood { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_all() {
test_media_rule("@media all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_or() {
test_media_rule("@media screen, print { }", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_default_expressions() {
test_media_rule("@media (min-width: 100px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media (max-width: 43px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_expressions() {
test_media_rule("@media screen and (min-width: 100px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media print and (max-width: 43px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media fridge and (max-width: 52px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(52))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_multiple_expressions() {
test_media_rule("@media (min-width: 100px) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
match q.expressions[1] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media not screen and (min-width: 100px) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
match q.expressions[1] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_malformed_expressions() {
test_media_rule("@media (min-width: 100blah) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen and (height: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media (min-width: 30em foo bar) {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not (min-width: 300px) {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media , {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
let q = &list.media_queries[1];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen 4px, print {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q0.media_type == MediaQueryType::All, css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen, {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q1.media_type == MediaQueryType::All, css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_matching_simple() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media not all { a { color: red; } }", 0);
media_query_test(&device, "@media not screen { a { color: red; } }", 0);
media_query_test(&device, "@media not print { a { color: red; } }", 1);
media_query_test(&device, "@media unknown { a { color: red; } }", 0);
media_query_test(&device, "@media not unknown { a { color: red; } }", 1);
media_query_test(&device, "@media { a { color: red; } }", 1);
media_query_test(&device, "@media screen { a { color: red; } }", 1);
media_query_test(&device, "@media print { a { color: red; } }", 0);
}
#[test]
fn test_matching_width() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 50px) { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 150px) { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 50px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 150px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 50px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 150px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media (max-width: 50px) { a { color: red; } }", 0);
media_query_test(&device, "@media (max-width: 150px) { a { color: red; } }", 0);
media_query_test(&device, "@media (max-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 3.1em) and (max-width: 6em) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 16em) and (max-width: 19.75em) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 3em) and (max-width: 250px) { a { color: red; } }", 0);
}
#[test]
fn test_matching_invalid() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media fridge { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (height: 100px) { a { color: red; } }", 0);
media_query_test(&device, "@media not print and (width: 100) { a { color: red; } }", 0);
}
}

View file

@ -4803,7 +4803,7 @@ impl ComputedValues {
/// Return a WritingMode bitflags from the relevant CSS properties.
fn get_writing_mode(inheritedbox_style: &style_structs::InheritedBox) -> WritingMode {
pub fn get_writing_mode(inheritedbox_style: &style_structs::InheritedBox) -> WritingMode {
use util::logical_geometry;
let mut flags = WritingMode::empty();
match inheritedbox_style.direction {
@ -4839,7 +4839,7 @@ fn get_writing_mode(inheritedbox_style: &style_structs::InheritedBox) -> Writing
/// The initial values for all style structs as defined by the specification.
lazy_static! {
static ref INITIAL_VALUES: ComputedValues = ComputedValues {
pub static ref INITIAL_VALUES: ComputedValues = ComputedValues {
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
% for longhand in style_struct.longhands:
@ -4857,11 +4857,6 @@ lazy_static! {
}
#[test]
fn initial_writing_mode_is_empty() {
assert_eq!(get_writing_mode(INITIAL_VALUES.get_inheritedbox()), WritingMode::empty())
}
/// Fast path for the function below. Only computes new inherited styles.
#[allow(unused_mut)]
fn cascade_with_cached_declarations(

View file

@ -31,7 +31,7 @@ pub enum Origin {
pub struct Stylesheet {
/// List of rules in the order they were found (important for
/// cascading order)
rules: Vec<CSSRule>,
pub rules: Vec<CSSRule>,
pub origin: Origin,
}
@ -322,136 +322,3 @@ pub fn iter_font_face_rules<F>(stylesheet: &Stylesheet, device: &Device, callbac
where F: Fn(&Atom, &Source) {
iter_font_face_rules_inner(&stylesheet.rules, device, callback)
}
#[test]
fn test_parse_stylesheet() {
use std::sync::Arc;
use cssparser;
use selectors::parser::*;
use string_cache::Atom;
use properties::{PropertyDeclaration, DeclaredValue, longhands};
use std::borrow::ToOwned;
let css = r"
@namespace url(http://www.w3.org/1999/xhtml);
/* FIXME: only if scripting is enabled */
input[type=hidden i] { display: none !important; }
html , body /**/ { display: block; }
#d1 > .ok { background: blue; }
";
let url = Url::parse("about::test").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent);
assert_eq!(stylesheet, Stylesheet {
origin: Origin::UserAgent,
rules: vec![
CSSRule::Namespace(None, ns!(HTML)),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(input),
lower_name: atom!(input),
}),
SimpleSelector::AttrEqual(AttrSelector {
name: atom!(type),
lower_name: atom!(type),
namespace: NamespaceConstraint::Specific(ns!("")),
}, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (1 << 10) + (1 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![]),
important: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
longhands::display::SpecifiedValue::none)),
]),
},
}),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(html),
lower_name: atom!(html),
}),
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0),
},
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(body),
lower_name: atom!(body),
}),
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
longhands::display::SpecifiedValue::block)),
]),
important: Arc::new(vec![]),
},
}),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Class(Atom::from_slice("ok")),
],
next: Some((box CompoundSelector {
simple_selectors: vec![
SimpleSelector::ID(Atom::from_slice("d1")),
],
next: None,
}, Combinator::Child)),
}),
pseudo_element: None,
specificity: (1 << 20) + (1 << 10) + (0 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
PropertyDeclaration::BackgroundColor(DeclaredValue::SpecifiedValue(
longhands::background_color::SpecifiedValue {
authored: Some("blue".to_owned()),
parsed: cssparser::Color::RGBA(cssparser::RGBA {
red: 0., green: 0., blue: 1., alpha: 1.
}),
}
)),
]),
important: Arc::new(vec![]),
},
}),
],
});
}

View file

@ -23,9 +23,24 @@ path = "../../components/net_traits"
[dependencies.profile]
path = "../../components/profile"
[dependencies.style]
path = "../../components/style"
[dependencies.geom]
git = "https://github.com/servo/rust-geom"
[dependencies.cssparser]
git = "https://github.com/servo/rust-cssparser"
[dependencies.selectors]
git = "https://github.com/servo/rust-selectors"
[dependencies.string_cache]
git = "https://github.com/servo/string-cache"
[dependencies.string_cache_plugin]
git = "https://github.com/servo/string-cache"
[dependencies]
cookie = "*"
url = "*"

View file

@ -1,14 +1,21 @@
#![feature(plugin)]
#![cfg_attr(test, feature(net, alloc))]
#![plugin(string_cache_plugin)]
extern crate cssparser;
extern crate geom;
extern crate gfx;
extern crate net;
extern crate net_traits;
extern crate profile;
extern crate selectors;
extern crate string_cache;
extern crate style;
extern crate util;
extern crate url;
#[cfg(test)] #[path="gfx/mod.rs"] mod gfx_tests;
#[cfg(test)] #[path="net/mod.rs"] mod net_tests;
#[cfg(test)] #[path="style/mod.rs"] mod style_tests;
#[cfg(test)] #[path="util/mod.rs"] mod util_tests;

View file

@ -0,0 +1,412 @@
use geom::size::TypedSize2D;
use style::stylesheets::{iter_stylesheet_media_rules, iter_stylesheet_style_rules, Stylesheet};
use style::stylesheets::Origin;
use style::media_queries::*;
use style::values::specified;
use std::borrow::ToOwned;
use url::Url;
use util::geometry::Au;
fn test_media_rule<F>(css: &str, callback: F) where F: Fn(&MediaQueryList, &str) {
let url = Url::parse("http://localhost").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::Author);
let mut rule_count = 0;
iter_stylesheet_media_rules(&stylesheet, |rule| {
rule_count += 1;
callback(&rule.media_queries, css);
});
assert!(rule_count > 0);
}
fn media_query_test(device: &Device, css: &str, expected_rule_count: u32) {
let url = Url::parse("http://localhost").unwrap();
let ss = Stylesheet::from_str(css, url, Origin::Author);
let mut rule_count: u32 = 0;
iter_stylesheet_style_rules(&ss, device, |_| rule_count += 1);
assert!(rule_count == expected_rule_count, css.to_owned());
}
#[test]
fn test_mq_empty() {
test_media_rule("@media { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_screen() {
test_media_rule("@media screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not screen { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_print() {
test_media_rule("@media print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not print { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_unknown() {
test_media_rule("@media fridge { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only glass { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not wood { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_all() {
test_media_rule("@media all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media only all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not all { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_or() {
test_media_rule("@media screen, print { }", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_mq_default_expressions() {
test_media_rule("@media (min-width: 100px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media (max-width: 43px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_expressions() {
test_media_rule("@media screen and (min-width: 100px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media print and (max-width: 43px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media fridge and (max-width: 52px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(52))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_multiple_expressions() {
test_media_rule("@media (min-width: 100px) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
match q.expressions[1] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))),
_ => panic!("wrong expression type"),
}
});
test_media_rule("@media not screen and (min-width: 100px) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
_ => panic!("wrong expression type"),
}
match q.expressions[1] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(200))),
_ => panic!("wrong expression type"),
}
});
}
#[test]
fn test_mq_malformed_expressions() {
test_media_rule("@media (min-width: 100blah) and (max-width: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen and (height: 200px) { }", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media (min-width: 30em foo bar) {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media not (min-width: 300px) {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media , {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
let q = &list.media_queries[1];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen 4px, print {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q0.media_type == MediaQueryType::All, css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
test_media_rule("@media screen, {}", |list, css| {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q1.media_type == MediaQueryType::All, css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
}
#[test]
fn test_matching_simple() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media not all { a { color: red; } }", 0);
media_query_test(&device, "@media not screen { a { color: red; } }", 0);
media_query_test(&device, "@media not print { a { color: red; } }", 1);
media_query_test(&device, "@media unknown { a { color: red; } }", 0);
media_query_test(&device, "@media not unknown { a { color: red; } }", 1);
media_query_test(&device, "@media { a { color: red; } }", 1);
media_query_test(&device, "@media screen { a { color: red; } }", 1);
media_query_test(&device, "@media print { a { color: red; } }", 0);
}
#[test]
fn test_matching_width() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 50px) { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 150px) { a { color: red; } }", 1);
media_query_test(&device, "@media (min-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 50px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 150px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 50px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 150px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media (max-width: 50px) { a { color: red; } }", 0);
media_query_test(&device, "@media (max-width: 150px) { a { color: red; } }", 0);
media_query_test(&device, "@media (max-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 0);
media_query_test(&device, "@media not screen and (min-width: 3.1em) and (max-width: 6em) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 16em) and (max-width: 19.75em) { a { color: red; } }", 1);
media_query_test(&device, "@media not screen and (min-width: 3em) and (max-width: 250px) { a { color: red; } }", 0);
}
#[test]
fn test_matching_invalid() {
let device = Device {
media_type: MediaType::Screen,
viewport_size: TypedSize2D(200.0, 100.0),
};
media_query_test(&device, "@media fridge { a { color: red; } }", 0);
media_query_test(&device, "@media screen and (height: 100px) { a { color: red; } }", 0);
media_query_test(&device, "@media not print and (width: 100) { a { color: red; } }", 0);
}

12
tests/unit/style/mod.rs Normal file
View file

@ -0,0 +1,12 @@
use util::logical_geometry::WritingMode;
use style::properties::{INITIAL_VALUES, get_writing_mode};
mod stylesheets;
mod media_queries;
#[test]
fn initial_writing_mode_is_empty() {
assert_eq!(get_writing_mode(INITIAL_VALUES.get_inheritedbox()), WritingMode::empty())
}

View file

@ -0,0 +1,134 @@
use std::borrow::ToOwned;
use std::sync::Arc;
use cssparser;
use selectors::parser::*;
use string_cache::Atom;
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
use style::stylesheets::{CSSRule, StyleRule, Origin, Stylesheet};
use url::Url;
#[test]
fn test_parse_stylesheet() {
let css = r"
@namespace url(http://www.w3.org/1999/xhtml);
/* FIXME: only if scripting is enabled */
input[type=hidden i] { display: none !important; }
html , body /**/ { display: block; }
#d1 > .ok { background: blue; }
";
let url = Url::parse("about::test").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent);
assert_eq!(stylesheet, Stylesheet {
origin: Origin::UserAgent,
rules: vec![
CSSRule::Namespace(None, ns!(HTML)),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(input),
lower_name: atom!(input),
}),
SimpleSelector::AttrEqual(AttrSelector {
name: atom!(type),
lower_name: atom!(type),
namespace: NamespaceConstraint::Specific(ns!("")),
}, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (1 << 10) + (1 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![]),
important: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
longhands::display::SpecifiedValue::none)),
]),
},
}),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(html),
lower_name: atom!(html),
}),
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0),
},
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Namespace(ns!(HTML)),
SimpleSelector::LocalName(LocalName {
name: atom!(body),
lower_name: atom!(body),
}),
],
next: None,
}),
pseudo_element: None,
specificity: (0 << 20) + (0 << 10) + (1 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::SpecifiedValue(
longhands::display::SpecifiedValue::block)),
]),
important: Arc::new(vec![]),
},
}),
CSSRule::Style(StyleRule {
selectors: vec![
Selector {
compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::Class(Atom::from_slice("ok")),
],
next: Some((Box::new(CompoundSelector {
simple_selectors: vec![
SimpleSelector::ID(Atom::from_slice("d1")),
],
next: None,
}), Combinator::Child)),
}),
pseudo_element: None,
specificity: (1 << 20) + (1 << 10) + (0 << 0),
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
PropertyDeclaration::BackgroundColor(DeclaredValue::SpecifiedValue(
longhands::background_color::SpecifiedValue {
authored: Some("blue".to_owned()),
parsed: cssparser::Color::RGBA(cssparser::RGBA {
red: 0., green: 0., blue: 1., alpha: 1.
}),
}
)),
]),
important: Arc::new(vec![]),
},
}),
],
});
}