aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/layout/display_list_builder.rs12
-rw-r--r--components/script/dom/element.rs14
-rw-r--r--components/script/dom/node.rs12
-rw-r--r--components/servo/Cargo.lock9
-rw-r--r--components/style/Cargo.toml1
-rw-r--r--components/style/errors.rs32
-rw-r--r--components/style/font_face.rs175
-rw-r--r--components/style/legacy.rs33
-rw-r--r--components/style/lib.rs47
-rw-r--r--components/style/media_queries.rs259
-rw-r--r--components/style/namespaces.rs69
-rw-r--r--components/style/parser.rs42
-rw-r--r--components/style/parsing_utils.rs101
-rw-r--r--components/style/properties/mod.rs.mako1858
-rw-r--r--components/style/selector_matching.rs35
-rw-r--r--components/style/selectors.rs644
-rw-r--r--components/style/stylesheets.rs404
-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())
-}