tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

rules_iterator.rs (11770B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! An iterator over a list of rules.
      6 
      7 use crate::context::QuirksMode;
      8 use crate::media_queries::Device;
      9 use crate::shared_lock::SharedRwLockReadGuard;
     10 use crate::stylesheets::{
     11    CssRule, CssRuleRef, CustomMediaEvaluator, CustomMediaMap, DocumentRule, ImportRule, MediaRule,
     12    SupportsRule,
     13 };
     14 use smallvec::SmallVec;
     15 use std::ops::Deref;
     16 use std::slice;
     17 
     18 /// An iterator over a list of rules.
     19 pub struct RulesIterator<'a, 'b, C, CMM>
     20 where
     21    'b: 'a,
     22    C: NestedRuleIterationCondition + 'static,
     23    CMM: Deref<Target = CustomMediaMap>,
     24 {
     25    device: &'a Device,
     26    quirks_mode: QuirksMode,
     27    custom_media: CMM,
     28    guard: &'a SharedRwLockReadGuard<'b>,
     29    stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
     30    _phantom: ::std::marker::PhantomData<C>,
     31 }
     32 
     33 impl<'a, 'b, C, CMM> RulesIterator<'a, 'b, C, CMM>
     34 where
     35    'b: 'a,
     36    C: NestedRuleIterationCondition + 'static,
     37    CMM: Deref<Target = CustomMediaMap>,
     38 {
     39    /// Returns the custom media map passed at construction.
     40    pub fn custom_media(&mut self) -> &mut CMM {
     41        &mut self.custom_media
     42    }
     43 
     44    /// Creates a new `RulesIterator` to iterate over `rules`.
     45    pub fn new(
     46        device: &'a Device,
     47        quirks_mode: QuirksMode,
     48        custom_media: CMM,
     49        guard: &'a SharedRwLockReadGuard<'b>,
     50        rules: slice::Iter<'a, CssRule>,
     51    ) -> Self {
     52        let mut stack = SmallVec::new();
     53        stack.push(rules);
     54        Self {
     55            device,
     56            quirks_mode,
     57            custom_media,
     58            guard,
     59            stack,
     60            _phantom: ::std::marker::PhantomData,
     61        }
     62    }
     63 
     64    /// Skips all the remaining children of the last nested rule processed.
     65    pub fn skip_children(&mut self) {
     66        self.stack.pop();
     67    }
     68 
     69    /// Returns the children of `rule`, and whether `rule` is effective.
     70    pub fn children(
     71        rule: &'a CssRule,
     72        device: &'a Device,
     73        quirks_mode: QuirksMode,
     74        custom_media_map: &CustomMediaMap,
     75        guard: &'a SharedRwLockReadGuard<'_>,
     76        effective: &mut bool,
     77    ) -> Option<slice::Iter<'a, CssRule>> {
     78        *effective = true;
     79        match *rule {
     80            CssRule::Namespace(_)
     81            | CssRule::FontFace(_)
     82            | CssRule::CounterStyle(_)
     83            | CssRule::CustomMedia(_)
     84            | CssRule::Keyframes(_)
     85            | CssRule::Margin(_)
     86            | CssRule::Property(_)
     87            | CssRule::LayerStatement(_)
     88            | CssRule::FontFeatureValues(_)
     89            | CssRule::FontPaletteValues(_)
     90            | CssRule::NestedDeclarations(_)
     91            | CssRule::PositionTry(_) => None,
     92            CssRule::Page(ref page_rule) => {
     93                let page_rule = page_rule.read_with(guard);
     94                let rules = page_rule.rules.read_with(guard);
     95                Some(rules.0.iter())
     96            },
     97            CssRule::Style(ref style_rule) => {
     98                let style_rule = style_rule.read_with(guard);
     99                style_rule
    100                    .rules
    101                    .as_ref()
    102                    .map(|r| r.read_with(guard).0.iter())
    103            },
    104            CssRule::Import(ref import_rule) => {
    105                let import_rule = import_rule.read_with(guard);
    106                if !C::process_import(guard, device, quirks_mode, custom_media_map, import_rule) {
    107                    *effective = false;
    108                    return None;
    109                }
    110                Some(import_rule.stylesheet.rules(guard).iter())
    111            },
    112            CssRule::Document(ref doc_rule) => {
    113                if !C::process_document(guard, device, quirks_mode, doc_rule) {
    114                    *effective = false;
    115                    return None;
    116                }
    117                Some(doc_rule.rules.read_with(guard).0.iter())
    118            },
    119            CssRule::Container(ref container_rule) => {
    120                Some(container_rule.rules.read_with(guard).0.iter())
    121            },
    122            CssRule::Media(ref media_rule) => {
    123                if !C::process_media(guard, device, quirks_mode, custom_media_map, media_rule) {
    124                    *effective = false;
    125                    return None;
    126                }
    127                Some(media_rule.rules.read_with(guard).0.iter())
    128            },
    129            CssRule::Supports(ref supports_rule) => {
    130                if !C::process_supports(guard, device, quirks_mode, supports_rule) {
    131                    *effective = false;
    132                    return None;
    133                }
    134                Some(supports_rule.rules.read_with(guard).0.iter())
    135            },
    136            CssRule::LayerBlock(ref layer_rule) => Some(layer_rule.rules.read_with(guard).0.iter()),
    137            CssRule::Scope(ref rule) => Some(rule.rules.read_with(guard).0.iter()),
    138            CssRule::StartingStyle(ref rule) => Some(rule.rules.read_with(guard).0.iter()),
    139        }
    140    }
    141 }
    142 
    143 impl<'a, 'b, C, CMM> Iterator for RulesIterator<'a, 'b, C, CMM>
    144 where
    145    'b: 'a,
    146    C: NestedRuleIterationCondition + 'static,
    147    CMM: Deref<Target = CustomMediaMap>,
    148 {
    149    type Item = &'a CssRule;
    150 
    151    fn next(&mut self) -> Option<Self::Item> {
    152        while !self.stack.is_empty() {
    153            let rule = {
    154                let nested_iter = self.stack.last_mut().unwrap();
    155                match nested_iter.next() {
    156                    Some(r) => r,
    157                    None => {
    158                        self.stack.pop();
    159                        continue;
    160                    },
    161                }
    162            };
    163 
    164            let mut effective = true;
    165            let children = Self::children(
    166                rule,
    167                self.device,
    168                self.quirks_mode,
    169                &self.custom_media,
    170                self.guard,
    171                &mut effective,
    172            );
    173            if !effective {
    174                continue;
    175            }
    176 
    177            if let Some(children) = children {
    178                // NOTE: It's important that `children` gets pushed even if
    179                // empty, so that `skip_children()` works as expected.
    180                self.stack.push(children);
    181            }
    182 
    183            return Some(rule);
    184        }
    185 
    186        None
    187    }
    188 }
    189 
    190 /// RulesIterator.
    191 pub trait NestedRuleIterationCondition {
    192    /// Whether we should process the nested rules in a given `@import` rule.
    193    fn process_import(
    194        guard: &SharedRwLockReadGuard,
    195        device: &Device,
    196        quirks_mode: QuirksMode,
    197        custom_media_map: &CustomMediaMap,
    198        rule: &ImportRule,
    199    ) -> bool;
    200 
    201    /// Whether we should process the nested rules in a given `@media` rule.
    202    fn process_media(
    203        guard: &SharedRwLockReadGuard,
    204        device: &Device,
    205        quirks_mode: QuirksMode,
    206        custom_media_map: &CustomMediaMap,
    207        rule: &MediaRule,
    208    ) -> bool;
    209 
    210    /// Whether we should process the nested rules in a given `@-moz-document`
    211    /// rule.
    212    fn process_document(
    213        guard: &SharedRwLockReadGuard,
    214        device: &Device,
    215        quirks_mode: QuirksMode,
    216        rule: &DocumentRule,
    217    ) -> bool;
    218 
    219    /// Whether we should process the nested rules in a given `@supports` rule.
    220    fn process_supports(
    221        guard: &SharedRwLockReadGuard,
    222        device: &Device,
    223        quirks_mode: QuirksMode,
    224        rule: &SupportsRule,
    225    ) -> bool;
    226 }
    227 
    228 /// A struct that represents the condition that a rule applies to the document.
    229 pub struct EffectiveRules;
    230 
    231 impl EffectiveRules {
    232    /// Returns whether a given rule is effective.
    233    pub fn is_effective(
    234        guard: &SharedRwLockReadGuard,
    235        device: &Device,
    236        quirks_mode: QuirksMode,
    237        custom_media_map: &CustomMediaMap,
    238        rule: &CssRuleRef,
    239    ) -> bool {
    240        match *rule {
    241            CssRuleRef::Import(import_rule) => {
    242                let import_rule = import_rule.read_with(guard);
    243                Self::process_import(guard, device, quirks_mode, custom_media_map, import_rule)
    244            },
    245            CssRuleRef::Document(doc_rule) => {
    246                Self::process_document(guard, device, quirks_mode, doc_rule)
    247            },
    248            CssRuleRef::Media(media_rule) => {
    249                Self::process_media(guard, device, quirks_mode, custom_media_map, media_rule)
    250            },
    251            CssRuleRef::Supports(supports_rule) => {
    252                Self::process_supports(guard, device, quirks_mode, supports_rule)
    253            },
    254            _ => true,
    255        }
    256    }
    257 }
    258 
    259 impl NestedRuleIterationCondition for EffectiveRules {
    260    fn process_import(
    261        guard: &SharedRwLockReadGuard,
    262        device: &Device,
    263        quirks_mode: QuirksMode,
    264        custom_media_map: &CustomMediaMap,
    265        rule: &ImportRule,
    266    ) -> bool {
    267        match rule.stylesheet.media(guard) {
    268            Some(m) => m.evaluate(
    269                device,
    270                quirks_mode,
    271                &mut CustomMediaEvaluator::new(custom_media_map, guard),
    272            ),
    273            None => true,
    274        }
    275    }
    276 
    277    fn process_media(
    278        guard: &SharedRwLockReadGuard,
    279        device: &Device,
    280        quirks_mode: QuirksMode,
    281        custom_media_map: &CustomMediaMap,
    282        rule: &MediaRule,
    283    ) -> bool {
    284        rule.media_queries.read_with(guard).evaluate(
    285            device,
    286            quirks_mode,
    287            &mut CustomMediaEvaluator::new(custom_media_map, guard),
    288        )
    289    }
    290 
    291    fn process_document(
    292        _: &SharedRwLockReadGuard,
    293        device: &Device,
    294        _: QuirksMode,
    295        rule: &DocumentRule,
    296    ) -> bool {
    297        rule.condition.evaluate(device)
    298    }
    299 
    300    fn process_supports(
    301        _: &SharedRwLockReadGuard,
    302        _: &Device,
    303        _: QuirksMode,
    304        rule: &SupportsRule,
    305    ) -> bool {
    306        rule.enabled
    307    }
    308 }
    309 
    310 /// A filter that processes all the rules in a rule list.
    311 pub struct AllRules;
    312 
    313 impl NestedRuleIterationCondition for AllRules {
    314    fn process_import(
    315        _: &SharedRwLockReadGuard,
    316        _: &Device,
    317        _: QuirksMode,
    318        _: &CustomMediaMap,
    319        _: &ImportRule,
    320    ) -> bool {
    321        true
    322    }
    323 
    324    fn process_media(
    325        _: &SharedRwLockReadGuard,
    326        _: &Device,
    327        _: QuirksMode,
    328        _: &CustomMediaMap,
    329        _: &MediaRule,
    330    ) -> bool {
    331        true
    332    }
    333 
    334    fn process_document(
    335        _: &SharedRwLockReadGuard,
    336        _: &Device,
    337        _: QuirksMode,
    338        _: &DocumentRule,
    339    ) -> bool {
    340        true
    341    }
    342 
    343    fn process_supports(
    344        _: &SharedRwLockReadGuard,
    345        _: &Device,
    346        _: QuirksMode,
    347        _: &SupportsRule,
    348    ) -> bool {
    349        true
    350    }
    351 }
    352 
    353 /// An iterator over all the effective rules of a stylesheet.
    354 ///
    355 /// NOTE: This iterator recurses into `@import` rules.
    356 pub type EffectiveRulesIterator<'a, 'b, CMM> = RulesIterator<'a, 'b, EffectiveRules, CMM>;
    357 
    358 impl<'a, 'b, CMM> EffectiveRulesIterator<'a, 'b, CMM>
    359 where
    360    CMM: Deref<Target = CustomMediaMap>,
    361 {
    362    /// Returns an iterator over the effective children of a rule, even if
    363    /// `rule` itself is not effective.
    364    pub fn effective_children(
    365        device: &'a Device,
    366        quirks_mode: QuirksMode,
    367        custom_media_map: CMM,
    368        guard: &'a SharedRwLockReadGuard<'b>,
    369        rule: &'a CssRule,
    370    ) -> Self {
    371        let children = RulesIterator::<AllRules, CMM>::children(
    372            rule,
    373            device,
    374            quirks_mode,
    375            &custom_media_map,
    376            guard,
    377            &mut false,
    378        );
    379        EffectiveRulesIterator::new(
    380            device,
    381            quirks_mode,
    382            custom_media_map,
    383            guard,
    384            children.unwrap_or([].iter()),
    385        )
    386    }
    387 }