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> {
/// Returns a selector list with a single `&`
pub fn ampersand() -> Self {
Self(smallvec::smallvec![Selector::ampersand()])
}
/// Parse a comma-separated list of Selectors.
/// <https://drafts.csswg.org/selectors/#grouping>
///
@ -647,6 +652,18 @@ pub struct Selector<Impl: SelectorImpl>(
);
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]
pub fn specificity(&self) -> u32 {
self.0.header.header.specificity()

View file

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

View file

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

View file

@ -92,12 +92,18 @@ pub enum Importance {
Important,
}
impl Default for Importance {
fn default() -> Self {
Self::Normal
}
}
impl Importance {
/// Return whether this is an important declaration.
pub fn important(self) -> bool {
match self {
Importance::Normal => false,
Importance::Important => true,
Self::Normal => false,
Self::Important => true,
}
}
}
@ -146,7 +152,7 @@ impl PropertyDeclarationIdSet {
/// Overridden declarations are skipped.
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
#[derive(Clone, ToShmem)]
#[derive(Clone, ToShmem, Default)]
pub struct PropertyDeclarationBlock {
/// The group of declarations, along with their importance.
///
@ -284,6 +290,12 @@ impl PropertyDeclarationBlock {
self.declarations.len()
}
/// Returns whether the block is empty.
#[inline]
pub fn is_empty(&self) -> bool {
self.declarations.is_empty()
}
/// Create an empty block
#[inline]
pub fn new() -> Self {
@ -1357,24 +1369,127 @@ pub fn parse_one_declaration_into(
}
/// A struct to parse property declarations.
struct PropertyDeclarationParser<'a, 'b: 'a> {
struct PropertyDeclarationParser<'a, 'b: 'a, 'i> {
context: &'a ParserContext<'b>,
declarations: &'a mut SourcePropertyDeclaration,
/// The last parsed property id if any.
state: &'a mut DeclarationParserState<'i>,
}
/// 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>,
}
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.
impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b> {
impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
type Prelude = ();
type AtRule = Importance;
type AtRule = ();
type Error = StyleParseErrorKind<'i>;
}
/// 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 QualifiedRule = Importance;
type QualifiedRule = ();
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("_")
}
impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
type Declaration = Importance;
impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
type Declaration = ();
type Error = StyleParseErrorKind<'i>;
fn parse_value<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<Importance, ParseError<'i>> {
let id = match PropertyId::parse(&name, self.context) {
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)
) -> Result<(), ParseError<'i>> {
self.state.parse_value(self.context, name, input)
}
}
impl<'a, 'b, 'i> RuleBodyItemParser<'i, Importance, StyleParseErrorKind<'i>> for PropertyDeclarationParser<'a, 'b> {
fn parse_declarations(&self) -> bool { true }
impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
for PropertyDeclarationParser<'a, 'b, 'i>
{
fn parse_declarations(&self) -> bool {
true
}
// 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]>;
@ -1486,18 +1589,6 @@ fn report_one_css_error<'i>(
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
/// block.
pub fn parse_property_declaration_list(
@ -1505,38 +1596,18 @@ pub fn parse_property_declaration_list(
input: &mut Parser,
selectors: Option<&SelectorList<SelectorImpl>>,
) -> PropertyDeclarationBlock {
let mut declarations = SourcePropertyDeclaration::new();
let mut block = PropertyDeclarationBlock::new();
let mut state = DeclarationParserState::default();
let mut parser = PropertyDeclarationParser {
context,
last_parsed_property_id: None,
declarations: &mut declarations,
state: &mut state,
};
let mut iter = RuleBodyParser::new(input, &mut parser);
let mut errors = SmallParseErrorVec::new();
while let Some(declaration) = iter.next() {
match declaration {
Ok(importance) => {
block.extend(iter.parser.declarations.drain(), 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.
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));
}
},
Ok(()) => {},
Err((error, slice)) => iter.parser.state.did_error(context, error, slice),
}
}
if !errors.is_empty() {
report_css_errors(context, &block, selectors, &mut errors)
}
block
parser.state.report_errors_if_needed(context, selectors);
state.output_block
}

View file

@ -37,7 +37,7 @@ use crate::selector_parser::PseudoElement;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
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::values::generics::text::LineHeight;
use crate::values::{computed, resolved};
@ -572,30 +572,29 @@ impl NonCustomPropertyId {
/// Returns whether a given rule allows a given property.
#[inline]
pub fn allowed_in_rule(self, rule_type: CssRuleType) -> bool {
pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool {
debug_assert!(
matches!(
rule_type,
CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style
),
rule_types.contains(CssRuleType::Keyframe) ||
rule_types.contains(CssRuleType::Page) ||
rule_types.contains(CssRuleType::Style),
"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():
${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
];
match rule_type {
% for name in RULE_VALUES:
CssRuleType::${name} => MAP[self.0] & ${RULE_VALUES[name]} != 0,
% endfor
_ => true
}
MAP[self.0] & rule_types.bits() != 0
}
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;
}
@ -728,7 +727,7 @@ impl From<AliasId> for NonCustomPropertyId {
}
/// A set of all properties
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Default)]
pub struct NonCustomPropertyIdSet {
storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32]
}
@ -1792,7 +1791,7 @@ impl UnparsedValue {
Some(shorthand) => shorthand,
};
let mut decls = SourcePropertyDeclaration::new();
let mut decls = SourcePropertyDeclaration::default();
// parse_into takes care of doing `parse_entirely` for us.
if shorthand.parse_into(&mut decls, &context, &mut input).is_err() {
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`
/// large enough to parse one CSS `key: value` declaration.
/// (Shorthands expand to multiple `PropertyDeclaration`s.)
#[derive(Default)]
pub struct SourcePropertyDeclaration {
/// The storage for the actual declarations (except for all).
pub declarations: SubpropertiesVec<PropertyDeclaration>,
@ -2618,19 +2618,10 @@ pub struct SourcePropertyDeclaration {
size_of_test!(SourcePropertyDeclaration, 568);
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.
#[inline]
pub fn with_one(decl: PropertyDeclaration) -> Self {
let mut result = Self::new();
let mut result = Self::default();
result.declarations.push(decl);
result
}
@ -2677,6 +2668,12 @@ pub enum AllShorthand {
WithVariables(Arc<UnparsedValue>)
}
impl Default for AllShorthand {
fn default() -> Self {
Self::NotSet
}
}
impl AllShorthand {
/// Iterates property declarations from the given all shorthand value.
#[inline]

View file

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

View file

@ -410,7 +410,7 @@ macro_rules! font_feature_values_blocks {
_: &ParserState,
input: &mut Parser<'i, 't>
) -> 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 {
$(
BlockType::$ident_camel => {

View file

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

View file

@ -352,6 +352,43 @@ pub enum CssRuleType {
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)]
pub enum RulesMutateError {
Syntax,
@ -422,10 +459,12 @@ impl CssRule {
dom_error: None,
insert_rule_context: Some(insert_rule_context),
allow_import_rules,
declaration_parser_state: Default::default(),
rules: Default::default(),
};
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)),
}
}

View file

@ -9,14 +9,16 @@ use crate::error_reporting::ContextualParseError;
use crate::font_face::parse_font_face_block;
use crate::media_queries::MediaList;
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::shared_lock::{Locked, SharedRwLock};
use crate::str::starts_with_ignore_ascii_case;
use crate::stylesheets::container_rule::{ContainerCondition, ContainerRule};
use crate::stylesheets::document_rule::DocumentCondition;
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::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
use crate::stylesheets::supports_rule::SupportsCondition;
@ -31,7 +33,8 @@ use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};
use crate::{Namespace, Prefix};
use cssparser::{
AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser,
ParserState, QualifiedRuleParser, RuleBodyParser, RuleBodyItemParser, SourcePosition,
ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
SourcePosition,
};
use selectors::SelectorList;
use servo_arc::Arc;
@ -76,7 +79,7 @@ impl<'a> InsertRuleContext<'a> {
}
/// 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.
pub shared_lock: &'a SharedRwLock,
/// 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>>,
/// Whether @import rules will be allowed.
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> {
fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a> {
impl<'a, 'i> TopLevelRuleParser<'a, 'i> {
fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a, 'i> {
NestedRuleParser {
shared_lock: self.shared_lock,
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
// we checked that already at the beginning), reject with a
// StateError.
if new_state == State::Namespaces &&
ctx.rule_list[ctx.index..]
if new_state == State::Namespaces
&& ctx.rule_list[ctx.index..]
.iter()
.any(|r| !matches!(*r, CssRule::Namespace(..)))
{
@ -195,16 +204,21 @@ pub enum AtRulePrelude {
/// A @document rule, with its conditional.
Document(DocumentCondition),
/// A @import rule prelude.
Import(CssUrl, Arc<Locked<MediaList>>, Option<ImportSupportsCondition>, ImportLayer),
Import(
CssUrl,
Arc<Locked<MediaList>>,
Option<ImportSupportsCondition>,
ImportLayer,
),
/// A @namespace rule prelude.
Namespace(Option<Prefix>, Namespace),
/// A @layer rule prelude.
Layer(Vec<LayerName>),
}
impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
type Prelude = AtRulePrelude;
type AtRule = (SourcePosition, CssRule);
type AtRule = SourcePosition;
type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>(
@ -294,9 +308,9 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
if !self.check_state(State::Body) {
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;
Ok((start.position(), rule))
Ok(start.position())
}
#[inline]
@ -305,7 +319,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
prelude: AtRulePrelude,
start: &ParserState,
) -> Result<Self::AtRule, ()> {
let rule = match prelude {
match prelude {
AtRulePrelude::Import(url, media, supports, layer) => {
let loader = self
.loader
@ -322,7 +336,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
);
self.state = State::Imports;
CssRule::Import(import_rule)
self.rules.push(CssRule::Import(import_rule))
},
AtRulePrelude::Namespace(prefix, url) => {
let namespaces = self.context.namespaces.to_mut();
@ -335,34 +349,33 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
};
self.state = State::Namespaces;
CssRule::Namespace(Arc::new(self.shared_lock.wrap(NamespaceRule {
prefix,
url,
source_location: start.source_location(),
})))
self.rules
.push(CssRule::Namespace(Arc::new(self.shared_lock.wrap(
NamespaceRule {
prefix,
url,
source_location: start.source_location(),
},
))));
},
AtRulePrelude::Layer(ref names) => {
if names.is_empty() {
return Err(());
}
AtRulePrelude::Layer(..) => {
AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?;
if self.state <= State::EarlyLayers {
self.state = State::EarlyLayers;
} else {
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)?,
};
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 QualifiedRule = (SourcePosition, CssRule);
type QualifiedRule = SourcePosition;
type Error = StyleParseErrorKind<'i>;
#[inline]
@ -384,45 +397,117 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> 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;
Ok((start.position(), rule))
Ok(start.position())
}
}
struct NestedRuleParser<'a, 'b: 'a> {
struct NestedRuleParser<'a, 'b: 'a, 'i> {
shared_lock: &'a SharedRwLock,
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 {
let old_rule_type = self.context.rule_type.take();
self.context.rule_type = Some(rule_type);
let old_rule_types = self.context.rule_types;
self.context.rule_types.insert(rule_type);
let r = cb(self);
self.context.rule_type = old_rule_type;
self.context.rule_types = old_rule_types;
r
}
fn parse_nested_rules(
fn parse_nested(
&mut self,
input: &mut Parser,
input: &mut Parser<'i, '_>,
rule_type: CssRuleType,
) -> Arc<Locked<CssRules>> {
selectors: Option<&SelectorList<SelectorImpl>>,
) -> NestedParseResult {
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 rules = Vec::new();
while let Some(result) = iter.next() {
match result {
Ok(rule) => rules.push(rule),
Ok(()) => {},
Err((error, slice)) => {
let location = error.location;
let error = ContextualParseError::InvalidRule(slice, error);
iter.parser.context.log_css_error(location, error);
if parse_declarations {
iter.parser.declaration_parser_state.did_error(iter.parser.context, error, slice);
} 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);
}
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 AtRule = CssRule;
type AtRule = ();
type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>(
@ -447,6 +532,9 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> 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,
"media" => {
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") &&
prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
// 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)?;
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)?;
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,
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> {
match prelude {
AtRulePrelude::FontFace => {
self.nest_for_rule(CssRuleType::FontFace, |p| {
Ok(CssRule::FontFace(Arc::new(p.shared_lock.wrap(
parse_font_face_block(&p.context, input, start.source_location()).into(),
))))
})
},
) -> Result<(), ParseError<'i>> {
let rule = match prelude {
AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
CssRule::FontFace(Arc::new(p.shared_lock.wrap(
parse_font_face_block(&p.context, input, start.source_location()).into(),
)))
}),
AtRulePrelude::FontFeatureValues(family_names) => {
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(
&p.context,
input,
family_names,
start.source_location(),
),
))))
)))
})
},
AtRulePrelude::FontPaletteValues(name) => {
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(
&p.context,
input,
name,
start.source_location(),
),
))))
)))
})
},
AtRulePrelude::CounterStyle(name) => {
self.nest_for_rule(CssRuleType::CounterStyle, |p| {
Ok(CssRule::CounterStyle(Arc::new(
p.shared_lock.wrap(
parse_counter_style_body(name, &p.context, input, start.source_location())?
.into(),
),
)))
})
let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {
parse_counter_style_body(name, &p.context, input, start.source_location())
})?;
CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))
},
AtRulePrelude::Media(media_queries) => {
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
media_queries,
rules: self.parse_nested_rules(input, CssRuleType::Media),
source_location: start.source_location(),
}))))
let source_location = start.source_location();
CssRule::Media(Arc::new(
self.shared_lock.wrap(MediaRule {
media_queries,
rules: self
.parse_nested(input, CssRuleType::Media, None)
.into_rules(self.shared_lock, source_location),
source_location,
}),
))
},
AtRulePrelude::Supports(condition) => {
let enabled = self.nest_for_rule(CssRuleType::Style, |p| {
condition.eval(&p.context)
});
Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(
SupportsRule {
let enabled =
self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));
let source_location = start.source_location();
CssRule::Supports(Arc::new(
self.shared_lock.wrap(SupportsRule {
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,
source_location: start.source_location(),
},
))))
source_location,
}),
))
},
AtRulePrelude::Viewport => {
self.nest_for_rule(CssRuleType::Viewport, |p| {
Ok(CssRule::Viewport(Arc::new(
p.shared_lock.wrap(ViewportRule::parse(&p.context, input)?),
)))
})
let body = self.nest_for_rule(CssRuleType::Viewport, |p| {
ViewportRule::parse(&p.context, input)
})?;
CssRule::Viewport(Arc::new(self.shared_lock.wrap(body)))
},
AtRulePrelude::Keyframes(name, vendor_prefix) => {
self.nest_for_rule(CssRuleType::Keyframe, |p| {
Ok(CssRule::Keyframes(Arc::new(p.shared_lock.wrap(
KeyframesRule {
name,
keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock),
vendor_prefix,
source_location: start.source_location(),
},
))))
CssRule::Keyframes(Arc::new(p.shared_lock.wrap(KeyframesRule {
name,
keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock),
vendor_prefix,
source_location: start.source_location(),
})))
})
},
AtRulePrelude::Page(selectors) => {
let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
// TODO: Support nesting in @page rules?
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,
block: Arc::new(self.shared_lock.wrap(declarations)),
source_location: start.source_location(),
}))))
})))
},
AtRulePrelude::Document(condition) => {
if !cfg!(feature = "gecko") {
unreachable!()
}
Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(
DocumentRule {
let source_location = start.source_location();
CssRule::Document(Arc::new(
self.shared_lock.wrap(DocumentRule {
condition,
rules: self.parse_nested_rules(input, CssRuleType::Document),
source_location: start.source_location(),
},
))))
rules: self
.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) => {
let name = match names.len() {
0 | 1 => names.into_iter().next(),
_ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
};
Ok(CssRule::LayerBlock(Arc::new(self.shared_lock.wrap(
LayerBlockRule {
let source_location = start.source_location();
CssRule::LayerBlock(Arc::new(
self.shared_lock.wrap(LayerBlockRule {
name,
rules: self.parse_nested_rules(input, CssRuleType::LayerBlock),
source_location: start.source_location(),
},
))))
rules: self
.parse_nested(input, CssRuleType::LayerBlock, None)
.into_rules(self.shared_lock, source_location),
source_location,
}),
))
},
AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => {
// 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]
@ -658,8 +758,8 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
&mut self,
prelude: AtRulePrelude,
start: &ParserState,
) -> Result<Self::AtRule, ()> {
Ok(match prelude {
) -> Result<(), ()> {
let rule = match prelude {
AtRulePrelude::Layer(names) => {
if names.is_empty() {
return Err(());
@ -670,7 +770,9 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
})))
},
_ => 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 QualifiedRule = CssRule;
type QualifiedRule = ();
type Error = StyleParseErrorKind<'i>;
fn parse_prelude<'t>(
@ -736,27 +838,46 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
selectors: Self::Prelude,
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<CssRule, ParseError<'i>> {
let declarations = self.nest_for_rule(CssRuleType::Style, |p| {
parse_property_declaration_list(&p.context, input, Some(&selectors))
});
let block = Arc::new(self.shared_lock.wrap(declarations));
Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
selectors,
block,
rules: None, // TODO(nesting)
source_location: start.source_location(),
}))))
) -> Result<(), ParseError<'i>> {
let result = self.parse_nested(input, CssRuleType::Style, Some(&selectors));
let block = Arc::new(self.shared_lock.wrap(result.declarations));
self.rules
.push(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
selectors,
block,
rules: if result.rules.is_empty() {
None
} 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> {
type Declaration = CssRule;
impl<'a, 'b, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'b, 'i> {
type Declaration = ();
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> {
fn parse_qualified(&self) -> bool { true }
// TODO: Nesting.
fn parse_declarations(&self) -> bool { false }
impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
for NestedRuleParser<'a, 'b, 'i>
{
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,
mut sanitization_data: Option<&mut SanitizationData>,
) -> (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 = Parser::new(&mut input);
@ -484,32 +483,26 @@ impl Stylesheet {
dom_error: None,
insert_rule_context: None,
allow_import_rules,
declaration_parser_state: Default::default(),
rules: Vec::new(),
};
{
let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
loop {
let result = match iter.next() {
Some(result) => result,
None => break,
};
while let Some(result) = iter.next() {
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 !data.kind.allows(&rule) {
continue;
if let Some(ref rule) = iter.parser.rules.last() {
if !data.kind.allows(rule) {
iter.parser.rules.pop();
continue;
}
}
let end = iter.input.position().byte_index();
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)) => {
let location = error.location;
@ -522,7 +515,7 @@ impl Stylesheet {
let source_map_url = input.current_source_map_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
@ -601,7 +594,7 @@ impl Clone for Stylesheet {
Stylesheet {
contents,
media: media,
media,
shared_lock: lock,
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>
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 = Parser::new(&mut input);
@ -439,7 +439,7 @@ impl Declaration {
let id =
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| {
PropertyDeclaration::parse_into(&mut declarations, id, &context, input)
.map_err(|_| input.new_custom_error(()))

View file

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