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 cssparser::{Parser, SourcePosition, UnicodeRange};
use error_reporting::ParseErrorReporter;
use parking_lot::RwLock;
use style_traits::OneOrMoreCommaSeparated;
use stylesheets::{CssRuleType, Origin, UrlExtraData, Namespaces};
@ -82,8 +81,8 @@ pub struct ParserContext<'a> {
pub parsing_mode: ParsingMode,
/// The quirks mode of this stylesheet.
pub quirks_mode: QuirksMode,
/// The list of all namespaces active in the current stylesheet
pub namespaces: Option<&'a RwLock<Namespaces>>,
/// The currently active namespaces.
pub namespaces: Option<&'a Namespaces>,
}
impl<'a> ParserContext<'a> {
@ -108,19 +107,21 @@ impl<'a> ParserContext<'a> {
}
/// 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,
rule_type: Option<CssRuleType>,
parsing_mode: ParsingMode,
quirks_mode: QuirksMode)
-> ParserContext<'a> {
quirks_mode: QuirksMode
) -> ParserContext<'a> {
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.
pub fn new_with_rule_type(context: &'a ParserContext,
rule_type: Option<CssRuleType>)
-> ParserContext<'a> {
pub fn new_with_rule_type(
context: &'a ParserContext,
rule_type: Option<CssRuleType>
) -> ParserContext<'a> {
ParserContext {
stylesheet_origin: context.stylesheet_origin,
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.
pub fn new_with_line_number_offset(stylesheet_origin: Origin,
pub fn new_with_line_number_offset(
stylesheet_origin: Origin,
url_data: &'a UrlExtraData,
error_reporter: &'a ParseErrorReporter,
line_number_offset: u64,
parsing_mode: ParsingMode,
quirks_mode: QuirksMode)
-> ParserContext<'a> {
quirks_mode: QuirksMode
) -> ParserContext<'a> {
ParserContext {
stylesheet_origin: stylesheet_origin,
url_data: url_data,

View file

@ -40,7 +40,6 @@ use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
use smallvec::SmallVec;
use std::{fmt, mem};
use std::borrow::Borrow;
use std::cell::Cell;
use std::mem::align_of;
use std::os::raw::c_void;
use std::slice;
@ -479,15 +478,19 @@ impl CssRule {
loader: Option<&StylesheetLoader>)
-> Result<(Self, State), SingleRuleParseError> {
let error_reporter = NullReporter;
let mut context = ParserContext::new(parent_stylesheet.origin,
let context = ParserContext::new(
parent_stylesheet.origin,
&parent_stylesheet.url_data,
&error_reporter,
None,
PARSING_MODE_DEFAULT,
parent_stylesheet.quirks_mode);
context.namespaces = Some(&parent_stylesheet.namespaces);
parent_stylesheet.quirks_mode
);
let mut input = Parser::new(css);
let mut guard = parent_stylesheet.namespaces.write();
// nested rules are in the body state
let state = state.unwrap_or(State::Body);
let mut rule_parser = TopLevelRuleParser {
@ -495,12 +498,13 @@ impl CssRule {
context: context,
shared_lock: &parent_stylesheet.shared_lock,
loader: loader,
state: Cell::new(state),
state: state,
namespaces: Some(&mut *guard),
};
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(_) => {
if let State::Invalid = rule_parser.state.get() {
if let State::Invalid = rule_parser.state {
Err(SingleRuleParseError::Hierarchy)
} else {
Err(SingleRuleParseError::Syntax)
@ -510,8 +514,10 @@ impl CssRule {
}
/// Deep clones this CssRule.
fn deep_clone_with_lock(&self,
lock: &SharedRwLock) -> CssRule {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock
) -> CssRule {
let guard = lock.read();
match *self {
CssRule::Namespace(ref arc) => {
@ -1215,10 +1221,19 @@ impl Stylesheet {
let namespaces = RwLock::new(Namespaces::default());
// FIXME: we really should update existing.url_data with the given url_data,
// otherwise newly inserted rule may not have the right base url.
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
css, url_data, existing.origin, &namespaces,
&existing.shared_lock, stylesheet_loader, error_reporter,
existing.quirks_mode, line_number_offset);
let (rules, dirty_on_viewport_size_change) =
Stylesheet::parse_rules(
css,
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());
existing.dirty_on_viewport_size_change
.store(dirty_on_viewport_size_change, Ordering::Release);
@ -1228,35 +1243,45 @@ impl Stylesheet {
*existing.rules.write_with(&mut guard) = CssRules(rules);
}
fn parse_rules(css: &str,
fn parse_rules(
css: &str,
url_data: &UrlExtraData,
origin: Origin,
namespaces: &RwLock<Namespaces>,
namespaces: &mut Namespaces,
shared_lock: &SharedRwLock,
stylesheet_loader: Option<&StylesheetLoader>,
error_reporter: &ParseErrorReporter,
quirks_mode: QuirksMode,
line_number_offset: u64)
-> (Vec<CssRule>, bool) {
line_number_offset: u64
) -> (Vec<CssRule>, bool) {
let mut rules = Vec::new();
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,
PARSING_MODE_DEFAULT,
quirks_mode);
context.namespaces = Some(namespaces);
quirks_mode
);
let rule_parser = TopLevelRuleParser {
stylesheet_origin: origin,
shared_lock: shared_lock,
loader: stylesheet_loader,
context: context,
state: Cell::new(State::Start),
state: State::Start,
namespaces: Some(namespaces),
};
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() {
match result {
Ok(rule) => rules.push(rule),
@ -1289,9 +1314,17 @@ impl Stylesheet {
-> Stylesheet {
let namespaces = RwLock::new(Namespaces::default());
let (rules, dirty_on_viewport_size_change) = Stylesheet::parse_rules(
css, &url_data, origin, &namespaces,
&shared_lock, stylesheet_loader, error_reporter, quirks_mode, line_number_offset,
css,
&url_data,
origin,
&mut *namespaces.write(),
&shared_lock,
stylesheet_loader,
error_reporter,
quirks_mode,
line_number_offset,
);
Stylesheet {
origin: origin,
url_data: url_data,
@ -1477,7 +1510,8 @@ struct TopLevelRuleParser<'a> {
shared_lock: &'a SharedRwLock,
loader: Option<&'a StylesheetLoader>,
context: ParserContext<'a>,
state: Cell<State>,
state: State,
namespaces: Option<&'a mut Namespaces>,
}
impl<'b> TopLevelRuleParser<'b> {
@ -1549,14 +1583,21 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
type Prelude = AtRulePrelude;
type AtRule = CssRule;
fn parse_prelude(&mut self, name: &str, input: &mut Parser)
-> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
fn parse_prelude(
&mut self,
name: &str,
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,
"import" => {
if self.state.get() <= State::Imports {
self.state.set(State::Imports);
if self.state > 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 specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
@ -1590,29 +1631,31 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
}, &mut |import_rule| {
Arc::new(self.shared_lock.wrap(import_rule))
});
return Ok(AtRuleType::WithoutBlock(CssRule::Import(arc)))
} else {
self.state.set(State::Invalid);
return Err(()) // "@import must be before any rule but @charset"
}
},
"namespace" => {
if self.state.get() <= State::Namespaces {
self.state.set(State::Namespaces);
if self.state > 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 url = Namespace::from(try!(input.expect_url_or_string()));
let id = register_namespace(&url)?;
let mut namespaces = self.namespaces.as_mut().unwrap();
let opt_prefix = if let Ok(prefix) = prefix_result {
let prefix = Prefix::from(prefix);
self.context.namespaces.expect("namespaces must be set whilst parsing rules")
.write().prefixes.insert(prefix.clone(), (url.clone(), id));
namespaces
.prefixes
.insert(prefix.clone(), (url.clone(), id));
Some(prefix)
} else {
self.context.namespaces.expect("namespaces must be set whilst parsing rules")
.write().default = Some((url.clone(), id));
namespaces.default = Some((url.clone(), id));
None
};
@ -1623,10 +1666,6 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
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
// anything left is invalid.
@ -1634,11 +1673,18 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
_ => {}
}
// Don't allow starting with an invalid state
if self.state.get() > State::Body {
self.state.set(State::Invalid);
if self.state > State::Body {
self.state = State::Invalid;
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)
}
@ -1655,13 +1701,24 @@ impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
#[inline]
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)
}
#[inline]
fn parse_block(&mut self, prelude: SelectorList<SelectorImpl>, input: &mut Parser)
-> Result<CssRule, ()> {
fn parse_block(
&mut self,
prelude: SelectorList<SelectorImpl>,
input: &mut Parser
) -> Result<CssRule, ()> {
QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input)
}
}
@ -1674,13 +1731,19 @@ struct NestedRuleParser<'a, 'b: 'a> {
}
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 nested_parser = NestedRuleParser {
stylesheet_origin: self.stylesheet_origin,
shared_lock: self.shared_lock,
context: &context,
};
let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
let mut rules = Vec::new();
while let Some(result) = iter.next() {
@ -1711,10 +1774,17 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
type Prelude = AtRulePrelude;
type AtRule = CssRule;
fn parse_prelude(&mut self, name: &str, input: &mut Parser)
-> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
let location = get_location_with_offset(input.current_source_location(),
self.context.line_number_offset);
fn parse_prelude(
&mut self,
name: &str,
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,
"media" => {
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;
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 {
stylesheet_origin: self.stylesheet_origin,
namespaces: &*ns,
namespaces: self.context.namespaces.unwrap(),
};
SelectorList::parse(&selector_parser, input)
}
fn parse_block(&mut self, prelude: SelectorList<SelectorImpl>, input: &mut Parser)
-> Result<CssRule, ()> {
fn parse_block(
&mut self,
prelude: SelectorList<SelectorImpl>,
input: &mut Parser
) -> Result<CssRule, ()> {
let location = get_location_with_offset(input.current_source_location(),
self.context.line_number_offset);
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")]
/// Get the namespace id from the namespace map
pub fn get_id_for_namespace(namespace: &Namespace, context: &ParserContext) -> Result<NamespaceId, ()> {
if let Some(map) = context.namespaces {
if let Some(ref entry) = map.read().prefixes.get(&namespace.0) {
Ok(entry.1)
} else {
Err(())
}
} else {
// if we don't have a namespace map (e.g. in inline styles)
fn get_id_for_namespace(namespace: &Namespace, context: &ParserContext) -> Result<NamespaceId, ()> {
let namespaces_map = match context.namespaces {
Some(map) => map,
None => {
// If we don't have a namespace map (e.g. in inline styles)
// 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")]
/// 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(())
}
@ -1340,12 +1342,15 @@ impl Attr {
let first = input.try(|i| i.expect_ident()).ok();
if let Ok(token) = input.try(|i| i.next_including_whitespace()) {
match token {
Token::Delim('|') => {
Token::Delim('|') => {}
_ => return Err(()),
}
// must be followed by an ident
let second_token = match input.next_including_whitespace()? {
Token::Ident(second) => second,
_ => return Err(()),
};
let ns_with_id = if let Some(ns) = first {
let ns: Namespace = ns.into();
let id = get_id_for_namespace(&ns, context)?;
@ -1358,9 +1363,7 @@ impl Attr {
attribute: second_token.into_owned(),
})
}
_ => return Err(())
}
}
if let Some(first) = first {
Ok(Attr {
namespace: None,