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