servo: Merge #4689 - Port to the new cssparser (from servo:newnewnewcss); r=larsbergstrom

https://github.com/servo/rust-cssparser/pull/68

r? @larsbergstrom

Source-Repo: https://github.com/servo/servo
Source-Revision: 59bca2962c19f653eec835fc54caf1a3eadcb906
This commit is contained in:
Simon Sapin 2015-01-21 14:27:48 -07:00
parent b706a00a11
commit 269dcf54f6
20 changed files with 2018 additions and 2388 deletions

View File

@ -46,8 +46,8 @@ use servo_util::opts;
use std::default::Default;
use std::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),
}
}

View File

@ -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);

View File

@ -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.

View File

@ -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)",
]
@ -488,6 +489,11 @@ dependencies = [
"pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "matches"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mime"
version = "0.0.1"
@ -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)",

View File

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

View File

@ -1,32 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::ast::{SyntaxError, SourceLocation};
pub struct ErrorLoggerIterator<I>(pub I);
impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLoggerIterator<I> {
fn next(&mut self) -> Option<T> {
let ErrorLoggerIterator(ref mut this) = *self;
loop {
match this.next() {
Some(Ok(v)) => return Some(v),
Some(Err(error)) => log_css_error(error.location,
format!("{}", error.reason).as_slice()),
None => return None,
}
}
}
}
/// Defaults to a no-op.
/// Set a `RUST_LOG=style::errors` environment variable
/// to log CSS parse errors to stderr.
pub fn log_css_error(location: SourceLocation, message: &str) {
// TODO eventually this will got into a "web console" or something.
info!("{}:{} {}", location.line, location.column, message)
}

View File

@ -2,24 +2,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* 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,
};
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());
}
}
}
match DeclarationListParser::new(input, parser).run() {
FontFaceRuleParser { family: Some(family), src: Some(src), .. } => {
Ok(FontFaceRule {
family: family,
sources: src,
})
}
}
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"),
_ => Err(())
}
}
fn parse_one_src(iter: ParserIter, base_url: &Url) -> Result<Source, ()> {
let url = match iter.next() {
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(())
}
}
}
fn parse_one_non_generic_family_name(input: &mut Parser) -> Result<String, ()> {
match parse_one_family(input) {
Ok(FontFamily::FamilyName(name)) => Ok(name),
_ => Err(())
}
}
fn parse_one_src(context: &ParserContext, input: &mut Parser) -> Result<Source, ()> {
let url = match input.next() {
// Parsing url()
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(())
}
}

View File

@ -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
}
}

View File

@ -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;

View File

@ -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!();
impl MediaQuery {
fn parse(input: &mut Parser) -> 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);
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(()),
}
// Parse any subsequent expressions
loop {
if input.try(|input| input.expect_ident_matching("and")).is_err() {
return Ok(MediaQuery::new(qualifier, media_type, expressions))
}
Some(component_value) => {
iter.push_back(component_value);
break;
}
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| {

View File

@ -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))
}
}

View File

@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::{Parser, SourcePosition};
use url::{Url, UrlParser};
use log;
use stylesheets::Origin;
use namespaces::NamespaceMap;
pub struct ParserContext<'a> {
pub stylesheet_origin: Origin,
pub base_url: &'a Url,
pub namespaces: NamespaceMap,
}
impl<'a> ParserContext<'a> {
pub fn in_user_agent_stylesheet(&self) -> bool {
self.stylesheet_origin == Origin::UserAgent
}
pub fn parse_url(&self, input: &str) -> Url {
UrlParser::new().base_url(self.base_url).parse(input)
.unwrap_or_else(|_| Url::parse("about:invalid").unwrap())
}
}
/// Defaults to a no-op.
/// Set a `RUST_LOG=style::errors` environment variable
/// to log CSS parse errors to stderr.
pub fn log_css_error(input: &mut Parser, position: SourcePosition, message: &str) {
if log_enabled!(log::INFO) {
let location = input.source_location(position);
// TODO eventually this will got into a "web console" or something.
info!("{}:{} {}", location.line, location.column, message)
}
}

View File

@ -1,101 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::ascii::AsciiExt;
use cssparser::ast::{SkipWhitespaceIterable, SkipWhitespaceIterator};
use cssparser::ast::ComponentValue::{mod, Ident, Comma};
pub fn one_component_value<'a>(input: &'a [ComponentValue]) -> Result<&'a ComponentValue, ()> {
let mut iter = input.skip_whitespace();
match iter.next() {
Some(value) => if iter.next().is_none() { Ok(value) } else { Err(()) },
None => Err(())
}
}
pub fn get_ident_lower(component_value: &ComponentValue) -> Result<String, ()> {
match component_value {
&Ident(ref value) => Ok(value.as_slice().to_ascii_lower()),
_ => Err(()),
}
}
pub struct BufferedIter<E, I> {
iter: I,
buffer: Option<E>,
}
impl<E, I: Iterator<E>> BufferedIter<E, I> {
pub fn new(iter: I) -> BufferedIter<E, I> {
BufferedIter {
iter: iter,
buffer: None,
}
}
#[inline]
pub fn push_back(&mut self, value: E) {
assert!(self.buffer.is_none());
self.buffer = Some(value);
}
#[inline]
pub fn is_eof(&mut self) -> bool {
match self.next() {
Some(value) => {
self.push_back(value);
false
}
None => true
}
}
#[inline]
pub fn next_as_result(&mut self) -> Result<E, ()> {
match self.next() {
Some(value) => Ok(value),
None => Err(()),
}
}
}
impl<E, I: Iterator<E>> Iterator<E> for BufferedIter<E, I> {
#[inline]
fn next(&mut self) -> Option<E> {
if self.buffer.is_some() {
self.buffer.take()
}
else {
self.iter.next()
}
}
}
pub type ParserIter<'a, 'b> = &'a mut BufferedIter<&'b ComponentValue, SkipWhitespaceIterator<'b>>;
#[inline]
pub fn parse_slice_comma_separated<T>(input: &[ComponentValue],
parse_one: |ParserIter| -> Result<T, ()>)
-> Result<Vec<T>, ()> {
parse_comma_separated(&mut BufferedIter::new(input.skip_whitespace()), parse_one)
}
#[inline]
pub fn parse_comma_separated<T>(iter: ParserIter,
parse_one: |ParserIter| -> Result<T, ()>)
-> Result<Vec<T>, ()> {
let mut values = vec![try!(parse_one(iter))];
loop {
match iter.next() {
Some(&Comma) => values.push(try!(parse_one(iter))),
Some(_) => return Err(()),
None => return Ok(values),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -22,14 +22,8 @@ use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use selectors::{CaseSensitivity, Combinator, CompoundSelector, LocalName};
use selectors::{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(),

File diff suppressed because it is too large Load Diff

View File

@ -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 mut state: uint = STATE_CHARSET;
let mut rules = vec!();
let mut namespaces = NamespaceMap::new();
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)
},
}
},
}
state = next_state;
}
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 {
rules: rules,
origin: origin,
rules: rules,
}
}
}
// 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_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()
}
struct MainRuleParser<'a, 'b: 'a> {
context: &'a mut ParserContext<'b>,
state: State,
}
#[deriving(Eq, PartialEq, Ord, PartialOrd)]
enum State {
Start = 1,
Imports = 2,
Namespaces = 3,
Body = 4,
}
enum AtRulePrelude {
FontFace,
Media(MediaQueryList),
}
impl<'a, 'b> AtRuleParser<AtRulePrelude, CSSRule> for MainRuleParser<'a, 'b> {
fn parse_prelude(&mut self, name: &str, input: &mut Parser)
-> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
match_ignore_ascii_case! { name:
"charset" => {
if self.state <= State::Start {
// Valid @charset rules are just ignored
self.state = State::Imports;
let charset = try!(input.expect_string()).into_owned();
return Ok(AtRuleType::WithoutBlock(CSSRule::Charset(charset)))
} else {
return Err(()) // "@charset must be the first rule"
}
},
"import" => {
if self.state <= State::Imports {
self.state = State::Imports;
// TODO: support @import
return Err(()) // "@import is not supported yet"
} else {
return Err(()) // "@import must be before any rule but @charset"
}
},
"namespace" => {
if self.state <= State::Namespaces {
self.state = State::Namespaces;
let (prefix, namespace) = try!(parse_namespace_rule(self.context, input));
return Ok(AtRuleType::WithoutBlock(CSSRule::Namespace(prefix, namespace)))
} else {
return Err(()) // "@namespace must be before any rule but @charset and @import"
}
}
_ => {}
}
self.state = State::Body;
match_ignore_ascii_case! { name:
"media" => {
let media_queries = parse_media_query_list(input);
Ok(AtRuleType::WithBlock(AtRulePrelude::Media(media_queries)))
},
"font-face" => {
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
}
_ => Err(())
}
}
fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
match prelude {
AtRulePrelude::FontFace => {
parse_font_face_block(self.context, input).map(CSSRule::FontFace)
}
AtRulePrelude::Media(media_queries) => {
Ok(CSSRule::Media(MediaRule {
media_queries: media_queries,
rules: parse_nested_rules(self.context, input),
}))
}
}
"font-face" => parse_font_face_rule(rule, parent_rules, base_url),
_ => log_css_error(rule.location,
format!("Unsupported at-rule: @{}", lower_name).as_slice())
}
}
pub fn parse_style_rule(context: &ParserContext,
rule: QualifiedRule,
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![]),
},
}),
],
});
}

View File

@ -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)
}
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, None) => unreachable!(),
}
};
// 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))
}
None => Vec::new(),
Some(_) => return Err(()),
}
} 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)] // Its 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,10 +702,16 @@ 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 {
@ -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())
}

View File

@ -131,9 +131,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)",
]
@ -310,35 +311,6 @@ dependencies = [
"gl_generator 0.0.1 (git+https://github.com/bjz/gl-rs.git)",
]
[[package]]
name = "glfw"
version = "0.0.1"
source = "git+https://github.com/servo/glfw-rs?ref=servo#b186cb444e349a36b992445dc5cb2c99d38f2a42"
dependencies = [
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4)",
"semver 0.1.4 (git+https://github.com/rust-lang/semver)",
]
[[package]]
name = "glfw-sys"
version = "3.0.4"
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#765dace7e4125b87c764f5ac0e7a80eae5c550b2"
[[package]]
name = "glfw_app"
version = "0.0.1"
dependencies = [
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
"compositing 0.0.1",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"msg 0.0.1",
"time 0.1.0 (git+https://github.com/rust-lang/time)",
"util 0.0.1",
]
[[package]]
name = "glutin"
version = "0.0.2"
@ -496,6 +468,11 @@ dependencies = [
"pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "matches"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mime"
version = "0.0.1"
@ -639,18 +616,13 @@ dependencies = [
"util 0.0.1",
]
[[package]]
name = "semver"
version = "0.1.4"
source = "git+https://github.com/rust-lang/semver#58dc6b1999d345ca925a2f12a6a84676e823e179"
[[package]]
name = "servo"
version = "0.0.1"
dependencies = [
"compositing 0.0.1",
"gfx 0.0.1",
"glfw_app 0.0.1",
"glutin_app 0.0.1",
"layout 0.0.1",
"msg 0.0.1",
"net 0.0.1",
@ -702,6 +674,7 @@ dependencies = [
"encoding 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",

View File

@ -102,9 +102,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)",
]
@ -410,6 +411,11 @@ dependencies = [
"pnacl-build-helper 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "matches"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mime"
version = "0.0.1"
@ -610,6 +616,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)",