mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
style: Parsing for relative selectors in :has()
Differential Revision: https://phabricator.services.mozilla.com/D171358
This commit is contained in:
parent
b6db94bdf5
commit
f7b29ac432
5 changed files with 208 additions and 65 deletions
|
@ -717,7 +717,8 @@ where
|
||||||
Component::Scope |
|
Component::Scope |
|
||||||
Component::ParentSelector |
|
Component::ParentSelector |
|
||||||
Component::Nth(..) |
|
Component::Nth(..) |
|
||||||
Component::Host(None) => 0,
|
Component::Host(None) |
|
||||||
|
Component::RelativeSelectorAnchor => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -360,7 +360,8 @@ where
|
||||||
Component::ExplicitAnyNamespace |
|
Component::ExplicitAnyNamespace |
|
||||||
Component::ExplicitNoNamespace |
|
Component::ExplicitNoNamespace |
|
||||||
Component::DefaultNamespace(..) |
|
Component::DefaultNamespace(..) |
|
||||||
Component::Namespace(..) => {
|
Component::Namespace(..) |
|
||||||
|
Component::RelativeSelectorAnchor => {
|
||||||
// Does not affect specificity
|
// Does not affect specificity
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,9 @@ where
|
||||||
/// Extra implementation-dependent matching data.
|
/// Extra implementation-dependent matching data.
|
||||||
pub extra_data: Impl::ExtraMatchingData<'a>,
|
pub extra_data: Impl::ExtraMatchingData<'a>,
|
||||||
|
|
||||||
|
/// The current element we're anchoring on for evaluating the relative selector.
|
||||||
|
current_relative_selector_anchor: Option<OpaqueElement>,
|
||||||
|
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
needs_selector_flags: NeedsSelectorFlags,
|
needs_selector_flags: NeedsSelectorFlags,
|
||||||
classes_and_ids_case_sensitivity: CaseSensitivity,
|
classes_and_ids_case_sensitivity: CaseSensitivity,
|
||||||
|
@ -198,6 +201,7 @@ where
|
||||||
in_negation: false,
|
in_negation: false,
|
||||||
pseudo_element_matching_fn: None,
|
pseudo_element_matching_fn: None,
|
||||||
extra_data: Default::default(),
|
extra_data: Default::default(),
|
||||||
|
current_relative_selector_anchor: None,
|
||||||
_impl: ::std::marker::PhantomData,
|
_impl: ::std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,4 +322,25 @@ where
|
||||||
pub fn shadow_host(&self) -> Option<OpaqueElement> {
|
pub fn shadow_host(&self) -> Option<OpaqueElement> {
|
||||||
self.current_host
|
self.current_host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs F with a deeper nesting level, with the given element as the anchor,
|
||||||
|
/// for a :has(...) selector, for example.
|
||||||
|
#[inline]
|
||||||
|
pub fn nest_for_relative_selector<F, R>(&mut self, anchor: OpaqueElement, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R,
|
||||||
|
{
|
||||||
|
// TODO(dshin): Nesting should be rejected at parse time.
|
||||||
|
let original_relative_selector_anchor = self.current_relative_selector_anchor.take();
|
||||||
|
self.current_relative_selector_anchor = Some(anchor);
|
||||||
|
let result = self.nest(f);
|
||||||
|
self.current_relative_selector_anchor = original_relative_selector_anchor;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current anchor element to evaluate the relative selector against.
|
||||||
|
#[inline]
|
||||||
|
pub fn relative_selector_anchor(&self) -> Option<OpaqueElement> {
|
||||||
|
self.current_relative_selector_anchor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -835,10 +835,34 @@ where
|
||||||
.nest_for_negation(|context| !list_matches_complex_selector(list, element, context)),
|
.nest_for_negation(|context| !list_matches_complex_selector(list, element, context)),
|
||||||
Component::Has(ref list) => context
|
Component::Has(ref list) => context
|
||||||
.shared
|
.shared
|
||||||
.nest(|context| has_children_matching(list, element, context)),
|
.nest_for_relative_selector(element.opaque(), |context| {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
for selector in list.iter() {
|
||||||
|
let mut selector_iter = selector.iter_raw_parse_order_from(0);
|
||||||
|
assert!(
|
||||||
|
matches!(selector_iter.next().unwrap(), Component::RelativeSelectorAnchor),
|
||||||
|
"Relative selector does not start with RelativeSelectorAnchor"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
selector_iter.next().unwrap().is_combinator(),
|
||||||
|
"Relative combinator does not exist"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(dshin): Proper matching for sibling relative combinators.
|
||||||
|
has_children_matching(list, element, context)
|
||||||
|
}),
|
||||||
Component::Combinator(_) => unsafe {
|
Component::Combinator(_) => unsafe {
|
||||||
debug_unreachable!("Shouldn't try to selector-match combinators")
|
debug_unreachable!("Shouldn't try to selector-match combinators")
|
||||||
},
|
},
|
||||||
|
Component::RelativeSelectorAnchor => {
|
||||||
|
let anchor = context.shared.relative_selector_anchor();
|
||||||
|
debug_assert!(
|
||||||
|
anchor.is_some(),
|
||||||
|
"Relative selector outside of relative selector matching?"
|
||||||
|
);
|
||||||
|
anchor.map_or(false, |a| a == element.opaque())
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -366,15 +366,24 @@ pub struct SelectorList<Impl: SelectorImpl>(
|
||||||
#[cfg_attr(feature = "shmem", shmem(field_bound))] pub SmallVec<[Selector<Impl>; 1]>,
|
#[cfg_attr(feature = "shmem", shmem(field_bound))] pub SmallVec<[Selector<Impl>; 1]>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// How to treat invalid selectors in a selector list.
|
/// Whether or not we're using forgiving parsing mode
|
||||||
enum ParseErrorRecovery {
|
enum ForgivingParsing {
|
||||||
/// Discard the entire selector list, this is the default behavior for
|
/// Discard the entire selector list upon encountering any invalid selector.
|
||||||
/// almost all of CSS.
|
/// This is the default behavior for almost all of CSS.
|
||||||
DiscardList,
|
No,
|
||||||
/// Ignore invalid selectors, potentially creating an empty selector list.
|
/// Ignore invalid selectors, potentially creating an empty selector list.
|
||||||
///
|
///
|
||||||
/// This is the error recovery mode of :is() and :where()
|
/// This is the error recovery mode of :is() and :where()
|
||||||
IgnoreInvalidSelector,
|
Yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flag indicating if we're parsing relative selectors.
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
enum ParseRelative {
|
||||||
|
/// Expect selectors to start with a combinator, assuming descendant combinator if not present.
|
||||||
|
Yes,
|
||||||
|
/// Treat as parse error if any selector begins with a combinator.
|
||||||
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Impl: SelectorImpl> SelectorList<Impl> {
|
impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
|
@ -393,7 +402,8 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
parser,
|
parser,
|
||||||
input,
|
input,
|
||||||
SelectorParsingState::empty(),
|
SelectorParsingState::empty(),
|
||||||
ParseErrorRecovery::DiscardList,
|
ForgivingParsing::No,
|
||||||
|
ParseRelative::No,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,23 +412,24 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
state: SelectorParsingState,
|
state: SelectorParsingState,
|
||||||
recovery: ParseErrorRecovery,
|
recovery: ForgivingParsing,
|
||||||
|
parse_relative: ParseRelative,
|
||||||
) -> Result<Self, ParseError<'i, P::Error>>
|
) -> Result<Self, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
{
|
{
|
||||||
let mut values = SmallVec::new();
|
let mut values = SmallVec::new();
|
||||||
loop {
|
loop {
|
||||||
let selector = input.parse_until_before(Delimiter::Comma, |input| {
|
let selector = input.parse_until_before(Delimiter::Comma, |i| {
|
||||||
parse_selector(parser, input, state)
|
parse_selector(parser, i, state, parse_relative)
|
||||||
});
|
});
|
||||||
|
|
||||||
let was_ok = selector.is_ok();
|
let was_ok = selector.is_ok();
|
||||||
match selector {
|
match selector {
|
||||||
Ok(selector) => values.push(selector),
|
Ok(selector) => values.push(selector),
|
||||||
Err(err) => match recovery {
|
Err(err) => match recovery {
|
||||||
ParseErrorRecovery::DiscardList => return Err(err),
|
ForgivingParsing::No => return Err(err),
|
||||||
ParseErrorRecovery::IgnoreInvalidSelector => {
|
ForgivingParsing::Yes => {
|
||||||
if !parser.allow_forgiving_selectors() {
|
if !parser.allow_forgiving_selectors() {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
@ -459,6 +470,7 @@ where
|
||||||
parser,
|
parser,
|
||||||
input,
|
input,
|
||||||
state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS,
|
state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS,
|
||||||
|
ParseRelative::No,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -905,7 +917,8 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
PseudoElement(..) |
|
PseudoElement(..) |
|
||||||
Combinator(..) |
|
Combinator(..) |
|
||||||
Host(None) |
|
Host(None) |
|
||||||
Part(..) => component.clone(),
|
Part(..) |
|
||||||
|
RelativeSelectorAnchor => component.clone(),
|
||||||
ParentSelector => {
|
ParentSelector => {
|
||||||
specificity += parent_specificity;
|
specificity += parent_specificity;
|
||||||
Is(parent.to_vec().into_boxed_slice())
|
Is(parent.to_vec().into_boxed_slice())
|
||||||
|
@ -1469,6 +1482,12 @@ pub enum Component<Impl: SelectorImpl> {
|
||||||
PseudoElement(#[cfg_attr(feature = "shmem", shmem(field_bound))] Impl::PseudoElement),
|
PseudoElement(#[cfg_attr(feature = "shmem", shmem(field_bound))] Impl::PseudoElement),
|
||||||
|
|
||||||
Combinator(Combinator),
|
Combinator(Combinator),
|
||||||
|
|
||||||
|
/// Used only for relative selectors, which starts with a combinator
|
||||||
|
/// (With an implied descendant combinator if not specified).
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/csswg-drafts/selectors-4/#typedef-relative-selector
|
||||||
|
RelativeSelectorAnchor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Impl: SelectorImpl> Component<Impl> {
|
impl<Impl: SelectorImpl> Component<Impl> {
|
||||||
|
@ -1722,6 +1741,14 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
|
||||||
if compound.is_empty() {
|
if compound.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if let Component::RelativeSelectorAnchor = compound.first().unwrap() {
|
||||||
|
debug_assert!(
|
||||||
|
compound.len() == 1,
|
||||||
|
"RelativeLeft should only be a simple selector"
|
||||||
|
);
|
||||||
|
combinators.next().unwrap().to_css_relative(dest)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. If there is only one simple selector in the compound selectors
|
// 1. If there is only one simple selector in the compound selectors
|
||||||
// which is a universal selector, append the result of
|
// which is a universal selector, append the result of
|
||||||
|
@ -1811,18 +1838,45 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Combinator {
|
||||||
|
fn to_css_internal<W>(&self, dest: &mut W, prefix_space: bool) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
if matches!(
|
||||||
|
*self,
|
||||||
|
Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment
|
||||||
|
) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if prefix_space {
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
}
|
||||||
|
match *self {
|
||||||
|
Combinator::Child => dest.write_str("> "),
|
||||||
|
Combinator::Descendant => Ok(()),
|
||||||
|
Combinator::NextSibling => dest.write_str("+ "),
|
||||||
|
Combinator::LaterSibling => dest.write_str("~ "),
|
||||||
|
Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment => unsafe {
|
||||||
|
debug_unreachable!("Already handled")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_css_relative<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
self.to_css_internal(dest, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCss for Combinator {
|
impl ToCss for Combinator {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
where
|
where
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
{
|
{
|
||||||
match *self {
|
self.to_css_internal(dest, true)
|
||||||
Combinator::Child => dest.write_str(" > "),
|
|
||||||
Combinator::Descendant => dest.write_str(" "),
|
|
||||||
Combinator::NextSibling => dest.write_str(" + "),
|
|
||||||
Combinator::LaterSibling => dest.write_str(" ~ "),
|
|
||||||
Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1952,6 +2006,7 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
dest.write_str(")")
|
dest.write_str(")")
|
||||||
},
|
},
|
||||||
NonTSPseudoClass(ref pseudo) => pseudo.to_css(dest),
|
NonTSPseudoClass(ref pseudo) => pseudo.to_css(dest),
|
||||||
|
RelativeSelectorAnchor => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2011,13 +2066,19 @@ fn parse_selector<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
mut state: SelectorParsingState,
|
mut state: SelectorParsingState,
|
||||||
|
parse_relative: ParseRelative,
|
||||||
) -> 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 mut builder = SelectorBuilder::default();
|
let mut builder = SelectorBuilder::default();
|
||||||
|
if parse_relative == ParseRelative::Yes {
|
||||||
|
builder.push_simple_selector(Component::RelativeSelectorAnchor);
|
||||||
|
// Do we see a combinator? If so, push that. Otherwise, push a descendant combinator.
|
||||||
|
builder
|
||||||
|
.push_combinator(parse_combinator::<P, Impl>(input).unwrap_or(Combinator::Descendant));
|
||||||
|
}
|
||||||
'outer_loop: loop {
|
'outer_loop: loop {
|
||||||
// Parse a sequence of simple selectors.
|
// Parse a sequence of simple selectors.
|
||||||
let empty = parse_compound_selector(parser, &mut state, input, &mut builder)?;
|
let empty = parse_compound_selector(parser, &mut state, input, &mut builder)?;
|
||||||
|
@ -2038,37 +2099,11 @@ where
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a combinator.
|
let combinator = if let Ok(c) = parse_combinator::<P, Impl>(input) {
|
||||||
let combinator;
|
c
|
||||||
let mut any_whitespace = false;
|
} else {
|
||||||
loop {
|
break 'outer_loop;
|
||||||
let before_this_token = input.state();
|
};
|
||||||
match input.next_including_whitespace() {
|
|
||||||
Err(_e) => break 'outer_loop,
|
|
||||||
Ok(&Token::WhiteSpace(_)) => any_whitespace = true,
|
|
||||||
Ok(&Token::Delim('>')) => {
|
|
||||||
combinator = Combinator::Child;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
Ok(&Token::Delim('+')) => {
|
|
||||||
combinator = Combinator::NextSibling;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
Ok(&Token::Delim('~')) => {
|
|
||||||
combinator = Combinator::LaterSibling;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
Ok(_) => {
|
|
||||||
input.reset(&before_this_token);
|
|
||||||
if any_whitespace {
|
|
||||||
combinator = Combinator::Descendant;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
break 'outer_loop;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !state.allows_combinators() {
|
if !state.allows_combinators() {
|
||||||
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||||
|
@ -2076,8 +2111,35 @@ where
|
||||||
|
|
||||||
builder.push_combinator(combinator);
|
builder.push_combinator(combinator);
|
||||||
}
|
}
|
||||||
|
return Ok(Selector(builder.build()));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Selector(builder.build()))
|
fn parse_combinator<'i, 't, P, Impl>(input: &mut CssParser<'i, 't>) -> Result<Combinator, ()> {
|
||||||
|
let mut any_whitespace = false;
|
||||||
|
loop {
|
||||||
|
let before_this_token = input.state();
|
||||||
|
match input.next_including_whitespace() {
|
||||||
|
Err(_e) => return Err(()),
|
||||||
|
Ok(&Token::WhiteSpace(_)) => any_whitespace = true,
|
||||||
|
Ok(&Token::Delim('>')) => {
|
||||||
|
return Ok(Combinator::Child);
|
||||||
|
},
|
||||||
|
Ok(&Token::Delim('+')) => {
|
||||||
|
return Ok(Combinator::NextSibling);
|
||||||
|
},
|
||||||
|
Ok(&Token::Delim('~')) => {
|
||||||
|
return Ok(Combinator::LaterSibling);
|
||||||
|
},
|
||||||
|
Ok(_) => {
|
||||||
|
input.reset(&before_this_token);
|
||||||
|
if any_whitespace {
|
||||||
|
return Ok(Combinator::Descendant);
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Impl: SelectorImpl> Selector<Impl> {
|
impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
|
@ -2090,7 +2152,12 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl = Impl>,
|
P: Parser<'i, Impl = Impl>,
|
||||||
{
|
{
|
||||||
parse_selector(parser, input, SelectorParsingState::empty())
|
parse_selector(
|
||||||
|
parser,
|
||||||
|
input,
|
||||||
|
SelectorParsingState::empty(),
|
||||||
|
ParseRelative::No,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2498,7 +2565,8 @@ where
|
||||||
state |
|
state |
|
||||||
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
||||||
SelectorParsingState::DISALLOW_PSEUDOS,
|
SelectorParsingState::DISALLOW_PSEUDOS,
|
||||||
ParseErrorRecovery::DiscardList,
|
ForgivingParsing::No,
|
||||||
|
ParseRelative::No,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
|
Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
|
||||||
|
@ -2603,7 +2671,7 @@ where
|
||||||
Ok(empty)
|
Ok(empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_is_where_has<'i, 't, P, Impl>(
|
fn parse_is_where<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
state: SelectorParsingState,
|
state: SelectorParsingState,
|
||||||
|
@ -2625,11 +2693,34 @@ where
|
||||||
state |
|
state |
|
||||||
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
||||||
SelectorParsingState::DISALLOW_PSEUDOS,
|
SelectorParsingState::DISALLOW_PSEUDOS,
|
||||||
ParseErrorRecovery::IgnoreInvalidSelector,
|
ForgivingParsing::Yes,
|
||||||
|
ParseRelative::No,
|
||||||
)?;
|
)?;
|
||||||
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_has<'i, 't, P, Impl>(
|
||||||
|
parser: &P,
|
||||||
|
input: &mut CssParser<'i, 't>,
|
||||||
|
state: SelectorParsingState,
|
||||||
|
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
|
||||||
|
where
|
||||||
|
P: Parser<'i, Impl = Impl>,
|
||||||
|
Impl: SelectorImpl,
|
||||||
|
{
|
||||||
|
debug_assert!(parser.parse_has());
|
||||||
|
let inner = SelectorList::parse_with_state(
|
||||||
|
parser,
|
||||||
|
input,
|
||||||
|
state |
|
||||||
|
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
||||||
|
SelectorParsingState::DISALLOW_PSEUDOS,
|
||||||
|
ForgivingParsing::No,
|
||||||
|
ParseRelative::Yes,
|
||||||
|
)?;
|
||||||
|
Ok(Component::Has(inner.0.into_vec().into_boxed_slice()))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_functional_pseudo_class<'i, 't, P, Impl>(
|
fn parse_functional_pseudo_class<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
|
@ -2645,9 +2736,9 @@ where
|
||||||
"nth-of-type" => return parse_nth_pseudo_class(parser, input, state, NthType::OfType),
|
"nth-of-type" => return parse_nth_pseudo_class(parser, input, state, NthType::OfType),
|
||||||
"nth-last-child" => return parse_nth_pseudo_class(parser, input, state, NthType::LastChild),
|
"nth-last-child" => return parse_nth_pseudo_class(parser, input, state, NthType::LastChild),
|
||||||
"nth-last-of-type" => return parse_nth_pseudo_class(parser, input, state, NthType::LastOfType),
|
"nth-last-of-type" => return parse_nth_pseudo_class(parser, input, state, NthType::LastOfType),
|
||||||
"is" if parser.parse_is_and_where() => return parse_is_where_has(parser, input, state, Component::Is),
|
"is" if parser.parse_is_and_where() => return parse_is_where(parser, input, state, Component::Is),
|
||||||
"where" if parser.parse_is_and_where() => return parse_is_where_has(parser, input, state, Component::Where),
|
"where" if parser.parse_is_and_where() => return parse_is_where(parser, input, state, Component::Where),
|
||||||
"has" if parser.parse_has() => return parse_is_where_has(parser, input, state, Component::Has),
|
"has" if parser.parse_has() => return parse_has(parser, input, state),
|
||||||
"host" => {
|
"host" => {
|
||||||
if !state.allows_tree_structural_pseudo_classes() {
|
if !state.allows_tree_structural_pseudo_classes() {
|
||||||
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||||
|
@ -2661,7 +2752,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if parser.parse_is_and_where() && parser.is_is_alias(&name) {
|
if parser.parse_is_and_where() && parser.is_is_alias(&name) {
|
||||||
return parse_is_where_has(parser, input, state, Component::Is);
|
return parse_is_where(parser, input, state, Component::Is);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !state.allows_custom_functional_pseudo_classes() {
|
if !state.allows_custom_functional_pseudo_classes() {
|
||||||
|
@ -2707,7 +2798,8 @@ where
|
||||||
state |
|
state |
|
||||||
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
||||||
SelectorParsingState::DISALLOW_PSEUDOS,
|
SelectorParsingState::DISALLOW_PSEUDOS,
|
||||||
ParseErrorRecovery::DiscardList,
|
ForgivingParsing::No,
|
||||||
|
ParseRelative::No,
|
||||||
)?;
|
)?;
|
||||||
Ok(Component::NthOf(NthOfSelectorData::new(
|
Ok(Component::NthOf(NthOfSelectorData::new(
|
||||||
&nth_data,
|
&nth_data,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue