tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

media_queries.rs (19629B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 use euclid::Scale;
      6 use euclid::Size2D;
      7 use servo_arc::Arc;
      8 use servo_url::ServoUrl;
      9 use std::borrow::ToOwned;
     10 use style::context::QuirksMode;
     11 use style::media_queries::*;
     12 use style::servo::media_queries::*;
     13 use style::shared_lock::SharedRwLock;
     14 use style::stylesheets::{AllRules, CssRule, Origin, Stylesheet, StylesheetInDocument};
     15 use style::values::{specified, CustomIdent};
     16 use style::Atom;
     17 use style_traits::ToCss;
     18 
     19 fn test_media_rule<F>(css: &str, callback: F)
     20 where
     21    F: Fn(&MediaList, &str),
     22 {
     23    let url = ServoUrl::parse("http://localhost").unwrap();
     24    let css_str = css.to_owned();
     25    let lock = SharedRwLock::new();
     26    let media_list = Arc::new(lock.wrap(MediaList::empty()));
     27    let stylesheet = Stylesheet::from_str(
     28        css,
     29        url,
     30        Origin::Author,
     31        media_list,
     32        lock,
     33        None,
     34        None,
     35        QuirksMode::NoQuirks,
     36        0,
     37    );
     38    let dummy = Device::new(
     39        MediaType::screen(),
     40        Size2D::new(200.0, 100.0),
     41        Scale::new(1.0),
     42    );
     43    let mut rule_count = 0;
     44    let guard = stylesheet.shared_lock.read();
     45    for rule in stylesheet.iter_rules::<AllRules>(&dummy, &guard) {
     46        if let CssRule::Media(ref lock) = *rule {
     47            rule_count += 1;
     48            callback(&lock.read_with(&guard).media_queries.read_with(&guard), css);
     49        }
     50    }
     51    assert!(rule_count > 0, css_str);
     52 }
     53 
     54 fn media_query_test(device: &Device, css: &str, expected_rule_count: usize) {
     55    let url = ServoUrl::parse("http://localhost").unwrap();
     56    let lock = SharedRwLock::new();
     57    let media_list = Arc::new(lock.wrap(MediaList::empty()));
     58    let ss = Stylesheet::from_str(
     59        css,
     60        url,
     61        Origin::Author,
     62        media_list,
     63        lock,
     64        None,
     65        None,
     66        QuirksMode::NoQuirks,
     67        0,
     68    );
     69    let mut rule_count = 0;
     70    ss.effective_style_rules(device, &ss.shared_lock.read(), |_| rule_count += 1);
     71    assert!(rule_count == expected_rule_count, css.to_owned());
     72 }
     73 
     74 #[test]
     75 fn test_mq_empty() {
     76    test_media_rule("@media { }", |list, css| {
     77        assert!(list.media_queries.len() == 0, css.to_owned());
     78    });
     79 }
     80 
     81 #[test]
     82 fn test_mq_screen() {
     83    test_media_rule("@media screen { }", |list, css| {
     84        assert!(list.media_queries.len() == 1, css.to_owned());
     85        let q = &list.media_queries[0];
     86        assert!(q.qualifier == None, css.to_owned());
     87        assert!(
     88            q.media_type == MediaQueryType::Concrete(MediaType::screen()),
     89            css.to_owned()
     90        );
     91        assert!(q.expressions.len() == 0, css.to_owned());
     92    });
     93 
     94    test_media_rule("@media only screen { }", |list, css| {
     95        assert!(list.media_queries.len() == 1, css.to_owned());
     96        let q = &list.media_queries[0];
     97        assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
     98        assert!(
     99            q.media_type == MediaQueryType::Concrete(MediaType::screen()),
    100            css.to_owned()
    101        );
    102        assert!(q.expressions.len() == 0, css.to_owned());
    103    });
    104 
    105    test_media_rule("@media not screen { }", |list, css| {
    106        assert!(list.media_queries.len() == 1, css.to_owned());
    107        let q = &list.media_queries[0];
    108        assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
    109        assert!(
    110            q.media_type == MediaQueryType::Concrete(MediaType::screen()),
    111            css.to_owned()
    112        );
    113        assert!(q.expressions.len() == 0, css.to_owned());
    114    });
    115 }
    116 
    117 #[test]
    118 fn test_mq_print() {
    119    test_media_rule("@media print { }", |list, css| {
    120        assert!(list.media_queries.len() == 1, css.to_owned());
    121        let q = &list.media_queries[0];
    122        assert!(q.qualifier == None, css.to_owned());
    123        assert!(
    124            q.media_type == MediaQueryType::Concrete(MediaType::print()),
    125            css.to_owned()
    126        );
    127        assert!(q.expressions.len() == 0, css.to_owned());
    128    });
    129 
    130    test_media_rule("@media only print { }", |list, css| {
    131        assert!(list.media_queries.len() == 1, css.to_owned());
    132        let q = &list.media_queries[0];
    133        assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
    134        assert!(
    135            q.media_type == MediaQueryType::Concrete(MediaType::print()),
    136            css.to_owned()
    137        );
    138        assert!(q.expressions.len() == 0, css.to_owned());
    139    });
    140 
    141    test_media_rule("@media not print { }", |list, css| {
    142        assert!(list.media_queries.len() == 1, css.to_owned());
    143        let q = &list.media_queries[0];
    144        assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
    145        assert!(
    146            q.media_type == MediaQueryType::Concrete(MediaType::print()),
    147            css.to_owned()
    148        );
    149        assert!(q.expressions.len() == 0, css.to_owned());
    150    });
    151 }
    152 
    153 #[test]
    154 fn test_mq_unknown() {
    155    test_media_rule("@media fridge { }", |list, css| {
    156        assert!(list.media_queries.len() == 1, css.to_owned());
    157        let q = &list.media_queries[0];
    158        assert!(q.qualifier == None, css.to_owned());
    159        assert!(
    160            q.media_type == MediaQueryType::Concrete(MediaType(CustomIdent(Atom::from("fridge")))),
    161            css.to_owned()
    162        );
    163        assert!(q.expressions.len() == 0, css.to_owned());
    164    });
    165 
    166    test_media_rule("@media only glass { }", |list, css| {
    167        assert!(list.media_queries.len() == 1, css.to_owned());
    168        let q = &list.media_queries[0];
    169        assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
    170        assert!(
    171            q.media_type == MediaQueryType::Concrete(MediaType(CustomIdent(Atom::from("glass")))),
    172            css.to_owned()
    173        );
    174        assert!(q.expressions.len() == 0, css.to_owned());
    175    });
    176 
    177    test_media_rule("@media not wood { }", |list, css| {
    178        assert!(list.media_queries.len() == 1, css.to_owned());
    179        let q = &list.media_queries[0];
    180        assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
    181        assert!(
    182            q.media_type == MediaQueryType::Concrete(MediaType(CustomIdent(Atom::from("wood")))),
    183            css.to_owned()
    184        );
    185        assert!(q.expressions.len() == 0, css.to_owned());
    186    });
    187 }
    188 
    189 #[test]
    190 fn test_mq_all() {
    191    test_media_rule("@media all { }", |list, css| {
    192        assert!(list.media_queries.len() == 1, css.to_owned());
    193        let q = &list.media_queries[0];
    194        assert!(q.qualifier == None, css.to_owned());
    195        assert!(q.media_type == MediaQueryType::All, css.to_owned());
    196        assert!(q.expressions.len() == 0, css.to_owned());
    197    });
    198 
    199    test_media_rule("@media only all { }", |list, css| {
    200        assert!(list.media_queries.len() == 1, css.to_owned());
    201        let q = &list.media_queries[0];
    202        assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
    203        assert!(q.media_type == MediaQueryType::All, css.to_owned());
    204        assert!(q.expressions.len() == 0, css.to_owned());
    205    });
    206 
    207    test_media_rule("@media not all { }", |list, css| {
    208        assert!(list.media_queries.len() == 1, css.to_owned());
    209        let q = &list.media_queries[0];
    210        assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
    211        assert!(q.media_type == MediaQueryType::All, css.to_owned());
    212        assert!(q.expressions.len() == 0, css.to_owned());
    213    });
    214 }
    215 
    216 #[test]
    217 fn test_mq_or() {
    218    test_media_rule("@media screen, print { }", |list, css| {
    219        assert!(list.media_queries.len() == 2, css.to_owned());
    220        let q0 = &list.media_queries[0];
    221        assert!(q0.qualifier == None, css.to_owned());
    222        assert!(
    223            q0.media_type == MediaQueryType::Concrete(MediaType::screen()),
    224            css.to_owned()
    225        );
    226        assert!(q0.expressions.len() == 0, css.to_owned());
    227 
    228        let q1 = &list.media_queries[1];
    229        assert!(q1.qualifier == None, css.to_owned());
    230        assert!(
    231            q1.media_type == MediaQueryType::Concrete(MediaType::print()),
    232            css.to_owned()
    233        );
    234        assert!(q1.expressions.len() == 0, css.to_owned());
    235    });
    236 }
    237 
    238 #[test]
    239 fn test_mq_default_expressions() {
    240    test_media_rule("@media (min-width: 100px) { }", |list, css| {
    241        assert!(list.media_queries.len() == 1, css.to_owned());
    242        let q = &list.media_queries[0];
    243        assert!(q.qualifier == None, css.to_owned());
    244        assert!(q.media_type == MediaQueryType::All, css.to_owned());
    245        assert!(q.expressions.len() == 1, css.to_owned());
    246        match *q.expressions[0].kind_for_testing() {
    247            ExpressionKind::Width(Range::Min(ref w)) => {
    248                assert!(*w == specified::Length::from_px(100.))
    249            },
    250            _ => panic!("wrong expression type"),
    251        }
    252    });
    253 
    254    test_media_rule("@media (max-width: 43px) { }", |list, css| {
    255        assert!(list.media_queries.len() == 1, css.to_owned());
    256        let q = &list.media_queries[0];
    257        assert!(q.qualifier == None, css.to_owned());
    258        assert!(q.media_type == MediaQueryType::All, css.to_owned());
    259        assert!(q.expressions.len() == 1, css.to_owned());
    260        match *q.expressions[0].kind_for_testing() {
    261            ExpressionKind::Width(Range::Max(ref w)) => {
    262                assert!(*w == specified::Length::from_px(43.))
    263            },
    264            _ => panic!("wrong expression type"),
    265        }
    266    });
    267 }
    268 
    269 #[test]
    270 fn test_mq_expressions() {
    271    test_media_rule("@media screen and (min-width: 100px) { }", |list, css| {
    272        assert!(list.media_queries.len() == 1, css.to_owned());
    273        let q = &list.media_queries[0];
    274        assert!(q.qualifier == None, css.to_owned());
    275        assert!(
    276            q.media_type == MediaQueryType::Concrete(MediaType::screen()),
    277            css.to_owned()
    278        );
    279        assert!(q.expressions.len() == 1, css.to_owned());
    280        match *q.expressions[0].kind_for_testing() {
    281            ExpressionKind::Width(Range::Min(ref w)) => {
    282                assert!(*w == specified::Length::from_px(100.))
    283            },
    284            _ => panic!("wrong expression type"),
    285        }
    286    });
    287 
    288    test_media_rule("@media print and (max-width: 43px) { }", |list, css| {
    289        assert!(list.media_queries.len() == 1, css.to_owned());
    290        let q = &list.media_queries[0];
    291        assert!(q.qualifier == None, css.to_owned());
    292        assert!(
    293            q.media_type == MediaQueryType::Concrete(MediaType::print()),
    294            css.to_owned()
    295        );
    296        assert!(q.expressions.len() == 1, css.to_owned());
    297        match *q.expressions[0].kind_for_testing() {
    298            ExpressionKind::Width(Range::Max(ref w)) => {
    299                assert!(*w == specified::Length::from_px(43.))
    300            },
    301            _ => panic!("wrong expression type"),
    302        }
    303    });
    304 
    305    test_media_rule("@media print and (width: 43px) { }", |list, css| {
    306        assert!(list.media_queries.len() == 1, css.to_owned());
    307        let q = &list.media_queries[0];
    308        assert!(q.qualifier == None, css.to_owned());
    309        assert!(
    310            q.media_type == MediaQueryType::Concrete(MediaType::print()),
    311            css.to_owned()
    312        );
    313        assert!(q.expressions.len() == 1, css.to_owned());
    314        match *q.expressions[0].kind_for_testing() {
    315            ExpressionKind::Width(Range::Eq(ref w)) => {
    316                assert!(*w == specified::Length::from_px(43.))
    317            },
    318            _ => panic!("wrong expression type"),
    319        }
    320    });
    321 
    322    test_media_rule("@media fridge and (max-width: 52px) { }", |list, css| {
    323        assert!(list.media_queries.len() == 1, css.to_owned());
    324        let q = &list.media_queries[0];
    325        assert!(q.qualifier == None, css.to_owned());
    326        assert!(
    327            q.media_type == MediaQueryType::Concrete(MediaType(CustomIdent(Atom::from("fridge")))),
    328            css.to_owned()
    329        );
    330        assert!(q.expressions.len() == 1, css.to_owned());
    331        match *q.expressions[0].kind_for_testing() {
    332            ExpressionKind::Width(Range::Max(ref w)) => {
    333                assert!(*w == specified::Length::from_px(52.))
    334            },
    335            _ => panic!("wrong expression type"),
    336        }
    337    });
    338 }
    339 
    340 #[test]
    341 fn test_to_css() {
    342    test_media_rule("@media print and (width: 43px) { }", |list, _| {
    343        let q = &list.media_queries[0];
    344        let dest = q.to_css_string();
    345        assert_eq!(dest, "print and (width: 43px)");
    346    });
    347 }
    348 
    349 #[test]
    350 fn test_mq_multiple_expressions() {
    351    test_media_rule(
    352        "@media (min-width: 100px) and (max-width: 200px) { }",
    353        |list, css| {
    354            assert!(list.media_queries.len() == 1, css.to_owned());
    355            let q = &list.media_queries[0];
    356            assert!(q.qualifier == None, css.to_owned());
    357            assert!(q.media_type == MediaQueryType::All, css.to_owned());
    358            assert!(q.expressions.len() == 2, css.to_owned());
    359            match *q.expressions[0].kind_for_testing() {
    360                ExpressionKind::Width(Range::Min(ref w)) => {
    361                    assert!(*w == specified::Length::from_px(100.))
    362                },
    363                _ => panic!("wrong expression type"),
    364            }
    365            match *q.expressions[1].kind_for_testing() {
    366                ExpressionKind::Width(Range::Max(ref w)) => {
    367                    assert!(*w == specified::Length::from_px(200.))
    368                },
    369                _ => panic!("wrong expression type"),
    370            }
    371        },
    372    );
    373 
    374    test_media_rule(
    375        "@media not screen and (min-width: 100px) and (max-width: 200px) { }",
    376        |list, css| {
    377            assert!(list.media_queries.len() == 1, css.to_owned());
    378            let q = &list.media_queries[0];
    379            assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
    380            assert!(
    381                q.media_type == MediaQueryType::Concrete(MediaType::screen()),
    382                css.to_owned()
    383            );
    384            assert!(q.expressions.len() == 2, css.to_owned());
    385            match *q.expressions[0].kind_for_testing() {
    386                ExpressionKind::Width(Range::Min(ref w)) => {
    387                    assert!(*w == specified::Length::from_px(100.))
    388                },
    389                _ => panic!("wrong expression type"),
    390            }
    391            match *q.expressions[1].kind_for_testing() {
    392                ExpressionKind::Width(Range::Max(ref w)) => {
    393                    assert!(*w == specified::Length::from_px(200.))
    394                },
    395                _ => panic!("wrong expression type"),
    396            }
    397        },
    398    );
    399 }
    400 
    401 #[test]
    402 fn test_mq_malformed_expressions() {
    403    fn check_malformed_expr(list: &MediaList, css: &str) {
    404        assert!(!list.media_queries.is_empty(), css.to_owned());
    405        for mq in &list.media_queries {
    406            assert!(mq.qualifier == Some(Qualifier::Not), css.to_owned());
    407            assert!(mq.media_type == MediaQueryType::All, css.to_owned());
    408            assert!(mq.expressions.is_empty(), css.to_owned());
    409        }
    410    }
    411 
    412    for rule in &[
    413        "@media (min-width: 100blah) and (max-width: 200px) { }",
    414        "@media screen and (height: 200px) { }",
    415        "@media (min-width: 30em foo bar) {}",
    416        "@media not {}",
    417        "@media not (min-width: 300px) {}",
    418        "@media , {}",
    419    ] {
    420        test_media_rule(rule, check_malformed_expr);
    421    }
    422 }
    423 
    424 #[test]
    425 fn test_matching_simple() {
    426    let device = Device::new(
    427        MediaType::screen(),
    428        Size2D::new(200.0, 100.0),
    429        Scale::new(1.0),
    430    );
    431 
    432    media_query_test(&device, "@media not all { a { color: red; } }", 0);
    433    media_query_test(&device, "@media not screen { a { color: red; } }", 0);
    434    media_query_test(&device, "@media not print { a { color: red; } }", 1);
    435 
    436    media_query_test(&device, "@media unknown { a { color: red; } }", 0);
    437    media_query_test(&device, "@media not unknown { a { color: red; } }", 1);
    438 
    439    media_query_test(&device, "@media { a { color: red; } }", 1);
    440    media_query_test(&device, "@media screen { a { color: red; } }", 1);
    441    media_query_test(&device, "@media print { a { color: red; } }", 0);
    442 }
    443 
    444 #[test]
    445 fn test_matching_width() {
    446    let device = Device::new(
    447        MediaType::screen(),
    448        Size2D::new(200.0, 100.0),
    449        Scale::new(1.0),
    450    );
    451 
    452    media_query_test(&device, "@media { a { color: red; } }", 1);
    453 
    454    media_query_test(&device, "@media (min-width: 50px) { a { color: red; } }", 1);
    455    media_query_test(
    456        &device,
    457        "@media (min-width: 150px) { a { color: red; } }",
    458        1,
    459    );
    460    media_query_test(
    461        &device,
    462        "@media (min-width: 300px) { a { color: red; } }",
    463        0,
    464    );
    465 
    466    media_query_test(
    467        &device,
    468        "@media screen and (min-width: 50px) { a { color: red; } }",
    469        1,
    470    );
    471    media_query_test(
    472        &device,
    473        "@media screen and (min-width: 150px) { a { color: red; } }",
    474        1,
    475    );
    476    media_query_test(
    477        &device,
    478        "@media screen and (min-width: 300px) { a { color: red; } }",
    479        0,
    480    );
    481 
    482    media_query_test(
    483        &device,
    484        "@media not screen and (min-width: 50px) { a { color: red; } }",
    485        0,
    486    );
    487    media_query_test(
    488        &device,
    489        "@media not screen and (min-width: 150px) { a { color: red; } }",
    490        0,
    491    );
    492    media_query_test(
    493        &device,
    494        "@media not screen and (min-width: 300px) { a { color: red; } }",
    495        1,
    496    );
    497 
    498    media_query_test(&device, "@media (max-width: 50px) { a { color: red; } }", 0);
    499    media_query_test(
    500        &device,
    501        "@media (max-width: 150px) { a { color: red; } }",
    502        0,
    503    );
    504    media_query_test(
    505        &device,
    506        "@media (max-width: 300px) { a { color: red; } }",
    507        1,
    508    );
    509 
    510    media_query_test(
    511        &device,
    512        "@media screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }",
    513        0,
    514    );
    515    media_query_test(
    516        &device,
    517        "@media screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }",
    518        0,
    519    );
    520    media_query_test(
    521        &device,
    522        "@media screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }",
    523        1,
    524    );
    525 
    526    media_query_test(
    527        &device,
    528        "@media not screen and (min-width: 50px) and (max-width: 100px) { a { color: red; } }",
    529        1,
    530    );
    531    media_query_test(
    532        &device,
    533        "@media not screen and (min-width: 250px) and (max-width: 300px) { a { color: red; } }",
    534        1,
    535    );
    536    media_query_test(
    537        &device,
    538        "@media not screen and (min-width: 50px) and (max-width: 250px) { a { color: red; } }",
    539        0,
    540    );
    541 
    542    media_query_test(
    543        &device,
    544        "@media not screen and (min-width: 3.1em) and (max-width: 6em) { a { color: red; } }",
    545        1,
    546    );
    547    media_query_test(
    548        &device,
    549        "@media not screen and (min-width: 16em) and (max-width: 19.75em) { a { color: red; } }",
    550        1,
    551    );
    552    media_query_test(
    553        &device,
    554        "@media not screen and (min-width: 3em) and (max-width: 250px) { a { color: red; } }",
    555        0,
    556    );
    557 }
    558 
    559 #[test]
    560 fn test_matching_invalid() {
    561    let device = Device::new(
    562        MediaType::screen(),
    563        Size2D::new(200.0, 100.0),
    564        Scale::new(1.0),
    565    );
    566 
    567    media_query_test(&device, "@media fridge { a { color: red; } }", 0);
    568    media_query_test(
    569        &device,
    570        "@media screen and (height: 100px) { a { color: red; } }",
    571        0,
    572    );
    573    media_query_test(
    574        &device,
    575        "@media not print and (width: 100) { a { color: red; } }",
    576        0,
    577    );
    578 }