summaryrefslogtreecommitdiffstats
path: root/servo/components/style/media_queries/media_condition.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /servo/components/style/media_queries/media_condition.rs
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/components/style/media_queries/media_condition.rs')
-rw-r--r--servo/components/style/media_queries/media_condition.rs185
1 files changed, 185 insertions, 0 deletions
diff --git a/servo/components/style/media_queries/media_condition.rs b/servo/components/style/media_queries/media_condition.rs
new file mode 100644
index 0000000000..f735704556
--- /dev/null
+++ b/servo/components/style/media_queries/media_condition.rs
@@ -0,0 +1,185 @@
+/* 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 media query condition:
+//!
+//! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition
+
+use super::{Device, MediaFeatureExpression};
+use crate::context::QuirksMode;
+use crate::parser::ParserContext;
+use cssparser::{Parser, Token};
+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,
+}
+
+/// Represents a media condition.
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
+pub enum MediaCondition {
+ /// A simple media feature expression, implicitly parenthesized.
+ Feature(MediaFeatureExpression),
+ /// A negation of a condition.
+ Not(Box<MediaCondition>),
+ /// A set of joint operations.
+ Operation(Box<[MediaCondition]>, Operator),
+ /// A condition wrapped in parenthesis.
+ InParens(Box<MediaCondition>),
+}
+
+impl ToCss for MediaCondition {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ match *self {
+ // NOTE(emilio): MediaFeatureExpression already includes the
+ // parenthesis.
+ MediaCondition::Feature(ref f) => f.to_css(dest),
+ MediaCondition::Not(ref c) => {
+ dest.write_str("not ")?;
+ c.to_css(dest)
+ },
+ MediaCondition::InParens(ref c) => {
+ dest.write_char('(')?;
+ c.to_css(dest)?;
+ dest.write_char(')')
+ },
+ MediaCondition::Operation(ref list, op) => {
+ let mut iter = list.iter();
+ iter.next().unwrap().to_css(dest)?;
+ for item in iter {
+ dest.write_char(' ')?;
+ op.to_css(dest)?;
+ dest.write_char(' ')?;
+ item.to_css(dest)?;
+ }
+ Ok(())
+ },
+ }
+ }
+}
+
+impl MediaCondition {
+ /// Parse a single media condition.
+ pub fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Self::parse_internal(context, input, AllowOr::Yes)
+ }
+
+ /// Parse a single media condition, disallowing `or` expressions.
+ ///
+ /// To be used from the legacy media query syntax.
+ pub fn parse_disallow_or<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Self::parse_internal(context, input, AllowOr::No)
+ }
+
+ fn parse_internal<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ allow_or: AllowOr,
+ ) -> Result<Self, ParseError<'i>> {
+ let location = input.current_source_location();
+
+ // FIXME(emilio): This can be cleaner with nll.
+ let is_negation = match *input.next()? {
+ Token::ParenthesisBlock => false,
+ Token::Ident(ref ident) if ident.eq_ignore_ascii_case("not") => true,
+ ref t => return Err(location.new_unexpected_token_error(t.clone())),
+ };
+
+ if is_negation {
+ let inner_condition = Self::parse_in_parens(context, input)?;
+ return Ok(MediaCondition::Not(Box::new(inner_condition)));
+ }
+
+ // ParenthesisBlock.
+ let first_condition = Self::parse_paren_block(context, input)?;
+ let operator = match input.try_parse(Operator::parse) {
+ Ok(op) => op,
+ Err(..) => return Ok(first_condition),
+ };
+
+ if allow_or == AllowOr::No && operator == Operator::Or {
+ return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+ }
+
+ let mut conditions = vec![];
+ conditions.push(first_condition);
+ conditions.push(Self::parse_in_parens(context, input)?);
+
+ let delim = match operator {
+ Operator::And => "and",
+ Operator::Or => "or",
+ };
+
+ loop {
+ if input.try_parse(|i| i.expect_ident_matching(delim)).is_err() {
+ return Ok(MediaCondition::Operation(
+ conditions.into_boxed_slice(),
+ operator,
+ ));
+ }
+
+ conditions.push(Self::parse_in_parens(context, input)?);
+ }
+ }
+
+ /// Parse a media condition in parentheses.
+ pub fn parse_in_parens<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ input.expect_parenthesis_block()?;
+ Self::parse_paren_block(context, input)
+ }
+
+ fn parse_paren_block<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ input.parse_nested_block(|input| {
+ // Base case.
+ if let Ok(inner) = input.try_parse(|i| Self::parse(context, i)) {
+ return Ok(MediaCondition::InParens(Box::new(inner)));
+ }
+ let expr = MediaFeatureExpression::parse_in_parenthesis_block(context, input)?;
+ Ok(MediaCondition::Feature(expr))
+ })
+ }
+
+ /// Whether this condition matches the device and quirks mode.
+ pub fn matches(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
+ match *self {
+ MediaCondition::Feature(ref f) => f.matches(device, quirks_mode),
+ MediaCondition::InParens(ref c) => c.matches(device, quirks_mode),
+ MediaCondition::Not(ref c) => !c.matches(device, quirks_mode),
+ MediaCondition::Operation(ref conditions, op) => {
+ let mut iter = conditions.iter();
+ match op {
+ Operator::And => iter.all(|c| c.matches(device, quirks_mode)),
+ Operator::Or => iter.any(|c| c.matches(device, quirks_mode)),
+ }
+ },
+ }
+ }
+}