tor-browser

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

error_reporting.rs (20719B)


      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 //! Types used to report parsing errors.
      6 
      7 #![deny(missing_docs)]
      8 
      9 use crate::selector_parser::SelectorImpl;
     10 use crate::stylesheets::UrlExtraData;
     11 use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token};
     12 use selectors::parser::{Combinator, Component, RelativeSelector, Selector};
     13 use selectors::visitor::{SelectorListKind, SelectorVisitor};
     14 use selectors::SelectorList;
     15 use std::fmt;
     16 use style_traits::ParseError;
     17 
     18 /// Errors that can be encountered while parsing CSS.
     19 #[derive(Debug)]
     20 pub enum ContextualParseError<'a> {
     21    /// A property declaration was not recognized.
     22    UnsupportedPropertyDeclaration(&'a str, ParseError<'a>, &'a [SelectorList<SelectorImpl>]),
     23    /// A property descriptor was not recognized.
     24    UnsupportedPropertyDescriptor(&'a str, ParseError<'a>),
     25    /// A font face descriptor was not recognized.
     26    UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>),
     27    /// A font feature values descriptor was not recognized.
     28    UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>),
     29    /// A font palette values descriptor was not recognized.
     30    UnsupportedFontPaletteValuesDescriptor(&'a str, ParseError<'a>),
     31    /// A keyframe rule was not valid.
     32    InvalidKeyframeRule(&'a str, ParseError<'a>),
     33    /// A font feature values rule was not valid.
     34    InvalidFontFeatureValuesRule(&'a str, ParseError<'a>),
     35    /// A rule was invalid for some reason.
     36    InvalidRule(&'a str, ParseError<'a>),
     37    /// A rule was not recognized.
     38    UnsupportedRule(&'a str, ParseError<'a>),
     39    /// A viewport descriptor declaration was not recognized.
     40    UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>),
     41    /// A counter style descriptor declaration was not recognized.
     42    UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError<'a>),
     43    /// A counter style rule had no symbols.
     44    InvalidCounterStyleWithoutSymbols(String),
     45    /// A counter style rule had less than two symbols.
     46    InvalidCounterStyleNotEnoughSymbols(String),
     47    /// A counter style rule did not have additive-symbols.
     48    InvalidCounterStyleWithoutAdditiveSymbols,
     49    /// A counter style rule had extends with symbols.
     50    InvalidCounterStyleExtendsWithSymbols,
     51    /// A counter style rule had extends with additive-symbols.
     52    InvalidCounterStyleExtendsWithAdditiveSymbols,
     53    /// A media rule was invalid for some reason.
     54    InvalidMediaRule(&'a str, ParseError<'a>),
     55    /// A value was not recognized.
     56    UnsupportedValue(&'a str, ParseError<'a>),
     57    /// A never-matching `:host` selector was found.
     58    NeverMatchingHostSelector(String),
     59 }
     60 
     61 impl<'a> fmt::Display for ContextualParseError<'a> {
     62    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     63        fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result {
     64            match *t {
     65                Token::Ident(ref i) => write!(f, "identifier {}", i),
     66                Token::AtKeyword(ref kw) => write!(f, "keyword @{}", kw),
     67                Token::Hash(ref h) => write!(f, "hash #{}", h),
     68                Token::IDHash(ref h) => write!(f, "id selector #{}", h),
     69                Token::QuotedString(ref s) => write!(f, "quoted string \"{}\"", s),
     70                Token::UnquotedUrl(ref u) => write!(f, "url {}", u),
     71                Token::Delim(ref d) => write!(f, "delimiter {}", d),
     72                Token::Number {
     73                    int_value: Some(i), ..
     74                } => write!(f, "number {}", i),
     75                Token::Number { value, .. } => write!(f, "number {}", value),
     76                Token::Percentage {
     77                    int_value: Some(i), ..
     78                } => write!(f, "percentage {}", i),
     79                Token::Percentage { unit_value, .. } => {
     80                    write!(f, "percentage {}", unit_value * 100.)
     81                },
     82                Token::Dimension {
     83                    value, ref unit, ..
     84                } => write!(f, "dimension {}{}", value, unit),
     85                Token::WhiteSpace(_) => write!(f, "whitespace"),
     86                Token::Comment(_) => write!(f, "comment"),
     87                Token::Colon => write!(f, "colon (:)"),
     88                Token::Semicolon => write!(f, "semicolon (;)"),
     89                Token::Comma => write!(f, "comma (,)"),
     90                Token::IncludeMatch => write!(f, "include match (~=)"),
     91                Token::DashMatch => write!(f, "dash match (|=)"),
     92                Token::PrefixMatch => write!(f, "prefix match (^=)"),
     93                Token::SuffixMatch => write!(f, "suffix match ($=)"),
     94                Token::SubstringMatch => write!(f, "substring match (*=)"),
     95                Token::CDO => write!(f, "CDO (<!--)"),
     96                Token::CDC => write!(f, "CDC (-->)"),
     97                Token::Function(ref name) => write!(f, "function {}", name),
     98                Token::ParenthesisBlock => write!(f, "parenthesis ("),
     99                Token::SquareBracketBlock => write!(f, "square bracket ["),
    100                Token::CurlyBracketBlock => write!(f, "curly bracket {{"),
    101                Token::BadUrl(ref _u) => write!(f, "bad url parse error"),
    102                Token::BadString(ref _s) => write!(f, "bad string parse error"),
    103                Token::CloseParenthesis => write!(f, "unmatched close parenthesis"),
    104                Token::CloseSquareBracket => write!(f, "unmatched close square bracket"),
    105                Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"),
    106            }
    107        }
    108 
    109        fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {
    110            match err.kind {
    111                ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => {
    112                    write!(f, "found unexpected ")?;
    113                    token_to_str(t, f)
    114                },
    115                ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => {
    116                    write!(f, "unexpected end of input")
    117                },
    118                ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => {
    119                    write!(f, "@ rule invalid: {}", i)
    120                },
    121                ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => {
    122                    write!(f, "@ rule invalid")
    123                },
    124                ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => {
    125                    write!(f, "qualified rule invalid")
    126                },
    127                ParseErrorKind::Custom(ref err) => write!(f, "{:?}", err),
    128            }
    129        }
    130 
    131        match *self {
    132            ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => {
    133                write!(f, "Unsupported property declaration: '{}', ", decl)?;
    134                parse_error_to_str(err, f)
    135            },
    136            ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => {
    137                write!(
    138                    f,
    139                    "Unsupported @property descriptor declaration: '{}', ",
    140                    decl
    141                )?;
    142                parse_error_to_str(err, f)
    143            },
    144            ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => {
    145                write!(
    146                    f,
    147                    "Unsupported @font-face descriptor declaration: '{}', ",
    148                    decl
    149                )?;
    150                parse_error_to_str(err, f)
    151            },
    152            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {
    153                write!(
    154                    f,
    155                    "Unsupported @font-feature-values descriptor declaration: '{}', ",
    156                    decl
    157                )?;
    158                parse_error_to_str(err, f)
    159            },
    160            ContextualParseError::UnsupportedFontPaletteValuesDescriptor(decl, ref err) => {
    161                write!(
    162                    f,
    163                    "Unsupported @font-palette-values descriptor declaration: '{}', ",
    164                    decl
    165                )?;
    166                parse_error_to_str(err, f)
    167            },
    168            ContextualParseError::InvalidKeyframeRule(rule, ref err) => {
    169                write!(f, "Invalid keyframe rule: '{}', ", rule)?;
    170                parse_error_to_str(err, f)
    171            },
    172            ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {
    173                write!(f, "Invalid font feature value rule: '{}', ", rule)?;
    174                parse_error_to_str(err, f)
    175            },
    176            ContextualParseError::InvalidRule(rule, ref err) => {
    177                write!(f, "Invalid rule: '{}', ", rule)?;
    178                parse_error_to_str(err, f)
    179            },
    180            ContextualParseError::UnsupportedRule(rule, ref err) => {
    181                write!(f, "Unsupported rule: '{}', ", rule)?;
    182                parse_error_to_str(err, f)
    183            },
    184            ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {
    185                write!(
    186                    f,
    187                    "Unsupported @viewport descriptor declaration: '{}', ",
    188                    decl
    189                )?;
    190                parse_error_to_str(err, f)
    191            },
    192            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {
    193                write!(
    194                    f,
    195                    "Unsupported @counter-style descriptor declaration: '{}', ",
    196                    decl
    197                )?;
    198                parse_error_to_str(err, f)
    199            },
    200            ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!(
    201                f,
    202                "Invalid @counter-style rule: 'system: {}' without 'symbols'",
    203                system
    204            ),
    205            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!(
    206                f,
    207                "Invalid @counter-style rule: 'system: {}' less than two 'symbols'",
    208                system
    209            ),
    210            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!(
    211                f,
    212                "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'"
    213            ),
    214            ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!(
    215                f,
    216                "Invalid @counter-style rule: 'system: extends …' with 'symbols'"
    217            ),
    218            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!(
    219                f,
    220                "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'"
    221            ),
    222            ContextualParseError::InvalidMediaRule(media_rule, ref err) => {
    223                write!(f, "Invalid media rule: {}, ", media_rule)?;
    224                parse_error_to_str(err, f)
    225            },
    226            ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
    227            ContextualParseError::NeverMatchingHostSelector(ref selector) => {
    228                write!(f, ":host selector is not featureless: {}", selector)
    229            },
    230        }
    231    }
    232 }
    233 
    234 /// A generic trait for an error reporter.
    235 pub trait ParseErrorReporter {
    236    /// Called when the style engine detects an error.
    237    ///
    238    /// Returns the current input being parsed, the source location it was
    239    /// reported from, and a message.
    240    fn report_error(
    241        &self,
    242        url: &UrlExtraData,
    243        location: SourceLocation,
    244        error: ContextualParseError,
    245    );
    246 }
    247 
    248 /// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log)
    249 /// at `info` level.
    250 ///
    251 /// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info`
    252 /// environment variable.
    253 /// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).)
    254 #[cfg(feature = "servo")]
    255 pub struct RustLogReporter;
    256 
    257 #[cfg(feature = "servo")]
    258 impl ParseErrorReporter for RustLogReporter {
    259    fn report_error(
    260        &self,
    261        url: &UrlExtraData,
    262        location: SourceLocation,
    263        error: ContextualParseError,
    264    ) {
    265        if log_enabled!(log::Level::Info) {
    266            info!(
    267                "Url:\t{}\n{}:{} {}",
    268                url.as_str(),
    269                location.line,
    270                location.column,
    271                error
    272            )
    273        }
    274    }
    275 }
    276 
    277 /// Any warning a selector may generate.
    278 /// TODO(dshin): Bug 1860634 - Merge with never matching host selector warning, which is part of the rule parser.
    279 #[repr(u8)]
    280 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    281 pub enum SelectorWarningKind {
    282    /// Relative Selector with not enough constraint, either outside or inside the selector. e.g. `*:has(.a)`, `.a:has(*)`.
    283    /// May cause expensive invalidations for every element inserted and/or removed.
    284    UnconstraintedRelativeSelector,
    285    /// `:scope` can have 3 meanings, but in all cases, the relationship is defined strictly by an ancestor-descendant
    286    /// relationship. This means that any presence of sibling selectors to its right would make it never match.
    287    SiblingCombinatorAfterScopeSelector,
    288 }
    289 
    290 impl SelectorWarningKind {
    291    /// Get all warnings for this selector.
    292    pub fn from_selector(selector: &Selector<SelectorImpl>) -> Vec<Self> {
    293        let mut result = vec![];
    294        if UnconstrainedRelativeSelectorVisitor::has_warning(selector, 0, false) {
    295            result.push(SelectorWarningKind::UnconstraintedRelativeSelector);
    296        }
    297        if SiblingCombinatorAfterScopeSelectorVisitor::has_warning(selector) {
    298            result.push(SelectorWarningKind::SiblingCombinatorAfterScopeSelector);
    299        }
    300        result
    301    }
    302 }
    303 
    304 /// Per-compound state for finding unconstrained relative selectors.
    305 struct PerCompoundState {
    306    /// Is there a relative selector in this compound?
    307    relative_selector_found: bool,
    308    /// Is this compound constrained in any way?
    309    constrained: bool,
    310    /// Nested below, or inside relative selector?
    311    in_relative_selector: bool,
    312 }
    313 
    314 impl PerCompoundState {
    315    fn new(in_relative_selector: bool) -> Self {
    316        Self {
    317            relative_selector_found: false,
    318            constrained: false,
    319            in_relative_selector,
    320        }
    321    }
    322 }
    323 
    324 /// Visitor to check if there's any unconstrained relative selector.
    325 struct UnconstrainedRelativeSelectorVisitor {
    326    compound_state: PerCompoundState,
    327 }
    328 
    329 impl UnconstrainedRelativeSelectorVisitor {
    330    fn new(in_relative_selector: bool) -> Self {
    331        Self {
    332            compound_state: PerCompoundState::new(in_relative_selector),
    333        }
    334    }
    335 
    336    fn has_warning(
    337        selector: &Selector<SelectorImpl>,
    338        offset: usize,
    339        in_relative_selector: bool,
    340    ) -> bool {
    341        let relative_selector = matches!(
    342            selector.iter_raw_parse_order_from(0).next().unwrap(),
    343            Component::RelativeSelectorAnchor
    344        );
    345        debug_assert!(
    346            !relative_selector || offset == 0,
    347            "Checking relative selector from non-rightmost?"
    348        );
    349        let mut visitor = Self::new(in_relative_selector);
    350        let mut iter = if relative_selector {
    351            selector.iter_skip_relative_selector_anchor()
    352        } else {
    353            selector.iter_from(offset)
    354        };
    355        loop {
    356            visitor.compound_state = PerCompoundState::new(in_relative_selector);
    357 
    358            for s in &mut iter {
    359                s.visit(&mut visitor);
    360            }
    361 
    362            if (visitor.compound_state.relative_selector_found
    363                || visitor.compound_state.in_relative_selector)
    364                && !visitor.compound_state.constrained
    365            {
    366                return true;
    367            }
    368 
    369            if iter.next_sequence().is_none() {
    370                break;
    371            }
    372        }
    373        false
    374    }
    375 }
    376 
    377 impl SelectorVisitor for UnconstrainedRelativeSelectorVisitor {
    378    type Impl = SelectorImpl;
    379 
    380    fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {
    381        match c {
    382            // Deferred to visit_selector_list
    383            Component::Is(..)
    384            | Component::Where(..)
    385            | Component::Negation(..)
    386            | Component::Has(..) => (),
    387            Component::ExplicitUniversalType => (),
    388            _ => self.compound_state.constrained |= true,
    389        };
    390        true
    391    }
    392 
    393    fn visit_selector_list(
    394        &mut self,
    395        _list_kind: SelectorListKind,
    396        list: &[Selector<Self::Impl>],
    397    ) -> bool {
    398        let mut all_constrained = true;
    399        for s in list {
    400            let mut offset = 0;
    401            // First, check the rightmost compound for constraint at this level.
    402            if !self.compound_state.in_relative_selector {
    403                let mut nested = Self::new(false);
    404                let mut iter = s.iter();
    405                loop {
    406                    for c in &mut iter {
    407                        c.visit(&mut nested);
    408                        offset += 1;
    409                    }
    410 
    411                    let c = iter.next_sequence();
    412                    offset += 1;
    413                    if c.map_or(true, |c| !c.is_pseudo_element()) {
    414                        break;
    415                    }
    416                }
    417                // Every single selector in the list must be constrained.
    418                all_constrained &= nested.compound_state.constrained;
    419            }
    420 
    421            if offset >= s.len() {
    422                continue;
    423            }
    424 
    425            // Then, recurse in to check at the deeper level.
    426            if Self::has_warning(s, offset, self.compound_state.in_relative_selector) {
    427                self.compound_state.constrained = false;
    428                if !self.compound_state.in_relative_selector {
    429                    self.compound_state.relative_selector_found = true;
    430                }
    431                return false;
    432            }
    433        }
    434        self.compound_state.constrained |= all_constrained;
    435        true
    436    }
    437 
    438    fn visit_relative_selector_list(&mut self, list: &[RelativeSelector<Self::Impl>]) -> bool {
    439        debug_assert!(
    440            !self.compound_state.in_relative_selector,
    441            "Nested relative selector"
    442        );
    443        self.compound_state.relative_selector_found = true;
    444 
    445        for rs in list {
    446            // If the inside is unconstrained, we are unconstrained no matter what.
    447            if Self::has_warning(&rs.selector, 0, true) {
    448                self.compound_state.constrained = false;
    449                return false;
    450            }
    451        }
    452        true
    453    }
    454 }
    455 
    456 struct SiblingCombinatorAfterScopeSelectorVisitor {
    457    right_combinator_is_sibling: bool,
    458    found: bool,
    459 }
    460 
    461 impl SiblingCombinatorAfterScopeSelectorVisitor {
    462    fn new(right_combinator_is_sibling: bool) -> Self {
    463        Self {
    464            right_combinator_is_sibling,
    465            found: false,
    466        }
    467    }
    468    fn has_warning(selector: &Selector<SelectorImpl>) -> bool {
    469        if !selector.has_scope_selector() {
    470            return false;
    471        }
    472        let visitor = SiblingCombinatorAfterScopeSelectorVisitor::new(false);
    473        visitor.find_never_matching_scope_selector(selector)
    474    }
    475 
    476    fn find_never_matching_scope_selector(mut self, selector: &Selector<SelectorImpl>) -> bool {
    477        selector.visit(&mut self);
    478        self.found
    479    }
    480 }
    481 
    482 impl SelectorVisitor for SiblingCombinatorAfterScopeSelectorVisitor {
    483    type Impl = SelectorImpl;
    484 
    485    fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {
    486        if !matches!(c, Component::Scope | Component::ImplicitScope) {
    487            return true;
    488        }
    489        // e.g. `:scope ~ .a` will never match.
    490        if self.right_combinator_is_sibling {
    491            self.found = true;
    492        }
    493        true
    494    }
    495 
    496    fn visit_selector_list(
    497        &mut self,
    498        _list_kind: SelectorListKind,
    499        list: &[Selector<Self::Impl>],
    500    ) -> bool {
    501        for s in list {
    502            let list_visitor = Self::new(self.right_combinator_is_sibling);
    503            self.found |= list_visitor.find_never_matching_scope_selector(s);
    504        }
    505        true
    506    }
    507 
    508    fn visit_complex_selector(&mut self, combinator_to_right: Option<Combinator>) -> bool {
    509        if let Some(c) = combinator_to_right {
    510            // Subject compounds' state is determined by the outer visitor. e.g: When there's `:is(.a .b) ~ .c`,
    511            // the inner visitor is assumed to be constructed with right_combinator_is_sibling == true.
    512            self.right_combinator_is_sibling = c.is_sibling();
    513        }
    514        true
    515    }
    516 
    517    // It's harder to discern if use of :scope <sibling-combinator> is invalid - at least for now, defer.
    518 }