servo: Merge #14739 - style: Cleanup and refactor how media types are represented (from servo:stylo-mq); r=Manishearth

This is necessary for the upcoming work in Stylo, given I plan to make MediaType
different for Servo and Gecko, and the Unknown variant doesn't fit in MediaType
to be fair, the device is never going to have any unknown media type.

Source-Repo: https://github.com/servo/servo
Source-Revision: d3875f6ec52fdb6bbe49719af6bff299c792ae0b
This commit is contained in:
Emilio Cobos Álvarez 2016-12-30 06:05:43 -08:00
Родитель 0c2495a286
Коммит e33a6984d2
2 изменённых файлов: 114 добавлений и 63 удалений

Просмотреть файл

@ -11,7 +11,8 @@ use app_units::Au;
use cssparser::{Delimiter, Parser, Token};
use euclid::size::{Size2D, TypedSize2D};
use serialize_comma_separated_list;
use std::fmt::{self, Write};
use std::ascii::AsciiExt;
use std::fmt;
use style_traits::{ToCss, ViewportPx};
use values::computed::{self, ToComputedValue};
use values::specified;
@ -85,6 +86,17 @@ pub enum Qualifier {
Not,
}
impl ToCss for Qualifier {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write
{
match *self {
Qualifier::Not => write!(dest, "not"),
Qualifier::Only => write!(dest, "only"),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaQuery {
@ -94,6 +106,12 @@ pub struct MediaQuery {
}
impl MediaQuery {
/// Return a media query that never matches, used for when we fail to parse
/// a given media query.
fn never_matching() -> Self {
Self::new(Some(Qualifier::Not), MediaQueryType::All, vec![])
}
pub fn new(qualifier: Option<Qualifier>, media_type: MediaQueryType,
expressions: Vec<Expression>) -> MediaQuery {
MediaQuery {
@ -108,22 +126,35 @@ impl ToCss for MediaQuery {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write
{
if self.qualifier == Some(Qualifier::Not) {
try!(write!(dest, "not "));
if let Some(qual) = self.qualifier {
try!(qual.to_css(dest));
try!(write!(dest, " "));
}
let mut type_ = String::new();
match self.media_type {
MediaQueryType::All => try!(write!(type_, "all")),
MediaQueryType::MediaType(MediaType::Screen) => try!(write!(type_, "screen")),
MediaQueryType::MediaType(MediaType::Print) => try!(write!(type_, "print")),
MediaQueryType::MediaType(MediaType::Unknown(ref desc)) => try!(write!(type_, "{}", desc)),
};
if self.expressions.is_empty() {
return write!(dest, "{}", type_)
} else if type_ != "all" || self.qualifier == Some(Qualifier::Not) {
try!(write!(dest, "{} and ", type_));
MediaQueryType::All => {
// We need to print "all" if there's a qualifier, or there's
// just an empty list of expressions.
//
// Otherwise, we'd serialize media queries like "(min-width:
// 40px)" in "all (min-width: 40px)", which is unexpected.
if self.qualifier.is_some() || self.expressions.is_empty() {
try!(write!(dest, "all"));
}
},
MediaQueryType::Known(MediaType::Screen) => try!(write!(dest, "screen")),
MediaQueryType::Known(MediaType::Print) => try!(write!(dest, "print")),
MediaQueryType::Unknown(ref desc) => try!(write!(dest, "{}", desc)),
}
if self.expressions.is_empty() {
return Ok(());
}
if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
try!(write!(dest, " and "));
}
for (i, &e) in self.expressions.iter().enumerate() {
try!(write!(dest, "("));
let (mm, l) = match e {
@ -133,10 +164,9 @@ impl ToCss for MediaQuery {
};
try!(write!(dest, "{}width: ", mm));
try!(l.to_css(dest));
if i == self.expressions.len() - 1 {
try!(write!(dest, ")"));
} else {
try!(write!(dest, ") and "));
try!(write!(dest, ")"));
if i != self.expressions.len() - 1 {
try!(write!(dest, " and "));
}
}
Ok(())
@ -148,7 +178,29 @@ impl ToCss for MediaQuery {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum MediaQueryType {
All, // Always true
MediaType(MediaType),
Known(MediaType),
Unknown(Atom),
}
impl MediaQueryType {
fn parse(ident: &str) -> Self {
if ident.eq_ignore_ascii_case("all") {
return MediaQueryType::All;
}
match MediaType::parse(ident) {
Some(media_type) => MediaQueryType::Known(media_type),
None => MediaQueryType::Unknown(Atom::from(ident)),
}
}
fn matches(&self, other: &MediaType) -> bool {
match *self {
MediaQueryType::All => true,
MediaQueryType::Known(ref known_type) => known_type == other,
MediaQueryType::Unknown(..) => false,
}
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
@ -156,7 +208,16 @@ pub enum MediaQueryType {
pub enum MediaType {
Screen,
Print,
Unknown(Atom),
}
impl MediaType {
fn parse(name: &str) -> Option<Self> {
Some(match_ignore_ascii_case! { name,
"screen" => MediaType::Screen,
"print" => MediaType::Print,
_ => return None
})
}
}
#[derive(Debug)]
@ -217,23 +278,20 @@ impl MediaQuery {
None
};
let media_type;
if let Ok(ident) = input.try(|input| input.expect_ident()) {
media_type = match_ignore_ascii_case! { ident,
"screen" => MediaQueryType::MediaType(MediaType::Screen),
"print" => MediaQueryType::MediaType(MediaType::Print),
"all" => MediaQueryType::All,
_ => MediaQueryType::MediaType(MediaType::Unknown(Atom::from(&*ident)))
let media_type = match input.try(|input| input.expect_ident()) {
Ok(ident) => MediaQueryType::parse(&*ident),
Err(()) => {
// Media type is only optional if qualifier is not specified.
if qualifier.is_some() {
return Err(())
}
// Without a media type, require at least one expression.
expressions.push(try!(Expression::parse(input)));
MediaQueryType::All
}
} else {
// Media type is only optional if qualifier is not specified.
if qualifier.is_some() {
return Err(())
}
media_type = MediaQueryType::All;
// Without a media type, require at least one expression
expressions.push(try!(Expression::parse(input)));
}
};
// Parse any subsequent expressions
loop {
@ -253,10 +311,8 @@ pub fn parse_media_query_list(input: &mut Parser) -> MediaList {
let mut media_queries = vec![];
loop {
media_queries.push(
input.parse_until_before(Delimiter::Comma, MediaQuery::parse)
.unwrap_or(MediaQuery::new(Some(Qualifier::Not),
MediaQueryType::All,
vec!())));
input.parse_until_before(Delimiter::Comma, MediaQuery::parse).ok()
.unwrap_or_else(MediaQuery::never_matching));
match input.next() {
Ok(Token::Comma) => {},
Ok(_) => unreachable!(),
@ -275,12 +331,7 @@ impl MediaList {
// 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| {
// Check if media matches. Unknown media never matches.
let media_match = match mq.media_type {
MediaQueryType::MediaType(MediaType::Unknown(_)) => false,
MediaQueryType::MediaType(ref media_type) => *media_type == device.media_type,
MediaQueryType::All => true,
};
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| {

Просмотреть файл

@ -74,7 +74,7 @@ fn test_mq_screen() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@ -82,7 +82,7 @@ fn test_mq_screen() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@ -90,7 +90,7 @@ fn test_mq_screen() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
@ -101,7 +101,7 @@ fn test_mq_print() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@ -109,7 +109,7 @@ fn test_mq_print() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@ -117,7 +117,7 @@ fn test_mq_print() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
@ -128,7 +128,7 @@ fn test_mq_unknown() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("fridge"))), css.to_owned());
assert!(q.media_type == MediaQueryType::Unknown(Atom::from("fridge")), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@ -136,7 +136,7 @@ fn test_mq_unknown() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("glass"))), css.to_owned());
assert!(q.media_type == MediaQueryType::Unknown(Atom::from("glass")), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@ -144,7 +144,7 @@ fn test_mq_unknown() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("wood"))), css.to_owned());
assert!(q.media_type == MediaQueryType::Unknown(Atom::from("wood")), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
@ -182,12 +182,12 @@ fn test_mq_or() {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.media_type == MediaQueryType::Known(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.media_type == MediaQueryType::Known(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
}
@ -225,7 +225,7 @@ fn test_mq_expressions() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
@ -237,7 +237,7 @@ fn test_mq_expressions() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
@ -249,7 +249,7 @@ fn test_mq_expressions() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Print), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Eq(w)) => assert!(w == specified::Length::Absolute(Au::from_px(43))),
@ -261,7 +261,7 @@ fn test_mq_expressions() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("fridge"))), css.to_owned());
assert!(q.media_type == MediaQueryType::Unknown(Atom::from("fridge")), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(52))),
@ -302,7 +302,7 @@ fn test_mq_multiple_expressions() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
assert!(q.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q.media_type == MediaQueryType::Known(MediaType::Screen), css.to_owned());
assert!(q.expressions.len() == 2, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Min(w)) => assert!(w == specified::Length::Absolute(Au::from_px(100))),
@ -377,7 +377,7 @@ fn test_mq_malformed_expressions() {
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == None, css.to_owned());
assert!(q1.media_type == MediaQueryType::MediaType(MediaType::Print), css.to_owned());
assert!(q1.media_type == MediaQueryType::Known(MediaType::Print), css.to_owned());
assert!(q1.expressions.len() == 0, css.to_owned());
});
@ -385,7 +385,7 @@ fn test_mq_malformed_expressions() {
assert!(list.media_queries.len() == 2, css.to_owned());
let q0 = &list.media_queries[0];
assert!(q0.qualifier == None, css.to_owned());
assert!(q0.media_type == MediaQueryType::MediaType(MediaType::Screen), css.to_owned());
assert!(q0.media_type == MediaQueryType::Known(MediaType::Screen), css.to_owned());
assert!(q0.expressions.len() == 0, css.to_owned());
let q1 = &list.media_queries[1];
assert!(q1.qualifier == Some(Qualifier::Not), css.to_owned());