diff options
Diffstat (limited to 'servo/tests/unit/style/viewport.rs')
-rw-r--r-- | servo/tests/unit/style/viewport.rs | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/servo/tests/unit/style/viewport.rs b/servo/tests/unit/style/viewport.rs new file mode 100644 index 0000000000..432f931329 --- /dev/null +++ b/servo/tests/unit/style/viewport.rs @@ -0,0 +1,383 @@ +/* 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 + })); +} |