Port to the new cssparser.

https://github.com/servo/rust-cssparser/pull/68
This commit is contained in:
Simon Sapin 2015-01-06 20:34:46 +00:00
parent ad328fda65
commit d034a6c6bc
20 changed files with 2018 additions and 2388 deletions

View file

@ -46,8 +46,8 @@ use servo_util::opts;
use std::default::Default; use std::default::Default;
use std::iter::repeat; use std::iter::repeat;
use std::num::FloatMath; use std::num::FloatMath;
use style::computed::{AngleOrCorner, LengthOrPercentage, HorizontalDirection, VerticalDirection}; use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection};
use style::computed::{Image, LinearGradient}; use style::computed::{Image, LinearGradient, LengthOrPercentage};
use style::computed_values::filter::Filter; use style::computed_values::filter::Filter;
use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; use style::computed_values::{background_attachment, background_repeat, border_style, overflow};
use style::computed_values::{position, visibility}; use style::computed_values::{position, visibility};
@ -222,13 +222,13 @@ fn build_border_radius(abs_bounds: &Rect<Au>, border_style: &Border) -> BorderRa
// radii will be relative to the width. // radii will be relative to the width.
BorderRadii { BorderRadii {
top_left: model::specified(border_style.border_top_left_radius.radius, top_left: model::specified(border_style.border_top_left_radius,
abs_bounds.size.width), abs_bounds.size.width),
top_right: model::specified(border_style.border_top_right_radius.radius, top_right: model::specified(border_style.border_top_right_radius,
abs_bounds.size.width), abs_bounds.size.width),
bottom_right: model::specified(border_style.border_bottom_right_radius.radius, bottom_right: model::specified(border_style.border_bottom_right_radius,
abs_bounds.size.width), abs_bounds.size.width),
bottom_left: model::specified(border_style.border_bottom_left_radius.radius, bottom_left: model::specified(border_style.border_bottom_left_radius,
abs_bounds.size.width), abs_bounds.size.width),
} }
} }

View file

@ -50,8 +50,8 @@ use dom::node::{window_from_node};
use dom::nodelist::NodeList; use dom::nodelist::NodeList;
use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::virtualmethods::{VirtualMethods, vtable_for};
use devtools_traits::AttrInfo; use devtools_traits::AttrInfo;
use style::{mod, StylesheetOrigin, SimpleColorAttribute, UnsignedIntegerAttribute}; use style::{mod, SimpleColorAttribute, UnsignedIntegerAttribute};
use style::{IntegerAttribute, LengthAttribute, ParserContext, matches}; use style::{IntegerAttribute, LengthAttribute, matches};
use servo_util::namespace; use servo_util::namespace;
use servo_util::str::{DOMString, LengthOrPercentageOrAuto}; use servo_util::str::{DOMString, LengthOrPercentageOrAuto};
@ -1112,10 +1112,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
// http://dom.spec.whatwg.org/#dom-element-matches // http://dom.spec.whatwg.org/#dom-element-matches
fn Matches(self, selectors: DOMString) -> Fallible<bool> { fn Matches(self, selectors: DOMString) -> Fallible<bool> {
let parser_context = ParserContext { match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) {
origin: StylesheetOrigin::Author,
};
match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) {
Err(()) => Err(Syntax), Err(()) => Err(Syntax),
Ok(ref selectors) => { Ok(ref selectors) => {
let root: JSRef<Node> = NodeCast::from_ref(self); let root: JSRef<Node> = NodeCast::from_ref(self);
@ -1126,10 +1123,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
// https://dom.spec.whatwg.org/#dom-element-closest // https://dom.spec.whatwg.org/#dom-element-closest
fn Closest(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { fn Closest(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
let parser_context = ParserContext { match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) {
origin: StylesheetOrigin::Author,
};
match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) {
Err(()) => Err(Syntax), Err(()) => Err(Syntax),
Ok(ref selectors) => { Ok(ref selectors) => {
let root: JSRef<Node> = NodeCast::from_ref(self); let root: JSRef<Node> = NodeCast::from_ref(self);

View file

@ -48,7 +48,7 @@ use devtools_traits::NodeInfo;
use script_traits::UntrustedNodeAddress; use script_traits::UntrustedNodeAddress;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::str::{DOMString, null_str_as_empty}; use servo_util::str::{DOMString, null_str_as_empty};
use style::{matches, StylesheetOrigin, ParserContext, SelectorList}; use style::{matches, SelectorList};
use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime}; use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime};
use js::jsfriendapi; use js::jsfriendapi;
@ -742,10 +742,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector // http://dom.spec.whatwg.org/#dom-parentnode-queryselector
fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
// Step 1. // Step 1.
let parser_context = ParserContext { match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) {
origin: StylesheetOrigin::Author,
};
match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) {
// Step 2. // Step 2.
Err(()) => return Err(Syntax), Err(()) => return Err(Syntax),
// Step 3. // Step 3.
@ -767,10 +764,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
// Step 1. // Step 1.
let nodes; let nodes;
let root = self.ancestors().last().unwrap_or(self.clone()); let root = self.ancestors().last().unwrap_or(self.clone());
let parser_context = ParserContext { match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) {
origin: StylesheetOrigin::Author,
};
match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) {
// Step 2. // Step 2.
Err(()) => return Err(Syntax), Err(()) => return Err(Syntax),
// Step 3. // Step 3.

View file

@ -123,9 +123,10 @@ dependencies = [
[[package]] [[package]]
name = "cssparser" name = "cssparser"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/servo/rust-cssparser#110bf3052d016bf6eda0689fb21cf971e2c23dc8" source = "git+https://github.com/servo/rust-cssparser#8d1b3e220e795f7baaa940919059d5f4ef4ec28c"
dependencies = [ dependencies = [
"encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"text_writer 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "text_writer 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -488,6 +489,11 @@ dependencies = [
"pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "matches"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.0.1" version = "0.0.1"
@ -678,6 +684,7 @@ dependencies = [
"encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"lazy_static 0.1.0 (git+https://github.com/Kimundi/lazy-static.rs)", "lazy_static 0.1.0 (git+https://github.com/Kimundi/lazy-static.rs)",
"matches 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)",
"string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)",

View file

@ -36,3 +36,4 @@ git = "https://github.com/servo/string-cache"
[dependencies] [dependencies]
text_writer = "0.1.1" text_writer = "0.1.1"
encoding = "0.2" encoding = "0.2"
matches = "0.1"

View file

@ -1,32 +0,0 @@
/* 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 cssparser::ast::{SyntaxError, SourceLocation};
pub struct ErrorLoggerIterator<I>(pub I);
impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLoggerIterator<I> {
fn next(&mut self) -> Option<T> {
let ErrorLoggerIterator(ref mut this) = *self;
loop {
match this.next() {
Some(Ok(v)) => return Some(v),
Some(Err(error)) => log_css_error(error.location,
format!("{}", error.reason).as_slice()),
None => return None,
}
}
}
}
/// Defaults to a no-op.
/// Set a `RUST_LOG=style::errors` environment variable
/// to log CSS parse errors to stderr.
pub fn log_css_error(location: SourceLocation, message: &str) {
// TODO eventually this will got into a "web console" or something.
info!("{}:{} {}", location.line, location.column, message)
}

View file

@ -2,24 +2,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::ast::*; use cssparser::{Token, Parser, DeclarationListParser, AtRuleParser, DeclarationParser};
use cssparser::ast::ComponentValue::*;
use cssparser::parse_declaration_list;
use errors::{ErrorLoggerIterator, log_css_error};
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use parsing_utils::{BufferedIter, ParserIter, parse_slice_comma_separated};
use properties::longhands::font_family::parse_one_family;
use properties::computed_values::font_family::FontFamily::FamilyName;
use stylesheets::CSSRule; use stylesheets::CSSRule;
use properties::longhands::font_family::parse_one_family;
use properties::computed_values::font_family::FontFamily;
use media_queries::Device; use media_queries::Device;
use url::{Url, UrlParser}; use url::{Url, UrlParser};
use parser::ParserContext;
pub fn iter_font_face_rules_inner(rules: &[CSSRule], device: &Device, pub fn iter_font_face_rules_inner(rules: &[CSSRule], device: &Device,
callback: |family: &str, source: &Source|) { callback: |family: &str, source: &Source|) {
for rule in rules.iter() { for rule in rules.iter() {
match *rule { match *rule {
CSSRule::Style(_) => {}, CSSRule::Style(..) |
CSSRule::Charset(..) |
CSSRule::Namespace(..) => {},
CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) { CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) {
iter_font_face_rules_inner(rule.rules.as_slice(), device, |f, s| callback(f, s)) iter_font_face_rules_inner(rule.rules.as_slice(), device, |f, s| callback(f, s))
}, },
@ -32,102 +31,94 @@ pub fn iter_font_face_rules_inner(rules: &[CSSRule], device: &Device,
} }
} }
#[deriving(Clone)] #[deriving(Clone, Show, PartialEq, Eq)]
pub enum Source { pub enum Source {
Url(UrlSource), Url(UrlSource),
Local(String), Local(String),
} }
#[deriving(Clone)] #[deriving(Clone, Show, PartialEq, Eq)]
pub struct UrlSource { pub struct UrlSource {
pub url: Url, pub url: Url,
pub format_hints: Vec<String>, pub format_hints: Vec<String>,
} }
#[deriving(Show, PartialEq, Eq)]
pub struct FontFaceRule { pub struct FontFaceRule {
pub family: String, pub family: String,
pub sources: Vec<Source>, pub sources: Vec<Source>,
} }
pub fn parse_font_face_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>, base_url: &Url) {
if rule.prelude.as_slice().skip_whitespace().next().is_some() {
log_css_error(rule.location, "@font-face prelude contains unexpected characters");
return;
}
let block = match rule.block { pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser)
Some(block) => block, -> Result<FontFaceRule, ()> {
None => { let parser = FontFaceRuleParser {
log_css_error(rule.location, "Invalid @font-face rule"); context: context,
return family: None,
} src: None,
}; };
match DeclarationListParser::new(input, parser).run() {
let mut maybe_family = None; FontFaceRuleParser { family: Some(family), src: Some(src), .. } => {
let mut maybe_sources = None; Ok(FontFaceRule {
family: family,
for item in ErrorLoggerIterator(parse_declaration_list(block.into_iter())) { sources: src,
match item { })
DeclarationListItem::AtRule(rule) => log_css_error(
rule.location, format!("Unsupported at-rule in declaration list: @{}", rule.name).as_slice()),
DeclarationListItem::Declaration(Declaration{ location, name, value, important }) => {
if important {
log_css_error(location, "!important is not allowed on @font-face descriptors");
continue
}
let name_lower = name.as_slice().to_ascii_lower();
match name_lower.as_slice() {
"font-family" => {
let iter = &mut BufferedIter::new(value.as_slice().skip_whitespace());
match parse_one_family(iter) {
Ok(FamilyName(name)) => {
maybe_family = Some(name);
},
// This also includes generic family names:
_ => log_css_error(location, "Invalid font-family in @font-face"),
}
},
"src" => {
match parse_slice_comma_separated(
value.as_slice(), |iter| parse_one_src(iter, base_url)) {
Ok(sources) => maybe_sources = Some(sources),
Err(()) => log_css_error(location, "Invalid src in @font-face"),
};
},
_ => {
log_css_error(location, format!("Unsupported declaration {}", name).as_slice());
}
}
}
} }
} _ => Err(())
match (maybe_family, maybe_sources) {
(Some(family), Some(sources)) => parent_rules.push(CSSRule::FontFace(FontFaceRule {
family: family,
sources: sources,
})),
(None, _) => log_css_error(rule.location, "@font-face without a font-family descriptor"),
_ => log_css_error(rule.location, "@font-face without an src descriptor"),
} }
} }
fn parse_one_src(iter: ParserIter, base_url: &Url) -> Result<Source, ()> { struct FontFaceRuleParser<'a, 'b: 'a> {
let url = match iter.next() { context: &'a ParserContext<'b>,
family: Option<String>,
src: Option<Vec<Source>>,
}
/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser<(), ()> for FontFaceRuleParser<'a, 'b> {}
impl<'a, 'b> DeclarationParser<()> for FontFaceRuleParser<'a, 'b> {
fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
match_ignore_ascii_case! { name:
"font-family" => {
self.family = Some(try!(parse_one_non_generic_family_name(input)));
Ok(())
},
"src" => {
self.src = Some(try!(input.parse_comma_separated(|input| {
parse_one_src(self.context, input)
})));
Ok(())
}
_ => Err(())
}
}
}
fn parse_one_non_generic_family_name(input: &mut Parser) -> Result<String, ()> {
match parse_one_family(input) {
Ok(FontFamily::FamilyName(name)) => Ok(name),
_ => Err(())
}
}
fn parse_one_src(context: &ParserContext, input: &mut Parser) -> Result<Source, ()> {
let url = match input.next() {
// Parsing url() // Parsing url()
Some(&URL(ref url)) => { Ok(Token::Url(url)) => {
UrlParser::new().base_url(base_url).parse(url.as_slice()).unwrap_or_else( UrlParser::new().base_url(context.base_url).parse(url.as_slice()).unwrap_or_else(
|_error| Url::parse("about:invalid").unwrap()) |_error| Url::parse("about:invalid").unwrap())
}, },
// Parsing local() with early return() // Parsing local() with early return
Some(&Function(ref name, ref arguments)) => { Ok(Token::Function(name)) => {
if name.as_slice().eq_ignore_ascii_case("local") { if name.eq_ignore_ascii_case("local") {
let iter = &mut BufferedIter::new(arguments.as_slice().skip_whitespace()); return Ok(Source::Local(try!(input.parse_nested_block(|input| {
match parse_one_family(iter) { parse_one_non_generic_family_name(input)
Ok(FamilyName(name)) => return Ok(Source::Local(name)), }))))
_ => return Err(())
}
} }
return Err(()) return Err(())
}, },
@ -135,18 +126,14 @@ fn parse_one_src(iter: ParserIter, base_url: &Url) -> Result<Source, ()> {
}; };
// Parsing optional format() // Parsing optional format()
let format_hints = match iter.next() { let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {
Some(&Function(ref name, ref arguments)) => { try!(input.parse_nested_block(|input| {
if !name.as_slice().eq_ignore_ascii_case("format") { input.parse_comma_separated(|input| {
return Err(()) Ok((try!(input.expect_string())).into_owned())
} })
try!(parse_slice_comma_separated(arguments.as_slice(), parse_one_format)) }))
} } else {
Some(component_value) => { vec![]
iter.push_back(component_value);
vec![]
}
None => vec![],
}; };
Ok(Source::Url(UrlSource { Ok(Source::Url(UrlSource {
@ -154,17 +141,3 @@ fn parse_one_src(iter: ParserIter, base_url: &Url) -> Result<Source, ()> {
format_hints: format_hints, format_hints: format_hints,
})) }))
} }
fn parse_one_format(iter: ParserIter) -> Result<String, ()> {
match iter.next() {
Some(&QuotedString(ref value)) => {
if iter.next().is_none() {
Ok(value.clone())
} else {
Err(())
}
}
_ => Err(())
}
}

View file

@ -6,10 +6,10 @@
//! `<input size>`, and so forth. //! `<input size>`, and so forth.
use node::{TElement, TElementAttributes, TNode}; use node::{TElement, TElementAttributes, TNode};
use properties::common_types::specified::CSSColor; use values::specified::CSSColor;
use values::{CSSFloat, specified};
use properties::DeclaredValue::SpecifiedValue; use properties::DeclaredValue::SpecifiedValue;
use properties::PropertyDeclaration::*; use properties::PropertyDeclaration;
use properties::{CSSFloat, specified};
use selector_matching::{DeclarationBlock, Stylist}; use selector_matching::{DeclarationBlock, Stylist};
use cssparser::Color; use cssparser::Color;
@ -110,13 +110,13 @@ impl PresentationalHintSynthesis for Stylist {
LengthOrPercentageOrAuto::Percentage(percentage) => { LengthOrPercentageOrAuto::Percentage(percentage) => {
let width_value = specified::LengthOrPercentageOrAuto::Percentage(percentage); let width_value = specified::LengthOrPercentageOrAuto::Percentage(percentage);
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
WidthDeclaration(SpecifiedValue(width_value)))); PropertyDeclaration::Width(SpecifiedValue(width_value))));
*shareable = false *shareable = false
} }
LengthOrPercentageOrAuto::Length(length) => { LengthOrPercentageOrAuto::Length(length) => {
let width_value = specified::LengthOrPercentageOrAuto::Length(specified::Length::Au(length)); let width_value = specified::LengthOrPercentageOrAuto::Length(specified::Length::Au(length));
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
WidthDeclaration(SpecifiedValue(width_value)))); PropertyDeclaration::Width(SpecifiedValue(width_value))));
*shareable = false *shareable = false
} }
} }
@ -160,8 +160,8 @@ impl PresentationalHintSynthesis for Stylist {
_ => specified::Length::Au(Au::from_px(value as int)), _ => specified::Length::Au(Au::from_px(value as int)),
}; };
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
WidthDeclaration(SpecifiedValue(specified::LengthOrPercentageOrAuto::Length( PropertyDeclaration::Width(SpecifiedValue(
value))))); specified::LengthOrPercentageOrAuto::Length(value)))));
*shareable = false *shareable = false
} }
Some(_) | None => {} Some(_) | None => {}
@ -177,8 +177,8 @@ impl PresentationalHintSynthesis for Stylist {
// https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-width // https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-width
let value = specified::Length::ServoCharacterWidth(value); let value = specified::Length::ServoCharacterWidth(value);
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
WidthDeclaration(SpecifiedValue(specified::LengthOrPercentageOrAuto::Length( PropertyDeclaration::Width(SpecifiedValue(
value))))); specified::LengthOrPercentageOrAuto::Length(value)))));
*shareable = false *shareable = false
} }
Some(_) | None => {} Some(_) | None => {}
@ -190,8 +190,8 @@ impl PresentationalHintSynthesis for Stylist {
// https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-height // https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-height
let value = specified::Length::Em(value as CSSFloat); let value = specified::Length::Em(value as CSSFloat);
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
HeightDeclaration(SpecifiedValue(specified::LengthOrPercentageOrAuto::Length( PropertyDeclaration::Height(SpecifiedValue(
value))))); specified::LengthOrPercentageOrAuto::Length(value)))));
*shareable = false *shareable = false
} }
Some(_) | None => {} Some(_) | None => {}
@ -216,7 +216,8 @@ impl PresentationalHintSynthesis for Stylist {
None => {} None => {}
Some(color) => { Some(color) => {
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
BackgroundColorDeclaration(SpecifiedValue(CSSColor { parsed: Color::RGBA(color), authored: None })))); PropertyDeclaration::BackgroundColor(SpecifiedValue(
CSSColor { parsed: Color::RGBA(color), authored: None }))));
*shareable = false *shareable = false
} }
} }
@ -236,13 +237,13 @@ impl PresentationalHintSynthesis for Stylist {
Some(length) => { Some(length) => {
let width_value = specified::Length::Au(Au::from_px(length as int)); let width_value = specified::Length::Au(Au::from_px(length as int));
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
BorderTopWidthDeclaration(SpecifiedValue(width_value)))); PropertyDeclaration::BorderTopWidth(SpecifiedValue(width_value))));
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
BorderLeftWidthDeclaration(SpecifiedValue(width_value)))); PropertyDeclaration::BorderLeftWidth(SpecifiedValue(width_value))));
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
BorderBottomWidthDeclaration(SpecifiedValue(width_value)))); PropertyDeclaration::BorderBottomWidth(SpecifiedValue(width_value))));
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
BorderRightWidthDeclaration(SpecifiedValue(width_value)))); PropertyDeclaration::BorderRightWidth(SpecifiedValue(width_value))));
*shareable = false *shareable = false
} }
} }

View file

@ -18,7 +18,12 @@ extern crate serialize;
extern crate text_writer; extern crate text_writer;
extern crate url; extern crate url;
#[phase(plugin, link)]
extern crate cssparser; extern crate cssparser;
#[phase(plugin)]
extern crate matches;
extern crate encoding; extern crate encoding;
extern crate string_cache; extern crate string_cache;
@ -34,39 +39,41 @@ extern crate lazy_static;
extern crate "util" as servo_util; extern crate "util" as servo_util;
// Public API
pub use media_queries::{Device, MediaType}; pub use media_queries::{Device, MediaType};
pub use stylesheets::{Stylesheet, iter_font_face_rules}; pub use stylesheets::{Stylesheet, iter_font_face_rules};
pub use selector_matching::{Stylist, StylesheetOrigin}; pub use selector_matching::{Stylist};
pub use selector_matching::{DeclarationBlock, CommonStyleAffectingAttributes}; pub use selector_matching::{DeclarationBlock, CommonStyleAffectingAttributes};
pub use selector_matching::{CommonStyleAffectingAttributeInfo, CommonStyleAffectingAttributeMode}; pub use selector_matching::{CommonStyleAffectingAttributeInfo, CommonStyleAffectingAttributeMode};
pub use selector_matching::{matches, matches_simple_selector, common_style_affecting_attributes}; pub use selector_matching::{matches, matches_simple_selector, common_style_affecting_attributes};
pub use selector_matching::{rare_style_affecting_attributes}; pub use selector_matching::{rare_style_affecting_attributes};
pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE, SELECTOR_WHITESPACE}; pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE, SELECTOR_WHITESPACE};
pub use properties::{cascade, cascade_anonymous, computed, longhands_from_shorthand}; pub use properties::{cascade, cascade_anonymous, longhands_from_shorthand};
pub use properties::{is_supported_property, make_inline}; pub use properties::{is_supported_property, make_inline};
pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs}; pub use properties::{PropertyDeclaration};
pub use properties::{computed_values, ComputedValues, style_structs};
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult}; pub use properties::{DeclaredValue, PropertyDeclarationParseResult};
pub use properties::{Angle, AngleOrCorner}; pub use values::CSSFloat;
pub use properties::{HorizontalDirection, VerticalDirection}; pub use values::specified::{Angle, AngleOrCorner, HorizontalDirection, VerticalDirection};
pub use values::computed;
pub use node::{TElement, TElementAttributes, TNode}; pub use node::{TElement, TElementAttributes, TNode};
pub use selectors::{PseudoElement, ParserContext, SelectorList}; pub use selectors::{PseudoElement, SelectorList};
pub use selectors::{AttrSelector, NamespaceConstraint}; pub use selectors::{AttrSelector, NamespaceConstraint};
pub use selectors::{SimpleSelector, parse_selector_list_from_str}; pub use selectors::{SimpleSelector, parse_author_origin_selector_list_from_str};
pub use cssparser::{Color, RGBA}; pub use cssparser::{Color, RGBA};
pub use legacy::{IntegerAttribute, LengthAttribute}; pub use legacy::{IntegerAttribute, LengthAttribute};
pub use legacy::{SimpleColorAttribute, UnsignedIntegerAttribute}; pub use legacy::{SimpleColorAttribute, UnsignedIntegerAttribute};
pub use font_face::Source; pub use font_face::Source;
pub use stylesheets::Origin as StylesheetOrigin;
mod stylesheets; pub mod stylesheets;
mod errors; pub mod parser;
mod selectors; pub mod selectors;
mod selector_matching; pub mod selector_matching;
mod properties; pub mod values;
mod namespaces; pub mod properties;
mod node; pub mod namespaces;
mod media_queries; pub mod node;
mod parsing_utils; pub mod media_queries;
mod font_face; pub mod font_face;
mod legacy; pub mod legacy;

View file

@ -3,31 +3,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use cssparser::parse_rule_list; use cssparser::{Token, Parser, Delimiter};
use cssparser::ast::*;
use cssparser::ast::ComponentValue::*;
use errors::{ErrorLoggerIterator, log_css_error};
use geom::size::TypedSize2D; use geom::size::TypedSize2D;
use selectors::ParserContext;
use stylesheets::{CSSRule, parse_style_rule, parse_nested_at_rule};
use namespaces::NamespaceMap;
use parsing_utils::{BufferedIter, ParserIter};
use properties::common_types::*;
use properties::longhands; use properties::longhands;
use servo_util::geometry::ViewportPx; use servo_util::geometry::{Au, ViewportPx};
use url::Url; use values::{computed, specified};
pub struct MediaRule {
pub media_queries: MediaQueryList,
pub rules: Vec<CSSRule>,
}
#[deriving(Show, PartialEq)]
pub struct MediaQueryList { pub struct MediaQueryList {
media_queries: Vec<MediaQuery> media_queries: Vec<MediaQuery>
} }
#[deriving(PartialEq, Eq, Copy)] #[deriving(PartialEq, Eq, Copy, Show)]
pub enum Range<T> { pub enum Range<T> {
Min(T), Min(T),
Max(T), Max(T),
@ -44,17 +33,18 @@ impl<T: Ord> Range<T> {
} }
} }
#[deriving(PartialEq, Eq, Copy)] #[deriving(PartialEq, Eq, Copy, Show)]
pub enum Expression { pub enum Expression {
Width(Range<Au>), Width(Range<Au>),
} }
#[deriving(PartialEq, Eq, Copy)] #[deriving(PartialEq, Eq, Copy, Show)]
pub enum Qualifier { pub enum Qualifier {
Only, Only,
Not, Not,
} }
#[deriving(Show, PartialEq)]
pub struct MediaQuery { pub struct MediaQuery {
qualifier: Option<Qualifier>, qualifier: Option<Qualifier>,
media_type: MediaQueryType, media_type: MediaQueryType,
@ -72,19 +62,21 @@ impl MediaQuery {
} }
} }
#[deriving(PartialEq, Eq, Copy)] #[deriving(PartialEq, Eq, Copy, Show)]
pub enum MediaQueryType { pub enum MediaQueryType {
All, // Always true All, // Always true
MediaType(MediaType), MediaType(MediaType),
} }
#[deriving(PartialEq, Eq, Copy)] #[deriving(PartialEq, Eq, Copy, Show)]
pub enum MediaType { pub enum MediaType {
Screen, Screen,
Print, Print,
Unknown, Unknown,
} }
#[allow(missing_copy_implementations)]
#[deriving(Show)]
pub struct Device { pub struct Device {
pub media_type: MediaType, pub media_type: MediaType,
pub viewport_size: TypedSize2D<ViewportPx, f32>, pub viewport_size: TypedSize2D<ViewportPx, f32>,
@ -99,42 +91,9 @@ impl Device {
} }
} }
pub fn parse_media_rule(context: &ParserContext,
rule: AtRule,
parent_rules: &mut Vec<CSSRule>,
namespaces: &NamespaceMap,
base_url: &Url) {
let media_queries = parse_media_query_list(rule.prelude.as_slice());
let block = match rule.block {
Some(block) => block,
None => {
log_css_error(rule.location, "Invalid @media rule");
return
}
};
let mut rules = vec!();
for rule in ErrorLoggerIterator(parse_rule_list(block.into_iter())) {
match rule {
Rule::QualifiedRule(rule) => {
parse_style_rule(context, rule, &mut rules, namespaces, base_url)
}
Rule::AtRule(rule) => parse_nested_at_rule(
context,
rule.name.as_slice().to_ascii_lower().as_slice(),
rule,
&mut rules,
namespaces,
base_url),
}
}
parent_rules.push(CSSRule::Media(MediaRule {
media_queries: media_queries,
rules: rules,
}))
}
fn parse_value_as_length(value: &ComponentValue) -> Result<Au, ()> { fn parse_non_negative_length(input: &mut Parser) -> Result<Au, ()> {
let length = try!(specified::Length::parse_non_negative(value)); let length = try!(specified::Length::parse_non_negative(input));
// http://dev.w3.org/csswg/mediaqueries3/ - Section 6 // http://dev.w3.org/csswg/mediaqueries3/ - Section 6
// em units are relative to the initial font-size. // em units are relative to the initial font-size.
@ -142,154 +101,87 @@ fn parse_value_as_length(value: &ComponentValue) -> Result<Au, ()> {
Ok(computed::compute_Au_with_font_size(length, initial_font_size, initial_font_size)) Ok(computed::compute_Au_with_font_size(length, initial_font_size, initial_font_size))
} }
fn parse_media_query_expression(iter: ParserIter) -> Result<Expression, ()> {
// Expect a parenthesis block with the condition
match iter.next() {
Some(&ParenthesisBlock(ref block)) => {
let iter = &mut BufferedIter::new(block.as_slice().skip_whitespace());
// Parse the variable (e.g. min-width) impl Expression {
let variable = match iter.next() { fn parse(input: &mut Parser) -> Result<Expression, ()> {
Some(&Ident(ref value)) => value, try!(input.expect_parenthesis_block());
_ => return Err(()) input.parse_nested_block(|input| {
}; let name = try!(input.expect_ident());
try!(input.expect_colon());
// Ensure a colon follows // TODO: Handle other media features
match iter.next() { match_ignore_ascii_case! { name:
Some(&Colon) => {},
_ => return Err(())
}
// Retrieve the value
let value = try!(iter.next_as_result());
// TODO: Handle other media query types
let expression = match variable.as_slice().to_ascii_lower().as_slice() {
"min-width" => { "min-width" => {
let au = try!(parse_value_as_length(value)); Ok(Expression::Width(Range::Min(try!(parse_non_negative_length(input)))))
Expression::Width(Range::Min(au)) },
}
"max-width" => { "max-width" => {
let au = try!(parse_value_as_length(value)); Ok(Expression::Width(Range::Max(try!(parse_non_negative_length(input)))))
Expression::Width(Range::Max(au))
} }
_ => return Err(()) _ => Err(())
};
if iter.is_eof() {
Ok(expression)
} else {
Err(())
} }
} })
_ => Err(())
} }
} }
fn parse_media_query(iter: ParserIter) -> Result<MediaQuery, ()> { impl MediaQuery {
let mut expressions = vec!(); fn parse(input: &mut Parser) -> Result<MediaQuery, ()> {
let mut expressions = vec![];
// Check for optional 'only' or 'not' let qualifier = if input.try(|input| input.expect_ident_matching("only")).is_ok() {
let qualifier = match iter.next() { Some(Qualifier::Only)
Some(&Ident(ref value)) if value.as_slice().to_ascii_lower().as_slice() == "only" => Some(Qualifier::Only), } else if input.try(|input| input.expect_ident_matching("not")).is_ok() {
Some(&Ident(ref value)) if value.as_slice().to_ascii_lower().as_slice() == "not" => Some(Qualifier::Not), Some(Qualifier::Not)
Some(component_value) => { } else {
iter.push_back(component_value);
None None
} };
None => return Err(()), // Empty queries are invalid
};
// Check for media type let media_type;
let media_type = match iter.next() { if let Ok(ident) = input.try(|input| input.expect_ident()) {
Some(&Ident(ref value)) => { media_type = match_ignore_ascii_case! { ident:
match value.as_slice().to_ascii_lower().as_slice() {
"screen" => MediaQueryType::MediaType(MediaType::Screen), "screen" => MediaQueryType::MediaType(MediaType::Screen),
"print" => MediaQueryType::MediaType(MediaType::Print), "print" => MediaQueryType::MediaType(MediaType::Print),
"all" => MediaQueryType::All, "all" => MediaQueryType::All
_ => MediaQueryType::MediaType(MediaType::Unknown), // Unknown media types never match _ => MediaQueryType::MediaType(MediaType::Unknown)
} }
} } else {
Some(component_value) => {
// Media type is only optional if qualifier is not specified. // Media type is only optional if qualifier is not specified.
if qualifier.is_some() { if qualifier.is_some() {
return Err(()); return Err(())
} }
iter.push_back(component_value); media_type = MediaQueryType::All;
// Without a media type, require at least one expression
// If no qualifier and media type present, an expression should exist here expressions.push(try!(Expression::parse(input)));
let expression = try!(parse_media_query_expression(iter));
expressions.push(expression);
MediaQueryType::All
} }
None => return Err(()),
};
// Parse any subsequent expressions // Parse any subsequent expressions
loop { loop {
// Each expression should begin with and if input.try(|input| input.expect_ident_matching("and")).is_err() {
match iter.next() { return Ok(MediaQuery::new(qualifier, media_type, expressions))
Some(&Ident(ref value)) => {
match value.as_slice().to_ascii_lower().as_slice() {
"and" => {
let expression = try!(parse_media_query_expression(iter));
expressions.push(expression);
}
_ => return Err(()),
}
} }
Some(component_value) => { expressions.push(try!(Expression::parse(input)))
iter.push_back(component_value);
break;
}
None => break,
} }
} }
Ok(MediaQuery::new(qualifier, media_type, expressions))
} }
pub fn parse_media_query_list(input: &[ComponentValue]) -> MediaQueryList { pub fn parse_media_query_list(input: &mut Parser) -> MediaQueryList {
let iter = &mut BufferedIter::new(input.skip_whitespace()); let queries = if input.is_exhausted() {
let mut media_queries = vec!(); vec![MediaQuery::new(None, MediaQueryType::All, vec!())]
if iter.is_eof() {
media_queries.push(MediaQuery::new(None, MediaQueryType::All, vec!()));
} else { } else {
let mut media_queries = vec![];
loop { loop {
// Attempt to parse a media query. media_queries.push(
let media_query_result = parse_media_query(iter); input.parse_until_before(Delimiter::Comma, MediaQuery::parse)
.unwrap_or(MediaQuery::new(Some(Qualifier::Not),
// Skip until next query or end MediaQueryType::All,
let mut trailing_tokens = false; vec!())));
let mut more_queries = false; match input.next() {
loop { Ok(Token::Comma) => continue,
match iter.next() { Ok(_) => unreachable!(),
Some(&Comma) => { Err(()) => break,
more_queries = true;
break;
}
Some(_) => trailing_tokens = true,
None => break,
}
}
// Add the media query if it was valid and no trailing tokens were found.
// Otherwise, create a 'not all' media query, that will never match.
let media_query = match (media_query_result, trailing_tokens) {
(Ok(media_query), false) => media_query,
_ => MediaQuery::new(Some(Qualifier::Not), MediaQueryType::All, vec!()),
};
media_queries.push(media_query);
if !more_queries {
break;
} }
} }
} media_queries
};
MediaQueryList { media_queries: media_queries } MediaQueryList { media_queries: queries }
} }
impl MediaQueryList { impl MediaQueryList {
@ -323,17 +215,16 @@ impl MediaQueryList {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use geom::size::TypedSize2D; use geom::size::TypedSize2D;
use properties::common_types::*; use servo_util::geometry::Au;
use stylesheets::{iter_stylesheet_media_rules, iter_stylesheet_style_rules, Stylesheet}; use stylesheets::{iter_stylesheet_media_rules, iter_stylesheet_style_rules, Stylesheet};
use selector_matching::StylesheetOrigin; use stylesheets::Origin;
use super::*; use super::*;
use url::Url; use url::Url;
use std::borrow::ToOwned; use std::borrow::ToOwned;
fn test_media_rule(css: &str, callback: |&MediaQueryList, &str|) { fn test_media_rule(css: &str, callback: |&MediaQueryList, &str|) {
let url = Url::parse("http://localhost").unwrap(); let url = Url::parse("http://localhost").unwrap();
let stylesheet = Stylesheet::from_str(css, url, let stylesheet = Stylesheet::from_str(css, url, Origin::Author);
StylesheetOrigin::Author);
let mut rule_count: int = 0; let mut rule_count: int = 0;
iter_stylesheet_media_rules(&stylesheet, |rule| { iter_stylesheet_media_rules(&stylesheet, |rule| {
rule_count += 1; rule_count += 1;
@ -344,7 +235,7 @@ mod tests {
fn media_query_test(device: &Device, css: &str, expected_rule_count: int) { fn media_query_test(device: &Device, css: &str, expected_rule_count: int) {
let url = Url::parse("http://localhost").unwrap(); let url = Url::parse("http://localhost").unwrap();
let ss = Stylesheet::from_str(css, url, StylesheetOrigin::Author); let ss = Stylesheet::from_str(css, url, Origin::Author);
let mut rule_count: int = 0; let mut rule_count: int = 0;
iter_stylesheet_style_rules(&ss, device, |_| rule_count += 1); iter_stylesheet_style_rules(&ss, device, |_| rule_count += 1);
assert!(rule_count == expected_rule_count, css.to_owned()); assert!(rule_count == expected_rule_count, css.to_owned());
@ -629,11 +520,15 @@ mod tests {
}); });
test_media_rule("@media , {}", |list, css| { test_media_rule("@media , {}", |list, css| {
assert!(list.media_queries.len() == 1, css.to_owned()); assert!(list.media_queries.len() == 2, css.to_owned());
let q = &list.media_queries[0]; let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned()); assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::All, css.to_owned()); assert!(q.media_type == MediaQueryType::All, css.to_owned());
assert!(q.expressions.len() == 0, 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| { test_media_rule("@media screen 4px, print {}", |list, css| {

View file

@ -2,13 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::ast::*; use cssparser::Parser;
use cssparser::ast::ComponentValue::*;
use std::collections::HashMap; use std::collections::HashMap;
use servo_util::namespace; use string_cache::{Atom, Namespace};
use errors::log_css_error; use parser::ParserContext;
use string_cache::Namespace;
#[deriving(Clone)]
pub struct NamespaceMap { pub struct NamespaceMap {
pub default: Option<Namespace>, pub default: Option<Namespace>,
pub prefix_map: HashMap<String, Namespace>, pub prefix_map: HashMap<String, Namespace>,
@ -22,45 +22,28 @@ impl NamespaceMap {
} }
pub fn parse_namespace_rule(rule: AtRule, namespaces: &mut NamespaceMap) { pub fn parse_namespace_rule(context: &mut ParserContext, input: &mut Parser)
let location = rule.location; -> Result<(Option<String>, Namespace), ()> {
macro_rules! syntax_error( let prefix = input.try(|input| input.expect_ident()).ok().map(|p| p.into_owned());
() => {{ let url = try!(input.expect_url_or_string());
log_css_error(location, "Invalid @namespace rule"); try!(input.expect_exhausted());
return
}}; let namespace = Namespace(Atom::from_slice(url.as_slice()));
); let is_duplicate = match prefix {
if rule.block.is_some() { syntax_error!() } Some(ref prefix) => {
let mut prefix: Option<String> = None; context.namespaces.prefix_map.insert(prefix.clone(), namespace.clone()).is_some()
let mut ns: Option<Namespace> = None;
let mut iter = rule.prelude.move_skip_whitespace();
for component_value in iter {
match component_value {
Ident(value) => {
if prefix.is_some() { syntax_error!() }
prefix = Some(value);
},
URL(value) | QuotedString(value) => {
if ns.is_some() { syntax_error!() }
ns = Some(namespace::from_domstring(Some(value)));
break
},
_ => syntax_error!(),
} }
} None => {
if iter.next().is_some() { syntax_error!() } let has_default = context.namespaces.default.is_some();
match (prefix, ns) { if !has_default {
(Some(prefix), Some(ns)) => { context.namespaces.default = Some(namespace.clone());
if namespaces.prefix_map.insert(prefix, ns).is_some() {
log_css_error(location, "Duplicate @namespace rule");
} }
}, has_default
(None, Some(ns)) => { }
if namespaces.default.is_some() { };
log_css_error(location, "Duplicate @namespace rule"); if is_duplicate {
} Err(()) // "Duplicate @namespace rule"
namespaces.default = Some(ns); } else {
}, Ok((prefix, namespace))
_ => syntax_error!()
} }
} }

View file

@ -0,0 +1,42 @@
/* 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 cssparser::{Parser, SourcePosition};
use url::{Url, UrlParser};
use log;
use stylesheets::Origin;
use namespaces::NamespaceMap;
pub struct ParserContext<'a> {
pub stylesheet_origin: Origin,
pub base_url: &'a Url,
pub namespaces: NamespaceMap,
}
impl<'a> ParserContext<'a> {
pub fn in_user_agent_stylesheet(&self) -> bool {
self.stylesheet_origin == Origin::UserAgent
}
pub fn parse_url(&self, input: &str) -> Url {
UrlParser::new().base_url(self.base_url).parse(input)
.unwrap_or_else(|_| Url::parse("about:invalid").unwrap())
}
}
/// Defaults to a no-op.
/// Set a `RUST_LOG=style::errors` environment variable
/// to log CSS parse errors to stderr.
pub fn log_css_error(input: &mut Parser, position: SourcePosition, message: &str) {
if log_enabled!(log::INFO) {
let location = input.source_location(position);
// TODO eventually this will got into a "web console" or something.
info!("{}:{} {}", location.line, location.column, message)
}
}

View file

@ -1,101 +0,0 @@
/* 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::AsciiExt;
use cssparser::ast::{SkipWhitespaceIterable, SkipWhitespaceIterator};
use cssparser::ast::ComponentValue::{mod, Ident, Comma};
pub fn one_component_value<'a>(input: &'a [ComponentValue]) -> Result<&'a ComponentValue, ()> {
let mut iter = input.skip_whitespace();
match iter.next() {
Some(value) => if iter.next().is_none() { Ok(value) } else { Err(()) },
None => Err(())
}
}
pub fn get_ident_lower(component_value: &ComponentValue) -> Result<String, ()> {
match component_value {
&Ident(ref value) => Ok(value.as_slice().to_ascii_lower()),
_ => Err(()),
}
}
pub struct BufferedIter<E, I> {
iter: I,
buffer: Option<E>,
}
impl<E, I: Iterator<E>> BufferedIter<E, I> {
pub fn new(iter: I) -> BufferedIter<E, I> {
BufferedIter {
iter: iter,
buffer: None,
}
}
#[inline]
pub fn push_back(&mut self, value: E) {
assert!(self.buffer.is_none());
self.buffer = Some(value);
}
#[inline]
pub fn is_eof(&mut self) -> bool {
match self.next() {
Some(value) => {
self.push_back(value);
false
}
None => true
}
}
#[inline]
pub fn next_as_result(&mut self) -> Result<E, ()> {
match self.next() {
Some(value) => Ok(value),
None => Err(()),
}
}
}
impl<E, I: Iterator<E>> Iterator<E> for BufferedIter<E, I> {
#[inline]
fn next(&mut self) -> Option<E> {
if self.buffer.is_some() {
self.buffer.take()
}
else {
self.iter.next()
}
}
}
pub type ParserIter<'a, 'b> = &'a mut BufferedIter<&'b ComponentValue, SkipWhitespaceIterator<'b>>;
#[inline]
pub fn parse_slice_comma_separated<T>(input: &[ComponentValue],
parse_one: |ParserIter| -> Result<T, ()>)
-> Result<Vec<T>, ()> {
parse_comma_separated(&mut BufferedIter::new(input.skip_whitespace()), parse_one)
}
#[inline]
pub fn parse_comma_separated<T>(iter: ParserIter,
parse_one: |ParserIter| -> Result<T, ()>)
-> Result<Vec<T>, ()> {
let mut values = vec![try!(parse_one(iter))];
loop {
match iter.next() {
Some(&Comma) => values.push(try!(parse_one(iter))),
Some(_) => return Err(()),
None => return Ok(values),
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -22,14 +22,8 @@ use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use selectors::{CaseSensitivity, Combinator, CompoundSelector, LocalName}; use selectors::{CaseSensitivity, Combinator, CompoundSelector, LocalName};
use selectors::{PseudoElement, SelectorList, SimpleSelector}; use selectors::{PseudoElement, SelectorList, SimpleSelector};
use selectors::{get_selector_list_selectors}; use selectors::{get_selector_list_selectors};
use stylesheets::{Stylesheet, iter_stylesheet_media_rules, iter_stylesheet_style_rules}; use stylesheets::{Stylesheet, iter_stylesheet_media_rules, iter_stylesheet_style_rules, Origin};
#[deriving(Clone, PartialEq, Eq, Copy)]
pub enum StylesheetOrigin {
UserAgent,
Author,
User,
}
/// The definition of whitespace per CSS Selectors Level 3 § 4. /// The definition of whitespace per CSS Selectors Level 3 § 4.
pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C']; pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C'];
@ -303,7 +297,7 @@ impl Stylist {
Url::parse(format!("chrome:///{}", filename).as_slice()).unwrap(), Url::parse(format!("chrome:///{}", filename).as_slice()).unwrap(),
None, None,
None, None,
StylesheetOrigin::UserAgent); Origin::UserAgent);
stylist.add_stylesheet(ua_stylesheet); stylist.add_stylesheet(ua_stylesheet);
} }
stylist stylist
@ -318,17 +312,17 @@ impl Stylist {
for stylesheet in self.stylesheets.iter() { for stylesheet in self.stylesheets.iter() {
let (mut element_map, mut before_map, mut after_map) = match stylesheet.origin { let (mut element_map, mut before_map, mut after_map) = match stylesheet.origin {
StylesheetOrigin::UserAgent => ( Origin::UserAgent => (
&mut self.element_map.user_agent, &mut self.element_map.user_agent,
&mut self.before_map.user_agent, &mut self.before_map.user_agent,
&mut self.after_map.user_agent, &mut self.after_map.user_agent,
), ),
StylesheetOrigin::Author => ( Origin::Author => (
&mut self.element_map.author, &mut self.element_map.author,
&mut self.before_map.author, &mut self.before_map.author,
&mut self.after_map.author, &mut self.after_map.author,
), ),
StylesheetOrigin::User => ( Origin::User => (
&mut self.element_map.user, &mut self.element_map.user,
&mut self.before_map.user, &mut self.before_map.user,
&mut self.after_map.user, &mut self.after_map.user,
@ -395,7 +389,7 @@ impl Stylist {
Url::parse("chrome:///quirks-mode.css").unwrap(), Url::parse("chrome:///quirks-mode.css").unwrap(),
None, None,
None, None,
StylesheetOrigin::UserAgent)) Origin::UserAgent))
} }
pub fn add_stylesheet(&mut self, stylesheet: Stylesheet) { pub fn add_stylesheet(&mut self, stylesheet: Stylesheet) {
@ -529,7 +523,7 @@ struct Rule {
/// A property declaration together with its precedence among rules of equal specificity so that /// A property declaration together with its precedence among rules of equal specificity so that
/// we can sort them. /// we can sort them.
#[deriving(Clone)] #[deriving(Clone, Show)]
pub struct DeclarationBlock { pub struct DeclarationBlock {
pub declarations: Arc<Vec<PropertyDeclaration>>, pub declarations: Arc<Vec<PropertyDeclaration>>,
source_order: uint, source_order: uint,
@ -1171,21 +1165,24 @@ mod tests {
use super::{DeclarationBlock, Rule, SelectorMap}; use super::{DeclarationBlock, Rule, SelectorMap};
use selectors::LocalName; use selectors::LocalName;
use string_cache::Atom; use string_cache::Atom;
use cssparser::Parser;
use parser::ParserContext;
use url::Url;
/// Helper method to get some Rules from selector strings. /// Helper method to get some Rules from selector strings.
/// Each sublist of the result contains the Rules for one StyleRule. /// Each sublist of the result contains the Rules for one StyleRule.
fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> { fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> {
use namespaces::NamespaceMap; use namespaces::NamespaceMap;
use selectors::{ParserContext, parse_selector_list}; use selectors::parse_selector_list;
use selector_matching::StylesheetOrigin; use stylesheets::Origin;
use cssparser::tokenize;
let namespaces = NamespaceMap::new();
css_selectors.iter().enumerate().map(|(i, selectors)| { css_selectors.iter().enumerate().map(|(i, selectors)| {
let context = ParserContext { let context = ParserContext {
origin: StylesheetOrigin::Author, stylesheet_origin: Origin::Author,
namespaces: NamespaceMap::new(),
base_url: &Url::parse("about:blank").unwrap(),
}; };
parse_selector_list(&context, tokenize(*selectors).map(|(c, _)| c), &namespaces) parse_selector_list(&context, &mut Parser::new(*selectors))
.unwrap().into_iter().map(|s| { .unwrap().into_iter().map(|s| {
Rule { Rule {
selector: s.compound_selectors.clone(), selector: s.compound_selectors.clone(),

File diff suppressed because it is too large Load diff

View file

@ -8,55 +8,75 @@ use url::Url;
use encoding::EncodingRef; use encoding::EncodingRef;
use cssparser::{decode_stylesheet_bytes, tokenize, parse_stylesheet_rules, ToCss}; use cssparser::{Parser, decode_stylesheet_bytes,
use cssparser::ast::*; QualifiedRuleParser, AtRuleParser, RuleListParser, AtRuleType};
use selectors::{mod, ParserContext}; use string_cache::Namespace;
use properties; use selectors::{Selector, parse_selector_list};
use errors::{ErrorLoggerIterator, log_css_error}; use parser::ParserContext;
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
use namespaces::{NamespaceMap, parse_namespace_rule}; use namespaces::{NamespaceMap, parse_namespace_rule};
use media_queries::{mod, Device, MediaRule}; use media_queries::{mod, Device, MediaQueryList, parse_media_query_list};
use font_face::{FontFaceRule, Source, parse_font_face_rule, iter_font_face_rules_inner}; use font_face::{FontFaceRule, Source, parse_font_face_block, iter_font_face_rules_inner};
use selector_matching::StylesheetOrigin;
#[deriving(Clone, PartialEq, Eq, Copy, Show)]
pub enum Origin {
UserAgent,
Author,
User,
}
#[deriving(Show, PartialEq)]
pub struct Stylesheet { pub struct Stylesheet {
/// List of rules in the order they were found (important for /// List of rules in the order they were found (important for
/// cascading order) /// cascading order)
rules: Vec<CSSRule>, rules: Vec<CSSRule>,
pub origin: StylesheetOrigin, pub origin: Origin,
} }
#[deriving(Show, PartialEq)]
pub enum CSSRule { pub enum CSSRule {
Charset(String),
Namespace(Option<String>, Namespace),
Style(StyleRule), Style(StyleRule),
Media(MediaRule), Media(MediaRule),
FontFace(FontFaceRule), FontFace(FontFaceRule),
} }
#[deriving(Show, PartialEq)]
pub struct MediaRule {
pub media_queries: MediaQueryList,
pub rules: Vec<CSSRule>,
}
#[deriving(Show, PartialEq)]
pub struct StyleRule { pub struct StyleRule {
pub selectors: Vec<selectors::Selector>, pub selectors: Vec<Selector>,
pub declarations: properties::PropertyDeclarationBlock, pub declarations: PropertyDeclarationBlock,
} }
impl Stylesheet { impl Stylesheet {
pub fn from_bytes_iter<I: Iterator<Vec<u8>>>( pub fn from_bytes_iter<I: Iterator<Vec<u8>>>(
mut input: I, base_url: Url, protocol_encoding_label: Option<&str>, mut input: I, base_url: Url, protocol_encoding_label: Option<&str>,
environment_encoding: Option<EncodingRef>, origin: StylesheetOrigin) -> Stylesheet { environment_encoding: Option<EncodingRef>, origin: Origin) -> Stylesheet {
let mut bytes = vec!(); let mut bytes = vec!();
// TODO: incremental decoding and tokenization/parsing // TODO: incremental decoding and tokenization/parsing
for chunk in input { for chunk in input {
bytes.push_all(chunk.as_slice()) bytes.push_all(chunk.as_slice())
} }
Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label, environment_encoding, origin) Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label,
environment_encoding, origin)
} }
pub fn from_bytes(bytes: &[u8], pub fn from_bytes(bytes: &[u8],
base_url: Url, base_url: Url,
protocol_encoding_label: Option<&str>, protocol_encoding_label: Option<&str>,
environment_encoding: Option<EncodingRef>, environment_encoding: Option<EncodingRef>,
origin: StylesheetOrigin) origin: Origin)
-> Stylesheet { -> Stylesheet {
// TODO: bytes.as_slice could be bytes.container_as_bytes() // TODO: bytes.as_slice could be bytes.container_as_bytes()
let (string, _) = decode_stylesheet_bytes( let (string, _) = decode_stylesheet_bytes(
@ -64,121 +84,139 @@ impl Stylesheet {
Stylesheet::from_str(string.as_slice(), base_url, origin) Stylesheet::from_str(string.as_slice(), base_url, origin)
} }
pub fn from_str(css: &str, base_url: Url, origin: StylesheetOrigin) -> Stylesheet { pub fn from_str<'i>(css: &'i str, base_url: Url, origin: Origin) -> Stylesheet {
static STATE_CHARSET: uint = 1; let mut context = ParserContext {
static STATE_IMPORTS: uint = 2; stylesheet_origin: origin,
static STATE_NAMESPACES: uint = 3; base_url: &base_url,
static STATE_BODY: uint = 4; namespaces: NamespaceMap::new()
let parser_context = ParserContext {
origin: origin,
}; };
let rule_parser = MainRuleParser {
let mut state: uint = STATE_CHARSET; context: &mut context,
state: State::Start,
let mut rules = vec!(); };
let mut namespaces = NamespaceMap::new(); let rules = RuleListParser::new_for_stylesheet(&mut Parser::new(css), rule_parser)
.filter_map(|result| result.ok())
for rule in ErrorLoggerIterator(parse_stylesheet_rules(tokenize(css))) { .collect();
let next_state; // Unitialized to force each branch to set it.
match rule {
Rule::QualifiedRule(rule) => {
next_state = STATE_BODY;
parse_style_rule(&parser_context, rule, &mut rules, &namespaces, &base_url)
},
Rule::AtRule(rule) => {
let lower_name = rule.name.as_slice().to_ascii_lower();
match lower_name.as_slice() {
"charset" => {
if state > STATE_CHARSET {
log_css_error(rule.location, "@charset must be the first rule")
}
// Valid @charset rules are just ignored
next_state = STATE_IMPORTS;
},
"import" => {
if state > STATE_IMPORTS {
next_state = state;
log_css_error(rule.location,
"@import must be before any rule but @charset")
} else {
next_state = STATE_IMPORTS;
// TODO: support @import
log_css_error(rule.location, "@import is not supported yet")
}
},
"namespace" => {
if state > STATE_NAMESPACES {
next_state = state;
log_css_error(
rule.location,
"@namespace must be before any rule but @charset and @import"
)
} else {
next_state = STATE_NAMESPACES;
parse_namespace_rule(rule, &mut namespaces)
}
},
_ => {
next_state = STATE_BODY;
parse_nested_at_rule(&parser_context,
lower_name.as_slice(),
rule,
&mut rules,
&namespaces,
&base_url)
},
}
},
}
state = next_state;
}
Stylesheet { Stylesheet {
rules: rules,
origin: origin, origin: origin,
rules: rules,
} }
} }
} }
// lower_name is passed explicitly to avoid computing it twice.
pub fn parse_nested_at_rule(context: &ParserContext, fn parse_nested_rules(context: &mut ParserContext, input: &mut Parser) -> Vec<CSSRule> {
lower_name: &str, let parser = MainRuleParser {
rule: AtRule, context: context,
parent_rules: &mut Vec<CSSRule>, state: State::Body,
namespaces: &NamespaceMap, };
base_url: &Url) { RuleListParser::new_for_nested_rule(input, parser)
match lower_name { .filter_map(|result| result.ok())
"media" => { .collect()
media_queries::parse_media_rule(context, rule, parent_rules, namespaces, base_url) }
struct MainRuleParser<'a, 'b: 'a> {
context: &'a mut ParserContext<'b>,
state: State,
}
#[deriving(Eq, PartialEq, Ord, PartialOrd)]
enum State {
Start = 1,
Imports = 2,
Namespaces = 3,
Body = 4,
}
enum AtRulePrelude {
FontFace,
Media(MediaQueryList),
}
impl<'a, 'b> AtRuleParser<AtRulePrelude, CSSRule> for MainRuleParser<'a, 'b> {
fn parse_prelude(&mut self, name: &str, input: &mut Parser)
-> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
match_ignore_ascii_case! { name:
"charset" => {
if self.state <= State::Start {
// Valid @charset rules are just ignored
self.state = State::Imports;
let charset = try!(input.expect_string()).into_owned();
return Ok(AtRuleType::WithoutBlock(CSSRule::Charset(charset)))
} else {
return Err(()) // "@charset must be the first rule"
}
},
"import" => {
if self.state <= State::Imports {
self.state = State::Imports;
// TODO: support @import
return Err(()) // "@import is not supported yet"
} else {
return Err(()) // "@import must be before any rule but @charset"
}
},
"namespace" => {
if self.state <= State::Namespaces {
self.state = State::Namespaces;
let (prefix, namespace) = try!(parse_namespace_rule(self.context, input));
return Ok(AtRuleType::WithoutBlock(CSSRule::Namespace(prefix, namespace)))
} else {
return Err(()) // "@namespace must be before any rule but @charset and @import"
}
}
_ => {}
}
self.state = State::Body;
match_ignore_ascii_case! { name:
"media" => {
let media_queries = parse_media_query_list(input);
Ok(AtRuleType::WithBlock(AtRulePrelude::Media(media_queries)))
},
"font-face" => {
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
}
_ => Err(())
}
}
fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
match prelude {
AtRulePrelude::FontFace => {
parse_font_face_block(self.context, input).map(CSSRule::FontFace)
}
AtRulePrelude::Media(media_queries) => {
Ok(CSSRule::Media(MediaRule {
media_queries: media_queries,
rules: parse_nested_rules(self.context, input),
}))
}
} }
"font-face" => parse_font_face_rule(rule, parent_rules, base_url),
_ => log_css_error(rule.location,
format!("Unsupported at-rule: @{}", lower_name).as_slice())
} }
} }
pub fn parse_style_rule(context: &ParserContext,
rule: QualifiedRule, impl<'a, 'b> QualifiedRuleParser<Vec<Selector>, CSSRule> for MainRuleParser<'a, 'b> {
parent_rules: &mut Vec<CSSRule>, fn parse_prelude(&mut self, input: &mut Parser) -> Result<Vec<Selector>, ()> {
namespaces: &NamespaceMap, self.state = State::Body;
base_url: &Url) { parse_selector_list(self.context, input)
let QualifiedRule { }
location,
prelude, fn parse_block(&mut self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> {
block Ok(CSSRule::Style(StyleRule {
} = rule; selectors: prelude,
// FIXME: avoid doing this for valid selectors declarations: parse_property_declaration_list(self.context, input)
let serialized = prelude.to_css_string(); }))
match selectors::parse_selector_list(context, prelude.into_iter(), namespaces) {
Ok(selectors) => parent_rules.push(CSSRule::Style(StyleRule{
selectors: selectors,
declarations: properties::parse_property_declaration_list(block.into_iter(), base_url)
})),
Err(()) => log_css_error(location, format!(
"Invalid/unsupported selector: {}", serialized).as_slice()),
} }
} }
pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device, pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device,
callback: |&StyleRule|) { callback: |&StyleRule|) {
for rule in rules.iter() { for rule in rules.iter() {
@ -187,7 +225,9 @@ pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device,
CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) { CSSRule::Media(ref rule) => if rule.media_queries.evaluate(device) {
iter_style_rules(rule.rules.as_slice(), device, |s| callback(s)) iter_style_rules(rule.rules.as_slice(), device, |s| callback(s))
}, },
CSSRule::FontFace(_) => {}, CSSRule::FontFace(..) |
CSSRule::Charset(..) |
CSSRule::Namespace(..) => {}
} }
} }
} }
@ -196,7 +236,10 @@ pub fn iter_stylesheet_media_rules(stylesheet: &Stylesheet, callback: |&MediaRul
for rule in stylesheet.rules.iter() { for rule in stylesheet.rules.iter() {
match *rule { match *rule {
CSSRule::Media(ref rule) => callback(rule), CSSRule::Media(ref rule) => callback(rule),
_ => {} CSSRule::Style(..) |
CSSRule::FontFace(..) |
CSSRule::Charset(..) |
CSSRule::Namespace(..) => {}
} }
} }
} }
@ -213,3 +256,134 @@ pub fn iter_font_face_rules(stylesheet: &Stylesheet, device: &Device,
callback: |family: &str, source: &Source|) { callback: |family: &str, source: &Source|) {
iter_font_face_rules_inner(stylesheet.rules.as_slice(), device, callback) iter_font_face_rules_inner(stylesheet.rules.as_slice(), device, callback)
} }
#[test]
fn test_parse_stylesheet() {
use std::sync::Arc;
use cssparser;
use selectors::*;
use string_cache::Atom;
use properties::{PropertyDeclaration, DeclaredValue, longhands};
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".into_string(), 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::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".into_string()),
parsed: cssparser::Color::RGBA(cssparser::RGBA {
red: 0., green: 0., blue: 1., alpha: 1.
}),
}
)),
]),
important: Arc::new(vec![]),
},
}),
],
});
}

View file

@ -5,10 +5,6 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![macro_escape] #![macro_escape]
use url::{Url, UrlParser};
pub use servo_util::geometry::Au;
macro_rules! define_css_keyword_enum { macro_rules! define_css_keyword_enum {
($name: ident: $( $css: expr => $variant: ident ),+,) => { ($name: ident: $( $css: expr => $variant: ident ),+,) => {
@ -22,14 +18,9 @@ macro_rules! define_css_keyword_enum {
} }
impl $name { impl $name {
pub fn parse(component_value: &::cssparser::ast::ComponentValue) -> Result<$name, ()> { pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
match component_value { match_ignore_ascii_case! { try!(input.expect_ident()):
&::cssparser::ast::ComponentValue::Ident(ref value) => { $( $css => Ok($name::$variant) ),+
match_ignore_ascii_case! { value:
$( $css => Ok($name::$variant) ),+
_ => Err(())
}
}
_ => Err(()) _ => Err(())
} }
} }
@ -63,12 +54,12 @@ pub mod specified {
use std::fmt; use std::fmt;
use std::fmt::{Formatter, Show}; use std::fmt::{Formatter, Show};
use url::Url; use url::Url;
use cssparser::{mod, ToCss, CssStringWriter}; use cssparser::{mod, Token, Parser, ToCss, CssStringWriter};
use cssparser::ast::*; use parser::ParserContext;
use cssparser::ast::ComponentValue::*;
use text_writer::{mod, TextWriter}; use text_writer::{mod, TextWriter};
use parsing_utils::{mod, BufferedIter, ParserIter}; use servo_util::geometry::Au;
use super::{Au, CSSFloat}; use super::CSSFloat;
use super::computed;
#[deriving(Clone, PartialEq)] #[deriving(Clone, PartialEq)]
pub struct CSSColor { pub struct CSSColor {
@ -76,17 +67,16 @@ pub mod specified {
pub authored: Option<String>, pub authored: Option<String>,
} }
impl CSSColor { impl CSSColor {
pub fn parse(component_value: &ComponentValue) -> Result<CSSColor, ()> { pub fn parse(input: &mut Parser) -> Result<CSSColor, ()> {
let parsed = cssparser::Color::parse(component_value); let start_position = input.position();
parsed.map(|parsed| { let authored = match input.next() {
let authored = match component_value { Ok(Token::Ident(s)) => Some(s.into_owned()),
&Ident(ref s) => Some(s.clone()), _ => None,
_ => None, };
}; input.reset(start_position);
CSSColor { Ok(CSSColor {
parsed: parsed, parsed: try!(cssparser::Color::parse(input)),
authored: authored, authored: authored,
}
}) })
} }
} }
@ -104,7 +94,7 @@ pub mod specified {
} }
} }
#[deriving(Clone)] #[deriving(Clone, PartialEq)]
pub struct CSSRGBA { pub struct CSSRGBA {
pub parsed: cssparser::RGBA, pub parsed: cssparser::RGBA,
pub authored: Option<String>, pub authored: Option<String>,
@ -150,13 +140,6 @@ pub mod specified {
/// This cannot be specified by the user directly and is only generated by /// This cannot be specified by the user directly and is only generated by
/// `Stylist::synthesize_rules_for_legacy_attributes()`. /// `Stylist::synthesize_rules_for_legacy_attributes()`.
ServoCharacterWidth(i32), ServoCharacterWidth(i32),
// XXX uncomment when supported:
// Ch(CSSFloat),
// Vw(CSSFloat),
// Vh(CSSFloat),
// Vmin(CSSFloat),
// Vmax(CSSFloat),
} }
impl fmt::Show for Length { impl fmt::Show for Length {
@ -184,23 +167,24 @@ pub mod specified {
const AU_PER_PC: CSSFloat = AU_PER_PT * 12.; const AU_PER_PC: CSSFloat = AU_PER_PT * 12.;
impl Length { impl Length {
#[inline] #[inline]
fn parse_internal(input: &ComponentValue, negative_ok: bool) -> Result<Length, ()> { fn parse_internal(input: &mut Parser, negative_ok: bool) -> Result<Length, ()> {
match input { match try!(input.next()) {
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0. Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => {
=> Length::parse_dimension(value.value, unit.as_slice()), Length::parse_dimension(value.value, unit.as_slice())
&Number(ref value) if value.value == 0. => Ok(Length::Au(Au(0))), }
Token::Number(ref value) if value.value == 0. => Ok(Length::Au(Au(0))),
_ => Err(()) _ => Err(())
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn parse(input: &ComponentValue) -> Result<Length, ()> { pub fn parse(input: &mut Parser) -> Result<Length, ()> {
Length::parse_internal(input, /* negative_ok = */ true) Length::parse_internal(input, /* negative_ok = */ true)
} }
pub fn parse_non_negative(input: &ComponentValue) -> Result<Length, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> {
Length::parse_internal(input, /* negative_ok = */ false) Length::parse_internal(input, /* negative_ok = */ false)
} }
pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> { pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> {
match unit.to_ascii_lower().as_slice() { match_ignore_ascii_case! { unit:
"px" => Ok(Length::from_px(value)), "px" => Ok(Length::from_px(value)),
"in" => Ok(Length::Au(Au((value * AU_PER_IN) as i32))), "in" => Ok(Length::Au(Au((value * AU_PER_IN) as i32))),
"cm" => Ok(Length::Au(Au((value * AU_PER_CM) as i32))), "cm" => Ok(Length::Au(Au((value * AU_PER_CM) as i32))),
@ -209,7 +193,7 @@ pub mod specified {
"pc" => Ok(Length::Au(Au((value * AU_PER_PC) as i32))), "pc" => Ok(Length::Au(Au((value * AU_PER_PC) as i32))),
"em" => Ok(Length::Em(value)), "em" => Ok(Length::Em(value)),
"ex" => Ok(Length::Ex(value)), "ex" => Ok(Length::Ex(value)),
"rem" => Ok(Length::Rem(value)), "rem" => Ok(Length::Rem(value))
_ => Err(()) _ => Err(())
} }
} }
@ -219,6 +203,7 @@ pub mod specified {
} }
} }
#[deriving(Clone, PartialEq, Copy)] #[deriving(Clone, PartialEq, Copy)]
pub enum LengthOrPercentage { pub enum LengthOrPercentage {
Length(Length), Length(Length),
@ -239,26 +224,29 @@ pub mod specified {
} }
} }
impl LengthOrPercentage { impl LengthOrPercentage {
fn parse_internal(input: &ComponentValue, negative_ok: bool) fn parse_internal(input: &mut Parser, negative_ok: bool)
-> Result<LengthOrPercentage, ()> { -> Result<LengthOrPercentage, ()> {
match input { match try!(input.next()) {
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => {
Length::parse_dimension(value.value, unit.as_slice()) Length::parse_dimension(value.value, unit.as_slice())
.map(LengthOrPercentage::Length), .map(LengthOrPercentage::Length)
&Percentage(ref value) if negative_ok || value.value >= 0. => }
Ok(LengthOrPercentage::Percentage(value.value / 100.)), Token::Percentage(ref value) if negative_ok || value.unit_value >= 0. => {
&Number(ref value) if value.value == 0. => Ok(LengthOrPercentage::Percentage(value.unit_value))
Ok(LengthOrPercentage::Length(Length::Au(Au(0)))), }
Token::Number(ref value) if value.value == 0. => {
Ok(LengthOrPercentage::Length(Length::Au(Au(0))))
}
_ => Err(()) _ => Err(())
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentage, ()> { pub fn parse(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
LengthOrPercentage::parse_internal(input, /* negative_ok = */ true) LengthOrPercentage::parse_internal(input, /* negative_ok = */ true)
} }
#[inline] #[inline]
pub fn parse_non_negative(input: &ComponentValue) -> Result<LengthOrPercentage, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
LengthOrPercentage::parse_internal(input, /* negative_ok = */ false) LengthOrPercentage::parse_internal(input, /* negative_ok = */ false)
} }
} }
@ -285,26 +273,31 @@ pub mod specified {
} }
} }
impl LengthOrPercentageOrAuto { impl LengthOrPercentageOrAuto {
fn parse_internal(input: &ComponentValue, negative_ok: bool) fn parse_internal(input: &mut Parser, negative_ok: bool)
-> Result<LengthOrPercentageOrAuto, ()> { -> Result<LengthOrPercentageOrAuto, ()> {
match input { match try!(input.next()) {
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => {
Length::parse_dimension(value.value, unit.as_slice()).map(LengthOrPercentageOrAuto::Length), Length::parse_dimension(value.value, unit.as_slice())
&Percentage(ref value) if negative_ok || value.value >= 0. => .map(LengthOrPercentageOrAuto::Length)
Ok(LengthOrPercentageOrAuto::Percentage(value.value / 100.)), }
&Number(ref value) if value.value == 0. => Token::Percentage(ref value) if negative_ok || value.unit_value >= 0. => {
Ok(LengthOrPercentageOrAuto::Length(Length::Au(Au(0)))), Ok(LengthOrPercentageOrAuto::Percentage(value.unit_value))
&Ident(ref value) if value.as_slice().eq_ignore_ascii_case("auto") => }
Ok(LengthOrPercentageOrAuto::Auto), Token::Number(ref value) if value.value == 0. => {
Ok(LengthOrPercentageOrAuto::Length(Length::Au(Au(0))))
}
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => {
Ok(LengthOrPercentageOrAuto::Auto)
}
_ => Err(()) _ => Err(())
} }
} }
#[inline] #[inline]
pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentageOrAuto, ()> { pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ true) LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ true)
} }
#[inline] #[inline]
pub fn parse_non_negative(input: &ComponentValue) -> Result<LengthOrPercentageOrAuto, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ false) LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ false)
} }
} }
@ -331,25 +324,32 @@ pub mod specified {
} }
} }
impl LengthOrPercentageOrNone { impl LengthOrPercentageOrNone {
fn parse_internal(input: &ComponentValue, negative_ok: bool) fn parse_internal(input: &mut Parser, negative_ok: bool)
-> Result<LengthOrPercentageOrNone, ()> { -> Result<LengthOrPercentageOrNone, ()> {
match input { match try!(input.next()) {
&Dimension(ref value, ref unit) if negative_ok || value.value >= 0. Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => {
=> Length::parse_dimension(value.value, unit.as_slice()).map(LengthOrPercentageOrNone::Length), Length::parse_dimension(value.value, unit.as_slice())
&Percentage(ref value) if negative_ok || value.value >= 0. .map(LengthOrPercentageOrNone::Length)
=> Ok(LengthOrPercentageOrNone::Percentage(value.value / 100.)), }
&Number(ref value) if value.value == 0. => Ok(LengthOrPercentageOrNone::Length(Length::Au(Au(0)))), Token::Percentage(ref value) if negative_ok || value.unit_value >= 0. => {
&Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => Ok(LengthOrPercentageOrNone::None), Ok(LengthOrPercentageOrNone::Percentage(value.unit_value))
}
Token::Number(ref value) if value.value == 0. => {
Ok(LengthOrPercentageOrNone::Length(Length::Au(Au(0))))
}
Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => {
Ok(LengthOrPercentageOrNone::None)
}
_ => Err(()) _ => Err(())
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentageOrNone, ()> { pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> {
LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ true) LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ true)
} }
#[inline] #[inline]
pub fn parse_non_negative(input: &ComponentValue) -> Result<LengthOrPercentageOrNone, ()> { pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> {
LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ false) LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ false)
} }
} }
@ -366,19 +366,27 @@ pub mod specified {
Bottom, Bottom,
} }
impl PositionComponent { impl PositionComponent {
pub fn parse(input: &ComponentValue) -> Result<PositionComponent, ()> { pub fn parse(input: &mut Parser) -> Result<PositionComponent, ()> {
match input { match try!(input.next()) {
&Dimension(ref value, ref unit) => Token::Dimension(ref value, ref unit) => {
Length::parse_dimension(value.value, unit.as_slice()).map(PositionComponent::Length), Length::parse_dimension(value.value, unit.as_slice())
&Percentage(ref value) => Ok(PositionComponent::Percentage(value.value / 100.)), .map(PositionComponent::Length)
&Number(ref value) if value.value == 0. => Ok(PositionComponent::Length(Length::Au(Au(0)))), }
&Ident(ref value) => { Token::Percentage(ref value) => {
if value.as_slice().eq_ignore_ascii_case("center") { Ok(PositionComponent::Center) } Ok(PositionComponent::Percentage(value.unit_value))
else if value.as_slice().eq_ignore_ascii_case("left") { Ok(PositionComponent::Left) } }
else if value.as_slice().eq_ignore_ascii_case("right") { Ok(PositionComponent::Right) } Token::Number(ref value) if value.value == 0. => {
else if value.as_slice().eq_ignore_ascii_case("top") { Ok(PositionComponent::Top) } Ok(PositionComponent::Length(Length::Au(Au(0))))
else if value.as_slice().eq_ignore_ascii_case("bottom") { Ok(PositionComponent::Bottom) } }
else { Err(()) } Token::Ident(value) => {
match_ignore_ascii_case! { value:
"center" => Ok(PositionComponent::Center),
"left" => Ok(PositionComponent::Left),
"right" => Ok(PositionComponent::Right),
"top" => Ok(PositionComponent::Top),
"bottom" => Ok(PositionComponent::Bottom)
_ => Err(())
}
} }
_ => Err(()) _ => Err(())
} }
@ -389,8 +397,10 @@ pub mod specified {
PositionComponent::Length(x) => LengthOrPercentage::Length(x), PositionComponent::Length(x) => LengthOrPercentage::Length(x),
PositionComponent::Percentage(x) => LengthOrPercentage::Percentage(x), PositionComponent::Percentage(x) => LengthOrPercentage::Percentage(x),
PositionComponent::Center => LengthOrPercentage::Percentage(0.5), PositionComponent::Center => LengthOrPercentage::Percentage(0.5),
PositionComponent::Left | PositionComponent::Top => LengthOrPercentage::Percentage(0.0), PositionComponent::Left |
PositionComponent::Right | PositionComponent::Bottom => LengthOrPercentage::Percentage(1.0), PositionComponent::Top => LengthOrPercentage::Percentage(0.0),
PositionComponent::Right |
PositionComponent::Bottom => LengthOrPercentage::Percentage(1.0),
} }
} }
} }
@ -416,30 +426,24 @@ pub mod specified {
} }
} }
static DEG_TO_RAD: CSSFloat = PI / 180.0; const RAD_PER_DEG: CSSFloat = PI / 180.0;
static GRAD_TO_RAD: CSSFloat = PI / 200.0; const RAD_PER_GRAD: CSSFloat = PI / 200.0;
const RAD_PER_TURN: CSSFloat = PI * 2.0;
impl Angle { impl Angle {
/// Parses an angle according to CSS-VALUES § 6.1. /// Parses an angle according to CSS-VALUES § 6.1.
fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle,()> { pub fn parse(input: &mut Parser) -> Result<Angle, ()> {
if unit.eq_ignore_ascii_case("deg") { match try!(input.next()) {
Ok(Angle(value * DEG_TO_RAD)) Token::Dimension(value, unit) => {
} else if unit.eq_ignore_ascii_case("grad") { match_ignore_ascii_case! { unit:
Ok(Angle(value * GRAD_TO_RAD)) "deg" => Ok(Angle(value.value * RAD_PER_DEG)),
} else if unit.eq_ignore_ascii_case("rad") { "grad" => Ok(Angle(value.value * RAD_PER_GRAD)),
Ok(Angle(value)) "turn" => Ok(Angle(value.value * RAD_PER_TURN)),
} else if unit.eq_ignore_ascii_case("turn") { "rad" => Ok(Angle(value.value))
Ok(Angle(value * 2.0 * PI)) _ => Err(())
} else { }
Err(())
}
}
/// Parses an angle from a token according to CSS-VALUES § 6.1.
pub fn parse(value: &ComponentValue) -> Result<Angle,()> {
match *value {
Dimension(ref value, ref unit) => {
Angle::parse_dimension(value.value, unit.as_slice())
} }
Token::Number(ref value) if value.value == 0. => Ok(Angle(0.)),
_ => Err(()) _ => Err(())
} }
} }
@ -471,33 +475,30 @@ pub mod specified {
} }
impl Image { impl Image {
pub fn from_component_value(component_value: &ComponentValue, base_url: &Url) pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Image, ()> {
-> Result<Image,()> { match try!(input.next()) {
match component_value { Token::Url(url) => {
&URL(ref url) => { Ok(Image::Url(context.parse_url(url.as_slice())))
let image_url = super::parse_url(url.as_slice(), base_url); }
Ok(Image::Url(image_url)) Token::Function(name) => {
}, match_ignore_ascii_case! { name:
&Function(ref name, ref args) => { "linear-gradient" => {
if name.as_slice().eq_ignore_ascii_case("linear-gradient") { Ok(Image::LinearGradient(try!(
Ok(Image::LinearGradient(try!( input.parse_nested_block(LinearGradient::parse_function))))
super::specified::LinearGradient::parse_function( }
args.as_slice())))) _ => Err(())
} else {
Err(())
} }
} }
_ => Err(()), _ => Err(())
} }
} }
pub fn to_computed_value(self, context: &super::computed::Context) pub fn to_computed_value(self, context: &computed::Context) -> computed::Image {
-> super::computed::Image {
match self { match self {
Image::Url(url) => super::computed::Image::Url(url), Image::Url(url) => computed::Image::Url(url),
Image::LinearGradient(linear_gradient) => { Image::LinearGradient(linear_gradient) => {
super::computed::Image::LinearGradient( computed::Image::LinearGradient(
super::computed::LinearGradient::compute(linear_gradient, context)) computed::LinearGradient::compute(linear_gradient, context))
} }
} }
} }
@ -585,154 +586,105 @@ pub mod specified {
define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right) define_css_keyword_enum!(HorizontalDirection: "left" => Left, "right" => Right)
define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom) define_css_keyword_enum!(VerticalDirection: "top" => Top, "bottom" => Bottom)
fn parse_color_stop(source: ParserIter) -> Result<ColorStop,()> { fn parse_one_color_stop(input: &mut Parser) -> Result<ColorStop, ()> {
let color = match source.next() {
Some(color) => try!(CSSColor::parse(color)),
None => return Err(()),
};
let position = match source.next() {
None => None,
Some(value) => {
match *value {
Comma => {
source.push_back(value);
None
}
ref position => Some(try!(LengthOrPercentage::parse(position))),
}
}
};
Ok(ColorStop { Ok(ColorStop {
color: color, color: try!(CSSColor::parse(input)),
position: position, position: input.try(LengthOrPercentage::parse).ok(),
}) })
} }
impl LinearGradient { impl LinearGradient {
/// Parses a linear gradient from the given arguments. /// Parses a linear gradient from the given arguments.
pub fn parse_function(args: &[ComponentValue]) -> Result<LinearGradient,()> { pub fn parse_function(input: &mut Parser) -> Result<LinearGradient, ()> {
let mut source = BufferedIter::new(args.skip_whitespace()); let angle_or_corner = if input.try(|input| input.expect_ident_matching("to")).is_ok() {
let (horizontal, vertical) =
// Parse the angle. if let Ok(value) = input.try(HorizontalDirection::parse) {
let (angle_or_corner, need_to_parse_comma) = match source.next() { (Some(value), input.try(VerticalDirection::parse).ok())
None => return Err(()), } else {
Some(token) => { let value = try!(VerticalDirection::parse(input));
match *token { (input.try(HorizontalDirection::parse).ok(), Some(value))
Dimension(ref value, ref unit) => { };
match Angle::parse_dimension(value.value, unit.as_slice()) { try!(input.expect_comma());
Ok(angle) => { match (horizontal, vertical) {
(AngleOrCorner::Angle(angle), true) (None, Some(VerticalDirection::Top)) => {
} AngleOrCorner::Angle(Angle(0.0))
Err(()) => { },
source.push_back(token); (Some(HorizontalDirection::Right), None) => {
(AngleOrCorner::Angle(Angle(PI)), false) AngleOrCorner::Angle(Angle(PI * 0.5))
} },
} (None, Some(VerticalDirection::Bottom)) => {
} AngleOrCorner::Angle(Angle(PI))
Ident(ref ident) if ident.as_slice().eq_ignore_ascii_case("to") => { },
let (mut horizontal, mut vertical) = (None, None); (Some(HorizontalDirection::Left), None) => {
loop { AngleOrCorner::Angle(Angle(PI * 1.5))
match source.next() { },
None => break, (Some(horizontal), Some(vertical)) => {
Some(token) => { AngleOrCorner::Corner(horizontal, vertical)
match *token {
Ident(ref ident) => {
let ident = ident.as_slice();
if ident.eq_ignore_ascii_case("top") &&
vertical.is_none() {
vertical = Some(VerticalDirection::Top)
} else if ident.eq_ignore_ascii_case("bottom") &&
vertical.is_none() {
vertical = Some(VerticalDirection::Bottom)
} else if ident.eq_ignore_ascii_case("left") &&
horizontal.is_none() {
horizontal = Some(HorizontalDirection::Left)
} else if ident.eq_ignore_ascii_case("right") &&
horizontal.is_none() {
horizontal = Some(HorizontalDirection::Right)
} else {
return Err(())
}
}
Comma => {
source.push_back(token);
break
}
_ => return Err(()),
}
}
}
}
(match (horizontal, vertical) {
(None, Some(VerticalDirection::Top)) => {
AngleOrCorner::Angle(Angle(0.0))
},
(Some(HorizontalDirection::Right), None) => {
AngleOrCorner::Angle(Angle(PI * 0.5))
},
(None, Some(VerticalDirection::Bottom)) => {
AngleOrCorner::Angle(Angle(PI))
},
(Some(HorizontalDirection::Left), None) => {
AngleOrCorner::Angle(Angle(PI * 1.5))
},
(Some(horizontal), Some(vertical)) => {
AngleOrCorner::Corner(horizontal, vertical)
}
(None, None) => return Err(()),
}, true)
}
_ => {
source.push_back(token);
(AngleOrCorner::Angle(Angle(PI)), false)
}
} }
(None, None) => unreachable!(),
} }
}; } else if let Ok(angle) = input.try(Angle::parse) {
try!(input.expect_comma());
// Parse the color stops. AngleOrCorner::Angle(angle)
let stops = if need_to_parse_comma {
match source.next() {
Some(&Comma) => {
try!(parsing_utils::parse_comma_separated(&mut source, parse_color_stop))
}
None => Vec::new(),
Some(_) => return Err(()),
}
} else { } else {
try!(parsing_utils::parse_comma_separated(&mut source, parse_color_stop)) AngleOrCorner::Angle(Angle(PI))
}; };
// Parse the color stops.
let stops = try!(input.parse_comma_separated(parse_one_color_stop));
if stops.len() < 2 { if stops.len() < 2 {
return Err(()) return Err(())
} }
Ok(LinearGradient { Ok(LinearGradient {
angle_or_corner: angle_or_corner, angle_or_corner: angle_or_corner,
stops: stops, stops: stops,
}) })
} }
} }
pub fn parse_border_width(input: &mut Parser) -> Result<Length, ()> {
input.try(Length::parse_non_negative).or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()):
"thin" => Ok(Length::from_px(1.)),
"medium" => Ok(Length::from_px(3.)),
"thick" => Ok(Length::from_px(5.))
_ => Err(())
}
})
}
define_css_keyword_enum! { BorderStyle:
"none" => none,
"solid" => solid,
"double" => double,
"dotted" => dotted,
"dashed" => dashed,
"hidden" => hidden,
"groove" => groove,
"ridge" => ridge,
"inset" => inset,
"outset" => outset,
}
} }
pub mod computed { pub mod computed {
pub use super::specified::{Angle, AngleOrCorner, HorizontalDirection}; pub use super::specified::BorderStyle;
pub use super::specified::{VerticalDirection}; use super::specified::{AngleOrCorner};
use super::{specified, CSSFloat};
pub use cssparser::Color as CSSColor; pub use cssparser::Color as CSSColor;
use super::*; use properties::longhands;
use super::super::longhands;
use std::fmt; use std::fmt;
use url::Url; use url::Url;
use servo_util::geometry::Au;
#[allow(missing_copy_implementations)] // Its kinda big #[allow(missing_copy_implementations)] // Its kinda big
pub struct Context { pub struct Context {
pub inherited_font_weight: longhands::font_weight::computed_value::T, pub inherited_font_weight: longhands::font_weight::computed_value::T,
pub inherited_font_size: longhands::font_size::computed_value::T, pub inherited_font_size: longhands::font_size::computed_value::T,
pub inherited_text_decorations_in_effect: longhands::_servo_text_decorations_in_effect::T, pub inherited_text_decorations_in_effect:
pub inherited_height: longhands::height::T, longhands::_servo_text_decorations_in_effect::computed_value::T,
pub inherited_height: longhands::height::computed_value::T,
pub color: longhands::color::computed_value::T, pub color: longhands::color::computed_value::T,
pub text_decoration: longhands::text_decoration::computed_value::T, pub text_decoration: longhands::text_decoration::computed_value::T,
pub font_size: longhands::font_size::computed_value::T, pub font_size: longhands::font_size::computed_value::T,
@ -750,10 +702,16 @@ pub mod computed {
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[inline] #[inline]
pub fn compute_CSSColor(value: specified::CSSColor, _context: &computed::Context) -> CSSColor { pub fn compute_CSSColor(value: specified::CSSColor, _context: &Context) -> CSSColor {
value.parsed value.parsed
} }
#[allow(non_snake_case)]
#[inline]
pub fn compute_BorderStyle(value: BorderStyle, _context: &Context) -> BorderStyle {
value
}
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[inline] #[inline]
pub fn compute_Au(value: specified::Length, context: &Context) -> Au { pub fn compute_Au(value: specified::Length, context: &Context) -> Au {
@ -951,8 +909,3 @@ pub mod computed {
pub type Length = Au; pub type Length = Au;
} }
pub fn parse_url(input: &str, base_url: &Url) -> Url {
UrlParser::new().base_url(base_url).parse(input)
.unwrap_or_else(|_| Url::parse("about:invalid").unwrap())
}

45
ports/cef/Cargo.lock generated
View file

@ -131,9 +131,10 @@ dependencies = [
[[package]] [[package]]
name = "cssparser" name = "cssparser"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/servo/rust-cssparser#110bf3052d016bf6eda0689fb21cf971e2c23dc8" source = "git+https://github.com/servo/rust-cssparser#8d1b3e220e795f7baaa940919059d5f4ef4ec28c"
dependencies = [ dependencies = [
"encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"text_writer 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "text_writer 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -310,35 +311,6 @@ dependencies = [
"gl_generator 0.0.1 (git+https://github.com/bjz/gl-rs.git)", "gl_generator 0.0.1 (git+https://github.com/bjz/gl-rs.git)",
] ]
[[package]]
name = "glfw"
version = "0.0.1"
source = "git+https://github.com/servo/glfw-rs?ref=servo#b186cb444e349a36b992445dc5cb2c99d38f2a42"
dependencies = [
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4)",
"semver 0.1.4 (git+https://github.com/rust-lang/semver)",
]
[[package]]
name = "glfw-sys"
version = "3.0.4"
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#765dace7e4125b87c764f5ac0e7a80eae5c550b2"
[[package]]
name = "glfw_app"
version = "0.0.1"
dependencies = [
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
"compositing 0.0.1",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"msg 0.0.1",
"time 0.1.0 (git+https://github.com/rust-lang/time)",
"util 0.0.1",
]
[[package]] [[package]]
name = "glutin" name = "glutin"
version = "0.0.2" version = "0.0.2"
@ -496,6 +468,11 @@ dependencies = [
"pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "matches"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.0.1" version = "0.0.1"
@ -639,18 +616,13 @@ dependencies = [
"util 0.0.1", "util 0.0.1",
] ]
[[package]]
name = "semver"
version = "0.1.4"
source = "git+https://github.com/rust-lang/semver#58dc6b1999d345ca925a2f12a6a84676e823e179"
[[package]] [[package]]
name = "servo" name = "servo"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"compositing 0.0.1", "compositing 0.0.1",
"gfx 0.0.1", "gfx 0.0.1",
"glfw_app 0.0.1", "glutin_app 0.0.1",
"layout 0.0.1", "layout 0.0.1",
"msg 0.0.1", "msg 0.0.1",
"net 0.0.1", "net 0.0.1",
@ -702,6 +674,7 @@ dependencies = [
"encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"lazy_static 0.1.0 (git+https://github.com/Kimundi/lazy-static.rs)", "lazy_static 0.1.0 (git+https://github.com/Kimundi/lazy-static.rs)",
"matches 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)",
"string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)",

9
ports/gonk/Cargo.lock generated
View file

@ -102,9 +102,10 @@ dependencies = [
[[package]] [[package]]
name = "cssparser" name = "cssparser"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/servo/rust-cssparser#110bf3052d016bf6eda0689fb21cf971e2c23dc8" source = "git+https://github.com/servo/rust-cssparser#8d1b3e220e795f7baaa940919059d5f4ef4ec28c"
dependencies = [ dependencies = [
"encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"text_writer 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "text_writer 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -410,6 +411,11 @@ dependencies = [
"pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "matches"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.0.1" version = "0.0.1"
@ -610,6 +616,7 @@ dependencies = [
"encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"lazy_static 0.1.0 (git+https://github.com/Kimundi/lazy-static.rs)", "lazy_static 0.1.0 (git+https://github.com/Kimundi/lazy-static.rs)",
"matches 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)",
"string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_macros 0.0.0 (git+https://github.com/servo/string-cache)",