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(
61 &self,
62 lock: &SharedRwLock,
63 guard: &SharedRwLockReadGuard,
64 ) -> Self {
65 let rules = self.rules.read_with(guard);
66 SupportsRule {
67 condition: self.condition.clone(),
68 rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
69 enabled: self.enabled,
70 source_location: self.source_location.clone(),
71 }
72 }
73}
74
75#[derive(Clone, Debug, ToShmem)]
79pub enum SupportsCondition {
80 Not(Box<SupportsCondition>),
82 Parenthesized(Box<SupportsCondition>),
84 And(Vec<SupportsCondition>),
86 Or(Vec<SupportsCondition>),
88 Declaration(Declaration),
90 Selector(RawSelector),
92 FontFormat(FontFaceSourceFormatKeyword),
94 FontTech(FontFaceSourceTechFlags),
96 FutureSyntax(String),
98}
99
100impl SupportsCondition {
101 pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
105 if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() {
106 let inner = SupportsCondition::parse_in_parens(input)?;
107 return Ok(SupportsCondition::Not(Box::new(inner)));
108 }
109
110 let in_parens = SupportsCondition::parse_in_parens(input)?;
111
112 let location = input.current_source_location();
113 let (keyword, wrapper) = match input.next() {
114 Err(..) => return Ok(in_parens),
116 Ok(&Token::Ident(ref ident)) => {
117 match_ignore_ascii_case! { &ident,
118 "and" => ("and", SupportsCondition::And as fn(_) -> _),
119 "or" => ("or", SupportsCondition::Or as fn(_) -> _),
120 _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
121 }
122 },
123 Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
124 };
125
126 let mut conditions = Vec::with_capacity(2);
127 conditions.push(in_parens);
128 loop {
129 conditions.push(SupportsCondition::parse_in_parens(input)?);
130 if input
131 .try_parse(|input| input.expect_ident_matching(keyword))
132 .is_err()
133 {
134 return Ok(wrapper(conditions));
138 }
139 }
140 }
141
142 fn parse_functional<'i, 't>(
144 function: &str,
145 input: &mut Parser<'i, 't>,
146 ) -> Result<Self, ParseError<'i>> {
147 match_ignore_ascii_case! { function,
148 "selector" => {
149 let pos = input.position();
150 consume_any_value(input)?;
151 Ok(SupportsCondition::Selector(RawSelector(
152 input.slice_from(pos).to_owned()
153 )))
154 },
155 "font-format" if static_prefs::pref!("layout.css.font-tech.enabled") => {
156 let kw = FontFaceSourceFormatKeyword::parse(input)?;
157 Ok(SupportsCondition::FontFormat(kw))
158 },
159 "font-tech" if static_prefs::pref!("layout.css.font-tech.enabled") => {
160 let flag = FontFaceSourceTechFlags::parse_one(input)?;
161 Ok(SupportsCondition::FontTech(flag))
162 },
163 _ => {
164 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
165 },
166 }
167 }
168
169 pub fn parse_for_import<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
172 input.expect_function_matching("supports")?;
173 input.parse_nested_block(parse_condition_or_declaration)
174 }
175
176 fn parse_in_parens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
178 input.skip_whitespace();
181 let pos = input.position();
182 let location = input.current_source_location();
183 match *input.next()? {
184 Token::ParenthesisBlock => {
185 let nested = input
186 .try_parse(|input| input.parse_nested_block(parse_condition_or_declaration));
187 if let Ok(nested) = nested {
188 return Ok(Self::Parenthesized(Box::new(nested)));
189 }
190 },
191 Token::Function(ref ident) => {
192 let ident = ident.clone();
193 let nested = input.try_parse(|input| {
194 input.parse_nested_block(|input| {
195 SupportsCondition::parse_functional(&ident, input)
196 })
197 });
198 if nested.is_ok() {
199 return nested;
200 }
201 },
202 ref t => return Err(location.new_unexpected_token_error(t.clone())),
203 }
204 input.parse_nested_block(consume_any_value)?;
205 Ok(SupportsCondition::FutureSyntax(
206 input.slice_from(pos).to_owned(),
207 ))
208 }
209
210 pub fn eval(&self, cx: &ParserContext) -> bool {
212 match *self {
213 SupportsCondition::Not(ref cond) => !cond.eval(cx),
214 SupportsCondition::Parenthesized(ref cond) => cond.eval(cx),
215 SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)),
216 SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)),
217 SupportsCondition::Declaration(ref decl) => decl.eval(cx),
218 SupportsCondition::Selector(ref selector) => selector.eval(cx),
219 SupportsCondition::FontFormat(ref format) => eval_font_format(format),
220 SupportsCondition::FontTech(ref tech) => eval_font_tech(tech),
221 SupportsCondition::FutureSyntax(_) => false,
222 }
223 }
224}
225
226#[cfg(feature = "gecko")]
227fn eval_font_format(kw: &FontFaceSourceFormatKeyword) -> bool {
228 use crate::gecko_bindings::bindings;
229 unsafe { bindings::Gecko_IsFontFormatSupported(*kw) }
230}
231
232#[cfg(feature = "gecko")]
233fn eval_font_tech(flag: &FontFaceSourceTechFlags) -> bool {
234 use crate::gecko_bindings::bindings;
235 unsafe { bindings::Gecko_IsFontTechSupported(*flag) }
236}
237
238#[cfg(feature = "servo")]
239fn eval_font_format(_: &FontFaceSourceFormatKeyword) -> bool {
240 false
241}
242
243#[cfg(feature = "servo")]
244fn eval_font_tech(_: &FontFaceSourceTechFlags) -> bool {
245 false
246}
247
248pub fn parse_condition_or_declaration<'i, 't>(
251 input: &mut Parser<'i, 't>,
252) -> Result<SupportsCondition, ParseError<'i>> {
253 if let Ok(condition) = input.try_parse(SupportsCondition::parse) {
254 Ok(condition)
255 } else {
256 Declaration::parse(input).map(SupportsCondition::Declaration)
257 }
258}
259
260impl ToCss for SupportsCondition {
261 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
262 where
263 W: Write,
264 {
265 match *self {
266 SupportsCondition::Not(ref cond) => {
267 dest.write_str("not ")?;
268 cond.to_css(dest)
269 },
270 SupportsCondition::Parenthesized(ref cond) => {
271 dest.write_char('(')?;
272 cond.to_css(dest)?;
273 dest.write_char(')')
274 },
275 SupportsCondition::And(ref vec) => {
276 let mut first = true;
277 for cond in vec {
278 if !first {
279 dest.write_str(" and ")?;
280 }
281 first = false;
282 cond.to_css(dest)?;
283 }
284 Ok(())
285 },
286 SupportsCondition::Or(ref vec) => {
287 let mut first = true;
288 for cond in vec {
289 if !first {
290 dest.write_str(" or ")?;
291 }
292 first = false;
293 cond.to_css(dest)?;
294 }
295 Ok(())
296 },
297 SupportsCondition::Declaration(ref decl) => decl.to_css(dest),
298 SupportsCondition::Selector(ref selector) => {
299 dest.write_str("selector(")?;
300 selector.to_css(dest)?;
301 dest.write_char(')')
302 },
303 SupportsCondition::FontFormat(ref kw) => {
304 dest.write_str("font-format(")?;
305 kw.to_css(dest)?;
306 dest.write_char(')')
307 },
308 SupportsCondition::FontTech(ref flag) => {
309 dest.write_str("font-tech(")?;
310 flag.to_css(dest)?;
311 dest.write_char(')')
312 },
313 SupportsCondition::FutureSyntax(ref s) => dest.write_str(&s),
314 }
315 }
316}
317
318#[derive(Clone, Debug, ToShmem)]
319pub struct RawSelector(pub String);
321
322impl ToCss for RawSelector {
323 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
324 where
325 W: Write,
326 {
327 dest.write_str(&self.0)
328 }
329}
330
331impl RawSelector {
332 pub fn eval(&self, context: &ParserContext) -> bool {
334 let mut input = ParserInput::new(&self.0);
335 let mut input = Parser::new(&mut input);
336 input
337 .parse_entirely(|input| -> Result<(), CssParseError<()>> {
338 let parser = SelectorParser {
339 namespaces: &context.namespaces,
340 stylesheet_origin: context.stylesheet_origin,
341 url_data: context.url_data,
342 for_supports_rule: true,
343 };
344
345 Selector::<SelectorImpl>::parse(&parser, input)
346 .map_err(|_| input.new_custom_error(()))?;
347
348 Ok(())
349 })
350 .is_ok()
351 }
352}
353
354#[derive(Clone, Debug, ToShmem)]
355pub struct Declaration(pub String);
357
358impl ToCss for Declaration {
359 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
360 where
361 W: Write,
362 {
363 dest.write_str(&self.0)
364 }
365}
366
367fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
369 input.expect_no_error_token().map_err(|err| err.into())
370}
371
372impl Declaration {
373 pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Declaration, ParseError<'i>> {
375 let pos = input.position();
376 input.expect_ident()?;
377 input.expect_colon()?;
378 consume_any_value(input)?;
379 Ok(Declaration(input.slice_from(pos).to_owned()))
380 }
381
382 pub fn eval(&self, context: &ParserContext) -> bool {
386 debug_assert!(context.rule_types().contains(CssRuleType::Style));
387
388 let mut input = ParserInput::new(&self.0);
389 let mut input = Parser::new(&mut input);
390 input
391 .parse_entirely(|input| -> Result<(), CssParseError<()>> {
392 let prop = input.expect_ident_cloned().unwrap();
393 input.expect_colon().unwrap();
394
395 let id =
396 PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?;
397
398 let mut declarations = SourcePropertyDeclaration::default();
399 input.parse_until_before(Delimiter::Bang, |input| {
400 PropertyDeclaration::parse_into(&mut declarations, id, &context, input)
401 .map_err(|_| input.new_custom_error(()))
402 })?;
403 let _ = input.try_parse(parse_important);
404 Ok(())
405 })
406 .is_ok()
407 }
408}