From c7b36fb43fecaebd46cd9483da556a9170183547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 14 Jun 2018 13:22:07 -0700 Subject: [PATCH] style: Move MediaList to its own module. And move the parsing from a free function to MediaList::parse. Bug: 1468846 Reviewed-by: xidorn MozReview-Commit-ID: 75ES6I2EEOE --- components/style/media_queries/media_list.rs | 143 +++++++++++++++++++ components/style/media_queries/mod.rs | 142 +----------------- components/style/stylesheets/rule_parser.rs | 16 ++- 3 files changed, 159 insertions(+), 142 deletions(-) create mode 100644 components/style/media_queries/media_list.rs diff --git a/components/style/media_queries/media_list.rs b/components/style/media_queries/media_list.rs new file mode 100644 index 00000000000..89926815d6c --- /dev/null +++ b/components/style/media_queries/media_list.rs @@ -0,0 +1,143 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! A media query list: +//! +//! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list + +use cssparser::{Delimiter, Parser}; +use cssparser::{ParserInput, Token}; +use context::QuirksMode; +use error_reporting::{ContextualParseError, ParseErrorReporter}; +use parser::{ParserContext, ParserErrorContext}; +use super::{Device, MediaQuery, Qualifier}; + +/// A type that encapsulates a media query list. +#[css(comma)] +#[derive(Clone, Debug, MallocSizeOf, ToCss)] +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, + error_reporter: &R, + ) -> MediaList + where + R: ParseErrorReporter, + { + 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); + let error_context = ParserErrorContext { error_reporter }; + context.log_css_error(&error_context, 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 (OR condition) + // https://drafts.csswg.org/mediaqueries-4/#mq-list + self.media_queries.is_empty() || self.media_queries.iter().any(|mq| { + let media_match = mq.media_type.matches(device.media_type()); + + // Check if all conditions match (AND condition) + let query_match = media_match && + mq.expressions + .iter() + .all(|expression| expression.matches(&device, quirks_mode)); + + // Apply the logical NOT qualifier to the result + match mq.qualifier { + Some(Qualifier::Not) => !query_match, + _ => query_match, + } + }) + } + + /// Whether this `MediaList` contains no media queries. + pub fn is_empty(&self) -> bool { + self.media_queries.is_empty() + } + + /// 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() + } +} diff --git a/components/style/media_queries/mod.rs b/components/style/media_queries/mod.rs index 11cf6c85dc7..e1db6ca40cd 100644 --- a/components/style/media_queries/mod.rs +++ b/components/style/media_queries/mod.rs @@ -7,40 +7,22 @@ //! [mq]: https://drafts.csswg.org/mediaqueries/ use Atom; -use context::QuirksMode; -use cssparser::{Delimiter, Parser}; -use cssparser::{ParserInput, Token}; -use error_reporting::{ContextualParseError, ParseErrorReporter}; -use parser::{ParserContext, ParserErrorContext}; +use cssparser::Parser; +use parser::ParserContext; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; use str::string_as_ascii_lowercase; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use values::CustomIdent; +mod media_list; + +pub use self::media_list::MediaList; #[cfg(feature = "servo")] pub use servo::media_queries::{Device, Expression}; #[cfg(feature = "gecko")] pub use gecko::media_queries::{Device, Expression}; -/// A type that encapsulates a media query list. -#[css(comma)] -#[derive(Clone, Debug, MallocSizeOf, ToCss)] -pub struct MediaList { - /// The list of media queries. - #[css(iterable)] - pub media_queries: Vec, -} - -impl MediaList { - /// Create an empty MediaList. - pub fn empty() -> Self { - MediaList { - media_queries: vec![], - } - } -} - /// #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)] pub enum Qualifier { @@ -233,117 +215,3 @@ impl MediaQuery { } } } - -/// 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_media_query_list( - context: &ParserContext, - input: &mut Parser, - error_reporter: &R, -) -> MediaList -where - R: ParseErrorReporter, -{ - if input.is_exhausted() { - return MediaList::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); - let error_context = ParserErrorContext { error_reporter }; - context.log_css_error(&error_context, location, error); - }, - } - - match input.next() { - Ok(&Token::Comma) => {}, - Ok(_) => unreachable!(), - Err(_) => break, - } - } - - MediaList { - media_queries: media_queries, - } -} - -impl MediaList { - /// 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 (OR condition) - // https://drafts.csswg.org/mediaqueries-4/#mq-list - self.media_queries.is_empty() || self.media_queries.iter().any(|mq| { - let media_match = mq.media_type.matches(device.media_type()); - - // Check if all conditions match (AND condition) - let query_match = media_match && - mq.expressions - .iter() - .all(|expression| expression.matches(&device, quirks_mode)); - - // Apply the logical NOT qualifier to the result - match mq.qualifier { - Some(Qualifier::Not) => !query_match, - _ => query_match, - } - }) - } - - /// Whether this `MediaList` contains no media queries. - pub fn is_empty(&self) -> bool { - self.media_queries.is_empty() - } - - /// 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() - } -} diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index a446068faac..259130dd89b 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -10,7 +10,7 @@ use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListP use cssparser::{BasicParseError, BasicParseErrorKind, CowRcStr, SourceLocation}; use error_reporting::{ContextualParseError, ParseErrorReporter}; use font_face::parse_font_face_block; -use media_queries::{parse_media_query_list, MediaList}; +use media_queries::MediaList; use parser::{Parse, ParserContext, ParserErrorContext}; use properties::parse_property_declaration_list; use selector_parser::{SelectorImpl, SelectorParser}; @@ -197,8 +197,11 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, let url_string = input.expect_url_or_string()?.as_ref().to_owned(); let url = CssUrl::parse_from_string(url_string, &self.context); - let media = parse_media_query_list(&self.context, input, - self.error_context.error_reporter); + let media = MediaList::parse( + &self.context, + input, + self.error_context.error_reporter, + ); let media = Arc::new(self.shared_lock.wrap(media)); let prelude = AtRuleNonBlockPrelude::Import(url, media, location); @@ -380,8 +383,11 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a match_ignore_ascii_case! { &*name, "media" => { - let media_queries = parse_media_query_list(self.context, input, - self.error_context.error_reporter); + let media_queries = MediaList::parse( + self.context, + input, + self.error_context.error_reporter, + ); let arc = Arc::new(self.shared_lock.wrap(media_queries)); Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location))) },