1use crate::derives::*;
8use crate::font_face::{FontFaceSourceFormatKeyword, FontFaceSourceTechFlags};
9use crate::parser::ParserContext;
10use crate::properties::{PropertyDeclaration, PropertyId, SourcePropertyDeclaration};
11use crate::selector_parser::{SelectorImpl, SelectorParser};
12use crate::shared_lock::{DeepCloneWithLock, Locked};
13use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
14use crate::stylesheets::rule_parser::AtRuleType;
15use crate::stylesheets::{CssRuleType, CssRules};
16use cssparser::{Delimiter, Parser, SourceLocation, Token};
17use cssparser::{ParseError as CssParseError, match_ignore_ascii_case};
18use cssparser::{parse_important, serialize_identifier};
19#[cfg(feature = "gecko")]
20use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
21use selectors::parser::{Selector, SelectorParseErrorKind};
22use servo_arc::Arc;
23use std::fmt::{self, Write};
24use std::str;
25use style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};
26
27#[derive(Debug, ToShmem)]
31pub struct SupportsRule {
32 pub condition: SupportsCondition,
34 pub rules: Arc<Locked<CssRules>>,
36 pub enabled: bool,
38 pub source_location: SourceLocation,
40}
41
42impl SupportsRule {
43 #[cfg(feature = "gecko")]
45 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
46 self.rules.unconditional_shallow_size_of(ops)
48 + self.rules.read_with(guard).size_of(guard, ops)
49 }
50}
51
52impl ToCssWithGuard for SupportsRule {
53 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
54 dest.write_str("@supports ")?;
55 self.condition.to_css(&mut CssWriter::new(dest))?;
56 self.rules.read_with(guard).to_css_block(guard, dest)
57 }
58}
59
60impl DeepCloneWithLock for SupportsRule {
61 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
62 let rules = self.rules.read_with(guard);
63 SupportsRule {
64 condition: self.condition.clone(),
65 rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
66 enabled: self.enabled,
67 source_location: self.source_location,
68 }
69 }
70}
71
72#[derive(Clone, Debug, ToShmem)]
76pub enum SupportsCondition {
77 Not(Box<SupportsCondition>),
79 Parenthesized(Box<SupportsCondition>),
81 And(Vec<SupportsCondition>),
83 Or(Vec<SupportsCondition>),
85 Declaration(Declaration),
87 AtRule(AtRuleKeyword),
89 Selector(RawSelector),
91 FontFormat(FontFaceSourceFormatKeyword),
93 FontTech(FontFaceSourceTechFlags),
95 NamedFeature(NamedFeature),
97 FutureSyntax(String),
99}
100
101impl SupportsCondition {
102 pub fn parse(input: &mut Parser) -> Result<Self, ParseError> {
106 if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() {
107 let inner = SupportsCondition::parse_in_parens(input)?;
108 return Ok(SupportsCondition::Not(Box::new(inner)));
109 }
110
111 let in_parens = SupportsCondition::parse_in_parens(input)?;
112
113 let (keyword, wrapper) = match input.next() {
114 Err(..) => return Ok(in_parens),
116 Ok(Token::Ident(ident)) => {
117 match_ignore_ascii_case! { &ident,
118 "and" => ("and", SupportsCondition::And as fn(_) -> _),
119 "or" => ("or", SupportsCondition::Or as fn(_) -> _),
120 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
121 }
122 },
123 Ok(_) => return Err(ParseError::unexpected_token()),
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(function: &str, input: &mut Parser) -> Result<Self, ParseError> {
144 match_ignore_ascii_case! { function,
145 "at-rule" if crate::pref!("layout.css.supports.at-rule.enabled") => {
146 let kw = AtRuleKeyword::parse(input)?;
147 Ok(SupportsCondition::AtRule(kw))
148 },
149 "selector" => {
150 let pos = input.position();
151 consume_any_value(input)?;
152 Ok(SupportsCondition::Selector(RawSelector(
153 input.slice_from(pos).to_owned()
154 )))
155 },
156 "font-format" if crate::pref!("layout.css.font-tech.enabled", gecko = true) => {
157 let kw = FontFaceSourceFormatKeyword::parse(input)?;
158 Ok(SupportsCondition::FontFormat(kw))
159 },
160 "font-tech" if crate::pref!("layout.css.font-tech.enabled", gecko = true) => {
161 let flag = FontFaceSourceTechFlags::parse_one(input)?;
162 Ok(SupportsCondition::FontTech(flag))
163 },
164 "named-feature" => {
165 let feature = NamedFeature::parse(input)?;
166 Ok(SupportsCondition::NamedFeature(feature))
167 },
168 _ => {
169 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
170 },
171 }
172 }
173
174 pub fn parse_for_import(input: &mut Parser) -> Result<Self, ParseError> {
177 input.expect_function_matching("supports")?;
178 input.parse_nested_block(parse_condition_or_declaration)
179 }
180
181 fn parse_in_parens(input: &mut Parser) -> Result<Self, ParseError> {
183 input.skip_whitespace();
186 let pos = input.position();
187 match *input.next()? {
188 Token::ParenthesisBlock => {
189 let nested = input
190 .try_parse(|input| input.parse_nested_block(parse_condition_or_declaration));
191 if let Ok(nested) = nested {
192 return Ok(Self::Parenthesized(Box::new(nested)));
193 }
194 },
195 Token::Function(ref ident) => {
196 let ident = ident.clone();
197 let nested = input.try_parse(|input| {
198 input.parse_nested_block(|input| {
199 SupportsCondition::parse_functional(&ident, input)
200 })
201 });
202 if nested.is_ok() {
203 return nested;
204 }
205 },
206 _ => return Err(ParseError::unexpected_token()),
207 }
208 input.parse_nested_block(consume_any_value)?;
209 Ok(SupportsCondition::FutureSyntax(
210 input.slice_from(pos).to_owned(),
211 ))
212 }
213
214 pub fn eval(&self, cx: &ParserContext) -> bool {
216 match *self {
217 SupportsCondition::Not(ref cond) => !cond.eval(cx),
218 SupportsCondition::Parenthesized(ref cond) => cond.eval(cx),
219 SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)),
220 SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)),
221 SupportsCondition::AtRule(ref kw) => kw.eval(cx),
222 SupportsCondition::Declaration(ref decl) => decl.eval(cx),
223 SupportsCondition::Selector(ref selector) => selector.eval(cx),
224 SupportsCondition::FontFormat(ref format) => eval_font_format(format),
225 SupportsCondition::FontTech(ref tech) => eval_font_tech(tech),
226 SupportsCondition::NamedFeature(ref feature) => feature.eval(),
227 SupportsCondition::FutureSyntax(_) => false,
228 }
229 }
230}
231
232#[cfg(feature = "gecko")]
233fn eval_font_format(kw: &FontFaceSourceFormatKeyword) -> bool {
234 use crate::gecko_bindings::bindings;
235 unsafe { bindings::Gecko_IsFontFormatSupported(*kw) }
236}
237
238#[cfg(feature = "gecko")]
239fn eval_font_tech(flag: &FontFaceSourceTechFlags) -> bool {
240 use crate::gecko_bindings::bindings;
241 unsafe { bindings::Gecko_IsFontTechSupported(*flag) }
242}
243
244#[cfg(feature = "servo")]
245fn eval_font_format(_: &FontFaceSourceFormatKeyword) -> bool {
246 false
247}
248
249#[cfg(feature = "servo")]
250fn eval_font_tech(_: &FontFaceSourceTechFlags) -> bool {
251 false
252}
253
254pub fn parse_condition_or_declaration(input: &mut Parser) -> Result<SupportsCondition, ParseError> {
257 if let Ok(condition) = input.try_parse(SupportsCondition::parse) {
258 Ok(condition)
259 } else {
260 Declaration::parse(input).map(SupportsCondition::Declaration)
261 }
262}
263
264impl ToCss for SupportsCondition {
265 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
266 where
267 W: Write,
268 {
269 match *self {
270 SupportsCondition::Not(ref cond) => {
271 dest.write_str("not ")?;
272 cond.to_css(dest)
273 },
274 SupportsCondition::Parenthesized(ref cond) => {
275 dest.write_char('(')?;
276 cond.to_css(dest)?;
277 dest.write_char(')')
278 },
279 SupportsCondition::And(ref vec) => {
280 let mut first = true;
281 for cond in vec {
282 if !first {
283 dest.write_str(" and ")?;
284 }
285 first = false;
286 cond.to_css(dest)?;
287 }
288 Ok(())
289 },
290 SupportsCondition::Or(ref vec) => {
291 let mut first = true;
292 for cond in vec {
293 if !first {
294 dest.write_str(" or ")?;
295 }
296 first = false;
297 cond.to_css(dest)?;
298 }
299 Ok(())
300 },
301 SupportsCondition::AtRule(ref kw) => {
302 dest.write_str("at-rule(")?;
303 kw.to_css(dest)?;
304 dest.write_char(')')
305 },
306 SupportsCondition::Declaration(ref decl) => decl.to_css(dest),
307 SupportsCondition::Selector(ref selector) => {
308 dest.write_str("selector(")?;
309 selector.to_css(dest)?;
310 dest.write_char(')')
311 },
312 SupportsCondition::FontFormat(ref kw) => {
313 dest.write_str("font-format(")?;
314 kw.to_css(dest)?;
315 dest.write_char(')')
316 },
317 SupportsCondition::FontTech(ref flag) => {
318 dest.write_str("font-tech(")?;
319 flag.to_css(dest)?;
320 dest.write_char(')')
321 },
322 SupportsCondition::NamedFeature(ref feature) => {
323 dest.write_str("named-feature(")?;
324 feature.to_css(dest)?;
325 dest.write_char(')')
326 },
327 SupportsCondition::FutureSyntax(ref s) => dest.write_str(s),
328 }
329 }
330}
331
332#[derive(Clone, Debug, ToShmem)]
333pub struct RawSelector(pub String);
335
336impl ToCss for RawSelector {
337 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
338 where
339 W: Write,
340 {
341 dest.write_str(&self.0)
342 }
343}
344
345impl RawSelector {
346 pub fn eval(&self, context: &ParserContext) -> bool {
348 let mut input = Parser::new(&self.0);
349 input
350 .parse_entirely(|input| -> Result<(), CssParseError<()>> {
351 let parser = SelectorParser {
352 namespaces: &context.namespaces,
353 stylesheet_origin: context.stylesheet_origin,
354 url_data: context.url_data,
355 for_supports_rule: true,
356 };
357
358 Selector::<SelectorImpl>::parse(&parser, input)
359 .map_err(|_| CssParseError::custom(()))?;
360
361 Ok(())
362 })
363 .is_ok()
364 }
365}
366
367#[derive(Clone, Debug, ToShmem)]
368pub struct Declaration(pub String);
370
371impl ToCss for Declaration {
372 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
373 where
374 W: Write,
375 {
376 dest.write_str(&self.0)
377 }
378}
379
380fn consume_any_value(input: &mut Parser) -> Result<(), ParseError> {
382 input.expect_no_error_token().map_err(|err| err.into())
383}
384
385impl Declaration {
386 pub fn parse(input: &mut Parser) -> Result<Declaration, ParseError> {
388 let pos = input.position();
389 input.expect_ident()?;
390 input.expect_colon()?;
391 consume_any_value(input)?;
392 Ok(Declaration(input.slice_from(pos).to_owned()))
393 }
394
395 pub fn eval(&self, context: &ParserContext) -> bool {
399 debug_assert!(context.rule_types().contains(CssRuleType::Style));
400
401 let mut input = Parser::new(&self.0);
402 input
403 .parse_entirely(|input| -> Result<(), CssParseError<()>> {
404 let prop = input.expect_ident_cloned().unwrap();
405 input.expect_colon().unwrap();
406
407 let id =
408 PropertyId::parse(&prop, context).map_err(|_| CssParseError::custom(()))?;
409
410 let mut declarations = SourcePropertyDeclaration::default();
411 input.parse_until_before(Delimiter::Bang, |input| {
412 PropertyDeclaration::parse_into(&mut declarations, id, context, input)
413 .map_err(|_| CssParseError::custom(()))
414 })?;
415 let _ = input.try_parse(parse_important);
416 Ok(())
417 })
418 .is_ok()
419 }
420}
421
422#[derive(Clone, Debug, ToShmem)]
424pub struct AtRuleKeyword(pub Box<str>);
425
426impl AtRuleKeyword {
427 pub fn parse(input: &mut Parser) -> Result<Self, ParseError> {
429 match input.next()? {
430 Token::AtKeyword(kw) => Ok(Self(kw.as_ref().into())),
431 _ => Err(ParseError::unexpected_token()),
432 }
433 }
434
435 pub fn eval(&self, context: &ParserContext) -> bool {
438 AtRuleType::is_supported(&self.0, context)
439 }
440}
441
442impl ToCss for AtRuleKeyword {
443 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
444 where
445 W: Write,
446 {
447 dest.write_char('@')?;
448 serialize_identifier(&self.0, dest)
449 }
450}
451
452#[derive(Clone, Copy, Debug, Parse, ToCss, ToShmem)]
456#[repr(u8)]
457pub enum NamedFeature {
458 AnchorPositionFollowsTransforms,
460 SingleAxisScrollContainer,
462}
463
464impl NamedFeature {
465 #[cfg(feature = "gecko")]
469 pub fn eval(self) -> bool {
470 match self {
471 Self::AnchorPositionFollowsTransforms => {
472 crate::pref!("layout.css.anchor-positioning.follows-transforms.enabled")
473 },
474 Self::SingleAxisScrollContainer => false,
476 }
477 }
478
479 #[cfg(feature = "servo")]
483 pub fn eval(self) -> bool {
484 false
485 }
486}