1use crate::font_face::{FontFaceSourceFormatKeyword, FontFaceSourceTechFlags};
8use crate::parser::ParserContext;
9use crate::properties::{PropertyDeclaration, PropertyId, SourcePropertyDeclaration};
10use crate::selector_parser::{SelectorImpl, SelectorParser};
11use crate::shared_lock::{DeepCloneWithLock, Locked};
12use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
13use crate::str::CssStringWriter;
14use crate::stylesheets::{CssRuleType, CssRules};
15use cssparser::parse_important;
16use cssparser::{Delimiter, Parser, SourceLocation, Token};
17use cssparser::{ParseError as CssParseError, ParserInput};
18#[cfg(feature = "gecko")]
19use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
20use selectors::parser::{Selector, SelectorParseErrorKind};
21use servo_arc::Arc;
22use std::fmt::{self, Write};
23use std::str;
24use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
25
26#[derive(Debug, ToShmem)]
30pub struct SupportsRule {
31 pub condition: SupportsCondition,
33 pub rules: Arc<Locked<CssRules>>,
35 pub enabled: bool,
37 pub source_location: SourceLocation,
39}
40
41impl SupportsRule {
42 #[cfg(feature = "gecko")]
44 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
45 self.rules.unconditional_shallow_size_of(ops)
47 + self.rules.read_with(guard).size_of(guard, ops)
48 }
49}
50
51impl ToCssWithGuard for SupportsRule {
52 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
53 dest.write_str("@supports ")?;
54 self.condition.to_css(&mut CssWriter::new(dest))?;
55 self.rules.read_with(guard).to_css_block(guard, dest)
56 }
57}
58
59impl DeepCloneWithLock for SupportsRule {
60 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
61 let rules = self.rules.read_with(guard);
62 SupportsRule {
63 condition: self.condition.clone(),
64 rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
65 enabled: self.enabled,
66 source_location: self.source_location.clone(),
67 }
68 }
69}
70
71#[derive(Clone, Debug, ToShmem)]
75pub enum SupportsCondition {
76 Not(Box<SupportsCondition>),
78 Parenthesized(Box<SupportsCondition>),
80 And(Vec<SupportsCondition>),
82 Or(Vec<SupportsCondition>),
84 Declaration(Declaration),
86 Selector(RawSelector),
88 FontFormat(FontFaceSourceFormatKeyword),
90 FontTech(FontFaceSourceTechFlags),
92 FutureSyntax(String),
94}
95
96impl SupportsCondition {
97 pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
101 if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() {
102 let inner = SupportsCondition::parse_in_parens(input)?;
103 return Ok(SupportsCondition::Not(Box::new(inner)));
104 }
105
106 let in_parens = SupportsCondition::parse_in_parens(input)?;
107
108 let location = input.current_source_location();
109 let (keyword, wrapper) = match input.next() {
110 Err(..) => return Ok(in_parens),
112 Ok(&Token::Ident(ref ident)) => {
113 match_ignore_ascii_case! { &ident,
114 "and" => ("and", SupportsCondition::And as fn(_) -> _),
115 "or" => ("or", SupportsCondition::Or as fn(_) -> _),
116 _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
117 }
118 },
119 Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
120 };
121
122 let mut conditions = Vec::with_capacity(2);
123 conditions.push(in_parens);
124 loop {
125 conditions.push(SupportsCondition::parse_in_parens(input)?);
126 if input
127 .try_parse(|input| input.expect_ident_matching(keyword))
128 .is_err()
129 {
130 return Ok(wrapper(conditions));
134 }
135 }
136 }
137
138 fn parse_functional<'i, 't>(
140 function: &str,
141 input: &mut Parser<'i, 't>,
142 ) -> Result<Self, ParseError<'i>> {
143 match_ignore_ascii_case! { function,
144 "selector" => {
145 let pos = input.position();
146 consume_any_value(input)?;
147 Ok(SupportsCondition::Selector(RawSelector(
148 input.slice_from(pos).to_owned()
149 )))
150 },
151 "font-format" if static_prefs::pref!("layout.css.font-tech.enabled") => {
152 let kw = FontFaceSourceFormatKeyword::parse(input)?;
153 Ok(SupportsCondition::FontFormat(kw))
154 },
155 "font-tech" if static_prefs::pref!("layout.css.font-tech.enabled") => {
156 let flag = FontFaceSourceTechFlags::parse_one(input)?;
157 Ok(SupportsCondition::FontTech(flag))
158 },
159 _ => {
160 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
161 },
162 }
163 }
164
165 pub fn parse_for_import<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
168 input.expect_function_matching("supports")?;
169 input.parse_nested_block(parse_condition_or_declaration)
170 }
171
172 fn parse_in_parens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
174 input.skip_whitespace();
177 let pos = input.position();
178 let location = input.current_source_location();
179 match *input.next()? {
180 Token::ParenthesisBlock => {
181 let nested = input
182 .try_parse(|input| input.parse_nested_block(parse_condition_or_declaration));
183 if let Ok(nested) = nested {
184 return Ok(Self::Parenthesized(Box::new(nested)));
185 }
186 },
187 Token::Function(ref ident) => {
188 let ident = ident.clone();
189 let nested = input.try_parse(|input| {
190 input.parse_nested_block(|input| {
191 SupportsCondition::parse_functional(&ident, input)
192 })
193 });
194 if nested.is_ok() {
195 return nested;
196 }
197 },
198 ref t => return Err(location.new_unexpected_token_error(t.clone())),
199 }
200 input.parse_nested_block(consume_any_value)?;
201 Ok(SupportsCondition::FutureSyntax(
202 input.slice_from(pos).to_owned(),
203 ))
204 }
205
206 pub fn eval(&self, cx: &ParserContext) -> bool {
208 match *self {
209 SupportsCondition::Not(ref cond) => !cond.eval(cx),
210 SupportsCondition::Parenthesized(ref cond) => cond.eval(cx),
211 SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)),
212 SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)),
213 SupportsCondition::Declaration(ref decl) => decl.eval(cx),
214 SupportsCondition::Selector(ref selector) => selector.eval(cx),
215 SupportsCondition::FontFormat(ref format) => eval_font_format(format),
216 SupportsCondition::FontTech(ref tech) => eval_font_tech(tech),
217 SupportsCondition::FutureSyntax(_) => false,
218 }
219 }
220}
221
222#[cfg(feature = "gecko")]
223fn eval_font_format(kw: &FontFaceSourceFormatKeyword) -> bool {
224 use crate::gecko_bindings::bindings;
225 unsafe { bindings::Gecko_IsFontFormatSupported(*kw) }
226}
227
228#[cfg(feature = "gecko")]
229fn eval_font_tech(flag: &FontFaceSourceTechFlags) -> bool {
230 use crate::gecko_bindings::bindings;
231 unsafe { bindings::Gecko_IsFontTechSupported(*flag) }
232}
233
234#[cfg(feature = "servo")]
235fn eval_font_format(_: &FontFaceSourceFormatKeyword) -> bool {
236 false
237}
238
239#[cfg(feature = "servo")]
240fn eval_font_tech(_: &FontFaceSourceTechFlags) -> bool {
241 false
242}
243
244pub fn parse_condition_or_declaration<'i, 't>(
247 input: &mut Parser<'i, 't>,
248) -> Result<SupportsCondition, ParseError<'i>> {
249 if let Ok(condition) = input.try_parse(SupportsCondition::parse) {
250 Ok(condition)
251 } else {
252 Declaration::parse(input).map(SupportsCondition::Declaration)
253 }
254}
255
256impl ToCss for SupportsCondition {
257 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
258 where
259 W: Write,
260 {
261 match *self {
262 SupportsCondition::Not(ref cond) => {
263 dest.write_str("not ")?;
264 cond.to_css(dest)
265 },
266 SupportsCondition::Parenthesized(ref cond) => {
267 dest.write_char('(')?;
268 cond.to_css(dest)?;
269 dest.write_char(')')
270 },
271 SupportsCondition::And(ref vec) => {
272 let mut first = true;
273 for cond in vec {
274 if !first {
275 dest.write_str(" and ")?;
276 }
277 first = false;
278 cond.to_css(dest)?;
279 }
280 Ok(())
281 },
282 SupportsCondition::Or(ref vec) => {
283 let mut first = true;
284 for cond in vec {
285 if !first {
286 dest.write_str(" or ")?;
287 }
288 first = false;
289 cond.to_css(dest)?;
290 }
291 Ok(())
292 },
293 SupportsCondition::Declaration(ref decl) => decl.to_css(dest),
294 SupportsCondition::Selector(ref selector) => {
295 dest.write_str("selector(")?;
296 selector.to_css(dest)?;
297 dest.write_char(')')
298 },
299 SupportsCondition::FontFormat(ref kw) => {
300 dest.write_str("font-format(")?;
301 kw.to_css(dest)?;
302 dest.write_char(')')
303 },
304 SupportsCondition::FontTech(ref flag) => {
305 dest.write_str("font-tech(")?;
306 flag.to_css(dest)?;
307 dest.write_char(')')
308 },
309 SupportsCondition::FutureSyntax(ref s) => dest.write_str(&s),
310 }
311 }
312}
313
314#[derive(Clone, Debug, ToShmem)]
315pub struct RawSelector(pub String);
317
318impl ToCss for RawSelector {
319 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
320 where
321 W: Write,
322 {
323 dest.write_str(&self.0)
324 }
325}
326
327impl RawSelector {
328 pub fn eval(&self, context: &ParserContext) -> bool {
330 let mut input = ParserInput::new(&self.0);
331 let mut input = Parser::new(&mut input);
332 input
333 .parse_entirely(|input| -> Result<(), CssParseError<()>> {
334 let parser = SelectorParser {
335 namespaces: &context.namespaces,
336 stylesheet_origin: context.stylesheet_origin,
337 url_data: context.url_data,
338 for_supports_rule: true,
339 };
340
341 Selector::<SelectorImpl>::parse(&parser, input)
342 .map_err(|_| input.new_custom_error(()))?;
343
344 Ok(())
345 })
346 .is_ok()
347 }
348}
349
350#[derive(Clone, Debug, ToShmem)]
351pub struct Declaration(pub String);
353
354impl ToCss for Declaration {
355 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
356 where
357 W: Write,
358 {
359 dest.write_str(&self.0)
360 }
361}
362
363fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
365 input.expect_no_error_token().map_err(|err| err.into())
366}
367
368impl Declaration {
369 pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Declaration, ParseError<'i>> {
371 let pos = input.position();
372 input.expect_ident()?;
373 input.expect_colon()?;
374 consume_any_value(input)?;
375 Ok(Declaration(input.slice_from(pos).to_owned()))
376 }
377
378 pub fn eval(&self, context: &ParserContext) -> bool {
382 debug_assert!(context.rule_types().contains(CssRuleType::Style));
383
384 let mut input = ParserInput::new(&self.0);
385 let mut input = Parser::new(&mut input);
386 input
387 .parse_entirely(|input| -> Result<(), CssParseError<()>> {
388 let prop = input.expect_ident_cloned().unwrap();
389 input.expect_colon().unwrap();
390
391 let id =
392 PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?;
393
394 let mut declarations = SourcePropertyDeclaration::default();
395 input.parse_until_before(Delimiter::Bang, |input| {
396 PropertyDeclaration::parse_into(&mut declarations, id, &context, input)
397 .map_err(|_| input.new_custom_error(()))
398 })?;
399 let _ = input.try_parse(parse_important);
400 Ok(())
401 })
402 .is_ok()
403 }
404}