style: Add a CSS error to the console when using non-featureless :host selectors

(which would never match by definition).

Differential Revision: https://phabricator.services.mozilla.com/D111610
This commit is contained in:
Oriol Brufau 2023-05-17 00:28:18 +02:00
parent 7425f2d922
commit 90a2687545
3 changed files with 51 additions and 3 deletions

View file

@ -859,7 +859,7 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> {
#[inline]
pub(crate) fn is_featureless_host_selector(&mut self) -> bool {
self.selector_length() > 0 &&
self.all(|component| matches!(*component, Component::Host(..))) &&
self.all(|component| component.is_host()) &&
self.next_sequence().is_none()
}
@ -1123,10 +1123,17 @@ pub enum Component<Impl: SelectorImpl> {
impl<Impl: SelectorImpl> Component<Impl> {
/// Returns true if this is a combinator.
#[inline]
pub fn is_combinator(&self) -> bool {
matches!(*self, Component::Combinator(_))
}
/// Returns true if this is a :host() selector.
#[inline]
pub fn is_host(&self) -> bool {
matches!(*self, Component::Host(..))
}
/// Returns the value as a combinator if applicable, None otherwise.
pub fn as_combinator(&self) -> Option<Combinator> {
match *self {

View file

@ -54,6 +54,8 @@ pub enum ContextualParseError<'a> {
InvalidMediaRule(&'a str, ParseError<'a>),
/// A value was not recognized.
UnsupportedValue(&'a str, ParseError<'a>),
/// A never-matching `:host` selector was found.
NeverMatchingHostSelector(String),
}
impl<'a> fmt::Display for ContextualParseError<'a> {
@ -210,6 +212,9 @@ impl<'a> fmt::Display for ContextualParseError<'a> {
parse_error_to_str(err, f)
},
ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
ContextualParseError::NeverMatchingHostSelector(ref selector) => {
write!(f, ":host selector is not featureless: {}", selector)
}
}
}
}

View file

@ -53,7 +53,7 @@ pub struct TopLevelRuleParser<'a> {
/// This won't contain any namespaces, and only nested parsers created with
/// `ParserContext::new_with_rule_type` will.
pub context: ParserContext<'a>,
/// The current stajkj/te of the parser.
/// The current state of the parser.
pub state: State,
/// Whether we have tried to parse was invalid due to being in the wrong
/// place (e.g. an @import rule was found while in the `Body` state). Reset
@ -587,6 +587,38 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
}
}
#[inline(never)]
fn check_for_useless_selector(
input: &mut Parser,
context: &ParserContext,
selectors: &SelectorList<SelectorImpl>,
) {
use cssparser::ToCss;
'selector_loop: for selector in selectors.0.iter() {
let mut current = selector.iter();
loop {
let mut found_host = false;
let mut found_non_host = false;
for component in &mut current {
if component.is_host() {
found_host = true;
} else {
found_non_host = true;
}
if found_host && found_non_host {
let location = input.current_source_location();
context.log_css_error(location, ContextualParseError::NeverMatchingHostSelector(selector.to_css_string()));
continue 'selector_loop;
}
}
if current.next_sequence().is_none() {
break;
}
}
}
}
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
type Prelude = SelectorList<SelectorImpl>;
type QualifiedRule = CssRule;
@ -601,7 +633,11 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
namespaces: self.namespaces,
url_data: Some(self.context.url_data),
};
SelectorList::parse(&selector_parser, input)
let selectors = SelectorList::parse(&selector_parser, input)?;
if self.context.error_reporting_enabled() {
check_for_useless_selector(input, &self.context, &selectors);
}
Ok(selectors)
}
fn parse_block<'t>(