From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- servo/components/style/media_queries/media_list.rs | 150 +++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 servo/components/style/media_queries/media_list.rs (limited to 'servo/components/style/media_queries/media_list.rs') diff --git a/servo/components/style/media_queries/media_list.rs b/servo/components/style/media_queries/media_list.rs new file mode 100644 index 0000000000..3c2ba9ee5c --- /dev/null +++ b/servo/components/style/media_queries/media_list.rs @@ -0,0 +1,150 @@ +/* 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 list: +//! +//! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list + +use super::{Device, MediaQuery, Qualifier}; +use crate::context::QuirksMode; +use crate::error_reporting::ContextualParseError; +use crate::parser::ParserContext; +use crate::queries::condition::KleeneValue; +use crate::values::computed; +use cssparser::{Delimiter, Parser}; +use cssparser::{ParserInput, Token}; + +/// A type that encapsulates a media query list. +#[derive(Clone, MallocSizeOf, ToCss, ToShmem)] +#[css(comma, derive_debug)] +pub struct MediaList { + /// The list of media queries. + #[css(iterable)] + pub media_queries: Vec, +} + +impl MediaList { + /// Parse a media query list from CSS. + /// + /// Always returns a media query list. If any invalid media query is + /// found, the media query list is only filled with the equivalent of + /// "not all", see: + /// + /// + pub fn parse(context: &ParserContext, input: &mut Parser) -> Self { + if input.is_exhausted() { + return Self::empty(); + } + + let mut media_queries = vec![]; + loop { + let start_position = input.position(); + match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) { + Ok(mq) => { + media_queries.push(mq); + }, + Err(err) => { + media_queries.push(MediaQuery::never_matching()); + let location = err.location; + let error = ContextualParseError::InvalidMediaRule( + input.slice_from(start_position), + err, + ); + context.log_css_error(location, error); + }, + } + + match input.next() { + Ok(&Token::Comma) => {}, + Ok(_) => unreachable!(), + Err(_) => break, + } + } + + MediaList { media_queries } + } + + /// Create an empty MediaList. + pub fn empty() -> Self { + MediaList { + media_queries: vec![], + } + } + + /// Evaluate a whole `MediaList` against `Device`. + pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> bool { + // Check if it is an empty media query list or any queries match. + // https://drafts.csswg.org/mediaqueries-4/#mq-list + if self.media_queries.is_empty() { + return true; + } + + computed::Context::for_media_query_evaluation(device, quirks_mode, |context| { + self.media_queries.iter().any(|mq| { + let mut query_match = if mq.media_type.matches(device.media_type()) { + mq.condition + .as_ref() + .map_or(KleeneValue::True, |c| c.matches(context)) + } else { + KleeneValue::False + }; + + // Apply the logical NOT qualifier to the result + if matches!(mq.qualifier, Some(Qualifier::Not)) { + query_match = !query_match; + } + query_match.to_bool(/* unknown = */ false) + }) + }) + } + + /// Whether this `MediaList` contains no media queries. + pub fn is_empty(&self) -> bool { + self.media_queries.is_empty() + } + + /// Whether this `MediaList` depends on the viewport size. + pub fn is_viewport_dependent(&self) -> bool { + self.media_queries.iter().any(|q| q.is_viewport_dependent()) + } + + /// Append a new media query item to the media list. + /// + /// + /// Returns true if added, false if fail to parse the medium string. + pub fn append_medium(&mut self, context: &ParserContext, new_medium: &str) -> bool { + let mut input = ParserInput::new(new_medium); + let mut parser = Parser::new(&mut input); + let new_query = match MediaQuery::parse(&context, &mut parser) { + Ok(query) => query, + Err(_) => { + return false; + }, + }; + // This algorithm doesn't actually matches the current spec, + // but it matches the behavior of Gecko and Edge. + // See https://github.com/w3c/csswg-drafts/issues/697 + self.media_queries.retain(|query| query != &new_query); + self.media_queries.push(new_query); + true + } + + /// Delete a media query from the media list. + /// + /// + /// Returns true if found and deleted, false otherwise. + pub fn delete_medium(&mut self, context: &ParserContext, old_medium: &str) -> bool { + let mut input = ParserInput::new(old_medium); + let mut parser = Parser::new(&mut input); + let old_query = match MediaQuery::parse(context, &mut parser) { + Ok(query) => query, + Err(_) => { + return false; + }, + }; + let old_len = self.media_queries.len(); + self.media_queries.retain(|query| query != &old_query); + old_len != self.media_queries.len() + } +} -- cgit v1.2.3