tor-browser

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

import_rule.rs (8531B)


      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 //! The [`@import`][import] at-rule.
      6 //!
      7 //! [import]: https://drafts.csswg.org/css-cascade-3/#at-import
      8 
      9 use crate::media_queries::MediaList;
     10 use crate::parser::{Parse, ParserContext};
     11 use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
     12 use crate::stylesheets::{
     13    layer_rule::LayerName, supports_rule::SupportsCondition, CssRule, CssRuleType,
     14    StylesheetInDocument,
     15 };
     16 use crate::values::CssUrl;
     17 use cssparser::{Parser, SourceLocation};
     18 use std::fmt::{self, Write};
     19 use style_traits::{CssStringWriter, CssWriter, ToCss};
     20 use to_shmem::{SharedMemoryBuilder, ToShmem};
     21 
     22 #[cfg(feature = "gecko")]
     23 type StyleSheet = crate::gecko::data::GeckoStyleSheet;
     24 #[cfg(feature = "servo")]
     25 type StyleSheet = ::servo_arc::Arc<crate::stylesheets::Stylesheet>;
     26 
     27 /// A sheet that is held from an import rule.
     28 #[derive(Debug)]
     29 pub enum ImportSheet {
     30    /// A bonafide stylesheet.
     31    Sheet(StyleSheet),
     32 
     33    /// An @import created while parsing off-main-thread, whose Gecko sheet has
     34    /// yet to be created and attached.
     35    Pending,
     36 
     37    /// An @import created with a false <supports-condition>, so will never be fetched.
     38    Refused,
     39 }
     40 
     41 impl ImportSheet {
     42    /// Creates a new ImportSheet from a stylesheet.
     43    pub fn new(sheet: StyleSheet) -> Self {
     44        ImportSheet::Sheet(sheet)
     45    }
     46 
     47    /// Creates a pending ImportSheet for a load that has not started yet.
     48    pub fn new_pending() -> Self {
     49        ImportSheet::Pending
     50    }
     51 
     52    /// Creates a refused ImportSheet for a load that will not happen.
     53    pub fn new_refused() -> Self {
     54        ImportSheet::Refused
     55    }
     56 
     57    /// Returns a reference to the stylesheet in this ImportSheet, if it exists.
     58    pub fn as_sheet(&self) -> Option<&StyleSheet> {
     59        match *self {
     60            #[cfg(feature = "gecko")]
     61            ImportSheet::Sheet(ref s) => {
     62                debug_assert!(!s.hack_is_null());
     63                if s.hack_is_null() {
     64                    return None;
     65                }
     66                Some(s)
     67            },
     68            #[cfg(feature = "servo")]
     69            ImportSheet::Sheet(ref s) => Some(s),
     70            ImportSheet::Refused | ImportSheet::Pending => None,
     71        }
     72    }
     73 
     74    /// Returns the media list for this import rule.
     75    pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
     76        self.as_sheet().and_then(|s| s.media(guard))
     77    }
     78 
     79    /// Returns the rule list for this import rule.
     80    pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
     81        match self.as_sheet() {
     82            Some(s) => s.contents(guard).rules(guard),
     83            None => &[],
     84        }
     85    }
     86 }
     87 
     88 impl DeepCloneWithLock for ImportSheet {
     89    fn deep_clone_with_lock(&self, _lock: &SharedRwLock, _guard: &SharedRwLockReadGuard) -> Self {
     90        match *self {
     91            #[cfg(feature = "gecko")]
     92            ImportSheet::Sheet(ref s) => {
     93                use crate::gecko_bindings::bindings;
     94                let clone = unsafe { bindings::Gecko_StyleSheet_Clone(s.raw() as *const _) };
     95                ImportSheet::Sheet(unsafe { StyleSheet::from_addrefed(clone) })
     96            },
     97            #[cfg(feature = "servo")]
     98            ImportSheet::Sheet(ref s) => {
     99                use servo_arc::Arc;
    100                ImportSheet::Sheet(Arc::new((&**s).clone()))
    101            },
    102            ImportSheet::Pending => ImportSheet::Pending,
    103            ImportSheet::Refused => ImportSheet::Refused,
    104        }
    105    }
    106 }
    107 
    108 /// The layer specified in an import rule (can be none, anonymous, or named).
    109 #[derive(Debug, Clone)]
    110 pub enum ImportLayer {
    111    /// No layer specified
    112    None,
    113 
    114    /// Anonymous layer (`layer`)
    115    Anonymous,
    116 
    117    /// Named layer (`layer(name)`)
    118    Named(LayerName),
    119 }
    120 
    121 /// The supports condition in an import rule.
    122 #[derive(Debug, Clone)]
    123 pub struct ImportSupportsCondition {
    124    /// The supports condition.
    125    pub condition: SupportsCondition,
    126 
    127    /// If the import is enabled, from the result of the import condition.
    128    pub enabled: bool,
    129 }
    130 
    131 impl ToCss for ImportLayer {
    132    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    133    where
    134        W: Write,
    135    {
    136        match *self {
    137            ImportLayer::None => Ok(()),
    138            ImportLayer::Anonymous => dest.write_str("layer"),
    139            ImportLayer::Named(ref name) => {
    140                dest.write_str("layer(")?;
    141                name.to_css(dest)?;
    142                dest.write_char(')')
    143            },
    144        }
    145    }
    146 }
    147 
    148 /// The [`@import`][import] at-rule.
    149 ///
    150 /// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
    151 #[derive(Debug)]
    152 pub struct ImportRule {
    153    /// The `<url>` this `@import` rule is loading.
    154    pub url: CssUrl,
    155 
    156    /// The stylesheet is always present. However, in the case of gecko async
    157    /// parsing, we don't actually have a Gecko sheet at first, and so the
    158    /// ImportSheet just has stub behavior until it appears.
    159    pub stylesheet: ImportSheet,
    160 
    161    /// A <supports-condition> for the rule.
    162    pub supports: Option<ImportSupportsCondition>,
    163 
    164    /// A `layer()` function name.
    165    pub layer: ImportLayer,
    166 
    167    /// The line and column of the rule's source code.
    168    pub source_location: SourceLocation,
    169 }
    170 
    171 impl ImportRule {
    172    /// Parses the layer() / layer / supports() part of the import header, as per
    173    /// https://drafts.csswg.org/css-cascade-5/#at-import:
    174    ///
    175    ///     [ layer | layer(<layer-name>) ]?
    176    ///     [ supports([ <supports-condition> | <declaration> ]) ]?
    177    ///
    178    /// We do this here so that the import preloader can look at this without having to parse the
    179    /// whole import rule or parse the media query list or what not.
    180    pub fn parse_layer_and_supports<'i, 't>(
    181        input: &mut Parser<'i, 't>,
    182        context: &mut ParserContext,
    183    ) -> (ImportLayer, Option<ImportSupportsCondition>) {
    184        let layer = if input
    185            .try_parse(|input| input.expect_ident_matching("layer"))
    186            .is_ok()
    187        {
    188            ImportLayer::Anonymous
    189        } else {
    190            input
    191                .try_parse(|input| {
    192                    input.expect_function_matching("layer")?;
    193                    input
    194                        .parse_nested_block(|input| LayerName::parse(context, input))
    195                        .map(|name| ImportLayer::Named(name))
    196                })
    197                .ok()
    198                .unwrap_or(ImportLayer::None)
    199        };
    200 
    201        let supports = input
    202            .try_parse(SupportsCondition::parse_for_import)
    203            .map(|condition| {
    204                let enabled =
    205                    context.nest_for_rule(CssRuleType::Style, |context| condition.eval(context));
    206                ImportSupportsCondition { condition, enabled }
    207            })
    208            .ok();
    209 
    210        (layer, supports)
    211    }
    212 }
    213 
    214 impl ToShmem for ImportRule {
    215    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
    216        Err(String::from(
    217            "ToShmem failed for ImportRule: cannot handle imported style sheets",
    218        ))
    219    }
    220 }
    221 
    222 impl DeepCloneWithLock for ImportRule {
    223    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
    224        ImportRule {
    225            url: self.url.clone(),
    226            stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard),
    227            supports: self.supports.clone(),
    228            layer: self.layer.clone(),
    229            source_location: self.source_location.clone(),
    230        }
    231    }
    232 }
    233 
    234 impl ToCssWithGuard for ImportRule {
    235    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
    236        dest.write_str("@import ")?;
    237        self.url.to_css(&mut CssWriter::new(dest))?;
    238 
    239        if !matches!(self.layer, ImportLayer::None) {
    240            dest.write_char(' ')?;
    241            self.layer.to_css(&mut CssWriter::new(dest))?;
    242        }
    243 
    244        if let Some(ref supports) = self.supports {
    245            dest.write_str(" supports(")?;
    246            supports.condition.to_css(&mut CssWriter::new(dest))?;
    247            dest.write_char(')')?;
    248        }
    249 
    250        if let Some(media) = self.stylesheet.media(guard) {
    251            if !media.is_empty() {
    252                dest.write_char(' ')?;
    253                media.to_css(&mut CssWriter::new(dest))?;
    254            }
    255        }
    256 
    257        dest.write_char(';')
    258    }
    259 }