1use super::{FeatureFlags, FeatureType, QueryFeatureExpression, QueryStyleRange};
11use crate::computed_value_flags::ComputedValueFlags;
12use crate::context::QuirksMode;
13use crate::custom_properties;
14use crate::derives::*;
15use crate::dom::AttributeTracker;
16use crate::properties::CSSWideKeyword;
17use crate::properties_and_values::rule::Descriptors as PropertyDescriptors;
18use crate::properties_and_values::value::{
19 AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,
20 SpecifiedValue as SpecifiedRegisteredValue,
21};
22use crate::stylesheets::{CssRuleType, CustomMediaEvaluator, Origin, UrlExtraData};
23use crate::stylist::Stylist;
24use crate::values::{computed, AtomString, DashedIdent};
25use crate::{error_reporting::ContextualParseError, parser::Parse, parser::ParserContext};
26use cssparser::{
27 match_ignore_ascii_case, parse_important, Parser, ParserInput, SourcePosition, Token,
28};
29use selectors::kleene_value::KleeneValue;
30use servo_arc::Arc;
31use std::fmt::{self, Write};
32use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
33
34#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
36#[allow(missing_docs)]
37pub enum Operator {
38 And,
39 Or,
40}
41
42#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
44enum AllowOr {
45 Yes,
46 No,
47}
48
49#[derive(Clone, Debug, PartialEq, ToShmem)]
50enum StyleFeatureValue {
51 Value(Option<Arc<custom_properties::SpecifiedValue>>),
52 Keyword(CSSWideKeyword),
53}
54
55trait OperationParser: Sized {
61 fn parse_internal<'i, 't>(
65 context: &ParserContext,
66 input: &mut Parser<'i, 't>,
67 feature_type: FeatureType,
68 allow_or: AllowOr,
69 ) -> Result<Self, ParseError<'i>> {
70 let location = input.current_source_location();
71
72 if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() {
73 let inner_condition = Self::parse_in_parens(context, input, feature_type)?;
74 return Ok(Self::new_not(Box::new(inner_condition)));
75 }
76
77 let first_condition = Self::parse_in_parens(context, input, feature_type)?;
78 let operator = match input.try_parse(Operator::parse) {
79 Ok(op) => op,
80 Err(..) => return Ok(first_condition),
81 };
82
83 if allow_or == AllowOr::No && operator == Operator::Or {
84 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
85 }
86
87 let mut conditions = vec![];
88 conditions.push(first_condition);
89 conditions.push(Self::parse_in_parens(context, input, feature_type)?);
90
91 let delim = match operator {
92 Operator::And => "and",
93 Operator::Or => "or",
94 };
95
96 loop {
97 if input.try_parse(|i| i.expect_ident_matching(delim)).is_err() {
98 return Ok(Self::new_operation(conditions.into_boxed_slice(), operator));
99 }
100
101 conditions.push(Self::parse_in_parens(context, input, feature_type)?);
102 }
103 }
104
105 fn parse_in_parens<'i, 't>(
107 context: &ParserContext,
108 input: &mut Parser<'i, 't>,
109 feature_type: FeatureType,
110 ) -> Result<Self, ParseError<'i>>;
111
112 fn new_not(inner: Box<Self>) -> Self;
115
116 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self;
118}
119
120#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
122pub enum StyleQuery {
123 Not(Box<StyleQuery>),
125 Operation(Box<[StyleQuery]>, Operator),
127 InParens(Box<StyleQuery>),
129 Feature(StyleFeature),
131 GeneralEnclosed(String),
133}
134
135impl ToCss for StyleQuery {
136 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
137 where
138 W: fmt::Write,
139 {
140 match *self {
141 StyleQuery::Not(ref c) => {
142 dest.write_str("not ")?;
143 c.maybe_parenthesized(dest)
144 },
145 StyleQuery::Operation(ref list, op) => {
146 let mut iter = list.iter();
147 let item = iter.next().unwrap();
148 item.maybe_parenthesized(dest)?;
149 for item in iter {
150 dest.write_char(' ')?;
151 op.to_css(dest)?;
152 dest.write_char(' ')?;
153 item.maybe_parenthesized(dest)?;
154 }
155 Ok(())
156 },
157 StyleQuery::InParens(ref c) => match &**c {
158 StyleQuery::Feature(_) | StyleQuery::InParens(_) => {
159 dest.write_char('(')?;
160 c.to_css(dest)?;
161 dest.write_char(')')
162 },
163 _ => c.to_css(dest),
164 },
165 StyleQuery::Feature(ref f) => f.to_css(dest),
166 StyleQuery::GeneralEnclosed(ref s) => dest.write_str(&s),
167 }
168 }
169}
170
171impl StyleQuery {
172 fn maybe_parenthesized<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
176 where
177 W: fmt::Write,
178 {
179 if let StyleQuery::GeneralEnclosed(ref s) = self {
180 dest.write_str(&s)
181 } else {
182 dest.write_char('(')?;
183 self.to_css(dest)?;
184 dest.write_char(')')
185 }
186 }
187
188 fn parse<'i, 't>(
189 context: &ParserContext,
190 input: &mut Parser<'i, 't>,
191 feature_type: FeatureType,
192 ) -> Result<Self, ParseError<'i>> {
193 if !static_prefs::pref!("layout.css.style-queries.enabled")
194 || feature_type != FeatureType::Container
195 {
196 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
197 }
198
199 if let Ok(feature) = input.try_parse(|input| StyleFeature::parse(context, input)) {
200 return Ok(Self::Feature(feature));
201 }
202
203 let inner = Self::parse_internal(context, input, feature_type, AllowOr::Yes)?;
204 Ok(Self::InParens(Box::new(inner)))
205 }
206
207 fn parse_in_parenthesis_block<'i>(
208 context: &ParserContext,
209 input: &mut Parser<'i, '_>,
210 ) -> Result<Self, ParseError<'i>> {
211 let feature_error = match input.try_parse(|input| StyleFeature::parse(context, input)) {
214 Ok(feature) => return Ok(Self::Feature(feature)),
215 Err(e) => e,
216 };
217
218 if let Ok(inner) = Self::parse(context, input, FeatureType::Container) {
219 return Ok(inner);
220 }
221
222 Err(feature_error)
223 }
224
225 fn try_parse_block<'i, T, F>(
226 context: &ParserContext,
227 input: &mut Parser<'i, '_>,
228 start: SourcePosition,
229 parse: F,
230 ) -> Option<T>
231 where
232 F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,
233 {
234 let nested = input.try_parse(|input| input.parse_nested_block(parse));
235 match nested {
236 Ok(nested) => Some(nested),
237 Err(e) => {
238 let loc = e.location;
241 let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
242 context.log_css_error(loc, error);
243 None
244 },
245 }
246 }
247
248 fn matches(
249 &self,
250 ctx: &computed::Context,
251 attribute_tracker: &mut AttributeTracker,
252 ) -> KleeneValue {
253 ctx.builder
254 .add_flags(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY);
255 match *self {
256 StyleQuery::Feature(ref f) => f.matches(ctx, attribute_tracker),
257 StyleQuery::Not(ref c) => !c.matches(ctx, attribute_tracker),
258 StyleQuery::InParens(ref c) => c.matches(ctx, attribute_tracker),
259 StyleQuery::Operation(ref conditions, op) => {
260 debug_assert!(!conditions.is_empty(), "We never create an empty op");
261 match op {
262 Operator::And => KleeneValue::any_false(conditions.iter(), |c| {
263 c.matches(ctx, attribute_tracker)
264 }),
265 Operator::Or => {
266 KleeneValue::any(conditions.iter(), |c| c.matches(ctx, attribute_tracker))
267 },
268 }
269 },
270 StyleQuery::GeneralEnclosed(_) => KleeneValue::Unknown,
271 }
272 }
273}
274
275impl OperationParser for StyleQuery {
276 fn parse_in_parens<'i, 't>(
277 context: &ParserContext,
278 input: &mut Parser<'i, 't>,
279 feature_type: FeatureType,
280 ) -> Result<Self, ParseError<'i>> {
281 assert!(feature_type == FeatureType::Container);
282 input.skip_whitespace();
283 let start = input.position();
284 let start_location = input.current_source_location();
285 match *input.next()? {
286 Token::ParenthesisBlock => {
287 if let Some(nested) = Self::try_parse_block(context, input, start, |i| {
288 Self::parse_in_parenthesis_block(context, i)
289 }) {
290 return Ok(nested);
291 }
292 input.parse_nested_block(|i| {
295 i.expect_ident()?;
296 i.expect_colon()?;
297 consume_any_value(i)
298 })?;
299 Ok(Self::GeneralEnclosed(input.slice_from(start).to_owned()))
300 },
301 ref t => return Err(start_location.new_unexpected_token_error(t.clone())),
302 }
303 }
304
305 fn new_not(inner: Box<Self>) -> Self {
306 Self::Not(inner)
307 }
308
309 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
310 Self::Operation(conditions, operator)
311 }
312}
313
314#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
317pub enum StyleFeature {
318 Plain(StyleFeaturePlain),
320 Range(QueryStyleRange),
322}
323
324impl StyleFeature {
325 fn parse<'i, 't>(
326 context: &ParserContext,
327 input: &mut Parser<'i, 't>,
328 ) -> Result<Self, ParseError<'i>> {
329 if let Ok(range) = input.try_parse(|i| QueryStyleRange::parse(context, i)) {
330 return Ok(Self::Range(range));
331 }
332
333 Ok(Self::Plain(StyleFeaturePlain::parse(context, input)?))
334 }
335
336 fn matches(
337 &self,
338 ctx: &computed::Context,
339 attribute_tracker: &mut AttributeTracker,
340 ) -> KleeneValue {
341 match self {
342 Self::Plain(plain) => plain.matches(ctx, attribute_tracker),
343 Self::Range(range) => range.evaluate(ctx, attribute_tracker),
344 }
345 }
346}
347
348#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
350pub struct StyleFeaturePlain {
351 name: custom_properties::Name,
352 #[ignore_malloc_size_of = "StyleFeatureValue has an Arc variant"]
353 value: StyleFeatureValue,
354}
355
356impl ToCss for StyleFeaturePlain {
357 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
358 where
359 W: fmt::Write,
360 {
361 dest.write_str("--")?;
362 crate::values::serialize_atom_identifier(&self.name, dest)?;
363 match self.value {
364 StyleFeatureValue::Keyword(k) => {
365 dest.write_str(": ")?;
366 k.to_css(dest)?;
367 },
368 StyleFeatureValue::Value(Some(ref v)) => {
369 dest.write_str(": ")?;
370 v.to_css(dest)?;
371 },
372 StyleFeatureValue::Value(None) => (),
373 }
374 Ok(())
375 }
376}
377
378impl StyleFeaturePlain {
379 fn parse<'i, 't>(
380 context: &ParserContext,
381 input: &mut Parser<'i, 't>,
382 ) -> Result<Self, ParseError<'i>> {
383 let ident = input.expect_ident()?;
384 let name = match custom_properties::parse_name(ident.as_ref()) {
386 Ok(name) => custom_properties::Name::from(name),
387 Err(()) => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
388 };
389 let value = if input.try_parse(|i| i.expect_colon()).is_ok() {
390 input.skip_whitespace();
391 if let Ok(keyword) = input.try_parse(|i| CSSWideKeyword::parse(i)) {
392 StyleFeatureValue::Keyword(keyword)
393 } else {
394 let value = custom_properties::SpecifiedValue::parse(
395 input,
396 Some(&context.namespaces.prefixes),
397 &context.url_data,
398 )?;
399 let _ = input.try_parse(parse_important);
401 StyleFeatureValue::Value(Some(Arc::new(value)))
402 }
403 } else {
404 StyleFeatureValue::Value(None)
405 };
406 Ok(Self { name, value })
407 }
408
409 fn substitute_and_compare(
412 value: &Arc<custom_properties::SpecifiedValue>,
413 registration: &PropertyDescriptors,
414 stylist: &Stylist,
415 ctx: &computed::Context,
416 attribute_tracker: &mut AttributeTracker,
417 current_value: Option<&ComputedRegisteredValue>,
418 ) -> bool {
419 let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(
420 Some(ctx.inherited_custom_properties().clone()),
421 None,
422 );
423 let custom_properties::SubstitutionResult { css, attr_taint } =
424 match custom_properties::substitute(
425 &value,
426 &substitution_functions,
427 stylist,
428 ctx,
429 attribute_tracker,
430 ) {
431 Ok(sub) => sub,
432 Err(_) => return current_value.is_none(),
433 };
434 if registration.is_universal() {
435 return match current_value {
436 Some(v) => v.as_universal().is_some_and(|v| v.css == css),
437 None => css.is_empty(),
438 };
439 }
440 let mut input = cssparser::ParserInput::new(&css);
441 let mut parser = Parser::new(&mut input);
442 let computed = SpecifiedRegisteredValue::compute(
443 &mut parser,
444 registration,
445 None,
446 &value.url_data,
447 ctx,
448 AllowComputationallyDependent::Yes,
449 attr_taint,
450 )
451 .ok();
452 computed.as_ref() == current_value
453 }
454
455 fn matches(
456 &self,
457 ctx: &computed::Context,
458 attribute_tracker: &mut AttributeTracker,
459 ) -> KleeneValue {
460 let stylist = ctx
462 .builder
463 .stylist
464 .expect("container queries should have a stylist around");
465 let registration = stylist.get_custom_property_registration(&self.name);
466 let current_value = ctx
467 .inherited_custom_properties()
468 .get(registration, &self.name);
469 KleeneValue::from(match self.value {
470 StyleFeatureValue::Value(Some(ref v)) => {
471 if ctx.container_info.is_none() {
472 false
474 } else if v.has_references() {
475 Self::substitute_and_compare(
478 v,
479 registration,
480 stylist,
481 ctx,
482 attribute_tracker,
483 current_value,
484 )
485 } else {
486 custom_properties::compute_variable_value(&v, registration, ctx).as_ref()
487 == current_value
488 }
489 },
490 StyleFeatureValue::Value(None) => current_value.is_some(),
491 StyleFeatureValue::Keyword(kw) => {
492 match kw {
493 CSSWideKeyword::Unset => current_value.is_none(),
494 CSSWideKeyword::Initial => {
495 if let Some(initial) = ®istration.initial_value {
496 let v = custom_properties::compute_variable_value(
497 &initial,
498 registration,
499 ctx,
500 );
501 v.as_ref() == current_value
502 } else {
503 current_value.is_none()
504 }
505 },
506 CSSWideKeyword::Inherit => {
507 if let Some(inherited) = ctx
508 .container_info
509 .as_ref()
510 .expect("queries should provide container info")
511 .inherited_style()
512 {
513 inherited.custom_properties().get(registration, &self.name)
514 == current_value
515 } else {
516 false
517 }
518 },
519 CSSWideKeyword::Revert
524 | CSSWideKeyword::RevertLayer
525 | CSSWideKeyword::RevertRule => false,
526 }
527 },
528 })
529 }
530}
531
532#[derive(
534 Clone,
535 Debug,
536 MallocSizeOf,
537 PartialEq,
538 Eq,
539 Parse,
540 SpecifiedValueInfo,
541 ToComputedValue,
542 ToCss,
543 ToShmem,
544)]
545#[repr(u8)]
546#[allow(missing_docs)]
547pub enum BoolValue {
548 False,
549 True,
550}
551
552#[derive(
555 Clone,
556 Debug,
557 Eq,
558 MallocSizeOf,
559 Parse,
560 PartialEq,
561 SpecifiedValueInfo,
562 ToComputedValue,
563 ToCss,
564 ToShmem,
565)]
566#[repr(u8)]
567pub enum MozPrefFeatureValue<I> {
568 #[css(skip)]
570 None,
571 Boolean(BoolValue),
573 Integer(I),
575 String(crate::values::AtomString),
577}
578
579type SpecifiedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::specified::Integer>;
580pub type ComputedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::computed::Integer>;
582
583#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
585pub struct MozPrefFeature {
586 name: crate::values::AtomString,
587 value: SpecifiedMozPrefFeatureValue,
588}
589
590impl MozPrefFeature {
591 fn parse<'i, 't>(
592 context: &ParserContext,
593 input: &mut Parser<'i, 't>,
594 feature_type: FeatureType,
595 ) -> Result<Self, ParseError<'i>> {
596 use crate::parser::Parse;
597 if !context.chrome_rules_enabled() || feature_type != FeatureType::Media {
598 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
599 }
600 let name = AtomString::parse(context, input)?;
601 let value = if input.try_parse(|i| i.expect_comma()).is_ok() {
602 SpecifiedMozPrefFeatureValue::parse(context, input)?
603 } else {
604 SpecifiedMozPrefFeatureValue::None
605 };
606 Ok(Self { name, value })
607 }
608
609 #[cfg(feature = "gecko")]
610 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
611 use crate::values::computed::ToComputedValue;
612 let value = self.value.to_computed_value(ctx);
613 KleeneValue::from(unsafe {
614 crate::gecko_bindings::bindings::Gecko_EvalMozPrefFeature(self.name.as_ptr(), &value)
615 })
616 }
617
618 #[cfg(feature = "servo")]
619 fn matches(&self, _: &computed::Context) -> KleeneValue {
620 KleeneValue::Unknown
621 }
622}
623
624impl ToCss for MozPrefFeature {
625 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
626 where
627 W: fmt::Write,
628 {
629 self.name.to_css(dest)?;
630 if !matches!(self.value, MozPrefFeatureValue::None) {
631 dest.write_str(", ")?;
632 self.value.to_css(dest)?;
633 }
634 Ok(())
635 }
636}
637
638#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
640pub enum QueryCondition {
641 Feature(QueryFeatureExpression),
643 Custom(DashedIdent),
645 Not(Box<QueryCondition>),
647 Operation(Box<[QueryCondition]>, Operator),
649 InParens(Box<QueryCondition>),
651 Style(StyleQuery),
653 MozPref(MozPrefFeature),
655 GeneralEnclosed(String, UrlExtraData),
657}
658
659impl ToCss for QueryCondition {
660 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
661 where
662 W: fmt::Write,
663 {
664 match *self {
665 QueryCondition::Feature(ref f) => f.to_css(dest),
668 QueryCondition::Custom(ref name) => {
669 dest.write_char('(')?;
670 name.to_css(dest)?;
671 dest.write_char(')')
672 },
673 QueryCondition::Not(ref c) => {
674 dest.write_str("not ")?;
675 c.to_css(dest)
676 },
677 QueryCondition::InParens(ref c) => {
678 dest.write_char('(')?;
679 c.to_css(dest)?;
680 dest.write_char(')')
681 },
682 QueryCondition::Style(ref c) => {
683 dest.write_str("style(")?;
684 c.to_css(dest)?;
685 dest.write_char(')')
686 },
687 QueryCondition::MozPref(ref c) => {
688 dest.write_str("-moz-pref(")?;
689 c.to_css(dest)?;
690 dest.write_char(')')
691 },
692 QueryCondition::Operation(ref list, op) => {
693 let mut iter = list.iter();
694 iter.next().unwrap().to_css(dest)?;
695 for item in iter {
696 dest.write_char(' ')?;
697 op.to_css(dest)?;
698 dest.write_char(' ')?;
699 item.to_css(dest)?;
700 }
701 Ok(())
702 },
703 QueryCondition::GeneralEnclosed(ref s, _) => dest.write_str(&s),
704 }
705 }
706}
707
708fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
710 input.expect_no_error_token().map_err(Into::into)
711}
712
713impl QueryCondition {
714 pub fn parse<'i, 't>(
716 context: &ParserContext,
717 input: &mut Parser<'i, 't>,
718 feature_type: FeatureType,
719 ) -> Result<Self, ParseError<'i>> {
720 Self::parse_internal(context, input, feature_type, AllowOr::Yes)
721 }
722
723 fn visit<F>(&self, visitor: &mut F)
724 where
725 F: FnMut(&Self),
726 {
727 visitor(self);
728 match *self {
729 Self::Custom(..)
730 | Self::Feature(..)
731 | Self::GeneralEnclosed(..)
732 | Self::Style(..)
733 | Self::MozPref(..) => {},
734 Self::Not(ref cond) => cond.visit(visitor),
735 Self::Operation(ref conds, _op) => {
736 for cond in conds.iter() {
737 cond.visit(visitor);
738 }
739 },
740 Self::InParens(ref cond) => cond.visit(visitor),
741 }
742 }
743
744 pub fn cumulative_flags(&self) -> FeatureFlags {
747 let mut result = FeatureFlags::empty();
748 self.visit(&mut |condition| {
749 if let Self::Style(..) = condition {
750 result.insert(FeatureFlags::STYLE);
751 }
752 if let Self::Feature(ref f) = condition {
753 result.insert(f.feature_flags())
754 }
755 });
756 result
757 }
758
759 pub fn parse_disallow_or<'i, 't>(
763 context: &ParserContext,
764 input: &mut Parser<'i, 't>,
765 feature_type: FeatureType,
766 ) -> Result<Self, ParseError<'i>> {
767 Self::parse_internal(context, input, feature_type, AllowOr::No)
768 }
769
770 fn parse_in_parenthesis_block<'i>(
771 context: &ParserContext,
772 input: &mut Parser<'i, '_>,
773 feature_type: FeatureType,
774 ) -> Result<Self, ParseError<'i>> {
775 let feature_error = match input.try_parse(|input| {
778 QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)
779 }) {
780 Ok(expr) => return Ok(Self::Feature(expr)),
781 Err(e) => e,
782 };
783 if static_prefs::pref!("layout.css.custom-media.enabled") {
784 if let Ok(custom) = input.try_parse(|input| DashedIdent::parse(context, input)) {
785 return Ok(Self::Custom(custom));
786 }
787 }
788 if let Ok(inner) = Self::parse(context, input, feature_type) {
789 return Ok(Self::InParens(Box::new(inner)));
790 }
791 Err(feature_error)
792 }
793
794 fn try_parse_block<'i, T, F>(
795 context: &ParserContext,
796 input: &mut Parser<'i, '_>,
797 start: SourcePosition,
798 parse: F,
799 ) -> Option<T>
800 where
801 F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,
802 {
803 let nested = input.try_parse(|input| input.parse_nested_block(parse));
804 match nested {
805 Ok(nested) => Some(nested),
806 Err(e) => {
807 let loc = e.location;
810 let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
811 context.log_css_error(loc, error);
812 None
813 },
814 }
815 }
816
817 pub fn matches(
823 &self,
824 context: &computed::Context,
825 custom: &mut CustomMediaEvaluator,
826 attribute_tracker: &mut AttributeTracker,
827 ) -> KleeneValue {
828 match *self {
829 Self::Custom(ref f) => custom.matches(f, context),
830 Self::Feature(ref f) => f.matches(context),
831 Self::GeneralEnclosed(ref str, ref url_data) => {
832 self.matches_general(&str, url_data, context, custom, attribute_tracker)
833 },
834 Self::InParens(ref c) => c.matches(context, custom, attribute_tracker),
835 Self::Not(ref c) => !c.matches(context, custom, attribute_tracker),
836 Self::Style(ref c) => c.matches(context, attribute_tracker),
837 Self::MozPref(ref c) => c.matches(context),
838 Self::Operation(ref conditions, op) => {
839 debug_assert!(!conditions.is_empty(), "We never create an empty op");
840 match op {
841 Operator::And => KleeneValue::any_false(conditions.iter(), |c| {
842 c.matches(context, custom, attribute_tracker)
843 }),
844 Operator::Or => KleeneValue::any(conditions.iter(), |c| {
845 c.matches(context, custom, attribute_tracker)
846 }),
847 }
848 },
849 }
850 }
851
852 fn matches_general(
855 &self,
856 css_text: &str,
857 url_data: &UrlExtraData,
858 context: &computed::Context,
859 custom: &mut CustomMediaEvaluator,
860 attribute_tracker: &mut AttributeTracker,
861 ) -> KleeneValue {
862 if !context.in_container_query {
864 return KleeneValue::Unknown;
865 }
866
867 let stylist = context
868 .builder
869 .stylist
870 .expect("container query should provide a Stylist");
871
872 let mut input = ParserInput::new(css_text);
874 let value = match custom_properties::SpecifiedValue::parse(
875 &mut Parser::new(&mut input),
876 None, url_data,
878 ) {
879 Ok(val) => val,
880 Err(_) => return KleeneValue::Unknown,
881 };
882
883 if !value.has_references() {
885 return KleeneValue::Unknown;
886 }
887
888 let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(
890 Some(context.inherited_custom_properties().clone()),
891 None,
892 );
893 let custom_properties::SubstitutionResult { css, attr_taint } =
894 match custom_properties::substitute(
895 &value,
896 &substitution_functions,
897 stylist,
898 context,
899 attribute_tracker,
900 ) {
901 Ok(sub) => sub,
902 Err(_) => return KleeneValue::Unknown,
903 };
904
905 let parser_context = ParserContext::new(
907 Origin::Author,
908 url_data,
909 Some(CssRuleType::Container),
910 ParsingMode::DEFAULT,
911 QuirksMode::NoQuirks,
912 Default::default(),
913 None,
914 None,
915 attr_taint,
916 );
917 let mut input = ParserInput::new(&css);
918 let result = match Self::parse(
919 &parser_context,
920 &mut Parser::new(&mut input),
921 FeatureType::Container,
922 ) {
923 Ok(Self::GeneralEnclosed(..)) => {
924 KleeneValue::Unknown
926 },
927 Ok(query) => query.matches(context, custom, attribute_tracker),
928 Err(_) => KleeneValue::Unknown,
929 };
930
931 result
932 }
933}
934
935impl OperationParser for QueryCondition {
936 fn parse_in_parens<'i, 't>(
940 context: &ParserContext,
941 input: &mut Parser<'i, 't>,
942 feature_type: FeatureType,
943 ) -> Result<Self, ParseError<'i>> {
944 input.skip_whitespace();
945 let start = input.position();
946 let start_location = input.current_source_location();
947 match *input.next()? {
948 Token::ParenthesisBlock => {
949 let nested = Self::try_parse_block(context, input, start, |input| {
950 Self::parse_in_parenthesis_block(context, input, feature_type)
951 });
952 if let Some(nested) = nested {
953 return Ok(nested);
954 }
955 },
956 Token::Function(ref name) => {
957 match_ignore_ascii_case! { name,
958 "style" => {
959 let query = Self::try_parse_block(context, input, start, |input| {
960 StyleQuery::parse(context, input, feature_type)
961 });
962 if let Some(query) = query {
963 return Ok(Self::Style(query));
964 }
965 },
966 "-moz-pref" => {
967 let feature = Self::try_parse_block(context, input, start, |input| {
968 MozPrefFeature::parse(context, input, feature_type)
969 });
970 if let Some(feature) = feature {
971 return Ok(Self::MozPref(feature));
972 }
973 },
974 _ => {},
975 }
976 },
977 ref t => return Err(start_location.new_unexpected_token_error(t.clone())),
978 }
979 input.parse_nested_block(consume_any_value)?;
980 Ok(Self::GeneralEnclosed(
981 input.slice_from(start).to_owned(),
982 context.url_data.clone(),
983 ))
984 }
985
986 fn new_not(inner: Box<Self>) -> Self {
987 Self::Not(inner)
988 }
989
990 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
991 Self::Operation(conditions, operator)
992 }
993}