зеркало из https://github.com/mozilla/gecko-dev.git
384 строки
16 KiB
Rust
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
|
|
}));
|
|
}
|