tor-browser

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

scope_rule.rs (17221B)


      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 //! A [`@scope`][scope] rule.
      6 //!
      7 //! [scope]: https://drafts.csswg.org/css-cascade-6/#scoped-styles
      8 
      9 use crate::applicable_declarations::ScopeProximity;
     10 use crate::derives::*;
     11 use crate::dom::TElement;
     12 use crate::parser::ParserContext;
     13 use crate::selector_parser::{SelectorImpl, SelectorParser};
     14 use crate::shared_lock::{
     15    DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
     16 };
     17 use crate::simple_buckets_map::SimpleBucketsMap;
     18 use crate::stylesheets::CssRules;
     19 use cssparser::{Parser, SourceLocation, ToCss};
     20 #[cfg(feature = "gecko")]
     21 use malloc_size_of::{
     22    MallocSizeOfOps, MallocUnconditionalShallowSizeOf, MallocUnconditionalSizeOf,
     23 };
     24 use selectors::context::{MatchingContext, QuirksMode};
     25 use selectors::matching::matches_selector;
     26 use selectors::parser::{Component, ParseRelative, Selector, SelectorList};
     27 use selectors::OpaqueElement;
     28 use servo_arc::Arc;
     29 use std::fmt::{self, Write};
     30 use style_traits::{CssStringWriter, CssWriter, ParseError};
     31 
     32 /// A scoped rule.
     33 #[derive(Debug, ToShmem)]
     34 pub struct ScopeRule {
     35    /// Bounds at which this rule applies.
     36    pub bounds: ScopeBounds,
     37    /// The nested rules inside the block.
     38    pub rules: Arc<Locked<CssRules>>,
     39    /// The source position where this rule was found.
     40    pub source_location: SourceLocation,
     41 }
     42 
     43 impl DeepCloneWithLock for ScopeRule {
     44    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
     45        let rules = self.rules.read_with(guard);
     46        Self {
     47            bounds: self.bounds.clone(),
     48            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
     49            source_location: self.source_location.clone(),
     50        }
     51    }
     52 }
     53 
     54 impl ToCssWithGuard for ScopeRule {
     55    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
     56        dest.write_str("@scope")?;
     57        {
     58            let mut writer = CssWriter::new(dest);
     59            if let Some(start) = self.bounds.start.as_ref() {
     60                writer.write_str(" (")?;
     61                start.to_css(&mut writer)?;
     62                writer.write_char(')')?;
     63            }
     64            if let Some(end) = self.bounds.end.as_ref() {
     65                writer.write_str(" to (")?;
     66                end.to_css(&mut writer)?;
     67                writer.write_char(')')?;
     68            }
     69        }
     70        self.rules.read_with(guard).to_css_block(guard, dest)
     71    }
     72 }
     73 
     74 impl ScopeRule {
     75    /// Measure heap usage.
     76    #[cfg(feature = "gecko")]
     77    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
     78        self.rules.unconditional_shallow_size_of(ops)
     79            + self.rules.read_with(guard).size_of(guard, ops)
     80            + self.bounds.size_of(ops)
     81    }
     82 }
     83 
     84 /// Bounds of the scope.
     85 #[derive(Debug, Clone, ToShmem)]
     86 pub struct ScopeBounds {
     87    /// Start of the scope.
     88    pub start: Option<SelectorList<SelectorImpl>>,
     89    /// End of the scope.
     90    pub end: Option<SelectorList<SelectorImpl>>,
     91 }
     92 
     93 impl ScopeBounds {
     94    #[cfg(feature = "gecko")]
     95    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
     96        fn bound_size_of(
     97            bound: &Option<SelectorList<SelectorImpl>>,
     98            ops: &mut MallocSizeOfOps,
     99        ) -> usize {
    100            bound
    101                .as_ref()
    102                .map(|list| list.unconditional_size_of(ops))
    103                .unwrap_or(0)
    104        }
    105        bound_size_of(&self.start, ops) + bound_size_of(&self.end, ops)
    106    }
    107 }
    108 
    109 fn parse_scope<'a>(
    110    context: &ParserContext,
    111    input: &mut Parser<'a, '_>,
    112    parse_relative: ParseRelative,
    113    for_end: bool,
    114 ) -> Result<Option<SelectorList<SelectorImpl>>, ParseError<'a>> {
    115    input.try_parse(|input| {
    116        if for_end {
    117            // scope-end not existing is valid.
    118            if input.try_parse(|i| i.expect_ident_matching("to")).is_err() {
    119                return Ok(None);
    120            }
    121        }
    122        let parens = input.try_parse(|i| i.expect_parenthesis_block());
    123        if for_end {
    124            // `@scope to {}` is NOT valid.
    125            parens?;
    126        } else if parens.is_err() {
    127            // `@scope {}` is valid.
    128            return Ok(None);
    129        }
    130        input.parse_nested_block(|input| {
    131            let selector_parser = SelectorParser {
    132                stylesheet_origin: context.stylesheet_origin,
    133                namespaces: &context.namespaces,
    134                url_data: context.url_data,
    135                for_supports_rule: false,
    136            };
    137            let parse_relative = if for_end {
    138                ParseRelative::ForScope
    139            } else {
    140                parse_relative
    141            };
    142            Ok(Some(SelectorList::parse_disallow_pseudo(
    143                &selector_parser,
    144                input,
    145                parse_relative,
    146            )?))
    147        })
    148    })
    149 }
    150 
    151 impl ScopeBounds {
    152    /// Parse a container condition.
    153    pub fn parse<'a>(
    154        context: &ParserContext,
    155        input: &mut Parser<'a, '_>,
    156        parse_relative: ParseRelative,
    157    ) -> Result<Self, ParseError<'a>> {
    158        let start = parse_scope(context, input, parse_relative, false)?;
    159        let end = parse_scope(context, input, parse_relative, true)?;
    160        Ok(Self { start, end })
    161    }
    162 }
    163 
    164 /// Types of implicit scope root.
    165 #[derive(Debug, Copy, Clone, MallocSizeOf)]
    166 pub enum ImplicitScopeRoot {
    167    /// This implicit scope root is in the light tree.
    168    InLightTree(OpaqueElement),
    169    /// This implicit scope root is the document element, regardless of which (light|shadow) tree
    170    /// the element being matched is. This is the case for e.g. if you specified an implicit scope
    171    /// within a user stylesheet.
    172    DocumentElement,
    173    /// The implicit scope root is in a constructed stylesheet - the scope root the element
    174    /// under consideration's shadow root (If one exists).
    175    Constructed,
    176    /// This implicit scope root is in the shadow tree.
    177    InShadowTree(OpaqueElement),
    178    /// This implicit scope root is the shadow host of the stylesheet-containing shadow tree.
    179    ShadowHost(OpaqueElement),
    180 }
    181 
    182 impl ImplicitScopeRoot {
    183    /// Return true if this matches the shadow host.
    184    pub fn matches_shadow_host(&self) -> bool {
    185        match self {
    186            Self::InLightTree(..) | Self::InShadowTree(..) | Self::DocumentElement => false,
    187            Self::ShadowHost(..) | Self::Constructed => true,
    188        }
    189    }
    190 
    191    /// Return the implicit scope root element.
    192    pub fn element(&self, current_host: Option<OpaqueElement>) -> ImplicitScopeTarget {
    193        match self {
    194            Self::InLightTree(e) | Self::InShadowTree(e) | Self::ShadowHost(e) => {
    195                ImplicitScopeTarget::Element(*e)
    196            },
    197            Self::Constructed | Self::DocumentElement => {
    198                if matches!(self, Self::Constructed) {
    199                    if let Some(host) = current_host {
    200                        return ImplicitScopeTarget::Element(host);
    201                    }
    202                }
    203                ImplicitScopeTarget::DocumentElement
    204            },
    205        }
    206    }
    207 }
    208 
    209 /// Target of this implicit scope.
    210 pub enum ImplicitScopeTarget {
    211    /// Target matches only the specified element.
    212    Element(OpaqueElement),
    213    /// Implicit scope whose target is the document element.
    214    DocumentElement,
    215 }
    216 
    217 impl ImplicitScopeTarget {
    218    /// Check if this element is the implicit scope.
    219    fn check<E: TElement>(&self, element: E) -> bool {
    220        match self {
    221            Self::Element(e) => element.opaque() == *e,
    222            Self::DocumentElement => element.is_root(),
    223        }
    224    }
    225 }
    226 
    227 /// Target of this scope.
    228 pub enum ScopeTarget<'a> {
    229    /// Target matches an element matching the specified selector list.
    230    Selector(&'a SelectorList<SelectorImpl>),
    231    /// Target matches an implicit scope target.
    232    Implicit(ImplicitScopeTarget),
    233 }
    234 
    235 impl<'a> ScopeTarget<'a> {
    236    /// Check if the given element is the scope.
    237    fn check<E: TElement>(
    238        &self,
    239        element: E,
    240        scope: Option<OpaqueElement>,
    241        scope_subject_map: &ScopeSubjectMap,
    242        context: &mut MatchingContext<E::Impl>,
    243    ) -> bool {
    244        match self {
    245            Self::Selector(list) => context.nest_for_scope_condition(scope, |context| {
    246                if scope_subject_map.early_reject(element, context.quirks_mode()) {
    247                    return false;
    248                }
    249                for selector in list.slice().iter() {
    250                    if matches_selector(selector, 0, None, &element, context) {
    251                        return true;
    252                    }
    253                }
    254                false
    255            }),
    256            Self::Implicit(t) => t.check(element),
    257        }
    258    }
    259 }
    260 
    261 /// A scope root candidate.
    262 #[derive(Clone, Copy, Debug)]
    263 pub struct ScopeRootCandidate {
    264    /// This candidate's scope root.
    265    pub root: OpaqueElement,
    266    /// Ancestor hop from the element under consideration to this scope root.
    267    pub proximity: ScopeProximity,
    268 }
    269 
    270 impl ScopeRootCandidate {
    271    /// Get the element corresponding to this scope root candidate.
    272    pub fn get_scope_root_element<E>(&self, originating_element: E) -> Option<E>
    273    where
    274        E: TElement,
    275    {
    276        // Could just unsafe-convert from opaque element - technically
    277        // faster as well, but it doesn't seem worth having to manually
    278        // assure safety every time.
    279        let mut e = originating_element;
    280        let hops = self.proximity.get()?;
    281        for _ in 0..hops {
    282            e = e.parent_element()?;
    283        }
    284        debug_assert_eq!(e.opaque(), self.root);
    285        Some(e)
    286    }
    287 }
    288 
    289 /// Collect potential scope roots for a given element and its scope target.
    290 /// The check may not pass the ceiling, if specified.
    291 pub fn collect_scope_roots<E>(
    292    element: E,
    293    ceiling: Option<OpaqueElement>,
    294    context: &mut MatchingContext<E::Impl>,
    295    target: &ScopeTarget,
    296    matches_shadow_host: bool,
    297    scope_subject_map: &ScopeSubjectMap,
    298 ) -> Vec<ScopeRootCandidate>
    299 where
    300    E: TElement,
    301 {
    302    let mut result = vec![];
    303    let mut parent = Some(element);
    304    let mut proximity = 0usize;
    305    while let Some(p) = parent {
    306        if target.check(p, ceiling, scope_subject_map, context) {
    307            result.push(ScopeRootCandidate {
    308                root: p.opaque(),
    309                proximity: ScopeProximity::new(proximity),
    310            });
    311            // Note that we can't really break here - we need to consider
    312            // ALL scope roots to figure out whch one didn't end.
    313        }
    314        if ceiling == Some(p.opaque()) {
    315            break;
    316        }
    317        parent = p.parent_element();
    318        proximity += 1;
    319        // We we got to the top of the shadow tree - keep going
    320        // if we may match the shadow host.
    321        if parent.is_none() && matches_shadow_host {
    322            parent = p.containing_shadow_host();
    323        }
    324    }
    325    result
    326 }
    327 
    328 /// Given the scope-end selector, check if the element is outside of the scope.
    329 /// That is, check if any ancestor to the root matches the scope-end selector.
    330 pub fn element_is_outside_of_scope<E>(
    331    selector: &Selector<E::Impl>,
    332    element: E,
    333    root: OpaqueElement,
    334    context: &mut MatchingContext<E::Impl>,
    335    root_may_be_shadow_host: bool,
    336 ) -> bool
    337 where
    338    E: TElement,
    339 {
    340    let mut parent = Some(element);
    341    context.nest_for_scope_condition(Some(root), |context| {
    342        while let Some(p) = parent {
    343            if matches_selector(selector, 0, None, &p, context) {
    344                return true;
    345            }
    346            if p.opaque() == root {
    347                // Reached the top, not lying outside of scope.
    348                break;
    349            }
    350            parent = p.parent_element();
    351            if parent.is_none() && root_may_be_shadow_host {
    352                if let Some(host) = p.containing_shadow_host() {
    353                    // Pretty much an edge case where user specified scope-start and -end of :host
    354                    return host.opaque() == root;
    355                }
    356            }
    357        }
    358        return false;
    359    })
    360 }
    361 
    362 /// A map containing simple selectors in subjects of scope selectors.
    363 /// This allows fast-rejecting scopes before running the full match.
    364 #[derive(Clone, Debug, Default, MallocSizeOf)]
    365 pub struct ScopeSubjectMap {
    366    buckets: SimpleBucketsMap<()>,
    367    any: bool,
    368 }
    369 
    370 impl ScopeSubjectMap {
    371    /// Add the `<scope-start>` of a scope.
    372    pub fn add_bound_start(
    373        &mut self,
    374        selectors: &SelectorList<SelectorImpl>,
    375        quirks_mode: QuirksMode,
    376    ) {
    377        if self.add_selector_list(selectors, quirks_mode) {
    378            self.any = true;
    379        }
    380    }
    381 
    382    fn add_selector_list(
    383        &mut self,
    384        selectors: &SelectorList<SelectorImpl>,
    385        quirks_mode: QuirksMode,
    386    ) -> bool {
    387        let mut is_any = false;
    388        for selector in selectors.slice().iter() {
    389            is_any = is_any || self.add_selector(selector, quirks_mode);
    390        }
    391        is_any
    392    }
    393 
    394    fn add_selector(&mut self, selector: &Selector<SelectorImpl>, quirks_mode: QuirksMode) -> bool {
    395        let mut is_any = true;
    396        let mut iter = selector.iter();
    397        while let Some(c) = iter.next() {
    398            let component_any = match c {
    399                Component::Class(cls) => {
    400                    match self.buckets.classes.try_entry(cls.0.clone(), quirks_mode) {
    401                        Ok(e) => {
    402                            e.or_insert(());
    403                            false
    404                        },
    405                        Err(_) => true,
    406                    }
    407                },
    408                Component::ID(id) => match self.buckets.ids.try_entry(id.0.clone(), quirks_mode) {
    409                    Ok(e) => {
    410                        e.or_insert(());
    411                        false
    412                    },
    413                    Err(_) => true,
    414                },
    415                Component::LocalName(local_name) => {
    416                    self.buckets
    417                        .local_names
    418                        .insert(local_name.lower_name.clone(), ());
    419                    false
    420                },
    421                Component::Is(ref list) | Component::Where(ref list) => {
    422                    self.add_selector_list(list, quirks_mode)
    423                },
    424                _ => true,
    425            };
    426 
    427            is_any = is_any && component_any;
    428        }
    429        is_any
    430    }
    431 
    432    /// Shrink the map as much as possible.
    433    pub fn shrink_if_needed(&mut self) {
    434        self.buckets.shrink_if_needed();
    435    }
    436 
    437    /// Clear the map.
    438    pub fn clear(&mut self) {
    439        self.buckets.clear();
    440        self.any = false;
    441    }
    442 
    443    /// Could a given element possibly be a scope root?
    444    fn early_reject<E: TElement>(&self, element: E, quirks_mode: QuirksMode) -> bool {
    445        if self.any {
    446            return false;
    447        }
    448 
    449        if let Some(id) = element.id() {
    450            if self.buckets.ids.get(id, quirks_mode).is_some() {
    451                return false;
    452            }
    453        }
    454 
    455        let mut found = false;
    456        element.each_class(|cls| {
    457            if self.buckets.classes.get(cls, quirks_mode).is_some() {
    458                found = true;
    459            }
    460        });
    461        if found {
    462            return false;
    463        }
    464 
    465        if self.buckets.local_names.get(element.local_name()).is_some() {
    466            return false;
    467        }
    468 
    469        true
    470    }
    471 }
    472 
    473 /// Determine if this selector list, when used as a scope bound selector, is considered trivial.
    474 pub fn scope_selector_list_is_trivial(list: &SelectorList<SelectorImpl>) -> bool {
    475    fn scope_selector_is_trivial(selector: &Selector<SelectorImpl>) -> bool {
    476        // A selector is trivial if:
    477        // * There is no selector conditional on its siblings and/or descendant to match, and
    478        // * There is no dependency on sibling relations, and
    479        // * There's no ID selector in the selector. A more correct approach may be to ensure that
    480        //   scoping roots of the style sharing candidates and targets have matching IDs, but that
    481        //   requires re-plumbing what we pass around for scope roots.
    482        let mut iter = selector.iter();
    483        loop {
    484            while let Some(c) = iter.next() {
    485                match c {
    486                    Component::ID(_)
    487                    | Component::Nth(_)
    488                    | Component::NthOf(_)
    489                    | Component::Has(_) => return false,
    490                    Component::Is(ref list)
    491                    | Component::Where(ref list)
    492                    | Component::Negation(ref list) => {
    493                        if !scope_selector_list_is_trivial(list) {
    494                            return false;
    495                        }
    496                    },
    497                    _ => (),
    498                }
    499            }
    500 
    501            match iter.next_sequence() {
    502                Some(c) => {
    503                    if c.is_sibling() {
    504                        return false;
    505                    }
    506                },
    507                None => return true,
    508            }
    509        }
    510    }
    511 
    512    list.slice().iter().all(|s| scope_selector_is_trivial(s))
    513 }