mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
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:
parent
b706a00a11
commit
269dcf54f6
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
9
servo/components/servo/Cargo.lock
generated
9
servo/components/servo/Cargo.lock
generated
@ -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)",
|
||||
|
@ -36,3 +36,4 @@ git = "https://github.com/servo/string-cache"
|
||||
[dependencies]
|
||||
text_writer = "0.1.1"
|
||||
encoding = "0.2"
|
||||
matches = "0.1"
|
||||
|
@ -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)
|
||||
}
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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| {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
42
servo/components/style/parser.rs
Normal file
42
servo/components/style/parser.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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
@ -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
@ -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![]),
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
@ -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)] // 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,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())
|
||||
}
|
45
servo/ports/cef/Cargo.lock
generated
45
servo/ports/cef/Cargo.lock
generated
@ -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)",
|
||||
|
9
servo/ports/gonk/Cargo.lock
generated
9
servo/ports/gonk/Cargo.lock
generated
@ -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)",
|
||||
|
Loading…
Reference in New Issue
Block a user