/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! A query condition: //! //! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition //! https://drafts.csswg.org/css-contain-3/#typedef-container-condition use super::{FeatureFlags, FeatureType, QueryFeatureExpression}; use crate::custom_properties; use crate::values::{computed, AtomString}; use crate::{error_reporting::ContextualParseError, parser::ParserContext}; use cssparser::{Parser, SourcePosition, Token}; use selectors::kleene_value::KleeneValue; use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; /// A binary `and` or `or` operator. #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] #[allow(missing_docs)] pub enum Operator { And, Or, } /// Whether to allow an `or` condition or not during parsing. #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)] enum AllowOr { Yes, No, } /// A style query feature: /// https://drafts.csswg.org/css-conditional-5/#typedef-style-feature #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pub struct StyleFeature { name: custom_properties::Name, // TODO: This is a "primary" reference, probably should be unconditionally measured. #[ignore_malloc_size_of = "Arc"] value: Option>, } impl ToCss for StyleFeature { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write, { dest.write_str("--")?; crate::values::serialize_atom_identifier(&self.name, dest)?; if let Some(ref v) = self.value { dest.write_str(": ")?; v.to_css(dest)?; } Ok(()) } } impl StyleFeature { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, feature_type: FeatureType, ) -> Result> { if !static_prefs::pref!("layout.css.style-queries.enabled") || feature_type != FeatureType::Container { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } // TODO: Allow parsing nested style feature queries. let ident = input.expect_ident()?; // TODO(emilio): Maybe support non-custom properties? let name = match custom_properties::parse_name(ident.as_ref()) { Ok(name) => custom_properties::Name::from(name), Err(()) => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), }; let value = if input.try_parse(|i| i.expect_colon()).is_ok() { input.skip_whitespace(); Some(Arc::new(custom_properties::SpecifiedValue::parse( input, &context.url_data, )?)) } else { None }; Ok(Self { name, value }) } fn matches(&self, ctx: &computed::Context) -> KleeneValue { // FIXME(emilio): Confirm this is the right style to query. let registration = ctx .builder .stylist .expect("container queries should have a stylist around") .get_custom_property_registration(&self.name); let current_value = ctx .inherited_custom_properties() .get(registration, &self.name); KleeneValue::from(match self.value { Some(ref v) => current_value.is_some_and(|cur| { custom_properties::compute_variable_value(v, registration, ctx) .is_some_and(|v| v == *cur) }), None => current_value.is_some(), }) } } /// A boolean value for a pref query. #[derive( Clone, Debug, MallocSizeOf, PartialEq, Eq, Parse, SpecifiedValueInfo, ToComputedValue, ToCss, ToShmem, )] #[repr(u8)] #[allow(missing_docs)] pub enum BoolValue { False, True, } /// Simple values we support for -moz-pref(). We don't want to deal with calc() and other /// shenanigans for now. #[derive( Clone, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToShmem, )] #[repr(u8)] pub enum MozPrefFeatureValue { /// No pref value, implicitly bool, but also used to represent missing prefs. #[css(skip)] None, /// A bool value. Boolean(BoolValue), /// An integer value, useful for int prefs. Integer(I), /// A string pref value. String(crate::values::AtomString), } type SpecifiedMozPrefFeatureValue = MozPrefFeatureValue; /// The computed -moz-pref() value. pub type ComputedMozPrefFeatureValue = MozPrefFeatureValue; /// A custom -moz-pref(, ) query feature. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pub struct MozPrefFeature { name: crate::values::AtomString, value: SpecifiedMozPrefFeatureValue, } impl MozPrefFeature { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, feature_type: FeatureType, ) -> Result> { use crate::parser::Parse; if !context.chrome_rules_enabled() || feature_type != FeatureType::Media { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } let name = AtomString::parse(context, input)?; let value = if input.try_parse(|i| i.expect_comma()).is_ok() { SpecifiedMozPrefFeatureValue::parse(context, input)? } else { SpecifiedMozPrefFeatureValue::None }; Ok(Self { name, value }) } #[cfg(feature = "gecko")] fn matches(&self, ctx: &computed::Context) -> KleeneValue { use crate::values::computed::ToComputedValue; let value = self.value.to_computed_value(ctx); KleeneValue::from(unsafe { crate::gecko_bindings::bindings::Gecko_EvalMozPrefFeature(self.name.as_ptr(), &value) }) } #[cfg(feature = "servo")] fn matches(&self, _: &computed::Context) -> KleeneValue { KleeneValue::Unknown } } impl ToCss for MozPrefFeature { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write, { self.name.to_css(dest)?; if !matches!(self.value, MozPrefFeatureValue::None) { dest.write_str(", ")?; self.value.to_css(dest)?; } Ok(()) } } /// Represents a condition. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pub enum QueryCondition { /// A simple feature expression, implicitly parenthesized. Feature(QueryFeatureExpression), /// A negation of a condition. Not(Box), /// A set of joint operations. Operation(Box<[QueryCondition]>, Operator), /// A condition wrapped in parenthesis. InParens(Box), /// A