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::{AtomString, DashedIdent, computed};
26use crate::{error_reporting::ContextualParseError, parser::Parse, parser::ParserContext};
27use cssparser::{
28 Parser, SourceLocation, SourcePosition, Token, match_ignore_ascii_case, parse_important,
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 && context.error_reporting_enabled()
134 {
135 let error =
138 ContextualParseError::InvalidMediaRule(input.slice_from(start), e.clone());
139 context.log_css_error(start_location, error);
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 enabled(feature_type: FeatureType) -> bool {
215 crate::pref!("layout.css.style-queries.enabled") && feature_type == FeatureType::Container
216 }
217
218 fn parse(
219 context: &ParserContext,
220 input: &mut Parser,
221 feature_type: FeatureType,
222 ) -> Result<Self, ParseError> {
223 if let Ok(feature) = input.try_parse(|input| StyleFeature::parse(context, input)) {
224 return Ok(Self::Feature(feature));
225 }
226
227 let inner = Self::parse_internal(context, input, feature_type, AllowOr::Yes)?;
228 Ok(Self::InParens(Box::new(inner)))
229 }
230
231 fn parse_in_parenthesis_block(
232 context: &ParserContext,
233 input: &mut Parser,
234 ) -> Result<Self, ParseError> {
235 let feature_error = match input.try_parse(|input| StyleFeature::parse(context, input)) {
238 Ok(feature) => return Ok(Self::Feature(feature)),
239 Err(e) => e,
240 };
241
242 if let Ok(inner) = Self::parse(context, input, FeatureType::Container) {
243 return Ok(inner);
244 }
245
246 Err(feature_error)
247 }
248
249 fn matches(
250 &self,
251 ctx: &computed::Context,
252 attribute_tracker: &mut AttributeTracker,
253 ) -> KleeneValue {
254 ctx.builder
255 .add_flags(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY);
256 match *self {
257 StyleQuery::Feature(ref f) => f.matches(ctx, attribute_tracker),
258 StyleQuery::Not(ref c) => !c.matches(ctx, attribute_tracker),
259 StyleQuery::InParens(ref c) => c.matches(ctx, attribute_tracker),
260 StyleQuery::Operation(ref conditions, op) => {
261 debug_assert!(!conditions.is_empty(), "We never create an empty op");
262 match op {
263 Operator::And => KleeneValue::any_false(conditions.iter(), |c| {
264 c.matches(ctx, attribute_tracker)
265 }),
266 Operator::Or => {
267 KleeneValue::any(conditions.iter(), |c| c.matches(ctx, attribute_tracker))
268 },
269 }
270 },
271 StyleQuery::GeneralEnclosed(_) => KleeneValue::Unknown,
272 }
273 }
274
275 fn collect_attribute_references(&self, references: &mut AttrReferenceSet) {
276 match self {
277 Self::Feature(c) => c.collect_attribute_references(references),
278 Self::GeneralEnclosed(_) => {},
279 Self::InParens(c) => c.collect_attribute_references(references),
280 Self::Not(c) => c.collect_attribute_references(references),
281 Self::Operation(c, _) => c
282 .iter()
283 .for_each(|c| c.collect_attribute_references(references)),
284 }
285 }
286}
287
288impl OperationParser for StyleQuery {
289 fn parse_in_parens(
290 context: &ParserContext,
291 input: &mut Parser,
292 feature_type: FeatureType,
293 ) -> Result<Self, ParseError> {
294 assert!(feature_type == FeatureType::Container);
295 input.skip_whitespace();
296 let start = input.position();
297 let start_location = input.current_source_location();
298 match *input.next()? {
299 Token::ParenthesisBlock => {
300 if let Some(nested) = try_parse_block(context, input, start, start_location, |i| {
301 Self::parse_in_parenthesis_block(context, i)
302 }) {
303 return Ok(nested);
304 }
305 input.parse_nested_block(|i| {
308 i.expect_ident()?;
309 i.expect_colon()?;
310 consume_any_value(i)
311 })?;
312 Ok(Self::GeneralEnclosed(input.slice_from(start).to_owned()))
313 },
314 _ => Err(ParseError::unexpected_token()),
315 }
316 }
317
318 fn new_not(inner: Box<Self>) -> Self {
319 Self::Not(inner)
320 }
321
322 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
323 Self::Operation(conditions, operator)
324 }
325}
326
327#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
330pub enum StyleFeature {
331 Plain(StyleFeaturePlain),
333 Range(QueryStyleRange),
335}
336
337impl StyleFeature {
338 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
339 if let Ok(range) = input.try_parse(|i| QueryStyleRange::parse(context, i)) {
340 return Ok(Self::Range(range));
341 }
342
343 Ok(Self::Plain(StyleFeaturePlain::parse(context, input)?))
344 }
345
346 fn matches(
347 &self,
348 ctx: &computed::Context,
349 attribute_tracker: &mut AttributeTracker,
350 ) -> KleeneValue {
351 match self {
352 Self::Plain(plain) => plain.matches(ctx, attribute_tracker),
353 Self::Range(range) => range.evaluate(ctx, attribute_tracker),
354 }
355 }
356
357 fn collect_attribute_references(&self, references: &mut AttrReferenceSet) {
358 match self {
359 Self::Plain(plain) => plain.collect_attribute_references(references),
360 Self::Range(range) => range.collect_attribute_references(references),
361 }
362 }
363}
364
365#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
367pub struct StyleFeaturePlain {
368 name: custom_properties::Name,
369 #[ignore_malloc_size_of = "StyleFeatureValue has an Arc variant"]
370 value: StyleFeatureValue,
371}
372
373impl ToCss for StyleFeaturePlain {
374 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
375 where
376 W: fmt::Write,
377 {
378 dest.write_str("--")?;
379 crate::values::serialize_atom_identifier(&self.name, dest)?;
380 match self.value {
381 StyleFeatureValue::Keyword(k) => {
382 dest.write_str(": ")?;
383 k.to_css(dest)?;
384 },
385 StyleFeatureValue::Value(Some(ref v)) => {
386 dest.write_str(": ")?;
387 v.to_css(dest)?;
388 },
389 StyleFeatureValue::Value(None) => (),
390 }
391 Ok(())
392 }
393}
394
395impl StyleFeaturePlain {
396 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
397 let ident = input.expect_ident()?;
398 let name = match custom_properties::parse_name(ident.as_ref()) {
400 Ok(name) => custom_properties::Name::from(name),
401 Err(()) => return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
402 };
403 let value = if input.try_parse(|i| i.expect_colon()).is_ok() {
404 input.skip_whitespace();
405 if let Ok(keyword) = input.try_parse(|i| CSSWideKeyword::parse(i)) {
406 StyleFeatureValue::Keyword(keyword)
407 } else {
408 let value = custom_properties::SpecifiedValue::parse(
409 input,
410 Some(&context.namespaces.prefixes),
411 context.url_data,
412 )?;
413 let _ = input.try_parse(parse_important);
415 StyleFeatureValue::Value(Some(Arc::new(value)))
416 }
417 } else {
418 StyleFeatureValue::Value(None)
419 };
420 Ok(Self { name, value })
421 }
422
423 fn substitute_and_compare(
426 value: &Arc<custom_properties::SpecifiedValue>,
427 registration: &PropertyDescriptors,
428 stylist: &Stylist,
429 ctx: &computed::Context,
430 attribute_tracker: &mut AttributeTracker,
431 current_value: Option<&ComputedRegisteredValue>,
432 ) -> bool {
433 let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(
434 Some(ctx.inherited_custom_properties().clone()),
435 None,
436 );
437 let custom_properties::SubstitutionResult { css, attr_taint } =
438 match custom_properties::substitute(
439 value,
440 None,
441 &substitution_functions,
442 stylist,
443 ctx,
444 attribute_tracker,
445 ) {
446 Ok(sub) => sub,
447 Err(_) => return current_value.is_none(),
448 };
449 if registration.is_universal() {
450 return match current_value {
451 Some(v) => v.as_universal().is_some_and(|v| v.css == css),
452 None => css.is_empty(),
453 };
454 }
455 let mut parser = Parser::new(&css);
456 let computed = SpecifiedRegisteredValue::compute(
457 &mut parser,
458 registration,
459 None,
460 &value.url_data,
461 ctx,
462 AllowComputationallyDependent::Yes,
463 attr_taint,
464 None,
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(
502 v,
503 registration,
504 ctx,
505 None,
506 )
507 .as_ref()
508 == current_value
509 }
510 },
511 StyleFeatureValue::Value(None) => current_value.is_some(),
512 StyleFeatureValue::Keyword(kw) => {
513 match kw {
514 CSSWideKeyword::Unset => current_value.is_none(),
515 CSSWideKeyword::Initial => {
516 if let Some(initial) = ®istration.initial_value {
517 let v = custom_properties::compute_variable_value(
518 initial,
519 registration,
520 ctx,
521 None,
522 );
523 v.as_ref() == current_value
524 } else {
525 current_value.is_none()
526 }
527 },
528 CSSWideKeyword::Inherit => {
529 if let Some(inherited) = ctx
530 .container_info
531 .as_ref()
532 .expect("queries should provide container info")
533 .inherited_style()
534 {
535 inherited.custom_properties().get(registration, &self.name)
536 == current_value
537 } else {
538 false
539 }
540 },
541 CSSWideKeyword::Revert
546 | CSSWideKeyword::RevertLayer
547 | CSSWideKeyword::RevertRule => false,
548 }
549 },
550 })
551 }
552
553 fn collect_attribute_references(&self, references: &mut AttrReferenceSet) {
554 if let StyleFeatureValue::Value(Some(v)) = &self.value {
555 v.collect_attribute_references(references)
556 }
557 }
558}
559
560#[derive(
562 Clone,
563 Debug,
564 MallocSizeOf,
565 PartialEq,
566 Eq,
567 Parse,
568 SpecifiedValueInfo,
569 ToComputedValue,
570 ToCss,
571 ToShmem,
572)]
573#[repr(u8)]
574#[allow(missing_docs)]
575pub enum BoolValue {
576 False,
577 True,
578}
579
580#[derive(
583 Clone,
584 Debug,
585 Eq,
586 MallocSizeOf,
587 Parse,
588 PartialEq,
589 SpecifiedValueInfo,
590 ToComputedValue,
591 ToCss,
592 ToShmem,
593)]
594#[repr(u8)]
595pub enum MozPrefFeatureValue<I> {
596 #[css(skip)]
598 None,
599 Boolean(BoolValue),
601 Integer(I),
603 String(crate::values::AtomString),
605}
606
607type SpecifiedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::specified::Integer>;
608pub type ComputedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::computed::Integer>;
610
611#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
613pub struct MozPrefFeature {
614 name: crate::values::AtomString,
615 value: SpecifiedMozPrefFeatureValue,
616}
617
618impl MozPrefFeature {
619 fn parse(
620 context: &ParserContext,
621 input: &mut Parser,
622 feature_type: FeatureType,
623 ) -> Result<Self, ParseError> {
624 use crate::parser::Parse;
625 if !context.chrome_rules_enabled() || feature_type != FeatureType::Media {
626 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
627 }
628 let name = AtomString::parse(context, input)?;
629 let value = if input.try_parse(|i| i.expect_comma()).is_ok() {
630 SpecifiedMozPrefFeatureValue::parse(context, input)?
631 } else {
632 SpecifiedMozPrefFeatureValue::None
633 };
634 Ok(Self { name, value })
635 }
636
637 #[cfg(feature = "gecko")]
638 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
639 use crate::values::computed::ToComputedValue;
640 let value = self.value.to_computed_value(ctx);
641 KleeneValue::from(unsafe {
642 crate::gecko_bindings::bindings::Gecko_EvalMozPrefFeature(self.name.as_ptr(), &value)
643 })
644 }
645
646 #[cfg(feature = "servo")]
647 fn matches(&self, _: &computed::Context) -> KleeneValue {
648 KleeneValue::Unknown
649 }
650}
651
652impl ToCss for MozPrefFeature {
653 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
654 where
655 W: fmt::Write,
656 {
657 self.name.to_css(dest)?;
658 if !matches!(self.value, MozPrefFeatureValue::None) {
659 dest.write_str(", ")?;
660 self.value.to_css(dest)?;
661 }
662 Ok(())
663 }
664}
665
666#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
668pub enum QueryCondition {
669 Feature(QueryFeatureExpression),
671 Custom(DashedIdent),
673 Not(Box<QueryCondition>),
675 Operation(Box<[QueryCondition]>, Operator),
677 InParens(Box<QueryCondition>),
679 Style(StyleQuery),
681 MozPref(MozPrefFeature),
683 GeneralEnclosed(String, UrlExtraData, FeatureFlags),
685}
686
687impl ToCss for QueryCondition {
688 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
689 where
690 W: fmt::Write,
691 {
692 match *self {
693 Self::Feature(ref f) => f.to_css(dest),
696 Self::Custom(ref name) => {
697 dest.write_char('(')?;
698 name.to_css(dest)?;
699 dest.write_char(')')
700 },
701 Self::Not(ref c) => {
702 dest.write_str("not ")?;
703 c.to_css(dest)
704 },
705 Self::InParens(ref c) => {
706 dest.write_char('(')?;
707 c.to_css(dest)?;
708 dest.write_char(')')
709 },
710 Self::Style(ref c) => {
711 dest.write_str("style(")?;
712 c.to_css(dest)?;
713 dest.write_char(')')
714 },
715 Self::MozPref(ref c) => {
716 dest.write_str("-moz-pref(")?;
717 c.to_css(dest)?;
718 dest.write_char(')')
719 },
720 Self::Operation(ref list, op) => {
721 let mut iter = list.iter();
722 iter.next().unwrap().to_css(dest)?;
723 for item in iter {
724 dest.write_char(' ')?;
725 op.to_css(dest)?;
726 dest.write_char(' ')?;
727 item.to_css(dest)?;
728 }
729 Ok(())
730 },
731 Self::GeneralEnclosed(ref s, ..) => dest.write_str(s),
732 }
733 }
734}
735
736fn consume_any_value(input: &mut Parser) -> Result<(), ParseError> {
738 input.expect_no_error_token().map_err(Into::into)
739}
740
741impl QueryCondition {
742 pub fn parse(
744 context: &ParserContext,
745 input: &mut Parser,
746 feature_type: FeatureType,
747 ) -> Result<Self, ParseError> {
748 Self::parse_internal(context, input, feature_type, AllowOr::Yes)
749 }
750
751 fn visit<F>(&self, visitor: &mut F)
752 where
753 F: FnMut(&Self),
754 {
755 visitor(self);
756 match *self {
757 Self::Custom(..)
758 | Self::Feature(..)
759 | Self::GeneralEnclosed(..)
760 | Self::Style(..)
761 | Self::MozPref(..) => {},
762 Self::Not(ref cond) => cond.visit(visitor),
763 Self::Operation(ref conds, _op) => {
764 for cond in conds.iter() {
765 cond.visit(visitor);
766 }
767 },
768 Self::InParens(ref cond) => cond.visit(visitor),
769 }
770 }
771
772 pub fn cumulative_flags(&self) -> FeatureFlags {
775 let mut result = FeatureFlags::empty();
776 self.visit(&mut |condition| match condition {
777 Self::Style(..) => result.insert(FeatureFlags::STYLE),
778 Self::Feature(f) => result.insert(f.feature_flags()),
779 Self::GeneralEnclosed(_, _, flags) => result.insert(*flags),
780 _ => {},
781 });
782 result
783 }
784
785 pub fn parse_disallow_or(
789 context: &ParserContext,
790 input: &mut Parser,
791 feature_type: FeatureType,
792 ) -> Result<Self, ParseError> {
793 Self::parse_internal(context, input, feature_type, AllowOr::No)
794 }
795
796 fn parse_in_parenthesis_block(
797 context: &ParserContext,
798 input: &mut Parser,
799 feature_type: FeatureType,
800 ) -> Result<Self, ParseError> {
801 let feature_error = match input.try_parse(|input| {
804 QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)
805 }) {
806 Ok(expr) => return Ok(Self::Feature(expr)),
807 Err(e) => e,
808 };
809 if crate::pref!("layout.css.custom-media.enabled")
810 && let Ok(custom) = input.try_parse(|input| DashedIdent::parse(context, input))
811 {
812 return Ok(Self::Custom(custom));
813 }
814 if let Ok(inner) = Self::parse(context, input, feature_type) {
815 return Ok(Self::InParens(Box::new(inner)));
816 }
817 Err(feature_error)
818 }
819
820 pub fn matches(
826 &self,
827 context: &computed::Context,
828 custom: &mut CustomMediaEvaluator,
829 attribute_tracker: &mut AttributeTracker,
830 ) -> KleeneValue {
831 match *self {
832 Self::Custom(ref f) => custom.matches(f, context),
833 Self::Feature(ref f) => f.matches(context),
834 Self::GeneralEnclosed(ref str, ref url_data, _) => {
835 self.matches_general(str, url_data, context, custom, attribute_tracker)
836 },
837 Self::InParens(ref c) => c.matches(context, custom, attribute_tracker),
838 Self::Not(ref c) => !c.matches(context, custom, attribute_tracker),
839 Self::Style(ref c) => c.matches(context, attribute_tracker),
840 Self::MozPref(ref c) => c.matches(context),
841 Self::Operation(ref conditions, op) => {
842 debug_assert!(!conditions.is_empty(), "We never create an empty op");
843 match op {
844 Operator::And => KleeneValue::any_false(conditions.iter(), |c| {
845 c.matches(context, custom, attribute_tracker)
846 }),
847 Operator::Or => KleeneValue::any(conditions.iter(), |c| {
848 c.matches(context, custom, attribute_tracker)
849 }),
850 }
851 },
852 }
853 }
854
855 fn matches_general(
858 &self,
859 css_text: &str,
860 url_data: &UrlExtraData,
861 context: &computed::Context,
862 custom: &mut CustomMediaEvaluator,
863 attribute_tracker: &mut AttributeTracker,
864 ) -> KleeneValue {
865 if !context.in_container_query {
867 return KleeneValue::Unknown;
868 }
869
870 let stylist = context
871 .builder
872 .stylist
873 .expect("container query should provide a Stylist");
874
875 let value = match custom_properties::SpecifiedValue::parse(
877 &mut Parser::new(css_text),
878 None, url_data,
880 ) {
881 Ok(val) => val,
882 Err(_) => return KleeneValue::Unknown,
883 };
884
885 if !value.has_references() {
887 return KleeneValue::Unknown;
888 }
889
890 let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(
892 Some(context.inherited_custom_properties().clone()),
893 None,
894 );
895 let custom_properties::SubstitutionResult { css, attr_taint } =
896 match custom_properties::substitute(
897 &value,
898 None,
899 &substitution_functions,
900 stylist,
901 context,
902 attribute_tracker,
903 ) {
904 Ok(sub) => sub,
905 Err(_) => return KleeneValue::Unknown,
906 };
907
908 let parser_context = ParserContext::new(
910 Origin::Author,
911 url_data,
912 Some(CssRuleType::Container),
913 ParsingMode::DEFAULT,
914 QuirksMode::NoQuirks,
915 Default::default(),
916 None,
917 None,
918 attr_taint,
919 );
920
921 match Self::parse(
922 &parser_context,
923 &mut Parser::new(&css),
924 FeatureType::Container,
925 ) {
926 Ok(Self::GeneralEnclosed(..)) => {
927 KleeneValue::Unknown
929 },
930 Ok(query) => query.matches(context, custom, attribute_tracker),
931 Err(_) => KleeneValue::Unknown,
932 }
933 }
934
935 pub fn collect_attribute_references(&self, references: &mut AttrReferenceSet) {
937 if let QueryCondition::Style(c) = self {
938 c.collect_attribute_references(references)
939 }
940 }
941}
942
943impl OperationParser for QueryCondition {
944 fn parse_in_parens(
948 context: &ParserContext,
949 input: &mut Parser,
950 feature_type: FeatureType,
951 ) -> Result<Self, ParseError> {
952 input.skip_whitespace();
953 let start = input.position();
954 let start_location = input.current_source_location();
955 let mut flags = FeatureFlags::empty();
956 match *input.next()? {
957 Token::ParenthesisBlock => {
958 let nested = try_parse_block(context, input, start, start_location, |input| {
959 Self::parse_in_parenthesis_block(context, input, feature_type)
960 });
961 if let Some(nested) = nested {
962 return Ok(nested);
963 }
964 },
965 Token::Function(ref name) => {
966 match_ignore_ascii_case! { name,
967 "style" if StyleQuery::enabled(feature_type) => {
968 let query = try_parse_block(context, input, start, start_location, |input| {
969 StyleQuery::parse(context, input, feature_type)
970 });
971 if let Some(query) = query {
972 return Ok(Self::Style(query));
973 }
974 flags.insert(FeatureFlags::STYLE);
975 },
976 "-moz-pref" => {
977 let feature = try_parse_block(context, input, start, start_location, |input| {
978 MozPrefFeature::parse(context, input, feature_type)
979 });
980 if let Some(feature) = feature {
981 return Ok(Self::MozPref(feature));
982 }
983 },
984 _ => {},
985 }
986 },
987 _ => return Err(ParseError::unexpected_token()),
988 }
989 input.parse_nested_block(consume_any_value)?;
990 Ok(Self::GeneralEnclosed(
991 input.slice_from(start).to_owned(),
992 context.url_data.clone(),
993 flags,
994 ))
995 }
996
997 fn new_not(inner: Box<Self>) -> Self {
998 Self::Not(inner)
999 }
1000
1001 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
1002 Self::Operation(conditions, operator)
1003 }
1004}