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}