Skip to content

Commit

Permalink
style: Move MediaList to its own module.
Browse files Browse the repository at this point in the history
And move the parsing from a free function to MediaList::parse.

Bug: 1468846
Reviewed-by: xidorn
MozReview-Commit-ID: 75ES6I2EEOE
  • Loading branch information
emilio committed Jun 18, 2018
1 parent ddf386b commit c7b36fb
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 142 deletions.
143 changes: 143 additions & 0 deletions 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()
}
}
142 changes: 5 additions & 137 deletions components/style/media_queries/mod.rs
Expand Up @@ -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<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 {
Expand Down Expand Up @@ -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:
///
/// <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()
}
}
16 changes: 11 additions & 5 deletions components/style/stylesheets/rule_parser.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)))
},
Expand Down

0 comments on commit c7b36fb

Please sign in to comment.