/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
usecrate::attr::{AttrSelectorOperator, AttrSelectorWithOptionalNamespace}; usecrate::attr::{NamespaceConstraint, ParsedAttrSelectorOperation, ParsedCaseSensitivity}; usecrate::bloom::BLOOM_HASH_MASK; usecrate::builder::{
relative_selector_list_specificity_and_flags, selector_list_specificity_and_flags,
SelectorBuilder, SelectorFlags, Specificity, SpecificityAndFlags,
}; usecrate::context::QuirksMode; usecrate::sink::Push; usecrate::visitor::SelectorListKind; pubusecrate::visitor::SelectorVisitor; use bitflags::bitflags; use cssparser::match_ignore_ascii_case; use cssparser::parse_nth; use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind}; use cssparser::{CowRcStr, Delimiter, SourceLocation}; use cssparser::{Parser as CssParser, ToCss, Token}; use debug_unreachable::debug_unreachable; use precomputed_hash::PrecomputedHash; use servo_arc::{Arc, ArcUnionBorrow, ThinArc, ThinArcUnion, UniqueArc}; use smallvec::SmallVec; use std::borrow::{Borrow, Cow}; use std::fmt::{self, Debug}; use std::iter::Rev; use std::slice;
#[cfg(feature = "to_shmem")] use to_shmem_derive::ToShmem;
/// A trait that represents a pseudo-element. pubtrait PseudoElement: Sized + ToCss { /// The `SelectorImpl` this pseudo-element is used for. typeImpl: SelectorImpl;
/// Whether the pseudo-element supports a given state selector to the right /// of it.
fn accepts_state_pseudo_classes(&self) -> bool { false
}
/// Whether this pseudo-element is valid after a ::slotted(..) pseudo.
fn valid_after_slotted(&self) -> bool { false
}
/// Whether this pseudo-element is valid when directly after a ::before/::after pseudo.
fn valid_after_before_or_after(&self) -> bool { false
}
/// Whether this pseudo-element is ::before or ::after pseudo element, /// which are treated specially when deciding what can come after them. /// https://drafts.csswg.org/css-pseudo-4/#generated-content
fn is_before_or_after(&self) -> bool { false
}
/// The count we contribute to the specificity from this pseudo-element.
fn specificity_count(&self) -> u32 { 1
}
/// A trait that represents a pseudo-class. pubtrait NonTSPseudoClass: Sized + ToCss { /// The `SelectorImpl` this pseudo-element is used for. typeImpl: SelectorImpl;
/// Whether this pseudo-class is :active or :hover.
fn is_active_or_hover(&self) -> bool;
/// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a /// Cow::Owned if `s` had to be converted into ASCII lowercase.
fn to_ascii_lowercase(s: &str) -> Cow<'_, str> { iflet Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { letmut string = s.to_owned();
string[first_uppercase..].make_ascii_lowercase();
string.into()
} else {
s.into()
}
}
bitflags! { /// Flags that indicate at which point of parsing a selector are we. #[derive(Copy, Clone)] struct SelectorParsingState: u16 { /// Whether we should avoid adding default namespaces to selectors that /// aren't type or universal selectors. const SKIP_DEFAULT_NAMESPACE = 1 << 0;
/// Whether we've parsed a ::slotted() pseudo-element already. /// /// If so, then we can only parse a subset of pseudo-elements, and /// whatever comes after them if so. const AFTER_SLOTTED = 1 << 1; /// Whether we've parsed a ::part() or element-backed pseudo-element already. /// /// If so, then we can only parse a subset of pseudo-elements, and /// whatever comes after them if so. const AFTER_PART_LIKE = 1 << 2; /// Whether we've parsed a non-element-backed pseudo-element (as in, an /// `Impl::PseudoElement` thus not accounting for `::slotted` or /// `::part`) already. /// /// If so, then other pseudo-elements and most other selectors are /// disallowed. const AFTER_NON_ELEMENT_BACKED_PSEUDO = 1 << 3; /// Whether we've parsed a non-stateful pseudo-element (again, as-in /// `Impl::PseudoElement`) already. If so, then other pseudo-classes are /// disallowed. If this flag is set, `AFTER_NON_ELEMENT_BACKED_PSEUDO` must be set /// as well. const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4; // Whether we've parsed a generated pseudo-element (as in ::before, ::after). // If so then some other pseudo elements are disallowed (e.g. another generated pseudo) // while others allowed (e.g. ::marker). const AFTER_BEFORE_OR_AFTER_PSEUDO = 1 << 5;
/// Whether we are after any of the pseudo-like things. const AFTER_PSEUDO = Self::AFTER_PART_LIKE.bits() | Self::AFTER_SLOTTED.bits() | Self::AFTER_NON_ELEMENT_BACKED_PSEUDO.bits() | Self::AFTER_BEFORE_OR_AFTER_PSEUDO.bits();
/// Whether we've parsed a pseudo-element which is in a pseudo-element tree (i.e. it is a /// descendant pseudo of a pseudo-element root). const IN_PSEUDO_ELEMENT_TREE = 1 << 9;
}
}
macro_rules! with_all_bounds {
(
[ $( $InSelector: tt )* ]
[ $( $CommonBounds: tt )* ]
[ $( $FromStr: tt )* ]
) => { /// This trait allows to define the parser implementation in regards /// of pseudo-classes/elements /// /// NB: We need Clone so that we can derive(Clone) on struct with that /// are parameterized on SelectorImpl. See /// <https://github.com/rust-lang/rust/issues/26925> pubtrait SelectorImpl: Clone + Debug + Sized + 'static { type ExtraMatchingData<'a>: Sized + Default; type AttrValue: $($InSelector)*; type Identifier: $($InSelector)* + PrecomputedHash; type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName> + PrecomputedHash; type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl> + PrecomputedHash; type NamespacePrefix: $($InSelector)* + Default; type BorrowedNamespaceUrl: ?Sized + Eq; type BorrowedLocalName: ?Sized + Eq;
/// This function can return an "Err" pseudo-element in order to support CSS2.1 /// pseudo-elements.
fn parse_non_ts_pseudo_class(
&self,
location: SourceLocation,
name: CowRcStr<'i>,
) -> Result<<Self::Implas SelectorImpl>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
Err(
location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
name,
)),
)
}
/// A selector list is a tagged pointer with either a single selector, or a ThinArc<()> of multiple /// selectors. #[derive(Clone, Eq, Debug, PartialEq)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] #[cfg_attr(feature = "to_shmem", shmem(no_bounds))] pubstruct SelectorList<Impl: SelectorImpl>( #[cfg_attr(feature = "to_shmem", shmem(field_bound))]
ThinArcUnion<SpecificityAndFlags, Component<Impl>, (), Selector<Impl>>,
);
pub fn from_one(selector: Selector<Impl>) -> Self { #[cfg(debug_assertions)] let selector_repr = unsafe { *(&selector as *const _ as *const usize) }; let list = Self(ThinArcUnion::from_first(selector.into_data())); #[cfg(debug_assertions)]
debug_assert_eq!(
selector_repr, unsafe { *(&list as *const _ as *const usize) }, "We rely on the same bit representation for the single selector variant"
);
list
}
/// Returns the address on the heap of the ThinArc for memory reporting. pub fn thin_arc_heap_ptr(&self) -> *const ::std::os::raw::c_void { matchself.0.borrow() {
ArcUnionBorrow::First(s) => s.with_arc(|a| a.heap_ptr()),
ArcUnionBorrow::Second(s) => s.with_arc(|a| a.heap_ptr()),
}
}
}
/// Uniquely identify a selector based on its components, which is behind ThinArc and /// is therefore stable. #[derive(Clone, Copy, Hash, Eq, PartialEq)] pubstruct SelectorKey(usize);
impl SelectorKey { /// Create a new key based on the given selector. pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>) -> Self { Self(selector.0.slice().as_ptr() as usize)
}
}
/// Whether or not we're using forgiving parsing mode #[derive(PartialEq)] enum ForgivingParsing { /// Discard the entire selector list upon encountering any invalid selector. /// This is the default behavior for almost all of CSS.
No, /// Ignore invalid selectors, potentially creating an empty selector list. /// /// This is the error recovery mode of :is() and :where()
Yes,
}
/// Flag indicating if we're parsing relative selectors. #[derive(Copy, Clone, PartialEq)] pubenum ParseRelative { /// Expect selectors to start with a combinator, assuming descendant combinator if not present.
ForHas, /// Allow selectors to start with a combinator, prepending a parent selector if so. Do nothing /// otherwise
ForNesting, /// Allow selectors to start with a combinator, prepending a scope selector if so. Do nothing /// otherwise
ForScope, /// Treat as parse error if any selector begins with a combinator.
No,
}
impl<Impl: SelectorImpl> SelectorList<Impl> { /// Returns a selector list with a single `:scope` selector (with specificity) pub fn scope() -> Self { Self::from_one(Selector::scope())
} /// Returns a selector list with a single implicit `:scope` selector (no specificity) pub fn implicit_scope() -> Self { Self::from_one(Selector::implicit_scope())
}
/// Parse a comma-separated list of Selectors. /// <https://drafts.csswg.org/selectors/#grouping> /// /// Return the Selectors or Err if there is an invalid selector. pub fn parse<'i, 't, P>(
parser: &P,
input: &mut CssParser<'i, 't>,
parse_relative: ParseRelative,
) -> Result<Self, ParseError<'i, P::Error>> where
P: Parser<'i, Impl = Impl>,
{ Self::parse_with_state(
parser,
input,
SelectorParsingState::empty(),
ForgivingParsing::No,
parse_relative,
)
}
/// Same as `parse`, but disallow parsing of pseudo-elements. pub fn parse_disallow_pseudo<'i, 't, P>(
parser: &P,
input: &mut CssParser<'i, 't>,
parse_relative: ParseRelative,
) -> Result<Self, ParseError<'i, P::Error>> where
P: Parser<'i, Impl = Impl>,
{ Self::parse_with_state(
parser,
input,
SelectorParsingState::DISALLOW_PSEUDOS,
ForgivingParsing::No,
parse_relative,
)
}
/// Replaces the parent selector in all the items of the selector list. pub fn replace_parent_selector(&self, parent: &SelectorList<Impl>) -> Self { Self::from_iter( self.slice()
.iter()
.map(|selector| selector.replace_parent_selector(parent)),
)
}
/// Creates a SelectorList from a Vec of selectors. Used in tests. #[allow(dead_code)] pub(crate) fn from_vec(v: Vec<Selector<Impl>>) -> Self {
SelectorList::from_iter(v.into_iter())
}
}
/// Parses one compound selector suitable for nested stuff like :-moz-any, etc.
fn parse_inner_compound_selector<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
) -> Result<Selector<Impl>, ParseError<'i, P::Error>> where
P: Parser<'i, Impl = Impl>, Impl: SelectorImpl,
{
parse_selector(
parser,
input,
state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS,
ParseRelative::No,
)
}
/// Ancestor hashes for the bloom filter. We precompute these and store them /// inline with selectors to optimize cache performance during matching. /// This matters a lot. /// /// We use 4 hashes, which is copied from Gecko, who copied it from WebKit. /// Note that increasing the number of hashes here will adversely affect the /// cache hit when fast-rejecting long lists of Rules with inline hashes. /// /// Because the bloom filter only uses the bottom 24 bits of the hash, we pack /// the fourth hash into the upper bits of the first three hashes in order to /// shrink Rule (whose size matters a lot). This scheme minimizes the runtime /// overhead of the packing for the first three hashes (we just need to mask /// off the upper bits) at the expense of making the fourth somewhat more /// complicated to assemble, because we often bail out before checking all the /// hashes. #[derive(Clone, Debug, Eq, PartialEq)] pubstruct AncestorHashes { pub packed_hashes: [u32; 3],
}
pub(crate) fn collect_selector_hashes<'a, Impl: SelectorImpl, Iter>(
iter: Iter,
quirks_mode: QuirksMode,
hashes: &mut [u32; 4],
len: &mut usize,
create_inner_iterator: fn(&'a Selector<Impl>) -> Iter,
) -> bool where
Iter: Iterator<Item = &'a Component<Impl>>,
{
for component in iter { let hash = match *component {
Component::LocalName(LocalName { ref name, ref lower_name,
}) => { // Only insert the local-name into the filter if it's all // lowercase. Otherwise we would need to test both hashes, and // our data structures aren't really set up for that. if name != lower_name { continue;
}
name.precomputed_hash()
},
Component::DefaultNamespace(ref url) | Component::Namespace(_, ref url) => {
url.precomputed_hash()
}, // In quirks mode, class and id selectors should match // case-insensitively, so just avoid inserting them into the filter.
Component::ID(ref id) if quirks_mode != QuirksMode::Quirks => id.precomputed_hash(),
Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => {
class.precomputed_hash()
},
Component::AttributeInNoNamespace { ref local_name, .. } ifImpl::should_collect_attr_hash(local_name) =>
{ // AttributeInNoNamespace is only used when local_name == // local_name_lower.
local_name.precomputed_hash()
},
Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower,
..
} => { // Only insert the local-name into the filter if it's all // lowercase. Otherwise we would need to test both hashes, and // our data structures aren't really set up for that. if local_name != local_name_lower || !Impl::should_collect_attr_hash(local_name) { continue;
}
local_name.precomputed_hash()
},
Component::AttributeOther(ref selector) => { if selector.local_name != selector.local_name_lower
|| !Impl::should_collect_attr_hash(&selector.local_name)
{ continue;
}
selector.local_name.precomputed_hash()
},
Component::Is(ref list) | Component::Where(ref list) => { // :where and :is OR their selectors, so we can't put any hash // in the filter if there's more than one selector, as that'd // exclude elements that may match one of the other selectors. let slice = list.slice(); if slice.len() == 1
&& !collect_selector_hashes(
create_inner_iterator(&slice[0]),
quirks_mode,
hashes,
len,
create_inner_iterator,
)
{ returnfalse;
} continue;
},
_ => continue,
};
fn collect_ancestor_hashes<Impl: SelectorImpl>( mut iter: SelectorIter<Impl>,
quirks_mode: QuirksMode,
hashes: &mut [u32; 4],
len: &mut usize,
) -> bool { loop { whilelet Some(item) = iter.next() { iflet Component::Is(ref list) | Component::Where(ref list) = item { let slice = list.slice(); if slice.len() == 1
&& !collect_ancestor_hashes(slice[0].iter(), quirks_mode, hashes, len)
{ returnfalse;
}
}
} let Some(c) = iter.next_sequence() else { returntrue;
}; match c { // We got to an ancestor combinator, let collect_selector_hashes take it from there.
Combinator::Child | Combinator::Descendant => break,
Combinator::LaterSibling | Combinator::NextSibling => {
iter.skip_until_ancestor(); break;
}, // Keep scanning the subject for other potential ancestor combinators inside :where() // and :is(). Note that if this is ever changed to stop at the "pseudo-element" // combinator and treat it as a regular ancestor combinator, we will need to fix the way // we compute hashes for revalidation selectors.
Combinator::Part | Combinator::SlotAssignment | Combinator::PseudoElement => {},
}
}
// Now, pack the fourth hash (if it exists) into the upper byte of each of // the other three hashes. if len == 4 { let fourth = hashes[3];
hashes[0] |= (fourth & 0x000000ff) << 24;
hashes[1] |= (fourth & 0x0000ff00) << 16;
hashes[2] |= (fourth & 0x00ff0000) << 8;
}
pub(super) type SelectorData<Impl> = ThinArc<SpecificityAndFlags, Component<Impl>>;
/// Whether a selector may match a featureless host element, and whether it may match other /// elements. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubenum MatchesFeaturelessHost { /// The selector may match a featureless host, but also a non-featureless element.
Yes, /// The selector is guaranteed to never match a non-featureless host element.
Only, /// The selector never matches a featureless host.
Never,
}
impl MatchesFeaturelessHost { /// Whether we may match. #[inline] pub fn may_match(self) -> bool { return !matches!(self, Self::Never);
}
}
/// A Selector stores a sequence of simple selectors and combinators. The /// iterator classes allow callers to iterate at either the raw sequence level or /// at the level of sequences of simple selectors separated by combinators. Most /// callers want the higher-level iterator. /// /// We store compound selectors internally right-to-left (in matching order). /// Additionally, we invert the order of top-level compound selectors so that /// each one matches left-to-right. This is because matching namespace, local name, /// id, and class are all relatively cheap, whereas matching pseudo-classes might /// be expensive (depending on the pseudo-class). Since authors tend to put the /// pseudo-classes on the right, it's faster to start matching on the left. /// /// This reordering doesn't change the semantics of selector matching, and we /// handle it in to_css to make it invisible to serialization. #[derive(Clone, Eq, PartialEq)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] #[cfg_attr(feature = "to_shmem", shmem(no_bounds))] #[repr(transparent)] pubstruct Selector<Impl: SelectorImpl>( #[cfg_attr(feature = "to_shmem", shmem(field_bound))] SelectorData<Impl>,
);
/// Whether this selector (pseudo-element part excluded) matches every element. /// /// Used for "pre-computed" pseudo-elements in components/style/stylist.rs #[inline] pub fn is_universal(&self) -> bool { self.iter_raw_match_order().all(|c| {
matches!(
*c,
Component::ExplicitUniversalType
| Component::ExplicitAnyNamespace
| Component::Combinator(Combinator::PseudoElement)
| Component::PseudoElement(..)
)
})
}
/// Whether this selector may match a featureless shadow host, with no combinators to the /// left, and optionally has a pseudo-element to the right. #[inline] pub fn matches_featureless_host(
&self,
scope_matches_featureless_host: bool,
) -> MatchesFeaturelessHost { let flags = self.flags(); if !flags.intersects(SelectorFlags::HAS_HOST | SelectorFlags::HAS_SCOPE) { return MatchesFeaturelessHost::Never;
}
letmut iter = self.iter(); if flags.intersects(SelectorFlags::HAS_PSEUDO) {
for _ in &mut iter { // Skip over pseudo-elements
} match iter.next_sequence() {
Some(c) if c.is_pseudo_element() => {},
_ => {
debug_assert!(false, "Pseudo selector without pseudo combinator?"); return MatchesFeaturelessHost::Never;
},
}
}
let compound_matches = crate::matching::compound_matches_featureless_host(
&mut iter,
scope_matches_featureless_host,
); if iter.next_sequence().is_some() { return MatchesFeaturelessHost::Never;
} return compound_matches;
}
/// Returns an iterator over this selector in matching order (right-to-left). /// When a combinator is reached, the iterator will return None, and /// next_sequence() may be called to continue to the next sequence. #[inline] pub fn iter(&self) -> SelectorIter<'_, Impl> {
SelectorIter {
iter: self.iter_raw_match_order(),
next_combinator: None,
}
}
/// Same as `iter()`, but skips `RelativeSelectorAnchor` and its associated combinator. #[inline] pub fn iter_skip_relative_selector_anchor(&self) -> SelectorIter<'_, Impl> { if cfg!(debug_assertions) { letmut selector_iter = self.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"
);
}
/// Returns an iterator over this selector in matching order (right-to-left), /// skipping the rightmost |offset| Components. #[inline] pub fn iter_from(&self, offset: usize) -> SelectorIter<'_, Impl> { let iter = self.0.slice()[offset..].iter();
SelectorIter {
iter,
next_combinator: None,
}
}
/// Returns the combinator at index `index` (zero-indexed from the right), /// or panics if the component is not a combinator. #[inline] pub fn combinator_at_match_order(&self, index: usize) -> Combinator { matchself.0.slice()[index] {
Component::Combinator(c) => c, ref other => panic!( "Not a combinator: {:?}, {:?}, index: {}",
other, self, index
),
}
}
/// Returns an iterator over the entire sequence of simple selectors and /// combinators, in matching order (from right to left). #[inline] pub fn iter_raw_match_order(&self) -> slice::Iter<'_, Component<Impl>> { self.0.slice().iter()
}
/// Returns the combinator at index `index` (zero-indexed from the left), /// or panics if the component is not a combinator. #[inline] pub fn combinator_at_parse_order(&self, index: usize) -> Combinator { matchself.0.slice()[self.len() - index - 1] {
Component::Combinator(c) => c, ref other => panic!( "Not a combinator: {:?}, {:?}, index: {}",
other, self, index
),
}
}
/// Returns an iterator over the sequence of simple selectors and /// combinators, in parse order (from left to right), starting from /// `offset`. #[inline] pub fn iter_raw_parse_order_from(
&self,
offset: usize,
) -> Rev<slice::Iter<'_, Component<Impl>>> { self.0.slice()[..self.len() - offset].iter().rev()
}
/// Creates a Selector from a vec of Components, specified in parse order. Used in tests. #[allow(dead_code)] pub(crate) fn from_vec(
vec: Vec<Component<Impl>>,
specificity: u32,
flags: SelectorFlags,
) -> Self { letmut builder = SelectorBuilder::default();
for component in vec.into_iter() { iflet Some(combinator) = component.as_combinator() {
builder.push_combinator(combinator);
} else {
builder.push_simple_selector(component);
}
} let spec = SpecificityAndFlags { specificity, flags };
Selector(builder.build_with_specificity_and_flags(spec, ParseRelative::No))
}
/// Returns count of simple selectors and combinators in the Selector. #[inline] pub fn len(&self) -> usize { self.0.len()
}
/// Returns the address on the heap of the ThinArc for memory reporting. pub fn thin_arc_heap_ptr(&self) -> *const ::std::os::raw::c_void { self.0.heap_ptr()
}
/// Traverse selector components inside `self`. /// /// Implementations of this method should call `SelectorVisitor` methods /// or other impls of `Visit` as appropriate based on the fields of `Self`. /// /// A return value of `false` indicates terminating the traversal. /// It should be propagated with an early return. /// On the contrary, `true` indicates that all fields of `self` have been traversed: /// /// ```rust,ignore /// if !visitor.visit_simple_selector(&self.some_simple_selector) { /// return false; /// } /// if !self.some_component.visit(visitor) { /// return false; /// } /// true /// ``` pub fn visit<V>(&self, visitor: &mut V) -> bool where
V: SelectorVisitor<Impl = Impl>,
{ letmut current = self.iter(); letmut combinator = None; loop { if !visitor.visit_complex_selector(combinator) { returnfalse;
}
for selector in &mut current { if !selector.visit(visitor) { returnfalse;
}
}
combinator = current.next_sequence(); if combinator.is_none() { break;
}
}
true
}
/// Parse a selector, without any pseudo-element. #[inline] pub fn parse<'i, 't, P>(
parser: &P,
input: &mut CssParser<'i, 't>,
) -> Result<Self, ParseError<'i, P::Error>> where
P: Parser<'i, Impl = Impl>,
{
parse_selector(
parser,
input,
SelectorParsingState::empty(),
ParseRelative::No,
)
}
/// Is the compound starting at the offset the subject compound, or referring to its pseudo-element? pub fn is_rightmost(&self, offset: usize) -> bool { // There can really be only one pseudo-element, and it's not really valid for anything else to // follow it.
offset == 0
|| matches!( self.combinator_at_match_order(offset - 1),
Combinator::PseudoElement
)
}
}
impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { /// Prepares this iterator to point to the next sequence to the left, /// returning the combinator if the sequence was found. #[inline] pub fn next_sequence(&mutself) -> Option<Combinator> { self.next_combinator.take()
}
/// Skips a sequence of simple selectors and all subsequent sequences until /// a non-pseudo-element ancestor combinator is reached.
fn skip_until_ancestor(&mutself) { loop { whileself.next().is_some() {} ifself.next_sequence().is_none_or(|c| c.is_ancestor()) { break;
}
}
}
#[inline] pub(crate) fn matches_for_stateless_pseudo_element(&mutself) -> bool { let first = matchself.next() {
Some(c) => c, // Note that this is the common path that we keep inline: the // pseudo-element not having anything to its right.
None => returntrue,
}; self.matches_for_stateless_pseudo_element_internal(first)
}
#[inline(never)]
fn matches_for_stateless_pseudo_element_internal(&mutself, first: &Component<Impl>) -> bool { if !first.matches_for_stateless_pseudo_element() { returnfalse;
}
for component in self { // The only other parser-allowed Components in this sequence are // state pseudo-classes, or one of the other things that can contain // them. if !component.matches_for_stateless_pseudo_element() { returnfalse;
}
} true
}
/// Returns remaining count of the simple selectors and combinators in the Selector. #[inline] pub fn selector_length(&self) -> usize { self.iter.len()
}
}
impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> { type Item = &'a Component<Impl>;
impl<'a, Impl: SelectorImpl> fmt::Debug for SelectorIter<'a, Impl> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let iter = self.iter.clone().rev();
for component in iter {
component.to_css(f)?
}
Ok(())
}
}
/// An iterator over all combinators in a selector. Does not traverse selectors within psuedoclasses. struct CombinatorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>); impl<'a, Impl: 'a + SelectorImpl> CombinatorIter<'a, Impl> {
fn new(inner: SelectorIter<'a, Impl>) -> Self { letmut result = CombinatorIter(inner);
result.consume_non_combinators();
result
}
impl<'a, Impl: SelectorImpl> Iterator for CombinatorIter<'a, Impl> { type Item = Combinator;
fn next(&mutself) -> Option<Self::Item> { let result = self.0.next_sequence(); self.consume_non_combinators();
result
}
}
/// An iterator over all simple selectors belonging to ancestors. struct AncestorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>); impl<'a, Impl: SelectorImpl> Iterator for AncestorIter<'a, Impl> { type Item = &'a Component<Impl>;
fn next(&mutself) -> Option<Self::Item> { // Grab the next simple selector in the sequence if available. let next = self.0.next(); if next.is_some() { return next;
} // See if there are more sequences. If so, skip any non-ancestor sequences. if !self.0.next_sequence()?.is_ancestor() { self.0.skip_until_ancestor();
} self.0.next()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] pubenum Combinator {
Child, // >
Descendant, // space
NextSibling, // +
LaterSibling, // ~ /// A dummy combinator we use to the left of pseudo-elements. /// /// It serializes as the empty string, and acts effectively as a child /// combinator in most cases. If we ever actually start using a child /// combinator for this, we will need to fix up the way hashes are computed /// for revalidation selectors.
PseudoElement, /// Another combinator used for ::slotted(), which represent the jump from /// a node to its assigned slot.
SlotAssignment, /// Another combinator used for `::part()`, which represents the jump from /// the part to the containing shadow host.
Part,
}
impl Combinator { /// Returns true if this combinator is a pseudo-element combinator. #[inline] pub fn is_pseudo_element(&self) -> bool {
matches!(*self, Combinator::PseudoElement)
}
/// Returns true if this combinator is a next- or later-sibling combinator. #[inline] pub fn is_sibling(&self) -> bool {
matches!(*self, Combinator::NextSibling | Combinator::LaterSibling)
}
/// Returns true if this combinator represents a jump to an ancestor. Note that this includes /// combinators like ::part() / ::slotted() and pseudo-elements! #[inline] pub fn is_ancestor(&self) -> bool {
!self.is_sibling()
}
}
/// An enum for the different types of :nth- pseudoclasses #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] #[cfg_attr(feature = "to_shmem", shmem(no_bounds))] pubenum NthType {
Child,
LastChild,
OnlyChild,
OfType,
LastOfType,
OnlyOfType,
}
/// The properties that comprise an An+B syntax #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] #[cfg_attr(feature = "to_shmem", shmem(no_bounds))] pubstruct AnPlusB(pub i32, pub i32);
impl AnPlusB { #[inline] pub fn matches_index(&self, i: i32) -> bool { // Is there a non-negative integer n such that An+B=i? match i.checked_sub(self.1) {
None => false,
Some(an) => match an.checked_div(self.0) {
Some(n) => n >= 0 && self.0 * n == an,
None /* a == 0 */ => an == 0,
},
}
}
}
impl ToCss for AnPlusB { /// Serialize <an+b> (part of the CSS Syntax spec). /// <https://drafts.csswg.org/css-syntax-3/#serialize-an-anb-value> #[inline]
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where
W: fmt::Write,
{ match (self.0, self.1) {
(0, 0) => dest.write_char('0'),
/// Returns true if this is an edge selector that is not `:*-of-type`` #[inline] pub fn is_simple_edge(&self) -> bool { self.an_plus_b.0 == 0
&& self.an_plus_b.1 == 1
&& !self.ty.is_of_type()
&& !self.ty.is_only()
}
/// Returns the An+B part of the selector #[inline] pub fn nth_data(&self) -> &NthSelectorData {
&self.0.header
}
/// Returns the selector list part of the selector #[inline] pub fn selectors(&self) -> &[Selector<Impl>] { self.0.slice()
}
}
/// Flag indicating where a given relative selector's match would be contained. #[derive(Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] pubenum RelativeSelectorMatchHint { /// Within this element's subtree.
InSubtree, /// Within this element's direct children.
InChild, /// This element's next sibling.
InNextSibling, /// Within this element's next sibling's subtree.
InNextSiblingSubtree, /// Within this element's subsequent siblings.
InSibling, /// Across this element's subsequent siblings and their subtrees.
InSiblingSubtree,
}
impl RelativeSelectorMatchHint { /// Create a new relative selector match hint based on its composition. pub fn new(
relative_combinator: Combinator,
has_child_or_descendants: bool,
has_adjacent_or_next_siblings: bool,
) -> Self { match relative_combinator {
Combinator::Descendant => RelativeSelectorMatchHint::InSubtree,
Combinator::Child => { if !has_child_or_descendants {
RelativeSelectorMatchHint::InChild
} else { // Technically, for any composition that consists of child combinators only, // the search space is depth-constrained, but it's probably not worth optimizing for.
RelativeSelectorMatchHint::InSubtree
}
},
Combinator::NextSibling => { if !has_child_or_descendants && !has_adjacent_or_next_siblings {
RelativeSelectorMatchHint::InNextSibling
} elseif !has_child_or_descendants && has_adjacent_or_next_siblings {
RelativeSelectorMatchHint::InSibling
} elseif has_child_or_descendants && !has_adjacent_or_next_siblings { // Match won't cross multiple siblings.
RelativeSelectorMatchHint::InNextSiblingSubtree
} else {
RelativeSelectorMatchHint::InSiblingSubtree
}
},
Combinator::LaterSibling => { if !has_child_or_descendants {
RelativeSelectorMatchHint::InSibling
} else { // Even if the match may not cross multiple siblings, we have to look until // we find a match anyway.
RelativeSelectorMatchHint::InSiblingSubtree
}
},
Combinator::Part | Combinator::PseudoElement | Combinator::SlotAssignment => {
debug_assert!(false, "Unexpected relative combinator");
RelativeSelectorMatchHint::InSubtree
},
}
}
/// Is the match traversal direction towards the descendant of this element (As opposed to siblings)? pub fn is_descendant_direction(&self) -> bool {
matches!(*self, Self::InChild | Self::InSubtree)
}
/// Is the match traversal terminated at the next sibling? pub fn is_next_sibling(&self) -> bool {
matches!(*self, Self::InNextSibling | Self::InNextSiblingSubtree)
}
/// Does the match involve matching the subtree? pub fn is_subtree(&self) -> bool {
matches!(
*self, Self::InSubtree | Self::InSiblingSubtree | Self::InNextSiblingSubtree
)
}
}
/// Count of combinators in a given relative selector, not traversing selectors of pseudoclasses. #[derive(Clone, Copy)] pubstruct RelativeSelectorCombinatorCount {
relative_combinator: Combinator, pub child_or_descendants: usize, pub adjacent_or_next_siblings: usize,
}
impl RelativeSelectorCombinatorCount { /// Create a new relative selector combinator count from a given relative selector. pub fn new<Impl: SelectorImpl>(relative_selector: &RelativeSelector<Impl>) -> Self { letmut result = RelativeSelectorCombinatorCount {
relative_combinator: relative_selector.selector.combinator_at_parse_order(1),
child_or_descendants: 0,
adjacent_or_next_siblings: 0,
};
/// Get the match hint based on the current combinator count. pub fn get_match_hint(&self) -> RelativeSelectorMatchHint {
RelativeSelectorMatchHint::new( self.relative_combinator, self.child_or_descendants != 0, self.adjacent_or_next_siblings != 0,
)
}
}
/// Storage for a relative selector. #[derive(Clone, Eq, PartialEq)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] #[cfg_attr(feature = "to_shmem", shmem(no_bounds))] pubstruct RelativeSelector<Impl: SelectorImpl> { /// Match space constraining hint. pub match_hint: RelativeSelectorMatchHint, /// The selector. Guaranteed to contain `RelativeSelectorAnchor` and the relative combinator in parse order. #[cfg_attr(feature = "to_shmem", shmem(field_bound))] pub selector: Selector<Impl>,
}
bitflags! { /// Composition of combinators in a given selector, not traversing selectors of pseudoclasses. #[derive(Clone, Debug, Eq, PartialEq)] struct CombinatorComposition: u8 { const DESCENDANTS = 1 << 0; const SIBLINGS = 1 << 1;
}
}
impl<Impl: SelectorImpl> RelativeSelector<Impl> {
fn from_selector_list(selector_list: SelectorList<Impl>) -> Box<[Self]> {
selector_list
.slice()
.iter()
.map(|selector| { // It's more efficient to keep track of all this during the parse time, but that seems like a lot of special // case handling for what it's worth. if cfg!(debug_assertions) { let relative_selector_anchor = selector.iter_raw_parse_order_from(0).next();
debug_assert!(
relative_selector_anchor.is_some(), "Relative selector is empty"
);
debug_assert!(
matches!(
relative_selector_anchor.unwrap(),
Component::RelativeSelectorAnchor
), "Relative selector anchor is missing"
);
} // Leave a hint for narrowing down the search space when we're matching. let composition = CombinatorComposition::for_relative_selector(&selector); let match_hint = RelativeSelectorMatchHint::new(
selector.combinator_at_parse_order(1),
composition.intersects(CombinatorComposition::DESCENDANTS),
composition.intersects(CombinatorComposition::SIBLINGS),
);
RelativeSelector {
match_hint,
selector: selector.clone(),
}
})
.collect()
}
}
/// A CSS simple selector or combinator. We store both in the same enum for /// optimal packing and cache performance, see [1]. /// /// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973 #[derive(Clone, Eq, PartialEq)] #[cfg_attr(feature = "to_shmem", derive(ToShmem))] #[cfg_attr(feature = "to_shmem", shmem(no_bounds))] pubenum Component<Impl: SelectorImpl> {
LocalName(LocalName<Impl>),
AttributeInNoNamespaceExists { #[cfg_attr(feature = "to_shmem", shmem(field_bound))]
local_name: Impl::LocalName,
local_name_lower: Impl::LocalName,
}, // Used only when local_name is already lowercase.
AttributeInNoNamespace {
local_name: Impl::LocalName,
operator: AttrSelectorOperator, #[cfg_attr(feature = "to_shmem", shmem(field_bound))]
value: Impl::AttrValue,
case_sensitivity: ParsedCaseSensitivity,
}, // Use a Box in the less common cases with more data to keep size_of::<Component>() small.
AttributeOther(Box<AttrSelectorWithOptionalNamespace<Impl>>),
/// Pseudo-classes
Negation(SelectorList<Impl>),
Root,
Empty,
Scope, /// :scope added implicitly into scoped rules (i.e. In `@scope`) not /// explicitly using `:scope` or `&` selectors. /// /// https://drafts.csswg.org/css-cascade-6/#scoped-rules /// /// Unlike the normal `:scope` selector, this does not add any specificity. /// See https://github.com/w3c/csswg-drafts/issues/10196
ImplicitScope,
ParentSelector,
Nth(NthSelectorData),
NthOf(NthOfSelectorData<Impl>),
NonTSPseudoClass(#[cfg_attr(feature = "to_shmem", shmem(field_bound))] Impl::NonTSPseudoClass), /// The ::slotted() pseudo-element: /// /// https://drafts.csswg.org/css-scoping/#slotted-pseudo /// /// The selector here is a compound selector, that is, no combinators. /// /// NOTE(emilio): This should support a list of selectors, but as of this /// writing no other browser does, and that allows them to put ::slotted() /// in the rule hash, so we do that too. /// /// See https://github.com/w3c/csswg-drafts/issues/2158
Slotted(Selector<Impl>), /// The `::part` pseudo-element. /// https://drafts.csswg.org/css-shadow-parts/#part
Part(#[cfg_attr(feature = "to_shmem", shmem(field_bound))] Box<[Impl::Identifier]>), /// The `:host` pseudo-class: /// /// https://drafts.csswg.org/css-scoping/#host-selector /// /// NOTE(emilio): This should support a list of selectors, but as of this /// writing no other browser does, and that allows them to put :host() /// in the rule hash, so we do that too. /// /// See https://github.com/w3c/csswg-drafts/issues/2158
Host(Option<Selector<Impl>>), /// The `:where` pseudo-class. /// /// https://drafts.csswg.org/selectors/#zero-matches /// /// The inner argument is conceptually a SelectorList, but we move the /// selectors to the heap to keep Component small. Where(SelectorList<Impl>), /// The `:is` pseudo-class. /// /// https://drafts.csswg.org/selectors/#matches-pseudo /// /// Same comment as above re. the argument.
Is(SelectorList<Impl>), /// The `:has` pseudo-class. /// /// https://drafts.csswg.org/selectors/#has-pseudo /// /// Same comment as above re. the argument.
Has(Box<[RelativeSelector<Impl>]>), /// An invalid selector inside :is() / :where().
Invalid(Arc<String>), /// An implementation-dependent pseudo-element selector.
PseudoElement(#[cfg_attr(feature = "to_shmem", shmem(field_bound))] Impl::PseudoElement),
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 {
Component::Combinator(c) => Some(c),
_ => None,
}
}
/// Whether a given selector (to the right of a pseudo-element) should match for stateless /// pseudo-elements. Note that generally nothing matches for those, but since we have :not(), /// we still need to traverse nested selector lists.
fn matches_for_stateless_pseudo_element(&self) -> bool { match *self {
Component::Negation(ref selectors) => !selectors.slice().iter().all(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.matches_for_stateless_pseudo_element())
}),
Component::Is(ref selectors) | Component::Where(ref selectors) => {
selectors.slice().iter().any(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.matches_for_stateless_pseudo_element())
})
},
_ => false,
}
}
pub fn visit<V>(&self, visitor: &mut V) -> bool where
V: SelectorVisitor<Impl = Impl>,
{ useself::Component::*; if !visitor.visit_simple_selector(self) { returnfalse;
}
// Returns true if this has any selector that requires an index calculation. e.g. // :nth-child, :first-child, etc. For nested selectors, return true only if the // indexed selector is in its subject compound. pub fn has_indexed_selector_in_subject(&self) -> bool { match *self {
Component::NthOf(..) | Component::Nth(..) => returntrue,
Component::Is(ref selectors)
| Component::Where(ref selectors)
| Component::Negation(ref selectors) => { // Check the subject compound.
for selector in selectors.slice() { letmut iter = selector.iter(); whilelet Some(c) = iter.next() { if c.has_indexed_selector_in_subject() { returntrue;
}
}
}
},
_ => (),
}; false
}
}
fn serialize_selector_list<'a, Impl, I, W>(iter: I, dest: &mut W) -> fmt::Result where Impl: SelectorImpl,
I: Iterator<Item = &'a Selector<Impl>>,
W: fmt::Write,
{ letmut first = true;
for selector in iter { if !first {
dest.write_str(", ")?;
}
first = false;
selector.to_css(dest)?;
}
Ok(())
}
impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where
W: fmt::Write,
{ // Compound selectors invert the order of their contents, so we need to // undo that during serialization. // // This two-iterator strategy involves walking over the selector twice. // We could do something more clever, but selector serialization probably // isn't hot enough to justify it, and the stringification likely // dominates anyway. // // NB: A parse-order iterator is a Rev<>, which doesn't expose as_slice(), // which we need for |split|. So we split by combinators on a match-order // sequence and then reverse.
letmut combinators_exhausted = false;
for compound in compound_selectors {
debug_assert!(!combinators_exhausted);
// https://drafts.csswg.org/cssom/#serializing-selectors let first_compound = match compound.first() {
None => continue,
Some(c) => c,
}; if matches!(
first_compound,
Component::RelativeSelectorAnchor | Component::ImplicitScope
) {
debug_assert!(
compound.len() == 1, "RelativeSelectorAnchor/ImplicitScope should only be a simple selector"
); iflet Some(c) = combinators.next() {
c.to_css_relative(dest)?;
} else { // Direct property declarations in `@scope` does not have // combinators, since its selector is `:implicit-scope`.
debug_assert!(
matches!(first_compound, Component::ImplicitScope), "Only implicit :scope may not have any combinator"
);
} continue;
}
// 1. If there is only one simple selector in the compound selectors // which is a universal selector, append the result of // serializing the universal selector to s. // // Check if `!compound.empty()` first--this can happen if we have // something like `... > ::before`, because we store `>` and `::` // both as combinators internally. // // If we are in this case, after we have serialized the universal // selector, we skip Step 2 and continue with the algorithm. let (can_elide_namespace, first_non_namespace) = match compound[0] {
Component::ExplicitAnyNamespace
| Component::ExplicitNoNamespace
| Component::Namespace(..) => (false, 1),
Component::DefaultNamespace(..) => (true, 1),
_ => (true, 0),
}; letmut perform_step_2 = true; let next_combinator = combinators.next(); if first_non_namespace == compound.len() - 1 { match (next_combinator, &compound[first_non_namespace]) { // We have to be careful here, because if there is a // pseudo element "combinator" there isn't really just // the one simple selector. Technically this compound // selector contains the pseudo element selector as well // -- Combinator::PseudoElement, just like // Combinator::SlotAssignment, don't exist in the // spec.
(Some(Combinator::PseudoElement), _)
| (Some(Combinator::SlotAssignment), _) => (),
(_, &Component::ExplicitUniversalType) => { // Iterate over everything so we serialize the namespace // too.
for simple in compound.iter() {
simple.to_css(dest)?;
} // Skip step 2, which is an "otherwise".
perform_step_2 = false;
},
_ => (),
}
}
// 2. Otherwise, for each simple selector in the compound selectors // that is not a universal selector of which the namespace prefix // maps to a namespace that is not the default namespace // serialize the simple selector and append the result to s. // // See https://github.com/w3c/csswg-drafts/issues/1606, which is // proposing to change this to match up with the behavior asserted // in cssom/serialize-namespaced-type-selectors.html, which the // following code tries to match. if perform_step_2 {
for simple in compound.iter() { iflet Component::ExplicitUniversalType = *simple { // Can't have a namespace followed by a pseudo-element // selector followed by a universal selector in the same // compound selector, so we don't have to worry about the // real namespace being in a different `compound`. if can_elide_namespace { continue;
}
}
simple.to_css(dest)?;
}
}
// 3. If this is not the last part of the chain of the selector // append a single SPACE (U+0020), followed by the combinator // ">", "+", "~", ">>", "||", as appropriate, followed by another // single SPACE (U+0020) if the combinator was not whitespace, to // s. match next_combinator {
Some(c) => c.to_css(dest)?,
None => combinators_exhausted = true,
};
// 4. If this is the last part of the chain of the selector and // there is a pseudo-element, append "::" followed by the name of // the pseudo-element, to s. // // (we handle this above)
}
/// * `Err(())`: Invalid selector, abort /// * `Ok(false)`: Not a type selector, could be something else. `input` was not consumed. /// * `Ok(true)`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
fn parse_type_selector<'i, 't, P, Impl, S>(
parser: &P,
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
sink: &mut S,
) -> Result<bool, ParseError<'i, P::Error>> where
P: Parser<'i, Impl = Impl>, Impl: SelectorImpl,
S: Push<Component<Impl>>,
{ match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {
Err(ParseError {
kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
..
})
| Ok(OptionalQName::None(_)) => Ok(false),
Ok(OptionalQName::Some(namespace, local_name)) => { if state.intersects(SelectorParsingState::AFTER_PSEUDO) { return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
} match namespace {
QNamePrefix::ImplicitAnyNamespace => {},
QNamePrefix::ImplicitDefaultNamespace(url) => {
sink.push(Component::DefaultNamespace(url))
},
QNamePrefix::ExplicitNamespace(prefix, url) => {
sink.push(match parser.default_namespace() {
Some(ref default_url) if url == *default_url => {
Component::DefaultNamespace(url)
},
_ => Component::Namespace(prefix, url),
})
},
QNamePrefix::ExplicitNoNamespace => sink.push(Component::ExplicitNoNamespace),
QNamePrefix::ExplicitAnyNamespace => { match parser.default_namespace() { // Element type selectors that have no namespace // component (no namespace separator) represent elements // without regard to the element's namespace (equivalent // to "*|") unless a default namespace has been declared // for namespaced selectors (e.g. in CSS, in the style // sheet). If a default namespace has been declared, // such selectors will represent only elements in the // default namespace. // -- Selectors § 6.1.1 // So we'll have this act the same as the // QNamePrefix::ImplicitAnyNamespace case.
None => {},
Some(_) => sink.push(Component::ExplicitAnyNamespace),
}
},
QNamePrefix::ImplicitNoNamespace => {
unreachable!() // Not returned with in_attr_selector = false
},
} match local_name {
Some(name) => sink.push(Component::LocalName(LocalName {
lower_name: to_ascii_lowercase(&name).as_ref().into(),
name: name.as_ref().into(),
})),
None => sink.push(Component::ExplicitUniversalType),
}
Ok(true)
},
Err(e) => Err(e),
}
}
#[derive(Debug)] enum QNamePrefix<Impl: SelectorImpl> {
ImplicitNoNamespace, // `foo` in attr selectors
ImplicitAnyNamespace, // `foo` in type selectors, without a default ns
ImplicitDefaultNamespace(Impl::NamespaceUrl), // `foo` in type selectors, with a default ns
ExplicitNoNamespace, // `|foo`
ExplicitAnyNamespace, // `*|foo`
ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo`
}
/// * `Err(())`: Invalid selector, abort /// * `Ok(None(token))`: Not a simple selector, could be something else. `input` was not consumed, /// but the token is still returned. /// * `Ok(Some(namespace, local_name))`: `None` for the local name means a `*` universal selector
fn parse_qualified_name<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
in_attr_selector: bool,
) -> Result<OptionalQName<'i, Impl>, ParseError<'i, P::Error>> where
P: Parser<'i, Impl = Impl>, Impl: SelectorImpl,
{ let default_namespace = |local_name| { let namespace = match parser.default_namespace() {
Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
None => QNamePrefix::ImplicitAnyNamespace,
};
Ok(OptionalQName::Some(namespace, local_name))
};
let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| { let location = input.current_source_location(); match input.next_including_whitespace() {
Ok(&Token::Delim('*')) if !in_attr_selector => Ok(OptionalQName::Some(namespace, None)),
Ok(&Token::Ident(ref local_name)) => {
Ok(OptionalQName::Some(namespace, Some(local_name.clone())))
},
Ok(t) if in_attr_selector => { let e = SelectorParseErrorKind::InvalidQualNameInAttr(t.clone());
Err(location.new_custom_error(e))
},
Ok(t) => Err(location.new_custom_error(
SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t.clone()),
)),
Err(e) => Err(e.into()),
}
};
let start = input.state(); match input.next_including_whitespace() {
Ok(Token::Ident(value)) => { let value = value.clone(); let after_ident = input.state(); match input.next_including_whitespace() {
Ok(&Token::Delim('|')) => { let prefix = value.as_ref().into(); let result = parser.namespace_for_prefix(&prefix); let url = result.ok_or(
after_ident
.source_location()
.new_custom_error(SelectorParseErrorKind::ExpectedNamespace(value)),
)?;
explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
},
_ => {
input.reset(&after_ident); if in_attr_selector {
Ok(OptionalQName::Some(
QNamePrefix::ImplicitNoNamespace,
Some(value),
))
} else {
default_namespace(Some(value))
}
},
}
},
Ok(Token::Delim('*')) => { let after_star = input.state(); match input.next_including_whitespace() {
Ok(&Token::Delim('|')) => {
explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace)
},
_ if !in_attr_selector => {
input.reset(&after_star);
default_namespace(None)
},
result => { let t = result?;
Err(after_star
.source_location()
.new_custom_error(SelectorParseErrorKind::ExpectedBarInAttr(t.clone())))
},
}
},
Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitNoNamespace),
Ok(t) => {
let t = t.clone();
input.reset(&start);
Ok(OptionalQName::None(t))
},
Err(e) => {
input.reset(&start);
Err(e.into())
},
}
}
fn parse_attribute_selector<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
Impl: SelectorImpl,
{
let namespace;
let local_name;
let value = match input.expect_ident_or_string() {
Ok(t) => t.clone(),
Err(BasicParseError {
kind: BasicParseErrorKind::UnexpectedToken(t),
location,
}) => return Err(location.new_custom_error(SelectorParseErrorKind::BadValueInAttr(t))),
Err(e) => return Err(e.into()),
};
let attribute_flags = parse_attribute_flags(input)?;
let value = value.as_ref().into();
let local_name_lower;
let local_name_is_ascii_lowercase;
let case_sensitivity;
{
let local_name_lower_cow = to_ascii_lowercase(&local_name);
case_sensitivity =
attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some());
local_name_lower = local_name_lower_cow.as_ref().into();
local_name_is_ascii_lowercase = matches!(local_name_lower_cow, Cow::Borrowed(..));
}
let local_name = local_name.as_ref().into(); ifnamespace.is_some() || !local_name_is_ascii_lowercase {
Ok(Component::AttributeOther(Box::new(
AttrSelectorWithOptionalNamespace { namespace,
local_name,
local_name_lower,
operation: ParsedAttrSelectorOperation::WithValue { operator,
case_sensitivity,
value,
},
},
)))
} else {
Ok(Component::AttributeInNoNamespace {
local_name, operator,
value,
case_sensitivity,
})
}
}
/// An attribute selector can have 's' or 'i' as flags, or no flags at all. enum AttributeFlags { // Matching should be case-sensitive ('s' flag).
CaseSensitive, // Matching should be case-insensitive ('i' flag).
AsciiCaseInsensitive, // No flags. Matching behavior depends on the name of the attribute.
CaseSensitivityDependsOnName,
}
let mut empty = true; if parse_type_selector(parser, input, *state, builder)? {
empty = false;
}
loop {
let result = match parse_one_simple_selector(parser, input, *state)? {
None => break,
Some(result) => result,
};
if empty { if let Some(url) = parser.default_namespace() { // If there was no explicit type selector, but there is a // default namespace, there is an implicit "<defaultns>|*" type // selector. Except for :host() or :not() / :is() / :where(), // where we ignore it. // // https://drafts.csswg.org/css-scoping/#host-element-in-tree: // // When considered within its own shadow trees, the shadow // host is featureless. Only the :host, :host(), and // :host-context() pseudo-classes are allowed to match it. // // https://drafts.csswg.org/selectors-4/#featureless: // // A featureless element does not match any selector at all, // except those it is explicitly defined to match. If a // given selector is allowed to match a featureless element, // it must do so while ignoring the default namespace. // // https://drafts.csswg.org/selectors-4/#matches // // Default namespace declarations do not affect the compound // selector representing the subject of any selector within // a :is() pseudo-class, unless that compound selector // contains an explicit universal selector or type selector. // // (Similar quotes for :where() / :not()) //
let ignore_default_ns = state
.intersects(SelectorParsingState::SKIP_DEFAULT_NAMESPACE)
|| matches!(
result,
SimpleSelectorParseResult::SimpleSelector(Component::Host(..))
); if !ignore_default_ns {
builder.push_simple_selector(Component::DefaultNamespace(url));
}
}
}
empty = false;
match result {
SimpleSelectorParseResult::SimpleSelector(s) => {
builder.push_simple_selector(s);
},
SimpleSelectorParseResult::PartPseudo(part_names) => {
state.insert(SelectorParsingState::AFTER_PART_LIKE);
builder.push_combinator(Combinator::Part);
builder.push_simple_selector(Component::Part(part_names));
},
SimpleSelectorParseResult::SlottedPseudo(selector) => {
state.insert(SelectorParsingState::AFTER_SLOTTED);
builder.push_combinator(Combinator::SlotAssignment);
builder.push_simple_selector(Component::Slotted(selector));
},
SimpleSelectorParseResult::PseudoElement(p) => { if p.parses_as_element_backed() {
state.insert(SelectorParsingState::AFTER_PART_LIKE);
} else {
state.insert(SelectorParsingState::AFTER_NON_ELEMENT_BACKED_PSEUDO); if p.is_before_or_after() {
state.insert(SelectorParsingState::AFTER_BEFORE_OR_AFTER_PSEUDO);
}
} if !p.accepts_state_pseudo_classes() {
state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);
} if p.is_in_pseudo_element_tree() {
state.insert(SelectorParsingState::IN_PSEUDO_ELEMENT_TREE);
}
builder.push_combinator(Combinator::PseudoElement);
builder.push_simple_selector(Component::PseudoElement(p));
},
}
}
Ok(empty)
}
fn parse_is_where<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
component: impl FnOnce(SelectorList<Impl>) -> Component<Impl>,
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
Impl: SelectorImpl,
{
debug_assert!(parser.parse_is_and_where()); // https://drafts.csswg.org/selectors/#matches-pseudo: // // Pseudo-elements cannot be represented by the matches-any // pseudo-class; they are not valid within :is(). //
let inner = SelectorList::parse_with_state(
parser,
input,
state
| SelectorParsingState::SKIP_DEFAULT_NAMESPACE
| SelectorParsingState::DISALLOW_PSEUDOS,
ForgivingParsing::Yes,
ParseRelative::No,
)?;
Ok(component(inner))
}
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()); if state.intersects(
SelectorParsingState::DISALLOW_RELATIVE_SELECTOR | SelectorParsingState::AFTER_PSEUDO,
) { return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
} // Nested `:has()` is disallowed, mark it as such. // Note: The spec defines ":has-allowed pseudo-element," but there's no // pseudo-element defined as such at the moment. // https://w3c.github.io/csswg-drafts/selectors-4/#has-allowed-pseudo-element
let inner = SelectorList::parse_with_state(
parser,
input,
state
| SelectorParsingState::SKIP_DEFAULT_NAMESPACE
| SelectorParsingState::DISALLOW_PSEUDOS
| SelectorParsingState::DISALLOW_RELATIVE_SELECTOR,
ForgivingParsing::No,
ParseRelative::ForHas,
)?;
Ok(Component::Has(RelativeSelector::from_selector_list(inner)))
}
let after_part = state.intersects(SelectorParsingState::AFTER_PART_LIKE);
P::parse_non_ts_functional_pseudo_class(parser, name, input, after_part)
.map(Component:NonTSPseudoClass)
} java.lang.StringIndexOutOfBoundsException: Range [17, 16) out of bounds for length 27
fn utils/java.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 27
,
input: &mut CssParser<'i, 't>,
we topropagate toparallel java.lang.StringIndexOutOfBoundsException: Range [71, 70) out of bounds for length 71
ty
> <Impl> ', P::>
where
java.lang.StringIndexOutOfBoundsException: Index 80 out of bounds for length 31
Impl java.lang.StringIndexOutOfBoundsException: Range [13, 12) out of bounds for length 23
{ if !static bool ReindexIsCurrentlyProcessingIndex(Oid indexOid);
electorParseErrorKind:InvalidState);
}
let (a, b) = parse_nth(input)?;
let nth_data = NthSelectorData {
ty,
is_function: true,
an_plus_b: AnPlusB(a, b),
*we that be trueifsaidindexis invalid. if !parser.parse_nth_child_of() || ty.is_of_type() { return List *indexoidlist
}
// Try to parse "of <selector-list>". if input.try_parse(|i| i.expect_ident_matching("of")).is_err() { return Ok * (hopefullythereisn' than one such).
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 // Whitespace between "of" and the selector list is optional // https://github.com/w3c/csswg-drafts/issues/8285
java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 51
parser,
input,
state
| SelectorParsingState::SKIP_DEFAULT_NAMESPACE
| SelectorParsingState::DISALLOW_PSEUDOS,
ForgivingParsing::No,
ParseRelative::No,
)?;
Ok(Component::NthOf(NthOfSelectorData::new are NOTIf,java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
&nth_data,
own localALTER TABLE command 'work well ifwe'
)))
}
/// Returns whether the name corresponds to a CSS2 pseudo-element that /// can be specified with the single colon syntax (in addition to the /// double-colon syntax, which can be used for all pseudo-elements).
pub is_css2_pseudo_element &) > { // ** Do not add to this list! **
match_ignore_ascii_case! { java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 30 ""|after|firstline" "letter> java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
_ => false,
}
}
/// Parse a simple selector other than a type selector. /// /// * `Err(())`: Invalid selector, abort /// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed. /// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
java.lang.StringIndexOutOfBoundsException: Range [30, 28) out of bounds for length 46
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
) -> Result<Option<SimpleSelectorParseResult<Impl>>, ParseError<'i, P::java.lang.StringIndexOutOfBoundsException: Index 76 out of bounds for length 73
where
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
:SelectorImpl
{
let start = input.state();
let token = match input.next_including_whitespace().map(|t| t.clone()) {
t =>tjava.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
Err.)= {
reset(start; return Ok(None);
},
};
Somejava.lang.StringIndexOutOfBoundsException: Range [18, 17) out of bounds for length 25
Token::IDHash(id) => {
stateintersects(SelectorParsingState returnErr(input.new_custom_error(electorParseErrorKind;
}
letjava.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
SimpleSelectorParseResult::SimpleSelector(*
},
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1 if state. *java.lang.StringIndexOutOfBoundsException: Range [26, 25) out of bounds for length 65 return(.new_custom_error::InvalidState);
}
let location =heapTupDesc RelationGetDescrheapRelation
SimpleSelectorParseResult::SimpleSelector(if
Component::ParentSelector
} else {
let atnum >i;
TokenTup;
java.lang.StringIndexOutOfBoundsException: Range [5, 4) out of bounds for length 21
let e = SelectorParseErrorKind::ClassNeedsIdent(t.clone());
Errlocatione);
(* ;
};
Component::Class(class.as_ref().into())
})
},
Token::SquareBracketBlock = /* Simple index column */
.intersects:java.lang.StringIndexOutOfBoundsException: Range [67, 66) out of bounds for length 69 returnjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
to> ->ttalign;
java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
electorParseResult:attr
},
Token::Colon => {
let location = input.java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 6
Token::Colon => (false *
>(true )
};
to-attcompression= ;
Token:: * Make sure the expressiontypes java.lang.StringIndexOutOfBoundsException: Range [66, 65) out of bounds for length 68
Token::Function(name) => (name, true),
t => {
lete=java.lang.StringIndexOutOfBoundsException: Range [51, 50) out of bounds for length 82 return Err(input.new_custom_error(e));
},
;
let is_pseudo_element = !is_single_colon *whethera column is of a safe type (which is why we if is_pseudo_element { // Pseudos after pseudo elements are not allowed in some cases: / - Some states will disallow pseudos, such as the interiors of // :has/:is/:where/:not (DISALLOW_PSEUDOS). // - Non-element backed pseudos do not allow other pseudos to follow (AFTER_NON_ELEMENT_BACKED_PSEUDO)... // - ... except ::before and ::after, which allow _some_ pseudos.
*later.
||(state.intersects(SelectorParsingState:AFTER_NON_ELEMENT_BACKED_PSEUDO)
ntersects(SelectorParsingState:AFTER_BEFORE_OR_AFTER_PSEUDO)
{ return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
}
let pseudo_element = if(!tuple)
P:parse_part(parser)&namejava.lang.StringIndexOutOfBoundsException: Range [83, 73) out of bounds for length 83 if !state.allows_part() {
java.lang.StringIndexOutOfBoundsException: Range [35, 34) out of bounds for length 39
input.new_custom_error(SelectorParseErrorKind::InvalidState)
);
}
letnames inputparse_nested_block(|
let mut result = Vec::with_capacity(1);
{ whilejava.lang.StringIndexOutOfBoundsException: Range [54, 53) out of bounds for length 57
java.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 4
}
Ok(result.into_boxed_slice())
);
java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 3
} if to>=keyTypejava.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26 ifstate.) { return Err(
input.java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 3
java.lang.StringIndexOutOfBoundsException: Range [19, 18) out of bounds for length 72
&& !pseudo_element.valid_after_slotted()
{
Errinput.new_custom_error(SelectorParseErrorKind::InvalidState));
}
SimpleSelectorParseResult:PseudoElement(pseudo_element)
} else {
pseudo_class ifis_functional {
input.parse_nested_block(|input| {
java.lang.StringIndexOutOfBoundsException: Range [0, 53) out of bounds for length 1
})?
} else {
parse_simple_pseudo_class(parser, location, *---------
};
SimpleSelectorParseResult::SimpleSelector(pseudo_class)
}
},
_ => { isready
input java.lang.StringIndexOutOfBoundsException: Range [13, 11) out of bounds for length 23 return java.lang.StringIndexOutOfBoundsException: Range [0, 21) out of bounds for length 19
,
}))
}
parse_simple_pseudo_classi >
,
name: CowRcStr<'i>,
state: *
) - java.lang.StringIndexOutOfBoundsException: Range [13, 12) out of bounds for length 48
where
: i = >
Impl: SelectorImpl,
{ if !state.allows_non_functional_pseudo_classes() { return Err(location. ;
}predDatum CStringGetTextDatum
if statejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 // If a descendant pseudo of a pseudo-element root has no other siblings, then :only-child
matches pseudo. thatdon accept other tree java.lang.StringIndexOutOfBoundsException: Range [95, 94) out of bounds for length 97 // this case (to match other browsers). And the spec mentions only `:only-child` as well. // https://drafts.csswg.org/css-view-transitions-1/#pseudo-root if() {
name"-child" java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56 return BoolGetDatumfalsejava.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 64 /* of_type = */ false,
)));
// Other non-functional pseudo classes are not allowed.
Perhapswe canrefactor this e.. - pseudoclasses
/fromothernon-sjava.lang.StringIndexOutOfBoundsException: Range [47, 39) out of bounds for length 90 return Err(location.java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 4
}
letpseudo_class=P:parser,location, name)?; if state.intersects(SelectorParsingState::AFTER_NON_ELEMENT_BACKED_PSEUDO)
&& !pseudo_class.is_user_action_state tableSpaceId:OID touse
{
.(java.lang.StringIndexOutOfBoundsException: Range [69, 67) out of bounds for length 84
}
Ok(Component::NonTSPseudoClass(pseudo_class))
}
// NB: pub module in order to access the DummyParser
test
mod
use super::*;
use crate::builder::SelectorFlags;
use crate::parser;
use cssparser::{serialize_identifier, Parser as CssParser, ParserInput, ToCss};
usestd:collections:java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
use std::fmt;
#[derive(Clone, Debug, Eqjava.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
pub enum PseudoClass {
Hover,
Lang(String),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub const int16 coloptions,
Before,
After,
Marker,
java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 1
Highlight(tring),
}
willuse the relfilenumber map ifand only if the parent does; and it
matches!(self, Self::DetailsContent)
}
}
impl parser(- 1)
java.lang.StringIndexOutOfBoundsException: Range [43, 42) out of bounds for length 44
fn is_active_or_hover(&self) -> bool {
matches!(* *fine aslong as collation deterministic; then java.lang.StringIndexOutOfBoundsException: Range [73, 72) out of bounds for length 75
}
#[inline]
(&)->bool {
self.is_active_or_hover()
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
}
impl ToCss for PseudoClass {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
match *self
PseudoClass::Hover => dest.write_str(":hover"),
::java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 65
PseudoClass::Lang(ref lang) => {
dest. for (i = 0; i < indexInfo->ii_Num++
serialize_identifier(lang, dest)?;
dest.write_char( (concurrent java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 86
},
}
}
}
impl ToCss for
fnto_css<>&self dest:&W -fmt:Result
where
W: fmt::Write,
{
match*
PseudoElement::Before => dest.write_str("::before"),
PseudoElement::After => dest.write_str("::after"),
PseudoElement::Marker => dest.write_str("::marker"),
PseudoElement::DetailsContent => dest.write_str("::details-content"),
PseudoElement::Highlight(ref ((flags INDEX_CREATE_IF_NOT_EXISTS=0
dest.write_str("::highlight(errmsg(relation \%\"alreadyexists skipping",
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
dest.write_char(')')
( java.lang.StringIndexOutOfBoundsException: Range [42, 41) out of bounds for length 50
}
}
}
#[derive((ERRCODE_DUPLICATE_OBJECT)
java.lang.StringIndexOutOfBoundsException: Range [15, 14) out of bounds for length 33
#[derive(Default)]
pub struct DummyParser {
default_ns:,
ns_prefixes: HashMap *Allocate anOID for index unless weretold what use.
}
java.lang.StringIndexOutOfBoundsException: Range [25, 21) out of bounds for length 45
ExtraMatchingData<>=:PhantomData<&a(>java.lang.StringIndexOutOfBoundsException: Index 70 out of bounds for length 70
type we want= for The
java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
type LocalName = java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 6
type tableSpaceId,pg_class relpersistence);
type NamespacePrefix = DummyAtom;
type BorrowedLocalName = DummyAtom;
java.lang.StringIndexOutOfBoundsException: Range [46, 33) out of bounds for length 46
type NonTSPseudoClass ,
type PseudoElement = PseudoElement;
}
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
pub struct DummyAttrValue(String);
impl ToCss for DummyAttrValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
usestd:fmt::Write;
dest.write_char('"')?;
write!(:newjava.lang.StringIndexOutOfBoundsException: Range [59, 55) out of bounds for length 74
* ' java.lang.StringIndexOutOfBoundsException: Range [27, 26) out of bounds for length 32
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
}
impl ToCss for DummyAtom {
java.lang.StringIndexOutOfBoundsException: Range [11, 10) out of bounds for length 64
where
W: fmt::Write,
{
serialize_identifier(&self.0, dest)
}
}
impl From<String> for DummyAtom {
fn from(string: String) -> Self {
DummyAtom(string)
*maintainconsistency of its index list
}
impl PrecomputedHash for DummyAtom {
fnprecomputed_hash(&self)-> u32 {
self.0.as_ptr() as u32
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
}
impl<'i> Parser<'i> for DummyParser {
type Impl = DummySelectorImpl;
type Error = SelectorParseErrorKind<'i>;
fn java.lang.StringIndexOutOfBoundsException: Range [40, 24) out of bounds for length 41 true
}
fn parse_nth_child_of(&self) -> bool { true
}
fn parse_is_and_where localaddr
java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16
}
(self-bool true
java.lang.StringIndexOutOfBoundsException: Range [27, 26) out of bounds for length 27
fn parse_parent_selector(&self) {
}
fn parse_part { true
}
java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 5 true arenosimply theindexan
}
fn parse_non_ts_pseudo_class(
&self,
location: SourceLocation,
name: CowRcStr<'i>,
) -> Result
match_ignore_ascii_case! { & { "hover" => return ObjectAddressSet(referenced, Rel ObjectAddressSet(referenced, RelationRelationId, "active" => return Ok(PseudoClass::Active),
_ => {}
}
Err(
location.java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 4
name,
),
)
}
fn parse_non_ts_functional_pseudo_class<'t>(
&self,
name: CowRcStr<'i>,
parserjava.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 3
after_part: bool,
) -> Result<PseudoClass, SelectorParseError<'i>> {
match_ignore_ascii_case! { &name, "lang"if !after_part => {
let lang = parser. recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC return Ok(PseudoClass /* placeholder for normal dependencies */
},
_ => {}
}
Err(
&eferenced,addrs);
name, }
)),
)
}
fn parse_pseudo_elementjava.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
&self,
location: SourceLocation,
name: CowRcStr<'i>,
)
match_ignore_ascii_case! { "before"=> after"=>Ok:), "marker"=>returnOk(PseudoElement::Marker), "details-content"=>returnOk(PseudoElement::DetailsContent), _=>{} } Err( location.new_custom_error*Advancethecommandcountersothatwecanseethe-entered name,
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
fnparse_functional_pseudo_element<'t>( &self, :<i>java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 31 :&mutCssParser<i,'t> )->Result<PseudoElement,SelectorParseError<'i>>{ match_ignore_ascii_case!{&name, "highlight"=>returnOk(PseudoElement::Highlight(parser.expect_ident()?.as_ref().to_owned())), _=>{} } Err( parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement( name, )), ) }
fnjava.lang.StringIndexOutOfBoundsException: Range [22, 21) out of bounds for length 26 input:&'istr, parse_relative:ParseRelative )->Result<SelectorList<DummySelectorImpl>,SelectorParseError<t"thetablespacetouseforthisjava.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59 parse_ns_relative(inputjava.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21 }
fnparse_relative_expected<'i,'a>( input:&'istr, parse_relative:ParseRelative, Anum_pg_index_indoption); )->Result<SelectorList<DummySelectorImpl>,SelectorParseError/* Fetch reloptions of index if any */ parse_ns_relative_expected(input,&DummyParser::default(),ERROR"cachelookupfailedforrelation%u",oldIndexId); }
fnparse_ns_relative<'i>( input:&'istr, parser:&DummyParser, parse_relative:ParseRelative, -Result<SelectorList<DummySelectorImpl>,SelectorParseError<'i>>{ parse_ns_relative_expected(if(java.lang.StringIndexOutOfBoundsException: Range [13, 12) out of bounds for length 34 }
fnparse_ns_relative_expected<ia>( input:&'istr, , :ParseRelative expected:Option<&'a )>Result<DummySelectorImpl>SelectorParseError'i>{ letmutparser_input=ParserInput::new(input); letresult=SelectorList::parse( (inti=0;i-java.lang.StringIndexOutOfBoundsException: Range [47, 46) out of bounds for length 52 &mutCssParser::new(&mutparser_input), ); ifletOk(refselectors)=result{ // We can't assume that the serialized parsed selector will equal // should serialize to 'foo'. assert_eq!( selectors.to_css_string(), matchexpected{ ()=x None=>input, } } result }
fnspecificity(a:u32,b:u32,c:u32)->u32{ asetjava.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20 }
#[test] fntest_ancestor_hashes_in_subject_position(){ fnancestor_hash_count(selector:&str)->usize{ letlist=parse(selector).unwrap(); assert_eq|INDEX_CREATE_CONCURRENT, letmuthashes=[0u32;4]; letmutlen=0; collect_ancestor_hashes( list.java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 0 QuirksMode::NoQuirks, &muthashes, &java.lang.StringIndexOutOfBoundsException: Range [21, 20) out of bounds for length 25 ); len }
// Subject-only selectors don't contribute any ancestor hashes. assert_eq!(ancestor_hash_count(".subject"),0); assert_eq!(java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 3
// An ancestor combinator inside :is() / :where() in subject position /stillcontributeancestorhashes(ug assert_eq!(ancestor_hash_count(":where(.ancestor>.subject)"),1); assert_eq!(ancestor_hash_count(":is(.ancestor.subject)")/* Open and lock the parent heap relation */ assert_eq!( ancestor_hash_count(":where(.ancestor> java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 );
// Real ancestors combine with ancestor combinators nested in a subject // :where(). assert_eq!(
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 2 );
// :is() / :where() with more than one selector OR their selectors, so no // hash can be collected from them, even when they contain ancestor // combinators. assert_eq!(ancestor_hash_count(":is(.a,.b).subject"),0); assert_eq!( ancestor_hash_count(":where(.ancestor>.subject,.other)"), 0 ); // collected. assert_eq!(ancestor_hash_count(".real-ancestor:where(.a>.b,.c)"),1);
// Pseudo-elements match on their originating element, so simple // selectors in front of the pseudo-element combinator are part of the // subject and don't contribute ancestor hashes. assert_eq!(ancestor_hash_count(".subject::before"),0); assert_eq!(ancestor_hash_count(".real-ancestor.subject::before") // An ancestor combinator nested in a subject :where() is still collected // even when the subject carries a pseudo-element. ssert_eq java.lang.StringIndexOutOfBoundsException: Range [66, 23) out of bounds for length 76 1 ); }
#[test] fntest_empty(){ letmutinput=ParserInput::new("HeapTuple, letlist=SelectorList::parse( &:default()java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36 java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3 ParseRelative::No, ); newClassRel newIndexId,; }
#[test] test_parsing(){ assert!(parse("").is_err()); assert!(parse(:lang(4)".is_err(); assert!(parse(":lang(enUS)").is_err()); assert_eq!( parse("EeÉ"), Ok(SelectorList::from_vec(vec![Selector::from_vec( ({ name:DummyAtom::from("EeÉ"), lower_name:DummyAtom::from("eeÉ"newClassTuple); })], specificity(0,0,1), ::empty() )])) ); assert_eq!( parse("|e"), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::ExplicitNoNamespace, Component::LocalName(LocalName{ name:DummyAtom::from("e"), lower_name:DummyAtom::from("e"), }), ], specificity(0,0,1), SelectorFlags::empty(), )])) ); // When the default namespace is not set, *| should be elided. // https://github.com/servo/servo/pull/17537 assert_eq!( parse_expected("*|e",Some("e")), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::LocalName(LocalNamejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 name:DummyAtom::from("e"), lower_name:DummyAtom::from("e"), })], specificity(0,,1, SelectorFlags::empty(), )])) ); // When the default namespace is set, *| should _not_ be elided (as foo // is no longer equivalent to *|foo--the former is only for foo in the // https://github.com/servo/servo/issues/16020 assert_eq!( parse_ns( "*|e", &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org")) ), java.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 34 vec![ Component::ExplicitAnyNamespace, Component::LocalName(LocalName{ name:DummyAtom::from("e"), lower_name:DummyAtom::from"", }), ], specificity(0,0,1), SelectorFlags::empty(), tgForm=(GETSTRUCT(; ); assert_eq!(
assert_eq!( parse_expected("*|*",Some("*")), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::ExplicitUniversalType], specificity(0,0,0), SelectorFlags::empty(), )])) ); assert_eq!(
java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21 "*|*"} &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org")) ), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::ExplicitAnyNamespace, Component::ExplicitUniversalType, ], specificity(0,0,0)java.lang.StringIndexOutOfBoundsException: Index 66 out of bounds for length 66 SelectorFlags::emptyeLock); )])) ); java.lang.StringIndexOutOfBoundsException: Range [18, 17) out of bounds for length 19 parse(".foo:lang(en-US)"), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::Class(DummyAtom::from("foo")), Component:java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1 ], specificity(0,2,0), SelectorFlags::empty(), )])) */ assert_eq!( parse( Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::ID(DummyAtom::from("bar"))], specificity(1,0,0), SelectorFlags::empty(), )])) ); assert_eq!( parse("e.foo#bar"), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::LocalName(LocalName{ index_close(userIndexRelation,NoLock)java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40 lower_name:DummyAtom::from("e"), }), *:tablethe(mustsuitablylockedcaller) Component::ID(DummyAtom::from("bar")), ], specificity(1 SelectorFlags::empty(), )])) ); * flags: bitmaskthatcanincludeanycombinationofthesebits: parse("e.foo#bar"), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component:LocalName(LocalName{ * allow_system_table_modsallow_system_table_mods:allowtabletobeasystemcatalog :DummyAtom:from(e), }), Component:(::(foo)) Component::Combinator(CombinatorDescendant, bool allow_system_table_mods ], specificity(1,1,1), SelectorFlags::empty(), )])) ); // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 letmutparser=DummyParser:default(); assert_eq!( parse_ns("[Foo]",&parser), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![java.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 62 local_name:DummyAtom::fromelogEconstraintscannothaveindex) local_name_lower:DummyAtom::from("foo"), }], specificity(0,1,0), *Note:thisnecessarilydorightthingiftheindex )])) ); assert!(parse_ns("svg|circle",deleteDependencyRecordsForClass(elationRelationId,, parser .ns_prefixes .DummyAtom(svg"into()java.lang.StringIndexOutOfBoundsException: Range [55, 54) out of bounds for length 68 assert_eq!( parse_ns("svg|circle",&parser), OkSelectorList:from_vec(ec![Selector:f( vec![ Component::Namespace(DummyAtom("svg".java.lang.StringIndexOutOfBoundsException: Range [0, 61) out of bounds for length 34 Component::LocalName(LocalName{ name:DummyAtom::from("circle"), lower_name:DummyAtom::from("circle"), }), '' specificity(0,0,1), SelectorFlags::empty(), )])) ); assert_eq!( parse_ns("svg|*",&parser), Ok(SelectorList*Registerindexinternallydependenton. vec![ Component::Namespace(DummyAtom("svg".into()),SVG.into()), ComponentObjectAddressSet(idxaddr,RelationRelationId,indexRelationId); ], specificity(0,0,0), SelectorFlags::empty(), )])) ); // Default namespace does not apply to attribute selectors // https://github.com/mozilla/servo/pull/1652 // but it does apply to implicit type selectors // https://github.com/servo/rust-selectors/pull/82 parser.default_ns=ObjectAddressSet(referenced,RelationRelationId, assert_eq!( parse_ns("[Foo]",&parser), Okjava.lang.StringIndexOutOfBoundsException: Range [24, 21) out of bounds for length 67 vec![ Component::DefaultNamespace(MATHML.into()), Component::AttributeInNoNamespaceExists{ local_name:DummyAtom::from("PK_ConstraintTrigger": local_name_lower:DummyAtom::from("foo")java.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 27 }, ], specificity(0,1,0), SelectorFlags::empty(), )])) );,indexRelationId,InvalidOid // Default namespace does apply to type selectors ! parse_ns("e",&parser), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::DefaultNamespace(MATHML.into()), Component(ark_as_primary||deferrable)) name:DummyAtom::fromjava.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20 lower_name:DummyAtom::fromjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 }), ], specificity(0,0,1), SelectorFlags::empty(), )])) ); if(&-indimmediate) parse_ns("*",&parser), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component:DefaultNamespace(into()) , ], specificity(0,0,0), SelectorFlags::empty(), )])) ); assert_eq!( parse_ns("*|*",&parser), Ok(SelectorList::from_vec vec![ Component::ExplicitAnyNamespace, Component:Ejava.lang.StringIndexOutOfBoundsException: Range [53, 52) out of bounds for length 53 ], specificity(0,0,0), SelectorFlags::empty(), )])) ); // Default namespace applies to universal and type selectors inside :not and :matches, nototherwise. assert_eq!( parse_ns(":not(.cl)",&parser), Ok(SelectorList vec!c&!concurrent_lock_mode) :java.lang.StringIndexOutOfBoundsException: Range [48, 47) out of bounds for length 63 Component::Negation(SelectorList::from_vec(vec*anotherbackendmightbeabouttoexecuteaqueryontheparenttable. vec![Component::Class(DummyAtom::from("cl"))], specificity(0,1,0), SelectorFlags::empty(), )])), ], specificity(0,1,0), SelectorFlags::empty(), )])) ); ( parse_ns(":not(*)",&parser), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::DefaultNamespace(MATHML.into()), Component::Negation(SelectorListCheckTableNotInUse(userIndexRelation,"INDEX"; vec![ Component::DefaultNamespace(MATHML.into()*Indexjava.lang.StringIndexOutOfBoundsException: Range [23, 22) out of bounds for length 23 Component::ExplicitUniversalType, ], specificity(0,0,0), SelectorFlags::empty(), )]),), ], specificity(0,0,0), SelectorFlags::empty(), )])) ); assert_eq!( parse_ns(":not(e)",&parser), Ok(SelectorList:*falseThatsjava.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 75 vec![topredicatelocksontheheap.Inthe Component::DefaultNamespace(MATHML.into()), Component::Negation(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::DefaultNamespace(MATHML.into()), Component::LocalName(LocalName{ name:DummyAtom::from("e"), lower_name:DummyAtom::from("e"), }), ], specificity(0,0,1), SelectorFlags::empty(), )])), ], specificity(0,0,1), SelectorFlags::empty(), )])) ); assert_eq!( parse("[attr|=\"foo\"]"), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::AttributeInNoNamespace{ * transaction has not executedtransactionalupdateschecking operator:AttrSelectorOperator::DashMatch, value::from"oo), case_sensitivity:ParsedCaseSensitivity::CaseSensitive, }], specificity(0,1,0), SelectorFlags::empty(), )])) ); // https://github.com/mozilla/servo/issues/1723 assert_eq!( parse(:before", Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::Combinator(Combinator::PseudoElement), Component::PseudoElement(PseudoElement::Before), ], specificity(0,0,1), SelectorFlags::HAS_PSEUDO, )] ); assert_eq!( parse("::before:hover"), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![ Component::Combinator(Combinator::PseudoElement), * Before committing, get alevellockontableto Component::NonTSPseudoClass(PseudoClass::Hover), ], specificity(0,1, samelockwithinjava.lang.StringIndexOutOfBoundsException: Range [58, 57) out of bounds for length 58 SelectorFlags::HAS_PSEUDO, )])) )java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10 assert_eq!( parse("::before:hover:hover"), Ok(SelectorList:from_vecvec[:( vec! Component::Combinator(Combinator::PseudoElement), Component::PseudoElement Component::NonTSPseudoClass(PseudoClass::Hover), Component::NonTSPseudoClass(PseudoClass::Hover), ], specificity(0,2,1), SelectorFlags::HAS_PSEUDO, )])) ); assert!(parse(":: * here, even though it willhere,evenwillbeused'ecalledby assert!(parse("::before:hover.foo").is_err()); assert!(parse("::before.foo").is_err*Updatingpg_indexinvolveTOASTtable,sowe assert!(parse("::before~bar").is_err()); assert!(parse("::before:active").is_ok());
// https://github.com/servo/servo/issues/15335 assert!(parse("::before").is_err()); assert_eq!( parse("div::after"), Ok(CommitTransactionCommand(); vec![ Component::LocalName(LocalName{ name:DummyAtom::from("div"), lower_name:DummyAtom::from("div"), }/* Component::Combinator(Combinator::Descendant), Component::Combinator(Combinator::PseudoElement), Component::PseudoElement(PseudoElement::After), ], specificity(0,0,2), SelectorFlags::HAS_PSEUDO, )])) ); assert_eq!( parse("#d1>.ok"), (:java.lang.StringIndexOutOfBoundsException: Range [38, 37) out of bounds for length 62 vec![ Component::ID(DummyAtom::from("d1")), Component::Combinator(Combinator::Child), Component::Class(DummyAtom::from("ok")), ], specificity(1,1,0), SelectorFlags::empty(), )])) ); parser.default_ns=None; assert!(parse((#.))is_ok() assert!(parse(":not(#provel assert!(parse("tablejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 // https://github.com/servo/servo/issues/16017 assert_eq!( ), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::Negation(SelectorList::from_vec(vec![ Selector:from_vec( vec![Component::ExplicitUniversalType], specificity(0,0,0), SelectorFlags::empty(), ) ]))], specificity(00,,0, )])) ); assert_eq!( parse_ns(":not(|*)",&parser), Ok(SelectorList::from_vec(vec![Selector::from_vec !Component:N(SelectorList:from_vec(ec![ Selector::from_vec( vec![ Component::ExplicitNoNamespaceRemoveStatistics(0)
Component::ExplicitUniversalType,
],
specificity
SelectorFlags::empty(),
)
]))],
specificity(0, 0, 0),
SelectorFlags::empty(),
)]))
);
java.lang.StringIndexOutOfBoundsException: Index 64 out of bounds for length 64 // https://github.com/servo/servo/pull/17537
assert_eq!(
parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")),
Ok(SelectorList::from_vec(vec other update theirrelcache lists ofindexes (In the
vec![Component::Negation( * concurrent case, this is redundant but harmless
Selector::from_vec java.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 43
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0),
SelectorFlags::empty(),
)
]))],
specificity(0, 0, 0),
SelectorFlags::empty(),
)]))
);
assert!(parse("::highlight(foo/
assert!(parse("::slotted()").is_err());
assert!(parse("::slotted(div)").is_ok());
assert!(parse("::slotted(div).foo").is_err());
assert!(parse("::slotted(div + bar)").is_err());
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
assert!(parse("div ::slotted(div)").is_ok());
assert!(parse("div + slot::slotted(div)").is_ok());
assert!(parse("div + slot::slotted(div.foo)").is_ok());
assert!(parse("slot::slotted(div,foo)::first-line").is_err());
java.lang.StringIndexOutOfBoundsException: Range [15, 14) out of bounds for length 57
assert!/*
"wjava.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 46
assert!/ assert!(parse("foo:where(::before)").is_ok()); }
#[test] fnparent_selector(){ assert!(parse("foo&").is_ok()); assert_eq!( parse("#foo&.bar"), Oki=0java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30 vecif(indexStruct->indisexclusion) Component::ID(DummyAtom::from("foo")), Component::Combinator(Combinator::Descendant), Component::&-java.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 34 Component::Class(DummyAtom::from("bar")), ], specificity(1,1,0), SelectorFlags::HAS_PARENT )])) );
java.lang.StringIndexOutOfBoundsException: Range [19, 18) out of bounds for length 54 letchild=parse("#foo&.bar").unwrap(); assert_eq!( 'sdatajava.lang.StringIndexOutOfBoundsException: Range [73, 72) out of bounds for length 75 parse("#foo:is(.bar,div.baz).bar").unwrap() );
lethas_child=parse("#foo:has(&.bar)").unwrap(); assert_eq!( has_child.replace_parent_selector(&parent), parse("#foo:has(:is(.bar/* check the number of keys, and copy attr numbers into the IndexInfo */
letchild= parse_relative_expected("+#foo",ParseRelative::ForNesting,Some("&+#foo")).java.lang.StringIndexOutOfBoundsException: Index 96 out of bounds for length 39 assert_eq!(child,parse("&+#foo").unwrap()); }
#[test] fntest_pseudo_iter(){ letlist=parse("q::before").unwrap(); letselector=&list.slice()[0]; assert!(!selector.is_universal()); letmutthemtomaybetterhereandelsewhere. assert_eq!( iter.next(), Some(Component::seudoElement(seudoElement:Before)java.lang.StringIndexOutOfBoundsException: Index 66 out of bounds for length 66 ; assert_eq!(iter.next(),None); letcombinator=iter.next_sequenceconstOid*pfamilies1,constOidopfamilies2 assert_eq!(combinator,Some(Combinator::PseudoElement)); assert_eq!( iter.next(), (&:LocalName(LocalName{ name:DummyAtom::from("q"), lower_name:DummyAtom::from("q"), } ) assert_eq!(iter.next(),None); assert_eq!(iter.next_sequence(),None); }
#[test] fntest_pseudo_before_marker(){ letlist=parse("::before:: * might differ!) Note that this checkscolumnsare letselector=&list.slice()[0]; letmutiter=selector.iter(); assert_eq!( ) Some(&Component::PseudoElement(PseudoElement::Marker)) ); assert_eq!(iter.next(),None); letcombinator=iter.next_sequence(); assert_eq!(combinator,Some(Combinator::PseudoElement)); assert_eq!( iter.next(), Some(&Component::PseudoElement(PseudoElement::Before)) ); assert_eq!(iter.next(),None); letcombinator=iter.next_sequence(); assert_eq!(combinator,Some(Combinator::PseudoElement)); assert_eq!((i=java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 39 assert_eq!(iter.next_sequence(),None); }
#[test] fntest_pseudo_on_element_backed_pseudo(){ letlist=parse("::details-content::before").unwrap(); letselector=&list.if(>,mapped) letmutiter=selector.iter(); if(info1>=NULL)-ii_Predicate=)java.lang.StringIndexOutOfBoundsException: Index 68 out of bounds for length 68 iter.next(), Some(&Componentmapped=map_variable_attnos(Nodejava.lang.StringIndexOutOfBoundsException: Index 60 out of bounds for length 60 ); assert_eq!(iter.next(),None); letcombinator=iter.next_sequence(); assert_eq!(combinator,Some(Combinator::PseudoElement)); assert_eq!( iter.next(), Some(&Component::PseudoElement(PseudoElement::DetailsContent)) ); assert_eq!(iter.next(),None); letcombinator=iter.next_sequence(); assert_eq!(combinator,Some(Combinator::PseudoElement)); assert_eq!(iter.next(),None); BuildSpeculativeIndexInfo }
assert_eq!( parse_relative_expected(">.foo"(indexInfo->ii_Expressions,estate)java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58 SelectorList:: indexpr_item = list_head(indexInfo)java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58 vec![ keycolindexInfo-ii_IndexAttrNumbers[i] Component::Combinator(Combinator::Child), Component::Class(DummyAtom::from("foo")), ], specificity(0,1,0), SelectorFlags::HAS_SCOPE )]) );
assert_eqjava.lang.StringIndexOutOfBoundsException: Range [16, 10) out of bounds for length 17 parse_relative_expected(".foo:scope>.bar",ParseRelative SelectorList::from_vec(vec![Selector::from_vec( vec![ elog(,"wrongnumberofindexexpressions"); Component::Combinator(Combinator::Descendant), Component::Scope, Component::Combinator(Combinator::Child), Component::Class(DummyAtom::from("bar")), ], 30), SelectorFlags::HAS_SCOPE )]) ); }
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.