1use super::{FeatureFlags, FeatureType, QueryFeatureExpression, QueryStyleRange};
11use crate::custom_properties;
12use crate::derives::*;
13use crate::dom::AttributeTracker;
14use crate::properties::CSSWideKeyword;
15use crate::properties_and_values::registry::PropertyRegistrationData;
16use crate::properties_and_values::value::{
17 AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,
18 SpecifiedValue as SpecifiedRegisteredValue,
19};
20use crate::stylesheets::CustomMediaEvaluator;
21use crate::stylist::Stylist;
22use crate::values::{computed, AtomString, DashedIdent};
23use crate::{error_reporting::ContextualParseError, parser::Parse, parser::ParserContext};
24use cssparser::{match_ignore_ascii_case, parse_important, Parser, SourcePosition, Token};
25use selectors::kleene_value::KleeneValue;
26use servo_arc::Arc;
27use std::fmt::{self, Write};
28use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
29
30#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
32#[allow(missing_docs)]
33pub enum Operator {
34 And,
35 Or,
36}
37
38#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
40enum AllowOr {
41 Yes,
42 No,
43}
44
45#[derive(Clone, Debug, PartialEq, ToShmem)]
46enum StyleFeatureValue {
47 Value(Option<Arc<custom_properties::SpecifiedValue>>),
48 Keyword(CSSWideKeyword),
49}
50
51trait OperationParser: Sized {
57 fn parse_internal<'i, 't>(
61 context: &ParserContext,
62 input: &mut Parser<'i, 't>,
63 feature_type: FeatureType,
64 allow_or: AllowOr,
65 ) -> Result<Self, ParseError<'i>> {
66 let location = input.current_source_location();
67
68 if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() {
69 let inner_condition = Self::parse_in_parens(context, input, feature_type)?;
70 return Ok(Self::new_not(Box::new(inner_condition)));
71 }
72
73 let first_condition = Self::parse_in_parens(context, input, feature_type)?;
74 let operator = match input.try_parse(Operator::parse) {
75 Ok(op) => op,
76 Err(..) => return Ok(first_condition),
77 };
78
79 if allow_or == AllowOr::No && operator == Operator::Or {
80 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
81 }
82
83 let mut conditions = vec![];
84 conditions.push(first_condition);
85 conditions.push(Self::parse_in_parens(context, input, feature_type)?);
86
87 let delim = match operator {
88 Operator::And => "and",
89 Operator::Or => "or",
90 };
91
92 loop {
93 if input.try_parse(|i| i.expect_ident_matching(delim)).is_err() {
94 return Ok(Self::new_operation(conditions.into_boxed_slice(), operator));
95 }
96
97 conditions.push(Self::parse_in_parens(context, input, feature_type)?);
98 }
99 }
100
101 fn parse_in_parens<'i, 't>(
103 context: &ParserContext,
104 input: &mut Parser<'i, 't>,
105 feature_type: FeatureType,
106 ) -> Result<Self, ParseError<'i>>;
107
108 fn new_not(inner: Box<Self>) -> Self;
111
112 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self;
114}
115
116#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
118pub enum StyleQuery {
119 Not(Box<StyleQuery>),
121 Operation(Box<[StyleQuery]>, Operator),
123 InParens(Box<StyleQuery>),
125 Feature(StyleFeature),
127 GeneralEnclosed(String),
129}
130
131impl ToCss for StyleQuery {
132 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
133 where
134 W: fmt::Write,
135 {
136 match *self {
137 StyleQuery::Not(ref c) => {
138 dest.write_str("not ")?;
139 c.maybe_parenthesized(dest)
140 },
141 StyleQuery::Operation(ref list, op) => {
142 let mut iter = list.iter();
143 let item = iter.next().unwrap();
144 item.maybe_parenthesized(dest)?;
145 for item in iter {
146 dest.write_char(' ')?;
147 op.to_css(dest)?;
148 dest.write_char(' ')?;
149 item.maybe_parenthesized(dest)?;
150 }
151 Ok(())
152 },
153 StyleQuery::InParens(ref c) => match &**c {
154 StyleQuery::Feature(_) | StyleQuery::InParens(_) => {
155 dest.write_char('(')?;
156 c.to_css(dest)?;
157 dest.write_char(')')
158 },
159 _ => c.to_css(dest),
160 },
161 StyleQuery::Feature(ref f) => f.to_css(dest),
162 StyleQuery::GeneralEnclosed(ref s) => dest.write_str(&s),
163 }
164 }
165}
166
167impl StyleQuery {
168 fn maybe_parenthesized<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
172 where
173 W: fmt::Write,
174 {
175 if let StyleQuery::GeneralEnclosed(ref s) = self {
176 dest.write_str(&s)
177 } else {
178 dest.write_char('(')?;
179 self.to_css(dest)?;
180 dest.write_char(')')
181 }
182 }
183
184 fn parse<'i, 't>(
185 context: &ParserContext,
186 input: &mut Parser<'i, 't>,
187 feature_type: FeatureType,
188 ) -> Result<Self, ParseError<'i>> {
189 if !static_prefs::pref!("layout.css.style-queries.enabled")
190 || feature_type != FeatureType::Container
191 {
192 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
193 }
194
195 if let Ok(feature) = input.try_parse(|input| StyleFeature::parse(context, input)) {
196 return Ok(Self::Feature(feature));
197 }
198
199 let inner = Self::parse_internal(context, input, feature_type, AllowOr::Yes)?;
200 Ok(Self::InParens(Box::new(inner)))
201 }
202
203 fn parse_in_parenthesis_block<'i>(
204 context: &ParserContext,
205 input: &mut Parser<'i, '_>,
206 ) -> Result<Self, ParseError<'i>> {
207 let feature_error = match input.try_parse(|input| StyleFeature::parse(context, input)) {
210 Ok(feature) => return Ok(Self::Feature(feature)),
211 Err(e) => e,
212 };
213
214 if let Ok(inner) = Self::parse(context, input, FeatureType::Container) {
215 return Ok(inner);
216 }
217
218 Err(feature_error)
219 }
220
221 fn try_parse_block<'i, T, F>(
222 context: &ParserContext,
223 input: &mut Parser<'i, '_>,
224 start: SourcePosition,
225 parse: F,
226 ) -> Option<T>
227 where
228 F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,
229 {
230 let nested = input.try_parse(|input| input.parse_nested_block(parse));
231 match nested {
232 Ok(nested) => Some(nested),
233 Err(e) => {
234 let loc = e.location;
237 let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
238 context.log_css_error(loc, error);
239 None
240 },
241 }
242 }
243
244 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
245 match *self {
246 StyleQuery::Feature(ref f) => f.matches(ctx),
247 StyleQuery::Not(ref c) => !c.matches(ctx),
248 StyleQuery::InParens(ref c) => c.matches(ctx),
249 StyleQuery::Operation(ref conditions, op) => {
250 debug_assert!(!conditions.is_empty(), "We never create an empty op");
251 match op {
252 Operator::And => KleeneValue::any_false(conditions.iter(), |c| c.matches(ctx)),
253 Operator::Or => KleeneValue::any(conditions.iter(), |c| c.matches(ctx)),
254 }
255 },
256 StyleQuery::GeneralEnclosed(_) => KleeneValue::Unknown,
257 }
258 }
259}
260
261impl OperationParser for StyleQuery {
262 fn parse_in_parens<'i, 't>(
263 context: &ParserContext,
264 input: &mut Parser<'i, 't>,
265 feature_type: FeatureType,
266 ) -> Result<Self, ParseError<'i>> {
267 assert!(feature_type == FeatureType::Container);
268 input.skip_whitespace();
269 let start = input.position();
270 let start_location = input.current_source_location();
271 match *input.next()? {
272 Token::ParenthesisBlock => {
273 if let Some(nested) = Self::try_parse_block(context, input, start, |i| {
274 Self::parse_in_parenthesis_block(context, i)
275 }) {
276 return Ok(nested);
277 }
278 input.parse_nested_block(|i| {
281 i.expect_ident()?;
282 i.expect_colon()?;
283 consume_any_value(i)
284 })?;
285 Ok(Self::GeneralEnclosed(input.slice_from(start).to_owned()))
286 },
287 ref t => return Err(start_location.new_unexpected_token_error(t.clone())),
288 }
289 }
290
291 fn new_not(inner: Box<Self>) -> Self {
292 Self::Not(inner)
293 }
294
295 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
296 Self::Operation(conditions, operator)
297 }
298}
299
300#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
303pub enum StyleFeature {
304 Plain(StyleFeaturePlain),
306 Range(QueryStyleRange),
308}
309
310impl StyleFeature {
311 fn parse<'i, 't>(
312 context: &ParserContext,
313 input: &mut Parser<'i, 't>,
314 ) -> Result<Self, ParseError<'i>> {
315 if let Ok(range) = input.try_parse(|i| QueryStyleRange::parse(context, i)) {
316 return Ok(Self::Range(range));
317 }
318
319 Ok(Self::Plain(StyleFeaturePlain::parse(context, input)?))
320 }
321
322 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
323 match self {
324 Self::Plain(plain) => plain.matches(ctx),
325 Self::Range(range) => range.evaluate(ctx),
326 }
327 }
328}
329
330#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
332pub struct StyleFeaturePlain {
333 name: custom_properties::Name,
334 #[ignore_malloc_size_of = "StyleFeatureValue has an Arc variant"]
335 value: StyleFeatureValue,
336}
337
338impl ToCss for StyleFeaturePlain {
339 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
340 where
341 W: fmt::Write,
342 {
343 dest.write_str("--")?;
344 crate::values::serialize_atom_identifier(&self.name, dest)?;
345 match self.value {
346 StyleFeatureValue::Keyword(k) => {
347 dest.write_str(": ")?;
348 k.to_css(dest)?;
349 },
350 StyleFeatureValue::Value(Some(ref v)) => {
351 dest.write_str(": ")?;
352 v.to_css(dest)?;
353 },
354 StyleFeatureValue::Value(None) => (),
355 }
356 Ok(())
357 }
358}
359
360impl StyleFeaturePlain {
361 fn parse<'i, 't>(
362 context: &ParserContext,
363 input: &mut Parser<'i, 't>,
364 ) -> Result<Self, ParseError<'i>> {
365 let ident = input.expect_ident()?;
366 let name = match custom_properties::parse_name(ident.as_ref()) {
368 Ok(name) => custom_properties::Name::from(name),
369 Err(()) => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
370 };
371 let value = if input.try_parse(|i| i.expect_colon()).is_ok() {
372 input.skip_whitespace();
373 if let Ok(keyword) = input.try_parse(|i| CSSWideKeyword::parse(i)) {
374 StyleFeatureValue::Keyword(keyword)
375 } else {
376 let value = custom_properties::SpecifiedValue::parse(
377 input,
378 Some(&context.namespaces.prefixes),
379 &context.url_data,
380 )?;
381 let _ = input.try_parse(parse_important);
383 StyleFeatureValue::Value(Some(Arc::new(value)))
384 }
385 } else {
386 StyleFeatureValue::Value(None)
387 };
388 Ok(Self { name, value })
389 }
390
391 fn substitute_and_compare(
394 value: &Arc<custom_properties::SpecifiedValue>,
395 registration: &PropertyRegistrationData,
396 stylist: &Stylist,
397 ctx: &computed::Context,
398 current_value: Option<&ComputedRegisteredValue>,
399 ) -> bool {
400 let substituted = match crate::custom_properties::substitute(
401 &value,
402 ctx.inherited_custom_properties(),
403 stylist,
404 ctx,
405 &mut AttributeTracker::new_dummy(),
407 ) {
408 Ok(sub) => sub,
409 Err(_) => return current_value.is_none(),
410 };
411 if registration.syntax.is_universal() {
412 return match current_value {
413 Some(v) => v.as_universal().is_some_and(|v| v.css == substituted),
414 None => substituted.is_empty(),
415 };
416 }
417 let mut input = cssparser::ParserInput::new(&substituted);
418 let mut parser = Parser::new(&mut input);
419 let computed = SpecifiedRegisteredValue::compute(
420 &mut parser,
421 registration,
422 None,
423 &value.url_data,
424 ctx,
425 AllowComputationallyDependent::Yes,
426 )
427 .ok();
428 computed.as_ref() == current_value
429 }
430
431 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
432 let stylist = ctx
434 .builder
435 .stylist
436 .expect("container queries should have a stylist around");
437 let registration = stylist.get_custom_property_registration(&self.name);
438 let current_value = ctx
439 .inherited_custom_properties()
440 .get(registration, &self.name);
441 KleeneValue::from(match self.value {
442 StyleFeatureValue::Value(Some(ref v)) => {
443 if ctx.container_info.is_none() {
444 false
446 } else if v.has_references() {
447 Self::substitute_and_compare(v, registration, stylist, ctx, current_value)
450 } else {
451 custom_properties::compute_variable_value(&v, registration, ctx).as_ref()
452 == current_value
453 }
454 },
455 StyleFeatureValue::Value(None) => current_value.is_some(),
456 StyleFeatureValue::Keyword(kw) => {
457 match kw {
458 CSSWideKeyword::Unset => current_value.is_none(),
459 CSSWideKeyword::Initial => {
460 if let Some(initial) = ®istration.initial_value {
461 let v = custom_properties::compute_variable_value(
462 &initial,
463 registration,
464 ctx,
465 );
466 v == current_value.cloned()
467 } else {
468 current_value.is_none()
469 }
470 },
471 CSSWideKeyword::Inherit => {
472 if let Some(inherited) = ctx
473 .container_info
474 .as_ref()
475 .expect("queries should provide container info")
476 .inherited_style()
477 {
478 current_value
479 == inherited.custom_properties().get(registration, &self.name)
480 } else {
481 false
482 }
483 },
484 CSSWideKeyword::Revert | CSSWideKeyword::RevertLayer => false,
489 }
490 },
491 })
492 }
493}
494
495#[derive(
497 Clone,
498 Debug,
499 MallocSizeOf,
500 PartialEq,
501 Eq,
502 Parse,
503 SpecifiedValueInfo,
504 ToComputedValue,
505 ToCss,
506 ToShmem,
507)]
508#[repr(u8)]
509#[allow(missing_docs)]
510pub enum BoolValue {
511 False,
512 True,
513}
514
515#[derive(
518 Clone,
519 Debug,
520 Eq,
521 MallocSizeOf,
522 Parse,
523 PartialEq,
524 SpecifiedValueInfo,
525 ToComputedValue,
526 ToCss,
527 ToShmem,
528)]
529#[repr(u8)]
530pub enum MozPrefFeatureValue<I> {
531 #[css(skip)]
533 None,
534 Boolean(BoolValue),
536 Integer(I),
538 String(crate::values::AtomString),
540}
541
542type SpecifiedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::specified::Integer>;
543pub type ComputedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::computed::Integer>;
545
546#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
548pub struct MozPrefFeature {
549 name: crate::values::AtomString,
550 value: SpecifiedMozPrefFeatureValue,
551}
552
553impl MozPrefFeature {
554 fn parse<'i, 't>(
555 context: &ParserContext,
556 input: &mut Parser<'i, 't>,
557 feature_type: FeatureType,
558 ) -> Result<Self, ParseError<'i>> {
559 use crate::parser::Parse;
560 if !context.chrome_rules_enabled() || feature_type != FeatureType::Media {
561 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
562 }
563 let name = AtomString::parse(context, input)?;
564 let value = if input.try_parse(|i| i.expect_comma()).is_ok() {
565 SpecifiedMozPrefFeatureValue::parse(context, input)?
566 } else {
567 SpecifiedMozPrefFeatureValue::None
568 };
569 Ok(Self { name, value })
570 }
571
572 #[cfg(feature = "gecko")]
573 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
574 use crate::values::computed::ToComputedValue;
575 let value = self.value.to_computed_value(ctx);
576 KleeneValue::from(unsafe {
577 crate::gecko_bindings::bindings::Gecko_EvalMozPrefFeature(self.name.as_ptr(), &value)
578 })
579 }
580
581 #[cfg(feature = "servo")]
582 fn matches(&self, _: &computed::Context) -> KleeneValue {
583 KleeneValue::Unknown
584 }
585}
586
587impl ToCss for MozPrefFeature {
588 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
589 where
590 W: fmt::Write,
591 {
592 self.name.to_css(dest)?;
593 if !matches!(self.value, MozPrefFeatureValue::None) {
594 dest.write_str(", ")?;
595 self.value.to_css(dest)?;
596 }
597 Ok(())
598 }
599}
600
601#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
603pub enum QueryCondition {
604 Feature(QueryFeatureExpression),
606 Custom(DashedIdent),
608 Not(Box<QueryCondition>),
610 Operation(Box<[QueryCondition]>, Operator),
612 InParens(Box<QueryCondition>),
614 Style(StyleQuery),
616 MozPref(MozPrefFeature),
618 GeneralEnclosed(String),
620}
621
622impl ToCss for QueryCondition {
623 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
624 where
625 W: fmt::Write,
626 {
627 match *self {
628 QueryCondition::Feature(ref f) => f.to_css(dest),
631 QueryCondition::Custom(ref name) => {
632 dest.write_char('(')?;
633 name.to_css(dest)?;
634 dest.write_char(')')
635 },
636 QueryCondition::Not(ref c) => {
637 dest.write_str("not ")?;
638 c.to_css(dest)
639 },
640 QueryCondition::InParens(ref c) => {
641 dest.write_char('(')?;
642 c.to_css(dest)?;
643 dest.write_char(')')
644 },
645 QueryCondition::Style(ref c) => {
646 dest.write_str("style(")?;
647 c.to_css(dest)?;
648 dest.write_char(')')
649 },
650 QueryCondition::MozPref(ref c) => {
651 dest.write_str("-moz-pref(")?;
652 c.to_css(dest)?;
653 dest.write_char(')')
654 },
655 QueryCondition::Operation(ref list, op) => {
656 let mut iter = list.iter();
657 iter.next().unwrap().to_css(dest)?;
658 for item in iter {
659 dest.write_char(' ')?;
660 op.to_css(dest)?;
661 dest.write_char(' ')?;
662 item.to_css(dest)?;
663 }
664 Ok(())
665 },
666 QueryCondition::GeneralEnclosed(ref s) => dest.write_str(&s),
667 }
668 }
669}
670
671fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
673 input.expect_no_error_token().map_err(Into::into)
674}
675
676impl QueryCondition {
677 pub fn parse<'i, 't>(
679 context: &ParserContext,
680 input: &mut Parser<'i, 't>,
681 feature_type: FeatureType,
682 ) -> Result<Self, ParseError<'i>> {
683 Self::parse_internal(context, input, feature_type, AllowOr::Yes)
684 }
685
686 fn visit<F>(&self, visitor: &mut F)
687 where
688 F: FnMut(&Self),
689 {
690 visitor(self);
691 match *self {
692 Self::Custom(..)
693 | Self::Feature(..)
694 | Self::GeneralEnclosed(..)
695 | Self::Style(..)
696 | Self::MozPref(..) => {},
697 Self::Not(ref cond) => cond.visit(visitor),
698 Self::Operation(ref conds, _op) => {
699 for cond in conds.iter() {
700 cond.visit(visitor);
701 }
702 },
703 Self::InParens(ref cond) => cond.visit(visitor),
704 }
705 }
706
707 pub fn cumulative_flags(&self) -> FeatureFlags {
710 let mut result = FeatureFlags::empty();
711 self.visit(&mut |condition| {
712 if let Self::Style(..) = condition {
713 result.insert(FeatureFlags::STYLE);
714 }
715 if let Self::Feature(ref f) = condition {
716 result.insert(f.feature_flags())
717 }
718 });
719 result
720 }
721
722 pub fn parse_disallow_or<'i, 't>(
726 context: &ParserContext,
727 input: &mut Parser<'i, 't>,
728 feature_type: FeatureType,
729 ) -> Result<Self, ParseError<'i>> {
730 Self::parse_internal(context, input, feature_type, AllowOr::No)
731 }
732
733 fn parse_in_parenthesis_block<'i>(
734 context: &ParserContext,
735 input: &mut Parser<'i, '_>,
736 feature_type: FeatureType,
737 ) -> Result<Self, ParseError<'i>> {
738 let feature_error = match input.try_parse(|input| {
741 QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)
742 }) {
743 Ok(expr) => return Ok(Self::Feature(expr)),
744 Err(e) => e,
745 };
746 if static_prefs::pref!("layout.css.custom-media.enabled") {
747 if let Ok(custom) = input.try_parse(|input| DashedIdent::parse(context, input)) {
748 return Ok(Self::Custom(custom));
749 }
750 }
751 if let Ok(inner) = Self::parse(context, input, feature_type) {
752 return Ok(Self::InParens(Box::new(inner)));
753 }
754 Err(feature_error)
755 }
756
757 fn try_parse_block<'i, T, F>(
758 context: &ParserContext,
759 input: &mut Parser<'i, '_>,
760 start: SourcePosition,
761 parse: F,
762 ) -> Option<T>
763 where
764 F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,
765 {
766 let nested = input.try_parse(|input| input.parse_nested_block(parse));
767 match nested {
768 Ok(nested) => Some(nested),
769 Err(e) => {
770 let loc = e.location;
773 let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
774 context.log_css_error(loc, error);
775 None
776 },
777 }
778 }
779
780 pub fn matches(
786 &self,
787 context: &computed::Context,
788 custom: &mut CustomMediaEvaluator,
789 ) -> KleeneValue {
790 match *self {
791 QueryCondition::Custom(ref f) => custom.matches(f, context),
792 QueryCondition::Feature(ref f) => f.matches(context),
793 QueryCondition::GeneralEnclosed(_) => KleeneValue::Unknown,
794 QueryCondition::InParens(ref c) => c.matches(context, custom),
795 QueryCondition::Not(ref c) => !c.matches(context, custom),
796 QueryCondition::Style(ref c) => c.matches(context),
797 QueryCondition::MozPref(ref c) => c.matches(context),
798 QueryCondition::Operation(ref conditions, op) => {
799 debug_assert!(!conditions.is_empty(), "We never create an empty op");
800 match op {
801 Operator::And => {
802 KleeneValue::any_false(conditions.iter(), |c| c.matches(context, custom))
803 },
804 Operator::Or => {
805 KleeneValue::any(conditions.iter(), |c| c.matches(context, custom))
806 },
807 }
808 },
809 }
810 }
811}
812
813impl OperationParser for QueryCondition {
814 fn parse_in_parens<'i, 't>(
818 context: &ParserContext,
819 input: &mut Parser<'i, 't>,
820 feature_type: FeatureType,
821 ) -> Result<Self, ParseError<'i>> {
822 input.skip_whitespace();
823 let start = input.position();
824 let start_location = input.current_source_location();
825 match *input.next()? {
826 Token::ParenthesisBlock => {
827 let nested = Self::try_parse_block(context, input, start, |input| {
828 Self::parse_in_parenthesis_block(context, input, feature_type)
829 });
830 if let Some(nested) = nested {
831 return Ok(nested);
832 }
833 },
834 Token::Function(ref name) => {
835 match_ignore_ascii_case! { name,
836 "style" => {
837 let query = Self::try_parse_block(context, input, start, |input| {
838 StyleQuery::parse(context, input, feature_type)
839 });
840 if let Some(query) = query {
841 return Ok(Self::Style(query));
842 }
843 },
844 "-moz-pref" => {
845 let feature = Self::try_parse_block(context, input, start, |input| {
846 MozPrefFeature::parse(context, input, feature_type)
847 });
848 if let Some(feature) = feature {
849 return Ok(Self::MozPref(feature));
850 }
851 },
852 _ => {},
853 }
854 },
855 ref t => return Err(start_location.new_unexpected_token_error(t.clone())),
856 }
857 input.parse_nested_block(consume_any_value)?;
858 Ok(Self::GeneralEnclosed(input.slice_from(start).to_owned()))
859 }
860
861 fn new_not(inner: Box<Self>) -> Self {
862 Self::Not(inner)
863 }
864
865 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
866 Self::Operation(conditions, operator)
867 }
868}