gecko-dev/servo/tests/unit/style/viewport.rs

384 строки
16 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 https://mozilla.org/MPL/2.0/. */
use cssparser::{Parser, ParserInput};
use euclid::Scale;
use euclid::Size2D;
use servo_arc::Arc;
use servo_config::prefs::{PREFS, PrefValue};
use servo_url::ServoUrl;
use style::context::QuirksMode;
use style::media_queries::{Device, MediaList, MediaType};
use style::parser::ParserContext;
use style::shared_lock::{SharedRwLock, StylesheetGuards};
use style::stylesheets::{CssRuleType, Stylesheet, StylesheetInDocument, Origin};
use style::stylesheets::viewport_rule::*;
use style::values::specified::LengthPercentageOrAuto::{self, Auto};
use style::values::specified::NoCalcLength::{self, ViewportPercentage};
use style::values::specified::ViewportPercentageLength::Vw;
use style_traits::{ParsingMode, PinchZoomFactor};
use style_traits::viewport::*;
macro_rules! stylesheet {
($css:expr, $origin:ident) => {
stylesheet!($css, $origin, SharedRwLock::new())
};
($css:expr, $origin:ident, $shared_lock:expr) => {
Arc::new(Stylesheet::from_str(
$css,
ServoUrl::parse("http://localhost").unwrap(),
Origin::$origin,
Arc::new($shared_lock.wrap(MediaList::empty())),
$shared_lock,
None,
None,
QuirksMode::NoQuirks,
0
))
}
}
fn test_viewport_rule<F>(css: &str,
device: &Device,
callback: F)
where F: Fn(&Vec<ViewportDescriptorDeclaration>, &str)
{
PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true));
let stylesheet = stylesheet!(css, Author);
let mut rule_count = 0;
stylesheet.effective_viewport_rules(&device, &stylesheet.shared_lock.read(), |rule| {
rule_count += 1;
callback(&rule.declarations, css);
});
assert!(rule_count > 0);
}
fn test_meta_viewport<F>(meta: &str, callback: F)
where F: Fn(&Vec<ViewportDescriptorDeclaration>, &str)
{
if let Some(mut rule) = ViewportRule::from_meta(meta) {
// from_meta uses a hash-map to collect the declarations, so we need to
// sort them in a stable order for the tests
rule.declarations.sort_by(|a, b| {
let a = a.descriptor.discriminant_value();
let b = b.descriptor.discriminant_value();
a.cmp(&b)
});
callback(&rule.declarations, meta);
} else {
panic!("no @viewport rule for {}", meta);
}
}
macro_rules! assert_decl_len {
($declarations:ident == 1) => {
assert_eq!($declarations.len(), 1,
"expected 1 declaration; have {}: {:?})",
$declarations.len(), $declarations)
};
($declarations:ident == $len:expr) => {
assert_eq!($declarations.len(), $len,
"expected {} declarations; have {}: {:?})",
$len, $declarations.len(), $declarations)
}
}
macro_rules! viewport_length {
($value:expr, px) => {
ViewportLength::Specified(LengthPercentageOrAuto::Length(NoCalcLength::from_px($value)))
};
($value:expr, vw) => {
ViewportLength::Specified(LengthPercentageOrAuto::Length(ViewportPercentage(Vw($value))))
}
}
#[test]
fn empty_viewport_rule() {
let device = Device::new(MediaType::screen(), Size2D::new(800., 600.), Scale::new(1.0));
test_viewport_rule("@viewport {}", &device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 0);
});
}
macro_rules! assert_decl_eq {
($d:expr, $origin:ident, $expected:ident: $value:expr) => {{
assert_eq!($d.origin, Origin::$origin);
assert_eq!($d.descriptor, ViewportDescriptor::$expected($value));
assert_eq!($d.important, false, "descriptor should not be !important");
}};
($d:expr, $origin:ident, $expected:ident: $value:expr, !important) => {{
assert_eq!($d.origin, Origin::$origin);
assert_eq!($d.descriptor, ViewportDescriptor::$expected($value));
assert_eq!($d.important, true, "descriptor should be !important");
}};
}
#[test]
fn simple_viewport_rules() {
let device = Device::new(MediaType::screen(), Size2D::new(800., 600.), Scale::new(1.0));
test_viewport_rule("@viewport { width: auto; height: auto;\
zoom: auto; min-zoom: 0; max-zoom: 200%;\
user-zoom: zoom; orientation: auto; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 9);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[1], Author, MaxWidth: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[2], Author, MinHeight: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[3], Author, MaxHeight: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[4], Author, Zoom: Zoom::Auto);
assert_decl_eq!(&declarations[5], Author, MinZoom: Zoom::Number(0.));
assert_decl_eq!(&declarations[6], Author, MaxZoom: Zoom::Percentage(2.));
assert_decl_eq!(&declarations[7], Author, UserZoom: UserZoom::Zoom);
assert_decl_eq!(&declarations[8], Author, Orientation: Orientation::Auto);
});
test_viewport_rule("@viewport { min-width: 200px; max-width: auto;\
min-height: 200px; max-height: auto; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 4);
assert_decl_eq!(&declarations[0], Author, MinWidth: viewport_length!(200., px));
assert_decl_eq!(&declarations[1], Author, MaxWidth: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[2], Author, MinHeight: viewport_length!(200., px));
assert_decl_eq!(&declarations[3], Author, MaxHeight: ViewportLength::Specified(Auto));
});
}
#[test]
fn simple_meta_viewport_contents() {
test_meta_viewport("width=500, height=600", |declarations, meta| {
println!("{}", meta);
assert_decl_len!(declarations == 4);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::ExtendToZoom);
assert_decl_eq!(&declarations[1], Author, MaxWidth: viewport_length!(500., px));
assert_decl_eq!(&declarations[2], Author, MinHeight: ViewportLength::ExtendToZoom);
assert_decl_eq!(&declarations[3], Author, MaxHeight: viewport_length!(600., px));
});
test_meta_viewport("initial-scale=1.0", |declarations, meta| {
println!("{}", meta);
assert_decl_len!(declarations == 3);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::ExtendToZoom);
assert_decl_eq!(&declarations[1], Author, MaxWidth: ViewportLength::ExtendToZoom);
assert_decl_eq!(&declarations[2], Author, Zoom: Zoom::Number(1.));
});
test_meta_viewport("initial-scale=2.0, height=device-width", |declarations, meta| {
println!("{}", meta);
assert_decl_len!(declarations == 5);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[1], Author, MaxWidth: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[2], Author, MinHeight: ViewportLength::ExtendToZoom);
assert_decl_eq!(&declarations[3], Author, MaxHeight: viewport_length!(100., vw));
assert_decl_eq!(&declarations[4], Author, Zoom: Zoom::Number(2.));
});
test_meta_viewport("width=480, initial-scale=2.0, user-scalable=1", |declarations, meta| {
println!("{}", meta);
assert_decl_len!(declarations == 4);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::ExtendToZoom);
assert_decl_eq!(&declarations[1], Author, MaxWidth: viewport_length!(480., px));
assert_decl_eq!(&declarations[2], Author, Zoom: Zoom::Number(2.));
assert_decl_eq!(&declarations[3], Author, UserZoom: UserZoom::Zoom);
});
}
#[test]
fn cascading_within_viewport_rule() {
let device = Device::new(MediaType::screen(), Size2D::new(800., 600.), Scale::new(1.0));
// normal order of appearance
test_viewport_rule("@viewport { min-width: 200px; min-width: auto; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 1);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto));
});
// !important order of appearance
test_viewport_rule("@viewport { min-width: 200px !important; min-width: auto !important; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 1);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto), !important);
});
// !important vs normal
test_viewport_rule("@viewport { min-width: auto !important; min-width: 200px; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 1);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto), !important);
});
// normal longhands vs normal shorthand
test_viewport_rule("@viewport { min-width: 200px; max-width: 200px; width: auto; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 2);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[1], Author, MaxWidth: ViewportLength::Specified(Auto));
});
// normal shorthand vs normal longhands
test_viewport_rule("@viewport { width: 200px; min-width: auto; max-width: auto; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 2);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto));
assert_decl_eq!(&declarations[1], Author, MaxWidth: ViewportLength::Specified(Auto));
});
// one !important longhand vs normal shorthand
test_viewport_rule("@viewport { min-width: auto !important; width: 200px; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 2);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto), !important);
assert_decl_eq!(&declarations[1], Author, MaxWidth: viewport_length!(200., px));
});
// both !important longhands vs normal shorthand
test_viewport_rule("@viewport { min-width: auto !important; max-width: auto !important; width: 200px; }",
&device, |declarations, css| {
println!("{}", css);
assert_decl_len!(declarations == 2);
assert_decl_eq!(&declarations[0], Author, MinWidth: ViewportLength::Specified(Auto), !important);
assert_decl_eq!(&declarations[1], Author, MaxWidth: ViewportLength::Specified(Auto), !important);
});
}
#[test]
fn multiple_stylesheets_cascading() {
PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true));
let device = Device::new(MediaType::screen(), Size2D::new(800., 600.), Scale::new(1.0));
let shared_lock = SharedRwLock::new();
let stylesheets = vec![
stylesheet!("@viewport { min-width: 100px; min-height: 100px; zoom: 1; }",
UserAgent,
shared_lock.clone()),
stylesheet!("@viewport { min-width: 200px; min-height: 200px; }",
User, shared_lock.clone()),
stylesheet!("@viewport { min-width: 300px; }",
Author, shared_lock.clone())
];
let declarations = Cascade::from_stylesheets(
stylesheets.iter().map(|s| (&**s, Origin::Author)),
&StylesheetGuards::same(&shared_lock.read()),
&device,
).finish();
assert_decl_len!(declarations == 3);
assert_decl_eq!(&declarations[0], UserAgent, Zoom: Zoom::Number(1.));
assert_decl_eq!(&declarations[1], User, MinHeight: viewport_length!(200., px));
assert_decl_eq!(&declarations[2], Author, MinWidth: viewport_length!(300., px));
let stylesheets = vec![
stylesheet!("@viewport { min-width: 100px !important; }",
UserAgent, shared_lock.clone()),
stylesheet!("@viewport { min-width: 200px !important; min-height: 200px !important; }",
User, shared_lock.clone()),
stylesheet!("@viewport { min-width: 300px !important; min-height: 300px !important; zoom: 3 !important; }",
Author, shared_lock.clone())
];
let declarations = Cascade::from_stylesheets(
stylesheets.iter().map(|s| (&**s, Origin::Author)),
&StylesheetGuards::same(&shared_lock.read()),
&device,
).finish();
assert_decl_len!(declarations == 3);
assert_decl_eq!(&declarations[0], UserAgent, MinWidth: viewport_length!(100., px), !important);
assert_decl_eq!(&declarations[1], User, MinHeight: viewport_length!(200., px), !important);
assert_decl_eq!(&declarations[2], Author, Zoom: Zoom::Number(3.), !important);
}
#[test]
fn constrain_viewport() {
let url = ServoUrl::parse("http://localhost").unwrap();
let context = ParserContext::new(
Origin::Author,
&url,
Some(CssRuleType::Viewport),
ParsingMode::DEFAULT,
QuirksMode::NoQuirks,
None,
None,
);
macro_rules! from_css {
($css:expr) => {
&ViewportRule::parse(&context, &mut Parser::new(&mut $css)).unwrap()
}
}
let initial_viewport = Size2D::new(800., 600.);
let device = Device::new(MediaType::screen(), initial_viewport, Scale::new(1.0));
let mut input = ParserInput::new("");
assert_eq!(ViewportConstraints::maybe_new(&device, from_css!(input), QuirksMode::NoQuirks), None);
let mut input = ParserInput::new("width: 320px auto");
assert_eq!(ViewportConstraints::maybe_new(&device, from_css!(input), QuirksMode::NoQuirks),
Some(ViewportConstraints {
size: initial_viewport,
initial_zoom: PinchZoomFactor::new(1.),
min_zoom: None,
max_zoom: None,
user_zoom: UserZoom::Zoom,
orientation: Orientation::Auto
}));
let mut input = ParserInput::new("width: 320px auto");
assert_eq!(ViewportConstraints::maybe_new(&device, from_css!(input), QuirksMode::NoQuirks),
Some(ViewportConstraints {
size: initial_viewport,
initial_zoom: PinchZoomFactor::new(1.),
min_zoom: None,
max_zoom: None,
user_zoom: UserZoom::Zoom,
orientation: Orientation::Auto
}));
let mut input = ParserInput::new("width: 800px; height: 600px;\
zoom: 1;\
user-zoom: zoom;\
orientation: auto;");
assert_eq!(ViewportConstraints::maybe_new(&device,
from_css!(input),
QuirksMode::NoQuirks),
Some(ViewportConstraints {
size: initial_viewport,
initial_zoom: PinchZoomFactor::new(1.),
min_zoom: None,
max_zoom: None,
user_zoom: UserZoom::Zoom,
orientation: Orientation::Auto
}));
let initial_viewport = Size2D::new(200., 150.);
let device = Device::new(MediaType::screen(), initial_viewport, Scale::new(1.0));
let mut input = ParserInput::new("width: 320px auto");
assert_eq!(ViewportConstraints::maybe_new(&device, from_css!(input), QuirksMode::NoQuirks),
Some(ViewportConstraints {
size: Size2D::new(320., 240.),
initial_zoom: PinchZoomFactor::new(1.),
min_zoom: None,
max_zoom: None,
user_zoom: UserZoom::Zoom,
orientation: Orientation::Auto
}));
}