tor-browser

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

mod.rs (15879B)


      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 #![deny(unsafe_code)]
      6 
      7 //! The rule tree.
      8 
      9 use crate::applicable_declarations::{ApplicableDeclarationList, CascadePriority};
     10 use crate::properties::{LonghandIdSet, PropertyDeclarationBlock};
     11 use crate::shared_lock::{Locked, StylesheetGuards};
     12 use crate::stylesheets::layer_rule::LayerOrder;
     13 use servo_arc::ArcBorrow;
     14 use smallvec::SmallVec;
     15 use std::io::{self, Write};
     16 
     17 mod core;
     18 mod level;
     19 mod map;
     20 mod source;
     21 mod unsafe_box;
     22 
     23 pub use self::core::{RuleTree, StrongRuleNode};
     24 pub use self::level::{CascadeLevel, ShadowCascadeOrder};
     25 pub use self::source::StyleSource;
     26 
     27 impl RuleTree {
     28    fn dump<W: Write>(&self, guards: &StylesheetGuards, writer: &mut W) {
     29        let _ = writeln!(writer, " + RuleTree");
     30        self.root().dump(guards, writer, 0);
     31    }
     32 
     33    /// Dump the rule tree to stdout.
     34    pub fn dump_stdout(&self, guards: &StylesheetGuards) {
     35        let mut stdout = io::stdout();
     36        self.dump(guards, &mut stdout);
     37    }
     38 
     39    /// Inserts the given rules, that must be in proper order by specifity, and
     40    /// returns the corresponding rule node representing the last inserted one.
     41    ///
     42    /// !important rules are detected and inserted into the appropriate position
     43    /// in the rule tree. This allows selector matching to ignore importance,
     44    /// while still maintaining the appropriate cascade order in the rule tree.
     45    pub fn insert_ordered_rules_with_important<'a, I>(
     46        &self,
     47        iter: I,
     48        guards: &StylesheetGuards,
     49    ) -> StrongRuleNode
     50    where
     51        I: Iterator<Item = (StyleSource, CascadePriority)>,
     52    {
     53        use self::CascadeLevel::*;
     54        let mut current = self.root().clone();
     55 
     56        let mut found_important = false;
     57 
     58        let mut important_author = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
     59        let mut important_user = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
     60        let mut important_ua = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
     61        let mut transition = None;
     62 
     63        for (source, priority) in iter {
     64            let level = priority.cascade_level();
     65            debug_assert!(!level.is_important(), "Important levels handled internally");
     66 
     67            let any_important = {
     68                let pdb = source.read(level.guard(guards));
     69                pdb.any_important()
     70            };
     71 
     72            if any_important {
     73                found_important = true;
     74                match level {
     75                    AuthorNormal { .. } => {
     76                        important_author.push((source.clone(), priority.important()))
     77                    },
     78                    UANormal => important_ua.push((source.clone(), priority.important())),
     79                    UserNormal => important_user.push((source.clone(), priority.important())),
     80                    _ => {},
     81                };
     82            }
     83 
     84            // We don't optimize out empty rules, even though we could.
     85            //
     86            // Inspector relies on every rule being inserted in the normal level
     87            // at least once, in order to return the rules with the correct
     88            // specificity order.
     89            //
     90            // TODO(emilio): If we want to apply these optimizations without
     91            // breaking inspector's expectations, we'd need to run
     92            // selector-matching again at the inspector's request. That may or
     93            // may not be a better trade-off.
     94            if matches!(level, Transitions) && found_important {
     95                // There can be at most one transition, and it will come at
     96                // the end of the iterator. Stash it and apply it after
     97                // !important rules.
     98                debug_assert!(transition.is_none());
     99                transition = Some(source);
    100            } else {
    101                current = current.ensure_child(self.root(), source, priority);
    102            }
    103        }
    104 
    105        // Early-return in the common case of no !important declarations.
    106        if !found_important {
    107            return current;
    108        }
    109 
    110        // Insert important declarations, in order of increasing importance,
    111        // followed by any transition rule.
    112        //
    113        // Important rules are sorted differently from unimportant ones by
    114        // shadow order and cascade order.
    115        if !important_author.is_empty()
    116            && important_author.first().unwrap().1 != important_author.last().unwrap().1
    117        {
    118            // We only need to sort if the important rules come from
    119            // different trees, but we need this sort to be stable.
    120            //
    121            // FIXME(emilio): This could maybe be smarter, probably by chunking
    122            // the important rules while inserting, and iterating the outer
    123            // chunks in reverse order.
    124            //
    125            // That is, if we have rules with levels like: -1 -1 -1 0 0 0 1 1 1,
    126            // we're really only sorting the chunks, while keeping elements
    127            // inside the same chunk already sorted. Seems like we could try to
    128            // keep a SmallVec-of-SmallVecs with the chunks and just iterate the
    129            // outer in reverse.
    130            important_author.sort_by_key(|&(_, priority)| priority);
    131        }
    132 
    133        for (source, priority) in important_author.drain(..) {
    134            current = current.ensure_child(self.root(), source, priority);
    135        }
    136 
    137        for (source, priority) in important_user.drain(..) {
    138            current = current.ensure_child(self.root(), source, priority);
    139        }
    140 
    141        for (source, priority) in important_ua.drain(..) {
    142            current = current.ensure_child(self.root(), source, priority);
    143        }
    144 
    145        if let Some(source) = transition {
    146            current = current.ensure_child(
    147                self.root(),
    148                source,
    149                CascadePriority::new(Transitions, LayerOrder::root()),
    150            );
    151        }
    152 
    153        current
    154    }
    155 
    156    /// Given a list of applicable declarations, insert the rules and return the
    157    /// corresponding rule node.
    158    pub fn compute_rule_node(
    159        &self,
    160        applicable_declarations: &mut ApplicableDeclarationList,
    161        guards: &StylesheetGuards,
    162    ) -> StrongRuleNode {
    163        self.insert_ordered_rules_with_important(
    164            applicable_declarations.drain(..).map(|d| d.for_rule_tree()),
    165            guards,
    166        )
    167    }
    168 
    169    /// Insert the given rules, that must be in proper order by specifity, and
    170    /// return the corresponding rule node representing the last inserted one.
    171    pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
    172    where
    173        I: Iterator<Item = (StyleSource, CascadePriority)>,
    174    {
    175        self.insert_ordered_rules_from(self.root().clone(), iter)
    176    }
    177 
    178    fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode
    179    where
    180        I: Iterator<Item = (StyleSource, CascadePriority)>,
    181    {
    182        let mut current = from;
    183        for (source, priority) in iter {
    184            current = current.ensure_child(self.root(), source, priority);
    185        }
    186        current
    187    }
    188 
    189    /// Replaces a rule in a given level (if present) for another rule.
    190    ///
    191    /// Returns the resulting node that represents the new path, or None if
    192    /// the old path is still valid.
    193    pub fn update_rule_at_level(
    194        &self,
    195        level: CascadeLevel,
    196        layer_order: LayerOrder,
    197        pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
    198        path: &StrongRuleNode,
    199        guards: &StylesheetGuards,
    200        important_rules_changed: &mut bool,
    201    ) -> Option<StrongRuleNode> {
    202        // TODO(emilio): Being smarter with lifetimes we could avoid a bit of
    203        // the refcount churn.
    204        let mut current = path.clone();
    205        *important_rules_changed = false;
    206 
    207        // First walk up until the first less-or-equally specific rule.
    208        let mut children = SmallVec::<[_; 10]>::new();
    209        while current.cascade_priority().cascade_level() > level {
    210            children.push((
    211                current.style_source().unwrap().clone(),
    212                current.cascade_priority(),
    213            ));
    214            current = current.parent().unwrap().clone();
    215        }
    216 
    217        let cascade_priority = CascadePriority::new(level, layer_order);
    218 
    219        // Then remove the one at the level we want to replace, if any.
    220        //
    221        // NOTE: Here we assume that only one rule can be at the level + priority we're replacing,
    222        // which holds because we only use this for HTML style attribute, animations and transition
    223        // rules.
    224        //
    225        // This is certainly true for HTML style attribute rules, animations, transitions, and
    226        // SMIL.
    227        if current.cascade_priority() == cascade_priority {
    228            *important_rules_changed |= level.is_important();
    229 
    230            let current_decls = current.style_source().unwrap().get();
    231 
    232            // If the only rule at the level we're replacing is exactly the
    233            // same as `pdb`, we're done, and `path` is still valid.
    234            if let Some(ref pdb) = pdb {
    235                // If the only rule at the level we're replacing is exactly the
    236                // same as `pdb`, we're done, and `path` is still valid.
    237                //
    238                // TODO(emilio): Another potential optimization is the one where
    239                // we can just replace the rule at that level for `pdb`, and
    240                // then we don't need to re-create the children, and `path` is
    241                // also equally valid. This is less likely, and would require an
    242                // in-place mutation of the source, which is, at best, fiddly,
    243                // so let's skip it for now.
    244                let is_here_already = ArcBorrow::ptr_eq(pdb, &current_decls.borrow_arc());
    245                if is_here_already {
    246                    debug!("Picking the fast path in rule replacement");
    247                    return None;
    248                }
    249            }
    250 
    251            current = current.parent().unwrap().clone();
    252        }
    253 
    254        // Insert the rule if it's relevant at this level in the cascade.
    255        //
    256        // These optimizations are likely to be important, because the levels where replacements
    257        // apply (style and animations) tend to trigger pretty bad styling cases already.
    258        if let Some(pdb) = pdb {
    259            if level.is_important() {
    260                if pdb.read_with(level.guard(guards)).any_important() {
    261                    current = current.ensure_child(
    262                        self.root(),
    263                        StyleSource::from_declarations(pdb.clone_arc()),
    264                        cascade_priority,
    265                    );
    266                    *important_rules_changed = true;
    267                }
    268            } else {
    269                if pdb.read_with(level.guard(guards)).any_normal() {
    270                    current = current.ensure_child(
    271                        self.root(),
    272                        StyleSource::from_declarations(pdb.clone_arc()),
    273                        cascade_priority,
    274                    );
    275                }
    276            }
    277        }
    278 
    279        // Now the rule is in the relevant place, push the children as
    280        // necessary.
    281        let rule = self.insert_ordered_rules_from(current, children.drain(..).rev());
    282        Some(rule)
    283    }
    284 
    285    /// Returns new rule nodes without Transitions level rule.
    286    pub fn remove_transition_rule_if_applicable(&self, path: &StrongRuleNode) -> StrongRuleNode {
    287        // Return a clone if there is no transition level.
    288        if path.cascade_level() != CascadeLevel::Transitions {
    289            return path.clone();
    290        }
    291 
    292        path.parent().unwrap().clone()
    293    }
    294 
    295    /// Returns new rule node without rules from declarative animations.
    296    pub fn remove_animation_rules(&self, path: &StrongRuleNode) -> StrongRuleNode {
    297        // Return a clone if there are no animation rules.
    298        if !path.has_animation_or_transition_rules() {
    299            return path.clone();
    300        }
    301 
    302        let iter = path
    303            .self_and_ancestors()
    304            .take_while(|node| node.cascade_level() >= CascadeLevel::SMILOverride);
    305        let mut last = path;
    306        let mut children = SmallVec::<[_; 10]>::new();
    307        for node in iter {
    308            if !node.cascade_level().is_animation() {
    309                children.push((
    310                    node.style_source().unwrap().clone(),
    311                    node.cascade_priority(),
    312                ));
    313            }
    314            last = node;
    315        }
    316 
    317        let rule = self
    318            .insert_ordered_rules_from(last.parent().unwrap().clone(), children.drain(..).rev());
    319        rule
    320    }
    321 }
    322 
    323 impl StrongRuleNode {
    324    /// Get an iterator for this rule node and its ancestors.
    325    pub fn self_and_ancestors(&self) -> SelfAndAncestors<'_> {
    326        SelfAndAncestors {
    327            current: Some(self),
    328        }
    329    }
    330 
    331    /// Returns true if there is either animation or transition level rule.
    332    pub fn has_animation_or_transition_rules(&self) -> bool {
    333        self.self_and_ancestors()
    334            .take_while(|node| node.cascade_level() >= CascadeLevel::SMILOverride)
    335            .any(|node| node.cascade_level().is_animation())
    336    }
    337 
    338    /// Get a set of properties whose CascadeLevel are higher than Animations
    339    /// but not equal to Transitions.
    340    ///
    341    /// If there are any custom properties, we set the boolean value of the
    342    /// returned tuple to true.
    343    pub fn get_properties_overriding_animations(
    344        &self,
    345        guards: &StylesheetGuards,
    346    ) -> (LonghandIdSet, bool) {
    347        use crate::properties::PropertyDeclarationId;
    348 
    349        // We want to iterate over cascade levels that override the animations
    350        // level, i.e.  !important levels and the transitions level.
    351        //
    352        // However, we actually want to skip the transitions level because
    353        // although it is higher in the cascade than animations, when both
    354        // transitions and animations are present for a given element and
    355        // property, transitions are suppressed so that they don't actually
    356        // override animations.
    357        let iter = self
    358            .self_and_ancestors()
    359            .skip_while(|node| node.cascade_level() == CascadeLevel::Transitions)
    360            .take_while(|node| node.cascade_level() > CascadeLevel::Animations);
    361        let mut result = (LonghandIdSet::new(), false);
    362        for node in iter {
    363            let style = node.style_source().unwrap();
    364            for (decl, important) in style
    365                .read(node.cascade_level().guard(guards))
    366                .declaration_importance_iter()
    367            {
    368                // Although we are only iterating over cascade levels that
    369                // override animations, in a given property declaration block we
    370                // can have a mixture of !important and non-!important
    371                // declarations but only the !important declarations actually
    372                // override animations.
    373                if important.important() {
    374                    match decl.id() {
    375                        PropertyDeclarationId::Longhand(id) => result.0.insert(id),
    376                        PropertyDeclarationId::Custom(_) => result.1 = true,
    377                    }
    378                }
    379            }
    380        }
    381        result
    382    }
    383 }
    384 
    385 /// An iterator over a rule node and its ancestors.
    386 #[derive(Clone)]
    387 pub struct SelfAndAncestors<'a> {
    388    current: Option<&'a StrongRuleNode>,
    389 }
    390 
    391 impl<'a> Iterator for SelfAndAncestors<'a> {
    392    type Item = &'a StrongRuleNode;
    393 
    394    fn next(&mut self) -> Option<Self::Item> {
    395        self.current.map(|node| {
    396            self.current = node.parent();
    397            node
    398        })
    399    }
    400 }