mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
style: Fix parsing of :is() and :where() to account for constraints from parent selectors.
Differential Revision: https://phabricator.services.mozilla.com/D75856
This commit is contained in:
parent
7cbc963fc7
commit
a457a2261b
1 changed files with 99 additions and 74 deletions
|
@ -104,41 +104,59 @@ bitflags! {
|
||||||
/// disallowed. If this flag is set, `AFTER_PSEUDO_ELEMENT` must be set
|
/// disallowed. If this flag is set, `AFTER_PSEUDO_ELEMENT` must be set
|
||||||
/// as well.
|
/// as well.
|
||||||
const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4;
|
const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4;
|
||||||
|
|
||||||
/// Whether we are after any of the pseudo-like things.
|
/// Whether we are after any of the pseudo-like things.
|
||||||
const AFTER_PSEUDO = Self::AFTER_PART.bits | Self::AFTER_SLOTTED.bits | Self::AFTER_PSEUDO_ELEMENT.bits;
|
const AFTER_PSEUDO = Self::AFTER_PART.bits | Self::AFTER_SLOTTED.bits | Self::AFTER_PSEUDO_ELEMENT.bits;
|
||||||
|
|
||||||
|
/// Whether we explicitly disallow combinators.
|
||||||
|
const DISALLOW_COMBINATORS = 1 << 5;
|
||||||
|
|
||||||
|
/// Whether we explicitly disallow pseudo-element-like things.
|
||||||
|
const DISALLOW_PSEUDOS = 1 << 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectorParsingState {
|
impl SelectorParsingState {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn allows_functional_pseudo_classes(self) -> bool {
|
fn allows_pseudos(self) -> bool {
|
||||||
!self.intersects(SelectorParsingState::AFTER_PSEUDO)
|
// NOTE(emilio): We allow pseudos after ::part and such.
|
||||||
|
!self.intersects(Self::AFTER_PSEUDO_ELEMENT | Self::DISALLOW_PSEUDOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn allows_slotted(self) -> bool {
|
fn allows_slotted(self) -> bool {
|
||||||
!self.intersects(SelectorParsingState::AFTER_PSEUDO)
|
!self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(emilio): Should we allow other ::part()s after ::part()?
|
|
||||||
//
|
|
||||||
// See https://github.com/w3c/csswg-drafts/issues/3841
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn allows_part(self) -> bool {
|
fn allows_part(self) -> bool {
|
||||||
!self.intersects(SelectorParsingState::AFTER_PSEUDO)
|
!self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(emilio): Maybe some of these should be allowed, but this gets us on
|
||||||
|
// the safe side for now, matching previous behavior. Gotta be careful with
|
||||||
|
// the ones like :-moz-any, which allow nested selectors but don't carry the
|
||||||
|
// state, and so on.
|
||||||
|
#[inline]
|
||||||
|
fn allows_custom_functional_pseudo_classes(self) -> bool {
|
||||||
|
!self.intersects(Self::AFTER_PSEUDO)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn allows_non_functional_pseudo_classes(self) -> bool {
|
fn allows_non_functional_pseudo_classes(self) -> bool {
|
||||||
!self.intersects(
|
!self.intersects(
|
||||||
SelectorParsingState::AFTER_SLOTTED |
|
Self::AFTER_SLOTTED | Self::AFTER_NON_STATEFUL_PSEUDO_ELEMENT,
|
||||||
SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn allows_tree_structural_pseudo_classes(self) -> bool {
|
fn allows_tree_structural_pseudo_classes(self) -> bool {
|
||||||
!self.intersects(SelectorParsingState::AFTER_PSEUDO)
|
!self.intersects(Self::AFTER_PSEUDO)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn allows_combinators(self) -> bool {
|
||||||
|
!self.intersects(Self::DISALLOW_COMBINATORS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +164,6 @@ pub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum SelectorParseErrorKind<'i> {
|
pub enum SelectorParseErrorKind<'i> {
|
||||||
PseudoElementInComplexSelector,
|
|
||||||
NoQualifiedNameInAttributeSelector(Token<'i>),
|
NoQualifiedNameInAttributeSelector(Token<'i>),
|
||||||
EmptySelector,
|
EmptySelector,
|
||||||
DanglingCombinator,
|
DanglingCombinator,
|
||||||
|
@ -321,6 +338,17 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i, P::Error>>
|
) -> Result<Self, ParseError<'i, P::Error>>
|
||||||
|
where
|
||||||
|
P: Parser<'i, Impl = Impl>,
|
||||||
|
{
|
||||||
|
Self::parse_with_state(parser, input, SelectorParsingState::empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_with_state<'i, 't, P>(
|
||||||
|
parser: &P,
|
||||||
|
input: &mut CssParser<'i, 't>,
|
||||||
|
state: SelectorParsingState,
|
||||||
|
) -> Result<Self, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
{
|
{
|
||||||
|
@ -328,7 +356,7 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
loop {
|
loop {
|
||||||
values.push(
|
values.push(
|
||||||
input
|
input
|
||||||
.parse_until_before(Delimiter::Comma, |input| parse_selector(parser, input))?,
|
.parse_until_before(Delimiter::Comma, |input| parse_selector(parser, input, state))?,
|
||||||
);
|
);
|
||||||
match input.next() {
|
match input.next() {
|
||||||
Err(_) => return Ok(SelectorList(values)),
|
Err(_) => return Ok(SelectorList(values)),
|
||||||
|
@ -344,30 +372,17 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses one compound selector suitable for nested stuff like ::-moz-any, etc.
|
/// Parses one compound selector suitable for nested stuff like :-moz-any, etc.
|
||||||
fn parse_inner_compound_selector<'i, 't, P, Impl>(
|
fn parse_inner_compound_selector<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
|
state: SelectorParsingState,
|
||||||
) -> Result<Selector<Impl>, ParseError<'i, P::Error>>
|
) -> Result<Selector<Impl>, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
let location = input.current_source_location();
|
parse_selector(parser, input, state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS)
|
||||||
let selector = parse_selector(parser, input)?;
|
|
||||||
|
|
||||||
// Ensure they're actually all compound selectors without pseudo-elements.
|
|
||||||
if selector.has_pseudo_element() {
|
|
||||||
return Err(
|
|
||||||
location.new_custom_error(SelectorParseErrorKind::PseudoElementInComplexSelector)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if selector.iter_raw_match_order().any(|s| s.is_combinator()) {
|
|
||||||
return Err(location.new_custom_error(SelectorParseErrorKind::NonCompoundSelector));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(selector)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a comma separated list of compound selectors.
|
/// Parse a comma separated list of compound selectors.
|
||||||
|
@ -380,7 +395,7 @@ where
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
input
|
input
|
||||||
.parse_comma_separated(|input| parse_inner_compound_selector(parser, input))
|
.parse_comma_separated(|input| parse_inner_compound_selector(parser, input, SelectorParsingState::empty()))
|
||||||
.map(|selectors| selectors.into_boxed_slice())
|
.map(|selectors| selectors.into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1542,6 +1557,7 @@ fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) ->
|
||||||
fn parse_selector<'i, 't, P, Impl>(
|
fn parse_selector<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
|
mut state: SelectorParsingState,
|
||||||
) -> Result<Selector<Impl>, ParseError<'i, P::Error>>
|
) -> Result<Selector<Impl>, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
|
@ -1554,16 +1570,14 @@ where
|
||||||
let mut part = false;
|
let mut part = false;
|
||||||
'outer_loop: loop {
|
'outer_loop: loop {
|
||||||
// Parse a sequence of simple selectors.
|
// Parse a sequence of simple selectors.
|
||||||
let state = match parse_compound_selector(parser, input, &mut builder)? {
|
let empty = parse_compound_selector(parser, &mut state, input, &mut builder)?;
|
||||||
Some(state) => state,
|
if empty {
|
||||||
None => {
|
return Err(input.new_custom_error(if builder.has_combinators() {
|
||||||
return Err(input.new_custom_error(if builder.has_combinators() {
|
SelectorParseErrorKind::DanglingCombinator
|
||||||
SelectorParseErrorKind::DanglingCombinator
|
} else {
|
||||||
} else {
|
SelectorParseErrorKind::EmptySelector
|
||||||
SelectorParseErrorKind::EmptySelector
|
}));
|
||||||
}));
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
|
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
|
||||||
has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
|
has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
|
||||||
|
@ -1604,6 +1618,11 @@ where
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !state.allows_combinators() {
|
||||||
|
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||||
|
}
|
||||||
|
|
||||||
builder.push_combinator(combinator);
|
builder.push_combinator(combinator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1620,7 +1639,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
{
|
{
|
||||||
parse_selector(parser, input)
|
parse_selector(parser, input, SelectorParsingState::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1630,6 +1649,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
fn parse_type_selector<'i, 't, P, Impl, S>(
|
fn parse_type_selector<'i, 't, P, Impl, S>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
|
state: SelectorParsingState,
|
||||||
sink: &mut S,
|
sink: &mut S,
|
||||||
) -> Result<bool, ParseError<'i, P::Error>>
|
) -> Result<bool, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
|
@ -1644,6 +1664,9 @@ where
|
||||||
}) |
|
}) |
|
||||||
Ok(OptionalQName::None(_)) => Ok(false),
|
Ok(OptionalQName::None(_)) => Ok(false),
|
||||||
Ok(OptionalQName::Some(namespace, local_name)) => {
|
Ok(OptionalQName::Some(namespace, local_name)) => {
|
||||||
|
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
|
||||||
|
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||||
|
}
|
||||||
match namespace {
|
match namespace {
|
||||||
QNamePrefix::ImplicitAnyNamespace => {},
|
QNamePrefix::ImplicitAnyNamespace => {},
|
||||||
QNamePrefix::ImplicitDefaultNamespace(url) => {
|
QNamePrefix::ImplicitDefaultNamespace(url) => {
|
||||||
|
@ -2015,11 +2038,14 @@ fn parse_attribute_flags<'i, 't>(
|
||||||
fn parse_negation<'i, 't, P, Impl>(
|
fn parse_negation<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
|
state: SelectorParsingState,
|
||||||
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
|
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
|
let state = state | SelectorParsingState::INSIDE_NEGATION;
|
||||||
|
|
||||||
// We use a sequence because a type selector may be represented as two Components.
|
// We use a sequence because a type selector may be represented as two Components.
|
||||||
let mut sequence = SmallVec::<[Component<Impl>; 2]>::new();
|
let mut sequence = SmallVec::<[Component<Impl>; 2]>::new();
|
||||||
|
|
||||||
|
@ -2027,7 +2053,7 @@ where
|
||||||
|
|
||||||
// Get exactly one simple selector. The parse logic in the caller will verify
|
// Get exactly one simple selector. The parse logic in the caller will verify
|
||||||
// that there are no trailing tokens after we're done.
|
// that there are no trailing tokens after we're done.
|
||||||
let is_type_sel = match parse_type_selector(parser, input, &mut sequence) {
|
let is_type_sel = match parse_type_selector(parser, input, state, &mut sequence) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(ParseError {
|
Err(ParseError {
|
||||||
kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
|
kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
|
||||||
|
@ -2036,7 +2062,7 @@ where
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
if !is_type_sel {
|
if !is_type_sel {
|
||||||
match parse_one_simple_selector(parser, input, SelectorParsingState::INSIDE_NEGATION)? {
|
match parse_one_simple_selector(parser, input, state)? {
|
||||||
Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
|
Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
|
||||||
sequence.push(s);
|
sequence.push(s);
|
||||||
},
|
},
|
||||||
|
@ -2063,12 +2089,13 @@ where
|
||||||
/// | [ HASH | class | attrib | pseudo | negation ]+
|
/// | [ HASH | class | attrib | pseudo | negation ]+
|
||||||
///
|
///
|
||||||
/// `Err(())` means invalid selector.
|
/// `Err(())` means invalid selector.
|
||||||
/// `Ok(None)` is an empty selector
|
/// `Ok(true)` is an empty selector
|
||||||
fn parse_compound_selector<'i, 't, P, Impl>(
|
fn parse_compound_selector<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
|
state: &mut SelectorParsingState,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
builder: &mut SelectorBuilder<Impl>,
|
builder: &mut SelectorBuilder<Impl>,
|
||||||
) -> Result<Option<SelectorParsingState>, ParseError<'i, P::Error>>
|
) -> Result<bool, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
|
@ -2076,13 +2103,12 @@ where
|
||||||
input.skip_whitespace();
|
input.skip_whitespace();
|
||||||
|
|
||||||
let mut empty = true;
|
let mut empty = true;
|
||||||
if parse_type_selector(parser, input, builder)? {
|
if parse_type_selector(parser, input, *state, builder)? {
|
||||||
empty = false;
|
empty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state = SelectorParsingState::empty();
|
|
||||||
loop {
|
loop {
|
||||||
let result = match parse_one_simple_selector(parser, input, state)? {
|
let result = match parse_one_simple_selector(parser, input, *state)? {
|
||||||
None => break,
|
None => break,
|
||||||
Some(result) => result,
|
Some(result) => result,
|
||||||
};
|
};
|
||||||
|
@ -2141,17 +2167,13 @@ where
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if empty {
|
Ok(empty)
|
||||||
// An empty selector is invalid.
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(state))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_is_or_where<'i, 't, P, Impl>(
|
fn parse_is_or_where<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
|
state: SelectorParsingState,
|
||||||
component: impl FnOnce(Box<[Selector<Impl>]>) -> Component<Impl>,
|
component: impl FnOnce(Box<[Selector<Impl>]>) -> Component<Impl>,
|
||||||
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
|
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
|
@ -2159,15 +2181,12 @@ where
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
debug_assert!(parser.parse_is_and_where());
|
debug_assert!(parser.parse_is_and_where());
|
||||||
let inner = SelectorList::parse(parser, input)?;
|
|
||||||
// https://drafts.csswg.org/selectors/#matches-pseudo:
|
// https://drafts.csswg.org/selectors/#matches-pseudo:
|
||||||
//
|
//
|
||||||
// Pseudo-elements cannot be represented by the matches-any
|
// Pseudo-elements cannot be represented by the matches-any
|
||||||
// pseudo-class; they are not valid within :is().
|
// pseudo-class; they are not valid within :is().
|
||||||
//
|
//
|
||||||
if inner.0.iter().any(|i| i.has_pseudo_element()) {
|
let inner = SelectorList::parse_with_state(parser, input, state | SelectorParsingState::DISALLOW_PSEUDOS)?;
|
||||||
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidPseudoElementInsideWhere));
|
|
||||||
}
|
|
||||||
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2181,40 +2200,46 @@ where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
if !state.allows_functional_pseudo_classes() {
|
|
||||||
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
|
||||||
}
|
|
||||||
debug_assert!(state.allows_tree_structural_pseudo_classes());
|
|
||||||
match_ignore_ascii_case! { &name,
|
match_ignore_ascii_case! { &name,
|
||||||
"nth-child" => return Ok(parse_nth_pseudo_class(input, Component::NthChild)?),
|
"nth-child" => return parse_nth_pseudo_class(parser, input, state, Component::NthChild),
|
||||||
"nth-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthOfType)?),
|
"nth-of-type" => return parse_nth_pseudo_class(parser, input, state, Component::NthOfType),
|
||||||
"nth-last-child" => return Ok(parse_nth_pseudo_class(input, Component::NthLastChild)?),
|
"nth-last-child" => return parse_nth_pseudo_class(parser, input, state, Component::NthLastChild),
|
||||||
"nth-last-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthLastOfType)?),
|
"nth-last-of-type" => return parse_nth_pseudo_class(parser, input, state, Component::NthLastOfType),
|
||||||
"is" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, Component::Is),
|
"is" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Is),
|
||||||
"where" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, Component::Where),
|
"where" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, state, Component::Where),
|
||||||
"host" => return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input)?))),
|
"host" => return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?))),
|
||||||
"not" => {
|
"not" => {
|
||||||
if state.intersects(SelectorParsingState::INSIDE_NEGATION) {
|
if state.intersects(SelectorParsingState::INSIDE_NEGATION) {
|
||||||
return Err(input.new_custom_error(
|
return Err(input.new_custom_error(
|
||||||
SelectorParseErrorKind::UnexpectedIdent("not".into())
|
SelectorParseErrorKind::UnexpectedIdent("not".into())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
debug_assert!(state.is_empty());
|
return parse_negation(parser, input, state)
|
||||||
return parse_negation(parser, input)
|
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !state.allows_custom_functional_pseudo_classes() {
|
||||||
|
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||||
|
}
|
||||||
|
|
||||||
P::parse_non_ts_functional_pseudo_class(parser, name, input).map(Component::NonTSPseudoClass)
|
P::parse_non_ts_functional_pseudo_class(parser, name, input).map(Component::NonTSPseudoClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_nth_pseudo_class<'i, 't, Impl, F>(
|
fn parse_nth_pseudo_class<'i, 't, P, Impl, F>(
|
||||||
|
_: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
|
state: SelectorParsingState,
|
||||||
selector: F,
|
selector: F,
|
||||||
) -> Result<Component<Impl>, BasicParseError<'i>>
|
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
|
P: Parser<'i, Impl = Impl>,
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
F: FnOnce(i32, i32) -> Component<Impl>,
|
F: FnOnce(i32, i32) -> Component<Impl>,
|
||||||
{
|
{
|
||||||
|
if !state.allows_tree_structural_pseudo_classes() {
|
||||||
|
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||||
|
}
|
||||||
let (a, b) = parse_nth(input)?;
|
let (a, b) = parse_nth(input)?;
|
||||||
Ok(selector(a, b))
|
Ok(selector(a, b))
|
||||||
}
|
}
|
||||||
|
@ -2299,7 +2324,7 @@ where
|
||||||
};
|
};
|
||||||
let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name);
|
let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name);
|
||||||
if is_pseudo_element {
|
if is_pseudo_element {
|
||||||
if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) {
|
if !state.allows_pseudos() {
|
||||||
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||||
}
|
}
|
||||||
let pseudo_element = if is_functional {
|
let pseudo_element = if is_functional {
|
||||||
|
@ -2326,7 +2351,7 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let selector = input.parse_nested_block(|input| {
|
let selector = input.parse_nested_block(|input| {
|
||||||
parse_inner_compound_selector(parser, input)
|
parse_inner_compound_selector(parser, input, state)
|
||||||
})?;
|
})?;
|
||||||
return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));
|
return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue