mirror of
https://github.com/servo/servo.git
synced 2025-06-24 17:14:33 +01:00
Rework MediaType to be an atom-based struct instead of an enum. MozReview-Commit-ID: 1Tfrs9PBkhA <!-- Please describe your changes on the following line: --> https://bugzilla.mozilla.org/show_bug.cgi?id=1371395 https://reviewboard.mozilla.org/r/163194/ --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18024) <!-- Reviewable:end -->
410 lines
18 KiB
Rust
410 lines
18 KiB
Rust
/* 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/. */
|
|
|
|
use cssparser::SourceLocation;
|
|
use euclid::ScaleFactor;
|
|
use euclid::TypedSize2D;
|
|
use servo_arc::Arc;
|
|
use servo_url::ServoUrl;
|
|
use std::borrow::ToOwned;
|
|
use style::Atom;
|
|
use style::context::QuirksMode;
|
|
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
|
|
use style::media_queries::*;
|
|
use style::servo::media_queries::*;
|
|
use style::shared_lock::SharedRwLock;
|
|
use style::stylesheets::{AllRules, Stylesheet, StylesheetInDocument, Origin, CssRule};
|
|
use style::values::{CustomIdent, specified};
|
|
use style_traits::ToCss;
|
|
|
|
pub struct CSSErrorReporterTest;
|
|
|
|
impl ParseErrorReporter for CSSErrorReporterTest {
|
|
fn report_error(&self,
|
|
_url: &ServoUrl,
|
|
_location: SourceLocation,
|
|
_error: ContextualParseError) {
|
|
}
|
|
}
|
|
|
|
fn test_media_rule<F>(css: &str, callback: F)
|
|
where F: Fn(&MediaList, &str),
|
|
{
|
|
let url = ServoUrl::parse("http://localhost").unwrap();
|
|
let css_str = css.to_owned();
|
|
let lock = SharedRwLock::new();
|
|
let media_list = Arc::new(lock.wrap(MediaList::empty()));
|
|
let stylesheet = Stylesheet::from_str(
|
|
css, url, Origin::Author, media_list, lock,
|
|
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
|
|
let dummy = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), ScaleFactor::new(1.0));
|
|
let mut rule_count = 0;
|
|
let guard = stylesheet.shared_lock.read();
|
|
for rule in stylesheet.iter_rules::<AllRules>(&dummy, &guard) {
|
|
if let CssRule::Media(ref lock) = *rule {
|
|
rule_count += 1;
|
|
callback(&lock.read_with(&guard).media_queries.read_with(&guard), css);
|
|
}
|
|
}
|
|
assert!(rule_count > 0, css_str);
|
|
}
|
|
|
|
fn media_query_test(device: &Device, css: &str, expected_rule_count: usize) {
|
|
let url = ServoUrl::parse("http://localhost").unwrap();
|
|
let lock = SharedRwLock::new();
|
|
let media_list = Arc::new(lock.wrap(MediaList::empty()));
|
|
let ss = Stylesheet::from_str(
|
|
css, url, Origin::Author, media_list, lock,
|
|
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
|
|
let mut rule_count = 0;
|
|
ss.effective_style_rules(device, &ss.shared_lock.read(), |_| rule_count += 1);
|
|
assert!(rule_count == expected_rule_count, css.to_owned());
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_empty() {
|
|
test_media_rule("@media { }", |list, css| {
|
|
assert!(list.media_queries.len() == 0, css.to_owned());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_screen() {
|
|
test_media_rule("@media screen { }", |list, css| {
|
|
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::Concrete(MediaType::screen()), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media only screen { }", |list, css| {
|
|
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::Concrete(MediaType::screen()), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media not screen { }", |list, css| {
|
|
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::Concrete(MediaType::screen()), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_print() {
|
|
test_media_rule("@media print { }", |list, css| {
|
|
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::Concrete(MediaType::print()), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media only print { }", |list, css| {
|
|
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::Concrete(MediaType::print()), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media not print { }", |list, css| {
|
|
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::Concrete(MediaType::print()), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_unknown() {
|
|
test_media_rule("@media fridge { }", |list, css| {
|
|
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::Concrete(
|
|
MediaType(CustomIdent(Atom::from("fridge")))), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media only glass { }", |list, css| {
|
|
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::Concrete(
|
|
MediaType(CustomIdent(Atom::from("glass")))), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media not wood { }", |list, css| {
|
|
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::Concrete(
|
|
MediaType(CustomIdent(Atom::from("wood")))), css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_all() {
|
|
test_media_rule("@media all { }", |list, css| {
|
|
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::All, css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media only all { }", |list, css| {
|
|
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::All, css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
|
|
test_media_rule("@media not all { }", |list, css| {
|
|
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::All, css.to_owned());
|
|
assert!(q.expressions.len() == 0, css.to_owned());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_or() {
|
|
test_media_rule("@media screen, print { }", |list, css| {
|
|
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::Concrete(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::Concrete(MediaType::print()), css.to_owned());
|
|
assert!(q1.expressions.len() == 0, css.to_owned());
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_default_expressions() {
|
|
test_media_rule("@media (min-width: 100px) { }", |list, css| {
|
|
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::All, css.to_owned());
|
|
assert!(q.expressions.len() == 1, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
|
|
test_media_rule("@media (max-width: 43px) { }", |list, css| {
|
|
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::All, css.to_owned());
|
|
assert!(q.expressions.len() == 1, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(43.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_expressions() {
|
|
test_media_rule("@media screen and (min-width: 100px) { }", |list, css| {
|
|
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::Concrete(MediaType::screen()), css.to_owned());
|
|
assert!(q.expressions.len() == 1, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
|
|
test_media_rule("@media print and (max-width: 43px) { }", |list, css| {
|
|
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::Concrete(MediaType::print()), css.to_owned());
|
|
assert!(q.expressions.len() == 1, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(43.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
|
|
test_media_rule("@media print and (width: 43px) { }", |list, css| {
|
|
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::Concrete(MediaType::print()), css.to_owned());
|
|
assert!(q.expressions.len() == 1, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Eq(ref w)) => assert!(*w == specified::Length::from_px(43.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
|
|
test_media_rule("@media fridge and (max-width: 52px) { }", |list, css| {
|
|
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::Concrete(
|
|
MediaType(CustomIdent(Atom::from("fridge")))), css.to_owned());
|
|
assert!(q.expressions.len() == 1, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(52.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_css() {
|
|
test_media_rule("@media print and (width: 43px) { }", |list, _| {
|
|
let q = &list.media_queries[0];
|
|
let mut dest = String::new();
|
|
assert_eq!(Ok(()), q.to_css(&mut dest));
|
|
assert_eq!(dest, "print and (width: 43px)");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_multiple_expressions() {
|
|
test_media_rule("@media (min-width: 100px) and (max-width: 200px) { }", |list, css| {
|
|
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::All, css.to_owned());
|
|
assert!(q.expressions.len() == 2, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
match *q.expressions[1].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(200.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
|
|
test_media_rule("@media not screen and (min-width: 100px) and (max-width: 200px) { }", |list, css| {
|
|
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::Concrete(MediaType::screen()), css.to_owned());
|
|
assert!(q.expressions.len() == 2, css.to_owned());
|
|
match *q.expressions[0].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Min(ref w)) => assert!(*w == specified::Length::from_px(100.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
match *q.expressions[1].kind_for_testing() {
|
|
ExpressionKind::Width(Range::Max(ref w)) => assert!(*w == specified::Length::from_px(200.)),
|
|
_ => panic!("wrong expression type"),
|
|
}
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_mq_malformed_expressions() {
|
|
fn check_malformed_expr(list: &MediaList, css: &str) {
|
|
assert!(!list.media_queries.is_empty(), css.to_owned());
|
|
for mq in &list.media_queries {
|
|
assert!(mq.qualifier == Some(Qualifier::Not), css.to_owned());
|
|
assert!(mq.media_type == MediaQueryType::All, css.to_owned());
|
|
assert!(mq.expressions.is_empty(), css.to_owned());
|
|
}
|
|
}
|
|
|
|
for rule in &[
|
|
"@media (min-width: 100blah) and (max-width: 200px) { }",
|
|
"@media screen and (height: 200px) { }",
|
|
"@media (min-width: 30em foo bar) {}",
|
|
"@media not {}",
|
|
"@media not (min-width: 300px) {}",
|
|
"@media , {}",
|
|
] {
|
|
test_media_rule(rule, check_malformed_expr);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_matching_simple() {
|
|
let device = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), ScaleFactor::new(1.0));
|
|
|
|
media_query_test(&device, "@media not all { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media not screen { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media not print { a { color: red; } }", 1);
|
|
|
|
media_query_test(&device, "@media unknown { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media not unknown { a { color: red; } }", 1);
|
|
|
|
media_query_test(&device, "@media { a { color: red; } }", 1);
|
|
media_query_test(&device, "@media screen { a { color: red; } }", 1);
|
|
media_query_test(&device, "@media print { a { color: red; } }", 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_matching_width() {
|
|
let device = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), ScaleFactor::new(1.0));
|
|
|
|
media_query_test(&device, "@media { a { color: red; } }", 1);
|
|
|
|
media_query_test(&device, "@media (min-width: 50px) { a { color: red; } }", 1);
|
|
media_query_test(&device, "@media (min-width: 150px) { a { color: red; } }", 1);
|
|
media_query_test(&device, "@media (min-width: 300px) { a { color: red; } }", 0);
|
|
|
|
media_query_test(&device, "@media screen and (min-width: 50px) { a { color: red; } }", 1);
|
|
media_query_test(&device, "@media screen and (min-width: 150px) { a { color: red; } }", 1);
|
|
media_query_test(&device, "@media screen and (min-width: 300px) { a { color: red; } }", 0);
|
|
|
|
media_query_test(&device, "@media not screen and (min-width: 50px) { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media not screen and (min-width: 150px) { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media not screen and (min-width: 300px) { a { color: red; } }", 1);
|
|
|
|
media_query_test(&device, "@media (max-width: 50px) { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media (max-width: 150px) { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media (max-width: 300px) { a { color: red; } }", 1);
|
|
|
|
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 1);
|
|
|
|
media_query_test(
|
|
&device, "@media not screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }", 1);
|
|
media_query_test(
|
|
&device, "@media not screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }", 1);
|
|
media_query_test(
|
|
&device, "@media not screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }", 0);
|
|
|
|
media_query_test(
|
|
&device, "@media not screen and (min-width: 3.1em) and (max-width: 6em) { a { color: red; } }", 1);
|
|
media_query_test(
|
|
&device, "@media not screen and (min-width: 16em) and (max-width: 19.75em) { a { color: red; } }", 1);
|
|
media_query_test(
|
|
&device, "@media not screen and (min-width: 3em) and (max-width: 250px) { a { color: red; } }", 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_matching_invalid() {
|
|
let device = Device::new(MediaType::screen(), TypedSize2D::new(200.0, 100.0), ScaleFactor::new(1.0));
|
|
|
|
media_query_test(&device, "@media fridge { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media screen and (height: 100px) { a { color: red; } }", 0);
|
|
media_query_test(&device, "@media not print and (width: 100) { a { color: red; } }", 0);
|
|
}
|