Auto merge of #17139 - emilio:parsing-simplify, r=nox

style: Simplify a bit the parsing code.

I plan to simplify it further, but this is worth landing on its own.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17139)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-06-02 05:38:48 -07:00 committed by GitHub
commit fa158a78b6
3 changed files with 247 additions and 169 deletions

View file

@ -7,7 +7,6 @@
use context::QuirksMode; use context::QuirksMode;
use cssparser::{Parser, SourcePosition, UnicodeRange}; use cssparser::{Parser, SourcePosition, UnicodeRange};
use error_reporting::ParseErrorReporter; use error_reporting::ParseErrorReporter;
use parking_lot::RwLock;
use style_traits::OneOrMoreCommaSeparated; use style_traits::OneOrMoreCommaSeparated;
use stylesheets::{CssRuleType, Origin, UrlExtraData, Namespaces}; use stylesheets::{CssRuleType, Origin, UrlExtraData, Namespaces};
@ -82,8 +81,8 @@ pub struct ParserContext<'a> {
pub parsing_mode: ParsingMode, pub parsing_mode: ParsingMode,
/// The quirks mode of this stylesheet. /// The quirks mode of this stylesheet.
pub quirks_mode: QuirksMode, pub quirks_mode: QuirksMode,
/// The list of all namespaces active in the current stylesheet /// The currently active namespaces.
pub namespaces: Option<&'a RwLock<Namespaces>>, pub namespaces: Option<&'a Namespaces>,
} }
impl<'a> ParserContext<'a> { impl<'a> ParserContext<'a> {
@ -108,19 +107,21 @@ impl<'a> ParserContext<'a> {
} }
/// Create a parser context for on-the-fly parsing in CSSOM /// Create a parser context for on-the-fly parsing in CSSOM
pub fn new_for_cssom(url_data: &'a UrlExtraData, pub fn new_for_cssom(
url_data: &'a UrlExtraData,
error_reporter: &'a ParseErrorReporter, error_reporter: &'a ParseErrorReporter,
rule_type: Option<CssRuleType>, rule_type: Option<CssRuleType>,
parsing_mode: ParsingMode, parsing_mode: ParsingMode,
quirks_mode: QuirksMode) quirks_mode: QuirksMode
-> ParserContext<'a> { ) -> ParserContext<'a> {
Self::new(Origin::Author, url_data, error_reporter, rule_type, parsing_mode, quirks_mode) Self::new(Origin::Author, url_data, error_reporter, rule_type, parsing_mode, quirks_mode)
} }
/// Create a parser context based on a previous context, but with a modified rule type. /// Create a parser context based on a previous context, but with a modified rule type.
pub fn new_with_rule_type(context: &'a ParserContext, pub fn new_with_rule_type(
rule_type: Option<CssRuleType>) context: &'a ParserContext,
-> ParserContext<'a> { rule_type: Option<CssRuleType>
) -> ParserContext<'a> {
ParserContext { ParserContext {
stylesheet_origin: context.stylesheet_origin, stylesheet_origin: context.stylesheet_origin,
url_data: context.url_data, url_data: context.url_data,
@ -134,13 +135,14 @@ impl<'a> ParserContext<'a> {
} }
/// Create a parser context for inline CSS which accepts additional line offset argument. /// Create a parser context for inline CSS which accepts additional line offset argument.
pub fn new_with_line_number_offset(stylesheet_origin: Origin, pub fn new_with_line_number_offset(
stylesheet_origin: Origin,
url_data: &'a UrlExtraData, url_data: &'a UrlExtraData,
error_reporter: &'a ParseErrorReporter, error_reporter: &'a ParseErrorReporter,
line_number_offset: u64, line_number_offset: u64,
parsing_mode: ParsingMode, parsing_mode: ParsingMode,
quirks_mode: QuirksMode) quirks_mode: QuirksMode
-> ParserContext<'a> { ) -> ParserContext<'a> {
ParserContext { ParserContext {
stylesheet_origin: stylesheet_origin, stylesheet_origin: stylesheet_origin,
url_data: url_data, url_data: url_data,

View file

@ -40,7 +40,6 @@ use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{fmt, mem}; use std::{fmt, mem};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cell::Cell;
use std::mem::align_of; use std::mem::align_of;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::slice; use std::slice;
@ -479,15 +478,19 @@ impl CssRule {
loader: Option<&StylesheetLoader>) loader: Option<&StylesheetLoader>)
-> Result<(Self, State), SingleRuleParseError> { -> Result<(Self, State), SingleRuleParseError> {
let error_reporter = NullReporter; let error_reporter = NullReporter;
let mut context = ParserContext::new(parent_stylesheet.origin, let context = ParserContext::new(
parent_stylesheet.origin,
&parent_stylesheet.url_data, &parent_stylesheet.url_data,
&error_reporter, &error_reporter,
None, None,
PARSING_MODE_DEFAULT, PARSING_MODE_DEFAULT,
parent_stylesheet.quirks_mode); parent_stylesheet.quirks_mode
context.namespaces = Some(&parent_stylesheet.namespaces); );
let mut input = Parser::new(css); let mut input = Parser::new(css);
let mut guard = parent_stylesheet.namespaces.write();
// nested rules are in the body state // nested rules are in the body state
let state = state.unwrap_or(State::Body); let state = state.unwrap_or(State::Body);
let mut rule_parser = TopLevelRuleParser { let mut rule_parser = TopLevelRuleParser {
@ -495,12 +498,13 @@ impl CssRule {
context: context, context: context,
shared_lock: &parent_stylesheet.shared_lock, shared_lock: &parent_stylesheet.shared_lock,
loader: loader, loader: loader,
state: Cell::new(state), state: state,
namespaces: Some(&mut *guard),
}; };
match parse_one_rule(&mut input, &mut rule_parser) { match parse_one_rule(&mut input, &mut rule_parser) {
Ok(result) => Ok((result, rule_parser.state.get())), Ok(result) => Ok((result, rule_parser.state)),
Err(_) => { Err(_) => {
if let State::Invalid = rule_parser.state.get() { if let State::Invalid = rule_parser.state {
Err(SingleRuleParseError::Hierarchy) Err(SingleRuleParseError::Hierarchy)
} else { } else {
Err(SingleRuleParseError::Syntax) Err(SingleRuleParseError::Syntax)
@ -510,8 +514,10 @@ impl CssRule {
} }
/// Deep clones this CssRule. /// Deep clones this CssRule.
fn deep_clone_with_lock(&self, fn deep_clone_with_lock(
lock: &SharedRwLock) -> CssRule { &self,
lock: &SharedRwLock
) -> CssRule {
let guard = lock.read(); let guard = lock.read();
match *self { match *self {
CssRule::Namespace(ref arc) => { CssRule::Namespace(ref arc) => {
@ -1215,10 +1221,19 @@ impl Stylesheet {
let namespaces = RwLock::new(Namespaces::default()); let namespaces = RwLock::new(Namespaces::default());
// FIXME: we really should update existing.url_data with the given url_data, // FIXME: we really should update existing.url_data with the given url_data,
// otherwise newly inserted rule may not have the right base url. // otherwise newly inserted rule may not have the right base url.
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules( let (rules, dirty_on_viewport_size_change) =
css, url_data, existing.origin, &namespaces, Stylesheet::parse_rules(
&existing.shared_lock, stylesheet_loader, error_reporter, css,
existing.quirks_mode, line_number_offset); url_data,
existing.origin,
&mut *namespaces.write(),
&existing.shared_lock,
stylesheet_loader,
error_reporter,
existing.quirks_mode,
line_number_offset
);
mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write()); mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write());
existing.dirty_on_viewport_size_change existing.dirty_on_viewport_size_change
.store(dirty_on_viewport_size_change, Ordering::Release); .store(dirty_on_viewport_size_change, Ordering::Release);
@ -1228,35 +1243,45 @@ impl Stylesheet {
*existing.rules.write_with(&mut guard) = CssRules(rules); *existing.rules.write_with(&mut guard) = CssRules(rules);
} }
fn parse_rules(css: &str, fn parse_rules(
css: &str,
url_data: &UrlExtraData, url_data: &UrlExtraData,
origin: Origin, origin: Origin,
namespaces: &RwLock<Namespaces>, namespaces: &mut Namespaces,
shared_lock: &SharedRwLock, shared_lock: &SharedRwLock,
stylesheet_loader: Option<&StylesheetLoader>, stylesheet_loader: Option<&StylesheetLoader>,
error_reporter: &ParseErrorReporter, error_reporter: &ParseErrorReporter,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
line_number_offset: u64) line_number_offset: u64
-> (Vec<CssRule>, bool) { ) -> (Vec<CssRule>, bool) {
let mut rules = Vec::new(); let mut rules = Vec::new();
let mut input = Parser::new(css); let mut input = Parser::new(css);
let mut context = ParserContext::new_with_line_number_offset(origin, url_data, error_reporter,
let context =
ParserContext::new_with_line_number_offset(
origin,
url_data,
error_reporter,
line_number_offset, line_number_offset,
PARSING_MODE_DEFAULT, PARSING_MODE_DEFAULT,
quirks_mode); quirks_mode
context.namespaces = Some(namespaces); );
let rule_parser = TopLevelRuleParser { let rule_parser = TopLevelRuleParser {
stylesheet_origin: origin, stylesheet_origin: origin,
shared_lock: shared_lock, shared_lock: shared_lock,
loader: stylesheet_loader, loader: stylesheet_loader,
context: context, context: context,
state: Cell::new(State::Start), state: State::Start,
namespaces: Some(namespaces),
}; };
input.look_for_viewport_percentages(); input.look_for_viewport_percentages();
{ {
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser); let mut iter =
RuleListParser::new_for_stylesheet(&mut input, rule_parser);
while let Some(result) = iter.next() { while let Some(result) = iter.next() {
match result { match result {
Ok(rule) => rules.push(rule), Ok(rule) => rules.push(rule),
@ -1289,9 +1314,17 @@ impl Stylesheet {
-> Stylesheet { -> Stylesheet {
let namespaces = RwLock::new(Namespaces::default()); let namespaces = RwLock::new(Namespaces::default());
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules( let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
css, &url_data, origin, &namespaces, css,
&shared_lock, stylesheet_loader, error_reporter, quirks_mode, line_number_offset, &url_data,
origin,
&mut *namespaces.write(),
&shared_lock,
stylesheet_loader,
error_reporter,
quirks_mode,
line_number_offset,
); );
Stylesheet { Stylesheet {
origin: origin, origin: origin,
url_data: url_data, url_data: url_data,
@ -1477,7 +1510,8 @@ struct TopLevelRuleParser<'a> {
shared_lock: &'a SharedRwLock, shared_lock: &'a SharedRwLock,
loader: Option<&'a StylesheetLoader>, loader: Option<&'a StylesheetLoader>,
context: ParserContext<'a>, context: ParserContext<'a>,
state: Cell<State>, state: State,
namespaces: Option<&'a mut Namespaces>,
} }
impl<'b> TopLevelRuleParser<'b> { impl<'b> TopLevelRuleParser<'b> {
@ -1549,14 +1583,21 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
type Prelude = AtRulePrelude; type Prelude = AtRulePrelude;
type AtRule = CssRule; type AtRule = CssRule;
fn parse_prelude(&mut self, name: &str, input: &mut Parser) fn parse_prelude(
-> Result<AtRuleType<AtRulePrelude, CssRule>, ()> { &mut self,
name: &str,
input: &mut Parser
) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
let location = get_location_with_offset(input.current_source_location(), let location = get_location_with_offset(input.current_source_location(),
self.context.line_number_offset); self.context.line_number_offset);
match_ignore_ascii_case! { name, match_ignore_ascii_case! { name,
"import" => { "import" => {
if self.state.get() <= State::Imports { if self.state > State::Imports {
self.state.set(State::Imports); self.state = State::Invalid;
return Err(()) // "@import must be before any rule but @charset"
}
self.state = State::Imports;
let url_string = input.expect_url_or_string()?; let url_string = input.expect_url_or_string()?;
let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?; let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
@ -1590,29 +1631,31 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
}, &mut |import_rule| { }, &mut |import_rule| {
Arc::new(self.shared_lock.wrap(import_rule)) Arc::new(self.shared_lock.wrap(import_rule))
}); });
return Ok(AtRuleType::WithoutBlock(CssRule::Import(arc))) return Ok(AtRuleType::WithoutBlock(CssRule::Import(arc)))
} else {
self.state.set(State::Invalid);
return Err(()) // "@import must be before any rule but @charset"
}
}, },
"namespace" => { "namespace" => {
if self.state.get() <= State::Namespaces { if self.state > State::Namespaces {
self.state.set(State::Namespaces); self.state = State::Invalid;
return Err(()) // "@namespace must be before any rule but @charset and @import"
}
self.state = State::Namespaces;
let prefix_result = input.try(|input| input.expect_ident()); let prefix_result = input.try(|input| input.expect_ident());
let url = Namespace::from(try!(input.expect_url_or_string())); let url = Namespace::from(try!(input.expect_url_or_string()));
let id = register_namespace(&url)?; let id = register_namespace(&url)?;
let mut namespaces = self.namespaces.as_mut().unwrap();
let opt_prefix = if let Ok(prefix) = prefix_result { let opt_prefix = if let Ok(prefix) = prefix_result {
let prefix = Prefix::from(prefix); let prefix = Prefix::from(prefix);
self.context.namespaces.expect("namespaces must be set whilst parsing rules") namespaces
.write().prefixes.insert(prefix.clone(), (url.clone(), id)); .prefixes
.insert(prefix.clone(), (url.clone(), id));
Some(prefix) Some(prefix)
} else { } else {
self.context.namespaces.expect("namespaces must be set whilst parsing rules") namespaces.default = Some((url.clone(), id));
.write().default = Some((url.clone(), id));
None None
}; };
@ -1623,10 +1666,6 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
source_location: location, source_location: location,
}) })
)))) ))))
} else {
self.state.set(State::Invalid);
return Err(()) // "@namespace must be before any rule but @charset and @import"
}
}, },
// @charset is removed by rust-cssparser if its the first rule in the stylesheet // @charset is removed by rust-cssparser if its the first rule in the stylesheet
// anything left is invalid. // anything left is invalid.
@ -1634,11 +1673,18 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
_ => {} _ => {}
} }
// Don't allow starting with an invalid state // Don't allow starting with an invalid state
if self.state.get() > State::Body { if self.state > State::Body {
self.state.set(State::Invalid); self.state = State::Invalid;
return Err(()); return Err(());
} }
self.state.set(State::Body); self.state = State::Body;
// "Freeze" the namespace map (no more namespace rules can be parsed
// after this point), and stick it in the context.
if self.namespaces.is_some() {
let namespaces = &*self.namespaces.take().unwrap();
self.context.namespaces = Some(namespaces);
}
AtRuleParser::parse_prelude(&mut self.nested(), name, input) AtRuleParser::parse_prelude(&mut self.nested(), name, input)
} }
@ -1655,13 +1701,24 @@ impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
#[inline] #[inline]
fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> { fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
self.state.set(State::Body); self.state = State::Body;
// "Freeze" the namespace map (no more namespace rules can be parsed
// after this point), and stick it in the context.
if self.namespaces.is_some() {
let namespaces = &*self.namespaces.take().unwrap();
self.context.namespaces = Some(namespaces);
}
QualifiedRuleParser::parse_prelude(&mut self.nested(), input) QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
} }
#[inline] #[inline]
fn parse_block(&mut self, prelude: SelectorList<SelectorImpl>, input: &mut Parser) fn parse_block(
-> Result<CssRule, ()> { &mut self,
prelude: SelectorList<SelectorImpl>,
input: &mut Parser
) -> Result<CssRule, ()> {
QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input) QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input)
} }
} }
@ -1674,13 +1731,19 @@ struct NestedRuleParser<'a, 'b: 'a> {
} }
impl<'a, 'b> NestedRuleParser<'a, 'b> { impl<'a, 'b> NestedRuleParser<'a, 'b> {
fn parse_nested_rules(&self, input: &mut Parser, rule_type: CssRuleType) -> Arc<Locked<CssRules>> { fn parse_nested_rules(
&mut self,
input: &mut Parser,
rule_type: CssRuleType
) -> Arc<Locked<CssRules>> {
let context = ParserContext::new_with_rule_type(self.context, Some(rule_type)); let context = ParserContext::new_with_rule_type(self.context, Some(rule_type));
let nested_parser = NestedRuleParser { let nested_parser = NestedRuleParser {
stylesheet_origin: self.stylesheet_origin, stylesheet_origin: self.stylesheet_origin,
shared_lock: self.shared_lock, shared_lock: self.shared_lock,
context: &context, context: &context,
}; };
let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser); let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
let mut rules = Vec::new(); let mut rules = Vec::new();
while let Some(result) = iter.next() { while let Some(result) = iter.next() {
@ -1711,10 +1774,17 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
type Prelude = AtRulePrelude; type Prelude = AtRulePrelude;
type AtRule = CssRule; type AtRule = CssRule;
fn parse_prelude(&mut self, name: &str, input: &mut Parser) fn parse_prelude(
-> Result<AtRuleType<AtRulePrelude, CssRule>, ()> { &mut self,
let location = get_location_with_offset(input.current_source_location(), name: &str,
self.context.line_number_offset); input: &mut Parser
) -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
let location =
get_location_with_offset(
input.current_source_location(),
self.context.line_number_offset
);
match_ignore_ascii_case! { name, match_ignore_ascii_case! { name,
"media" => { "media" => {
let media_queries = parse_media_query_list(self.context, input); let media_queries = parse_media_query_list(self.context, input);
@ -1855,16 +1925,19 @@ impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
type QualifiedRule = CssRule; type QualifiedRule = CssRule;
fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> { fn parse_prelude(&mut self, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
let ns = self.context.namespaces.expect("namespaces must be set when parsing rules").read();
let selector_parser = SelectorParser { let selector_parser = SelectorParser {
stylesheet_origin: self.stylesheet_origin, stylesheet_origin: self.stylesheet_origin,
namespaces: &*ns, namespaces: self.context.namespaces.unwrap(),
}; };
SelectorList::parse(&selector_parser, input) SelectorList::parse(&selector_parser, input)
} }
fn parse_block(&mut self, prelude: SelectorList<SelectorImpl>, input: &mut Parser) fn parse_block(
-> Result<CssRule, ()> { &mut self,
prelude: SelectorList<SelectorImpl>,
input: &mut Parser
) -> Result<CssRule, ()> {
let location = get_location_with_offset(input.current_source_location(), let location = get_location_with_offset(input.current_source_location(),
self.context.line_number_offset); self.context.line_number_offset);
let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style)); let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style));

View file

@ -1311,23 +1311,25 @@ impl Parse for Attr {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
/// Get the namespace id from the namespace map /// Get the namespace id from the namespace map
pub fn get_id_for_namespace(namespace: &Namespace, context: &ParserContext) -> Result<NamespaceId, ()> { fn get_id_for_namespace(namespace: &Namespace, context: &ParserContext) -> Result<NamespaceId, ()> {
if let Some(map) = context.namespaces { let namespaces_map = match context.namespaces {
if let Some(ref entry) = map.read().prefixes.get(&namespace.0) { Some(map) => map,
Ok(entry.1) None => {
} else { // If we don't have a namespace map (e.g. in inline styles)
Err(())
}
} else {
// if we don't have a namespace map (e.g. in inline styles)
// we can't parse namespaces // we can't parse namespaces
Err(()) return Err(());
}
};
match namespaces_map.prefixes.get(&namespace.0) {
Some(entry) => Ok(entry.1),
None => Err(()),
} }
} }
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
/// Get the namespace id from the namespace map /// Get the namespace id from the namespace map
pub fn get_id_for_namespace(_: &Namespace, _: &ParserContext) -> Result<NamespaceId, ()> { fn get_id_for_namespace(_: &Namespace, _: &ParserContext) -> Result<NamespaceId, ()> {
Ok(()) Ok(())
} }
@ -1340,12 +1342,15 @@ impl Attr {
let first = input.try(|i| i.expect_ident()).ok(); let first = input.try(|i| i.expect_ident()).ok();
if let Ok(token) = input.try(|i| i.next_including_whitespace()) { if let Ok(token) = input.try(|i| i.next_including_whitespace()) {
match token { match token {
Token::Delim('|') => { Token::Delim('|') => {}
_ => return Err(()),
}
// must be followed by an ident // must be followed by an ident
let second_token = match input.next_including_whitespace()? { let second_token = match input.next_including_whitespace()? {
Token::Ident(second) => second, Token::Ident(second) => second,
_ => return Err(()), _ => return Err(()),
}; };
let ns_with_id = if let Some(ns) = first { let ns_with_id = if let Some(ns) = first {
let ns: Namespace = ns.into(); let ns: Namespace = ns.into();
let id = get_id_for_namespace(&ns, context)?; let id = get_id_for_namespace(&ns, context)?;
@ -1358,9 +1363,7 @@ impl Attr {
attribute: second_token.into_owned(), attribute: second_token.into_owned(),
}) })
} }
_ => return Err(())
}
}
if let Some(first) = first { if let Some(first) = first {
Ok(Attr { Ok(Attr {
namespace: None, namespace: None,