tor-browser

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

media_rule.rs (7137B)


      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 [`@media`][media] rule.
      6 //!
      7 //! [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
      8 
      9 use crate::derives::*;
     10 use crate::media_queries::MediaList;
     11 use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};
     12 use crate::shared_lock::{DeepCloneWithLock, Locked};
     13 use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
     14 use crate::stylesheets::CssRules;
     15 use crate::values::{computed, DashedIdent};
     16 use crate::Atom;
     17 use cssparser::Parser;
     18 use cssparser::SourceLocation;
     19 #[cfg(feature = "gecko")]
     20 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
     21 use selectors::kleene_value::KleeneValue;
     22 use servo_arc::Arc;
     23 use std::fmt::{self, Write};
     24 use style_traits::{CssStringWriter, CssWriter, ParseError, ToCss};
     25 
     26 /// An [`@media`][media] rule.
     27 ///
     28 /// [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
     29 #[derive(Debug, ToShmem)]
     30 pub struct MediaRule {
     31    /// The list of media queries used by this media rule.
     32    pub media_queries: Arc<Locked<MediaList>>,
     33    /// The nested rules to this media rule.
     34    pub rules: Arc<Locked<CssRules>>,
     35    /// The source position where this media rule was found.
     36    pub source_location: SourceLocation,
     37 }
     38 
     39 impl MediaRule {
     40    /// Measure heap usage.
     41    #[cfg(feature = "gecko")]
     42    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
     43        // Measurement of other fields may be added later.
     44        self.rules.unconditional_shallow_size_of(ops)
     45            + self.rules.read_with(guard).size_of(guard, ops)
     46    }
     47 }
     48 
     49 impl ToCssWithGuard for MediaRule {
     50    // Serialization of MediaRule is not specced.
     51    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule
     52    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
     53        dest.write_str("@media ")?;
     54        self.media_queries
     55            .read_with(guard)
     56            .to_css(&mut CssWriter::new(dest))?;
     57        self.rules.read_with(guard).to_css_block(guard, dest)
     58    }
     59 }
     60 
     61 impl DeepCloneWithLock for MediaRule {
     62    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
     63        let media_queries = self.media_queries.read_with(guard);
     64        let rules = self.rules.read_with(guard);
     65        MediaRule {
     66            media_queries: Arc::new(lock.wrap(media_queries.clone())),
     67            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
     68            source_location: self.source_location.clone(),
     69        }
     70    }
     71 }
     72 
     73 /// The condition associated to a custom-media query.
     74 #[derive(Debug, ToShmem, Clone, MallocSizeOf)]
     75 pub enum CustomMediaCondition {
     76    /// Unconditionally true.
     77    True,
     78    /// Unconditionally false.
     79    False,
     80    /// A MediaList.
     81    MediaList(#[ignore_malloc_size_of = "Arc"] Arc<Locked<MediaList>>),
     82 }
     83 
     84 impl CustomMediaCondition {
     85    /// Parses the possible keywords for this condition.
     86    pub(crate) fn parse_keyword<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
     87        Ok(try_match_ident_ignore_ascii_case! { input,
     88            "true" => Self::True,
     89            "false" => Self::False,
     90        })
     91    }
     92 }
     93 
     94 impl DeepCloneWithLock for CustomMediaCondition {
     95    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
     96        match self {
     97            Self::True => Self::True,
     98            Self::False => Self::False,
     99            Self::MediaList(ref m) => {
    100                Self::MediaList(Arc::new(lock.wrap(m.read_with(guard).clone())))
    101            },
    102        }
    103    }
    104 }
    105 
    106 /// A `@custom-media` rule.
    107 /// https://drafts.csswg.org/mediaqueries-5/#custom-mq
    108 #[derive(Debug, ToShmem)]
    109 pub struct CustomMediaRule {
    110    /// The name of the custom media rule.
    111    pub name: DashedIdent,
    112    /// The list of media conditions used by this media rule.
    113    pub condition: CustomMediaCondition,
    114    /// The source position where this media rule was found.
    115    pub source_location: SourceLocation,
    116 }
    117 
    118 impl DeepCloneWithLock for CustomMediaRule {
    119    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
    120        Self {
    121            name: self.name.clone(),
    122            condition: self.condition.deep_clone_with_lock(lock, guard),
    123            source_location: self.source_location.clone(),
    124        }
    125    }
    126 }
    127 
    128 impl ToCssWithGuard for CustomMediaRule {
    129    // Serialization of MediaRule is not specced.
    130    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule
    131    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
    132        dest.write_str("@custom-media ")?;
    133        self.name.to_css(&mut CssWriter::new(dest))?;
    134        dest.write_char(' ')?;
    135        match self.condition {
    136            CustomMediaCondition::True => dest.write_str("true"),
    137            CustomMediaCondition::False => dest.write_str("false"),
    138            CustomMediaCondition::MediaList(ref m) => {
    139                m.read_with(guard).to_css(&mut CssWriter::new(dest))
    140            },
    141        }
    142    }
    143 }
    144 
    145 /// The currently effective @custom-media conditions.
    146 pub type CustomMediaMap = PrecomputedHashMap<Atom, CustomMediaCondition>;
    147 
    148 /// A struct that can evaluate custom media conditions.
    149 pub struct CustomMediaEvaluator<'a> {
    150    map: Option<(&'a CustomMediaMap, &'a SharedRwLockReadGuard<'a>)>,
    151    /// Set of media queries we're currently evaluating, needed for cycle detection.
    152    currently_evaluating: PrecomputedHashSet<Atom>,
    153 }
    154 
    155 impl<'a> CustomMediaEvaluator<'a> {
    156    /// Construct a new custom media evaluator with the given map and guard.
    157    pub fn new(map: &'a CustomMediaMap, guard: &'a SharedRwLockReadGuard<'a>) -> Self {
    158        Self {
    159            map: Some((map, guard)),
    160            currently_evaluating: Default::default(),
    161        }
    162    }
    163 
    164    /// Returns an evaluator that can't evaluate custom queries.
    165    pub fn none() -> Self {
    166        Self {
    167            map: None,
    168            currently_evaluating: Default::default(),
    169        }
    170    }
    171 
    172    /// Evaluates a custom media query.
    173    pub fn matches(&mut self, ident: &DashedIdent, context: &computed::Context) -> KleeneValue {
    174        let Some((map, guard)) = self.map else {
    175            return KleeneValue::Unknown;
    176        };
    177        let Some(condition) = map.get(&ident.0) else {
    178            return KleeneValue::Unknown;
    179        };
    180        let media = match condition {
    181            CustomMediaCondition::True => return KleeneValue::True,
    182            CustomMediaCondition::False => return KleeneValue::False,
    183            CustomMediaCondition::MediaList(ref m) => m,
    184        };
    185        if !self.currently_evaluating.insert(ident.0.clone()) {
    186            // Found a cycle while evaluating this rule.
    187            return KleeneValue::False;
    188        }
    189        let result = media.read_with(guard).matches(context, self);
    190        self.currently_evaluating.remove(&ident.0);
    191        result.into()
    192    }
    193 }