style: [css-nesting] Do a first pass at parsing nested rules mixed with declarations

Plumb everything up. This factors out declaration and rule parsing so
we share the code with the regular declaration parser.

This could be made a bit nicer in the future. We need to decide what to
do for @page and @keyframe (it seems conditional rules inside ought to
work, but that's not so easy because per spec we create a nested style
rule).

But this is a first pass that passes a good chunk of the tests. There
are other fixups to cssom, and I think some of the tests we fail are
actually wrong...

Differential Revision: https://phabricator.services.mozilla.com/D178266
This commit is contained in:
Emilio Cobos Álvarez 2023-05-17 18:18:29 +00:00 committed by Martin Robinson
parent b92440ef7c
commit 7c4ec6e9cc
13 changed files with 523 additions and 286 deletions

View file

@ -392,6 +392,11 @@ enum ParseRelative {
} }
impl<Impl: SelectorImpl> SelectorList<Impl> { impl<Impl: SelectorImpl> SelectorList<Impl> {
/// Returns a selector list with a single `&`
pub fn ampersand() -> Self {
Self(smallvec::smallvec![Selector::ampersand()])
}
/// Parse a comma-separated list of Selectors. /// Parse a comma-separated list of Selectors.
/// <https://drafts.csswg.org/selectors/#grouping> /// <https://drafts.csswg.org/selectors/#grouping>
/// ///
@ -647,6 +652,18 @@ pub struct Selector<Impl: SelectorImpl>(
); );
impl<Impl: SelectorImpl> Selector<Impl> { impl<Impl: SelectorImpl> Selector<Impl> {
/// See Arc::mark_as_intentionally_leaked
pub fn mark_as_intentionally_leaked(&self) {
self.0.with_arc(|a| a.mark_as_intentionally_leaked())
}
fn ampersand() -> Self {
Self(ThinArc::from_header_and_iter(SpecificityAndFlags {
specificity: 0,
flags: SelectorFlags::HAS_PARENT,
}, std::iter::once(Component::ParentSelector)))
}
#[inline] #[inline]
pub fn specificity(&self) -> u32 { pub fn specificity(&self) -> u32 {
self.0.header.header.specificity() self.0.header.header.specificity()

View file

@ -311,6 +311,10 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
type Impl = SelectorImpl; type Impl = SelectorImpl;
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
fn parse_parent_selector(&self) -> bool {
static_prefs::pref!("layout.css.nesting.enabled")
}
#[inline] #[inline]
fn parse_slotted(&self) -> bool { fn parse_slotted(&self) -> bool {
true true

View file

@ -6,7 +6,7 @@
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
use crate::stylesheets::{CssRuleType, Namespaces, Origin, UrlExtraData}; use crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData};
use crate::use_counters::UseCounters; use crate::use_counters::UseCounters;
use cssparser::{Parser, SourceLocation, UnicodeRange}; use cssparser::{Parser, SourceLocation, UnicodeRange};
use std::borrow::Cow; use std::borrow::Cow;
@ -45,8 +45,8 @@ pub struct ParserContext<'a> {
pub stylesheet_origin: Origin, pub stylesheet_origin: Origin,
/// The extra data we need for resolving url values. /// The extra data we need for resolving url values.
pub url_data: &'a UrlExtraData, pub url_data: &'a UrlExtraData,
/// The current rule type, if any. /// The current rule types, if any.
pub rule_type: Option<CssRuleType>, pub rule_types: CssRuleTypes,
/// The mode to use when parsing. /// The mode to use when parsing.
pub parsing_mode: ParsingMode, pub parsing_mode: ParsingMode,
/// The quirks mode of this stylesheet. /// The quirks mode of this stylesheet.
@ -75,7 +75,7 @@ impl<'a> ParserContext<'a> {
Self { Self {
stylesheet_origin, stylesheet_origin,
url_data, url_data,
rule_type, rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(),
parsing_mode, parsing_mode,
quirks_mode, quirks_mode,
error_reporter, error_reporter,
@ -86,23 +86,22 @@ impl<'a> ParserContext<'a> {
/// Temporarily sets the rule_type and executes the callback function, returning its result. /// Temporarily sets the rule_type and executes the callback function, returning its result.
pub fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R { pub fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R {
let old_rule_type = std::mem::replace(&mut self.rule_type, Some(rule_type)); let old_rule_types = self.rule_types;
self.rule_types.insert(rule_type);
let r = cb(self); let r = cb(self);
self.rule_type = old_rule_type; self.rule_types = old_rule_types;
r r
} }
/// Whether we're in a @page rule. /// Whether we're in a @page rule.
#[inline] #[inline]
pub fn in_page_rule(&self) -> bool { pub fn in_page_rule(&self) -> bool {
self.rule_type self.rule_types.contains(CssRuleType::Page)
.map_or(false, |rule_type| rule_type == CssRuleType::Page)
} }
/// Get the rule type, which assumes that one is available. /// Get the rule type, which assumes that one is available.
pub fn rule_type(&self) -> CssRuleType { pub fn rule_types(&self) -> CssRuleTypes {
self.rule_type self.rule_types
.expect("Rule type expected, but none was found.")
} }
/// Returns whether CSS error reporting is enabled. /// Returns whether CSS error reporting is enabled.

View file

@ -92,12 +92,18 @@ pub enum Importance {
Important, Important,
} }
impl Default for Importance {
fn default() -> Self {
Self::Normal
}
}
impl Importance { impl Importance {
/// Return whether this is an important declaration. /// Return whether this is an important declaration.
pub fn important(self) -> bool { pub fn important(self) -> bool {
match self { match self {
Importance::Normal => false, Self::Normal => false,
Importance::Important => true, Self::Important => true,
} }
} }
} }
@ -146,7 +152,7 @@ impl PropertyDeclarationIdSet {
/// Overridden declarations are skipped. /// Overridden declarations are skipped.
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
#[derive(Clone, ToShmem)] #[derive(Clone, ToShmem, Default)]
pub struct PropertyDeclarationBlock { pub struct PropertyDeclarationBlock {
/// The group of declarations, along with their importance. /// The group of declarations, along with their importance.
/// ///
@ -284,6 +290,12 @@ impl PropertyDeclarationBlock {
self.declarations.len() self.declarations.len()
} }
/// Returns whether the block is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.declarations.is_empty()
}
/// Create an empty block /// Create an empty block
#[inline] #[inline]
pub fn new() -> Self { pub fn new() -> Self {
@ -1357,24 +1369,127 @@ pub fn parse_one_declaration_into(
} }
/// A struct to parse property declarations. /// A struct to parse property declarations.
struct PropertyDeclarationParser<'a, 'b: 'a> { struct PropertyDeclarationParser<'a, 'b: 'a, 'i> {
context: &'a ParserContext<'b>, context: &'a ParserContext<'b>,
declarations: &'a mut SourcePropertyDeclaration, state: &'a mut DeclarationParserState<'i>,
/// The last parsed property id if any. }
/// The state needed to parse a declaration block.
///
/// It stores declarations in output_block.
#[derive(Default)]
pub struct DeclarationParserState<'i> {
/// The output block where results are stored.
output_block: PropertyDeclarationBlock,
/// Declarations from the last declaration parsed. (note that a shorthand might expand to
/// multiple declarations).
declarations: SourcePropertyDeclaration,
/// The importance from the last declaration parsed.
importance: Importance,
/// A list of errors that have happened so far. Not all of them might be reported.
errors: SmallParseErrorVec<'i>,
/// The last parsed property id, if any.
last_parsed_property_id: Option<PropertyId>, last_parsed_property_id: Option<PropertyId>,
} }
impl<'i> DeclarationParserState<'i> {
/// Returns whether any parsed declarations have been parsed so far.
pub fn has_parsed_declarations(&self) -> bool {
!self.output_block.is_empty()
}
/// Takes the parsed declarations.
pub fn take_declarations(&mut self) -> PropertyDeclarationBlock {
std::mem::take(&mut self.output_block)
}
/// Parse a single declaration value.
pub fn parse_value<'t>(
&mut self,
context: &ParserContext,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>> {
let id = match PropertyId::parse(&name, context) {
Ok(id) => id,
Err(..) => {
return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name)));
},
};
if context.error_reporting_enabled() {
self.last_parsed_property_id = Some(id.clone());
}
input.parse_until_before(Delimiter::Bang, |input| {
PropertyDeclaration::parse_into(&mut self.declarations, id, context, input)
})?;
self.importance = match input.try_parse(parse_important) {
Ok(()) => Importance::Important,
Err(_) => Importance::Normal,
};
// In case there is still unparsed text in the declaration, we should roll back.
input.expect_exhausted()?;
self.output_block.extend(self.declarations.drain(), self.importance);
// We've successfully parsed a declaration, so forget about
// `last_parsed_property_id`. It'd be wrong to associate any
// following error with this property.
self.last_parsed_property_id = None;
Ok(())
}
/// Reports any CSS errors that have ocurred if needed.
#[inline]
pub fn report_errors_if_needed(
&mut self,
context: &ParserContext,
selectors: Option<&SelectorList<SelectorImpl>>,
) {
if self.errors.is_empty() {
return;
}
self.do_report_css_errors(context, selectors);
}
#[cold]
fn do_report_css_errors(
&mut self,
context: &ParserContext,
selectors: Option<&SelectorList<SelectorImpl>>,
) {
for (error, slice, property) in self.errors.drain(..) {
report_one_css_error(
context,
Some(&self.output_block),
selectors,
error,
slice,
property,
)
}
}
/// Resets the declaration parser state, and reports the error if needed.
#[inline]
pub fn did_error(&mut self, context: &ParserContext, error: ParseError<'i>, slice: &'i str) {
self.declarations.clear();
if !context.error_reporting_enabled() {
return;
}
let property = self.last_parsed_property_id.take();
self.errors.push((error, slice, property));
}
}
/// Default methods reject all at rules. /// Default methods reject all at rules.
impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
type Prelude = (); type Prelude = ();
type AtRule = Importance; type AtRule = ();
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
} }
/// Default methods reject all rules. /// Default methods reject all rules.
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
type Prelude = (); type Prelude = ();
type QualifiedRule = Importance; type QualifiedRule = ();
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
} }
@ -1383,41 +1498,29 @@ fn is_non_mozilla_vendor_identifier(name: &str) -> bool {
(name.starts_with("-") && !name.starts_with("-moz-")) || name.starts_with("_") (name.starts_with("-") && !name.starts_with("-moz-")) || name.starts_with("_")
} }
impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
type Declaration = Importance; type Declaration = ();
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
fn parse_value<'t>( fn parse_value<'t>(
&mut self, &mut self,
name: CowRcStr<'i>, name: CowRcStr<'i>,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Importance, ParseError<'i>> { ) -> Result<(), ParseError<'i>> {
let id = match PropertyId::parse(&name, self.context) { self.state.parse_value(self.context, name, input)
Ok(id) => id,
Err(..) => {
return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name)));
},
};
if self.context.error_reporting_enabled() {
self.last_parsed_property_id = Some(id.clone());
}
input.parse_until_before(Delimiter::Bang, |input| {
PropertyDeclaration::parse_into(self.declarations, id, self.context, input)
})?;
let importance = match input.try_parse(parse_important) {
Ok(()) => Importance::Important,
Err(_) => Importance::Normal,
};
// In case there is still unparsed text in the declaration, we should roll back.
input.expect_exhausted()?;
Ok(importance)
} }
} }
impl<'a, 'b, 'i> RuleBodyItemParser<'i, Importance, StyleParseErrorKind<'i>> for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
fn parse_declarations(&self) -> bool { true } for PropertyDeclarationParser<'a, 'b, 'i>
{
fn parse_declarations(&self) -> bool {
true
}
// TODO(emilio): Nesting. // TODO(emilio): Nesting.
fn parse_qualified(&self) -> bool { false } fn parse_qualified(&self) -> bool {
false
}
} }
type SmallParseErrorVec<'i> = SmallVec<[(ParseError<'i>, &'i str, Option<PropertyId>); 2]>; type SmallParseErrorVec<'i> = SmallVec<[(ParseError<'i>, &'i str, Option<PropertyId>); 2]>;
@ -1486,18 +1589,6 @@ fn report_one_css_error<'i>(
context.log_css_error(location, error); context.log_css_error(location, error);
} }
#[cold]
fn report_css_errors(
context: &ParserContext,
block: &PropertyDeclarationBlock,
selectors: Option<&SelectorList<SelectorImpl>>,
errors: &mut SmallParseErrorVec,
) {
for (error, slice, property) in errors.drain(..) {
report_one_css_error(context, Some(block), selectors, error, slice, property)
}
}
/// Parse a list of property declarations and return a property declaration /// Parse a list of property declarations and return a property declaration
/// block. /// block.
pub fn parse_property_declaration_list( pub fn parse_property_declaration_list(
@ -1505,38 +1596,18 @@ pub fn parse_property_declaration_list(
input: &mut Parser, input: &mut Parser,
selectors: Option<&SelectorList<SelectorImpl>>, selectors: Option<&SelectorList<SelectorImpl>>,
) -> PropertyDeclarationBlock { ) -> PropertyDeclarationBlock {
let mut declarations = SourcePropertyDeclaration::new(); let mut state = DeclarationParserState::default();
let mut block = PropertyDeclarationBlock::new();
let mut parser = PropertyDeclarationParser { let mut parser = PropertyDeclarationParser {
context, context,
last_parsed_property_id: None, state: &mut state,
declarations: &mut declarations,
}; };
let mut iter = RuleBodyParser::new(input, &mut parser); let mut iter = RuleBodyParser::new(input, &mut parser);
let mut errors = SmallParseErrorVec::new();
while let Some(declaration) = iter.next() { while let Some(declaration) = iter.next() {
match declaration { match declaration {
Ok(importance) => { Ok(()) => {},
block.extend(iter.parser.declarations.drain(), importance); Err((error, slice)) => iter.parser.state.did_error(context, error, slice),
// We've successfully parsed a declaration, so forget about
// `last_parsed_property_id`. It'd be wrong to associate any
// following error with this property.
iter.parser.last_parsed_property_id = None;
},
Err((error, slice)) => {
iter.parser.declarations.clear();
if context.error_reporting_enabled() {
let property = iter.parser.last_parsed_property_id.take();
errors.push((error, slice, property));
}
},
} }
} }
parser.state.report_errors_if_needed(context, selectors);
if !errors.is_empty() { state.output_block
report_css_errors(context, &block, selectors, &mut errors)
}
block
} }

View file

@ -37,7 +37,7 @@ use crate::selector_parser::PseudoElement;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
use to_shmem::impl_trivial_to_shmem; use to_shmem::impl_trivial_to_shmem;
use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin, UrlExtraData};
use crate::use_counters::UseCounters; use crate::use_counters::UseCounters;
use crate::values::generics::text::LineHeight; use crate::values::generics::text::LineHeight;
use crate::values::{computed, resolved}; use crate::values::{computed, resolved};
@ -572,30 +572,29 @@ impl NonCustomPropertyId {
/// Returns whether a given rule allows a given property. /// Returns whether a given rule allows a given property.
#[inline] #[inline]
pub fn allowed_in_rule(self, rule_type: CssRuleType) -> bool { pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool {
debug_assert!( debug_assert!(
matches!( rule_types.contains(CssRuleType::Keyframe) ||
rule_type, rule_types.contains(CssRuleType::Page) ||
CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style rule_types.contains(CssRuleType::Style),
),
"Declarations are only expected inside a keyframe, page, or style rule." "Declarations are only expected inside a keyframe, page, or style rule."
); );
static MAP: [u8; NON_CUSTOM_PROPERTY_ID_COUNT] = [ static MAP: [u32; NON_CUSTOM_PROPERTY_ID_COUNT] = [
% for property in data.longhands + data.shorthands + data.all_aliases(): % for property in data.longhands + data.shorthands + data.all_aliases():
${property.rule_types_allowed}, % for name in RULE_VALUES:
% if property.rule_types_allowed & RULE_VALUES[name] != 0:
CssRuleType::${name}.bit() |
% endif
% endfor
0,
% endfor % endfor
]; ];
match rule_type { MAP[self.0] & rule_types.bits() != 0
% for name in RULE_VALUES:
CssRuleType::${name} => MAP[self.0] & ${RULE_VALUES[name]} != 0,
% endfor
_ => true
}
} }
fn allowed_in(self, context: &ParserContext) -> bool { fn allowed_in(self, context: &ParserContext) -> bool {
if !self.allowed_in_rule(context.rule_type()) { if !self.allowed_in_rule(context.rule_types()) {
return false; return false;
} }
@ -728,7 +727,7 @@ impl From<AliasId> for NonCustomPropertyId {
} }
/// A set of all properties /// A set of all properties
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Default)]
pub struct NonCustomPropertyIdSet { pub struct NonCustomPropertyIdSet {
storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32] storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32]
} }
@ -1792,7 +1791,7 @@ impl UnparsedValue {
Some(shorthand) => shorthand, Some(shorthand) => shorthand,
}; };
let mut decls = SourcePropertyDeclaration::new(); let mut decls = SourcePropertyDeclaration::default();
// parse_into takes care of doing `parse_entirely` for us. // parse_into takes care of doing `parse_entirely` for us.
if shorthand.parse_into(&mut decls, &context, &mut input).is_err() { if shorthand.parse_into(&mut decls, &context, &mut input).is_err() {
return invalid_at_computed_value_time(); return invalid_at_computed_value_time();
@ -2606,6 +2605,7 @@ pub type SubpropertiesVec<T> = ArrayVec<T, SUB_PROPERTIES_ARRAY_CAP>;
/// A stack-allocated vector of `PropertyDeclaration` /// A stack-allocated vector of `PropertyDeclaration`
/// large enough to parse one CSS `key: value` declaration. /// large enough to parse one CSS `key: value` declaration.
/// (Shorthands expand to multiple `PropertyDeclaration`s.) /// (Shorthands expand to multiple `PropertyDeclaration`s.)
#[derive(Default)]
pub struct SourcePropertyDeclaration { pub struct SourcePropertyDeclaration {
/// The storage for the actual declarations (except for all). /// The storage for the actual declarations (except for all).
pub declarations: SubpropertiesVec<PropertyDeclaration>, pub declarations: SubpropertiesVec<PropertyDeclaration>,
@ -2618,19 +2618,10 @@ pub struct SourcePropertyDeclaration {
size_of_test!(SourcePropertyDeclaration, 568); size_of_test!(SourcePropertyDeclaration, 568);
impl SourcePropertyDeclaration { impl SourcePropertyDeclaration {
/// Create one. Its big, try not to move it around.
#[inline]
pub fn new() -> Self {
SourcePropertyDeclaration {
declarations: ::arrayvec::ArrayVec::new(),
all_shorthand: AllShorthand::NotSet,
}
}
/// Create one with a single PropertyDeclaration. /// Create one with a single PropertyDeclaration.
#[inline] #[inline]
pub fn with_one(decl: PropertyDeclaration) -> Self { pub fn with_one(decl: PropertyDeclaration) -> Self {
let mut result = Self::new(); let mut result = Self::default();
result.declarations.push(decl); result.declarations.push(decl);
result result
} }
@ -2677,6 +2668,12 @@ pub enum AllShorthand {
WithVariables(Arc<UnparsedValue>) WithVariables(Arc<UnparsedValue>)
} }
impl Default for AllShorthand {
fn default() -> Self {
Self::NotSet
}
}
impl AllShorthand { impl AllShorthand {
/// Iterates property declarations from the given all shorthand value. /// Iterates property declarations from the given all shorthand value.
#[inline] #[inline]

View file

@ -13,7 +13,7 @@ use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter; use crate::str::CssStringWriter;
use crate::stylesheets::CssRules; use crate::stylesheets::CssRules;
use crate::values::CssUrl; use crate::values::CssUrl;
use cssparser::{Parser, SourceLocation}; use cssparser::{Parser, SourceLocation, BasicParseErrorKind};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use servo_arc::Arc; use servo_arc::Arc;
@ -260,11 +260,9 @@ impl DocumentCondition {
let condition = DocumentCondition(conditions); let condition = DocumentCondition(conditions);
if !condition.allowed_in(context) { if !condition.allowed_in(context) {
return Err( return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(
input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule( "-moz-document".into(),
"-moz-document".into(), )));
)),
);
} }
Ok(condition) Ok(condition)
} }

View file

@ -410,7 +410,7 @@ macro_rules! font_feature_values_blocks {
_: &ParserState, _: &ParserState,
input: &mut Parser<'i, 't> input: &mut Parser<'i, 't>
) -> Result<Self::AtRule, ParseError<'i>> { ) -> Result<Self::AtRule, ParseError<'i>> {
debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues); debug_assert!(self.context.rule_types().contains(CssRuleType::FontFeatureValues));
match prelude { match prelude {
$( $(
BlockType::$ident_camel => { BlockType::$ident_camel => {

View file

@ -225,7 +225,7 @@ impl Keyframe {
let mut input = ParserInput::new(css); let mut input = ParserInput::new(css);
let mut input = Parser::new(&mut input); let mut input = Parser::new(&mut input);
let mut declarations = SourcePropertyDeclaration::new(); let mut declarations = SourcePropertyDeclaration::default();
let mut rule_parser = KeyframeListParser { let mut rule_parser = KeyframeListParser {
context: &mut context, context: &mut context,
shared_lock: &lock, shared_lock: &lock,
@ -539,7 +539,7 @@ pub fn parse_keyframe_list<'a>(
input: &mut Parser, input: &mut Parser,
shared_lock: &SharedRwLock, shared_lock: &SharedRwLock,
) -> Vec<Arc<Locked<Keyframe>>> { ) -> Vec<Arc<Locked<Keyframe>>> {
let mut declarations = SourcePropertyDeclaration::new(); let mut declarations = SourcePropertyDeclaration::default();
let mut parser = KeyframeListParser { let mut parser = KeyframeListParser {
context, context,
shared_lock, shared_lock,

View file

@ -352,6 +352,43 @@ pub enum CssRuleType {
FontPaletteValues = 19, FontPaletteValues = 19,
} }
impl CssRuleType {
/// Returns a bit that identifies this rule type.
#[inline]
pub const fn bit(self) -> u32 {
1 << self as u32
}
}
/// Set of rule types.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CssRuleTypes(u32);
impl From<CssRuleType> for CssRuleTypes {
fn from(ty: CssRuleType) -> Self {
Self(ty.bit())
}
}
impl CssRuleTypes {
/// Returns whether the rule is in the current set.
#[inline]
pub fn contains(self, ty: CssRuleType) -> bool {
self.0 & ty.bit() != 0
}
/// Returns all the rules specified in the set.
pub fn bits(self) -> u32 {
self.0
}
/// Inserts a rule type into the set.
#[inline]
pub fn insert(&mut self, ty: CssRuleType) {
self.0 |= ty.bit()
}
}
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum RulesMutateError { pub enum RulesMutateError {
Syntax, Syntax,
@ -422,10 +459,12 @@ impl CssRule {
dom_error: None, dom_error: None,
insert_rule_context: Some(insert_rule_context), insert_rule_context: Some(insert_rule_context),
allow_import_rules, allow_import_rules,
declaration_parser_state: Default::default(),
rules: Default::default(),
}; };
match parse_one_rule(&mut input, &mut rule_parser) { match parse_one_rule(&mut input, &mut rule_parser) {
Ok((_, rule)) => Ok(rule), Ok(_) => Ok(rule_parser.rules.pop().unwrap()),
Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)), Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)),
} }
} }

View file

@ -9,14 +9,16 @@ use crate::error_reporting::ContextualParseError;
use crate::font_face::parse_font_face_block; use crate::font_face::parse_font_face_block;
use crate::media_queries::MediaList; use crate::media_queries::MediaList;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::properties::parse_property_declaration_list; use crate::properties::declaration_block::{
parse_property_declaration_list, DeclarationParserState, PropertyDeclarationBlock,
};
use crate::selector_parser::{SelectorImpl, SelectorParser}; use crate::selector_parser::{SelectorImpl, SelectorParser};
use crate::shared_lock::{Locked, SharedRwLock}; use crate::shared_lock::{Locked, SharedRwLock};
use crate::str::starts_with_ignore_ascii_case; use crate::str::starts_with_ignore_ascii_case;
use crate::stylesheets::container_rule::{ContainerCondition, ContainerRule}; use crate::stylesheets::container_rule::{ContainerCondition, ContainerRule};
use crate::stylesheets::document_rule::DocumentCondition; use crate::stylesheets::document_rule::DocumentCondition;
use crate::stylesheets::font_feature_values_rule::parse_family_name_list; use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
use crate::stylesheets::import_rule::{ImportRule, ImportLayer, ImportSupportsCondition}; use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};
use crate::stylesheets::keyframes_rule::parse_keyframe_list; use crate::stylesheets::keyframes_rule::parse_keyframe_list;
use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule}; use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
use crate::stylesheets::supports_rule::SupportsCondition; use crate::stylesheets::supports_rule::SupportsCondition;
@ -31,7 +33,8 @@ use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};
use crate::{Namespace, Prefix}; use crate::{Namespace, Prefix};
use cssparser::{ use cssparser::{
AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser, AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser,
ParserState, QualifiedRuleParser, RuleBodyParser, RuleBodyItemParser, SourcePosition, ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
SourcePosition,
}; };
use selectors::SelectorList; use selectors::SelectorList;
use servo_arc::Arc; use servo_arc::Arc;
@ -76,7 +79,7 @@ impl<'a> InsertRuleContext<'a> {
} }
/// The parser for the top-level rules in a stylesheet. /// The parser for the top-level rules in a stylesheet.
pub struct TopLevelRuleParser<'a> { pub struct TopLevelRuleParser<'a, 'i> {
/// A reference to the lock we need to use to create rules. /// A reference to the lock we need to use to create rules.
pub shared_lock: &'a SharedRwLock, pub shared_lock: &'a SharedRwLock,
/// A reference to a stylesheet loader if applicable, for `@import` rules. /// A reference to a stylesheet loader if applicable, for `@import` rules.
@ -93,13 +96,19 @@ pub struct TopLevelRuleParser<'a> {
pub insert_rule_context: Option<InsertRuleContext<'a>>, pub insert_rule_context: Option<InsertRuleContext<'a>>,
/// Whether @import rules will be allowed. /// Whether @import rules will be allowed.
pub allow_import_rules: AllowImportRules, pub allow_import_rules: AllowImportRules,
/// Parser state for declaration blocks in either nested rules or style rules.
pub declaration_parser_state: DeclarationParserState<'i>,
/// The rules we've parsed so far.
pub rules: Vec<CssRule>,
} }
impl<'a> TopLevelRuleParser<'a> { impl<'a, 'i> TopLevelRuleParser<'a, 'i> {
fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a> { fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a, 'i> {
NestedRuleParser { NestedRuleParser {
shared_lock: self.shared_lock, shared_lock: self.shared_lock,
context: &mut self.context, context: &mut self.context,
declaration_parser_state: &mut self.declaration_parser_state,
rules: &mut self.rules,
} }
} }
@ -133,8 +142,8 @@ impl<'a> TopLevelRuleParser<'a> {
// If there's anything that isn't a namespace rule (or import rule, but // If there's anything that isn't a namespace rule (or import rule, but
// we checked that already at the beginning), reject with a // we checked that already at the beginning), reject with a
// StateError. // StateError.
if new_state == State::Namespaces && if new_state == State::Namespaces
ctx.rule_list[ctx.index..] && ctx.rule_list[ctx.index..]
.iter() .iter()
.any(|r| !matches!(*r, CssRule::Namespace(..))) .any(|r| !matches!(*r, CssRule::Namespace(..)))
{ {
@ -195,16 +204,21 @@ pub enum AtRulePrelude {
/// A @document rule, with its conditional. /// A @document rule, with its conditional.
Document(DocumentCondition), Document(DocumentCondition),
/// A @import rule prelude. /// A @import rule prelude.
Import(CssUrl, Arc<Locked<MediaList>>, Option<ImportSupportsCondition>, ImportLayer), Import(
CssUrl,
Arc<Locked<MediaList>>,
Option<ImportSupportsCondition>,
ImportLayer,
),
/// A @namespace rule prelude. /// A @namespace rule prelude.
Namespace(Option<Prefix>, Namespace), Namespace(Option<Prefix>, Namespace),
/// A @layer rule prelude. /// A @layer rule prelude.
Layer(Vec<LayerName>), Layer(Vec<LayerName>),
} }
impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
type Prelude = AtRulePrelude; type Prelude = AtRulePrelude;
type AtRule = (SourcePosition, CssRule); type AtRule = SourcePosition;
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>( fn parse_prelude<'t>(
@ -294,9 +308,9 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
if !self.check_state(State::Body) { if !self.check_state(State::Body) {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
} }
let rule = AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?;
self.state = State::Body; self.state = State::Body;
Ok((start.position(), rule)) Ok(start.position())
} }
#[inline] #[inline]
@ -305,7 +319,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
prelude: AtRulePrelude, prelude: AtRulePrelude,
start: &ParserState, start: &ParserState,
) -> Result<Self::AtRule, ()> { ) -> Result<Self::AtRule, ()> {
let rule = match prelude { match prelude {
AtRulePrelude::Import(url, media, supports, layer) => { AtRulePrelude::Import(url, media, supports, layer) => {
let loader = self let loader = self
.loader .loader
@ -322,7 +336,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
); );
self.state = State::Imports; self.state = State::Imports;
CssRule::Import(import_rule) self.rules.push(CssRule::Import(import_rule))
}, },
AtRulePrelude::Namespace(prefix, url) => { AtRulePrelude::Namespace(prefix, url) => {
let namespaces = self.context.namespaces.to_mut(); let namespaces = self.context.namespaces.to_mut();
@ -335,34 +349,33 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
}; };
self.state = State::Namespaces; self.state = State::Namespaces;
CssRule::Namespace(Arc::new(self.shared_lock.wrap(NamespaceRule { self.rules
prefix, .push(CssRule::Namespace(Arc::new(self.shared_lock.wrap(
url, NamespaceRule {
source_location: start.source_location(), prefix,
}))) url,
source_location: start.source_location(),
},
))));
}, },
AtRulePrelude::Layer(ref names) => { AtRulePrelude::Layer(..) => {
if names.is_empty() { AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?;
return Err(());
}
if self.state <= State::EarlyLayers { if self.state <= State::EarlyLayers {
self.state = State::EarlyLayers; self.state = State::EarlyLayers;
} else { } else {
self.state = State::Body; self.state = State::Body;
} }
AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)
.expect("All validity checks on the nested parser should be done before changing self.state")
}, },
_ => AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?, _ => AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?,
}; };
Ok((start.position(), rule)) Ok(start.position())
} }
} }
impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> { impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
type Prelude = SelectorList<SelectorImpl>; type Prelude = SelectorList<SelectorImpl>;
type QualifiedRule = (SourcePosition, CssRule); type QualifiedRule = SourcePosition;
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
#[inline] #[inline]
@ -384,45 +397,117 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
start: &ParserState, start: &ParserState,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self::QualifiedRule, ParseError<'i>> { ) -> Result<Self::QualifiedRule, ParseError<'i>> {
let rule = QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)?;
self.state = State::Body; self.state = State::Body;
Ok((start.position(), rule)) Ok(start.position())
} }
} }
struct NestedRuleParser<'a, 'b: 'a> { struct NestedRuleParser<'a, 'b: 'a, 'i> {
shared_lock: &'a SharedRwLock, shared_lock: &'a SharedRwLock,
context: &'a mut ParserContext<'b>, context: &'a mut ParserContext<'b>,
declaration_parser_state: &'a mut DeclarationParserState<'i>,
rules: &'a mut Vec<CssRule>,
} }
impl<'a, 'b> NestedRuleParser<'a, 'b> { struct NestedParseResult {
rules: Vec<CssRule>,
declarations: PropertyDeclarationBlock,
}
impl NestedParseResult {
fn into_rules(
mut self,
shared_lock: &SharedRwLock,
source_location: SourceLocation,
) -> Arc<Locked<CssRules>> {
lazy_static! {
static ref AMPERSAND: SelectorList<SelectorImpl> = {
let list = SelectorList::ampersand();
list.0
.iter()
.for_each(|selector| selector.mark_as_intentionally_leaked());
list
};
};
if !self.declarations.is_empty() {
self.rules.insert(
0,
CssRule::Style(Arc::new(shared_lock.wrap(StyleRule {
selectors: AMPERSAND.clone(),
block: Arc::new(shared_lock.wrap(self.declarations)),
rules: None,
source_location,
}))),
)
}
CssRules::new(self.rules, shared_lock)
}
}
impl<'a, 'b, 'i> NestedRuleParser<'a, 'b, 'i> {
/// When nesting is disabled, we prevent parsing at rules and qualified rules inside style
/// rules.
fn allow_at_and_qualified_rules(&self) -> bool {
if !self.context.rule_types.contains(CssRuleType::Style) {
return true;
}
static_prefs::pref!("layout.css.nesting.enabled")
}
fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R { fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R {
let old_rule_type = self.context.rule_type.take(); let old_rule_types = self.context.rule_types;
self.context.rule_type = Some(rule_type); self.context.rule_types.insert(rule_type);
let r = cb(self); let r = cb(self);
self.context.rule_type = old_rule_type; self.context.rule_types = old_rule_types;
r r
} }
fn parse_nested_rules( fn parse_nested(
&mut self, &mut self,
input: &mut Parser, input: &mut Parser<'i, '_>,
rule_type: CssRuleType, rule_type: CssRuleType,
) -> Arc<Locked<CssRules>> { selectors: Option<&SelectorList<SelectorImpl>>,
) -> NestedParseResult {
self.nest_for_rule(rule_type, |parser| { self.nest_for_rule(rule_type, |parser| {
let parse_declarations = parser.parse_declarations();
let mut old_declaration_state = std::mem::take(parser.declaration_parser_state);
let mut rules = std::mem::take(parser.rules);
let mut iter = RuleBodyParser::new(input, parser); let mut iter = RuleBodyParser::new(input, parser);
let mut rules = Vec::new();
while let Some(result) = iter.next() { while let Some(result) = iter.next() {
match result { match result {
Ok(rule) => rules.push(rule), Ok(()) => {},
Err((error, slice)) => { Err((error, slice)) => {
let location = error.location; if parse_declarations {
let error = ContextualParseError::InvalidRule(slice, error); iter.parser.declaration_parser_state.did_error(iter.parser.context, error, slice);
iter.parser.context.log_css_error(location, error); } else {
let location = error.location;
let error = ContextualParseError::InvalidRule(slice, error);
iter.parser.context.log_css_error(location, error);
}
}, },
} }
} }
CssRules::new(rules, iter.parser.shared_lock) let declarations = if parse_declarations {
parser
.declaration_parser_state
.report_errors_if_needed(parser.context, selectors);
parser.declaration_parser_state.take_declarations()
} else {
PropertyDeclarationBlock::default()
};
debug_assert!(
!parser.declaration_parser_state.has_parsed_declarations(),
"Parsed but didn't consume declarations"
);
std::mem::swap(parser.declaration_parser_state, &mut old_declaration_state);
std::mem::swap(parser.rules, &mut rules);
NestedParseResult {
rules,
declarations,
}
}) })
} }
} }
@ -437,9 +522,9 @@ fn container_queries_enabled() -> bool {
.unwrap_or(false); .unwrap_or(false);
} }
impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> {
type Prelude = AtRulePrelude; type Prelude = AtRulePrelude;
type AtRule = CssRule; type AtRule = ();
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>( fn parse_prelude<'t>(
@ -447,6 +532,9 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
name: CowRcStr<'i>, name: CowRcStr<'i>,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self::Prelude, ParseError<'i>> { ) -> Result<Self::Prelude, ParseError<'i>> {
if !self.allow_at_and_qualified_rules() {
return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
}
Ok(match_ignore_ascii_case! { &*name, Ok(match_ignore_ascii_case! { &*name,
"media" => { "media" => {
let media_queries = MediaList::parse(self.context, input); let media_queries = MediaList::parse(self.context, input);
@ -499,7 +587,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
if cfg!(feature = "servo") && if cfg!(feature = "servo") &&
prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) { prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
// Servo should not support @-moz-keyframes. // Servo should not support @-moz-keyframes.
return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone()))) return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())))
} }
let name = KeyframesName::parse(self.context, input)?; let name = KeyframesName::parse(self.context, input)?;
AtRulePrelude::Keyframes(name, prefix) AtRulePrelude::Keyframes(name, prefix)
@ -513,7 +601,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
let cond = DocumentCondition::parse(self.context, input)?; let cond = DocumentCondition::parse(self.context, input)?;
AtRulePrelude::Document(cond) AtRulePrelude::Document(cond)
}, },
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone()))) _ => return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())))
}) })
} }
@ -522,135 +610,147 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
prelude: AtRulePrelude, prelude: AtRulePrelude,
start: &ParserState, start: &ParserState,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> { ) -> Result<(), ParseError<'i>> {
match prelude { let rule = match prelude {
AtRulePrelude::FontFace => { AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
self.nest_for_rule(CssRuleType::FontFace, |p| { CssRule::FontFace(Arc::new(p.shared_lock.wrap(
Ok(CssRule::FontFace(Arc::new(p.shared_lock.wrap( parse_font_face_block(&p.context, input, start.source_location()).into(),
parse_font_face_block(&p.context, input, start.source_location()).into(), )))
)))) }),
})
},
AtRulePrelude::FontFeatureValues(family_names) => { AtRulePrelude::FontFeatureValues(family_names) => {
self.nest_for_rule(CssRuleType::FontFeatureValues, |p| { self.nest_for_rule(CssRuleType::FontFeatureValues, |p| {
Ok(CssRule::FontFeatureValues(Arc::new(p.shared_lock.wrap( CssRule::FontFeatureValues(Arc::new(p.shared_lock.wrap(
FontFeatureValuesRule::parse( FontFeatureValuesRule::parse(
&p.context, &p.context,
input, input,
family_names, family_names,
start.source_location(), start.source_location(),
), ),
)))) )))
}) })
}, },
AtRulePrelude::FontPaletteValues(name) => { AtRulePrelude::FontPaletteValues(name) => {
self.nest_for_rule(CssRuleType::FontPaletteValues, |p| { self.nest_for_rule(CssRuleType::FontPaletteValues, |p| {
Ok(CssRule::FontPaletteValues(Arc::new(p.shared_lock.wrap( CssRule::FontPaletteValues(Arc::new(p.shared_lock.wrap(
FontPaletteValuesRule::parse( FontPaletteValuesRule::parse(
&p.context, &p.context,
input, input,
name, name,
start.source_location(), start.source_location(),
), ),
)))) )))
}) })
}, },
AtRulePrelude::CounterStyle(name) => { AtRulePrelude::CounterStyle(name) => {
self.nest_for_rule(CssRuleType::CounterStyle, |p| { let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {
Ok(CssRule::CounterStyle(Arc::new( parse_counter_style_body(name, &p.context, input, start.source_location())
p.shared_lock.wrap( })?;
parse_counter_style_body(name, &p.context, input, start.source_location())? CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))
.into(),
),
)))
})
}, },
AtRulePrelude::Media(media_queries) => { AtRulePrelude::Media(media_queries) => {
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule { let source_location = start.source_location();
media_queries, CssRule::Media(Arc::new(
rules: self.parse_nested_rules(input, CssRuleType::Media), self.shared_lock.wrap(MediaRule {
source_location: start.source_location(), media_queries,
})))) rules: self
.parse_nested(input, CssRuleType::Media, None)
.into_rules(self.shared_lock, source_location),
source_location,
}),
))
}, },
AtRulePrelude::Supports(condition) => { AtRulePrelude::Supports(condition) => {
let enabled = self.nest_for_rule(CssRuleType::Style, |p| { let enabled =
condition.eval(&p.context) self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));
}); let source_location = start.source_location();
Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap( CssRule::Supports(Arc::new(
SupportsRule { self.shared_lock.wrap(SupportsRule {
condition, condition,
rules: self.parse_nested_rules(input, CssRuleType::Supports), rules: self
.parse_nested(input, CssRuleType::Supports, None)
.into_rules(self.shared_lock, source_location),
enabled, enabled,
source_location: start.source_location(), source_location,
}, }),
)))) ))
}, },
AtRulePrelude::Viewport => { AtRulePrelude::Viewport => {
self.nest_for_rule(CssRuleType::Viewport, |p| { let body = self.nest_for_rule(CssRuleType::Viewport, |p| {
Ok(CssRule::Viewport(Arc::new( ViewportRule::parse(&p.context, input)
p.shared_lock.wrap(ViewportRule::parse(&p.context, input)?), })?;
))) CssRule::Viewport(Arc::new(self.shared_lock.wrap(body)))
})
}, },
AtRulePrelude::Keyframes(name, vendor_prefix) => { AtRulePrelude::Keyframes(name, vendor_prefix) => {
self.nest_for_rule(CssRuleType::Keyframe, |p| { self.nest_for_rule(CssRuleType::Keyframe, |p| {
Ok(CssRule::Keyframes(Arc::new(p.shared_lock.wrap( CssRule::Keyframes(Arc::new(p.shared_lock.wrap(KeyframesRule {
KeyframesRule { name,
name, keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock),
keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock), vendor_prefix,
vendor_prefix, source_location: start.source_location(),
source_location: start.source_location(), })))
},
))))
}) })
}, },
AtRulePrelude::Page(selectors) => { AtRulePrelude::Page(selectors) => {
let declarations = self.nest_for_rule(CssRuleType::Page, |p| { let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
// TODO: Support nesting in @page rules?
parse_property_declaration_list(&p.context, input, None) parse_property_declaration_list(&p.context, input, None)
}); });
Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule { CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
selectors, selectors,
block: Arc::new(self.shared_lock.wrap(declarations)), block: Arc::new(self.shared_lock.wrap(declarations)),
source_location: start.source_location(), source_location: start.source_location(),
})))) })))
}, },
AtRulePrelude::Document(condition) => { AtRulePrelude::Document(condition) => {
if !cfg!(feature = "gecko") { if !cfg!(feature = "gecko") {
unreachable!() unreachable!()
} }
Ok(CssRule::Document(Arc::new(self.shared_lock.wrap( let source_location = start.source_location();
DocumentRule { CssRule::Document(Arc::new(
self.shared_lock.wrap(DocumentRule {
condition, condition,
rules: self.parse_nested_rules(input, CssRuleType::Document), rules: self
source_location: start.source_location(), .parse_nested(input, CssRuleType::Document, None)
}, .into_rules(self.shared_lock, source_location),
)))) source_location,
}),
))
},
AtRulePrelude::Container(condition) => {
let source_location = start.source_location();
CssRule::Container(Arc::new(
self.shared_lock.wrap(ContainerRule {
condition,
rules: self
.parse_nested(input, CssRuleType::Container, None)
.into_rules(self.shared_lock, source_location),
source_location,
}),
))
}, },
AtRulePrelude::Container(condition) => Ok(CssRule::Container(Arc::new(
self.shared_lock.wrap(ContainerRule {
condition,
rules: self.parse_nested_rules(input, CssRuleType::Container),
source_location: start.source_location(),
}),
))),
AtRulePrelude::Layer(names) => { AtRulePrelude::Layer(names) => {
let name = match names.len() { let name = match names.len() {
0 | 1 => names.into_iter().next(), 0 | 1 => names.into_iter().next(),
_ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)), _ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
}; };
Ok(CssRule::LayerBlock(Arc::new(self.shared_lock.wrap( let source_location = start.source_location();
LayerBlockRule { CssRule::LayerBlock(Arc::new(
self.shared_lock.wrap(LayerBlockRule {
name, name,
rules: self.parse_nested_rules(input, CssRuleType::LayerBlock), rules: self
source_location: start.source_location(), .parse_nested(input, CssRuleType::LayerBlock, None)
}, .into_rules(self.shared_lock, source_location),
)))) source_location,
}),
))
}, },
AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => { AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => {
// These rules don't have blocks. // These rules don't have blocks.
Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock)) return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock));
}, },
} };
self.rules.push(rule);
Ok(())
} }
#[inline] #[inline]
@ -658,8 +758,8 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
&mut self, &mut self,
prelude: AtRulePrelude, prelude: AtRulePrelude,
start: &ParserState, start: &ParserState,
) -> Result<Self::AtRule, ()> { ) -> Result<(), ()> {
Ok(match prelude { let rule = match prelude {
AtRulePrelude::Layer(names) => { AtRulePrelude::Layer(names) => {
if names.is_empty() { if names.is_empty() {
return Err(()); return Err(());
@ -670,7 +770,9 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
}))) })))
}, },
_ => return Err(()), _ => return Err(()),
}) };
self.rules.push(rule);
Ok(())
} }
} }
@ -709,9 +811,9 @@ fn check_for_useless_selector(
} }
} }
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> { impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> {
type Prelude = SelectorList<SelectorImpl>; type Prelude = SelectorList<SelectorImpl>;
type QualifiedRule = CssRule; type QualifiedRule = ();
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>( fn parse_prelude<'t>(
@ -736,27 +838,46 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
selectors: Self::Prelude, selectors: Self::Prelude,
start: &ParserState, start: &ParserState,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> { ) -> Result<(), ParseError<'i>> {
let declarations = self.nest_for_rule(CssRuleType::Style, |p| { let result = self.parse_nested(input, CssRuleType::Style, Some(&selectors));
parse_property_declaration_list(&p.context, input, Some(&selectors)) let block = Arc::new(self.shared_lock.wrap(result.declarations));
}); self.rules
let block = Arc::new(self.shared_lock.wrap(declarations)); .push(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule { selectors,
selectors, block,
block, rules: if result.rules.is_empty() {
rules: None, // TODO(nesting) None
source_location: start.source_location(), } else {
})))) Some(CssRules::new(result.rules, self.shared_lock))
},
source_location: start.source_location(),
}))));
Ok(())
} }
} }
impl<'a, 'b, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'b> { impl<'a, 'b, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'b, 'i> {
type Declaration = CssRule; type Declaration = ();
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
fn parse_value<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>> {
self.declaration_parser_state.parse_value(self.context, name, input)
}
} }
impl<'a, 'b, 'i> RuleBodyItemParser<'i, CssRule, StyleParseErrorKind<'i>> for NestedRuleParser<'a, 'b> { impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
fn parse_qualified(&self) -> bool { true } for NestedRuleParser<'a, 'b, 'i>
// TODO: Nesting. {
fn parse_declarations(&self) -> bool { false } fn parse_qualified(&self) -> bool {
self.allow_at_and_qualified_rules()
}
/// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse
/// raw declarations there.
fn parse_declarations(&self) -> bool {
self.context.rule_types.contains(CssRuleType::Style)
}
} }

View file

@ -461,7 +461,6 @@ impl Stylesheet {
allow_import_rules: AllowImportRules, allow_import_rules: AllowImportRules,
mut sanitization_data: Option<&mut SanitizationData>, mut sanitization_data: Option<&mut SanitizationData>,
) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) { ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
let mut rules = Vec::new();
let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset); let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
let mut input = Parser::new(&mut input); let mut input = Parser::new(&mut input);
@ -484,32 +483,26 @@ impl Stylesheet {
dom_error: None, dom_error: None,
insert_rule_context: None, insert_rule_context: None,
allow_import_rules, allow_import_rules,
declaration_parser_state: Default::default(),
rules: Vec::new(),
}; };
{ {
let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser); let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
while let Some(result) = iter.next() {
loop {
let result = match iter.next() {
Some(result) => result,
None => break,
};
match result { match result {
Ok((rule_start, rule)) => { Ok(rule_start) => {
// TODO(emilio, nesting): sanitize nested CSS rules, probably?
if let Some(ref mut data) = sanitization_data { if let Some(ref mut data) = sanitization_data {
if !data.kind.allows(&rule) { if let Some(ref rule) = iter.parser.rules.last() {
continue; if !data.kind.allows(rule) {
iter.parser.rules.pop();
continue;
}
} }
let end = iter.input.position().byte_index(); let end = iter.input.position().byte_index();
data.output.push_str(&css[rule_start.byte_index()..end]); data.output.push_str(&css[rule_start.byte_index()..end]);
} }
// Use a fallible push here, and if it fails, just fall
// out of the loop. This will cause the page to be
// shown incorrectly, but it's better than OOMing.
if rules.try_reserve(1).is_err() {
break;
}
rules.push(rule);
}, },
Err((error, slice)) => { Err((error, slice)) => {
let location = error.location; let location = error.location;
@ -522,7 +515,7 @@ impl Stylesheet {
let source_map_url = input.current_source_map_url().map(String::from); let source_map_url = input.current_source_map_url().map(String::from);
let source_url = input.current_source_url().map(String::from); let source_url = input.current_source_url().map(String::from);
(rule_parser.context.namespaces.into_owned(), rules, source_map_url, source_url) (rule_parser.context.namespaces.into_owned(), rule_parser.rules, source_map_url, source_url)
} }
/// Creates an empty stylesheet and parses it with a given base url, origin /// Creates an empty stylesheet and parses it with a given base url, origin
@ -601,7 +594,7 @@ impl Clone for Stylesheet {
Stylesheet { Stylesheet {
contents, contents,
media: media, media,
shared_lock: lock, shared_lock: lock,
disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)), disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
} }

View file

@ -427,7 +427,7 @@ impl Declaration {
/// ///
/// <https://drafts.csswg.org/css-conditional-3/#support-definition> /// <https://drafts.csswg.org/css-conditional-3/#support-definition>
pub fn eval(&self, context: &ParserContext) -> bool { pub fn eval(&self, context: &ParserContext) -> bool {
debug_assert_eq!(context.rule_type(), CssRuleType::Style); debug_assert!(context.rule_types().contains(CssRuleType::Style));
let mut input = ParserInput::new(&self.0); let mut input = ParserInput::new(&self.0);
let mut input = Parser::new(&mut input); let mut input = Parser::new(&mut input);
@ -439,7 +439,7 @@ impl Declaration {
let id = let id =
PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?; PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?;
let mut declarations = SourcePropertyDeclaration::new(); let mut declarations = SourcePropertyDeclaration::default();
input.parse_until_before(Delimiter::Bang, |input| { input.parse_until_before(Delimiter::Bang, |input| {
PropertyDeclaration::parse_into(&mut declarations, id, &context, input) PropertyDeclaration::parse_into(&mut declarations, id, &context, input)
.map_err(|_| input.new_custom_error(())) .map_err(|_| input.new_custom_error(()))

View file

@ -121,8 +121,6 @@ pub enum StyleParseErrorKind<'i> {
DisallowedImportRule, DisallowedImportRule,
/// Unexpected @charset rule encountered. /// Unexpected @charset rule encountered.
UnexpectedCharsetRule, UnexpectedCharsetRule,
/// Unsupported @ rule
UnsupportedAtRule(CowRcStr<'i>),
/// A placeholder for many sources of errors that require more specific variants. /// A placeholder for many sources of errors that require more specific variants.
UnspecifiedError, UnspecifiedError,
/// An unexpected token was found within a namespace rule. /// An unexpected token was found within a namespace rule.