tor-browser

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

rule_collector.rs (18201B)


      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 //! Collects a series of applicable rules for a given element.
      6 
      7 use crate::applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
      8 use crate::dom::{TElement, TNode, TShadowRoot};
      9 use crate::properties::{AnimationDeclarations, PropertyDeclarationBlock};
     10 use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder};
     11 use crate::selector_map::SelectorMap;
     12 use crate::selector_parser::PseudoElement;
     13 use crate::shared_lock::Locked;
     14 use crate::stylesheets::{layer_rule::LayerOrder, Origin};
     15 use crate::stylist::{AuthorStylesEnabled, CascadeData, Rule, RuleInclusion, Stylist};
     16 use selectors::matching::MatchingContext;
     17 use servo_arc::ArcBorrow;
     18 use smallvec::SmallVec;
     19 
     20 /// This is a bit of a hack so <svg:use> matches the rules of the enclosing
     21 /// tree.
     22 ///
     23 /// This function returns the containing shadow host ignoring <svg:use> shadow
     24 /// trees, since those match the enclosing tree's rules.
     25 ///
     26 /// Only a handful of places need to really care about this. This is not a
     27 /// problem for invalidation and that kind of stuff because they still don't
     28 /// match rules based on elements outside of the shadow tree, and because the
     29 /// <svg:use> subtrees are immutable and recreated each time the source tree
     30 /// changes.
     31 ///
     32 /// We historically allow cross-document <svg:use> to have these rules applied,
     33 /// but I think that's not great. Gecko is the only engine supporting that.
     34 ///
     35 /// See https://github.com/w3c/svgwg/issues/504 for the relevant spec
     36 /// discussion.
     37 #[inline]
     38 pub fn containing_shadow_ignoring_svg_use<E: TElement>(
     39    element: E,
     40 ) -> Option<<E::ConcreteNode as TNode>::ConcreteShadowRoot> {
     41    let mut shadow = element.containing_shadow()?;
     42    loop {
     43        let host = shadow.host();
     44        let host_is_svg_use_element =
     45            host.is_svg_element() && host.local_name() == &**local_name!("use");
     46        if !host_is_svg_use_element {
     47            return Some(shadow);
     48        }
     49        debug_assert!(
     50            shadow.style_data().is_none(),
     51            "We allow no stylesheets in <svg:use> subtrees"
     52        );
     53        shadow = host.containing_shadow()?;
     54    }
     55 }
     56 
     57 /// An object that we use with all the intermediate state needed for the
     58 /// cascade.
     59 ///
     60 /// This is done basically to be able to organize the cascade in smaller
     61 /// functions, and be able to reason about it easily.
     62 pub struct RuleCollector<'a, 'b: 'a, E>
     63 where
     64    E: TElement,
     65 {
     66    element: E,
     67    rule_hash_target: E,
     68    stylist: &'a Stylist,
     69    pseudo_elements: SmallVec<[PseudoElement; 1]>,
     70    style_attribute: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
     71    smil_override: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
     72    animation_declarations: AnimationDeclarations,
     73    rule_inclusion: RuleInclusion,
     74    rules: &'a mut ApplicableDeclarationList,
     75    context: &'a mut MatchingContext<'b, E::Impl>,
     76    matches_user_and_content_rules: bool,
     77    matches_document_author_rules: bool,
     78    in_sort_scope: bool,
     79 }
     80 
     81 impl<'a, 'b: 'a, E> RuleCollector<'a, 'b, E>
     82 where
     83    E: TElement,
     84 {
     85    /// Trivially construct a new collector.
     86    pub fn new(
     87        stylist: &'a Stylist,
     88        element: E,
     89        pseudo_elements: SmallVec<[PseudoElement; 1]>,
     90        style_attribute: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
     91        smil_override: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
     92        animation_declarations: AnimationDeclarations,
     93        rule_inclusion: RuleInclusion,
     94        rules: &'a mut ApplicableDeclarationList,
     95        context: &'a mut MatchingContext<'b, E::Impl>,
     96    ) -> Self {
     97        let rule_hash_target = element.rule_hash_target();
     98        let matches_user_and_content_rules = rule_hash_target.matches_user_and_content_rules();
     99 
    100        debug_assert!(pseudo_elements.iter().all(|p| !p.is_precomputed()));
    101 
    102        Self {
    103            element,
    104            rule_hash_target,
    105            stylist,
    106            pseudo_elements,
    107            style_attribute,
    108            smil_override,
    109            animation_declarations,
    110            rule_inclusion,
    111            context,
    112            rules,
    113            matches_user_and_content_rules,
    114            matches_document_author_rules: matches_user_and_content_rules,
    115            in_sort_scope: false,
    116        }
    117    }
    118 
    119    /// Sets up the state necessary to collect rules from a given DOM tree
    120    /// (either the document tree, or a shadow tree).
    121    ///
    122    /// All rules in the same tree need to be matched together, and this
    123    /// function takes care of sorting them by specificity and source order.
    124    #[inline]
    125    fn in_tree(&mut self, host: Option<E>, f: impl FnOnce(&mut Self)) {
    126        debug_assert!(!self.in_sort_scope, "Nested sorting makes no sense");
    127        let start = self.rules.len();
    128        self.in_sort_scope = true;
    129        let old_host = self.context.current_host.take();
    130        self.context.current_host = host.map(|e| e.opaque());
    131        f(self);
    132        if start != self.rules.len() {
    133            self.rules[start..].sort_unstable_by_key(|block| block.sort_key());
    134        }
    135        self.context.current_host = old_host;
    136        self.in_sort_scope = false;
    137    }
    138 
    139    #[inline]
    140    fn in_shadow_tree(&mut self, host: E, f: impl FnOnce(&mut Self)) {
    141        self.in_tree(Some(host), f);
    142    }
    143 
    144    fn collect_stylist_rules(&mut self, origin: Origin) {
    145        let cascade_level = match origin {
    146            Origin::UserAgent => CascadeLevel::UANormal,
    147            Origin::User => CascadeLevel::UserNormal,
    148            Origin::Author => CascadeLevel::same_tree_author_normal(),
    149        };
    150 
    151        let cascade_data = self.stylist.cascade_data().borrow_for_origin(origin);
    152        let map = match cascade_data.normal_rules(&self.pseudo_elements) {
    153            Some(m) => m,
    154            None => return,
    155        };
    156 
    157        self.in_tree(None, |collector| {
    158            collector.collect_rules_in_map(map, cascade_level, cascade_data);
    159        });
    160    }
    161 
    162    fn collect_user_agent_rules(&mut self) {
    163        self.collect_stylist_rules(Origin::UserAgent);
    164        #[cfg(feature = "gecko")]
    165        self.collect_view_transition_dynamic_rules();
    166    }
    167 
    168    #[cfg(feature = "gecko")]
    169    fn collect_view_transition_dynamic_rules(&mut self) {
    170        if !self
    171            .pseudo_elements
    172            .first()
    173            .is_some_and(|p| p.is_named_view_transition())
    174        {
    175            return;
    176        }
    177        let len_before_vt_rules = self.rules.len();
    178        self.element
    179            .synthesize_view_transition_dynamic_rules(self.rules);
    180        if cfg!(debug_assertions) && self.rules.len() != len_before_vt_rules {
    181            for declaration in &self.rules[len_before_vt_rules..] {
    182                assert_eq!(declaration.level(), CascadeLevel::UANormal);
    183            }
    184        }
    185    }
    186 
    187    fn collect_user_rules(&mut self) {
    188        if !self.matches_user_and_content_rules {
    189            return;
    190        }
    191 
    192        self.collect_stylist_rules(Origin::User);
    193    }
    194 
    195    /// Presentational hints.
    196    ///
    197    /// These go before author rules, but after user rules, see:
    198    /// https://drafts.csswg.org/css-cascade/#preshint
    199    fn collect_presentational_hints(&mut self) {
    200        if !self.pseudo_elements.is_empty() {
    201            return;
    202        }
    203 
    204        let length_before_preshints = self.rules.len();
    205        self.element
    206            .synthesize_presentational_hints_for_legacy_attributes(
    207                self.context.visited_handling(),
    208                self.rules,
    209            );
    210        if cfg!(debug_assertions) && self.rules.len() != length_before_preshints {
    211            for declaration in &self.rules[length_before_preshints..] {
    212                assert_eq!(declaration.level(), CascadeLevel::PresHints);
    213            }
    214        }
    215    }
    216 
    217    #[inline]
    218    fn collect_rules_in_list(
    219        &mut self,
    220        part_rules: &[Rule],
    221        cascade_level: CascadeLevel,
    222        cascade_data: &CascadeData,
    223    ) {
    224        debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
    225        SelectorMap::get_matching_rules(
    226            self.element,
    227            part_rules,
    228            &mut self.rules,
    229            &mut self.context,
    230            cascade_level,
    231            cascade_data,
    232            &self.stylist,
    233        );
    234    }
    235 
    236    #[inline]
    237    fn collect_rules_in_map(
    238        &mut self,
    239        map: &SelectorMap<Rule>,
    240        cascade_level: CascadeLevel,
    241        cascade_data: &CascadeData,
    242    ) {
    243        debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
    244        map.get_all_matching_rules(
    245            self.element,
    246            self.rule_hash_target,
    247            &mut self.rules,
    248            &mut self.context,
    249            cascade_level,
    250            cascade_data,
    251            &self.stylist,
    252        );
    253    }
    254 
    255    /// Collects the rules for the ::slotted pseudo-element and the :host
    256    /// pseudo-class.
    257    fn collect_host_and_slotted_rules(&mut self) {
    258        let mut slots = SmallVec::<[_; 3]>::new();
    259        let mut current = self.rule_hash_target.assigned_slot();
    260        let mut shadow_cascade_order = ShadowCascadeOrder::for_outermost_shadow_tree();
    261 
    262        while let Some(slot) = current {
    263            debug_assert!(
    264                self.matches_user_and_content_rules,
    265                "We should not slot NAC anywhere"
    266            );
    267            slots.push(slot);
    268            current = slot.assigned_slot();
    269            shadow_cascade_order.dec();
    270        }
    271 
    272        self.collect_host_rules(shadow_cascade_order);
    273 
    274        // Match slotted rules in reverse order, so that the outer slotted rules
    275        // come before the inner rules (and thus have less priority).
    276        for slot in slots.iter().rev() {
    277            shadow_cascade_order.inc();
    278 
    279            let shadow = slot.containing_shadow().unwrap();
    280            let data = match shadow.style_data() {
    281                Some(d) => d,
    282                None => continue,
    283            };
    284            let slotted_rules = match data.slotted_rules(&self.pseudo_elements) {
    285                Some(r) => r,
    286                None => continue,
    287            };
    288 
    289            self.in_shadow_tree(shadow.host(), |collector| {
    290                let cascade_level = CascadeLevel::AuthorNormal {
    291                    shadow_cascade_order,
    292                };
    293                collector.collect_rules_in_map(slotted_rules, cascade_level, data);
    294            });
    295        }
    296    }
    297 
    298    fn collect_rules_from_containing_shadow_tree(&mut self) {
    299        if !self.matches_user_and_content_rules {
    300            return;
    301        }
    302 
    303        let containing_shadow = containing_shadow_ignoring_svg_use(self.rule_hash_target);
    304        let containing_shadow = match containing_shadow {
    305            Some(s) => s,
    306            None => return,
    307        };
    308 
    309        self.matches_document_author_rules = false;
    310 
    311        let cascade_data = match containing_shadow.style_data() {
    312            Some(c) => c,
    313            None => return,
    314        };
    315 
    316        let cascade_level = CascadeLevel::same_tree_author_normal();
    317        self.in_shadow_tree(containing_shadow.host(), |collector| {
    318            if let Some(map) = cascade_data.normal_rules(&collector.pseudo_elements) {
    319                collector.collect_rules_in_map(map, cascade_level, cascade_data);
    320            }
    321 
    322            // Collect rules from :host::part() and such
    323            let hash_target = collector.rule_hash_target;
    324            if !hash_target.has_part_attr() {
    325                return;
    326            }
    327 
    328            let part_rules = match cascade_data.part_rules(&collector.pseudo_elements) {
    329                Some(p) => p,
    330                None => return,
    331            };
    332 
    333            hash_target.each_part(|part| {
    334                if let Some(part_rules) = part_rules.get(&part.0) {
    335                    collector.collect_rules_in_list(part_rules, cascade_level, cascade_data);
    336                }
    337            });
    338        });
    339    }
    340 
    341    /// Collects the rules for the :host pseudo-class.
    342    fn collect_host_rules(&mut self, shadow_cascade_order: ShadowCascadeOrder) {
    343        let shadow = match self.rule_hash_target.shadow_root() {
    344            Some(s) => s,
    345            None => return,
    346        };
    347 
    348        let style_data = match shadow.style_data() {
    349            Some(d) => d,
    350            None => return,
    351        };
    352 
    353        let host_rules = match style_data.featureless_host_rules(&self.pseudo_elements) {
    354            Some(rules) => rules,
    355            None => return,
    356        };
    357 
    358        let rule_hash_target = self.rule_hash_target;
    359        self.in_shadow_tree(rule_hash_target, |collector| {
    360            let cascade_level = CascadeLevel::AuthorNormal {
    361                shadow_cascade_order,
    362            };
    363            debug_assert!(!collector.context.featureless(), "How?");
    364            collector.context.featureless = true;
    365            collector.collect_rules_in_map(host_rules, cascade_level, style_data);
    366            collector.context.featureless = false;
    367        });
    368    }
    369 
    370    fn collect_document_author_rules(&mut self) {
    371        if !self.matches_document_author_rules {
    372            return;
    373        }
    374 
    375        self.collect_stylist_rules(Origin::Author);
    376    }
    377 
    378    fn collect_part_rules_from_outer_trees(&mut self) {
    379        if !self.rule_hash_target.has_part_attr() {
    380            return;
    381        }
    382 
    383        let mut inner_shadow = match self.rule_hash_target.containing_shadow() {
    384            Some(s) => s,
    385            None => return,
    386        };
    387 
    388        let mut shadow_cascade_order = ShadowCascadeOrder::for_innermost_containing_tree();
    389 
    390        let mut parts = SmallVec::<[_; 3]>::new();
    391        self.rule_hash_target.each_part(|p| parts.push(p.clone()));
    392 
    393        loop {
    394            if parts.is_empty() {
    395                return;
    396            }
    397 
    398            let inner_shadow_host = inner_shadow.host();
    399            let outer_shadow = inner_shadow_host.containing_shadow();
    400            let cascade_data = match outer_shadow {
    401                Some(shadow) => shadow.style_data(),
    402                None => Some(
    403                    self.stylist
    404                        .cascade_data()
    405                        .borrow_for_origin(Origin::Author),
    406                ),
    407            };
    408 
    409            if let Some(cascade_data) = cascade_data {
    410                if let Some(part_rules) = cascade_data.part_rules(&self.pseudo_elements) {
    411                    let containing_host = outer_shadow.map(|s| s.host());
    412                    let cascade_level = CascadeLevel::AuthorNormal {
    413                        shadow_cascade_order,
    414                    };
    415                    self.in_tree(containing_host, |collector| {
    416                        for p in &parts {
    417                            if let Some(part_rules) = part_rules.get(&p.0) {
    418                                collector.collect_rules_in_list(
    419                                    part_rules,
    420                                    cascade_level,
    421                                    cascade_data,
    422                                );
    423                            }
    424                        }
    425                    });
    426                    shadow_cascade_order.inc();
    427                }
    428            }
    429 
    430            inner_shadow = match outer_shadow {
    431                Some(s) => s,
    432                None => break, // Nowhere to export to.
    433            };
    434 
    435            let mut new_parts = SmallVec::new();
    436            for part in &parts {
    437                inner_shadow_host.each_exported_part(part, |exported_part| {
    438                    new_parts.push(exported_part.clone());
    439                });
    440            }
    441            parts = new_parts;
    442        }
    443    }
    444 
    445    fn collect_style_attribute(&mut self) {
    446        if let Some(sa) = self.style_attribute {
    447            self.rules
    448                .push(ApplicableDeclarationBlock::from_declarations(
    449                    sa.clone_arc(),
    450                    CascadeLevel::same_tree_author_normal(),
    451                    LayerOrder::style_attribute(),
    452                ));
    453        }
    454    }
    455 
    456    fn collect_animation_rules(&mut self) {
    457        if let Some(so) = self.smil_override {
    458            self.rules
    459                .push(ApplicableDeclarationBlock::from_declarations(
    460                    so.clone_arc(),
    461                    CascadeLevel::SMILOverride,
    462                    LayerOrder::root(),
    463                ));
    464        }
    465 
    466        // The animations sheet (CSS animations, script-generated
    467        // animations, and CSS transitions that are no longer tied to CSS
    468        // markup).
    469        if let Some(anim) = self.animation_declarations.animations.take() {
    470            self.rules
    471                .push(ApplicableDeclarationBlock::from_declarations(
    472                    anim,
    473                    CascadeLevel::Animations,
    474                    LayerOrder::root(),
    475                ));
    476        }
    477 
    478        // The transitions sheet (CSS transitions that are tied to CSS
    479        // markup).
    480        if let Some(anim) = self.animation_declarations.transitions.take() {
    481            self.rules
    482                .push(ApplicableDeclarationBlock::from_declarations(
    483                    anim,
    484                    CascadeLevel::Transitions,
    485                    LayerOrder::root(),
    486                ));
    487        }
    488    }
    489 
    490    /// Collects all the rules, leaving the result in `self.rules`.
    491    ///
    492    /// Note that `!important` rules are handled during rule tree insertion.
    493    pub fn collect_all(mut self) {
    494        self.collect_user_agent_rules();
    495        self.collect_user_rules();
    496        if self.rule_inclusion == RuleInclusion::DefaultOnly {
    497            return;
    498        }
    499        self.collect_presentational_hints();
    500        // FIXME(emilio): Should the author styles enabled stuff avoid the
    501        // presentational hints from getting pushed? See bug 1505770.
    502        if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No {
    503            return;
    504        }
    505        self.collect_host_and_slotted_rules();
    506        self.collect_rules_from_containing_shadow_tree();
    507        self.collect_document_author_rules();
    508        self.collect_style_attribute();
    509        self.collect_part_rules_from_outer_trees();
    510        self.collect_animation_rules();
    511    }
    512 }