Bug 1468846: Move MediaList to its own module. r?xidorn
And move the parsing from a free function to MediaList::parse.
MozReview-Commit-ID: 75ES6I2EEOE
new file mode 100644
--- /dev/null
+++ b/servo/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<MediaQuery>,
+}
+
+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:
+ ///
+ /// <https://drafts.csswg.org/mediaqueries/#error-handling>
+ pub fn parse<R>(
+ 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.
+ /// <https://drafts.csswg.org/cssom/#dom-medialist-appendmedium>
+ ///
+ /// 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.
+ /// <https://drafts.csswg.org/cssom/#dom-medialist-deletemedium>
+ ///
+ /// 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()
+ }
+}
--- a/servo/components/style/media_queries/mod.rs
+++ b/servo/components/style/media_queries/mod.rs
@@ -2,50 +2,32 @@
* 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/. */
//! [Media queries][mq].
//!
//! [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<MediaQuery>,
-}
-
-impl MediaList {
- /// Create an empty MediaList.
- pub fn empty() -> Self {
- MediaList {
- media_queries: vec![],
- }
- }
-}
-
/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
pub enum Qualifier {
/// Hide a media query from legacy UAs:
/// <https://drafts.csswg.org/mediaqueries/#mq-only>
Only,
/// Negate a media query:
/// <https://drafts.csswg.org/mediaqueries/#mq-not>
@@ -228,122 +210,8 @@ impl MediaQuery {
media_type,
expressions,
});
}
expressions.push(Expression::parse(context, input)?)
}
}
}
-
-/// 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:
-///
-/// <https://drafts.csswg.org/mediaqueries/#error-handling>
-pub fn parse_media_query_list<R>(
- 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.
- /// <https://drafts.csswg.org/cssom/#dom-medialist-appendmedium>
- ///
- /// 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.
- /// <https://drafts.csswg.org/cssom/#dom-medialist-deletemedium>
- ///
- /// 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()
- }
-}
--- a/servo/components/style/stylesheets/rule_parser.rs
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -5,17 +5,17 @@
//! Parsing of the stylesheet contents.
use {Namespace, Prefix};
use counter_style::{parse_counter_style_body, parse_counter_style_name_definition};
use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser};
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};
use selectors::SelectorList;
use servo_arc::Arc;
use shared_lock::{Locked, SharedRwLock};
use str::starts_with_ignore_ascii_case;
use style_traits::{ParseError, StyleParseErrorKind};
@@ -192,18 +192,21 @@ impl<'a, 'i, R: ParseErrorReporter> AtRu
"import" => {
if !self.check_state(State::Imports) {
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
}
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);
return Ok(AtRuleType::WithoutBlock(prelude));
},
"namespace" => {
if !self.check_state(State::Namespaces) {
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))
@@ -375,18 +378,21 @@ impl<'a, 'b, 'i, R: ParseErrorReporter>
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
let location = input.current_source_location();
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)))
},
"supports" => {
let cond = SupportsCondition::parse(input)?;
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond, location)))
},
"font-face" => {
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -120,17 +120,17 @@ use style::gecko_bindings::structs::nsID
use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
use style::gecko_bindings::structs::nsTArray;
use style::gecko_bindings::structs::nsresult;
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI};
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::gecko_properties;
use style::invalidation::element::restyle_hints;
-use style::media_queries::{MediaList, parse_media_query_list};
+use style::media_queries::MediaList;
use style::parser::{Parse, ParserContext, self};
use style::properties::{ComputedValues, DeclarationSource, Importance};
use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
use style::properties::{PropertyDeclarationId, ShorthandId};
use style::properties::{SourcePropertyDeclaration, StyleBuilder};
use style::properties::{parse_one_declaration_into, parse_style_attribute};
use style::properties::animated_properties::AnimationValue;
use style::properties::animated_properties::compare_property_priority;
@@ -3750,17 +3750,17 @@ pub unsafe extern "C" fn Servo_MediaList
origin,
url_data,
Some(CssRuleType::Media),
ParsingMode::DEFAULT,
QuirksMode::NoQuirks,
);
write_locked_arc(list, |list: &mut MediaList| {
- *list = parse_media_query_list(
+ *list = MediaList::parse(
&context,
&mut parser,
&NullReporter,
);
})
}
#[no_mangle]