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(&self, ctx: &computed::Context) -> KleeneValue {
249 ctx.builder
250 .add_flags(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY);
251 match *self {
252 StyleQuery::Feature(ref f) => f.matches(ctx),
253 StyleQuery::Not(ref c) => !c.matches(ctx),
254 StyleQuery::InParens(ref c) => c.matches(ctx),
255 StyleQuery::Operation(ref conditions, op) => {
256 debug_assert!(!conditions.is_empty(), "We never create an empty op");
257 match op {
258 Operator::And => KleeneValue::any_false(conditions.iter(), |c| c.matches(ctx)),
259 Operator::Or => KleeneValue::any(conditions.iter(), |c| c.matches(ctx)),
260 }
261 },
262 StyleQuery::GeneralEnclosed(_) => KleeneValue::Unknown,
263 }
264 }
265}
266
267impl OperationParser for StyleQuery {
268 fn parse_in_parens<'i, 't>(
269 context: &ParserContext,
270 input: &mut Parser<'i, 't>,
271 feature_type: FeatureType,
272 ) -> Result<Self, ParseError<'i>> {
273 assert!(feature_type == FeatureType::Container);
274 input.skip_whitespace();
275 let start = input.position();
276 let start_location = input.current_source_location();
277 match *input.next()? {
278 Token::ParenthesisBlock => {
279 if let Some(nested) = Self::try_parse_block(context, input, start, |i| {
280 Self::parse_in_parenthesis_block(context, i)
281 }) {
282 return Ok(nested);
283 }
284 input.parse_nested_block(|i| {
287 i.expect_ident()?;
288 i.expect_colon()?;
289 consume_any_value(i)
290 })?;
291 Ok(Self::GeneralEnclosed(input.slice_from(start).to_owned()))
292 },
293 ref t => return Err(start_location.new_unexpected_token_error(t.clone())),
294 }
295 }
296
297 fn new_not(inner: Box<Self>) -> Self {
298 Self::Not(inner)
299 }
300
301 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
302 Self::Operation(conditions, operator)
303 }
304}
305
306#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
309pub enum StyleFeature {
310 Plain(StyleFeaturePlain),
312 Range(QueryStyleRange),
314}
315
316impl StyleFeature {
317 fn parse<'i, 't>(
318 context: &ParserContext,
319 input: &mut Parser<'i, 't>,
320 ) -> Result<Self, ParseError<'i>> {
321 if let Ok(range) = input.try_parse(|i| QueryStyleRange::parse(context, i)) {
322 return Ok(Self::Range(range));
323 }
324
325 Ok(Self::Plain(StyleFeaturePlain::parse(context, input)?))
326 }
327
328 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
329 match self {
330 Self::Plain(plain) => plain.matches(ctx),
331 Self::Range(range) => range.evaluate(ctx),
332 }
333 }
334}
335
336#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
338pub struct StyleFeaturePlain {
339 name: custom_properties::Name,
340 #[ignore_malloc_size_of = "StyleFeatureValue has an Arc variant"]
341 value: StyleFeatureValue,
342}
343
344impl ToCss for StyleFeaturePlain {
345 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
346 where
347 W: fmt::Write,
348 {
349 dest.write_str("--")?;
350 crate::values::serialize_atom_identifier(&self.name, dest)?;
351 match self.value {
352 StyleFeatureValue::Keyword(k) => {
353 dest.write_str(": ")?;
354 k.to_css(dest)?;
355 },
356 StyleFeatureValue::Value(Some(ref v)) => {
357 dest.write_str(": ")?;
358 v.to_css(dest)?;
359 },
360 StyleFeatureValue::Value(None) => (),
361 }
362 Ok(())
363 }
364}
365
366impl StyleFeaturePlain {
367 fn parse<'i, 't>(
368 context: &ParserContext,
369 input: &mut Parser<'i, 't>,
370 ) -> Result<Self, ParseError<'i>> {
371 let ident = input.expect_ident()?;
372 let name = match custom_properties::parse_name(ident.as_ref()) {
374 Ok(name) => custom_properties::Name::from(name),
375 Err(()) => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
376 };
377 let value = if input.try_parse(|i| i.expect_colon()).is_ok() {
378 input.skip_whitespace();
379 if let Ok(keyword) = input.try_parse(|i| CSSWideKeyword::parse(i)) {
380 StyleFeatureValue::Keyword(keyword)
381 } else {
382 let value = custom_properties::SpecifiedValue::parse(
383 input,
384 Some(&context.namespaces.prefixes),
385 &context.url_data,
386 )?;
387 let _ = input.try_parse(parse_important);
389 StyleFeatureValue::Value(Some(Arc::new(value)))
390 }
391 } else {
392 StyleFeatureValue::Value(None)
393 };
394 Ok(Self { name, value })
395 }
396
397 fn substitute_and_compare(
400 value: &Arc<custom_properties::SpecifiedValue>,
401 registration: &PropertyDescriptors,
402 stylist: &Stylist,
403 ctx: &computed::Context,
404 current_value: Option<&ComputedRegisteredValue>,
405 ) -> bool {
406 let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(
407 Some(ctx.inherited_custom_properties().clone()),
408 None,
409 );
410 let custom_properties::SubstitutionResult { css, .. } = match custom_properties::substitute(
412 &value,
413 &substitution_functions,
414 stylist,
415 ctx,
416 &mut AttributeTracker::new_dummy(),
418 ) {
419 Ok(sub) => sub,
420 Err(_) => return current_value.is_none(),
421 };
422 if registration.is_universal() {
423 return match current_value {
424 Some(v) => v.as_universal().is_some_and(|v| v.css == css),
425 None => css.is_empty(),
426 };
427 }
428 let mut input = cssparser::ParserInput::new(&css);
429 let mut parser = Parser::new(&mut input);
430 let computed = SpecifiedRegisteredValue::compute(
431 &mut parser,
432 registration,
433 None,
434 &value.url_data,
435 ctx,
436 AllowComputationallyDependent::Yes,
437 )
438 .ok();
439 computed.as_ref() == current_value
440 }
441
442 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
443 let stylist = ctx
445 .builder
446 .stylist
447 .expect("container queries should have a stylist around");
448 let registration = stylist.get_custom_property_registration(&self.name);
449 let current_value = ctx
450 .inherited_custom_properties()
451 .get(registration, &self.name);
452 KleeneValue::from(match self.value {
453 StyleFeatureValue::Value(Some(ref v)) => {
454 if ctx.container_info.is_none() {
455 false
457 } else if v.has_references() {
458 Self::substitute_and_compare(v, registration, stylist, ctx, current_value)
461 } else {
462 custom_properties::compute_variable_value(&v, registration, ctx).as_ref()
463 == current_value
464 }
465 },
466 StyleFeatureValue::Value(None) => current_value.is_some(),
467 StyleFeatureValue::Keyword(kw) => {
468 match kw {
469 CSSWideKeyword::Unset => current_value.is_none(),
470 CSSWideKeyword::Initial => {
471 if let Some(initial) = ®istration.initial_value {
472 let v = custom_properties::compute_variable_value(
473 &initial,
474 registration,
475 ctx,
476 );
477 v.as_ref() == current_value
478 } else {
479 current_value.is_none()
480 }
481 },
482 CSSWideKeyword::Inherit => {
483 if let Some(inherited) = ctx
484 .container_info
485 .as_ref()
486 .expect("queries should provide container info")
487 .inherited_style()
488 {
489 inherited.custom_properties().get(registration, &self.name)
490 == current_value
491 } else {
492 false
493 }
494 },
495 CSSWideKeyword::Revert
500 | CSSWideKeyword::RevertLayer
501 | CSSWideKeyword::RevertRule => false,
502 }
503 },
504 })
505 }
506}
507
508#[derive(
510 Clone,
511 Debug,
512 MallocSizeOf,
513 PartialEq,
514 Eq,
515 Parse,
516 SpecifiedValueInfo,
517 ToComputedValue,
518 ToCss,
519 ToShmem,
520)]
521#[repr(u8)]
522#[allow(missing_docs)]
523pub enum BoolValue {
524 False,
525 True,
526}
527
528#[derive(
531 Clone,
532 Debug,
533 Eq,
534 MallocSizeOf,
535 Parse,
536 PartialEq,
537 SpecifiedValueInfo,
538 ToComputedValue,
539 ToCss,
540 ToShmem,
541)]
542#[repr(u8)]
543pub enum MozPrefFeatureValue<I> {
544 #[css(skip)]
546 None,
547 Boolean(BoolValue),
549 Integer(I),
551 String(crate::values::AtomString),
553}
554
555type SpecifiedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::specified::Integer>;
556pub type ComputedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::computed::Integer>;
558
559#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
561pub struct MozPrefFeature {
562 name: crate::values::AtomString,
563 value: SpecifiedMozPrefFeatureValue,
564}
565
566impl MozPrefFeature {
567 fn parse<'i, 't>(
568 context: &ParserContext,
569 input: &mut Parser<'i, 't>,
570 feature_type: FeatureType,
571 ) -> Result<Self, ParseError<'i>> {
572 use crate::parser::Parse;
573 if !context.chrome_rules_enabled() || feature_type != FeatureType::Media {
574 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
575 }
576 let name = AtomString::parse(context, input)?;
577 let value = if input.try_parse(|i| i.expect_comma()).is_ok() {
578 SpecifiedMozPrefFeatureValue::parse(context, input)?
579 } else {
580 SpecifiedMozPrefFeatureValue::None
581 };
582 Ok(Self { name, value })
583 }
584
585 #[cfg(feature = "gecko")]
586 fn matches(&self, ctx: &computed::Context) -> KleeneValue {
587 use crate::values::computed::ToComputedValue;
588 let value = self.value.to_computed_value(ctx);
589 KleeneValue::from(unsafe {
590 crate::gecko_bindings::bindings::Gecko_EvalMozPrefFeature(self.name.as_ptr(), &value)
591 })
592 }
593
594 #[cfg(feature = "servo")]
595 fn matches(&self, _: &computed::Context) -> KleeneValue {
596 KleeneValue::Unknown
597 }
598}
599
600impl ToCss for MozPrefFeature {
601 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
602 where
603 W: fmt::Write,
604 {
605 self.name.to_css(dest)?;
606 if !matches!(self.value, MozPrefFeatureValue::None) {
607 dest.write_str(", ")?;
608 self.value.to_css(dest)?;
609 }
610 Ok(())
611 }
612}
613
614#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
616pub enum QueryCondition {
617 Feature(QueryFeatureExpression),
619 Custom(DashedIdent),
621 Not(Box<QueryCondition>),
623 Operation(Box<[QueryCondition]>, Operator),
625 InParens(Box<QueryCondition>),
627 Style(StyleQuery),
629 MozPref(MozPrefFeature),
631 GeneralEnclosed(String, UrlExtraData),
633}
634
635impl ToCss for QueryCondition {
636 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
637 where
638 W: fmt::Write,
639 {
640 match *self {
641 QueryCondition::Feature(ref f) => f.to_css(dest),
644 QueryCondition::Custom(ref name) => {
645 dest.write_char('(')?;
646 name.to_css(dest)?;
647 dest.write_char(')')
648 },
649 QueryCondition::Not(ref c) => {
650 dest.write_str("not ")?;
651 c.to_css(dest)
652 },
653 QueryCondition::InParens(ref c) => {
654 dest.write_char('(')?;
655 c.to_css(dest)?;
656 dest.write_char(')')
657 },
658 QueryCondition::Style(ref c) => {
659 dest.write_str("style(")?;
660 c.to_css(dest)?;
661 dest.write_char(')')
662 },
663 QueryCondition::MozPref(ref c) => {
664 dest.write_str("-moz-pref(")?;
665 c.to_css(dest)?;
666 dest.write_char(')')
667 },
668 QueryCondition::Operation(ref list, op) => {
669 let mut iter = list.iter();
670 iter.next().unwrap().to_css(dest)?;
671 for item in iter {
672 dest.write_char(' ')?;
673 op.to_css(dest)?;
674 dest.write_char(' ')?;
675 item.to_css(dest)?;
676 }
677 Ok(())
678 },
679 QueryCondition::GeneralEnclosed(ref s, _) => dest.write_str(&s),
680 }
681 }
682}
683
684fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
686 input.expect_no_error_token().map_err(Into::into)
687}
688
689impl QueryCondition {
690 pub fn parse<'i, 't>(
692 context: &ParserContext,
693 input: &mut Parser<'i, 't>,
694 feature_type: FeatureType,
695 ) -> Result<Self, ParseError<'i>> {
696 Self::parse_internal(context, input, feature_type, AllowOr::Yes)
697 }
698
699 fn visit<F>(&self, visitor: &mut F)
700 where
701 F: FnMut(&Self),
702 {
703 visitor(self);
704 match *self {
705 Self::Custom(..)
706 | Self::Feature(..)
707 | Self::GeneralEnclosed(..)
708 | Self::Style(..)
709 | Self::MozPref(..) => {},
710 Self::Not(ref cond) => cond.visit(visitor),
711 Self::Operation(ref conds, _op) => {
712 for cond in conds.iter() {
713 cond.visit(visitor);
714 }
715 },
716 Self::InParens(ref cond) => cond.visit(visitor),
717 }
718 }
719
720 pub fn cumulative_flags(&self) -> FeatureFlags {
723 let mut result = FeatureFlags::empty();
724 self.visit(&mut |condition| {
725 if let Self::Style(..) = condition {
726 result.insert(FeatureFlags::STYLE);
727 }
728 if let Self::Feature(ref f) = condition {
729 result.insert(f.feature_flags())
730 }
731 });
732 result
733 }
734
735 pub fn parse_disallow_or<'i, 't>(
739 context: &ParserContext,
740 input: &mut Parser<'i, 't>,
741 feature_type: FeatureType,
742 ) -> Result<Self, ParseError<'i>> {
743 Self::parse_internal(context, input, feature_type, AllowOr::No)
744 }
745
746 fn parse_in_parenthesis_block<'i>(
747 context: &ParserContext,
748 input: &mut Parser<'i, '_>,
749 feature_type: FeatureType,
750 ) -> Result<Self, ParseError<'i>> {
751 let feature_error = match input.try_parse(|input| {
754 QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)
755 }) {
756 Ok(expr) => return Ok(Self::Feature(expr)),
757 Err(e) => e,
758 };
759 if static_prefs::pref!("layout.css.custom-media.enabled") {
760 if let Ok(custom) = input.try_parse(|input| DashedIdent::parse(context, input)) {
761 return Ok(Self::Custom(custom));
762 }
763 }
764 if let Ok(inner) = Self::parse(context, input, feature_type) {
765 return Ok(Self::InParens(Box::new(inner)));
766 }
767 Err(feature_error)
768 }
769
770 fn try_parse_block<'i, T, F>(
771 context: &ParserContext,
772 input: &mut Parser<'i, '_>,
773 start: SourcePosition,
774 parse: F,
775 ) -> Option<T>
776 where
777 F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,
778 {
779 let nested = input.try_parse(|input| input.parse_nested_block(parse));
780 match nested {
781 Ok(nested) => Some(nested),
782 Err(e) => {
783 let loc = e.location;
786 let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
787 context.log_css_error(loc, error);
788 None
789 },
790 }
791 }
792
793 pub fn matches(
799 &self,
800 context: &computed::Context,
801 custom: &mut CustomMediaEvaluator,
802 ) -> KleeneValue {
803 match *self {
804 Self::Custom(ref f) => custom.matches(f, context),
805 Self::Feature(ref f) => f.matches(context),
806 Self::GeneralEnclosed(ref str, ref url_data) => {
807 self.matches_general(&str, url_data, context, custom)
808 },
809 Self::InParens(ref c) => c.matches(context, custom),
810 Self::Not(ref c) => !c.matches(context, custom),
811 Self::Style(ref c) => c.matches(context),
812 Self::MozPref(ref c) => c.matches(context),
813 Self::Operation(ref conditions, op) => {
814 debug_assert!(!conditions.is_empty(), "We never create an empty op");
815 match op {
816 Operator::And => {
817 KleeneValue::any_false(conditions.iter(), |c| c.matches(context, custom))
818 },
819 Operator::Or => {
820 KleeneValue::any(conditions.iter(), |c| c.matches(context, custom))
821 },
822 }
823 },
824 }
825 }
826
827 fn matches_general(
830 &self,
831 css_text: &str,
832 url_data: &UrlExtraData,
833 context: &computed::Context,
834 custom: &mut CustomMediaEvaluator,
835 ) -> KleeneValue {
836 if !context.in_container_query {
838 return KleeneValue::Unknown;
839 }
840
841 let stylist = context
842 .builder
843 .stylist
844 .expect("container query should provide a Stylist");
845
846 let mut input = ParserInput::new(css_text);
848 let value = match custom_properties::SpecifiedValue::parse(
849 &mut Parser::new(&mut input),
850 None, url_data,
852 ) {
853 Ok(val) => val,
854 Err(_) => return KleeneValue::Unknown,
855 };
856
857 if !value.has_references() {
859 return KleeneValue::Unknown;
860 }
861
862 let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(
864 Some(context.inherited_custom_properties().clone()),
865 None,
866 );
867 let custom_properties::SubstitutionResult {
868 css,
869 attribute_tainted,
870 } = match custom_properties::substitute(
871 &value,
872 &substitution_functions,
873 stylist,
874 context,
875 &mut AttributeTracker::new_dummy(),
877 ) {
878 Ok(sub) => sub,
879 Err(_) => return KleeneValue::Unknown,
880 };
881
882 let mut parsing_mode = ParsingMode::DEFAULT;
884 if attribute_tainted {
885 parsing_mode.insert(ParsingMode::DISALLOW_URLS);
886 }
887 let parser_context = ParserContext::new(
888 Origin::Author,
889 url_data,
890 Some(CssRuleType::Container),
891 parsing_mode,
892 QuirksMode::NoQuirks,
893 Default::default(),
894 None,
895 None,
896 );
897 let mut input = ParserInput::new(&css);
898 let result = match Self::parse(
899 &parser_context,
900 &mut Parser::new(&mut input),
901 FeatureType::Container,
902 ) {
903 Ok(Self::GeneralEnclosed(..)) => {
904 KleeneValue::Unknown
906 },
907 Ok(query) => query.matches(context, custom),
908 Err(_) => KleeneValue::Unknown,
909 };
910
911 result
912 }
913}
914
915impl OperationParser for QueryCondition {
916 fn parse_in_parens<'i, 't>(
920 context: &ParserContext,
921 input: &mut Parser<'i, 't>,
922 feature_type: FeatureType,
923 ) -> Result<Self, ParseError<'i>> {
924 input.skip_whitespace();
925 let start = input.position();
926 let start_location = input.current_source_location();
927 match *input.next()? {
928 Token::ParenthesisBlock => {
929 let nested = Self::try_parse_block(context, input, start, |input| {
930 Self::parse_in_parenthesis_block(context, input, feature_type)
931 });
932 if let Some(nested) = nested {
933 return Ok(nested);
934 }
935 },
936 Token::Function(ref name) => {
937 match_ignore_ascii_case! { name,
938 "style" => {
939 let query = Self::try_parse_block(context, input, start, |input| {
940 StyleQuery::parse(context, input, feature_type)
941 });
942 if let Some(query) = query {
943 return Ok(Self::Style(query));
944 }
945 },
946 "-moz-pref" => {
947 let feature = Self::try_parse_block(context, input, start, |input| {
948 MozPrefFeature::parse(context, input, feature_type)
949 });
950 if let Some(feature) = feature {
951 return Ok(Self::MozPref(feature));
952 }
953 },
954 _ => {},
955 }
956 },
957 ref t => return Err(start_location.new_unexpected_token_error(t.clone())),
958 }
959 input.parse_nested_block(consume_any_value)?;
960 Ok(Self::GeneralEnclosed(
961 input.slice_from(start).to_owned(),
962 context.url_data.clone(),
963 ))
964 }
965
966 fn new_not(inner: Box<Self>) -> Self {
967 Self::Not(inner)
968 }
969
970 fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {
971 Self::Operation(conditions, operator)
972 }
973}