diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/layout/display_list_builder.rs | 12 | ||||
-rw-r--r-- | components/script/dom/element.rs | 14 | ||||
-rw-r--r-- | components/script/dom/node.rs | 12 | ||||
-rw-r--r-- | components/servo/Cargo.lock | 9 | ||||
-rw-r--r-- | components/style/Cargo.toml | 1 | ||||
-rw-r--r-- | components/style/errors.rs | 32 | ||||
-rw-r--r-- | components/style/font_face.rs | 175 | ||||
-rw-r--r-- | components/style/legacy.rs | 33 | ||||
-rw-r--r-- | components/style/lib.rs | 47 | ||||
-rw-r--r-- | components/style/media_queries.rs | 259 | ||||
-rw-r--r-- | components/style/namespaces.rs | 69 | ||||
-rw-r--r-- | components/style/parser.rs | 42 | ||||
-rw-r--r-- | components/style/parsing_utils.rs | 101 | ||||
-rw-r--r-- | components/style/properties/mod.rs.mako | 1858 | ||||
-rw-r--r-- | components/style/selector_matching.rs | 35 | ||||
-rw-r--r-- | components/style/selectors.rs | 644 | ||||
-rw-r--r-- | components/style/stylesheets.rs | 404 | ||||
-rw-r--r-- | components/style/values.rs (renamed from components/style/properties/common_types.rs) | 467 |
18 files changed, 1932 insertions, 2282 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 2e4ae665ea9..b866ae2c2fd 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -46,8 +46,8 @@ use servo_util::opts; use std::default::Default; use std::iter::repeat; use std::num::FloatMath; -use style::computed::{AngleOrCorner, LengthOrPercentage, HorizontalDirection, VerticalDirection}; -use style::computed::{Image, LinearGradient}; +use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection}; +use style::computed::{Image, LinearGradient, LengthOrPercentage}; use style::computed_values::filter::Filter; use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; 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. 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), - 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), - 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), - 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), } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index b684b90579e..efafdbe7b11 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -50,8 +50,8 @@ use dom::node::{window_from_node}; use dom::nodelist::NodeList; use dom::virtualmethods::{VirtualMethods, vtable_for}; use devtools_traits::AttrInfo; -use style::{mod, StylesheetOrigin, SimpleColorAttribute, UnsignedIntegerAttribute}; -use style::{IntegerAttribute, LengthAttribute, ParserContext, matches}; +use style::{mod, SimpleColorAttribute, UnsignedIntegerAttribute}; +use style::{IntegerAttribute, LengthAttribute, matches}; use servo_util::namespace; 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 fn Matches(self, selectors: DOMString) -> Fallible<bool> { - let parser_context = ParserContext { - origin: StylesheetOrigin::Author, - }; - match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { + match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) { Err(()) => Err(Syntax), Ok(ref selectors) => { 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 fn Closest(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { - let parser_context = ParserContext { - origin: StylesheetOrigin::Author, - }; - match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { + match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) { Err(()) => Err(Syntax), Ok(ref selectors) => { let root: JSRef<Node> = NodeCast::from_ref(self); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index d65a82880c5..d23c14a586f 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -48,7 +48,7 @@ use devtools_traits::NodeInfo; use script_traits::UntrustedNodeAddress; use servo_util::geometry::Au; 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::jsfriendapi; @@ -742,10 +742,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { // http://dom.spec.whatwg.org/#dom-parentnode-queryselector fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { // Step 1. - let parser_context = ParserContext { - origin: StylesheetOrigin::Author, - }; - match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { + match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) { // Step 2. Err(()) => return Err(Syntax), // Step 3. @@ -767,10 +764,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { // Step 1. let nodes; let root = self.ancestors().last().unwrap_or(self.clone()); - let parser_context = ParserContext { - origin: StylesheetOrigin::Author, - }; - match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { + match style::parse_author_origin_selector_list_from_str(selectors.as_slice()) { // Step 2. Err(()) => return Err(Syntax), // Step 3. diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 8d302666dd8..eebb40319ad 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -123,9 +123,10 @@ dependencies = [ [[package]] name = "cssparser" version = "0.1.1" -source = "git+https://github.com/servo/rust-cssparser#110bf3052d016bf6eda0689fb21cf971e2c23dc8" +source = "git+https://github.com/servo/rust-cssparser#8d1b3e220e795f7baaa940919059d5f4ef4ec28c" dependencies = [ "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)", ] @@ -489,6 +490,11 @@ dependencies = [ ] [[package]] +name = "matches" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "mime" version = "0.0.1" source = "git+https://github.com/hyperium/mime.rs#7898f1c29c7f5d35d0c3c7aed37ebcfc95a40873" @@ -678,6 +684,7 @@ dependencies = [ "encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)", "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", "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)", diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index ec0a3fcc2bb..8b053ffae62 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -36,3 +36,4 @@ git = "https://github.com/servo/string-cache" [dependencies] text_writer = "0.1.1" encoding = "0.2" +matches = "0.1" diff --git a/components/style/errors.rs b/components/style/errors.rs deleted file mode 100644 index 1f238aeca4e..00000000000 --- a/components/style/errors.rs +++ /dev/null @@ -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) -} diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 2e06f514510..e18bcb32f01 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -2,24 +2,23 @@ * 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::*; -use cssparser::ast::ComponentValue::*; -use cssparser::parse_declaration_list; -use errors::{ErrorLoggerIterator, log_css_error}; +use cssparser::{Token, Parser, DeclarationListParser, AtRuleParser, DeclarationParser}; 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 properties::longhands::font_family::parse_one_family; +use properties::computed_values::font_family::FontFamily; use media_queries::Device; use url::{Url, UrlParser}; +use parser::ParserContext; pub fn iter_font_face_rules_inner(rules: &[CSSRule], device: &Device, callback: |family: &str, source: &Source|) { for rule in rules.iter() { match *rule { - CSSRule::Style(_) => {}, + CSSRule::Style(..) | + CSSRule::Charset(..) | + CSSRule::Namespace(..) => {}, 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)) }, @@ -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 { Url(UrlSource), Local(String), } -#[deriving(Clone)] +#[deriving(Clone, Show, PartialEq, Eq)] pub struct UrlSource { pub url: Url, pub format_hints: Vec<String>, } +#[deriving(Show, PartialEq, Eq)] pub struct FontFaceRule { pub family: String, 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 { - Some(block) => block, - None => { - log_css_error(rule.location, "Invalid @font-face rule"); - return - } +pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser) + -> Result<FontFaceRule, ()> { + let parser = FontFaceRuleParser { + context: context, + family: None, + src: None, }; + match DeclarationListParser::new(input, parser).run() { + FontFaceRuleParser { family: Some(family), src: Some(src), .. } => { + Ok(FontFaceRule { + family: family, + sources: src, + }) + } + _ => Err(()) + } +} - let mut maybe_family = None; - let mut maybe_sources = None; - - for item in ErrorLoggerIterator(parse_declaration_list(block.into_iter())) { - 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()); - } - } + +struct FontFaceRuleParser<'a, 'b: 'a> { + 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(()) } } +} - 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_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(iter: ParserIter, base_url: &Url) -> Result<Source, ()> { - let url = match iter.next() { +fn parse_one_src(context: &ParserContext, input: &mut Parser) -> Result<Source, ()> { + let url = match input.next() { // Parsing url() - Some(&URL(ref url)) => { - UrlParser::new().base_url(base_url).parse(url.as_slice()).unwrap_or_else( + Ok(Token::Url(url)) => { + UrlParser::new().base_url(context.base_url).parse(url.as_slice()).unwrap_or_else( |_error| Url::parse("about:invalid").unwrap()) }, - // Parsing local() with early return() - Some(&Function(ref name, ref arguments)) => { - if name.as_slice().eq_ignore_ascii_case("local") { - let iter = &mut BufferedIter::new(arguments.as_slice().skip_whitespace()); - match parse_one_family(iter) { - Ok(FamilyName(name)) => return Ok(Source::Local(name)), - _ => return Err(()) - } + // Parsing local() with early return + Ok(Token::Function(name)) => { + if name.eq_ignore_ascii_case("local") { + return Ok(Source::Local(try!(input.parse_nested_block(|input| { + parse_one_non_generic_family_name(input) + })))) } return Err(()) }, @@ -135,18 +126,14 @@ fn parse_one_src(iter: ParserIter, base_url: &Url) -> Result<Source, ()> { }; // Parsing optional format() - let format_hints = match iter.next() { - Some(&Function(ref name, ref arguments)) => { - if !name.as_slice().eq_ignore_ascii_case("format") { - return Err(()) - } - try!(parse_slice_comma_separated(arguments.as_slice(), parse_one_format)) - } - Some(component_value) => { - iter.push_back(component_value); - vec![] - } - None => vec![], + let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() { + try!(input.parse_nested_block(|input| { + input.parse_comma_separated(|input| { + Ok((try!(input.expect_string())).into_owned()) + }) + })) + } else { + vec![] }; Ok(Source::Url(UrlSource { @@ -154,17 +141,3 @@ fn parse_one_src(iter: ParserIter, base_url: &Url) -> Result<Source, ()> { 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(()) - } -} diff --git a/components/style/legacy.rs b/components/style/legacy.rs index 19381ebf2e7..0316df68185 100644 --- a/components/style/legacy.rs +++ b/components/style/legacy.rs @@ -6,10 +6,10 @@ //! `<input size>`, and so forth. 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::PropertyDeclaration::*; -use properties::{CSSFloat, specified}; +use properties::PropertyDeclaration; use selector_matching::{DeclarationBlock, Stylist}; use cssparser::Color; @@ -110,13 +110,13 @@ impl PresentationalHintSynthesis for Stylist { LengthOrPercentageOrAuto::Percentage(percentage) => { let width_value = specified::LengthOrPercentageOrAuto::Percentage(percentage); matching_rules_list.vec_push(DeclarationBlock::from_declaration( - WidthDeclaration(SpecifiedValue(width_value)))); + PropertyDeclaration::Width(SpecifiedValue(width_value)))); *shareable = false } LengthOrPercentageOrAuto::Length(length) => { let width_value = specified::LengthOrPercentageOrAuto::Length(specified::Length::Au(length)); matching_rules_list.vec_push(DeclarationBlock::from_declaration( - WidthDeclaration(SpecifiedValue(width_value)))); + PropertyDeclaration::Width(SpecifiedValue(width_value)))); *shareable = false } } @@ -160,8 +160,8 @@ impl PresentationalHintSynthesis for Stylist { _ => specified::Length::Au(Au::from_px(value as int)), }; matching_rules_list.vec_push(DeclarationBlock::from_declaration( - WidthDeclaration(SpecifiedValue(specified::LengthOrPercentageOrAuto::Length( - value))))); + PropertyDeclaration::Width(SpecifiedValue( + specified::LengthOrPercentageOrAuto::Length(value))))); *shareable = false } Some(_) | None => {} @@ -177,8 +177,8 @@ impl PresentationalHintSynthesis for Stylist { // https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-width let value = specified::Length::ServoCharacterWidth(value); matching_rules_list.vec_push(DeclarationBlock::from_declaration( - WidthDeclaration(SpecifiedValue(specified::LengthOrPercentageOrAuto::Length( - value))))); + PropertyDeclaration::Width(SpecifiedValue( + specified::LengthOrPercentageOrAuto::Length(value))))); *shareable = false } Some(_) | None => {} @@ -190,8 +190,8 @@ impl PresentationalHintSynthesis for Stylist { // https://html.spec.whatwg.org/multipage/rendering.html#textarea-effective-height let value = specified::Length::Em(value as CSSFloat); matching_rules_list.vec_push(DeclarationBlock::from_declaration( - HeightDeclaration(SpecifiedValue(specified::LengthOrPercentageOrAuto::Length( - value))))); + PropertyDeclaration::Height(SpecifiedValue( + specified::LengthOrPercentageOrAuto::Length(value))))); *shareable = false } Some(_) | None => {} @@ -216,7 +216,8 @@ impl PresentationalHintSynthesis for Stylist { None => {} Some(color) => { 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 } } @@ -236,13 +237,13 @@ impl PresentationalHintSynthesis for Stylist { Some(length) => { let width_value = specified::Length::Au(Au::from_px(length as int)); 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( - BorderLeftWidthDeclaration(SpecifiedValue(width_value)))); + PropertyDeclaration::BorderLeftWidth(SpecifiedValue(width_value)))); 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( - BorderRightWidthDeclaration(SpecifiedValue(width_value)))); + PropertyDeclaration::BorderRightWidth(SpecifiedValue(width_value)))); *shareable = false } } diff --git a/components/style/lib.rs b/components/style/lib.rs index 0c9e0c4c3ed..5359d5617bc 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -18,7 +18,12 @@ extern crate serialize; extern crate text_writer; extern crate url; +#[phase(plugin, link)] extern crate cssparser; + +#[phase(plugin)] +extern crate matches; + extern crate encoding; extern crate string_cache; @@ -34,39 +39,41 @@ extern crate lazy_static; extern crate "util" as servo_util; -// Public API pub use media_queries::{Device, MediaType}; 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::{CommonStyleAffectingAttributeInfo, CommonStyleAffectingAttributeMode}; pub use selector_matching::{matches, matches_simple_selector, common_style_affecting_attributes}; pub use selector_matching::{rare_style_affecting_attributes}; 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::{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::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult}; -pub use properties::{Angle, AngleOrCorner}; -pub use properties::{HorizontalDirection, VerticalDirection}; +pub use properties::{DeclaredValue, PropertyDeclarationParseResult}; +pub use values::CSSFloat; +pub use values::specified::{Angle, AngleOrCorner, HorizontalDirection, VerticalDirection}; +pub use values::computed; 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::{SimpleSelector, parse_selector_list_from_str}; +pub use selectors::{SimpleSelector, parse_author_origin_selector_list_from_str}; pub use cssparser::{Color, RGBA}; pub use legacy::{IntegerAttribute, LengthAttribute}; pub use legacy::{SimpleColorAttribute, UnsignedIntegerAttribute}; pub use font_face::Source; +pub use stylesheets::Origin as StylesheetOrigin; -mod stylesheets; -mod errors; -mod selectors; -mod selector_matching; -mod properties; -mod namespaces; -mod node; -mod media_queries; -mod parsing_utils; -mod font_face; -mod legacy; +pub mod stylesheets; +pub mod parser; +pub mod selectors; +pub mod selector_matching; +pub mod values; +pub mod properties; +pub mod namespaces; +pub mod node; +pub mod media_queries; +pub mod font_face; +pub mod legacy; diff --git a/components/style/media_queries.rs b/components/style/media_queries.rs index 0c504a5dcb3..f74c4a31ba5 100644 --- a/components/style/media_queries.rs +++ b/components/style/media_queries.rs @@ -3,31 +3,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::ascii::AsciiExt; -use cssparser::parse_rule_list; -use cssparser::ast::*; -use cssparser::ast::ComponentValue::*; +use cssparser::{Token, Parser, Delimiter}; -use errors::{ErrorLoggerIterator, log_css_error}; 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 servo_util::geometry::ViewportPx; -use url::Url; +use servo_util::geometry::{Au, ViewportPx}; +use values::{computed, specified}; -pub struct MediaRule { - pub media_queries: MediaQueryList, - pub rules: Vec<CSSRule>, -} +#[deriving(Show, PartialEq)] pub struct MediaQueryList { media_queries: Vec<MediaQuery> } -#[deriving(PartialEq, Eq, Copy)] +#[deriving(PartialEq, Eq, Copy, Show)] pub enum Range<T> { Min(T), Max(T), @@ -44,17 +33,18 @@ impl<T: Ord> Range<T> { } } -#[deriving(PartialEq, Eq, Copy)] +#[deriving(PartialEq, Eq, Copy, Show)] pub enum Expression { Width(Range<Au>), } -#[deriving(PartialEq, Eq, Copy)] +#[deriving(PartialEq, Eq, Copy, Show)] pub enum Qualifier { Only, Not, } +#[deriving(Show, PartialEq)] pub struct MediaQuery { qualifier: Option<Qualifier>, media_type: MediaQueryType, @@ -72,19 +62,21 @@ impl MediaQuery { } } -#[deriving(PartialEq, Eq, Copy)] +#[deriving(PartialEq, Eq, Copy, Show)] pub enum MediaQueryType { All, // Always true MediaType(MediaType), } -#[deriving(PartialEq, Eq, Copy)] +#[deriving(PartialEq, Eq, Copy, Show)] pub enum MediaType { Screen, Print, Unknown, } +#[allow(missing_copy_implementations)] +#[deriving(Show)] pub struct Device { pub media_type: MediaType, 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, ()> { - let length = try!(specified::Length::parse_non_negative(value)); +fn parse_non_negative_length(input: &mut Parser) -> Result<Au, ()> { + let length = try!(specified::Length::parse_non_negative(input)); // http://dev.w3.org/csswg/mediaqueries3/ - Section 6 // 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)) } -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) - let variable = match iter.next() { - Some(&Ident(ref value)) => value, - _ => return Err(()) - }; - - // Ensure a colon follows - match iter.next() { - 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() { +impl Expression { + fn parse(input: &mut Parser) -> Result<Expression, ()> { + try!(input.expect_parenthesis_block()); + input.parse_nested_block(|input| { + let name = try!(input.expect_ident()); + try!(input.expect_colon()); + // TODO: Handle other media features + match_ignore_ascii_case! { name: "min-width" => { - let au = try!(parse_value_as_length(value)); - Expression::Width(Range::Min(au)) - } + Ok(Expression::Width(Range::Min(try!(parse_non_negative_length(input))))) + }, "max-width" => { - let au = try!(parse_value_as_length(value)); - Expression::Width(Range::Max(au)) + Ok(Expression::Width(Range::Max(try!(parse_non_negative_length(input))))) } - _ => return Err(()) - }; - - if iter.is_eof() { - Ok(expression) - } else { - Err(()) + _ => Err(()) } - } - _ => Err(()) + }) } } -fn parse_media_query(iter: ParserIter) -> Result<MediaQuery, ()> { - let mut expressions = vec!(); - - // Check for optional 'only' or 'not' - let qualifier = match iter.next() { - Some(&Ident(ref value)) if value.as_slice().to_ascii_lower().as_slice() == "only" => Some(Qualifier::Only), - Some(&Ident(ref value)) if value.as_slice().to_ascii_lower().as_slice() == "not" => Some(Qualifier::Not), - Some(component_value) => { - iter.push_back(component_value); +impl MediaQuery { + fn parse(input: &mut Parser) -> Result<MediaQuery, ()> { + let mut expressions = vec![]; + + let qualifier = if input.try(|input| input.expect_ident_matching("only")).is_ok() { + Some(Qualifier::Only) + } else if input.try(|input| input.expect_ident_matching("not")).is_ok() { + Some(Qualifier::Not) + } else { None - } - None => return Err(()), // Empty queries are invalid - }; + }; - // Check for media type - let media_type = match iter.next() { - Some(&Ident(ref value)) => { - match value.as_slice().to_ascii_lower().as_slice() { + let media_type; + if let Ok(ident) = input.try(|input| input.expect_ident()) { + media_type = match_ignore_ascii_case! { ident: "screen" => MediaQueryType::MediaType(MediaType::Screen), "print" => MediaQueryType::MediaType(MediaType::Print), - "all" => MediaQueryType::All, - _ => MediaQueryType::MediaType(MediaType::Unknown), // Unknown media types never match + "all" => MediaQueryType::All + _ => MediaQueryType::MediaType(MediaType::Unknown) } - } - Some(component_value) => { + } else { // Media type is only optional if qualifier is not specified. if qualifier.is_some() { - return Err(()); + return Err(()) } - iter.push_back(component_value); - - // If no qualifier and media type present, an expression should exist here - let expression = try!(parse_media_query_expression(iter)); - expressions.push(expression); - - MediaQueryType::All + media_type = MediaQueryType::All; + // Without a media type, require at least one expression + expressions.push(try!(Expression::parse(input))); } - None => return Err(()), - }; - // Parse any subsequent expressions - loop { - // Each expression should begin with and - match iter.next() { - 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) => { - iter.push_back(component_value); - break; + // Parse any subsequent expressions + loop { + if input.try(|input| input.expect_ident_matching("and")).is_err() { + return Ok(MediaQuery::new(qualifier, media_type, expressions)) } - None => break, + expressions.push(try!(Expression::parse(input))) } } - - Ok(MediaQuery::new(qualifier, media_type, expressions)) } -pub fn parse_media_query_list(input: &[ComponentValue]) -> MediaQueryList { - let iter = &mut BufferedIter::new(input.skip_whitespace()); - let mut media_queries = vec!(); - - if iter.is_eof() { - media_queries.push(MediaQuery::new(None, MediaQueryType::All, vec!())); +pub fn parse_media_query_list(input: &mut Parser) -> MediaQueryList { + let queries = if input.is_exhausted() { + vec![MediaQuery::new(None, MediaQueryType::All, vec!())] } else { + let mut media_queries = vec![]; loop { - // Attempt to parse a media query. - let media_query_result = parse_media_query(iter); - - // Skip until next query or end - let mut trailing_tokens = false; - let mut more_queries = false; - loop { - match iter.next() { - Some(&Comma) => { - 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.push( + input.parse_until_before(Delimiter::Comma, MediaQuery::parse) + .unwrap_or(MediaQuery::new(Some(Qualifier::Not), + MediaQueryType::All, + vec!()))); + match input.next() { + Ok(Token::Comma) => continue, + Ok(_) => unreachable!(), + Err(()) => break, } } - } - - MediaQueryList { media_queries: media_queries } + media_queries + }; + MediaQueryList { media_queries: queries } } impl MediaQueryList { @@ -323,17 +215,16 @@ impl MediaQueryList { #[cfg(test)] mod tests { 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 selector_matching::StylesheetOrigin; + use stylesheets::Origin; use super::*; use url::Url; use std::borrow::ToOwned; fn test_media_rule(css: &str, callback: |&MediaQueryList, &str|) { let url = Url::parse("http://localhost").unwrap(); - let stylesheet = Stylesheet::from_str(css, url, - StylesheetOrigin::Author); + let stylesheet = Stylesheet::from_str(css, url, Origin::Author); let mut rule_count: int = 0; iter_stylesheet_media_rules(&stylesheet, |rule| { rule_count += 1; @@ -344,7 +235,7 @@ mod tests { fn media_query_test(device: &Device, css: &str, expected_rule_count: int) { let url = Url::parse("http://localhost").unwrap(); - let ss = Stylesheet::from_str(css, url, StylesheetOrigin::Author); + let ss = Stylesheet::from_str(css, url, Origin::Author); let mut rule_count: int = 0; iter_stylesheet_style_rules(&ss, device, |_| rule_count += 1); assert!(rule_count == expected_rule_count, css.to_owned()); @@ -629,11 +520,15 @@ mod tests { }); 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]; assert!(q.qualifier == Some(Qualifier::Not), css.to_owned()); assert!(q.media_type == MediaQueryType::All, css.to_owned()); assert!(q.expressions.len() == 0, css.to_owned()); + let q = &list.media_queries[1]; + assert!(q.qualifier == Some(Qualifier::Not), css.to_owned()); + assert!(q.media_type == MediaQueryType::All, css.to_owned()); + assert!(q.expressions.len() == 0, css.to_owned()); }); test_media_rule("@media screen 4px, print {}", |list, css| { diff --git a/components/style/namespaces.rs b/components/style/namespaces.rs index b1e1df89cd7..3d3be8403b6 100644 --- a/components/style/namespaces.rs +++ b/components/style/namespaces.rs @@ -2,13 +2,13 @@ * 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::*; -use cssparser::ast::ComponentValue::*; +use cssparser::Parser; use std::collections::HashMap; -use servo_util::namespace; -use errors::log_css_error; -use string_cache::Namespace; +use string_cache::{Atom, Namespace}; +use parser::ParserContext; + +#[deriving(Clone)] pub struct NamespaceMap { pub default: Option<Namespace>, pub prefix_map: HashMap<String, Namespace>, @@ -22,45 +22,28 @@ impl NamespaceMap { } -pub fn parse_namespace_rule(rule: AtRule, namespaces: &mut NamespaceMap) { - let location = rule.location; - macro_rules! syntax_error( - () => {{ - log_css_error(location, "Invalid @namespace rule"); - return - }}; - ); - if rule.block.is_some() { syntax_error!() } - let mut prefix: Option<String> = None; - 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!(), +pub fn parse_namespace_rule(context: &mut ParserContext, input: &mut Parser) + -> Result<(Option<String>, Namespace), ()> { + let prefix = input.try(|input| input.expect_ident()).ok().map(|p| p.into_owned()); + let url = try!(input.expect_url_or_string()); + try!(input.expect_exhausted()); + + let namespace = Namespace(Atom::from_slice(url.as_slice())); + let is_duplicate = match prefix { + Some(ref prefix) => { + context.namespaces.prefix_map.insert(prefix.clone(), namespace.clone()).is_some() } - } - if iter.next().is_some() { syntax_error!() } - match (prefix, ns) { - (Some(prefix), Some(ns)) => { - if namespaces.prefix_map.insert(prefix, ns).is_some() { - log_css_error(location, "Duplicate @namespace rule"); + None => { + let has_default = context.namespaces.default.is_some(); + if !has_default { + context.namespaces.default = Some(namespace.clone()); } - }, - (None, Some(ns)) => { - if namespaces.default.is_some() { - log_css_error(location, "Duplicate @namespace rule"); - } - namespaces.default = Some(ns); - }, - _ => syntax_error!() + has_default + } + }; + if is_duplicate { + Err(()) // "Duplicate @namespace rule" + } else { + Ok((prefix, namespace)) } } diff --git a/components/style/parser.rs b/components/style/parser.rs new file mode 100644 index 00000000000..6619ffd06dd --- /dev/null +++ b/components/style/parser.rs @@ -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) + } +} diff --git a/components/style/parsing_utils.rs b/components/style/parsing_utils.rs deleted file mode 100644 index 97ebb3cad4d..00000000000 --- a/components/style/parsing_utils.rs +++ /dev/null @@ -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), - } - } -} diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako index 9f26568c89d..77d60cf8516 100644 --- a/components/style/properties/mod.rs.mako +++ b/components/style/properties/mod.rs.mako @@ -4,30 +4,27 @@ // This file is a Mako template: http://www.makotemplates.org/ -pub use std::ascii::AsciiExt; +use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::fmt; use std::fmt::Show; use std::sync::Arc; use servo_util::logical_geometry::{WritingMode, LogicalMargin}; -pub use url::Url; - -pub use cssparser::*; -pub use cssparser::ast::*; -pub use cssparser::ast::ComponentValue::*; -pub use geom::SideOffsets2D; -pub use self::common_types::specified::{Angle, AngleOrCorner}; -pub use self::common_types::specified::{HorizontalDirection, VerticalDirection}; - -use errors::{ErrorLoggerIterator, log_css_error}; -pub use parsing_utils::*; -pub use self::common_types::*; +use servo_util::geometry::Au; +use url::Url; +use cssparser::{Parser, Color, RGBA, AtRuleParser, DeclarationParser, + DeclarationListParser, parse_important}; +use geom::SideOffsets2D; + +use values::specified::BorderStyle; +use values::computed; use selector_matching::DeclarationBlock; +use parser::ParserContext; +use namespaces::NamespaceMap; +use stylesheets::Origin; - -pub use self::property_bit_field::PropertyBitField; -pub mod common_types; +use self::property_bit_field::PropertyBitField; <%! @@ -96,14 +93,13 @@ def switch_to_style_struct(name): %> pub mod longhands { - pub use super::*; - pub use std; + use values::computed; pub fn computed_as_specified<T>(value: T, _context: &computed::Context) -> T { value } - <%def name="raw_longhand(name, no_super=False, derived_from=None, experimental=False)"> + <%def name="raw_longhand(name, derived_from=None, experimental=False)"> <% if derived_from is not None: derived_from = derived_from.split() @@ -118,51 +114,44 @@ pub mod longhands { DERIVED_LONGHANDS.setdefault(name, []).append(property) %> pub mod ${property.ident} { - % if not no_super: - use super::*; + % if derived_from is None: + use cssparser::Parser; + use parser::ParserContext; + use properties::{CSSWideKeyword, DeclaredValue}; % endif - pub use self::computed_value::*; + #[allow(unused_imports)] + use values::{computed, specified}; ${caller.body()} % if derived_from is None: - pub fn parse_declared(input: &[ComponentValue], base_url: &Url) + pub fn parse_declared(context: &ParserContext, input: &mut Parser) -> Result<DeclaredValue<SpecifiedValue>, ()> { - match CSSWideKeyword::parse(input) { + match input.try(CSSWideKeyword::parse) { Ok(CSSWideKeyword::InheritKeyword) => Ok(DeclaredValue::Inherit), Ok(CSSWideKeyword::InitialKeyword) => Ok(DeclaredValue::Initial), Ok(CSSWideKeyword::UnsetKeyword) => Ok(DeclaredValue::${ "Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"}), - Err(()) => parse_specified(input, base_url), + Err(()) => parse_specified(context, input), } } % endif } </%def> - <%def name="longhand(name, no_super=False, derived_from=None, experimental=False)"> + <%def name="longhand(name, derived_from=None, experimental=False)"> <%self:raw_longhand name="${name}" derived_from="${derived_from}" - experimental="${experimental}" no_super="${no_super}"> + experimental="${experimental}"> ${caller.body()} % if derived_from is None: - pub fn parse_specified(_input: &[ComponentValue], _base_url: &Url) + pub fn parse_specified(context: &ParserContext, input: &mut Parser) -> Result<DeclaredValue<SpecifiedValue>, ()> { - parse(_input, _base_url).map(super::DeclaredValue::SpecifiedValue) + parse(context, input).map(DeclaredValue::SpecifiedValue) } % endif </%self:raw_longhand> </%def> - <%def name="single_component_value(name, derived_from=None, experimental=False)"> - <%self:longhand name="${name}" derived_from="${derived_from}" - experimental="${experimental}"> - ${caller.body()} - pub fn parse(input: &[ComponentValue], base_url: &Url) -> Result<SpecifiedValue, ()> { - one_component_value(input).and_then(|c| from_component_value(c, base_url)) - } - </%self:longhand> - </%def> - <%def name="single_keyword_computed(name, values, experimental=False)"> - <%self:single_component_value name="${name}" experimental="${experimental}"> + <%self:longhand name="${name}" experimental="${experimental}"> pub use self::computed_value::T as SpecifiedValue; ${caller.body()} pub mod computed_value { @@ -173,13 +162,13 @@ pub mod longhands { } } #[inline] pub fn get_initial_value() -> computed_value::T { - T::${to_rust_ident(values.split()[0])} + computed_value::T::${to_rust_ident(values.split()[0])} } - pub fn from_component_value(v: &ComponentValue, _base_url: &Url) - -> Result<SpecifiedValue, ()> { - computed_value::T::parse(v) + pub fn parse(_context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + computed_value::T::parse(input) } - </%self:single_component_value> + </%self:longhand> </%def> <%def name="single_keyword(name, values, experimental=False)"> @@ -192,18 +181,20 @@ pub mod longhands { </%def> <%def name="predefined_type(name, type, initial_value, parse_method='parse')"> - <%self:single_component_value name="${name}"> - pub use super::super::common_types::computed::compute_${type} as to_computed_value; + <%self:longhand name="${name}"> + #[allow(unused_imports)] + use servo_util::geometry::Au; + pub use values::computed::compute_${type} as to_computed_value; pub type SpecifiedValue = specified::${type}; pub mod computed_value { - pub type T = super::super::computed::${type}; + pub use values::computed::${type} as T; } #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} } - #[inline] pub fn from_component_value(v: &ComponentValue, _base_url: &Url) - -> Result<SpecifiedValue, ()> { - specified::${type}::${parse_method}(v) + #[inline] pub fn parse(_context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + specified::${type}::${parse_method}(input) } - </%self:single_component_value> + </%self:longhand> </%def> @@ -227,48 +218,29 @@ pub mod longhands { ${new_style_struct("Border", is_inherited=False)} % for side in ["top", "right", "bottom", "left"]: - ${predefined_type("border-%s-color" % side, "CSSColor", "super::super::computed::CSSColor::CurrentColor")} + ${predefined_type("border-%s-color" % side, "CSSColor", "::cssparser::Color::CurrentColor")} % endfor - ${single_keyword("border-top-style", values="none solid double dotted dashed hidden groove ridge inset outset")} - - % for side in ["right", "bottom", "left"]: - <%self:longhand name="border-${side}-style"> - pub use super::border_top_style::{get_initial_value, parse, to_computed_value}; - pub type SpecifiedValue = super::border_top_style::SpecifiedValue; - pub mod computed_value { - pub type T = super::super::border_top_style::computed_value::T; - } - </%self:longhand> + % for side in ["top", "right", "bottom", "left"]: + ${predefined_type("border-%s-style" % side, "BorderStyle", "computed::BorderStyle::none")} % endfor - pub fn parse_border_width(component_value: &ComponentValue, _base_url: &Url) - -> Result<specified::Length, ()> { - match component_value { - &Ident(ref value) => { - match value.as_slice().to_ascii_lower().as_slice() { - "thin" => Ok(specified::Length::from_px(1.)), - "medium" => Ok(specified::Length::from_px(3.)), - "thick" => Ok(specified::Length::from_px(5.)), - _ => Err(()) - } - }, - _ => specified::Length::parse_non_negative(component_value) - } - } % for side in ["top", "right", "bottom", "left"]: <%self:longhand name="border-${side}-width"> + use servo_util::geometry::Au; + #[inline] + pub fn parse(_context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + specified::parse_border_width(input) + } pub type SpecifiedValue = specified::Length; pub mod computed_value { - use super::super::Au; + use servo_util::geometry::Au; pub type T = Au; } #[inline] pub fn get_initial_value() -> computed_value::T { Au::from_px(3) // medium } - pub fn parse(input: &[ComponentValue], base_url: &Url) -> Result<SpecifiedValue, ()> { - one_component_value(input).and_then(|c| parse_border_width(c, base_url)) - } #[inline] pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) -> computed_value::T { @@ -281,106 +253,39 @@ pub mod longhands { </%self:longhand> % endfor - <%self:longhand name="border-top-left-radius"> - #[deriving(Clone, Show, PartialEq, Copy)] - pub struct SpecifiedValue { - pub radius: specified::LengthOrPercentage, - } - - pub mod computed_value { - use super::super::computed; - - #[deriving(Clone, PartialEq, Copy, Show)] - pub struct T { - pub radius: computed::LengthOrPercentage, - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T { - radius: computed::LengthOrPercentage::Length(Au(0)), - } - } - #[inline] - pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) - -> computed_value::T { - computed_value::T { - radius: computed::compute_LengthOrPercentage(value.radius, context), - } - } - - pub fn parse(input: &[ComponentValue], _: &Url) -> Result<SpecifiedValue,()> { - let mut iter = input.skip_whitespace(); - - let radius = match iter.next() { - None => return Err(()), - Some(cv) => cv, - }; - - let radius = try!(specified::LengthOrPercentage::parse(radius)); - - if iter.next().is_some() { return Err(()); } - - Ok(SpecifiedValue { - radius: radius, - }) - } - </%self:longhand> - - % for corner in ["top-right", "bottom-right", "bottom-left"]: - <%self:longhand name="border-${corner}-radius"> - pub type SpecifiedValue = super::border_top_left_radius::SpecifiedValue; - - pub mod computed_value { - pub type T = super::super::border_top_left_radius::computed_value::T; - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - super::border_top_left_radius::get_initial_value() - } - #[inline] - pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) - -> computed_value::T { - super::border_top_left_radius::to_computed_value(value, context) - } - - pub fn parse(input: &[ComponentValue], u: &Url) -> Result<SpecifiedValue,()> { - super::border_top_left_radius::parse(input, u) - } - </%self:longhand> + // FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage> + % for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]: + ${predefined_type("border-" + corner + "-radius", "LengthOrPercentage", + "computed::LengthOrPercentage::Length(Au(0))", + "parse_non_negative")} % endfor ${new_style_struct("Outline", is_inherited=False)} // TODO(pcwalton): `invert` - ${predefined_type("outline-color", "CSSColor", "super::super::computed::CSSColor::CurrentColor")} + ${predefined_type("outline-color", "CSSColor", "::cssparser::Color::CurrentColor")} - <%self:single_component_value name="outline-style"> - pub use super::border_top_style::{get_initial_value, to_computed_value}; - pub type SpecifiedValue = super::border_top_style::SpecifiedValue; + <%self:longhand name="outline-style"> + pub use values::specified::BorderStyle as SpecifiedValue; + pub use super::computed_as_specified as to_computed_value; + pub fn get_initial_value() -> SpecifiedValue { SpecifiedValue::none } pub mod computed_value { - pub type T = super::super::border_top_style::computed_value::T; + pub use values::specified::BorderStyle as T; } - pub fn from_component_value(value: &ComponentValue, base_url: &Url) - -> Result<SpecifiedValue,()> { - match value { - &Ident(ref ident) if ident.eq_ignore_ascii_case("hidden") => { - // `hidden` is not a valid value. - Err(()) - } - _ => super::border_top_style::from_component_value(value, base_url) + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + match SpecifiedValue::parse(input) { + Ok(SpecifiedValue::hidden) => Err(()), + result => result } } - </%self:single_component_value> + </%self:longhand> <%self:longhand name="outline-width"> pub use super::border_top_width::{get_initial_value, parse}; - pub use computed::compute_Au as to_computed_value; + pub use values::computed::compute_Au as to_computed_value; pub type SpecifiedValue = super::border_top_width::SpecifiedValue; pub mod computed_value { - pub type T = super::super::border_top_width::computed_value::T; + pub use servo_util::geometry::Au as T; } </%self:longhand> @@ -407,6 +312,7 @@ pub mod longhands { #[inline] pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) -> computed_value::T { + use self::computed_value::T; // if context.is_root_element && value == list_item { // return block // } @@ -431,26 +337,25 @@ pub mod longhands { ${single_keyword("float", "none left right")} ${single_keyword("clear", "none left right both")} - <%self:longhand name="-servo-display-for-hypothetical-box" derived_from="display" no_super="True"> + <%self:longhand name="-servo-display-for-hypothetical-box" derived_from="display"> pub use super::computed_as_specified as to_computed_value; pub use super::display::{SpecifiedValue, get_initial_value}; pub use super::display::{parse}; - use super::computed; - use super::display; pub mod computed_value { pub type T = super::SpecifiedValue; } #[inline] - pub fn derive_from_display(_: display::computed_value::T, context: &computed::Context) + pub fn derive_from_display(_: super::display::computed_value::T, + context: &computed::Context) -> computed_value::T { context.display } </%self:longhand> - <%self:single_component_value name="z-index"> + <%self:longhand name="z-index"> pub use super::computed_as_specified as to_computed_value; pub type SpecifiedValue = computed_value::T; pub mod computed_value { @@ -481,19 +386,16 @@ pub mod longhands { } #[inline] pub fn get_initial_value() -> computed_value::T { - T::Auto - } - fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> { - match *input { - Ident(ref keyword) if keyword.as_slice().eq_ignore_ascii_case("auto") => Ok(T::Auto), - Number(NumericValue { - int_value: Some(value), - .. - }) => Ok(T::Number(value as i32)), - _ => Err(()) + computed_value::T::Auto + } + fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("auto")).is_ok() { + Ok(computed_value::T::Auto) + } else { + Ok(computed_value::T::Number(try!(input.expect_integer()) as i32)) } } - </%self:single_component_value> + </%self:longhand> ${new_style_struct("InheritedBox", is_inherited=True)} @@ -506,29 +408,29 @@ pub mod longhands { ${predefined_type("width", "LengthOrPercentageOrAuto", "computed::LengthOrPercentageOrAuto::Auto", "parse_non_negative")} - <%self:single_component_value name="height"> + <%self:longhand name="height"> pub type SpecifiedValue = specified::LengthOrPercentageOrAuto; pub mod computed_value { - pub type T = super::super::computed::LengthOrPercentageOrAuto; + pub use values::computed::LengthOrPercentageOrAuto as T; } #[inline] pub fn get_initial_value() -> computed_value::T { computed::LengthOrPercentageOrAuto::Auto } #[inline] - pub fn from_component_value(v: &ComponentValue, _base_url: &Url) - -> Result<SpecifiedValue, ()> { - specified::LengthOrPercentageOrAuto::parse_non_negative(v) + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + specified::LengthOrPercentageOrAuto::parse_non_negative(input) } pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) -> computed_value::T { match (value, context.inherited_height) { - (specified::LengthOrPercentageOrAuto::Percentage(_), computed::LengthOrPercentageOrAuto::Auto) + (specified::LengthOrPercentageOrAuto::Percentage(_), + computed::LengthOrPercentageOrAuto::Auto) if !context.is_root_element && !context.positioned => { computed::LengthOrPercentageOrAuto::Auto }, _ => computed::compute_LengthOrPercentageOrAuto(value, context) } } - </%self:single_component_value> + </%self:longhand> ${predefined_type("min-width", "LengthOrPercentage", "computed::LengthOrPercentage::Length(Au(0))", @@ -546,8 +448,9 @@ pub mod longhands { ${switch_to_style_struct("InheritedBox")} - <%self:single_component_value name="line-height"> + <%self:longhand name="line-height"> use std::fmt; + use values::CSSFloat; #[deriving(Clone, PartialEq, Copy)] pub enum SpecifiedValue { Normal, @@ -566,23 +469,29 @@ pub mod longhands { } } /// normal | <number> | <length> | <percentage> - pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Result<SpecifiedValue, ()> { - match input { - &Number(ref value) if value.value >= 0. => - Ok(SpecifiedValue::Number(value.value)), - &Percentage(ref value) if value.value >= 0. => - Ok(SpecifiedValue::Percentage(value.value / 100.)), - &Dimension(ref value, ref unit) if value.value >= 0. => + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + use std::ascii::AsciiExt; + use cssparser::Token; + match try!(input.next()) { + Token::Number(ref value) if value.value >= 0. => { + Ok(SpecifiedValue::Number(value.value)) + } + Token::Percentage(ref value) if value.unit_value >= 0. => { + Ok(SpecifiedValue::Percentage(value.unit_value)) + } + Token::Dimension(ref value, ref unit) if value.value >= 0. => { specified::Length::parse_dimension(value.value, unit.as_slice()) - .map(SpecifiedValue::Length), - &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("normal") => - Ok(SpecifiedValue::Normal), + .map(SpecifiedValue::Length) + } + Token::Ident(ref value) if value.as_slice().eq_ignore_ascii_case("normal") => { + Ok(SpecifiedValue::Normal) + } _ => Err(()), } } pub mod computed_value { - use super::super::{Au, CSSFloat}; + use values::CSSFloat; + use servo_util::geometry::Au; use std::fmt; #[deriving(PartialEq, Copy, Clone)] pub enum T { @@ -601,22 +510,27 @@ pub mod longhands { } } #[inline] - pub fn get_initial_value() -> computed_value::T { T::Normal } + pub fn get_initial_value() -> computed_value::T { computed_value::T::Normal } #[inline] pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) -> computed_value::T { match value { - SpecifiedValue::Normal => T::Normal, - SpecifiedValue::Length(value) => T::Length(computed::compute_Au(value, context)), - SpecifiedValue::Number(value) => T::Number(value), - SpecifiedValue::Percentage(value) => T::Length(computed::compute_Au(specified::Length::Em(value), context)), + SpecifiedValue::Normal => computed_value::T::Normal, + SpecifiedValue::Length(value) => { + computed_value::T::Length(computed::compute_Au(value, context)) + } + SpecifiedValue::Number(value) => computed_value::T::Number(value), + SpecifiedValue::Percentage(value) => { + computed_value::T::Length(computed::compute_Au( + specified::Length::Em(value), context)) + } } } - </%self:single_component_value> + </%self:longhand> ${switch_to_style_struct("Box")} - <%self:single_component_value name="vertical-align"> + <%self:longhand name="vertical-align"> use std::fmt; <% vertical_align_keywords = ( "baseline sub super top text-top middle bottom text-bottom".split()) %> @@ -640,23 +554,26 @@ pub mod longhands { } /// baseline | sub | super | top | text-top | middle | bottom | text-bottom /// | <percentage> | <length> - pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Result<SpecifiedValue, ()> { - match input { - &Ident(ref value) => { - match value.as_slice().to_ascii_lower().as_slice() { - % for keyword in vertical_align_keywords: + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + input.try(specified::LengthOrPercentage::parse_non_negative) + .map(SpecifiedValue::LengthOrPercentage) + .or_else(|()| { + match_ignore_ascii_case! { try!(input.expect_ident()): + % for keyword in vertical_align_keywords[:-1]: "${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)}), - % endfor - _ => Err(()), - } - }, - _ => specified::LengthOrPercentage::parse_non_negative(input) - .map(SpecifiedValue::LengthOrPercentage) - } + % endfor + + // Hack to work around quirks of macro_rules parsing in match_ignore_ascii_case! + % for keyword in vertical_align_keywords[-1:]: + "${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)}) + % endfor + _ => Err(()) + } + }) } pub mod computed_value { - use super::super::{Au, CSSFloat}; + use values::CSSFloat; + use servo_util::geometry::Au; use std::fmt; #[allow(non_camel_case_types)] #[deriving(PartialEq, Copy, Clone)] @@ -680,22 +597,29 @@ pub mod longhands { } } #[inline] - pub fn get_initial_value() -> computed_value::T { T::baseline } + pub fn get_initial_value() -> computed_value::T { computed_value::T::baseline } #[inline] pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) -> computed_value::T { match value { % for keyword in vertical_align_keywords: - SpecifiedValue::${to_rust_ident(keyword)} => computed_value::T::${to_rust_ident(keyword)}, + SpecifiedValue::${to_rust_ident(keyword)} => { + computed_value::T::${to_rust_ident(keyword)} + } % endfor - SpecifiedValue::LengthOrPercentage(value) - => match computed::compute_LengthOrPercentage(value, context) { - computed::LengthOrPercentage::Length(value) => T::Length(value), - computed::LengthOrPercentage::Percentage(value) => T::Percentage(value) + SpecifiedValue::LengthOrPercentage(value) => { + match computed::compute_LengthOrPercentage(value, context) { + computed::LengthOrPercentage::Length(value) => { + computed_value::T::Length(value) + } + computed::LengthOrPercentage::Percentage(value) => { + computed_value::T::Percentage(value) + } + } } } } - </%self:single_component_value> + </%self:longhand> // CSS 2.1, Section 11 - Visual effects @@ -713,8 +637,11 @@ pub mod longhands { <%self:longhand name="content"> pub use super::computed_as_specified as to_computed_value; + pub use self::computed_value::T as SpecifiedValue; + pub use self::computed_value::ContentItem; + use cssparser::Token; pub mod computed_value { - use std::fmt; + use std::fmt; #[deriving(PartialEq, Eq, Clone)] pub enum ContentItem { StringContent(String), @@ -748,31 +675,31 @@ pub mod longhands { } } } - pub type SpecifiedValue = computed_value::T; - #[inline] pub fn get_initial_value() -> computed_value::T { T::normal } + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::normal + } // normal | none | [ <string> ]+ // TODO: <uri>, <counter>, attr(<identifier>), open-quote, close-quote, no-open-quote, no-close-quote - pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Result<SpecifiedValue, ()> { - match one_component_value(input) { - Ok(&Ident(ref keyword)) => { - match keyword.as_slice().to_ascii_lower().as_slice() { - "normal" => return Ok(T::normal), - "none" => return Ok(T::none), - _ => () + pub fn parse(_context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + return Ok(SpecifiedValue::normal) + } + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(SpecifiedValue::none) + } + let mut content = vec![]; + loop { + match input.next() { + Ok(Token::QuotedString(value)) => { + content.push(ContentItem::StringContent(value.into_owned())) } - }, - _ => () - } - let mut content = vec!(); - for component_value in input.skip_whitespace() { - match component_value { - &QuotedString(ref value) - => content.push(ContentItem::StringContent(value.clone())), - _ => return Err(()) // invalid/unsupported value + Ok(_) => return Err(()), + Err(()) => return Ok(SpecifiedValue::Content(content)) } } - Ok(T::Content(content)) } </%self:longhand> @@ -791,26 +718,26 @@ pub mod longhands { ${single_keyword("list-style-type", "disc none circle square disclosure-open disclosure-closed")} - <%self:single_component_value name="list-style-image"> + <%self:longhand name="list-style-image"> + use url::Url; pub use super::computed_as_specified as to_computed_value; pub type SpecifiedValue = Option<Url>; pub mod computed_value { use url::Url; pub type T = Option<Url>; } - pub fn from_component_value(input: &ComponentValue, base_url: &Url) - -> Result<SpecifiedValue,()> { - match *input { - URL(ref url) => Ok(Some(super::parse_url(url.as_slice(), base_url))), - Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => Ok(None), - _ => Err(()), + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + Ok(None) + } else { + Ok(Some(context.parse_url(try!(input.expect_url()).as_slice()))) } } #[inline] pub fn get_initial_value() -> computed_value::T { None } - </%self:single_component_value> + </%self:longhand> // CSS 2.1, Section 13 - Paged media @@ -818,34 +745,24 @@ pub mod longhands { ${new_style_struct("Background", is_inherited=False)} ${predefined_type("background-color", "CSSColor", - "Color::RGBA(RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")} + "::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")} - <%self:single_component_value name="background-image"> - use super::common_types::specified as common_specified; - use super::super::common_types::specified::CSSImage as CSSImage; + <%self:longhand name="background-image"> + use values::specified::{CSSImage, Image}; pub mod computed_value { - use super::super::super::common_types::computed; + use values::computed; pub type T = Option<computed::Image>; } - pub type SpecifiedValue = common_specified::CSSImage; + pub type SpecifiedValue = CSSImage; #[inline] pub fn get_initial_value() -> computed_value::T { None } - pub fn from_component_value(component_value: &ComponentValue, base_url: &Url) - -> Result<SpecifiedValue, ()> { - match component_value { - &Ident(ref value) - if value.as_slice().eq_ignore_ascii_case("none") => { - Ok(CSSImage(None)) - } - _ => { - match common_specified::Image::from_component_value(component_value, - base_url) { - Err(err) => Err(err), - Ok(result) => Ok(CSSImage(Some(result))), - } - } + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + Ok(CSSImage(None)) + } else { + Ok(CSSImage(Some(try!(Image::parse(context, input))))) } } pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) @@ -855,13 +772,13 @@ pub mod longhands { CSSImage(Some(image)) => Some(image.to_computed_value(context)), } } - </%self:single_component_value> + </%self:longhand> <%self:longhand name="background-position"> use std::fmt; pub mod computed_value { - use super::super::super::common_types::computed::LengthOrPercentage; + use values::computed::LengthOrPercentage; use std::fmt; #[deriving(PartialEq, Copy, Clone)] @@ -889,7 +806,7 @@ pub mod longhands { impl SpecifiedValue { fn new(first: specified::PositionComponent, second: specified::PositionComponent) - -> Result<SpecifiedValue,()> { + -> Result<SpecifiedValue, ()> { let (horiz, vert) = match (category(first), category(second)) { // Don't allow two vertical keywords or two horizontal keywords. (PositionCategory::HorizontalKeyword, PositionCategory::HorizontalKeyword) | @@ -950,36 +867,13 @@ pub mod longhands { } } - pub fn parse_one(first: &ComponentValue) -> Result<SpecifiedValue, ()> { - let first = try!(specified::PositionComponent::parse(first)); - // If only one value is provided, use `center` for the second. - SpecifiedValue::new(first, specified::PositionComponent::Center) - } - - pub fn parse_two(first: &ComponentValue, second: &ComponentValue) - -> Result<SpecifiedValue, ()> { - let first = try!(specified::PositionComponent::parse(first)); - let second = try!(specified::PositionComponent::parse(second)); + pub fn parse(_context: &ParserContext, input: &mut Parser) + -> Result<SpecifiedValue, ()> { + let first = try!(specified::PositionComponent::parse(input)); + let second = input.try(specified::PositionComponent::parse) + .unwrap_or(specified::PositionComponent::Center); SpecifiedValue::new(first, second) } - - pub fn parse(input: &[ComponentValue], _: &Url) -> Result<SpecifiedValue, ()> { - let mut input_iter = input.skip_whitespace(); - let first = input_iter.next(); - let second = input_iter.next(); - if input_iter.next().is_some() { - return Err(()) - } - match (first, second) { - (Some(first), Some(second)) => { - parse_two(first, second) - } - (Some(first), None) => { - parse_one(first) - } - _ => Err(()) - } - } </%self:longhand> ${single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")} @@ -989,7 +883,8 @@ pub mod longhands { ${new_style_struct("Color", is_inherited=True)} <%self:raw_longhand name="color"> - use super::super::common_types::specified::{CSSColor, CSSRGBA}; + use cssparser::{Color, RGBA}; + use values::specified::{CSSColor, CSSRGBA}; #[inline] pub fn to_computed_value(value: SpecifiedValue, _context: &computed::Context) -> computed_value::T { @@ -1004,19 +899,17 @@ pub mod longhands { #[inline] pub fn get_initial_value() -> computed_value::T { RGBA { red: 0., green: 0., blue: 0., alpha: 1. } /* black */ } - pub fn parse_specified(input: &[ComponentValue], _base_url: &Url) + pub fn parse_specified(_context: &ParserContext, input: &mut Parser) -> Result<DeclaredValue<SpecifiedValue>, ()> { - match one_component_value(input).and_then(CSSColor::parse) { - Ok(CSSColor { parsed: Color::RGBA(rgba), authored }) => { - let rgba = CSSRGBA { - parsed: rgba, - authored: authored, - }; - Ok(DeclaredValue::SpecifiedValue(rgba)) - } - Ok(CSSColor { parsed: Color::CurrentColor, .. }) => Ok(DeclaredValue::Inherit), - Err(()) => Err(()), - } + let value = try!(CSSColor::parse(input)); + let rgba = match value.parsed { + Color::RGBA(rgba) => rgba, + Color::CurrentColor => return Ok(DeclaredValue::Inherit) + }; + Ok(DeclaredValue::SpecifiedValue(CSSRGBA { + parsed: rgba, + authored: value.authored, + })) } </%self:raw_longhand> @@ -1027,6 +920,7 @@ pub mod longhands { <%self:longhand name="font-family"> pub use super::computed_as_specified as to_computed_value; use std::borrow::ToOwned; + use self::computed_value::FontFamily; pub mod computed_value { use std::fmt; #[deriving(PartialEq, Eq, Clone)] @@ -1069,44 +963,31 @@ pub mod longhands { pub fn get_initial_value() -> computed_value::T { vec![FontFamily::FamilyName("serif".to_owned())] } - /// <familiy-name># - /// <familiy-name> = <string> | [ <ident>+ ] - /// TODO: <generic-familiy> - pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Result<SpecifiedValue, ()> { - parse_slice_comma_separated(input, parse_one_family) - } - pub fn parse_one_family<'a>(iter: ParserIter) -> Result<FontFamily, ()> { - // TODO: avoid copying strings? - let mut idents = match iter.next() { - Some(&QuotedString(ref value)) => return Ok(FontFamily::FamilyName(value.clone())), - Some(&Ident(ref value)) => { -// match value.as_slice().to_ascii_lower().as_slice() { -// "serif" => return Ok(Serif), -// "sans-serif" => return Ok(SansSerif), -// "cursive" => return Ok(Cursive), -// "fantasy" => return Ok(Fantasy), -// "monospace" => return Ok(Monospace), -// _ => { - vec![value.as_slice()] -// } -// } - } - _ => return Err(()) - }; - loop { - match iter.next() { - Some(&Ident(ref value)) => { - idents.push(value.as_slice()); - iter.next(); - } - Some(component_value) => { - iter.push_back(component_value); - break - } - None => break, - } + /// <family-name># + /// <family-name> = <string> | [ <ident>+ ] + /// TODO: <generic-family> + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + input.parse_comma_separated(parse_one_family) + } + pub fn parse_one_family(input: &mut Parser) -> Result<FontFamily, ()> { + if let Ok(value) = input.try(|input| input.expect_string()) { + return Ok(FontFamily::FamilyName(value.into_owned())) + } + let first_ident = try!(input.expect_ident()); +// match_ignore_ascii_case! { first_ident: +// "serif" => return Ok(Serif), +// "sans-serif" => return Ok(SansSerif), +// "cursive" => return Ok(Cursive), +// "fantasy" => return Ok(Fantasy), +// "monospace" => return Ok(Monospace) +// _ => {} +// } + let mut value = first_ident.into_owned(); + while let Ok(ident) = input.try(|input| input.expect_ident()) { + value.push_str(" "); + value.push_str(ident.as_slice()); } - Ok(FontFamily::FamilyName(idents.connect(" "))) + Ok(FontFamily::FamilyName(value)) } </%self:longhand> @@ -1114,14 +995,14 @@ pub mod longhands { ${single_keyword("font-style", "normal italic oblique")} ${single_keyword("font-variant", "normal small-caps")} - <%self:single_component_value name="font-weight"> + <%self:longhand name="font-weight"> use std::fmt; #[deriving(Clone, PartialEq, Eq, Copy)] pub enum SpecifiedValue { Bolder, Lighter, % for weight in range(100, 901, 100): - SpecifiedWeight${weight}, + Weight${weight}, % endfor } impl fmt::Show for SpecifiedValue { @@ -1130,38 +1011,35 @@ pub mod longhands { &SpecifiedValue::Bolder => write!(f, "bolder"), &SpecifiedValue::Lighter => write!(f, "lighter"), % for weight in range(100, 901, 100): - &SpecifiedValue::SpecifiedWeight${weight} => write!(f, "{}", ${weight}i), + &SpecifiedValue::Weight${weight} => write!(f, "{}", ${weight}i), % endfor } } } /// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 - pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Result<SpecifiedValue, ()> { - match input { - &Ident(ref value) => { - match value.as_slice().to_ascii_lower().as_slice() { - "bold" => Ok(SpecifiedValue::SpecifiedWeight700), - "normal" => Ok(SpecifiedValue::SpecifiedWeight400), - "bolder" => Ok(SpecifiedValue::Bolder), - "lighter" => Ok(SpecifiedValue::Lighter), - _ => Err(()), - } - }, - &Number(ref value) => match value.int_value { - Some(100) => Ok(SpecifiedValue::SpecifiedWeight100), - Some(200) => Ok(SpecifiedValue::SpecifiedWeight200), - Some(300) => Ok(SpecifiedValue::SpecifiedWeight300), - Some(400) => Ok(SpecifiedValue::SpecifiedWeight400), - Some(500) => Ok(SpecifiedValue::SpecifiedWeight500), - Some(600) => Ok(SpecifiedValue::SpecifiedWeight600), - Some(700) => Ok(SpecifiedValue::SpecifiedWeight700), - Some(800) => Ok(SpecifiedValue::SpecifiedWeight800), - Some(900) => Ok(SpecifiedValue::SpecifiedWeight900), - _ => Err(()), - }, - _ => Err(()) - } + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + input.try(|input| { + match_ignore_ascii_case! { try!(input.expect_ident()): + "bold" => Ok(SpecifiedValue::Weight700), + "normal" => Ok(SpecifiedValue::Weight400), + "bolder" => Ok(SpecifiedValue::Bolder), + "lighter" => Ok(SpecifiedValue::Lighter) + _ => Err(()) + } + }).or_else(|()| { + match try!(input.expect_integer()) { + 100 => Ok(SpecifiedValue::Weight100), + 200 => Ok(SpecifiedValue::Weight200), + 300 => Ok(SpecifiedValue::Weight300), + 400 => Ok(SpecifiedValue::Weight400), + 500 => Ok(SpecifiedValue::Weight500), + 600 => Ok(SpecifiedValue::Weight600), + 700 => Ok(SpecifiedValue::Weight700), + 800 => Ok(SpecifiedValue::Weight800), + 900 => Ok(SpecifiedValue::Weight900), + _ => Err(()) + } + }) } pub mod computed_value { use std::fmt; @@ -1199,7 +1077,7 @@ pub mod longhands { -> computed_value::T { match value { % for weight in range(100, 901, 100): - SpecifiedValue::SpecifiedWeight${weight} => computed_value::T::Weight${weight}, + SpecifiedValue::Weight${weight} => computed_value::T::Weight${weight}, % endfor SpecifiedValue::Bolder => match context.inherited_font_weight { computed_value::T::Weight100 => computed_value::T::Weight400, @@ -1225,12 +1103,13 @@ pub mod longhands { }, } } - </%self:single_component_value> + </%self:longhand> - <%self:single_component_value name="font-size"> + <%self:longhand name="font-size"> + use servo_util::geometry::Au; pub type SpecifiedValue = specified::Length; // Percentages are the same as em. pub mod computed_value { - use super::super::Au; + use servo_util::geometry::Au; pub type T = Au; } const MEDIUM_PX: int = 16; @@ -1244,30 +1123,31 @@ pub mod longhands { return context.font_size } /// <length> | <percentage> | <absolute-size> | <relative-size> - pub fn from_component_value(input: &ComponentValue, _base_url: &Url) - -> Result<SpecifiedValue, ()> { - match specified::LengthOrPercentage::parse_non_negative(input) { - Ok(specified::LengthOrPercentage::Length(value)) => return Ok(value), - Ok(specified::LengthOrPercentage::Percentage(value)) => return Ok(specified::Length::Em(value)), - Err(()) => (), - } - match try!(get_ident_lower(input)).as_slice() { - "xx-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 5)), - "x-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 4)), - "small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 8 / 9)), - "medium" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX))), - "large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 6 / 5)), - "x-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 2)), - "xx-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 2)), - - // https://github.com/servo/servo/issues/3423#issuecomment-56321664 - "smaller" => Ok(specified::Length::Em(0.85)), - "larger" => Ok(specified::Length::Em(1.2)), + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + input.try(specified::LengthOrPercentage::parse_non_negative) + .map(|value| match value { + specified::LengthOrPercentage::Length(value) => value, + specified::LengthOrPercentage::Percentage(value) => specified::Length::Em(value) + }) + .or_else(|()| { + match_ignore_ascii_case! { try!(input.expect_ident()): + "xx-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 5)), + "x-small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 4)), + "small" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 8 / 9)), + "medium" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX))), + "large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 6 / 5)), + "x-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 3 / 2)), + "xx-large" => Ok(specified::Length::Au(Au::from_px(MEDIUM_PX) * 2)), + + // https://github.com/servo/servo/issues/3423#issuecomment-56321664 + "smaller" => Ok(specified::Length::Em(0.85)), + "larger" => Ok(specified::Length::Em(1.2)) - _ => return Err(()) - } + _ => Err(()) + } + }) } - </%self:single_component_value> + </%self:longhand> // CSS 2.1, Section 16 - Text @@ -1276,10 +1156,10 @@ pub mod longhands { // TODO: initial value should be 'start' (CSS Text Level 3, direction-dependent.) ${single_keyword("text-align", "left right center justify")} - <%self:single_component_value name="letter-spacing"> + <%self:longhand name="letter-spacing"> pub type SpecifiedValue = Option<specified::Length>; pub mod computed_value { - use super::super::Au; + use servo_util::geometry::Au; pub type T = Option<Au>; } #[inline] @@ -1291,18 +1171,19 @@ pub mod longhands { -> computed_value::T { value.map(|length| computed::compute_Au(length, context)) } - pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> { - match input { - &Ident(ref value) if value.eq_ignore_ascii_case("normal") => Ok(None), - _ => specified::Length::parse_non_negative(input).map(|length| Some(length)), + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + Ok(None) + } else { + specified::Length::parse_non_negative(input).map(Some) } } - </%self:single_component_value> + </%self:longhand> - <%self:single_component_value name="word-spacing"> + <%self:longhand name="word-spacing"> pub type SpecifiedValue = Option<specified::Length>; pub mod computed_value { - use super::super::Au; + use servo_util::geometry::Au; pub type T = Option<Au>; } #[inline] @@ -1314,13 +1195,14 @@ pub mod longhands { -> computed_value::T { value.map(|length| computed::compute_Au(length, context)) } - pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> { - match input { - &Ident(ref value) if value.eq_ignore_ascii_case("normal") => Ok(None), - _ => specified::Length::parse_non_negative(input).map(|length| Some(length)), + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + Ok(None) + } else { + specified::Length::parse_non_negative(input).map(Some) } } - </%self:single_component_value> + </%self:longhand> ${predefined_type("text-indent", "LengthOrPercentage", "computed::LengthOrPercentage::Length(Au(0))")} @@ -1370,37 +1252,34 @@ pub mod longhands { pub mod computed_value { pub type T = super::SpecifiedValue; #[allow(non_upper_case_globals)] - pub const none: T = super::SpecifiedValue { underline: false, overline: false, line_through: false }; + pub const none: T = super::SpecifiedValue { + underline: false, overline: false, line_through: false + }; } #[inline] pub fn get_initial_value() -> computed_value::T { - none + computed_value::none } /// none | [ underline || overline || line-through || blink ] - pub fn parse(input: &[ComponentValue], _base_url: &Url) -> Result<SpecifiedValue, ()> { + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { let mut result = SpecifiedValue { underline: false, overline: false, line_through: false, }; - match one_component_value(input) { - Ok(&Ident(ref value)) - if value.as_slice().eq_ignore_ascii_case("none") => return Ok(result), - _ => {} + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(result) } let mut blink = false; let mut empty = true; - for component_value in input.skip_whitespace() { - match get_ident_lower(component_value) { - Err(()) => return Err(()), - Ok(keyword) => match keyword.as_slice() { - "underline" => if result.underline { return Err(()) } - else { empty = false; result.underline = true }, - "overline" => if result.overline { return Err(()) } - else { empty = false; result.overline = true }, - "line-through" => if result.line_through { return Err(()) } - else { empty = false; result.line_through = true }, - "blink" => if blink { return Err(()) } - else { empty = false; blink = true }, - _ => return Err(()), - } + loop { + match_ignore_ascii_case! { try!(input.expect_ident()): + "underline" => if result.underline { return Err(()) } + else { empty = false; result.underline = true }, + "overline" => if result.overline { return Err(()) } + else { empty = false; result.overline = true }, + "line-through" => if result.line_through { return Err(()) } + else { empty = false; result.line_through = true }, + "blink" => if blink { return Err(()) } + else { empty = false; blink = true } + _ => break, } } if !empty { Ok(result) } else { Err(()) } @@ -1411,6 +1290,7 @@ pub mod longhands { <%self:longhand name="-servo-text-decorations-in-effect" derived_from="display text-decoration"> + use cssparser::RGBA; pub use super::computed_as_specified as to_computed_value; #[deriving(Clone, PartialEq, Copy)] @@ -1445,7 +1325,9 @@ pub mod longhands { // Start with no declarations if this is a block; otherwise, start with the // declarations in effect and add in the text decorations that this inline specifies. let mut result = match context.display { - display::computed_value::T::inline => context.inherited_text_decorations_in_effect, + super::display::computed_value::T::inline => { + context.inherited_text_decorations_in_effect + } _ => { SpecifiedValue { underline: None, @@ -1469,14 +1351,15 @@ pub mod longhands { } #[inline] - pub fn derive_from_text_decoration(_: text_decoration::computed_value::T, + pub fn derive_from_text_decoration(_: super::text_decoration::computed_value::T, context: &computed::Context) -> computed_value::T { derive(context) } #[inline] - pub fn derive_from_display(_: display::computed_value::T, context: &computed::Context) + pub fn derive_from_display(_: super::display::computed_value::T, + context: &computed::Context) -> computed_value::T { derive(context) } @@ -1521,9 +1404,10 @@ pub mod longhands { ${new_style_struct("Pointing", is_inherited=True)} - <%self:single_component_value name="cursor"> + <%self:longhand name="cursor"> use servo_util::cursor as util_cursor; pub use super::computed_as_specified as to_computed_value; + pub use self::computed_value::T as SpecifiedValue; pub mod computed_value { use servo_util::cursor::Cursor; @@ -1533,26 +1417,21 @@ pub mod longhands { SpecifiedCursor(Cursor), } } - pub type SpecifiedValue = computed_value::T; #[inline] pub fn get_initial_value() -> computed_value::T { computed_value::T::AutoCursor } - pub fn from_component_value(value: &ComponentValue, _: &Url) - -> Result<SpecifiedValue,()> { - match value { - &Ident(ref ident) => { - if ident.eq_ignore_ascii_case("auto") { - Ok(T::AutoCursor) - } else { - util_cursor::Cursor::from_css_keyword(ident.as_slice()) - .map(T::SpecifiedCursor) - } - } - _ => Err(()) + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + use std::ascii::AsciiExt; + let ident = try!(input.expect_ident()); + if ident.eq_ignore_ascii_case("auto") { + Ok(SpecifiedValue::AutoCursor) + } else { + util_cursor::Cursor::from_css_keyword(ident.as_slice()) + .map(SpecifiedValue::SpecifiedCursor) } } - </%self:single_component_value> + </%self:longhand> // NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact) // is nonstandard, slated for CSS4-UI. @@ -1562,10 +1441,11 @@ pub mod longhands { // Box-shadow, etc. ${new_style_struct("Effects", is_inherited=False)} - <%self:single_component_value name="opacity"> + <%self:longhand name="opacity"> + use values::CSSFloat; pub type SpecifiedValue = CSSFloat; pub mod computed_value { - use super::super::CSSFloat; + use values::CSSFloat; pub type T = CSSFloat; } #[inline] @@ -1583,13 +1463,10 @@ pub mod longhands { value } } - fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> { - match *input { - Number(ref value) => Ok(value.value), - _ => Err(()) - } + fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + input.expect_number() } - </%self:single_component_value> + </%self:longhand> <%self:longhand name="box-shadow"> use cssparser; @@ -1622,8 +1499,8 @@ pub mod longhands { } pub mod computed_value { - use super::super::Au; - use super::super::super::computed; + use servo_util::geometry::Au; + use values::computed; use std::fmt; pub type T = Vec<BoxShadow>; @@ -1655,14 +1532,12 @@ pub mod longhands { Vec::new() } - pub fn parse(input: &[ComponentValue], _: &Url) -> Result<SpecifiedValue,()> { - match one_component_value(input) { - Ok(&Ident(ref value)) if value.as_slice().eq_ignore_ascii_case("none") => { - return Ok(Vec::new()) - } - _ => {} + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + Ok(Vec::new()) + } else { + input.parse_comma_separated(parse_one_box_shadow) } - parse_slice_comma_separated(input, parse_one_box_shadow) } pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) @@ -1684,69 +1559,49 @@ pub mod longhands { } } - pub fn parse_one_box_shadow(iter: ParserIter) -> Result<SpecifiedBoxShadow,()> { + pub fn parse_one_box_shadow(input: &mut Parser) -> Result<SpecifiedBoxShadow, ()> { + use servo_util::geometry::Au; let mut lengths = [specified::Length::Au(Au(0)), ..4]; let mut lengths_parsed = false; let mut color = None; let mut inset = false; loop { - match iter.next() { - Some(&Ident(ref value)) if value.eq_ignore_ascii_case("inset") && !inset => { + if !inset { + if input.try(|input| input.expect_ident_matching("inset")).is_ok() { inset = true; continue } - Some(value) => { - // Try to parse a length. - match specified::Length::parse(value) { - Ok(the_length) if !lengths_parsed => { - lengths[0] = the_length; - let mut length_parsed_count = 1; - while length_parsed_count < 4 { - match iter.next() { - Some(value) => { - match specified::Length::parse(value) { - Ok(the_length) => { - lengths[length_parsed_count] = the_length; - } - Err(_) => { - iter.push_back(value); - break - } - } - } - None => break, - } - length_parsed_count += 1; - } - - // The first two lengths must be specified. - if length_parsed_count < 2 { - return Err(()) - } - - lengths_parsed = true; - continue + } + if !lengths_parsed { + if let Ok(value) = input.try(specified::Length::parse) { + lengths[0] = value; + let mut length_parsed_count = 1; + while length_parsed_count < 4 { + if let Ok(value) = input.try(specified::Length::parse) { + lengths[length_parsed_count] = value + } else { + break } - Ok(_) => return Err(()), - Err(()) => {} + length_parsed_count += 1; } - // Try to parse a color. - match specified::CSSColor::parse(value) { - Ok(ref the_color) if color.is_none() => { - color = Some(the_color.clone()); - continue - } - Ok(_) => return Err(()), - Err(()) => {} + // The first two lengths must be specified. + if length_parsed_count < 2 { + return Err(()) } - iter.push_back(value); - break + lengths_parsed = true; + continue } - None => break, } + if color.is_none() { + if let Ok(value) = input.try(specified::CSSColor::parse) { + color = Some(value); + continue + } + } + break } // Lengths must be specified. @@ -1765,11 +1620,11 @@ pub mod longhands { } </%self:longhand> - <%self:single_component_value name="clip"> + <%self:longhand name="clip"> // NB: `top` and `left` are 0 if `auto` per CSS 2.1 11.1.2. pub mod computed_value { - use super::super::Au; + use servo_util::geometry::Au; #[deriving(Clone, PartialEq, Eq, Copy, Show)] pub struct ClipRect { @@ -1807,45 +1662,48 @@ pub mod longhands { }) } - pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> { - match *input { - Function(ref name, ref args) if name.as_slice().eq_ignore_ascii_case("rect") => { - let sides = try!(parse_slice_comma_separated(args.as_slice(), |parser| { - match parser.next() { - Some(&Ident(ref ident)) if ident.eq_ignore_ascii_case("auto") => { - Ok(None) - } - Some(arg) => { - match specified::Length::parse(arg) { - Err(_) => { - parser.push_back(arg); - Err(()) - } - Ok(value) => Ok(Some(value)), - } - } - None => Err(()), - } - })); - if sides.len() != 4 { - return Err(()) + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + use std::ascii::AsciiExt; + use servo_util::geometry::Au; + use values::specified::Length; + + if input.try(|input| input.expect_ident_matching("auto")).is_ok() { + return Ok(None) + } + if !try!(input.expect_function()).eq_ignore_ascii_case("rect") { + return Err(()) + } + let sides = try!(input.parse_nested_block(|input| { + input.parse_comma_separated(|input| { + if input.try(|input| input.expect_ident_matching("auto")).is_ok() { + Ok(None) + } else { + Length::parse(input).map(Some) } - Ok(Some(SpecifiedClipRect { - top: sides[0].unwrap_or(specified::Length::Au(Au(0))), - right: sides[1], - bottom: sides[2], - left: sides[3].unwrap_or(specified::Length::Au(Au(0))), - })) - } - Ident(ref ident) if ident.as_slice().eq_ignore_ascii_case("auto") => Ok(None), - _ => Err(()) + }) + })); + if sides.len() == 4 { + Ok(Some(SpecifiedClipRect { + top: sides[0].unwrap_or(Length::Au(Au(0))), + right: sides[1], + bottom: sides[2], + left: sides[3].unwrap_or(Length::Au(Au(0))), + })) + } else { + Err(()) } } - </%self:single_component_value> + </%self:longhand> <%self:longhand name="filter"> + use values::specified::Angle; + pub use super::computed_as_specified as to_computed_value; + pub use self::computed_value::T as SpecifiedValue; + pub use self::computed_value::Filter; + pub mod computed_value { - use super::super::{Angle, CSSFloat}; + use values::specified::Angle; + use values::CSSFloat; // TODO(pcwalton): `blur`, `drop-shadow` #[deriving(Clone, PartialEq, Show)] @@ -1900,86 +1758,39 @@ pub mod longhands { } } - // TODO(pcwalton): `blur`, `drop-shadow` - #[deriving(Clone, Show)] - pub enum SpecifiedFilter { - Brightness(CSSFloat), - Contrast(CSSFloat), - Grayscale(CSSFloat), - HueRotate(Angle), - Invert(CSSFloat), - Opacity(CSSFloat), - Saturate(CSSFloat), - Sepia(CSSFloat), - } - - pub type SpecifiedValue = Vec<SpecifiedFilter>; - #[inline] pub fn get_initial_value() -> computed_value::T { computed_value::T::new(Vec::new()) } - pub fn to_computed_value(value: SpecifiedValue, _: &computed::Context) - -> computed_value::T { - computed_value::T::new(value.into_iter().map(|filter| { - match filter { - SpecifiedFilter::Brightness(amount) => { - computed_value::Filter::Brightness(amount) - } - SpecifiedFilter::Contrast(amount) => computed_value::Filter::Contrast(amount), - SpecifiedFilter::Grayscale(amount) => { - computed_value::Filter::Grayscale(amount) - } - SpecifiedFilter::HueRotate(angle) => computed_value::Filter::HueRotate(angle), - SpecifiedFilter::Invert(amount) => computed_value::Filter::Invert(amount), - SpecifiedFilter::Opacity(amount) => computed_value::Filter::Opacity(amount), - SpecifiedFilter::Saturate(amount) => computed_value::Filter::Saturate(amount), - SpecifiedFilter::Sepia(amount) => computed_value::Filter::Sepia(amount), - } - }).collect()) - } - - pub fn parse(input: &[ComponentValue], _: &Url) -> Result<SpecifiedValue,()> { + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { let mut filters = Vec::new(); - for filter in input.skip_whitespace() { - let name; - let args; - match *filter { - Function(ref function_name, ref function_args) => { - name = function_name; - args = function_args; - } - _ => return Err(()), - } - - if name.eq_ignore_ascii_case("brightness") && args.len() == 1 { - filters.push(SpecifiedFilter::Brightness(try!(parse_percentage(&args[0])))); - } else if name.eq_ignore_ascii_case("contrast") && args.len() == 1 { - filters.push(SpecifiedFilter::Contrast(try!(parse_percentage(&args[0])))); - } else if name.eq_ignore_ascii_case("grayscale") && args.len() == 1 { - filters.push(SpecifiedFilter::Grayscale(try!(parse_percentage(&args[0])))); - } else if name.eq_ignore_ascii_case("hue-rotate") && args.len() == 1 { - filters.push(SpecifiedFilter::HueRotate(try!(Angle::parse(&args[0])))); - } else if name.eq_ignore_ascii_case("invert") && args.len() == 1 { - filters.push(SpecifiedFilter::Invert(try!(parse_percentage(&args[0])))); - } else if name.eq_ignore_ascii_case("opacity") && args.len() == 1 { - filters.push(SpecifiedFilter::Opacity(try!(parse_percentage(&args[0])))); - } else if name.eq_ignore_ascii_case("saturate") && args.len() == 1 { - filters.push(SpecifiedFilter::Saturate(try!(parse_percentage(&args[0])))); - } else if name.eq_ignore_ascii_case("sepia") && args.len() == 1 { - filters.push(SpecifiedFilter::Sepia(try!(parse_percentage(&args[0])))); + loop { + if let Ok(function_name) = input.try(|input| input.expect_function()) { + filters.push(try!(input.parse_nested_block(|input| { + match_ignore_ascii_case! { function_name: + "brightness" => parse_factor(input).map(Filter::Brightness), + "contrast" => parse_factor(input).map(Filter::Contrast), + "grayscale" => parse_factor(input).map(Filter::Grayscale), + "hue-rotate" => Angle::parse(input).map(Filter::HueRotate), + "invert" => parse_factor(input).map(Filter::Invert), + "opacity" => parse_factor(input).map(Filter::Opacity), + "saturate" => parse_factor(input).map(Filter::Saturate), + "sepia" => parse_factor(input).map(Filter::Sepia) + _ => Err(()) + } + }))); } else { - return Err(()) + return Ok(SpecifiedValue::new(filters)) } } - Ok(filters) } - fn parse_percentage(input: &ComponentValue) -> Result<CSSFloat,()> { - match *input { - Number(ref value) => Ok(value.value), - Percentage(ref value) => Ok(value.value / 100.0), + fn parse_factor(input: &mut Parser) -> Result<::values::CSSFloat, ()> { + use cssparser::Token; + match input.next() { + Ok(Token::Number(value)) => Ok(value.value), + Ok(Token::Percentage(value)) => Ok(value.unit_value), _ => Err(()) } } @@ -1991,8 +1802,9 @@ pub mod longhands { pub mod shorthands { - pub use super::*; - pub use super::longhands::*; + use cssparser::Parser; + use parser::ParserContext; + use values::specified; <%def name="shorthand(name, sub_properties)"> <% @@ -2000,13 +1812,18 @@ pub mod shorthands { SHORTHANDS.append(shorthand) %> pub mod ${shorthand.ident} { - use super::*; + use cssparser::Parser; + use parser::ParserContext; + use properties::longhands; + + #[allow(missing_copy_implementations)] pub struct Longhands { % for sub_property in shorthand.sub_properties: - pub ${sub_property.ident}: Option<${sub_property.ident}::SpecifiedValue>, + pub ${sub_property.ident}: + Option<longhands::${sub_property.ident}::SpecifiedValue>, % endfor } - pub fn parse(input: &[ComponentValue], base_url: &Url) -> Result<Longhands, ()> { + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> { ${caller.body()} } } @@ -2016,187 +1833,161 @@ pub mod shorthands { <%self:shorthand name="${name}" sub_properties="${ ' '.join(sub_property_pattern % side for side in ['top', 'right', 'bottom', 'left'])}"> - let mut iter = input.skip_whitespace().map(|c| ${parser_function}(c, base_url).ok()); + use values::specified; + let _unused = context; // zero or more than four values is invalid. // one value sets them all // two values set (top, bottom) and (left, right) // three values set top, (left, right) and bottom // four values set them in order - let top = iter.next().unwrap_or(None); - let right = iter.next().unwrap_or(top.clone()); - let bottom = iter.next().unwrap_or(top.clone()); - let left = iter.next().unwrap_or(right.clone()); - if top.is_some() && right.is_some() && bottom.is_some() && left.is_some() - && iter.next().is_none() { - Ok(Longhands { - % for side in ["top", "right", "bottom", "left"]: - ${to_rust_ident(sub_property_pattern % side)}: ${side}, - % endfor - }) - } else { - Err(()) - } - </%self:shorthand> - </%def> - - // TODO: other background-* properties - <%self:shorthand name="background" - sub_properties="background-color background-position background-repeat background-attachment background-image"> - use std::mem; - - let (mut color, mut image, mut position, mut repeat, mut attachment) = - (None, None, None, None, None); - let mut unused_component_value = None; - let mut any = false; - - for component_value in input.skip_whitespace() { - // Try `background-position` first because it might not use the value. - if position.is_none() { - match mem::replace(&mut unused_component_value, None) { - Some(saved_component_value) => { - // First try parsing a pair of values, then a single value. - match background_position::parse_two(saved_component_value, - component_value) { - Ok(v) => { - position = Some(v); - any = true; - continue - }, - Err(()) => { - match background_position::parse_one(saved_component_value) { - Ok(v) => { - position = Some(v); - any = true; - // We haven't used the current `component_value`; - // keep attempting to parse it below. - }, - // If we get here, parsing failed. - Err(()) => return Err(()) - } - } + let top = try!(${parser_function}(input)); + let right; + let bottom; + let left; + match input.try(${parser_function}) { + Err(()) => { + right = top.clone(); + bottom = top.clone(); + left = top.clone(); + } + Ok(value) => { + right = value; + match input.try(${parser_function}) { + Err(()) => { + bottom = top.clone(); + left = right.clone(); + } + Ok(value) => { + bottom = value; + match input.try(${parser_function}) { + Err(()) => { + left = right.clone(); + } + Ok(value) => { + left = value; } } - None => () // Wait until we have a pair of potential values. - } - } - if color.is_none() { - match background_color::from_component_value(component_value, base_url) { - Ok(v) => { - color = Some(v); - any = true; - continue - }, - Err(()) => () } } - if image.is_none() { - match background_image::from_component_value(component_value, base_url) { - Ok(v) => { - image = Some(v); - any = true; - continue - }, - Err(()) => (), - } - } + } + } + Ok(Longhands { + % for side in ["top", "right", "bottom", "left"]: + ${to_rust_ident(sub_property_pattern % side)}: Some(${side}), + % endfor + }) + </%self:shorthand> + </%def> - if repeat.is_none() { - match background_repeat::from_component_value(component_value, base_url) { - Ok(v) => { - repeat = Some(v); - any = true; - continue - }, - Err(()) => () - } - } + // TODO: other background-* properties + <%self:shorthand name="background" + sub_properties="background-color background-position background-repeat background-attachment background-image"> + use properties::longhands::{background_color, background_position, background_repeat, + background_attachment, background_image}; - if attachment.is_none() { - match background_attachment::from_component_value(component_value, - base_url) { - Ok(v) => { - attachment = Some(v); - any = true; - continue - }, - Err(()) => () - } - } + let mut color = None; + let mut image = None; + let mut position = None; + let mut repeat = None; + let mut attachment = None; + let mut any = false; - // Save the component value. It may the first of a background-position pair. - unused_component_value = Some(component_value); + loop { + if position.is_none() { + if let Ok(value) = input.try(|input| background_position::parse(context, input)) { + position = Some(value); + any = true; + continue } - - if position.is_none() { - // Check for a lone trailing background-position value. - match mem::replace(&mut unused_component_value, None) { - Some(saved_component_value) => { - match background_position::parse_one(saved_component_value) { - Ok(v) => { - position = Some(v); - any = true; - }, - Err(()) => return Err(()) - } - } - None => () - } + } + if color.is_none() { + if let Ok(value) = input.try(|input| background_color::parse(context, input)) { + color = Some(value); + any = true; + continue } - - if any && unused_component_value.is_none() { - Ok(Longhands { - background_color: color, - background_image: image, - background_position: position, - background_repeat: repeat, - background_attachment: attachment, - }) - } else { - Err(()) + } + if image.is_none() { + if let Ok(value) = input.try(|input| background_image::parse(context, input)) { + image = Some(value); + any = true; + continue + } + } + if repeat.is_none() { + if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) { + repeat = Some(value); + any = true; + continue + } + } + if attachment.is_none() { + if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) { + attachment = Some(value); + any = true; + continue } + } + break + } + + if any { + Ok(Longhands { + background_color: color, + background_image: image, + background_position: position, + background_repeat: repeat, + background_attachment: attachment, + }) + } else { + Err(()) + } </%self:shorthand> - ${four_sides_shorthand("margin", "margin-%s", "margin_top::from_component_value")} - ${four_sides_shorthand("padding", "padding-%s", "padding_top::from_component_value")} + ${four_sides_shorthand("margin", "margin-%s", "specified::LengthOrPercentageOrAuto::parse")} + ${four_sides_shorthand("padding", "padding-%s", "specified::LengthOrPercentage::parse")} - pub fn parse_color(value: &ComponentValue, _base_url: &Url) -> Result<specified::CSSColor, ()> { - specified::CSSColor::parse(value) - } - ${four_sides_shorthand("border-color", "border-%s-color", "parse_color")} + ${four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse")} ${four_sides_shorthand("border-style", "border-%s-style", - "border_top_style::from_component_value")} - ${four_sides_shorthand("border-width", "border-%s-width", "parse_border_width")} + "specified::BorderStyle::parse")} + ${four_sides_shorthand("border-width", "border-%s-width", + "specified::parse_border_width")} - pub fn parse_border(input: &[ComponentValue], base_url: &Url) + pub fn parse_border(context: &ParserContext, input: &mut Parser) -> Result<(Option<specified::CSSColor>, - Option<border_top_style::SpecifiedValue>, + Option<specified::BorderStyle>, Option<specified::Length>), ()> { + use values::specified; + let _unused = context; let mut color = None; let mut style = None; let mut width = None; let mut any = false; - for component_value in input.skip_whitespace() { + loop { if color.is_none() { - match specified::CSSColor::parse(component_value) { - Ok(c) => { color = Some(c); any = true; continue }, - Err(()) => () + if let Ok(value) = input.try(specified::CSSColor::parse) { + color = Some(value); + any = true; + continue } } if style.is_none() { - match border_top_style::from_component_value(component_value, base_url) { - Ok(s) => { style = Some(s); any = true; continue }, - Err(()) => () + if let Ok(value) = input.try(specified::BorderStyle::parse) { + style = Some(value); + any = true; + continue } } if width.is_none() { - match parse_border_width(component_value, base_url) { - Ok(w) => { width = Some(w); any = true; continue }, - Err(()) => () + if let Ok(value) = input.try(specified::parse_border_width) { + width = Some(value); + any = true; + continue } } - return Err(()) + break } if any { Ok((color, style, width)) } else { Err(()) } } @@ -2207,12 +1998,11 @@ pub mod shorthands { 'border-%s-%s' % (side, prop) for prop in ['color', 'style', 'width'] )}"> - parse_border(input, base_url).map(|(color, style, width)| { - Longhands { - % for prop in ["color", "style", "width"]: - ${"border_%s_%s: %s," % (side, prop, prop)} - % endfor - } + let (color, style, width) = try!(super::parse_border(context, input)); + Ok(Longhands { + % for prop in ["color", "style", "width"]: + ${"border_%s_%s: %s," % (side, prop, prop)} + % endfor }) </%self:shorthand> % endfor @@ -2222,14 +2012,13 @@ pub mod shorthands { for side in ['top', 'right', 'bottom', 'left'] for prop in ['color', 'style', 'width'] )}"> - parse_border(input, base_url).map(|(color, style, width)| { - Longhands { - % for side in ["top", "right", "bottom", "left"]: - % for prop in ["color", "style", "width"]: - ${"border_%s_%s: %s.clone()," % (side, prop, prop)} - % endfor + let (color, style, width) = try!(super::parse_border(context, input)); + Ok(Longhands { + % for side in ["top", "right", "bottom", "left"]: + % for prop in ["color", "style", "width"]: + ${"border_%s_%s: %s.clone()," % (side, prop, prop)} % endfor - } + % endfor }) </%self:shorthand> @@ -2237,27 +2026,21 @@ pub mod shorthands { 'border-%s-radius' % (corner) for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left'] )}"> - - use std::iter::Peekable; - - let _ignored = base_url; - - fn parse_one_set_of_border_radii<'a,I>(mut input: Peekable< &'a ComponentValue,I >) - -> Result<[specified::LengthOrPercentage, ..4],()> - where I: Iterator< &'a ComponentValue > { - let (mut count, mut values) = (0u, [specified::LengthOrPercentage::Length(specified::Length::Au(Au(0))), ..4]); + use servo_util::geometry::Au; + use values::specified::{Length, LengthOrPercentage}; + let _ignored = context; + + fn parse_one_set_of_border_radii(mut input: &mut Parser) + -> Result<[LengthOrPercentage, ..4], ()> { + let mut count = 0; + let mut values = [LengthOrPercentage::Length(Length::Au(Au(0))), ..4]; while count < 4 { - let token = match input.peek() { - None => break, - Some(token) => *token, - }; - let value = match specified::LengthOrPercentage::parse(token) { - Err(_) => break, - Ok(value) => value, - }; - drop(input.next()); - values[count] = value; - count += 1 + if let Ok(value) = input.try(LengthOrPercentage::parse) { + values[count] = value; + count += 1; + } else { + break + } } match count { @@ -2269,60 +2052,48 @@ pub mod shorthands { } } - let input = input.skip_whitespace().peekable(); let radii = try!(parse_one_set_of_border_radii(input)); // TODO(pcwalton): Elliptical borders. Ok(Longhands { - border_top_left_radius: Some(border_top_left_radius::SpecifiedValue { - radius: radii[0], - }), - border_top_right_radius: Some(border_top_left_radius::SpecifiedValue { - radius: radii[1], - }), - border_bottom_right_radius: Some(border_top_left_radius::SpecifiedValue { - radius: radii[2], - }), - border_bottom_left_radius: Some(border_top_left_radius::SpecifiedValue { - radius: radii[3], - }), + border_top_left_radius: Some(radii[0]), + border_top_right_radius: Some(radii[1]), + border_bottom_right_radius: Some(radii[2]), + border_bottom_left_radius: Some(radii[3]), }) </%self:shorthand> <%self:shorthand name="outline" sub_properties="outline-color outline-style outline-width"> - let (mut color, mut style, mut width, mut any) = (None, None, None, false); - for component_value in input.skip_whitespace() { + use values::specified; + + let _unused = context; + let mut color = None; + let mut style = None; + let mut width = None; + let mut any = false; + loop { if color.is_none() { - match specified::CSSColor::parse(component_value) { - Ok(c) => { - color = Some(c); - any = true; - continue - } - Err(()) => {} + if let Ok(value) = input.try(specified::CSSColor::parse) { + color = Some(value); + any = true; + continue } } if style.is_none() { - match border_top_style::from_component_value(component_value, base_url) { - Ok(s) => { - style = Some(s); - any = true; - continue - } - Err(()) => {} + if let Ok(value) = input.try(specified::BorderStyle::parse) { + style = Some(value); + any = true; + continue } } if width.is_none() { - match parse_border_width(component_value, base_url) { - Ok(w) => { - width = Some(w); - any = true; - continue - } - Err(()) => {} + if let Ok(value) = input.try(specified::parse_border_width) { + width = Some(value); + any = true; + continue } } - return Err(()) + break } if any { Ok(Longhands { @@ -2337,71 +2108,55 @@ pub mod shorthands { <%self:shorthand name="font" sub_properties="font-style font-variant font-weight font-size line-height font-family"> - let mut iter = input.skip_whitespace(); - let mut nb_normals = 0u; + use properties::longhands::{font_style, font_variant, font_weight, font_size, + line_height, font_family}; + let mut nb_normals = 0; let mut style = None; let mut variant = None; let mut weight = None; - let mut size = None; - let mut line_height = None; - for component_value in iter { + let size; + loop { // Special-case 'normal' because it is valid in each of // font-style, font-weight and font-variant. // Leaves the values to None, 'normal' is the initial value for each of them. - match get_ident_lower(component_value) { - Ok(ref ident) if ident.as_slice().eq_ignore_ascii_case("normal") => { - nb_normals += 1; - continue; - } - _ => {} + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + nb_normals += 1; + continue; } if style.is_none() { - match font_style::from_component_value(component_value, base_url) { - Ok(s) => { style = Some(s); continue }, - Err(()) => () + if let Ok(value) = input.try(|input| font_style::parse(context, input)) { + style = Some(value); + continue } } if weight.is_none() { - match font_weight::from_component_value(component_value, base_url) { - Ok(w) => { weight = Some(w); continue }, - Err(()) => () + if let Ok(value) = input.try(|input| font_weight::parse(context, input)) { + weight = Some(value); + continue } } if variant.is_none() { - match font_variant::from_component_value(component_value, base_url) { - Ok(v) => { variant = Some(v); continue }, - Err(()) => () + if let Ok(value) = input.try(|input| font_variant::parse(context, input)) { + variant = Some(value); + continue } } - match font_size::from_component_value(component_value, base_url) { - Ok(s) => { size = Some(s); break }, - Err(()) => return Err(()) - } + size = Some(try!(font_size::parse(context, input))); + break } #[inline] - fn count<T>(opt: &Option<T>) -> uint { - match opt { - &Some(_) => 1, - &None => 0, - } + fn count<T>(opt: &Option<T>) -> u8 { + if opt.is_some() { 1 } else { 0 } } if size.is_none() || (count(&style) + count(&weight) + count(&variant) + nb_normals) > 3 { return Err(()) } - let mut copied_iter = iter.clone(); - match copied_iter.next() { - Some(&Delim('/')) => { - iter = copied_iter; - line_height = match iter.next() { - Some(v) => line_height::from_component_value(v, base_url).ok(), - _ => return Err(()), - }; - if line_height.is_none() { return Err(()) } - } - _ => () - } - let family = try!(parse_comma_separated( - &mut BufferedIter::new(iter), font_family::parse_one_family)); + let line_height = if input.try(|input| input.expect_delim('/')).is_ok() { + Some(try!(line_height::parse(context, input))) + } else { + None + }; + let family = try!(input.parse_comma_separated(font_family::parse_one_family)); Ok(Longhands { font_style: style, font_variant: variant, @@ -2415,64 +2170,54 @@ pub mod shorthands { // Per CSS-TEXT 6.2, "for legacy reasons, UAs must treat `word-wrap` as an alternate name for // the `overflow-wrap` property, as if it were a shorthand of `overflow-wrap`." <%self:shorthand name="word-wrap" sub_properties="overflow-wrap"> - overflow_wrap::parse(input, base_url).map(|specified_value| { - Longhands { - overflow_wrap: Some(specified_value), - } + use properties::longhands::overflow_wrap; + Ok(Longhands { + overflow_wrap: Some(try!(overflow_wrap::parse(context, input))), }) </%self:shorthand> <%self:shorthand name="list-style" sub_properties="list-style-image list-style-position list-style-type"> + use properties::longhands::{list_style_image, list_style_position, list_style_type}; + // `none` is ambiguous until we've finished parsing the shorthands, so we count the number // of times we see it. let mut nones = 0u8; let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false); - for component_value in input.skip_whitespace() { - match component_value { - &Ident(ref value) if value.eq_ignore_ascii_case("none") => { - nones = nones + 1; - if nones > 2 { - return Err(()) - } - any = true; - continue + loop { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + nones = nones + 1; + if nones > 2 { + return Err(()) } - _ => {} + any = true; + continue } if list_style_type.is_none() { - match list_style_type::from_component_value(component_value, base_url) { - Ok(v) => { - list_style_type = Some(v); - any = true; - continue - }, - Err(()) => () + if let Ok(value) = input.try(|input| list_style_type::parse(context, input)) { + list_style_type = Some(value); + any = true; + continue } } if image.is_none() { - match list_style_image::from_component_value(component_value, base_url) { - Ok(v) => { - image = Some(v); - any = true; - continue - }, - Err(()) => (), + if let Ok(value) = input.try(|input| list_style_image::parse(context, input)) { + image = Some(value); + any = true; + continue } } if position.is_none() { - match list_style_position::from_component_value(component_value, base_url) { - Ok(v) => { - position = Some(v); - any = true; - continue - }, - Err(()) => () + if let Ok(value) = input.try(|input| list_style_position::parse(context, input)) { + position = Some(value); + any = true; + continue } } + break } // If there are two `none`s, then we can't have a type or image; if there is one `none`, @@ -2483,14 +2228,14 @@ pub mod shorthands { Ok(Longhands { list_style_position: position, list_style_image: Some(None), - list_style_type: Some(list_style_type::T::none), + list_style_type: Some(list_style_type::SpecifiedValue::none), }) } (true, 1, None, Some(image)) => { Ok(Longhands { list_style_position: position, list_style_image: Some(image), - list_style_type: Some(list_style_type::T::none), + list_style_type: Some(list_style_type::SpecifiedValue::none), }) } (true, 1, Some(list_style_type), None) => { @@ -2504,7 +2249,7 @@ pub mod shorthands { Ok(Longhands { list_style_position: position, list_style_image: Some(None), - list_style_type: Some(list_style_type::T::none), + list_style_type: Some(list_style_type::SpecifiedValue::none), }) } (true, 0, list_style_type, image) => { @@ -2564,6 +2309,7 @@ mod property_bit_field { /// Declarations are stored in reverse order. /// Overridden declarations are skipped. +#[deriving(Show, PartialEq)] pub struct PropertyDeclarationBlock { pub important: Arc<Vec<PropertyDeclaration>>, pub normal: Arc<Vec<PropertyDeclaration>>, @@ -2571,49 +2317,89 @@ pub struct PropertyDeclarationBlock { pub fn parse_style_attribute(input: &str, base_url: &Url) -> PropertyDeclarationBlock { - parse_property_declaration_list(tokenize(input), base_url) + let context = ParserContext { + stylesheet_origin: Origin::Author, + base_url: base_url, + namespaces: NamespaceMap::new(), + }; + parse_property_declaration_list(&context, &mut Parser::new(input)) } -pub fn parse_property_declaration_list<I: Iterator<Node>>(input: I, base_url: &Url) -> PropertyDeclarationBlock { - let mut important_declarations = vec!(); - let mut normal_declarations = vec!(); - let mut important_seen = PropertyBitField::new(); - let mut normal_seen = PropertyBitField::new(); - let items: Vec<DeclarationListItem> = - ErrorLoggerIterator(parse_declaration_list(input)).collect(); - for item in items.into_iter().rev() { - 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: l, name: n, value: v, important: i}) => { - // TODO: only keep the last valid declaration for a given name. - let (list, seen) = if i { - (&mut important_declarations, &mut important_seen) - } else { - (&mut normal_declarations, &mut normal_seen) - }; - match PropertyDeclaration::parse(n.as_slice(), v.as_slice(), list, base_url, seen) { - PropertyDeclarationParseResult::UnknownProperty => log_css_error(l, format!( - "Unsupported property: {}:{}", n, v.to_css_string()).as_slice()), - PropertyDeclarationParseResult::ExperimentalProperty => log_css_error(l, format!( - "Experimental property, use `servo --enable_experimental` \ - or `servo -e` to enable: {}:{}", - n, v.to_css_string()).as_slice()), - PropertyDeclarationParseResult::InvalidValue => log_css_error(l, format!( - "Invalid value: {}:{}", n, v.to_css_string()).as_slice()), - PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => (), - } +struct PropertyDeclarationParser<'a, 'b: 'a> { + context: &'a ParserContext<'b>, + important_declarations: Vec<PropertyDeclaration>, + normal_declarations: Vec<PropertyDeclaration>, +} + + +/// Default methods reject all at rules. +impl<'a, 'b> AtRuleParser<(), ()> for PropertyDeclarationParser<'a, 'b> {} + + +impl<'a, 'b> DeclarationParser<()> for PropertyDeclarationParser<'a, 'b> { + fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> { + let mut results = vec![]; + let important = try!(input.parse_entirely(|input| { + match PropertyDeclaration::parse(name, self.context, input, &mut results) { + PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {} + _ => return Err(()) } + Ok(input.try(parse_important).is_ok()) + })); + if important { + self.important_declarations.push_all(results.as_slice()); + } else { + self.normal_declarations.push_all(results.as_slice()); } + Ok(()) } +} + + +pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser) + -> PropertyDeclarationBlock { + let parser = PropertyDeclarationParser { + context: context, + important_declarations: vec![], + normal_declarations: vec![], + }; + let parser = DeclarationListParser::new(input, parser).run(); PropertyDeclarationBlock { - important: Arc::new(important_declarations), - normal: Arc::new(normal_declarations), + important: Arc::new(deduplicate_property_declarations(parser.important_declarations)), + normal: Arc::new(deduplicate_property_declarations(parser.normal_declarations)), + } +} + + +/// Only keep the last declaration for any given property. +/// The input is in source order, output in reverse source order. +fn deduplicate_property_declarations(declarations: Vec<PropertyDeclaration>) + -> Vec<PropertyDeclaration> { + let mut deduplicated = vec![]; + let mut seen = PropertyBitField::new(); + for declaration in declarations.into_iter().rev() { + match declaration { + % for property in LONGHANDS: + PropertyDeclaration::${property.camel_case}(..) => { + % if property.derived_from is None: + if seen.get_${property.ident}() { + continue + } + seen.set_${property.ident}() + % else: + unreachable!(); + % endif + }, + % endfor + } + deduplicated.push(declaration) } + deduplicated } +#[deriving(Copy, PartialEq, Eq, Show)] pub enum CSSWideKeyword { InitialKeyword, InheritKeyword, @@ -2621,20 +2407,18 @@ pub enum CSSWideKeyword { } impl CSSWideKeyword { - pub fn parse(input: &[ComponentValue]) -> Result<CSSWideKeyword, ()> { - one_component_value(input).and_then(get_ident_lower).and_then(|keyword| { - match keyword.as_slice() { - "initial" => Ok(CSSWideKeyword::InitialKeyword), - "inherit" => Ok(CSSWideKeyword::InheritKeyword), - "unset" => Ok(CSSWideKeyword::UnsetKeyword), - _ => Err(()) - } - }) + pub fn parse(input: &mut Parser) -> Result<CSSWideKeyword, ()> { + match_ignore_ascii_case! { try!(input.expect_ident()): + "initial" => Ok(CSSWideKeyword::InitialKeyword), + "inherit" => Ok(CSSWideKeyword::InheritKeyword), + "unset" => Ok(CSSWideKeyword::UnsetKeyword) + _ => Err(()) + } } } -#[deriving(Clone, PartialEq, Eq, Copy)] +#[deriving(Clone, PartialEq, Eq, Copy, Show)] pub enum DeclaredValue<T> { SpecifiedValue(T), Initial, @@ -2654,10 +2438,10 @@ impl<T: Show> DeclaredValue<T> { } } -#[deriving(Clone)] +#[deriving(Clone, PartialEq)] pub enum PropertyDeclaration { % for property in LONGHANDS: - ${property.camel_case}Declaration(DeclaredValue<longhands::${property.ident}::SpecifiedValue>), + ${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>), % endfor } @@ -2671,14 +2455,14 @@ pub enum PropertyDeclarationParseResult { } impl PropertyDeclaration { - pub fn name(&self) -> String { + pub fn name(&self) -> &'static str { match self { % for property in LONGHANDS: % if property.derived_from is None: - &PropertyDeclaration::${property.camel_case}Declaration(..) => "${property.name}".to_owned(), + &PropertyDeclaration::${property.camel_case}(..) => "${property.name}", % endif % endfor - _ => "".to_owned(), + _ => "", } } @@ -2686,7 +2470,7 @@ impl PropertyDeclaration { match self { % for property in LONGHANDS: % if property.derived_from is None: - &PropertyDeclaration::${property.camel_case}Declaration(ref value) => + &PropertyDeclaration::${property.camel_case}(ref value) => value.specified_value() .unwrap_or_else(|| format!("{}", longhands::${property.ident}::get_initial_value())), % endif @@ -2700,18 +2484,16 @@ impl PropertyDeclaration { match (self, name_lower.as_slice()) { % for property in LONGHANDS: % if property.derived_from is None: - (&PropertyDeclaration::${property.camel_case}Declaration(..), "${property.name}") => true, + (&PropertyDeclaration::${property.camel_case}(..), "${property.name}") => true, % endif % endfor _ => false, } } - pub fn parse(name: &str, value: &[ComponentValue], - result_list: &mut Vec<PropertyDeclaration>, - base_url: &Url, - seen: &mut PropertyBitField) -> PropertyDeclarationParseResult { - match name.to_ascii_lower().as_slice() { + pub fn parse(name: &str, context: &ParserContext, input: &mut Parser, + result_list: &mut Vec<PropertyDeclaration>) -> PropertyDeclarationParseResult { + match_ignore_ascii_case! { name: % for property in LONGHANDS: % if property.derived_from is None: "${property.name}" => { @@ -2720,13 +2502,9 @@ impl PropertyDeclaration { return PropertyDeclarationParseResult::ExperimentalProperty } % endif - if seen.get_${property.ident}() { - return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration - } - match longhands::${property.ident}::parse_declared(value, base_url) { + match longhands::${property.ident}::parse_declared(context, input) { Ok(value) => { - seen.set_${property.ident}(); - result_list.push(PropertyDeclaration::${property.camel_case}Declaration(value)); + result_list.push(PropertyDeclaration::${property.camel_case}(value)); PropertyDeclarationParseResult::ValidOrIgnoredDeclaration }, Err(()) => PropertyDeclarationParseResult::InvalidValue, @@ -2738,56 +2516,40 @@ impl PropertyDeclaration { % endfor % for shorthand in SHORTHANDS: "${shorthand.name}" => { - if ${" && ".join("seen.get_%s()" % sub_property.ident - for sub_property in shorthand.sub_properties)} { - return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration - } - match CSSWideKeyword::parse(value) { + match input.try(CSSWideKeyword::parse) { Ok(CSSWideKeyword::InheritKeyword) => { % for sub_property in shorthand.sub_properties: - if !seen.get_${sub_property.ident}() { - seen.set_${sub_property.ident}(); - result_list.push( - PropertyDeclaration::${sub_property.camel_case}Declaration( - DeclaredValue::Inherit)); - } + result_list.push( + PropertyDeclaration::${sub_property.camel_case}( + DeclaredValue::Inherit)); % endfor PropertyDeclarationParseResult::ValidOrIgnoredDeclaration }, Ok(CSSWideKeyword::InitialKeyword) => { % for sub_property in shorthand.sub_properties: - if !seen.get_${sub_property.ident}() { - seen.set_${sub_property.ident}(); - result_list.push( - PropertyDeclaration::${sub_property.camel_case}Declaration( - DeclaredValue::Initial)); - } + result_list.push( + PropertyDeclaration::${sub_property.camel_case}( + DeclaredValue::Initial)); % endfor PropertyDeclarationParseResult::ValidOrIgnoredDeclaration }, Ok(CSSWideKeyword::UnsetKeyword) => { % for sub_property in shorthand.sub_properties: - if !seen.get_${sub_property.ident}() { - seen.set_${sub_property.ident}(); - result_list.push(PropertyDeclaration::${sub_property.camel_case}Declaration( - DeclaredValue::${"Inherit" if sub_property.style_struct.inherited else "Initial"} - )); - } + result_list.push(PropertyDeclaration::${sub_property.camel_case}( + DeclaredValue::${"Inherit" if sub_property.style_struct.inherited else "Initial"} + )); % endfor PropertyDeclarationParseResult::ValidOrIgnoredDeclaration }, - Err(()) => match shorthands::${shorthand.ident}::parse(value, base_url) { + Err(()) => match shorthands::${shorthand.ident}::parse(context, input) { Ok(result) => { % for sub_property in shorthand.sub_properties: - if !seen.get_${sub_property.ident}() { - seen.set_${sub_property.ident}(); - result_list.push(PropertyDeclaration::${sub_property.camel_case}Declaration( - match result.${sub_property.ident} { - Some(value) => DeclaredValue::SpecifiedValue(value), - None => DeclaredValue::Initial, - } - )); - } + result_list.push(PropertyDeclaration::${sub_property.camel_case}( + match result.${sub_property.ident} { + Some(value) => DeclaredValue::SpecifiedValue(value), + None => DeclaredValue::Initial, + } + )); % endfor PropertyDeclarationParseResult::ValidOrIgnoredDeclaration }, @@ -2796,7 +2558,11 @@ impl PropertyDeclaration { } }, % endfor - _ => PropertyDeclarationParseResult::UnknownProperty, + + // Hack to work around quirks of macro_rules parsing in match_ignore_ascii_case! + "_nonexistent" => PropertyDeclarationParseResult::UnknownProperty + + _ => PropertyDeclarationParseResult::UnknownProperty } } } @@ -2840,7 +2606,7 @@ impl ComputedValues { /// Usage example: /// let top_color = style.resolve_color(style.Border.border_top_color); #[inline] - pub fn resolve_color(&self, color: computed::CSSColor) -> RGBA { + pub fn resolve_color(&self, color: Color) -> RGBA { match color { Color::RGBA(rgba) => rgba, Color::CurrentColor => self.get_color().color, @@ -3026,7 +2792,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[DeclarationBlock] % for style_struct in STYLE_STRUCTS: % for property in style_struct.longhands: % if property.derived_from is None: - PropertyDeclaration::${property.camel_case}Declaration(ref ${'_' if not style_struct.inherited else ''}declared_value) => { + PropertyDeclaration::${property.camel_case}(ref ${'_' if not style_struct.inherited else ''}declared_value) => { % if style_struct.inherited: if seen.get_${property.ident}() { continue @@ -3072,7 +2838,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[DeclarationBlock] % endif } % else: - PropertyDeclaration::${property.camel_case}Declaration(_) => { + PropertyDeclaration::${property.camel_case}(_) => { // Do not allow stylesheets to set derived properties. } % endif @@ -3160,7 +2926,7 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock], // Declarations are stored in reverse source order, we want them in forward order here. for declaration in sub_list.declarations.iter().rev() { match *declaration { - PropertyDeclaration::FontSizeDeclaration(ref value) => { + PropertyDeclaration::FontSize(ref value) => { context.font_size = match *value { DeclaredValue::SpecifiedValue(specified_value) => computed::compute_Au_with_font_size( specified_value, context.inherited_font_size, context.root_font_size), @@ -3168,35 +2934,35 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock], DeclaredValue::Inherit => context.inherited_font_size, } } - PropertyDeclaration::ColorDeclaration(ref value) => { + PropertyDeclaration::Color(ref value) => { context.color = match *value { DeclaredValue::SpecifiedValue(ref specified_value) => specified_value.parsed, DeclaredValue::Initial => longhands::color::get_initial_value(), DeclaredValue::Inherit => inherited_style.get_color().color.clone(), }; } - PropertyDeclaration::DisplayDeclaration(ref value) => { + PropertyDeclaration::Display(ref value) => { context.display = get_specified!(get_box, display, value); } - PropertyDeclaration::PositionDeclaration(ref value) => { + PropertyDeclaration::Position(ref value) => { context.positioned = match get_specified!(get_box, position, value) { - longhands::position::T::absolute | longhands::position::T::fixed => true, + longhands::position::SpecifiedValue::absolute | + longhands::position::SpecifiedValue::fixed => true, _ => false, } } - PropertyDeclaration::FloatDeclaration(ref value) => { + PropertyDeclaration::Float(ref value) => { context.floated = get_specified!(get_box, float, value) - != longhands::float::T::none; + != longhands::float::SpecifiedValue::none; } - PropertyDeclaration::TextDecorationDeclaration(ref value) => { + PropertyDeclaration::TextDecoration(ref value) => { context.text_decoration = get_specified!(get_text, text_decoration, value); } % for side in ["top", "right", "bottom", "left"]: - PropertyDeclaration::Border${side.capitalize()}StyleDeclaration(ref value) => { + PropertyDeclaration::Border${side.capitalize()}Style(ref value) => { context.border_${side}_present = match get_specified!(get_border, border_${side}_style, value) { - longhands::border_top_style::T::none | - longhands::border_top_style::T::hidden => false, + BorderStyle::none | BorderStyle::hidden => false, _ => true, }; } @@ -3238,7 +3004,7 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock], % for style_struct in STYLE_STRUCTS: % for property in style_struct.longhands: % if property.derived_from is None: - PropertyDeclaration::${property.camel_case}Declaration(ref declared_value) => { + PropertyDeclaration::${property.camel_case}(ref declared_value) => { if seen.get_${property.ident}() { continue } @@ -3278,7 +3044,7 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock], % endif } % else: - PropertyDeclaration::${property.camel_case}Declaration(_) => { + PropertyDeclaration::${property.camel_case}(_) => { // Do not allow stylesheets to set derived properties. } % endif @@ -3410,7 +3176,7 @@ pub mod computed_values { pub use super::longhands::border_top_style::computed_value as border_style; pub use cssparser::RGBA; - pub use super::common_types::computed::{ + pub use values::computed::{ LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 0870d99711a..dba0294baec 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -22,14 +22,8 @@ use properties::{PropertyDeclaration, PropertyDeclarationBlock}; use selectors::{CaseSensitivity, Combinator, CompoundSelector, LocalName}; use selectors::{PseudoElement, SelectorList, SimpleSelector}; 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. pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C']; @@ -303,7 +297,7 @@ impl Stylist { Url::parse(format!("chrome:///{}", filename).as_slice()).unwrap(), None, None, - StylesheetOrigin::UserAgent); + Origin::UserAgent); stylist.add_stylesheet(ua_stylesheet); } stylist @@ -318,17 +312,17 @@ impl Stylist { for stylesheet in self.stylesheets.iter() { 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.before_map.user_agent, &mut self.after_map.user_agent, ), - StylesheetOrigin::Author => ( + Origin::Author => ( &mut self.element_map.author, &mut self.before_map.author, &mut self.after_map.author, ), - StylesheetOrigin::User => ( + Origin::User => ( &mut self.element_map.user, &mut self.before_map.user, &mut self.after_map.user, @@ -395,7 +389,7 @@ impl Stylist { Url::parse("chrome:///quirks-mode.css").unwrap(), None, None, - StylesheetOrigin::UserAgent)) + Origin::UserAgent)) } 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 /// we can sort them. -#[deriving(Clone)] +#[deriving(Clone, Show)] pub struct DeclarationBlock { pub declarations: Arc<Vec<PropertyDeclaration>>, source_order: uint, @@ -1171,21 +1165,24 @@ mod tests { use super::{DeclarationBlock, Rule, SelectorMap}; use selectors::LocalName; use string_cache::Atom; + use cssparser::Parser; + use parser::ParserContext; + use url::Url; /// Helper method to get some Rules from selector strings. /// Each sublist of the result contains the Rules for one StyleRule. fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> { use namespaces::NamespaceMap; - use selectors::{ParserContext, parse_selector_list}; - use selector_matching::StylesheetOrigin; - use cssparser::tokenize; + use selectors::parse_selector_list; + use stylesheets::Origin; - let namespaces = NamespaceMap::new(); css_selectors.iter().enumerate().map(|(i, selectors)| { 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| { Rule { selector: s.compound_selectors.clone(), diff --git a/components/style/selectors.rs b/components/style/selectors.rs index 92570409552..55b2fc687d0 100644 --- a/components/style/selectors.rs +++ b/components/style/selectors.rs @@ -2,49 +2,42 @@ * 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::{cmp, iter}; +use std::cmp; use std::ascii::{AsciiExt, OwnedAsciiExt}; use std::sync::Arc; +use std::str::CowString; -use cssparser::ast::*; -use cssparser::ast::ComponentValue::*; -use cssparser::{tokenize, parse_nth}; - -use selector_matching::StylesheetOrigin; +use cssparser::{Token, Parser, parse_nth}; use string_cache::{Atom, Namespace}; +use url::Url; +use parser::ParserContext; use namespaces::NamespaceMap; +use stylesheets::Origin; -/// Ambient data used by the parser. -#[deriving(Copy)] -pub struct ParserContext { - /// The origin of this stylesheet. - pub origin: StylesheetOrigin, -} -#[deriving(PartialEq, Clone)] +#[deriving(PartialEq, Clone, Show)] pub struct Selector { pub compound_selectors: Arc<CompoundSelector>, pub pseudo_element: Option<PseudoElement>, pub specificity: u32, } -#[deriving(Eq, PartialEq, Clone, Hash, Copy)] +#[deriving(Eq, PartialEq, Clone, Hash, Copy, Show)] pub enum PseudoElement { Before, After, -// FirstLine, -// FirstLetter, + // ... } -#[deriving(PartialEq, Clone)] +#[deriving(PartialEq, Clone, Show)] pub struct CompoundSelector { pub simple_selectors: Vec<SimpleSelector>, pub next: Option<(Box<CompoundSelector>, Combinator)>, // c.next is left of c } -#[deriving(PartialEq, Clone, Copy)] +#[deriving(PartialEq, Clone, Copy, Show)] pub enum Combinator { Child, // > Descendant, // space @@ -52,7 +45,7 @@ pub enum Combinator { LaterSibling, // ~ } -#[deriving(Eq, PartialEq, Clone, Hash)] +#[deriving(Eq, PartialEq, Clone, Hash, Show)] pub enum SimpleSelector { ID(Atom), Class(Atom), @@ -79,9 +72,7 @@ pub enum SimpleSelector { Checked, Indeterminate, FirstChild, LastChild, OnlyChild, -// Empty, Root, -// Lang(String), NthChild(i32, i32), NthLastChild(i32, i32), NthOfType(i32, i32), @@ -94,27 +85,27 @@ pub enum SimpleSelector { } -#[deriving(Eq, PartialEq, Clone, Hash, Copy)] +#[deriving(Eq, PartialEq, Clone, Hash, Copy, Show)] pub enum CaseSensitivity { CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive. CaseInsensitive, } -#[deriving(Eq, PartialEq, Clone, Hash)] +#[deriving(Eq, PartialEq, Clone, Hash, Show)] pub struct LocalName { pub name: Atom, pub lower_name: Atom, } -#[deriving(Eq, PartialEq, Clone, Hash)] +#[deriving(Eq, PartialEq, Clone, Hash, Show)] pub struct AttrSelector { pub name: Atom, pub lower_name: Atom, pub namespace: NamespaceConstraint, } -#[deriving(Eq, PartialEq, Clone, Hash)] +#[deriving(Eq, PartialEq, Clone, Hash, Show)] pub enum NamespaceConstraint { Any, Specific(Namespace), @@ -132,9 +123,6 @@ pub fn get_selector_list_selectors<'a>(selector_list: &'a SelectorList) -> &'a [ } -type Iter<I> = iter::Peekable<ComponentValue, I>; - - fn compute_specificity(mut selector: &CompoundSelector, pseudo_element: &Option<PseudoElement>) -> u32 { struct Specificity { @@ -183,7 +171,6 @@ fn compute_specificity(mut selector: &CompoundSelector, &SimpleSelector::OnlyChild | &SimpleSelector::Root | &SimpleSelector::Checked | &SimpleSelector::Indeterminate | -// &SimpleSelector::Empty | &SimpleSelector::Lang(*) | &SimpleSelector::NthChild(..) | &SimpleSelector::NthLastChild(..) | &SimpleSelector::NthOfType(..) | @@ -207,14 +194,87 @@ fn compute_specificity(mut selector: &CompoundSelector, +pub fn parse_author_origin_selector_list_from_str(input: &str) + -> Result<SelectorList,()> { + let context = ParserContext { + stylesheet_origin: Origin::Author, + namespaces: NamespaceMap::new(), + base_url: &Url::parse("about:blank").unwrap(), + }; + parse_selector_list(&context, &mut Parser::new(input)) + .map(|s| SelectorList { selectors: s }) +} + +/// Parse a comma-separated list of Selectors. +/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping +/// +/// Return the Selectors or None if there is an invalid selector. +pub fn parse_selector_list(context: &ParserContext, input: &mut Parser) + -> Result<Vec<Selector>,()> { + input.parse_comma_separated(|input| parse_selector(context, input)) +} + + +/// Build up a Selector. +/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; +/// +/// `Err` means invalid selector. +fn parse_selector(context: &ParserContext, input: &mut Parser) -> Result<Selector,()> { + let (first, mut pseudo_element) = try!(parse_simple_selectors(context, input)); + let mut compound = CompoundSelector{ simple_selectors: first, next: None }; + + 'outer_loop: while pseudo_element.is_none() { + let combinator; + let mut any_whitespace = false; + loop { + let position = input.position(); + match input.next_including_whitespace() { + Err(()) => break 'outer_loop, + Ok(Token::WhiteSpace(_)) => any_whitespace = true, + Ok(Token::Delim('>')) => { + combinator = Combinator::Child; + break + } + Ok(Token::Delim('+')) => { + combinator = Combinator::NextSibling; + break + } + Ok(Token::Delim('~')) => { + combinator = Combinator::LaterSibling; + break + } + Ok(_) => { + input.reset(position); + if any_whitespace { + combinator = Combinator::Descendant; + break + } else { + break 'outer_loop + } + } + } + } + let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, input)); + compound = CompoundSelector { + simple_selectors: simple_selectors, + next: Some((box compound, combinator)) + }; + pseudo_element = pseudo; + } + Ok(Selector { + specificity: compute_specificity(&compound, &pseudo_element), + compound_selectors: Arc::new(compound), + pseudo_element: pseudo_element, + }) +} + + /// * `Err(())`: Invalid selector, abort -/// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed. +/// * `Ok(None)`: Not a type selector, could be something else. `input` was not consumed. /// * `Ok(Some(vec))`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`) -fn parse_type_selector<I: Iterator<ComponentValue>>( - iter: &mut Iter<I>, namespaces: &NamespaceMap) +fn parse_type_selector(context: &ParserContext, input: &mut Parser) -> Result<Option<Vec<SimpleSelector>>, ()> { - skip_whitespace(iter); - match try!(parse_qualified_name(iter, /* in_attr_selector = */ false, namespaces)) { + match try!(parse_qualified_name(context, input, /* in_attr_selector = */ false)) { None => Ok(None), Some((namespace, local_name)) => { let mut simple_selectors = vec!(); @@ -228,7 +288,7 @@ fn parse_type_selector<I: Iterator<ComponentValue>>( Some(name) => { simple_selectors.push(SimpleSelector::LocalName(LocalName { name: Atom::from_slice(name.as_slice()), - lower_name: Atom::from_slice(name.into_ascii_lower().as_slice()) + lower_name: Atom::from_slice(name.into_owned().into_ascii_lower().as_slice()) })) } None => (), @@ -239,6 +299,7 @@ fn parse_type_selector<I: Iterator<ComponentValue>>( } +#[deriving(Show)] enum SimpleSelectorParseResult { SimpleSelector(SimpleSelector), PseudoElement(PseudoElement), @@ -246,219 +307,145 @@ enum SimpleSelectorParseResult { /// * `Err(())`: Invalid selector, abort -/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. +/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed. /// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector -fn parse_qualified_name<I: Iterator<ComponentValue>>( - iter: &mut Iter<I>, in_attr_selector: bool, namespaces: &NamespaceMap) - -> Result<Option<(NamespaceConstraint, Option<String>)>, ()> { +fn parse_qualified_name<'i, 't> + (context: &ParserContext, input: &mut Parser<'i, 't>, + in_attr_selector: bool) + -> Result<Option<(NamespaceConstraint, Option<CowString<'i>>)>, ()> { let default_namespace = |local_name| { - let namespace = match namespaces.default { + let namespace = match context.namespaces.default { Some(ref ns) => NamespaceConstraint::Specific(ns.clone()), None => NamespaceConstraint::Any, }; Ok(Some((namespace, local_name))) }; - let explicit_namespace = |iter: &mut Iter<I>, namespace| { - assert!(iter.next() == Some(Delim('|')), - "Implementation error, this should not happen."); - match iter.peek() { - Some(&Delim('*')) if !in_attr_selector => { - iter.next(); + let explicit_namespace = |input: &mut Parser<'i, 't>, namespace| { + match input.next_including_whitespace() { + Ok(Token::Delim('*')) if !in_attr_selector => { Ok(Some((namespace, None))) }, - Some(&Ident(_)) => { - let local_name = get_next_ident(iter); + Ok(Token::Ident(local_name)) => { Ok(Some((namespace, Some(local_name)))) }, _ => Err(()), } }; - match iter.peek() { - Some(&Ident(_)) => { - let value = get_next_ident(iter); - match iter.peek() { - Some(&Delim('|')) => { - let namespace = match namespaces.prefix_map.get(&value) { - None => return Err(()), // Undeclared namespace prefix - Some(ref ns) => (*ns).clone(), - }; - explicit_namespace(iter, NamespaceConstraint::Specific(namespace)) + let position = input.position(); + match input.next_including_whitespace() { + Ok(Token::Ident(value)) => { + let position = input.position(); + match input.next_including_whitespace() { + Ok(Token::Delim('|')) => { + let result = context.namespaces.prefix_map.get(value.as_slice()); + let namespace = try!(result.ok_or(())); + explicit_namespace(input, NamespaceConstraint::Specific(namespace.clone())) }, - _ if in_attr_selector => Ok(Some( - (NamespaceConstraint::Specific(ns!("")), Some(value)))), - _ => default_namespace(Some(value)), + _ => { + input.reset(position); + if in_attr_selector { + Ok(Some((NamespaceConstraint::Specific(ns!("")), Some(value)))) + } else { + default_namespace(Some(value)) + } + } } }, - Some(&Delim('*')) => { - iter.next(); // Consume '*' - match iter.peek() { - Some(&Delim('|')) => explicit_namespace(iter, NamespaceConstraint::Any), + Ok(Token::Delim('*')) => { + let position = input.position(); + match input.next_including_whitespace() { + Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Any), _ => { - if !in_attr_selector { default_namespace(None) } - else { Err(()) } + input.reset(position); + if in_attr_selector { + Err(()) + } else { + default_namespace(None) + } }, } }, - Some(&Delim('|')) => explicit_namespace(iter, NamespaceConstraint::Specific(ns!(""))), - _ => Ok(None), + Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Specific(ns!(""))), + _ => { + input.reset(position); + Ok(None) + } } } -fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &NamespaceMap) +fn parse_attribute_selector(context: &ParserContext, input: &mut Parser) -> Result<SimpleSelector, ()> { - let iter = &mut content.into_iter().peekable(); - let attr = match try!(parse_qualified_name(iter, /* in_attr_selector = */ true, namespaces)) { + let attr = match try!(parse_qualified_name(context, input, /* in_attr_selector = */ true)) { None => return Err(()), - Some((_, None)) => panic!("Implementation error, this should not happen."), + Some((_, None)) => unreachable!(), Some((namespace, Some(local_name))) => AttrSelector { namespace: namespace, lower_name: Atom::from_slice(local_name.as_slice().to_ascii_lower().as_slice()), name: Atom::from_slice(local_name.as_slice()), }, }; - skip_whitespace(iter); + + fn parse_value(input: &mut Parser) -> Result<String, ()> { + Ok((try!(input.expect_ident_or_string())).into_owned()) + } // TODO: deal with empty value or value containing whitespace (see spec) - let result = match iter.next() { + match input.next() { // [foo] - None => SimpleSelector::AttrExists(attr), + Err(()) => Ok(SimpleSelector::AttrExists(attr)), // [foo=bar] - Some(Delim('=')) => - SimpleSelector::AttrEqual(attr, try!(parse_attribute_value(iter)), - try!(parse_attribute_flags(iter))), - + Ok(Token::Delim('=')) => { + Ok(SimpleSelector::AttrEqual(attr, try!(parse_value(input)), + try!(parse_attribute_flags(input)))) + } // [foo~=bar] - Some(IncludeMatch) => - SimpleSelector::AttrIncludes(attr, try!(parse_attribute_value(iter))), - + Ok(Token::IncludeMatch) => { + Ok(SimpleSelector::AttrIncludes(attr, try!(parse_value(input)))) + } // [foo|=bar] - Some(DashMatch) => { - let value = try!(parse_attribute_value(iter)); + Ok(Token::DashMatch) => { + let value = try!(parse_value(input)); let dashing_value = format!("{}-", value); - SimpleSelector::AttrDashMatch(attr, value, dashing_value) - }, - + Ok(SimpleSelector::AttrDashMatch(attr, value, dashing_value)) + } // [foo^=bar] - Some(PrefixMatch) => - SimpleSelector::AttrPrefixMatch(attr, try!(parse_attribute_value(iter))), - + Ok(Token::PrefixMatch) => { + Ok(SimpleSelector::AttrPrefixMatch(attr, try!(parse_value(input)))) + } // [foo*=bar] - Some(SubstringMatch) => - SimpleSelector::AttrSubstringMatch(attr, try!(parse_attribute_value(iter))), - + Ok(Token::SubstringMatch) => { + Ok(SimpleSelector::AttrSubstringMatch(attr, try!(parse_value(input)))) + } // [foo$=bar] - Some(SuffixMatch) => - SimpleSelector::AttrSuffixMatch(attr, try!(parse_attribute_value(iter))), - - _ => return Err(()) - }; - skip_whitespace(iter); - if iter.next().is_none() { Ok(result) } else { Err(()) } -} - - -fn parse_attribute_value<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> Result<String, ()> { - skip_whitespace(iter); - match iter.next() { - Some(Ident(value)) | Some(QuotedString(value)) => Ok(value), - _ => Err(()) - } -} - - -fn parse_attribute_flags<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) - -> Result<CaseSensitivity, ()> { - skip_whitespace(iter); - match iter.next() { - None => Ok(CaseSensitivity::CaseSensitive), - Some(Ident(ref value)) if value.as_slice().eq_ignore_ascii_case("i") - => Ok(CaseSensitivity::CaseInsensitive), + Ok(Token::SuffixMatch) => { + Ok(SimpleSelector::AttrSuffixMatch(attr, try!(parse_value(input)))) + } _ => Err(()) } } -pub fn parse_selector_list_from_str(context: &ParserContext, input: &str) - -> Result<SelectorList,()> { - let namespaces = NamespaceMap::new(); - let iter = tokenize(input).map(|(token, _)| token); - parse_selector_list(context, iter, &namespaces).map(|s| SelectorList { selectors: s }) -} -/// Parse a comma-separated list of Selectors. -/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping -/// -/// Return the Selectors or None if there is an invalid selector. -pub fn parse_selector_list<I>(context: &ParserContext, iter: I, namespaces: &NamespaceMap) - -> Result<Vec<Selector>,()> - where I: Iterator<ComponentValue> { - let iter = &mut iter.peekable(); - let mut results = vec![try!(parse_selector(context, iter, namespaces))]; - - loop { - skip_whitespace(iter); - match iter.peek() { - None => break, // EOF - Some(&Comma) => { - iter.next(); - } - _ => return Err(()), +fn parse_attribute_flags(input: &mut Parser) -> Result<CaseSensitivity, ()> { + match input.next() { + Err(()) => Ok(CaseSensitivity::CaseSensitive), + Ok(Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => { + Ok(CaseSensitivity::CaseInsensitive) } - results.push(try!(parse_selector(context, iter, namespaces))); + _ => Err(()) } - Ok(results) } -/// Build up a Selector. -/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; -/// -/// `Err` means invalid selector. -fn parse_selector<I>(context: &ParserContext, iter: &mut Iter<I>, namespaces: &NamespaceMap) - -> Result<Selector,()> - where I: Iterator<ComponentValue> { - let (first, mut pseudo_element) = try!(parse_simple_selectors(context, iter, namespaces)); - let mut compound = CompoundSelector{ simple_selectors: first, next: None }; - while pseudo_element.is_none() { - let any_whitespace = skip_whitespace(iter); - let combinator = match iter.peek() { - None => break, // EOF - Some(&Comma) => break, - Some(&Delim('>')) => { iter.next(); Combinator::Child }, - Some(&Delim('+')) => { iter.next(); Combinator::NextSibling }, - Some(&Delim('~')) => { iter.next(); Combinator::LaterSibling }, - Some(_) => { - if any_whitespace { Combinator::Descendant } - else { return Err(()) } - } - }; - let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, iter, namespaces)); - compound = CompoundSelector { - simple_selectors: simple_selectors, - next: Some((box compound, combinator)) - }; - pseudo_element = pseudo; - } - Ok(Selector { - specificity: compute_specificity(&compound, &pseudo_element), - compound_selectors: Arc::new(compound), - pseudo_element: pseudo_element, - }) -} /// Level 3: Parse **one** simple_selector -fn parse_negation(context: &ParserContext, - arguments: Vec<ComponentValue>, - namespaces: &NamespaceMap) - -> Result<SimpleSelector,()> { - let iter = &mut arguments.into_iter().peekable(); - match try!(parse_type_selector(iter, namespaces)) { +fn parse_negation(context: &ParserContext, input: &mut Parser) -> Result<SimpleSelector,()> { + match try!(parse_type_selector(context, input)) { Some(type_selector) => Ok(SimpleSelector::Negation(type_selector)), None => { match try!(parse_one_simple_selector(context, - iter, - namespaces, + input, /* inside_negation = */ true)) { Some(SimpleSelectorParseResult::SimpleSelector(simple_selector)) => { Ok(SimpleSelector::Negation(vec![simple_selector])) @@ -474,13 +461,18 @@ fn parse_negation(context: &ParserContext, /// | [ HASH | class | attrib | pseudo | negation ]+ /// /// `Err(())` means invalid selector -fn parse_simple_selectors<I>(context: &ParserContext, - iter: &mut Iter<I>, - namespaces: &NamespaceMap) - -> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()> - where I: Iterator<ComponentValue> { +fn parse_simple_selectors(context: &ParserContext, input: &mut Parser) + -> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()> { + // Consume any leading whitespace. + loop { + let position = input.position(); + if !matches!(input.next_including_whitespace(), Ok(Token::WhiteSpace(_))) { + input.reset(position); + break + } + } let mut empty = true; - let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) { + let mut simple_selectors = match try!(parse_type_selector(context, input)) { None => vec![], Some(s) => { empty = false; s } }; @@ -488,12 +480,18 @@ fn parse_simple_selectors<I>(context: &ParserContext, let mut pseudo_element = None; loop { match try!(parse_one_simple_selector(context, - iter, - namespaces, + input, /* inside_negation = */ false)) { None => break, - Some(SimpleSelectorParseResult::SimpleSelector(s)) => { simple_selectors.push(s); empty = false }, - Some(SimpleSelectorParseResult::PseudoElement(p)) => { pseudo_element = Some(p); empty = false; break }, + Some(SimpleSelectorParseResult::SimpleSelector(s)) => { + simple_selectors.push(s); + empty = false + } + Some(SimpleSelectorParseResult::PseudoElement(p)) => { + pseudo_element = Some(p); + empty = false; + break + } } } if empty { @@ -505,100 +503,111 @@ fn parse_simple_selectors<I>(context: &ParserContext, } fn parse_functional_pseudo_class(context: &ParserContext, - name: String, - arguments: Vec<ComponentValue>, - namespaces: &NamespaceMap, + input: &mut Parser, + name: &str, inside_negation: bool) -> Result<SimpleSelector,()> { - match name.as_slice().to_ascii_lower().as_slice() { -// "lang" => parse_lang(arguments), - "nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| SimpleSelector::NthChild(a, b)), - "nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| SimpleSelector::NthLastChild(a, b)), - "nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| SimpleSelector::NthOfType(a, b)), - "nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| SimpleSelector::NthLastOfType(a, b)), + match_ignore_ascii_case! { name: + "nth-child" => parse_nth_pseudo_class(input, SimpleSelector::NthChild), + "nth-of-type" => parse_nth_pseudo_class(input, SimpleSelector::NthOfType), + "nth-last-child" => parse_nth_pseudo_class(input, SimpleSelector::NthLastChild), + "nth-last-of-type" => parse_nth_pseudo_class(input, SimpleSelector::NthLastOfType), "not" => { if inside_negation { Err(()) } else { - parse_negation(context, arguments, namespaces) + parse_negation(context, input) } } _ => Err(()) } } + +fn parse_nth_pseudo_class(input: &mut Parser, selector: |i32, i32| -> SimpleSelector) + -> Result<SimpleSelector, ()> { + let (a, b) = try!(parse_nth(input)); + Ok(selector(a, b)) +} + + /// Parse a simple selector other than a type selector. /// /// * `Err(())`: Invalid selector, abort -/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. +/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed. /// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element -fn parse_one_simple_selector<I>(context: &ParserContext, - iter: &mut Iter<I>, - namespaces: &NamespaceMap, - inside_negation: bool) - -> Result<Option<SimpleSelectorParseResult>,()> - where I: Iterator<ComponentValue> { - match iter.peek() { - Some(&IDHash(_)) => match iter.next() { - Some(IDHash(id)) => Ok(Some(SimpleSelectorParseResult::SimpleSelector( - SimpleSelector::ID(Atom::from_slice(id.as_slice()))))), - _ => panic!("Implementation error, this should not happen."), - }, - Some(&Delim('.')) => { - iter.next(); - match iter.next() { - Some(Ident(class)) => Ok(Some(SimpleSelectorParseResult::SimpleSelector( - SimpleSelector::Class(Atom::from_slice(class.as_slice()))))), +fn parse_one_simple_selector(context: &ParserContext, + input: &mut Parser, + inside_negation: bool) + -> Result<Option<SimpleSelectorParseResult>,()> { + let start_position = input.position(); + match input.next_including_whitespace() { + Ok(Token::IDHash(id)) => { + let id = SimpleSelector::ID(Atom::from_slice(id.as_slice())); + Ok(Some(SimpleSelectorParseResult::SimpleSelector(id))) + } + Ok(Token::Delim('.')) => { + match input.next_including_whitespace() { + Ok(Token::Ident(class)) => { + let class = SimpleSelector::Class(Atom::from_slice(class.as_slice())); + Ok(Some(SimpleSelectorParseResult::SimpleSelector(class))) + } _ => Err(()), } } - Some(&SquareBracketBlock(_)) => match iter.next() { - Some(SquareBracketBlock(content)) - => Ok(Some(SimpleSelectorParseResult::SimpleSelector(try!(parse_attribute_selector(content, namespaces))))), - _ => panic!("Implementation error, this should not happen."), - }, - Some(&Colon) => { - iter.next(); - match iter.next() { - Some(Ident(name)) => match parse_simple_pseudo_class(context, name.as_slice()) { - Err(()) => { - match name.as_slice().to_ascii_lower().as_slice() { - // Supported CSS 2.1 pseudo-elements only. - // ** Do not add to this list! ** - "before" => Ok(Some(SimpleSelectorParseResult::PseudoElement(PseudoElement::Before))), - "after" => Ok(Some(SimpleSelectorParseResult::PseudoElement(PseudoElement::After))), -// "first-line" => SimpleSelectorParseResult::PseudoElement(FirstLine), -// "first-letter" => SimpleSelectorParseResult::PseudoElement(FirstLetter), - _ => Err(()) - } - }, - Ok(result) => Ok(Some(SimpleSelectorParseResult::SimpleSelector(result))), - }, - Some(Function(name, arguments)) - => { - Ok(Some(SimpleSelectorParseResult::SimpleSelector(try!(parse_functional_pseudo_class( - context, - name, - arguments, - namespaces, - inside_negation))))) + Ok(Token::SquareBracketBlock) => { + let attr = try!(input.parse_nested_block(|input| { + parse_attribute_selector(context, input) + })); + Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr))) + } + Ok(Token::Colon) => { + match input.next_including_whitespace() { + Ok(Token::Ident(name)) => { + match parse_simple_pseudo_class(context, name.as_slice()) { + Err(()) => { + let pseudo_element = match_ignore_ascii_case! { name: + // Supported CSS 2.1 pseudo-elements only. + // ** Do not add to this list! ** + "before" => PseudoElement::Before, + "after" => PseudoElement::After, + "first-line" => return Err(()), + "first-letter" => return Err(()) + _ => return Err(()) + }; + Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element))) + }, + Ok(result) => Ok(Some(SimpleSelectorParseResult::SimpleSelector(result))), + } + } + Ok(Token::Function(name)) => { + let name = name.as_slice(); + let pseudo = try!(input.parse_nested_block(|input| { + parse_functional_pseudo_class(context, input, name, inside_negation) + })); + Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo))) } - Some(Colon) => { - match iter.next() { - Some(Ident(name)) - => Ok(Some(SimpleSelectorParseResult::PseudoElement(try!(parse_pseudo_element(name))))), - _ => Err(()), + Ok(Token::Colon) => { + match input.next() { + Ok(Token::Ident(name)) => { + let pseudo = try!(parse_pseudo_element(name.as_slice())); + Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo))) + } + _ => Err(()) } } - _ => Err(()), + _ => Err(()) } } - _ => Ok(None), + _ => { + input.reset(start_position); + Ok(None) + } } } fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<SimpleSelector,()> { - match name.to_ascii_lower().as_slice() { + match_ignore_ascii_case! { name: "any-link" => Ok(SimpleSelector::AnyLink), "link" => Ok(SimpleSelector::Link), "visited" => Ok(SimpleSelector::Visited), @@ -614,76 +623,48 @@ fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<Simp "first-of-type" => Ok(SimpleSelector::FirstOfType), "last-of-type" => Ok(SimpleSelector::LastOfType), "only-of-type" => Ok(SimpleSelector::OnlyOfType), - "-servo-nonzero-border" if context.origin == StylesheetOrigin::UserAgent => Ok(SimpleSelector::ServoNonzeroBorder), -// "empty" => Ok(Empty), + "-servo-nonzero-border" => { + if context.in_user_agent_stylesheet() { + Ok(SimpleSelector::ServoNonzeroBorder) + } else { + Err(()) + } + } _ => Err(()) } } -fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> { - match name.as_slice().to_ascii_lower().as_slice() { - // All supported pseudo-elements +fn parse_pseudo_element(name: &str) -> Result<PseudoElement, ()> { + match_ignore_ascii_case! { name: "before" => Ok(PseudoElement::Before), - "after" => Ok(PseudoElement::After), -// "first-line" => Some(FirstLine), -// "first-letter" => Some(FirstLetter), + "after" => Ok(PseudoElement::After) _ => Err(()) } } -//fn parse_lang(arguments: vec!(ComponentValue)) -> Result<SimpleSelector, ()> { -// let mut iter = arguments.move_skip_whitespace(); -// match iter.next() { -// Some(Ident(value)) => { -// if "" == value || iter.next().is_some() { None } -// else { Ok(Lang(value)) } -// }, -// _ => Err(()), -// } -//} - - - -/// Assuming the next token is an ident, consume it and return its value -#[inline] -fn get_next_ident<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> String { - match iter.next() { - Some(Ident(value)) => value, - _ => panic!("Implementation error, this should not happen."), - } -} - - -#[inline] -fn skip_whitespace<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> bool { - let mut any_whitespace = false; - loop { - if iter.peek() != Some(&WhiteSpace) { return any_whitespace } - any_whitespace = true; - iter.next(); - } -} - - #[cfg(test)] mod tests { use std::sync::Arc; - use cssparser; + use cssparser::Parser; use namespaces::NamespaceMap; - use selector_matching::StylesheetOrigin; + use stylesheets::Origin; use string_cache::Atom; + use parser::ParserContext; + use url::Url; use super::*; fn parse(input: &str) -> Result<Vec<Selector>, ()> { - parse_ns(input, &NamespaceMap::new()) + parse_ns(input, NamespaceMap::new()) } - fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> { + fn parse_ns(input: &str, namespaces: NamespaceMap) -> Result<Vec<Selector>, ()> { let context = ParserContext { - origin: StylesheetOrigin::Author, + stylesheet_origin: Origin::Author, + namespaces: namespaces, + base_url: &Url::parse("about:blank").unwrap(), }; - parse_selector_list(&context, cssparser::tokenize(input).map(|(v, _)| v), namespaces) + parse_selector_list(&context, &mut Parser::new(input)) } fn specificity(a: u32, b: u32, c: u32) -> u32 { @@ -692,8 +673,8 @@ mod tests { #[test] fn test_parsing() { - assert!(parse("") == Err(())) - assert!(parse("EeÉ") == Ok(vec!(Selector { + assert_eq!(parse(""), Err(())) + assert_eq!(parse("EeÉ"), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(SimpleSelector::LocalName(LocalName { name: Atom::from_slice("EeÉ"), @@ -703,7 +684,7 @@ mod tests { pseudo_element: None, specificity: specificity(0, 0, 1), }))) - assert!(parse(".foo") == Ok(vec!(Selector { + assert_eq!(parse(".foo"), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(SimpleSelector::Class(Atom::from_slice("foo"))), next: None, @@ -711,7 +692,7 @@ mod tests { pseudo_element: None, specificity: specificity(0, 1, 0), }))) - assert!(parse("#bar") == Ok(vec!(Selector { + assert_eq!(parse("#bar"), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(SimpleSelector::ID(Atom::from_slice("bar"))), next: None, @@ -719,7 +700,7 @@ mod tests { pseudo_element: None, specificity: specificity(1, 0, 0), }))) - assert!(parse("e.foo#bar") == Ok(vec!(Selector { + assert_eq!(parse("e.foo#bar"), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(SimpleSelector::LocalName(LocalName { name: Atom::from_slice("e"), @@ -731,7 +712,7 @@ mod tests { pseudo_element: None, specificity: specificity(1, 1, 1), }))) - assert!(parse("e.foo #bar") == Ok(vec!(Selector { + assert_eq!(parse("e.foo #bar"), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(SimpleSelector::ID(Atom::from_slice("bar"))), next: Some((box CompoundSelector { @@ -748,7 +729,7 @@ mod tests { // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 let mut namespaces = NamespaceMap::new(); - assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector { + assert_eq!(parse_ns("[Foo]", namespaces.clone()), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(SimpleSelector::AttrExists(AttrSelector { name: Atom::from_slice("Foo"), @@ -763,7 +744,7 @@ mod tests { // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 namespaces.default = Some(ns!(MathML)); - assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector { + assert_eq!(parse_ns("[Foo]", namespaces.clone()), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(SimpleSelector::AttrExists(AttrSelector { name: Atom::from_slice("Foo"), @@ -776,7 +757,7 @@ mod tests { specificity: specificity(0, 1, 0), }))) // Default namespace does apply to type selectors - assert!(parse_ns("e", &namespaces) == Ok(vec!(Selector { + assert_eq!(parse_ns("e", namespaces), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!( SimpleSelector::Namespace(ns!(MathML)), @@ -790,7 +771,7 @@ mod tests { specificity: specificity(0, 0, 1), }))) // https://github.com/mozilla/servo/issues/1723 - assert!(parse("::before") == Ok(vec!(Selector { + assert_eq!(parse("::before"), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(), next: None, @@ -798,7 +779,7 @@ mod tests { pseudo_element: Some(PseudoElement::Before), specificity: specificity(0, 0, 1), }))) - assert!(parse("div :after") == Ok(vec!(Selector { + assert_eq!(parse("div :after"), Ok(vec!(Selector { compound_selectors: Arc::new(CompoundSelector { simple_selectors: vec!(), next: Some((box CompoundSelector { @@ -811,5 +792,20 @@ mod tests { pseudo_element: Some(PseudoElement::After), specificity: specificity(0, 0, 2), }))) + assert_eq!(parse("#d1 > .ok"), Ok(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), + }])) } } diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 71eb15cc1e2..cc3e3f42543 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -8,55 +8,75 @@ use url::Url; use encoding::EncodingRef; -use cssparser::{decode_stylesheet_bytes, tokenize, parse_stylesheet_rules, ToCss}; -use cssparser::ast::*; -use selectors::{mod, ParserContext}; -use properties; -use errors::{ErrorLoggerIterator, log_css_error}; +use cssparser::{Parser, decode_stylesheet_bytes, + QualifiedRuleParser, AtRuleParser, RuleListParser, AtRuleType}; +use string_cache::Namespace; +use selectors::{Selector, parse_selector_list}; +use parser::ParserContext; +use properties::{PropertyDeclarationBlock, parse_property_declaration_list}; use namespaces::{NamespaceMap, parse_namespace_rule}; -use media_queries::{mod, Device, MediaRule}; -use font_face::{FontFaceRule, Source, parse_font_face_rule, iter_font_face_rules_inner}; -use selector_matching::StylesheetOrigin; +use media_queries::{mod, Device, MediaQueryList, parse_media_query_list}; +use font_face::{FontFaceRule, Source, parse_font_face_block, iter_font_face_rules_inner}; +#[deriving(Clone, PartialEq, Eq, Copy, Show)] +pub enum Origin { + UserAgent, + Author, + User, +} + + +#[deriving(Show, PartialEq)] pub struct Stylesheet { /// List of rules in the order they were found (important for /// cascading order) rules: Vec<CSSRule>, - pub origin: StylesheetOrigin, + pub origin: Origin, } +#[deriving(Show, PartialEq)] pub enum CSSRule { + Charset(String), + Namespace(Option<String>, Namespace), Style(StyleRule), Media(MediaRule), FontFace(FontFaceRule), } +#[deriving(Show, PartialEq)] +pub struct MediaRule { + pub media_queries: MediaQueryList, + pub rules: Vec<CSSRule>, +} + +#[deriving(Show, PartialEq)] pub struct StyleRule { - pub selectors: Vec<selectors::Selector>, - pub declarations: properties::PropertyDeclarationBlock, + pub selectors: Vec<Selector>, + pub declarations: PropertyDeclarationBlock, } impl Stylesheet { pub fn from_bytes_iter<I: Iterator<Vec<u8>>>( 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!(); // TODO: incremental decoding and tokenization/parsing for chunk in input { 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], base_url: Url, protocol_encoding_label: Option<&str>, environment_encoding: Option<EncodingRef>, - origin: StylesheetOrigin) + origin: Origin) -> Stylesheet { // TODO: bytes.as_slice could be bytes.container_as_bytes() let (string, _) = decode_stylesheet_bytes( @@ -64,121 +84,139 @@ impl Stylesheet { Stylesheet::from_str(string.as_slice(), base_url, origin) } - pub fn from_str(css: &str, base_url: Url, origin: StylesheetOrigin) -> Stylesheet { - static STATE_CHARSET: uint = 1; - static STATE_IMPORTS: uint = 2; - static STATE_NAMESPACES: uint = 3; - static STATE_BODY: uint = 4; - - let parser_context = ParserContext { - origin: origin, + pub fn from_str<'i>(css: &'i str, base_url: Url, origin: Origin) -> Stylesheet { + let mut context = ParserContext { + stylesheet_origin: origin, + base_url: &base_url, + namespaces: NamespaceMap::new() }; + let rule_parser = MainRuleParser { + context: &mut context, + state: State::Start, + }; + let rules = RuleListParser::new_for_stylesheet(&mut Parser::new(css), rule_parser) + .filter_map(|result| result.ok()) + .collect(); + Stylesheet { + origin: origin, + rules: rules, + } + } +} - let mut state: uint = STATE_CHARSET; - let mut rules = vec!(); - let mut namespaces = NamespaceMap::new(); +fn parse_nested_rules(context: &mut ParserContext, input: &mut Parser) -> Vec<CSSRule> { + let parser = MainRuleParser { + context: context, + state: State::Body, + }; + RuleListParser::new_for_nested_rule(input, parser) + .filter_map(|result| result.ok()) + .collect() +} - for rule in ErrorLoggerIterator(parse_stylesheet_rules(tokenize(css))) { - 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) - }, - } - }, + +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" + } } - state = next_state; + _ => {} } - Stylesheet { - rules: rules, - origin: origin, + + 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(()) } } -} -// lower_name is passed explicitly to avoid computing it twice. -pub fn parse_nested_at_rule(context: &ParserContext, - lower_name: &str, - rule: AtRule, - parent_rules: &mut Vec<CSSRule>, - namespaces: &NamespaceMap, - base_url: &Url) { - match lower_name { - "media" => { - media_queries::parse_media_rule(context, rule, parent_rules, namespaces, base_url) + 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, - parent_rules: &mut Vec<CSSRule>, - namespaces: &NamespaceMap, - base_url: &Url) { - let QualifiedRule { - location, - prelude, - block - } = rule; - // FIXME: avoid doing this for valid selectors - 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()), + +impl<'a, 'b> QualifiedRuleParser<Vec<Selector>, CSSRule> for MainRuleParser<'a, 'b> { + fn parse_prelude(&mut self, input: &mut Parser) -> Result<Vec<Selector>, ()> { + self.state = State::Body; + parse_selector_list(self.context, input) + } + + fn parse_block(&mut self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> { + Ok(CSSRule::Style(StyleRule { + selectors: prelude, + declarations: parse_property_declaration_list(self.context, input) + })) } } + pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device, callback: |&StyleRule|) { 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) { 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() { match *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|) { 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![]), + }, + }), + ], + }); +} diff --git a/components/style/properties/common_types.rs b/components/style/values.rs index 1ca01d2d504..fb8806c6c38 100644 --- a/components/style/properties/common_types.rs +++ b/components/style/values.rs @@ -5,10 +5,6 @@ #![allow(non_camel_case_types)] #![macro_escape] -use url::{Url, UrlParser}; - -pub use servo_util::geometry::Au; - macro_rules! define_css_keyword_enum { ($name: ident: $( $css: expr => $variant: ident ),+,) => { @@ -22,14 +18,9 @@ macro_rules! define_css_keyword_enum { } impl $name { - pub fn parse(component_value: &::cssparser::ast::ComponentValue) -> Result<$name, ()> { - match component_value { - &::cssparser::ast::ComponentValue::Ident(ref value) => { - match_ignore_ascii_case! { value: - $( $css => Ok($name::$variant) ),+ - _ => Err(()) - } - } + pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> { + match_ignore_ascii_case! { try!(input.expect_ident()): + $( $css => Ok($name::$variant) ),+ _ => Err(()) } } @@ -63,12 +54,12 @@ pub mod specified { use std::fmt; use std::fmt::{Formatter, Show}; use url::Url; - use cssparser::{mod, ToCss, CssStringWriter}; - use cssparser::ast::*; - use cssparser::ast::ComponentValue::*; + use cssparser::{mod, Token, Parser, ToCss, CssStringWriter}; + use parser::ParserContext; use text_writer::{mod, TextWriter}; - use parsing_utils::{mod, BufferedIter, ParserIter}; - use super::{Au, CSSFloat}; + use servo_util::geometry::Au; + use super::CSSFloat; + use super::computed; #[deriving(Clone, PartialEq)] pub struct CSSColor { @@ -76,17 +67,16 @@ pub mod specified { pub authored: Option<String>, } impl CSSColor { - pub fn parse(component_value: &ComponentValue) -> Result<CSSColor, ()> { - let parsed = cssparser::Color::parse(component_value); - parsed.map(|parsed| { - let authored = match component_value { - &Ident(ref s) => Some(s.clone()), - _ => None, - }; - CSSColor { - parsed: parsed, - authored: authored, - } + pub fn parse(input: &mut Parser) -> Result<CSSColor, ()> { + let start_position = input.position(); + let authored = match input.next() { + Ok(Token::Ident(s)) => Some(s.into_owned()), + _ => None, + }; + input.reset(start_position); + Ok(CSSColor { + parsed: try!(cssparser::Color::parse(input)), + authored: authored, }) } } @@ -104,7 +94,7 @@ pub mod specified { } } - #[deriving(Clone)] + #[deriving(Clone, PartialEq)] pub struct CSSRGBA { pub parsed: cssparser::RGBA, pub authored: Option<String>, @@ -150,13 +140,6 @@ pub mod specified { /// This cannot be specified by the user directly and is only generated by /// `Stylist::synthesize_rules_for_legacy_attributes()`. ServoCharacterWidth(i32), - - // XXX uncomment when supported: -// Ch(CSSFloat), -// Vw(CSSFloat), -// Vh(CSSFloat), -// Vmin(CSSFloat), -// Vmax(CSSFloat), } impl fmt::Show for Length { @@ -184,23 +167,24 @@ pub mod specified { const AU_PER_PC: CSSFloat = AU_PER_PT * 12.; impl Length { #[inline] - fn parse_internal(input: &ComponentValue, negative_ok: bool) -> Result<Length, ()> { - match input { - &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. - => Length::parse_dimension(value.value, unit.as_slice()), - &Number(ref value) if value.value == 0. => Ok(Length::Au(Au(0))), + fn parse_internal(input: &mut Parser, negative_ok: bool) -> Result<Length, ()> { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => { + Length::parse_dimension(value.value, unit.as_slice()) + } + Token::Number(ref value) if value.value == 0. => Ok(Length::Au(Au(0))), _ => Err(()) } } #[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) } - 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) } 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)), "in" => Ok(Length::Au(Au((value * AU_PER_IN) 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))), "em" => Ok(Length::Em(value)), "ex" => Ok(Length::Ex(value)), - "rem" => Ok(Length::Rem(value)), + "rem" => Ok(Length::Rem(value)) _ => Err(()) } } @@ -219,6 +203,7 @@ pub mod specified { } } + #[deriving(Clone, PartialEq, Copy)] pub enum LengthOrPercentage { Length(Length), @@ -239,26 +224,29 @@ pub mod specified { } } impl LengthOrPercentage { - fn parse_internal(input: &ComponentValue, negative_ok: bool) - -> Result<LengthOrPercentage, ()> { - match input { - &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => + fn parse_internal(input: &mut Parser, negative_ok: bool) + -> Result<LengthOrPercentage, ()> { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => { Length::parse_dimension(value.value, unit.as_slice()) - .map(LengthOrPercentage::Length), - &Percentage(ref value) if negative_ok || value.value >= 0. => - Ok(LengthOrPercentage::Percentage(value.value / 100.)), - &Number(ref value) if value.value == 0. => - Ok(LengthOrPercentage::Length(Length::Au(Au(0)))), + .map(LengthOrPercentage::Length) + } + Token::Percentage(ref value) if negative_ok || value.unit_value >= 0. => { + Ok(LengthOrPercentage::Percentage(value.unit_value)) + } + Token::Number(ref value) if value.value == 0. => { + Ok(LengthOrPercentage::Length(Length::Au(Au(0)))) + } _ => Err(()) } } #[allow(dead_code)] #[inline] - pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentage, ()> { + pub fn parse(input: &mut Parser) -> Result<LengthOrPercentage, ()> { LengthOrPercentage::parse_internal(input, /* negative_ok = */ true) } #[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) } } @@ -285,26 +273,31 @@ pub mod specified { } } impl LengthOrPercentageOrAuto { - fn parse_internal(input: &ComponentValue, negative_ok: bool) + fn parse_internal(input: &mut Parser, negative_ok: bool) -> Result<LengthOrPercentageOrAuto, ()> { - match input { - &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => - Length::parse_dimension(value.value, unit.as_slice()).map(LengthOrPercentageOrAuto::Length), - &Percentage(ref value) if negative_ok || value.value >= 0. => - Ok(LengthOrPercentageOrAuto::Percentage(value.value / 100.)), - &Number(ref value) if value.value == 0. => - Ok(LengthOrPercentageOrAuto::Length(Length::Au(Au(0)))), - &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("auto") => - Ok(LengthOrPercentageOrAuto::Auto), + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => { + Length::parse_dimension(value.value, unit.as_slice()) + .map(LengthOrPercentageOrAuto::Length) + } + Token::Percentage(ref value) if negative_ok || value.unit_value >= 0. => { + Ok(LengthOrPercentageOrAuto::Percentage(value.unit_value)) + } + 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(()) } } #[inline] - pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentageOrAuto, ()> { + pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> { LengthOrPercentageOrAuto::parse_internal(input, /* negative_ok = */ true) } #[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) } } @@ -331,25 +324,32 @@ pub mod specified { } } impl LengthOrPercentageOrNone { - fn parse_internal(input: &ComponentValue, negative_ok: bool) + fn parse_internal(input: &mut Parser, negative_ok: bool) -> Result<LengthOrPercentageOrNone, ()> { - match input { - &Dimension(ref value, ref unit) if negative_ok || value.value >= 0. - => Length::parse_dimension(value.value, unit.as_slice()).map(LengthOrPercentageOrNone::Length), - &Percentage(ref value) if negative_ok || value.value >= 0. - => Ok(LengthOrPercentageOrNone::Percentage(value.value / 100.)), - &Number(ref value) if value.value == 0. => Ok(LengthOrPercentageOrNone::Length(Length::Au(Au(0)))), - &Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => Ok(LengthOrPercentageOrNone::None), + match try!(input.next()) { + Token::Dimension(ref value, ref unit) if negative_ok || value.value >= 0. => { + Length::parse_dimension(value.value, unit.as_slice()) + .map(LengthOrPercentageOrNone::Length) + } + Token::Percentage(ref value) if negative_ok || value.unit_value >= 0. => { + 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(()) } } #[allow(dead_code)] #[inline] - pub fn parse(input: &ComponentValue) -> Result<LengthOrPercentageOrNone, ()> { + pub fn parse(input: &mut Parser) -> Result<LengthOrPercentageOrNone, ()> { LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ true) } #[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) } } @@ -366,19 +366,27 @@ pub mod specified { Bottom, } impl PositionComponent { - pub fn parse(input: &ComponentValue) -> Result<PositionComponent, ()> { - match input { - &Dimension(ref value, ref unit) => - Length::parse_dimension(value.value, unit.as_slice()).map(PositionComponent::Length), - &Percentage(ref value) => Ok(PositionComponent::Percentage(value.value / 100.)), - &Number(ref value) if value.value == 0. => Ok(PositionComponent::Length(Length::Au(Au(0)))), - &Ident(ref value) => { - if value.as_slice().eq_ignore_ascii_case("center") { Ok(PositionComponent::Center) } - 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) } - else if value.as_slice().eq_ignore_ascii_case("top") { Ok(PositionComponent::Top) } - else if value.as_slice().eq_ignore_ascii_case("bottom") { Ok(PositionComponent::Bottom) } - else { Err(()) } + pub fn parse(input: &mut Parser) -> Result<PositionComponent, ()> { + match try!(input.next()) { + Token::Dimension(ref value, ref unit) => { + Length::parse_dimension(value.value, unit.as_slice()) + .map(PositionComponent::Length) + } + Token::Percentage(ref value) => { + Ok(PositionComponent::Percentage(value.unit_value)) + } + Token::Number(ref value) if value.value == 0. => { + Ok(PositionComponent::Length(Length::Au(Au(0)))) + } + 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(()) } @@ -389,8 +397,10 @@ pub mod specified { PositionComponent::Length(x) => LengthOrPercentage::Length(x), PositionComponent::Percentage(x) => LengthOrPercentage::Percentage(x), PositionComponent::Center => LengthOrPercentage::Percentage(0.5), - PositionComponent::Left | PositionComponent::Top => LengthOrPercentage::Percentage(0.0), - PositionComponent::Right | PositionComponent::Bottom => LengthOrPercentage::Percentage(1.0), + PositionComponent::Left | + 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; - static GRAD_TO_RAD: CSSFloat = PI / 200.0; + const RAD_PER_DEG: CSSFloat = PI / 180.0; + const RAD_PER_GRAD: CSSFloat = PI / 200.0; + const RAD_PER_TURN: CSSFloat = PI * 2.0; impl Angle { /// Parses an angle according to CSS-VALUES § 6.1. - fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Angle,()> { - if unit.eq_ignore_ascii_case("deg") { - Ok(Angle(value * DEG_TO_RAD)) - } else if unit.eq_ignore_ascii_case("grad") { - Ok(Angle(value * GRAD_TO_RAD)) - } else if unit.eq_ignore_ascii_case("rad") { - Ok(Angle(value)) - } else if unit.eq_ignore_ascii_case("turn") { - Ok(Angle(value * 2.0 * PI)) - } 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()) + pub fn parse(input: &mut Parser) -> Result<Angle, ()> { + match try!(input.next()) { + Token::Dimension(value, unit) => { + match_ignore_ascii_case! { unit: + "deg" => Ok(Angle(value.value * RAD_PER_DEG)), + "grad" => Ok(Angle(value.value * RAD_PER_GRAD)), + "turn" => Ok(Angle(value.value * RAD_PER_TURN)), + "rad" => Ok(Angle(value.value)) + _ => Err(()) + } } + Token::Number(ref value) if value.value == 0. => Ok(Angle(0.)), _ => Err(()) } } @@ -471,33 +475,30 @@ pub mod specified { } impl Image { - pub fn from_component_value(component_value: &ComponentValue, base_url: &Url) - -> Result<Image,()> { - match component_value { - &URL(ref url) => { - let image_url = super::parse_url(url.as_slice(), base_url); - Ok(Image::Url(image_url)) - }, - &Function(ref name, ref args) => { - if name.as_slice().eq_ignore_ascii_case("linear-gradient") { - Ok(Image::LinearGradient(try!( - super::specified::LinearGradient::parse_function( - args.as_slice())))) - } else { - Err(()) + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Image, ()> { + match try!(input.next()) { + Token::Url(url) => { + Ok(Image::Url(context.parse_url(url.as_slice()))) + } + Token::Function(name) => { + match_ignore_ascii_case! { name: + "linear-gradient" => { + Ok(Image::LinearGradient(try!( + input.parse_nested_block(LinearGradient::parse_function)))) + } + _ => Err(()) } } - _ => Err(()), + _ => Err(()) } } - pub fn to_computed_value(self, context: &super::computed::Context) - -> super::computed::Image { + pub fn to_computed_value(self, context: &computed::Context) -> computed::Image { match self { - Image::Url(url) => super::computed::Image::Url(url), + Image::Url(url) => computed::Image::Url(url), Image::LinearGradient(linear_gradient) => { - super::computed::Image::LinearGradient( - super::computed::LinearGradient::compute(linear_gradient, context)) + computed::Image::LinearGradient( + 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!(VerticalDirection: "top" => Top, "bottom" => Bottom) - fn parse_color_stop(source: ParserIter) -> 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))), - } - } - }; - + fn parse_one_color_stop(input: &mut Parser) -> Result<ColorStop, ()> { Ok(ColorStop { - color: color, - position: position, + color: try!(CSSColor::parse(input)), + position: input.try(LengthOrPercentage::parse).ok(), }) } impl LinearGradient { /// Parses a linear gradient from the given arguments. - pub fn parse_function(args: &[ComponentValue]) -> Result<LinearGradient,()> { - let mut source = BufferedIter::new(args.skip_whitespace()); - - // Parse the angle. - let (angle_or_corner, need_to_parse_comma) = match source.next() { - None => return Err(()), - Some(token) => { - match *token { - Dimension(ref value, ref unit) => { - match Angle::parse_dimension(value.value, unit.as_slice()) { - Ok(angle) => { - (AngleOrCorner::Angle(angle), true) - } - Err(()) => { - source.push_back(token); - (AngleOrCorner::Angle(Angle(PI)), false) - } - } - } - Ident(ref ident) if ident.as_slice().eq_ignore_ascii_case("to") => { - let (mut horizontal, mut vertical) = (None, None); - loop { - match source.next() { - None => break, - Some(token) => { - 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) - } - } - } - }; - - // Parse the color stops. - let stops = if need_to_parse_comma { - match source.next() { - Some(&Comma) => { - try!(parsing_utils::parse_comma_separated(&mut source, parse_color_stop)) + pub fn parse_function(input: &mut Parser) -> Result<LinearGradient, ()> { + let angle_or_corner = if input.try(|input| input.expect_ident_matching("to")).is_ok() { + let (horizontal, vertical) = + if let Ok(value) = input.try(HorizontalDirection::parse) { + (Some(value), input.try(VerticalDirection::parse).ok()) + } else { + let value = try!(VerticalDirection::parse(input)); + (input.try(HorizontalDirection::parse).ok(), Some(value)) + }; + try!(input.expect_comma()); + 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 => Vec::new(), - Some(_) => return Err(()), + (None, None) => unreachable!(), } + } else if let Ok(angle) = input.try(Angle::parse) { + try!(input.expect_comma()); + AngleOrCorner::Angle(angle) } 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 { return Err(()) } - Ok(LinearGradient { angle_or_corner: angle_or_corner, 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 use super::specified::{Angle, AngleOrCorner, HorizontalDirection}; - pub use super::specified::{VerticalDirection}; + pub use super::specified::BorderStyle; + use super::specified::{AngleOrCorner}; + use super::{specified, CSSFloat}; pub use cssparser::Color as CSSColor; - use super::*; - use super::super::longhands; + use properties::longhands; use std::fmt; use url::Url; + use servo_util::geometry::Au; #[allow(missing_copy_implementations)] // It’s kinda big pub struct Context { pub inherited_font_weight: longhands::font_weight::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_height: longhands::height::T, + pub inherited_text_decorations_in_effect: + 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 text_decoration: longhands::text_decoration::computed_value::T, pub font_size: longhands::font_size::computed_value::T, @@ -750,12 +702,18 @@ pub mod computed { #[allow(non_snake_case)] #[inline] - pub fn compute_CSSColor(value: specified::CSSColor, _context: &computed::Context) -> CSSColor { + pub fn compute_CSSColor(value: specified::CSSColor, _context: &Context) -> CSSColor { value.parsed } #[allow(non_snake_case)] #[inline] + pub fn compute_BorderStyle(value: BorderStyle, _context: &Context) -> BorderStyle { + value + } + + #[allow(non_snake_case)] + #[inline] pub fn compute_Au(value: specified::Length, context: &Context) -> Au { compute_Au_with_font_size(value, context.font_size, context.root_font_size) } @@ -951,8 +909,3 @@ pub mod computed { 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()) -} |