style/stylesheets/
import_rule.rs1use crate::media_queries::MediaList;
10use crate::parser::{Parse, ParserContext};
11use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
12use crate::stylesheets::{
13    layer_rule::LayerName, supports_rule::SupportsCondition, CssRule, CssRuleType,
14    StylesheetInDocument,
15};
16use crate::values::CssUrl;
17use cssparser::{Parser, SourceLocation};
18use std::fmt::{self, Write};
19use style_traits::{CssStringWriter, CssWriter, ToCss};
20use to_shmem::{SharedMemoryBuilder, ToShmem};
21
22#[cfg(feature = "gecko")]
23type StyleSheet = crate::gecko::data::GeckoStyleSheet;
24#[cfg(feature = "servo")]
25type StyleSheet = ::servo_arc::Arc<crate::stylesheets::Stylesheet>;
26
27#[derive(Debug)]
29pub enum ImportSheet {
30    Sheet(StyleSheet),
32
33    Pending,
36
37    Refused,
39}
40
41impl ImportSheet {
42    pub fn new(sheet: StyleSheet) -> Self {
44        ImportSheet::Sheet(sheet)
45    }
46
47    pub fn new_pending() -> Self {
49        ImportSheet::Pending
50    }
51
52    pub fn new_refused() -> Self {
54        ImportSheet::Refused
55    }
56
57    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    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    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
88impl 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#[derive(Debug, Clone)]
110pub enum ImportLayer {
111    None,
113
114    Anonymous,
116
117    Named(LayerName),
119}
120
121#[derive(Debug, Clone)]
123pub struct ImportSupportsCondition {
124    pub condition: SupportsCondition,
126
127    pub enabled: bool,
129}
130
131impl 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#[derive(Debug)]
152pub struct ImportRule {
153    pub url: CssUrl,
155
156    pub stylesheet: ImportSheet,
160
161    pub supports: Option<ImportSupportsCondition>,
163
164    pub layer: ImportLayer,
166
167    pub source_location: SourceLocation,
169}
170
171impl ImportRule {
172    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
214impl 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
222impl 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
234impl 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}