summaryrefslogtreecommitdiffstats
path: root/servo/components/style/stylesheets/rules_iterator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/stylesheets/rules_iterator.rs')
-rw-r--r--servo/components/style/stylesheets/rules_iterator.rs331
1 files changed, 331 insertions, 0 deletions
diff --git a/servo/components/style/stylesheets/rules_iterator.rs b/servo/components/style/stylesheets/rules_iterator.rs
new file mode 100644
index 0000000000..76d41c8184
--- /dev/null
+++ b/servo/components/style/stylesheets/rules_iterator.rs
@@ -0,0 +1,331 @@
+/* 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<C>,
+}
+
+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<slice::Iter<'a, CssRule>> {
+ *effective = true;
+ match *rule {
+ CssRule::Namespace(_) |
+ CssRule::FontFace(_) |
+ CssRule::CounterStyle(_) |
+ CssRule::Keyframes(_) |
+ CssRule::Margin(_) |
+ CssRule::Property(_) |
+ CssRule::LayerStatement(_) |
+ CssRule::FontFeatureValues(_) |
+ CssRule::FontPaletteValues(_) => None,
+ CssRule::Page(ref page_rule) => {
+ let page_rule = page_rule.read_with(guard);
+ let rules = page_rule.rules.read_with(guard);
+ Some(rules.0.iter())
+ },
+ 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<Self::Item> {
+ 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::<AllRules>::children(rule, device, quirks_mode, guard, &mut false);
+ EffectiveRulesIterator::new(device, quirks_mode, guard, children.unwrap_or([].iter()))
+ }
+}