Parse at-rule without block in two stages

This commit is contained in:
Xidorn Quan 2017-09-01 11:54:48 +10:00
parent 09df9c4316
commit 2bca62045f
19 changed files with 133 additions and 106 deletions

View file

@ -111,8 +111,8 @@ pub enum VendorPrefix {
WebKit,
}
/// A rule prelude for a given at-rule.
pub enum AtRulePrelude {
/// A rule prelude for at-rule with block.
pub enum AtRuleBlockPrelude {
/// A @font-face rule prelude.
FontFace(SourceLocation),
/// A @font-feature-values rule prelude, with its FamilyName list.
@ -133,25 +133,31 @@ pub enum AtRulePrelude {
Document(DocumentCondition, SourceLocation),
}
/// A rule prelude for at-rule without block.
pub enum AtRuleNonBlockPrelude {
/// A @import rule prelude.
Import(SpecifiedUrl, Arc<Locked<MediaList>>, SourceLocation),
/// A @namespace rule prelude.
Namespace(Option<Prefix>, Namespace, SourceLocation),
}
#[cfg(feature = "gecko")]
fn register_namespace(ns: &Namespace) -> Result<i32, ()> {
fn register_namespace(ns: &Namespace) -> i32 {
use gecko_bindings::bindings;
let id = unsafe { bindings::Gecko_RegisterNamespace(ns.0.as_ptr()) };
if id == -1 {
Err(())
} else {
Ok(id)
}
debug_assert!(id >= 0);
id
}
#[cfg(feature = "servo")]
fn register_namespace(_: &Namespace) -> Result<(), ()> {
Ok(()) // servo doesn't use namespace ids
fn register_namespace(_: &Namespace) {
// servo doesn't use namespace ids
}
impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, R> {
type Prelude = AtRulePrelude;
type PreludeNoBlock = AtRuleNonBlockPrelude;
type PreludeBlock = AtRuleBlockPrelude;
type AtRule = CssRule;
type Error = SelectorParseError<'i, StyleParseError<'i>>;
@ -159,7 +165,7 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>
) -> Result<AtRuleType<AtRulePrelude, CssRule>, ParseError<'i>> {
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
let location = get_location_with_offset(input.current_source_location());
match_ignore_ascii_case! { &*name,
"import" => {
@ -175,19 +181,8 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
let media = parse_media_query_list(&self.context, input);
let media = Arc::new(self.shared_lock.wrap(media));
let loader =
self.loader.expect("Expected a stylesheet loader for @import");
let import_rule = loader.request_stylesheet(
specified_url,
location,
&self.context,
&self.shared_lock,
media,
);
self.state = State::Imports;
return Ok(AtRuleType::WithoutBlock(CssRule::Import(import_rule)))
let prelude = AtRuleNonBlockPrelude::Import(specified_url, media, location);
return Ok(AtRuleType::WithoutBlock(prelude));
},
"namespace" => {
if self.state > State::Namespaces {
@ -196,7 +191,8 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
return Err(StyleParseError::UnexpectedNamespaceRule.into())
}
let prefix_result = input.try(|i| i.expect_ident_cloned());
let prefix = input.try(|i| i.expect_ident_cloned())
.map(|s| Prefix::from(s.as_ref())).ok();
let maybe_namespace = match input.expect_url_or_string() {
Ok(url_or_string) => url_or_string,
Err(BasicParseError::UnexpectedToken(t)) =>
@ -204,29 +200,8 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
Err(e) => return Err(e.into()),
};
let url = Namespace::from(maybe_namespace.as_ref());
let id = register_namespace(&url)
.map_err(|()| StyleParseError::UnspecifiedError)?;
let opt_prefix = if let Ok(prefix) = prefix_result {
let prefix = Prefix::from(prefix.as_ref());
self.namespaces
.prefixes
.insert(prefix.clone(), (url.clone(), id));
Some(prefix)
} else {
self.namespaces.default = Some((url.clone(), id));
None
};
self.state = State::Namespaces;
return Ok(AtRuleType::WithoutBlock(CssRule::Namespace(Arc::new(
self.shared_lock.wrap(NamespaceRule {
prefix: opt_prefix,
url: url,
source_location: location,
})
))))
let prelude = AtRuleNonBlockPrelude::Namespace(prefix, url, location);
return Ok(AtRuleType::WithoutBlock(prelude));
},
// @charset is removed by rust-cssparser if its the first rule in the stylesheet
// anything left is invalid.
@ -243,12 +218,55 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
#[inline]
fn parse_block<'t>(
&mut self,
prelude: AtRulePrelude,
prelude: AtRuleBlockPrelude,
input: &mut Parser<'i, 't>
) -> Result<CssRule, ParseError<'i>> {
AtRuleParser::parse_block(&mut self.nested(), prelude, input)
.map(|rule| { self.state = State::Body; rule })
}
#[inline]
fn rule_without_block(&mut self, prelude: AtRuleNonBlockPrelude) -> CssRule {
match prelude {
AtRuleNonBlockPrelude::Import(specified_url, media, location) => {
let loader =
self.loader.expect("Expected a stylesheet loader for @import");
let import_rule = loader.request_stylesheet(
specified_url,
location,
&self.context,
&self.shared_lock,
media,
);
self.state = State::Imports;
CssRule::Import(import_rule)
}
AtRuleNonBlockPrelude::Namespace(prefix, url, location) => {
let id = register_namespace(&url);
let opt_prefix = if let Some(prefix) = prefix {
self.namespaces
.prefixes
.insert(prefix.clone(), (url.clone(), id));
Some(prefix)
} else {
self.namespaces.default = Some((url.clone(), id));
None
};
self.state = State::Namespaces;
CssRule::Namespace(Arc::new(
self.shared_lock.wrap(NamespaceRule {
prefix: opt_prefix,
url: url,
source_location: location,
})
))
}
}
}
}
pub struct QualifiedRuleParserPrelude {
@ -322,7 +340,8 @@ impl<'a, 'b, R: ParseErrorReporter> NestedRuleParser<'a, 'b, R> {
}
impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a, 'b, R> {
type Prelude = AtRulePrelude;
type PreludeNoBlock = AtRuleNonBlockPrelude;
type PreludeBlock = AtRuleBlockPrelude;
type AtRule = CssRule;
type Error = SelectorParseError<'i, StyleParseError<'i>>;
@ -330,21 +349,21 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>
) -> Result<AtRuleType<AtRulePrelude, CssRule>, ParseError<'i>> {
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
let location = get_location_with_offset(input.current_source_location());
match_ignore_ascii_case! { &*name,
"media" => {
let media_queries = parse_media_query_list(self.context, input);
let arc = Arc::new(self.shared_lock.wrap(media_queries));
Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc, location)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
},
"supports" => {
let cond = SupportsCondition::parse(input)?;
Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond, location)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond, location)))
},
"font-face" => {
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace(location)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFace(location)))
},
"font-feature-values" => {
if !cfg!(feature = "gecko") {
@ -352,7 +371,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
return Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
}
let family_names = parse_family_name_list(self.context, input)?;
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFeatureValues(family_names, location)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFeatureValues(family_names, location)))
},
"counter-style" => {
if !cfg!(feature = "gecko") {
@ -366,11 +385,11 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
if name.0 == atom!("decimal") || name.0 == atom!("disc") {
return Err(StyleParseError::UnspecifiedError.into())
}
Ok(AtRuleType::WithBlock(AtRulePrelude::CounterStyle(name)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::CounterStyle(name)))
},
"viewport" => {
if viewport_rule::enabled() {
Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Viewport))
} else {
Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
}
@ -390,11 +409,11 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
}
let name = KeyframesName::parse(self.context, input)?;
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix, location)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Keyframes(name, prefix, location)))
},
"page" => {
if cfg!(feature = "gecko") {
Ok(AtRuleType::WithBlock(AtRulePrelude::Page(location)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Page(location)))
} else {
Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
}
@ -402,7 +421,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
"-moz-document" => {
if cfg!(feature = "gecko") {
let cond = DocumentCondition::parse(self.context, input)?;
Ok(AtRuleType::WithBlock(AtRulePrelude::Document(cond, location)))
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Document(cond, location)))
} else {
Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
}
@ -413,11 +432,11 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
fn parse_block<'t>(
&mut self,
prelude: AtRulePrelude,
prelude: AtRuleBlockPrelude,
input: &mut Parser<'i, 't>
) -> Result<CssRule, ParseError<'i>> {
match prelude {
AtRulePrelude::FontFace(location) => {
AtRuleBlockPrelude::FontFace(location) => {
let context =
ParserContext::new_with_rule_type(
self.context,
@ -428,7 +447,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
parse_font_face_block(&context, self.error_context, input, location).into()))))
}
AtRulePrelude::FontFeatureValues(family_names, location) => {
AtRuleBlockPrelude::FontFeatureValues(family_names, location) => {
let context =
ParserContext::new_with_rule_type(
self.context,
@ -438,7 +457,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
Ok(CssRule::FontFeatureValues(Arc::new(self.shared_lock.wrap(
FontFeatureValuesRule::parse(&context, self.error_context, input, family_names, location)))))
}
AtRulePrelude::CounterStyle(name) => {
AtRuleBlockPrelude::CounterStyle(name) => {
let context =
ParserContext::new_with_rule_type(
self.context,
@ -448,14 +467,14 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(
parse_counter_style_body(name, &context, self.error_context, input)?.into()))))
}
AtRulePrelude::Media(media_queries, location) => {
AtRuleBlockPrelude::Media(media_queries, location) => {
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
media_queries: media_queries,
rules: self.parse_nested_rules(input, CssRuleType::Media),
source_location: location,
}))))
}
AtRulePrelude::Supports(cond, location) => {
AtRuleBlockPrelude::Supports(cond, location) => {
let eval_context =
ParserContext::new_with_rule_type(
self.context,
@ -470,7 +489,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
source_location: location,
}))))
}
AtRulePrelude::Viewport => {
AtRuleBlockPrelude::Viewport => {
let context =
ParserContext::new_with_rule_type(
self.context,
@ -480,7 +499,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
ViewportRule::parse(&context, self.error_context, input)?))))
}
AtRulePrelude::Keyframes(name, prefix, location) => {
AtRuleBlockPrelude::Keyframes(name, prefix, location) => {
let context =
ParserContext::new_with_rule_type(
self.context,
@ -495,7 +514,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
source_location: location,
}))))
}
AtRulePrelude::Page(location) => {
AtRuleBlockPrelude::Page(location) => {
let context =
ParserContext::new_with_rule_type(
self.context,
@ -508,7 +527,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
source_location: location,
}))))
}
AtRulePrelude::Document(cond, location) => {
AtRuleBlockPrelude::Document(cond, location) => {
if cfg!(feature = "gecko") {
Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(DocumentRule {
condition: cond,