2013-09-04 17:00:52 +00:00
|
|
|
/* 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/. */
|
|
|
|
|
2015-04-11 00:17:49 +00:00
|
|
|
use std::ascii::AsciiExt;
|
2015-01-30 22:27:53 +00:00
|
|
|
use std::cell::Cell;
|
2013-10-22 16:16:17 +00:00
|
|
|
use std::iter::Iterator;
|
2015-04-11 00:17:49 +00:00
|
|
|
use std::slice;
|
2014-04-04 22:52:50 +00:00
|
|
|
use url::Url;
|
2013-12-17 17:04:28 +00:00
|
|
|
|
|
|
|
use encoding::EncodingRef;
|
|
|
|
|
2015-01-21 21:27:48 +00:00
|
|
|
use cssparser::{Parser, decode_stylesheet_bytes,
|
|
|
|
QualifiedRuleParser, AtRuleParser, RuleListParser, AtRuleType};
|
2015-01-30 22:27:53 +00:00
|
|
|
use string_cache::{Atom, Namespace};
|
2015-02-23 15:39:47 +00:00
|
|
|
use selectors::parser::{Selector, parse_selector_list};
|
2015-01-30 22:27:53 +00:00
|
|
|
use parser::{ParserContext, log_css_error};
|
2015-01-21 21:27:48 +00:00
|
|
|
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
|
2015-04-11 00:17:49 +00:00
|
|
|
use media_queries::{Device, MediaQueryList, parse_media_query_list};
|
|
|
|
use font_face::{FontFaceRule, parse_font_face_block};
|
2015-07-22 18:25:57 +00:00
|
|
|
use smallvec::SmallVec;
|
2015-05-06 17:41:09 +00:00
|
|
|
use viewport::ViewportRule;
|
2013-09-04 17:00:52 +00:00
|
|
|
|
|
|
|
|
2015-04-18 10:44:50 +00:00
|
|
|
/// Each style rule has an origin, which determines where it enters the cascade.
|
|
|
|
///
|
|
|
|
/// http://dev.w3.org/csswg/css-cascade/#cascading-origins
|
2015-02-12 00:24:45 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
|
2015-01-21 21:27:48 +00:00
|
|
|
pub enum Origin {
|
2015-04-18 10:44:50 +00:00
|
|
|
/// http://dev.w3.org/csswg/css-cascade/#cascade-origin-ua
|
2015-01-21 21:27:48 +00:00
|
|
|
UserAgent,
|
2015-04-18 10:44:50 +00:00
|
|
|
|
|
|
|
/// http://dev.w3.org/csswg/css-cascade/#cascade-origin-author
|
2015-01-21 21:27:48 +00:00
|
|
|
Author,
|
2015-04-18 10:44:50 +00:00
|
|
|
|
|
|
|
/// http://dev.w3.org/csswg/css-cascade/#cascade-origin-user
|
2015-01-21 21:27:48 +00:00
|
|
|
User,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-12 00:24:45 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2013-09-04 17:00:52 +00:00
|
|
|
pub struct Stylesheet {
|
2013-11-14 04:13:34 +00:00
|
|
|
/// List of rules in the order they were found (important for
|
|
|
|
/// cascading order)
|
2015-04-08 02:16:49 +00:00
|
|
|
pub rules: Vec<CSSRule>,
|
2015-01-21 21:27:48 +00:00
|
|
|
pub origin: Origin,
|
2013-09-04 17:00:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-12 00:24:45 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2013-09-04 17:00:52 +00:00
|
|
|
pub enum CSSRule {
|
2015-01-21 21:27:48 +00:00
|
|
|
Charset(String),
|
|
|
|
Namespace(Option<String>, Namespace),
|
2014-12-18 01:45:49 +00:00
|
|
|
Style(StyleRule),
|
|
|
|
Media(MediaRule),
|
|
|
|
FontFace(FontFaceRule),
|
2015-05-06 17:41:09 +00:00
|
|
|
Viewport(ViewportRule),
|
2013-09-04 17:00:52 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 00:24:45 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2015-01-21 21:27:48 +00:00
|
|
|
pub struct MediaRule {
|
|
|
|
pub media_queries: MediaQueryList,
|
|
|
|
pub rules: Vec<CSSRule>,
|
|
|
|
}
|
2013-09-04 17:00:52 +00:00
|
|
|
|
2015-04-11 00:17:49 +00:00
|
|
|
impl MediaRule {
|
|
|
|
#[inline]
|
|
|
|
pub fn evaluate(&self, device: &Device) -> bool {
|
|
|
|
self.media_queries.evaluate(device)
|
|
|
|
}
|
|
|
|
}
|
2015-01-21 21:27:48 +00:00
|
|
|
|
2015-02-12 00:24:45 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2013-09-04 17:00:52 +00:00
|
|
|
pub struct StyleRule {
|
2015-01-21 21:27:48 +00:00
|
|
|
pub selectors: Vec<Selector>,
|
|
|
|
pub declarations: PropertyDeclarationBlock,
|
2013-09-04 17:00:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-18 21:25:34 +00:00
|
|
|
impl Stylesheet {
|
2015-01-28 01:15:50 +00:00
|
|
|
pub fn from_bytes_iter<I: Iterator<Item=Vec<u8>>>(
|
2015-02-13 10:06:49 +00:00
|
|
|
input: I, base_url: Url, protocol_encoding_label: Option<&str>,
|
2015-01-21 21:27:48 +00:00
|
|
|
environment_encoding: Option<EncodingRef>, origin: Origin) -> Stylesheet {
|
2015-02-13 10:06:49 +00:00
|
|
|
let mut bytes = vec![];
|
2014-12-12 09:12:51 +00:00
|
|
|
// TODO: incremental decoding and tokenization/parsing
|
2013-10-18 21:25:34 +00:00
|
|
|
for chunk in input {
|
2015-02-13 10:06:49 +00:00
|
|
|
bytes.push_all(&chunk)
|
2013-10-18 21:25:34 +00:00
|
|
|
}
|
2015-02-13 10:06:49 +00:00
|
|
|
Stylesheet::from_bytes(&bytes, base_url, protocol_encoding_label,
|
2015-01-21 21:27:48 +00:00
|
|
|
environment_encoding, origin)
|
2013-12-17 17:04:28 +00:00
|
|
|
}
|
|
|
|
|
2014-12-16 02:33:46 +00:00
|
|
|
pub fn from_bytes(bytes: &[u8],
|
|
|
|
base_url: Url,
|
|
|
|
protocol_encoding_label: Option<&str>,
|
|
|
|
environment_encoding: Option<EncodingRef>,
|
2015-01-21 21:27:48 +00:00
|
|
|
origin: Origin)
|
2014-12-16 02:33:46 +00:00
|
|
|
-> Stylesheet {
|
2014-05-04 21:25:39 +00:00
|
|
|
// TODO: bytes.as_slice could be bytes.container_as_bytes()
|
2014-07-07 06:30:04 +00:00
|
|
|
let (string, _) = decode_stylesheet_bytes(
|
2015-02-13 10:06:49 +00:00
|
|
|
bytes, protocol_encoding_label, environment_encoding);
|
|
|
|
Stylesheet::from_str(&string, base_url, origin)
|
2013-10-18 21:25:34 +00:00
|
|
|
}
|
|
|
|
|
2015-01-21 21:27:48 +00:00
|
|
|
pub fn from_str<'i>(css: &'i str, base_url: Url, origin: Origin) -> Stylesheet {
|
2015-01-30 22:27:53 +00:00
|
|
|
let rule_parser = TopLevelRuleParser {
|
|
|
|
context: ParserContext::new(origin, &base_url),
|
|
|
|
state: Cell::new(State::Start),
|
2014-12-16 02:33:46 +00:00
|
|
|
};
|
2015-01-30 22:27:53 +00:00
|
|
|
let mut input = Parser::new(css);
|
|
|
|
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
|
|
|
|
let mut rules = Vec::new();
|
|
|
|
while let Some(result) = iter.next() {
|
|
|
|
match result {
|
|
|
|
Ok(rule) => {
|
|
|
|
if let CSSRule::Namespace(ref prefix, ref namespace) = rule {
|
|
|
|
if let Some(prefix) = prefix.as_ref() {
|
2015-02-23 15:39:47 +00:00
|
|
|
iter.parser.context.selector_context.namespace_prefixes.insert(
|
2015-01-30 22:27:53 +00:00
|
|
|
prefix.clone(), namespace.clone());
|
|
|
|
} else {
|
2015-02-23 15:39:47 +00:00
|
|
|
iter.parser.context.selector_context.default_namespace =
|
|
|
|
Some(namespace.clone());
|
2015-01-30 22:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rules.push(rule);
|
|
|
|
}
|
|
|
|
Err(range) => {
|
2015-02-12 00:24:45 +00:00
|
|
|
let pos = range.start;
|
2015-01-30 22:27:53 +00:00
|
|
|
let message = format!("Invalid rule: '{}'", iter.input.slice(range));
|
2015-02-12 00:24:45 +00:00
|
|
|
log_css_error(iter.input, pos, &*message);
|
2015-01-30 22:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-21 21:27:48 +00:00
|
|
|
Stylesheet {
|
|
|
|
origin: origin,
|
|
|
|
rules: rules,
|
|
|
|
}
|
|
|
|
}
|
2015-04-11 00:17:49 +00:00
|
|
|
|
|
|
|
/// Return an iterator over all the rules within the style-sheet.
|
|
|
|
#[inline]
|
|
|
|
pub fn rules<'a>(&'a self) -> Rules<'a> {
|
|
|
|
Rules::new(self.rules.iter(), None)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return an iterator over the effective rules within the style-sheet, as
|
|
|
|
/// according to the supplied `Device`.
|
|
|
|
///
|
|
|
|
/// If a condition does not hold, its associated conditional group rule and
|
|
|
|
/// nested rules will be skipped. Use `rules` if all rules need to be
|
|
|
|
/// examined.
|
|
|
|
#[inline]
|
|
|
|
pub fn effective_rules<'a>(&'a self, device: &'a Device) -> Rules<'a> {
|
|
|
|
Rules::new(self.rules.iter(), Some(device))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `CSSRule` iterator.
|
|
|
|
///
|
|
|
|
/// The iteration order is pre-order. Specifically, this implies that a
|
|
|
|
/// conditional group rule will come before its nested rules.
|
|
|
|
pub struct Rules<'a> {
|
|
|
|
// 2 because normal case is likely to be just one level of nesting (@media)
|
2015-07-22 18:25:57 +00:00
|
|
|
stack: SmallVec<[slice::Iter<'a, CSSRule>; 2]>,
|
2015-04-11 00:17:49 +00:00
|
|
|
device: Option<&'a Device>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Rules<'a> {
|
|
|
|
fn new(iter: slice::Iter<'a, CSSRule>, device: Option<&'a Device>) -> Rules<'a> {
|
2015-07-22 18:25:57 +00:00
|
|
|
let mut stack: SmallVec<[slice::Iter<'a, CSSRule>; 2]> = SmallVec::new();
|
2015-04-11 00:17:49 +00:00
|
|
|
stack.push(iter);
|
|
|
|
|
|
|
|
Rules { stack: stack, device: device }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for Rules<'a> {
|
|
|
|
type Item = &'a CSSRule;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<&'a CSSRule> {
|
|
|
|
while !self.stack.is_empty() {
|
|
|
|
let top = self.stack.len() - 1;
|
2015-05-05 14:11:30 +00:00
|
|
|
while let Some(rule) = self.stack[top].next() {
|
2015-04-11 00:17:49 +00:00
|
|
|
// handle conditional group rules
|
|
|
|
match rule {
|
|
|
|
&CSSRule::Media(ref rule) => {
|
|
|
|
if let Some(device) = self.device {
|
|
|
|
if rule.evaluate(device) {
|
|
|
|
self.stack.push(rule.rules.iter());
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.stack.push(rule.rules.iter());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Some(rule)
|
|
|
|
}
|
|
|
|
|
|
|
|
self.stack.pop();
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
// TODO: track total number of rules in style-sheet for upper bound?
|
|
|
|
(0, None)
|
|
|
|
}
|
2015-01-21 21:27:48 +00:00
|
|
|
}
|
2014-12-16 02:33:46 +00:00
|
|
|
|
2015-04-11 00:17:49 +00:00
|
|
|
pub mod rule_filter {
|
|
|
|
//! Specific `CSSRule` variant iterators.
|
|
|
|
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
use super::{CSSRule, MediaRule, StyleRule};
|
|
|
|
use super::super::font_face::FontFaceRule;
|
2015-05-06 17:41:09 +00:00
|
|
|
use super::super::viewport::ViewportRule;
|
2015-04-11 00:17:49 +00:00
|
|
|
|
|
|
|
macro_rules! rule_filter {
|
|
|
|
($variant:ident -> $value:ty) => {
|
|
|
|
/// An iterator that only yields rules that are of the synonymous `CSSRule` variant.
|
|
|
|
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
|
|
|
pub struct $variant<'a, I> {
|
|
|
|
iter: I,
|
|
|
|
_lifetime: PhantomData<&'a ()>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, I> $variant<'a, I> where I: Iterator<Item=&'a CSSRule> {
|
|
|
|
pub fn new(iter: I) -> $variant<'a, I> {
|
|
|
|
$variant {
|
|
|
|
iter: iter,
|
|
|
|
_lifetime: PhantomData
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, I> Iterator for $variant<'a, I> where I: Iterator<Item=&'a CSSRule> {
|
|
|
|
type Item = &'a $value;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<&'a $value> {
|
|
|
|
while let Some(rule) = self.iter.next() {
|
|
|
|
match rule {
|
|
|
|
&CSSRule::$variant(ref value) => return Some(value),
|
|
|
|
_ => continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
(0, self.iter.size_hint().1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rule_filter!(FontFace -> FontFaceRule);
|
|
|
|
rule_filter!(Media -> MediaRule);
|
|
|
|
rule_filter!(Style -> StyleRule);
|
2015-05-06 17:41:09 +00:00
|
|
|
rule_filter!(Viewport -> ViewportRule);
|
2015-04-11 00:17:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Extension methods for `CSSRule` iterators.
|
|
|
|
pub trait CSSRuleIteratorExt<'a>: Iterator<Item=&'a CSSRule> {
|
|
|
|
/// Yield only @font-face rules.
|
|
|
|
fn font_face(self) -> rule_filter::FontFace<'a, Self>;
|
|
|
|
|
|
|
|
/// Yield only @media rules.
|
|
|
|
fn media(self) -> rule_filter::Media<'a, Self>;
|
|
|
|
|
|
|
|
/// Yield only style rules.
|
|
|
|
fn style(self) -> rule_filter::Style<'a, Self>;
|
2015-05-06 17:41:09 +00:00
|
|
|
|
|
|
|
/// Yield only @viewport rules.
|
|
|
|
fn viewport(self) -> rule_filter::Viewport<'a, Self>;
|
2015-04-11 00:17:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, I> CSSRuleIteratorExt<'a> for I where I: Iterator<Item=&'a CSSRule> {
|
|
|
|
#[inline]
|
|
|
|
fn font_face(self) -> rule_filter::FontFace<'a, I> {
|
|
|
|
rule_filter::FontFace::new(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn media(self) -> rule_filter::Media<'a, I> {
|
|
|
|
rule_filter::Media::new(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn style(self) -> rule_filter::Style<'a, I> {
|
|
|
|
rule_filter::Style::new(self)
|
|
|
|
}
|
2015-05-06 17:41:09 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn viewport(self) -> rule_filter::Viewport<'a, I> {
|
|
|
|
rule_filter::Viewport::new(self)
|
|
|
|
}
|
2015-04-11 00:17:49 +00:00
|
|
|
}
|
2013-10-18 21:25:34 +00:00
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
fn parse_nested_rules(context: &ParserContext, input: &mut Parser) -> Vec<CSSRule> {
|
|
|
|
let mut iter = RuleListParser::new_for_nested_rule(input, NestedRuleParser { context: context });
|
|
|
|
let mut rules = Vec::new();
|
|
|
|
while let Some(result) = iter.next() {
|
|
|
|
match result {
|
|
|
|
Ok(rule) => rules.push(rule),
|
|
|
|
Err(range) => {
|
2015-02-12 00:24:45 +00:00
|
|
|
let pos = range.start;
|
2015-01-30 22:27:53 +00:00
|
|
|
let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
|
2015-02-12 00:24:45 +00:00
|
|
|
log_css_error(iter.input, pos, &*message);
|
2015-01-30 22:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rules
|
2015-01-21 21:27:48 +00:00
|
|
|
}
|
2013-10-18 21:25:34 +00:00
|
|
|
|
2015-01-21 21:27:48 +00:00
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
struct TopLevelRuleParser<'a> {
|
|
|
|
context: ParserContext<'a>,
|
|
|
|
state: Cell<State>,
|
2015-01-21 21:27:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-28 22:52:49 +00:00
|
|
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
|
2015-01-21 21:27:48 +00:00
|
|
|
enum State {
|
|
|
|
Start = 1,
|
|
|
|
Imports = 2,
|
|
|
|
Namespaces = 3,
|
|
|
|
Body = 4,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum AtRulePrelude {
|
|
|
|
FontFace,
|
|
|
|
Media(MediaQueryList),
|
2015-05-06 17:41:09 +00:00
|
|
|
Viewport,
|
2015-01-21 21:27:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
|
|
|
|
type Prelude = AtRulePrelude;
|
|
|
|
type AtRule = CSSRule;
|
|
|
|
|
|
|
|
fn parse_prelude(&self, name: &str, input: &mut Parser)
|
2015-01-21 21:27:48 +00:00
|
|
|
-> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
|
2015-01-28 01:15:50 +00:00
|
|
|
match_ignore_ascii_case! { name,
|
2015-01-21 21:27:48 +00:00
|
|
|
"charset" => {
|
2015-01-30 22:27:53 +00:00
|
|
|
if self.state.get() <= State::Start {
|
2015-01-21 21:27:48 +00:00
|
|
|
// Valid @charset rules are just ignored
|
2015-01-30 22:27:53 +00:00
|
|
|
self.state.set(State::Imports);
|
2015-01-21 21:27:48 +00:00
|
|
|
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" => {
|
2015-01-30 22:27:53 +00:00
|
|
|
if self.state.get() <= State::Imports {
|
|
|
|
self.state.set(State::Imports);
|
2015-01-21 21:27:48 +00:00
|
|
|
// TODO: support @import
|
|
|
|
return Err(()) // "@import is not supported yet"
|
|
|
|
} else {
|
|
|
|
return Err(()) // "@import must be before any rule but @charset"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"namespace" => {
|
2015-01-30 22:27:53 +00:00
|
|
|
if self.state.get() <= State::Namespaces {
|
|
|
|
self.state.set(State::Namespaces);
|
|
|
|
|
|
|
|
let prefix = input.try(|input| input.expect_ident()).ok().map(|p| p.into_owned());
|
2015-02-13 10:06:49 +00:00
|
|
|
let url = Namespace(Atom::from_slice(&*try!(input.expect_url_or_string())));
|
2015-01-30 22:27:53 +00:00
|
|
|
return Ok(AtRuleType::WithoutBlock(CSSRule::Namespace(prefix, url)))
|
2015-01-21 21:27:48 +00:00
|
|
|
} else {
|
|
|
|
return Err(()) // "@namespace must be before any rule but @charset and @import"
|
|
|
|
}
|
2013-10-18 21:25:34 +00:00
|
|
|
}
|
2015-01-21 21:27:48 +00:00
|
|
|
_ => {}
|
2013-09-04 17:00:52 +00:00
|
|
|
}
|
2015-01-21 21:27:48 +00:00
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
self.state.set(State::Body);
|
|
|
|
AtRuleParser::parse_prelude(&NestedRuleParser { context: &self.context }, name, input)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn parse_block(&self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
|
|
|
|
AtRuleParser::parse_block(&NestedRuleParser { context: &self.context }, prelude, input)
|
|
|
|
}
|
|
|
|
}
|
2015-01-21 21:27:48 +00:00
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
|
|
|
|
impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
|
|
|
|
type Prelude = Vec<Selector>;
|
|
|
|
type QualifiedRule = CSSRule;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn parse_prelude(&self, input: &mut Parser) -> Result<Vec<Selector>, ()> {
|
|
|
|
self.state.set(State::Body);
|
|
|
|
QualifiedRuleParser::parse_prelude(&NestedRuleParser { context: &self.context }, input)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn parse_block(&self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> {
|
|
|
|
QualifiedRuleParser::parse_block(&NestedRuleParser { context: &self.context },
|
|
|
|
prelude, input)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct NestedRuleParser<'a, 'b: 'a> {
|
|
|
|
context: &'a ParserContext<'b>,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
|
|
|
|
type Prelude = AtRulePrelude;
|
|
|
|
type AtRule = CSSRule;
|
|
|
|
|
|
|
|
fn parse_prelude(&self, name: &str, input: &mut Parser)
|
|
|
|
-> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
|
2015-01-28 01:15:50 +00:00
|
|
|
match_ignore_ascii_case! { name,
|
2015-01-21 21:27:48 +00:00
|
|
|
"media" => {
|
|
|
|
let media_queries = parse_media_query_list(input);
|
|
|
|
Ok(AtRuleType::WithBlock(AtRulePrelude::Media(media_queries)))
|
|
|
|
},
|
|
|
|
"font-face" => {
|
|
|
|
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
|
2015-05-06 17:41:09 +00:00
|
|
|
},
|
|
|
|
"viewport" => {
|
|
|
|
if ::util::opts::experimental_enabled() {
|
|
|
|
Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
2015-01-21 21:27:48 +00:00
|
|
|
}
|
|
|
|
_ => Err(())
|
2014-11-07 00:24:28 +00:00
|
|
|
}
|
2013-09-04 17:00:52 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
fn parse_block(&self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
|
2015-01-21 21:27:48 +00:00
|
|
|
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),
|
|
|
|
}))
|
|
|
|
}
|
2015-05-06 17:41:09 +00:00
|
|
|
AtRulePrelude::Viewport => {
|
|
|
|
ViewportRule::parse(input, self.context).map(CSSRule::Viewport)
|
|
|
|
}
|
2014-12-16 02:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-09-04 17:00:52 +00:00
|
|
|
|
2015-01-21 21:27:48 +00:00
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
|
|
|
|
type Prelude = Vec<Selector>;
|
|
|
|
type QualifiedRule = CSSRule;
|
|
|
|
|
|
|
|
fn parse_prelude(&self, input: &mut Parser) -> Result<Vec<Selector>, ()> {
|
2015-02-23 15:39:47 +00:00
|
|
|
parse_selector_list(&self.context.selector_context, input)
|
2015-01-21 21:27:48 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 22:27:53 +00:00
|
|
|
fn parse_block(&self, prelude: Vec<Selector>, input: &mut Parser) -> Result<CSSRule, ()> {
|
2015-01-21 21:27:48 +00:00
|
|
|
Ok(CSSRule::Style(StyleRule {
|
|
|
|
selectors: prelude,
|
|
|
|
declarations: parse_property_declaration_list(self.context, input)
|
|
|
|
}))
|
2013-09-04 17:00:52 +00:00
|
|
|
}
|
|
|
|
}
|