/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
/// The information we need particularly to do CSSOM insertRule stuff. pubstruct InsertRuleContext<'a> { /// The rule list we're about to insert into. pub rule_list: &'a [CssRule], /// The index we're about to get inserted at. pub index: usize, /// The containing rule types of our ancestors. pub containing_rule_types: CssRuleTypes, /// Rule type determining if and how we parse relative selector syntax. pub parse_relative_rule_type: Option<CssRuleType>,
}
impl<'a> InsertRuleContext<'a> { /// Returns the max rule state allowable for insertion at a given index in /// the rule list. pubfn max_rule_state_at_index(&self, index: usize) -> State { let rule = matchself.rule_list.get(index) {
Some(rule) => rule,
None => return State::Body,
}; match rule {
CssRule::Import(..) => State::Imports,
CssRule::Namespace(..) => State::Namespaces,
CssRule::LayerStatement(..) => { // If there are @import / @namespace after this layer, then // we're in the early-layers phase, otherwise we're in the body // and everything is fair game. let next_non_layer_statement_rule = self.rule_list[index + 1..]
.iter()
.find(|r| !matches!(*r, CssRule::LayerStatement(..))); iflet Some(non_layer) = next_non_layer_statement_rule { if matches!(*non_layer, CssRule::Import(..) | CssRule::Namespace(..)) { return State::EarlyLayers;
}
}
State::Body
},
_ => State::Body,
}
}
}
/// The parser for the top-level rules in a stylesheet. pubstruct 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. pub loader: Option<&'a dyn StylesheetLoader>, /// The top-level parser context. pub context: ParserContext<'a>, /// The current state of the parser. pub state: State, /// Whether we have tried to parse was invalid due to being in the wrong /// place (e.g. an @import rule was found while in the `Body` state). Reset /// to `false` when `take_had_hierarchy_error` is called. pub dom_error: Option<RulesMutateError>, /// The info we need insert a rule in a list. pub insert_rule_context: Option<InsertRuleContext<'a>>, /// Whether @import rules will be allowed. pub allow_import_rules: AllowImportRules, /// Whether to keep declarations into first_declaration_block, rather than turning it into a /// nested declarations rule. pub wants_first_declaration_block: bool, /// The first declaration block, only relevant when wants_first_declaration_block is true. pub first_declaration_block: PropertyDeclarationBlock, /// Parser state for declaration blocks in either nested rules or style rules. pub declaration_parser_state: DeclarationParserState<'i>, /// State we keep around only for error reporting purposes. Right now that contains just the /// selectors stack for nesting, if any. /// /// TODO(emilio): This isn't populated properly for `insertRule()` but... pub error_reporting_state: Vec<SelectorList<SelectorImpl>>, /// The rules we've parsed so far. pub rules: Vec<CssRule>,
}
impl<'a, 'i> TopLevelRuleParser<'a, 'i> { #[inline] fn nested(&mutself) -> &mut NestedRuleParser<'a, 'i> { // SAFETY: NestedRuleParser is just a repr(transparent) wrapper over TopLevelRuleParser
const_assert!(
std::mem::size_of::<TopLevelRuleParser<'static, 'static>>()
== std::mem::size_of::<NestedRuleParser<'static, 'static>>()
);
const_assert!(
std::mem::align_of::<TopLevelRuleParser<'static, 'static>>()
== std::mem::align_of::<NestedRuleParser<'static, 'static>>()
); unsafe { &mut *(selfas *mut _ as *mut NestedRuleParser<'a, 'i>) }
}
/// Returns the current state of the parser. #[inline] pubfn state(&self) -> State { self.state
}
/// If we're in a nested state, this returns whether declarations can be parsed. See /// RuleBodyItemParser::parse_declarations(). #[inline] pubfn can_parse_declarations(&self) -> bool { // We also have to check for page rules here because we currently don't // have a bespoke parser for page rules, and parse them as though they // are style rules. // Scope rules can have direct declarations, behaving as if `:where(:scope)`. // See https://drafts.csswg.org/css-cascade-6/#scoped-declarations self.in_specified_rule(
CssRuleType::Style.bit() | CssRuleType::Page.bit() | CssRuleType::Scope.bit(),
)
}
/// Checks whether we can parse a rule that would transition us to /// `new_state`. /// /// This is usually a simple branch, but we may need more bookkeeping if /// doing `insertRule` from CSSOM. fn check_state(&mutself, new_state: State) -> bool { ifself.state > new_state { self.dom_error = Some(RulesMutateError::HierarchyRequest); returnfalse;
}
let max_rule_state = ctx.max_rule_state_at_index(ctx.index); if new_state > max_rule_state { self.dom_error = Some(RulesMutateError::HierarchyRequest); returnfalse;
}
// 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..]
.iter()
.any(|r| !matches!(*r, CssRule::Namespace(..)))
{ self.dom_error = Some(RulesMutateError::InvalidState); returnfalse;
}
true
}
}
/// The current state of the parser. #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] pubenum State { /// We haven't started parsing rules.
Start = 1, /// We're parsing early `@layer` statement rules.
EarlyLayers = 2, /// We're parsing `@import` and early `@layer` statement rules.
Imports = 3, /// We're parsing `@namespace` rules.
Namespaces = 4, /// We're parsing the main body of the stylesheet.
Body = 5,
}
/// A rule prelude for at-rule with block. pubenum AtRulePrelude { /// A @font-face rule prelude.
FontFace, /// A @font-feature-values rule prelude, with its FamilyName list.
FontFeatureValues(Vec<FamilyName>), /// A @font-palette-values rule prelude, with its identifier.
FontPaletteValues(DashedIdent), /// A @counter-style rule prelude, with its counter style name.
CounterStyle(CustomIdent), /// A @media rule prelude, with its media queries.
Media(Arc<Locked<MediaList>>), /// A @container rule prelude.
Container(ArcSlice<ContainerCondition>), /// An @supports rule, with its conditional
Supports(SupportsCondition), /// A @keyframes rule, with its animation name and vendor prefix if exists.
Keyframes(KeyframesName, Option<VendorPrefix>), /// A @page rule prelude, with its page name if it exists.
Page(PageSelectors), /// A @property rule prelude.
Property(PropertyRuleName), /// A @document rule, with its conditional.
Document(DocumentCondition), /// A @import rule prelude.
Import(
CssUrl,
Arc<Locked<MediaList>>,
Option<ImportSupportsCondition>,
ImportLayer,
), /// A @margin rule prelude.
Margin(MarginRuleType), /// A @namespace rule prelude.
Namespace(Option<Prefix>, Namespace), /// A @layer rule prelude.
Layer(Vec<LayerName>), /// A @scope rule prelude.
Scope(ScopeBounds), /// A @starting-style prelude.
StartingStyle, /// A @appearance-base prelude (UA sheets only).
AppearanceBase, /// A @position-try prelude for Anchor Positioning.
PositionTry(DashedIdent), /// A @custom-media prelude.
CustomMedia(DashedIdent, CustomMediaCondition), /// A @view-transition prelude.
ViewTransition,
}
impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> { type Prelude = AtRulePrelude; type AtRule = SourcePosition; type Error = StyleParseErrorKind<'i>;
// FIXME(emilio): We should always be able to have a loader // around! See bug 1533783. ifself.loader.is_none() {
error!("Saw @import rule, but no way to trigger the load"); return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
}
let url_string = input.expect_url_or_string()?.as_ref().to_owned(); let url = CssUrl::new_from_untainted_string(url_string, &self.context, CorsMode::None);
let (layer, supports) = ImportRule::parse_layer_and_supports(input, &mutself.context);
let media = MediaList::parse(&mutself.context, input); let media = Arc::new(self.shared_lock.wrap(media));
let prefix = input.try_parse(|i| i.expect_ident_cloned())
.map(|s| Prefix::from(s.as_ref())).ok(); let maybe_namespace = match input.expect_url_or_string() {
Ok(url_or_string) => url_or_string,
Err(BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location }) => { return Err(location.new_custom_error(StyleParseErrorKind::UnexpectedTokenWithinNamespace(t)))
}
Err(e) => return Err(e.into()),
}; let url = Namespace::from(maybe_namespace.as_ref()); return Ok(AtRulePrelude::Namespace(prefix, url));
}, // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet // anything left is invalid. "charset" => { self.dom_error = Some(RulesMutateError::HierarchyRequest); return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
}, "layer" => { let state_to_check = ifself.state <= State::EarlyLayers { // The real state depends on whether there's a block or not. // We don't know that yet, but the parse_block check deals // with that.
State::EarlyLayers
} else {
State::Body
}; if !self.check_state(state_to_check) { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
},
_ => { // All other rules have blocks, so we do this check early in // parse_block instead.
}
}
impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> { type Prelude = SelectorList<SelectorImpl>; type QualifiedRule = SourcePosition; type Error = StyleParseErrorKind<'i>;
fn nest_for_rule<R>(&mutself, rule_type: CssRuleType, cb: impl FnOnce(&mutSelf) -> R) -> R { let old = self.context.nesting_context.save(rule_type); let r = cb(self); self.context.nesting_context.restore(old);
r
}
impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> { type Prelude = SelectorList<SelectorImpl>; type QualifiedRule = (); type Error = StyleParseErrorKind<'i>;
/// 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.can_parse_declarations()
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.