Handle parser state in CSSOM insert_rule

This commit is contained in:
Manish Goregaokar 2016-11-16 14:43:33 -08:00
parent 262408d9cb
commit 2793d5f0a9
3 changed files with 103 additions and 29 deletions

View file

@ -6,7 +6,7 @@
use {Atom, Prefix, Namespace};
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes};
use cssparser::{AtRuleType, RuleListParser, SourcePosition, Token};
use cssparser::{AtRuleType, RuleListParser, SourcePosition, Token, parse_one_rule};
use cssparser::ToCss as ParserToCss;
use encoding::EncodingRef;
use error_reporting::ParseErrorReporter;
@ -70,6 +70,36 @@ impl CssRules {
}
})
}
// Provides the parser state at a given insertion index
pub fn state_at_index(rules: &[CssRule], at: usize) -> State {
let mut state = State::Start;
for rule in rules.iter().take(at) {
state = match *rule {
// CssRule::Charset(..) => State::Start,
// CssRule::Import(..) => State::Imports,
CssRule::Namespace(..) => State::Namespaces,
_ => State::Body,
};
}
state
}
// Provides the maximum allowed parser state at a given index,
// searching in reverse. If inserting at this index, the parser
// must not be in a state greater than this post-insertion.
pub fn state_at_index_rev(rules: &[CssRule], at: usize) -> State {
let mut state = State::Body;
for rule in rules.iter().skip(at).rev() {
state = match *rule {
// CssRule::Charset(..) => State::Start,
// CssRule::Import(..) => State::Imports,
CssRule::Namespace(..) => State::Namespaces,
_ => State::Body,
};
}
state
}
}
#[derive(Debug)]
@ -119,6 +149,11 @@ impl ParseErrorReporter for MemoryHoleReporter {
}
}
pub enum SingleRuleParseError {
Syntax,
Hierarchy,
}
impl CssRule {
/// Call `f` with the slice of rules directly contained inside this rule.
///
@ -142,25 +177,35 @@ impl CssRule {
}
}
pub fn from_str(css: &str, origin: Origin,
base_url: Url, extra_data: ParserContextExtraData) -> Result<Self, ()> {
// input state is None for a nested rule
// Returns a parsed CSS rule and the final state of the parser
pub fn parse(css: &str, origin: Origin,
base_url: Url,
extra_data: ParserContextExtraData,
state: Option<State>) -> Result<(Self, State), SingleRuleParseError> {
let error_reporter = Box::new(MemoryHoleReporter);
let mut namespaces = Namespaces::default();
let rule_parser = TopLevelRuleParser {
context: ParserContext::new_with_extra_data(origin, &base_url, error_reporter.clone(),
extra_data),
state: Cell::new(State::Start),
let context = ParserContext::new_with_extra_data(origin, &base_url,
error_reporter.clone(),
extra_data);
let mut input = Parser::new(css);
// nested rules are in the body state
let state = state.unwrap_or(State::Body);
let mut rule_parser = TopLevelRuleParser {
context: context,
state: Cell::new(state),
namespaces: &mut namespaces,
};
let mut input = Parser::new(css);
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
if let Some(Ok(rule)) = iter.next() {
if iter.next().is_some() {
return Err(());
match parse_one_rule(&mut input, &mut rule_parser) {
Ok(result) => Ok((result, rule_parser.state.get())),
Err(_) => {
if let State::Invalid = rule_parser.state.get() {
Err(SingleRuleParseError::Hierarchy)
} else {
Err(SingleRuleParseError::Syntax)
}
}
return Ok(rule);
} else {
return Err(());
}
}
}
@ -418,11 +463,12 @@ impl<'b> TopLevelRuleParser<'b> {
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum State {
pub enum State {
Start = 1,
Imports = 2,
Namespaces = 3,
Body = 4,
Invalid = 5,
}
@ -451,6 +497,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
// TODO: support @import
return Err(()) // "@import is not supported yet"
} else {
self.state.set(State::Invalid);
return Err(()) // "@import must be before any rule but @charset"
}
},
@ -477,6 +524,7 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
}
)))))
} else {
self.state.set(State::Invalid);
return Err(()) // "@namespace must be before any rule but @charset and @import"
}
},
@ -485,7 +533,11 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
"charset" => return Err(()), // (insert appropriate error message)
_ => {}
}
// Don't allow starting with an invalid state
if self.state.get() > State::Body {
self.state.set(State::Invalid);
return Err(());
}
self.state.set(State::Body);
AtRuleParser::parse_prelude(&mut self.nested(), name, input)
}