mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
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
This commit is contained in:
parent
ddf386b02b
commit
c7b36fb43f
3 changed files with 159 additions and 142 deletions
143
components/style/media_queries/media_list.rs
Normal file
143
components/style/media_queries/media_list.rs
Normal file
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,40 +7,22 @@
|
||||||
//! [mq]: https://drafts.csswg.org/mediaqueries/
|
//! [mq]: https://drafts.csswg.org/mediaqueries/
|
||||||
|
|
||||||
use Atom;
|
use Atom;
|
||||||
use context::QuirksMode;
|
use cssparser::Parser;
|
||||||
use cssparser::{Delimiter, Parser};
|
use parser::ParserContext;
|
||||||
use cssparser::{ParserInput, Token};
|
|
||||||
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
|
||||||
use parser::{ParserContext, ParserErrorContext};
|
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use str::string_as_ascii_lowercase;
|
use str::string_as_ascii_lowercase;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
use values::CustomIdent;
|
use values::CustomIdent;
|
||||||
|
|
||||||
|
mod media_list;
|
||||||
|
|
||||||
|
pub use self::media_list::MediaList;
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
pub use servo::media_queries::{Device, Expression};
|
pub use servo::media_queries::{Device, Expression};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use gecko::media_queries::{Device, Expression};
|
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>
|
/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
|
||||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
|
||||||
pub enum Qualifier {
|
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:
|
|
||||||
///
|
|
||||||
/// <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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListP
|
||||||
use cssparser::{BasicParseError, BasicParseErrorKind, CowRcStr, SourceLocation};
|
use cssparser::{BasicParseError, BasicParseErrorKind, CowRcStr, SourceLocation};
|
||||||
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
||||||
use font_face::parse_font_face_block;
|
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 parser::{Parse, ParserContext, ParserErrorContext};
|
||||||
use properties::parse_property_declaration_list;
|
use properties::parse_property_declaration_list;
|
||||||
use selector_parser::{SelectorImpl, SelectorParser};
|
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_string = input.expect_url_or_string()?.as_ref().to_owned();
|
||||||
let url = CssUrl::parse_from_string(url_string, &self.context);
|
let url = CssUrl::parse_from_string(url_string, &self.context);
|
||||||
|
|
||||||
let media = parse_media_query_list(&self.context, input,
|
let media = MediaList::parse(
|
||||||
self.error_context.error_reporter);
|
&self.context,
|
||||||
|
input,
|
||||||
|
self.error_context.error_reporter,
|
||||||
|
);
|
||||||
let media = Arc::new(self.shared_lock.wrap(media));
|
let media = Arc::new(self.shared_lock.wrap(media));
|
||||||
|
|
||||||
let prelude = AtRuleNonBlockPrelude::Import(url, media, location);
|
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,
|
match_ignore_ascii_case! { &*name,
|
||||||
"media" => {
|
"media" => {
|
||||||
let media_queries = parse_media_query_list(self.context, input,
|
let media_queries = MediaList::parse(
|
||||||
self.error_context.error_reporter);
|
self.context,
|
||||||
|
input,
|
||||||
|
self.error_context.error_reporter,
|
||||||
|
);
|
||||||
let arc = Arc::new(self.shared_lock.wrap(media_queries));
|
let arc = Arc::new(self.shared_lock.wrap(media_queries));
|
||||||
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
|
Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue