From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../components/style/stylesheets/rules_iterator.rs | 327 +++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 servo/components/style/stylesheets/rules_iterator.rs (limited to 'servo/components/style/stylesheets/rules_iterator.rs') diff --git a/servo/components/style/stylesheets/rules_iterator.rs b/servo/components/style/stylesheets/rules_iterator.rs new file mode 100644 index 0000000000..950bcd238e --- /dev/null +++ b/servo/components/style/stylesheets/rules_iterator.rs @@ -0,0 +1,327 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! An iterator over a list of rules. + +use crate::context::QuirksMode; +use crate::media_queries::Device; +use crate::shared_lock::SharedRwLockReadGuard; +use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule}; +use smallvec::SmallVec; +use std::slice; + +/// An iterator over a list of rules. +pub struct RulesIterator<'a, 'b, C> +where + 'b: 'a, + C: NestedRuleIterationCondition + 'static, +{ + device: &'a Device, + quirks_mode: QuirksMode, + guard: &'a SharedRwLockReadGuard<'b>, + stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>, + _phantom: ::std::marker::PhantomData, +} + +impl<'a, 'b, C> RulesIterator<'a, 'b, C> +where + 'b: 'a, + C: NestedRuleIterationCondition + 'static, +{ + /// Creates a new `RulesIterator` to iterate over `rules`. + pub fn new( + device: &'a Device, + quirks_mode: QuirksMode, + guard: &'a SharedRwLockReadGuard<'b>, + rules: slice::Iter<'a, CssRule>, + ) -> Self { + let mut stack = SmallVec::new(); + stack.push(rules); + Self { + device, + quirks_mode, + guard, + stack, + _phantom: ::std::marker::PhantomData, + } + } + + /// Skips all the remaining children of the last nested rule processed. + pub fn skip_children(&mut self) { + self.stack.pop(); + } + + /// Returns the children of `rule`, and whether `rule` is effective. + pub fn children( + rule: &'a CssRule, + device: &'a Device, + quirks_mode: QuirksMode, + guard: &'a SharedRwLockReadGuard<'_>, + effective: &mut bool, + ) -> Option> { + *effective = true; + match *rule { + CssRule::Namespace(_) | + CssRule::FontFace(_) | + CssRule::CounterStyle(_) | + CssRule::Viewport(_) | + CssRule::Keyframes(_) | + CssRule::Page(_) | + CssRule::Property(_) | + CssRule::LayerStatement(_) | + CssRule::FontFeatureValues(_) | + CssRule::FontPaletteValues(_) => None, + CssRule::Style(ref style_rule) => { + let style_rule = style_rule.read_with(guard); + style_rule + .rules + .as_ref() + .map(|r| r.read_with(guard).0.iter()) + }, + CssRule::Import(ref import_rule) => { + let import_rule = import_rule.read_with(guard); + if !C::process_import(guard, device, quirks_mode, import_rule) { + *effective = false; + return None; + } + Some(import_rule.stylesheet.rules(guard).iter()) + }, + CssRule::Document(ref doc_rule) => { + if !C::process_document(guard, device, quirks_mode, doc_rule) { + *effective = false; + return None; + } + Some(doc_rule.rules.read_with(guard).0.iter()) + }, + CssRule::Container(ref container_rule) => { + Some(container_rule.rules.read_with(guard).0.iter()) + }, + CssRule::Media(ref media_rule) => { + if !C::process_media(guard, device, quirks_mode, media_rule) { + *effective = false; + return None; + } + Some(media_rule.rules.read_with(guard).0.iter()) + }, + CssRule::Supports(ref supports_rule) => { + if !C::process_supports(guard, device, quirks_mode, supports_rule) { + *effective = false; + return None; + } + Some(supports_rule.rules.read_with(guard).0.iter()) + }, + CssRule::LayerBlock(ref layer_rule) => Some(layer_rule.rules.read_with(guard).0.iter()), + } + } +} + +impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C> +where + 'b: 'a, + C: NestedRuleIterationCondition + 'static, +{ + type Item = &'a CssRule; + + fn next(&mut self) -> Option { + while !self.stack.is_empty() { + let rule = { + let nested_iter = self.stack.last_mut().unwrap(); + match nested_iter.next() { + Some(r) => r, + None => { + self.stack.pop(); + continue; + }, + } + }; + + let mut effective = true; + let children = Self::children( + rule, + self.device, + self.quirks_mode, + self.guard, + &mut effective, + ); + if !effective { + continue; + } + + if let Some(children) = children { + // NOTE: It's important that `children` gets pushed even if + // empty, so that `skip_children()` works as expected. + self.stack.push(children); + } + + return Some(rule); + } + + None + } +} + +/// RulesIterator. +pub trait NestedRuleIterationCondition { + /// Whether we should process the nested rules in a given `@import` rule. + fn process_import( + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + rule: &ImportRule, + ) -> bool; + + /// Whether we should process the nested rules in a given `@media` rule. + fn process_media( + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + rule: &MediaRule, + ) -> bool; + + /// Whether we should process the nested rules in a given `@-moz-document` + /// rule. + fn process_document( + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + rule: &DocumentRule, + ) -> bool; + + /// Whether we should process the nested rules in a given `@supports` rule. + fn process_supports( + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + rule: &SupportsRule, + ) -> bool; +} + +/// A struct that represents the condition that a rule applies to the document. +pub struct EffectiveRules; + +impl EffectiveRules { + /// Returns whether a given rule is effective. + pub fn is_effective( + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + rule: &CssRule, + ) -> bool { + match *rule { + CssRule::Import(ref import_rule) => { + let import_rule = import_rule.read_with(guard); + Self::process_import(guard, device, quirks_mode, import_rule) + }, + CssRule::Document(ref doc_rule) => { + Self::process_document(guard, device, quirks_mode, doc_rule) + }, + CssRule::Media(ref media_rule) => { + Self::process_media(guard, device, quirks_mode, media_rule) + }, + CssRule::Supports(ref supports_rule) => { + Self::process_supports(guard, device, quirks_mode, supports_rule) + }, + _ => true, + } + } +} + +impl NestedRuleIterationCondition for EffectiveRules { + fn process_import( + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + rule: &ImportRule, + ) -> bool { + match rule.stylesheet.media(guard) { + Some(m) => m.evaluate(device, quirks_mode), + None => true, + } + } + + fn process_media( + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + rule: &MediaRule, + ) -> bool { + rule.media_queries + .read_with(guard) + .evaluate(device, quirks_mode) + } + + fn process_document( + _: &SharedRwLockReadGuard, + device: &Device, + _: QuirksMode, + rule: &DocumentRule, + ) -> bool { + rule.condition.evaluate(device) + } + + fn process_supports( + _: &SharedRwLockReadGuard, + _: &Device, + _: QuirksMode, + rule: &SupportsRule, + ) -> bool { + rule.enabled + } +} + +/// A filter that processes all the rules in a rule list. +pub struct AllRules; + +impl NestedRuleIterationCondition for AllRules { + fn process_import( + _: &SharedRwLockReadGuard, + _: &Device, + _: QuirksMode, + _: &ImportRule, + ) -> bool { + true + } + + fn process_media(_: &SharedRwLockReadGuard, _: &Device, _: QuirksMode, _: &MediaRule) -> bool { + true + } + + fn process_document( + _: &SharedRwLockReadGuard, + _: &Device, + _: QuirksMode, + _: &DocumentRule, + ) -> bool { + true + } + + fn process_supports( + _: &SharedRwLockReadGuard, + _: &Device, + _: QuirksMode, + _: &SupportsRule, + ) -> bool { + true + } +} + +/// An iterator over all the effective rules of a stylesheet. +/// +/// NOTE: This iterator recurses into `@import` rules. +pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>; + +impl<'a, 'b> EffectiveRulesIterator<'a, 'b> { + /// Returns an iterator over the effective children of a rule, even if + /// `rule` itself is not effective. + pub fn effective_children( + device: &'a Device, + quirks_mode: QuirksMode, + guard: &'a SharedRwLockReadGuard<'b>, + rule: &'a CssRule, + ) -> Self { + let children = + RulesIterator::::children(rule, device, quirks_mode, guard, &mut false); + EffectiveRulesIterator::new(device, quirks_mode, guard, children.unwrap_or([].iter())) + } +} -- cgit v1.2.3