tor-browser

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

stylesheets.rs (23146B)


      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 cssparser::{self, SourceLocation};
      6 use html5ever::Namespace as NsAtom;
      7 use parking_lot::RwLock;
      8 use selectors::attr::*;
      9 use selectors::parser::*;
     10 use servo_arc::Arc;
     11 use servo_config::prefs::{PrefValue, PREFS};
     12 use servo_url::ServoUrl;
     13 use std::borrow::ToOwned;
     14 use std::cell::RefCell;
     15 use std::sync::atomic::AtomicBool;
     16 use style::context::QuirksMode;
     17 use style::error_reporting::{ContextualParseError, ParseErrorReporter};
     18 use style::media_queries::MediaList;
     19 use style::properties::longhands::{self, animation_timing_function};
     20 use style::properties::{CSSWideKeyword, CustomDeclaration};
     21 use style::properties::{CustomDeclarationValue, Importance};
     22 use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
     23 use style::shared_lock::SharedRwLock;
     24 use style::stylesheets::keyframes_rule::{Keyframe, KeyframePercentage, KeyframeSelector};
     25 use style::stylesheets::{
     26    CssRule, CssRules, KeyframesRule, NamespaceRule, StyleRule, Stylesheet, StylesheetContents,
     27 };
     28 use style::stylesheets::{Namespaces, Origin};
     29 use style::values::computed::Percentage;
     30 use style::values::specified::TimingFunction;
     31 use style::values::specified::{LengthPercentageOrAuto, PositionComponent};
     32 use style::values::{CustomIdent, KeyframesName};
     33 use stylo_atoms::Atom;
     34 
     35 pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
     36 where
     37    I: IntoIterator<Item = (PropertyDeclaration, Importance)>,
     38 {
     39    let mut block = PropertyDeclarationBlock::new();
     40    for (d, i) in iterable {
     41        block.push(d, i);
     42    }
     43    block
     44 }
     45 
     46 #[test]
     47 fn test_parse_stylesheet() {
     48    let css = r"
     49        @namespace url(http://www.w3.org/1999/xhtml);
     50        /* FIXME: only if scripting is enabled */
     51        input[type=hidden i] {
     52            display: block !important;
     53            display: none !important;
     54            display: inline;
     55            --a: b !important;
     56            --a: inherit !important;
     57            --a: c;
     58        }
     59        html , body /**/ {
     60            display: none;
     61            display: block;
     62        }
     63        #d1 > .ok { background: blue; }
     64        @keyframes foo {
     65            from { width: 0% }
     66            to {
     67                width: 100%;
     68                width: 50% !important; /* !important not allowed here */
     69                animation-name: 'foo'; /* animation properties not allowed here */
     70                animation-timing-function: ease; /* … except animation-timing-function */
     71            }
     72        }";
     73    let url = ServoUrl::parse("about::test").unwrap();
     74    let lock = SharedRwLock::new();
     75    let media = Arc::new(lock.wrap(MediaList::empty()));
     76    let stylesheet = Stylesheet::from_str(
     77        css,
     78        url.clone(),
     79        Origin::UserAgent,
     80        media,
     81        lock,
     82        None,
     83        None,
     84        QuirksMode::NoQuirks,
     85        0,
     86    );
     87    let mut namespaces = Namespaces::default();
     88    namespaces.default = Some(ns!(html));
     89    let expected = Stylesheet {
     90        contents: StylesheetContents {
     91            origin: Origin::UserAgent,
     92            namespaces: RwLock::new(namespaces),
     93            url_data: RwLock::new(url),
     94            quirks_mode: QuirksMode::NoQuirks,
     95            rules: CssRules::new(
     96                vec![
     97                    CssRule::Namespace(Arc::new(stylesheet.shared_lock.wrap(NamespaceRule {
     98                        prefix: None,
     99                        url: NsAtom::from("http://www.w3.org/1999/xhtml"),
    100                        source_location: SourceLocation {
    101                            line: 1,
    102                            column: 19,
    103                        },
    104                    }))),
    105                    CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
    106                        selectors: SelectorList::from_vec(vec![Selector::from_vec(
    107                            vec![
    108                                Component::DefaultNamespace(NsAtom::from(
    109                                    "http://www.w3.org/1999/xhtml",
    110                                )),
    111                                Component::LocalName(LocalName {
    112                                    name: local_name!("input"),
    113                                    lower_name: local_name!("input"),
    114                                }),
    115                                Component::AttributeInNoNamespace {
    116                                    local_name: local_name!("type"),
    117                                    operator: AttrSelectorOperator::Equal,
    118                                    value: "hidden".to_owned(),
    119                                    case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive,
    120                                    never_matches: false,
    121                                },
    122                            ],
    123                            (0 << 20) + (1 << 10) + (1 << 0),
    124                        )]),
    125                        block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
    126                            (
    127                                PropertyDeclaration::Display(
    128                                    longhands::display::SpecifiedValue::None,
    129                                ),
    130                                Importance::Important,
    131                            ),
    132                            (
    133                                PropertyDeclaration::Custom(CustomDeclaration {
    134                                    name: Atom::from("a"),
    135                                    value: CustomDeclarationValue::CSSWideKeyword(
    136                                        CSSWideKeyword::Inherit,
    137                                    ),
    138                                }),
    139                                Importance::Important,
    140                            ),
    141                        ]))),
    142                        source_location: SourceLocation { line: 3, column: 9 },
    143                    }))),
    144                    CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
    145                        selectors: SelectorList::from_vec(vec![
    146                            Selector::from_vec(
    147                                vec![
    148                                    Component::DefaultNamespace(NsAtom::from(
    149                                        "http://www.w3.org/1999/xhtml",
    150                                    )),
    151                                    Component::LocalName(LocalName {
    152                                        name: local_name!("html"),
    153                                        lower_name: local_name!("html"),
    154                                    }),
    155                                ],
    156                                (0 << 20) + (0 << 10) + (1 << 0),
    157                            ),
    158                            Selector::from_vec(
    159                                vec![
    160                                    Component::DefaultNamespace(NsAtom::from(
    161                                        "http://www.w3.org/1999/xhtml",
    162                                    )),
    163                                    Component::LocalName(LocalName {
    164                                        name: local_name!("body"),
    165                                        lower_name: local_name!("body"),
    166                                    }),
    167                                ],
    168                                (0 << 20) + (0 << 10) + (1 << 0),
    169                            ),
    170                        ]),
    171                        block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![(
    172                            PropertyDeclaration::Display(longhands::display::SpecifiedValue::Block),
    173                            Importance::Normal,
    174                        )]))),
    175                        source_location: SourceLocation {
    176                            line: 11,
    177                            column: 9,
    178                        },
    179                    }))),
    180                    CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
    181                        selectors: SelectorList::from_vec(vec![Selector::from_vec(
    182                            vec![
    183                                Component::DefaultNamespace(NsAtom::from(
    184                                    "http://www.w3.org/1999/xhtml",
    185                                )),
    186                                Component::ID(Atom::from("d1")),
    187                                Component::Combinator(Combinator::Child),
    188                                Component::DefaultNamespace(NsAtom::from(
    189                                    "http://www.w3.org/1999/xhtml",
    190                                )),
    191                                Component::Class(Atom::from("ok")),
    192                            ],
    193                            (1 << 20) + (1 << 10) + (0 << 0),
    194                        )]),
    195                        block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
    196                            (
    197                                PropertyDeclaration::BackgroundColor(
    198                                    longhands::background_color::SpecifiedValue::Numeric {
    199                                        authored: Some("blue".to_owned().into_boxed_str()),
    200                                        parsed: cssparser::RGBA::new(0, 0, 255, 255),
    201                                    },
    202                                ),
    203                                Importance::Normal,
    204                            ),
    205                            (
    206                                PropertyDeclaration::BackgroundPositionX(
    207                                    longhands::background_position_x::SpecifiedValue(vec![
    208                                        PositionComponent::zero(),
    209                                    ]),
    210                                ),
    211                                Importance::Normal,
    212                            ),
    213                            (
    214                                PropertyDeclaration::BackgroundPositionY(
    215                                    longhands::background_position_y::SpecifiedValue(vec![
    216                                        PositionComponent::zero(),
    217                                    ]),
    218                                ),
    219                                Importance::Normal,
    220                            ),
    221                            (
    222                                PropertyDeclaration::BackgroundRepeat(
    223                                    longhands::background_repeat::SpecifiedValue(
    224                                        vec![longhands::background_repeat::single_value
    225                                                       ::get_initial_specified_value()],
    226                                    ),
    227                                ),
    228                                Importance::Normal,
    229                            ),
    230                            (
    231                                PropertyDeclaration::BackgroundAttachment(
    232                                    longhands::background_attachment::SpecifiedValue(
    233                                        vec![longhands::background_attachment::single_value
    234                                                       ::get_initial_specified_value()],
    235                                    ),
    236                                ),
    237                                Importance::Normal,
    238                            ),
    239                            (
    240                                PropertyDeclaration::BackgroundImage(
    241                                    longhands::background_image::SpecifiedValue(
    242                                        vec![longhands::background_image::single_value
    243                                                       ::get_initial_specified_value()],
    244                                    ),
    245                                ),
    246                                Importance::Normal,
    247                            ),
    248                            (
    249                                PropertyDeclaration::BackgroundSize(
    250                                    longhands::background_size::SpecifiedValue(
    251                                        vec![longhands::background_size::single_value
    252                                                       ::get_initial_specified_value()],
    253                                    ),
    254                                ),
    255                                Importance::Normal,
    256                            ),
    257                            (
    258                                PropertyDeclaration::BackgroundOrigin(
    259                                    longhands::background_origin::SpecifiedValue(
    260                                        vec![longhands::background_origin::single_value
    261                                                       ::get_initial_specified_value()],
    262                                    ),
    263                                ),
    264                                Importance::Normal,
    265                            ),
    266                            (
    267                                PropertyDeclaration::BackgroundClip(
    268                                    longhands::background_clip::SpecifiedValue(
    269                                        vec![longhands::background_clip::single_value
    270                                                       ::get_initial_specified_value()],
    271                                    ),
    272                                ),
    273                                Importance::Normal,
    274                            ),
    275                        ]))),
    276                        source_location: SourceLocation {
    277                            line: 15,
    278                            column: 9,
    279                        },
    280                    }))),
    281                    CssRule::Keyframes(Arc::new(stylesheet.shared_lock.wrap(KeyframesRule {
    282                        name: KeyframesName::Ident(CustomIdent("foo".into())),
    283                        keyframes: vec![
    284                            Arc::new(stylesheet.shared_lock.wrap(Keyframe {
    285                                selector: KeyframeSelector::new_for_unit_testing(vec![
    286                                    KeyframePercentage::new(0.),
    287                                ]),
    288                                block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![(
    289                                    PropertyDeclaration::Width(LengthPercentageOrAuto::Percentage(
    290                                        Percentage(0.),
    291                                    )),
    292                                    Importance::Normal,
    293                                )]))),
    294                                source_location: SourceLocation {
    295                                    line: 17,
    296                                    column: 13,
    297                                },
    298                            })),
    299                            Arc::new(stylesheet.shared_lock.wrap(Keyframe {
    300                                selector: KeyframeSelector::new_for_unit_testing(vec![
    301                                    KeyframePercentage::new(1.),
    302                                ]),
    303                                block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
    304                                    (
    305                                        PropertyDeclaration::Width(
    306                                            LengthPercentageOrAuto::Percentage(Percentage(1.)),
    307                                        ),
    308                                        Importance::Normal,
    309                                    ),
    310                                    (
    311                                        PropertyDeclaration::AnimationTimingFunction(
    312                                            animation_timing_function::SpecifiedValue(vec![
    313                                                TimingFunction::ease(),
    314                                            ]),
    315                                        ),
    316                                        Importance::Normal,
    317                                    ),
    318                                ]))),
    319                                source_location: SourceLocation {
    320                                    line: 18,
    321                                    column: 13,
    322                                },
    323                            })),
    324                        ],
    325                        vendor_prefix: None,
    326                        source_location: SourceLocation {
    327                            line: 16,
    328                            column: 19,
    329                        },
    330                    }))),
    331                ],
    332                &stylesheet.shared_lock,
    333            ),
    334            source_map_url: RwLock::new(None),
    335            source_url: RwLock::new(None),
    336        },
    337        media: Arc::new(stylesheet.shared_lock.wrap(MediaList::empty())),
    338        shared_lock: stylesheet.shared_lock.clone(),
    339        disabled: AtomicBool::new(false),
    340    };
    341 
    342    assert_eq!(format!("{:#?}", stylesheet), format!("{:#?}", expected));
    343 }
    344 
    345 #[derive(Debug)]
    346 struct CSSError {
    347    pub url: ServoUrl,
    348    pub line: u32,
    349    pub column: u32,
    350    pub message: String,
    351 }
    352 
    353 struct TestingErrorReporter {
    354    errors: RefCell<Vec<CSSError>>,
    355 }
    356 
    357 impl TestingErrorReporter {
    358    pub fn new() -> Self {
    359        TestingErrorReporter {
    360            errors: RefCell::new(Vec::new()),
    361        }
    362    }
    363 
    364    fn assert_messages_contain(&self, expected_errors: &[(u32, u32, &str)]) {
    365        let errors = self.errors.borrow();
    366        for (i, (error, &(line, column, message))) in errors.iter().zip(expected_errors).enumerate()
    367        {
    368            assert_eq!(
    369                (error.line, error.column),
    370                (line, column),
    371                "line/column numbers of the {}th error: {:?}",
    372                i + 1,
    373                error.message
    374            );
    375            assert!(
    376                error.message.contains(message),
    377                "{:?} does not contain {:?}",
    378                error.message,
    379                message
    380            );
    381        }
    382        if errors.len() < expected_errors.len() {
    383            panic!("Missing errors: {:#?}", &expected_errors[errors.len()..]);
    384        }
    385        if errors.len() > expected_errors.len() {
    386            panic!("Extra errors: {:#?}", &errors[expected_errors.len()..]);
    387        }
    388    }
    389 }
    390 
    391 impl ParseErrorReporter for TestingErrorReporter {
    392    fn report_error(&self, url: &ServoUrl, location: SourceLocation, error: ContextualParseError) {
    393        self.errors.borrow_mut().push(CSSError {
    394            url: url.clone(),
    395            line: location.line,
    396            column: location.column,
    397            message: error.to_string(),
    398        })
    399    }
    400 }
    401 
    402 #[test]
    403 fn test_report_error_stylesheet() {
    404    PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true));
    405    let css = r"
    406    div {
    407        background-color: red;
    408        display: invalid;
    409        background-image: linear-gradient(0deg, black, invalid, transparent);
    410        invalid: true;
    411    }
    412    @media (min-width: 10px invalid 1000px) {}
    413    @font-face { src: url(), invalid, url(); }
    414    @counter-style foo { symbols: a 0invalid b }
    415    @font-feature-values Sans Sans { @foo {} @swash { foo: 1 invalid 2 } }
    416    @invalid;
    417    @media screen { @invalid; }
    418    @supports (color: green) and invalid and (margin: 0) {}
    419    @keyframes foo { from invalid {} to { margin: 0 invalid 0; } }
    420    @viewport { width: 320px invalid auto; }
    421    ";
    422    let url = ServoUrl::parse("about::test").unwrap();
    423    let error_reporter = TestingErrorReporter::new();
    424 
    425    let lock = SharedRwLock::new();
    426    let media = Arc::new(lock.wrap(MediaList::empty()));
    427    Stylesheet::from_str(
    428        css,
    429        url.clone(),
    430        Origin::UserAgent,
    431        media,
    432        lock,
    433        None,
    434        Some(&error_reporter),
    435        QuirksMode::NoQuirks,
    436        5,
    437    );
    438 
    439    error_reporter.assert_messages_contain(&[
    440        (
    441            8,
    442            18,
    443            "Unsupported property declaration: 'display: invalid;'",
    444        ),
    445        (
    446            9,
    447            27,
    448            "Unsupported property declaration: 'background-image:",
    449        ), // FIXME: column should be around 56
    450        (10, 17, "Unsupported property declaration: 'invalid: true;'"),
    451        (12, 28, "Invalid media rule"),
    452        (13, 30, "Unsupported @font-face descriptor declaration"),
    453        // When @counter-style is supported, this should be replaced with two errors
    454        (14, 19, "Invalid rule: '@counter-style "),
    455        // When @font-feature-values is supported, this should be replaced with two errors
    456        (15, 25, "Invalid rule: '@font-feature-values "),
    457        (16, 13, "Invalid rule: '@invalid'"),
    458        (17, 29, "Invalid rule: '@invalid'"),
    459        (18, 34, "Invalid rule: '@supports "),
    460        (19, 26, "Invalid keyframe rule: 'from invalid '"),
    461        (
    462            19,
    463            52,
    464            "Unsupported keyframe property declaration: 'margin: 0 invalid 0;'",
    465        ),
    466        (
    467            20,
    468            29,
    469            "Unsupported @viewport descriptor declaration: 'width: 320px invalid auto;'",
    470        ),
    471    ]);
    472 
    473    assert_eq!(error_reporter.errors.borrow()[0].url, url);
    474 }
    475 
    476 #[test]
    477 fn test_no_report_unrecognized_vendor_properties() {
    478    let css = r"
    479    div {
    480        -o-background-color: red;
    481        _background-color: red;
    482        -moz-background-color: red;
    483    }
    484    ";
    485    let url = ServoUrl::parse("about::test").unwrap();
    486    let error_reporter = TestingErrorReporter::new();
    487 
    488    let lock = SharedRwLock::new();
    489    let media = Arc::new(lock.wrap(MediaList::empty()));
    490    Stylesheet::from_str(
    491        css,
    492        url,
    493        Origin::UserAgent,
    494        media,
    495        lock,
    496        None,
    497        Some(&error_reporter),
    498        QuirksMode::NoQuirks,
    499        0,
    500    );
    501 
    502    error_reporter.assert_messages_contain(&[(
    503        4,
    504        31,
    505        "Unsupported property declaration: '-moz-background-color: red;'",
    506    )]);
    507 }
    508 
    509 #[test]
    510 fn test_source_map_url() {
    511    let tests = vec![
    512        ("", None),
    513        (
    514            "/*# sourceMappingURL=something */",
    515            Some("something".to_string()),
    516        ),
    517    ];
    518 
    519    for test in tests {
    520        let url = ServoUrl::parse("about::test").unwrap();
    521        let lock = SharedRwLock::new();
    522        let media = Arc::new(lock.wrap(MediaList::empty()));
    523        let stylesheet = Stylesheet::from_str(
    524            test.0,
    525            url.clone(),
    526            Origin::UserAgent,
    527            media,
    528            lock,
    529            None,
    530            None,
    531            QuirksMode::NoQuirks,
    532            0,
    533        );
    534        let url_opt = stylesheet.contents.source_map_url.read();
    535        assert_eq!(*url_opt, test.1);
    536    }
    537 }
    538 
    539 #[test]
    540 fn test_source_url() {
    541    let tests = vec![
    542        ("", None),
    543        ("/*# sourceURL=something */", Some("something".to_string())),
    544    ];
    545 
    546    for test in tests {
    547        let url = ServoUrl::parse("about::test").unwrap();
    548        let lock = SharedRwLock::new();
    549        let media = Arc::new(lock.wrap(MediaList::empty()));
    550        let stylesheet = Stylesheet::from_str(
    551            test.0,
    552            url.clone(),
    553            Origin::UserAgent,
    554            media,
    555            lock,
    556            None,
    557            None,
    558            QuirksMode::NoQuirks,
    559            0,
    560        );
    561        let url_opt = stylesheet.contents.source_url.read();
    562        assert_eq!(*url_opt, test.1);
    563    }
    564 }