mirror of
https://github.com/servo/servo.git
synced 2025-06-18 13:24:29 +00:00
selectors: Add parsing support for ::slotted().
Without turning it on yet, of course. The reason why I didn't use the general PseudoElement mechanism is because this pseudo is a bit of its own thing, and I found easier to make ::selectors know about it (because you need to jump to the assigned slot) than the other way around. Also, we need to support ::slotted(..)::before and such, and supporting multiple pseudo-elements like that breaks some other invariants around the SelectorMap, and fixing those would require special-casing slotted a lot more in other parts of the code. Let me know if you think otherwise. I also don't like much the boolean tuple return value, but I plan to do some cleanup in the area in a bit, so it should go away soon, I'd hope.
This commit is contained in:
parent
0fa605d243
commit
7886e033aa
4 changed files with 175 additions and 42 deletions
|
@ -264,12 +264,23 @@ fn complex_selector_specificity<Impl>(mut iter: slice::Iter<Component<Impl>>)
|
||||||
-> Specificity
|
-> Specificity
|
||||||
where Impl: SelectorImpl
|
where Impl: SelectorImpl
|
||||||
{
|
{
|
||||||
fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
|
fn simple_selector_specificity<Impl>(
|
||||||
specificity: &mut Specificity)
|
simple_selector: &Component<Impl>,
|
||||||
where Impl: SelectorImpl
|
specificity: &mut Specificity,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
Impl: SelectorImpl
|
||||||
{
|
{
|
||||||
match *simple_selector {
|
match *simple_selector {
|
||||||
Component::Combinator(..) => unreachable!(),
|
Component::Combinator(..) => unreachable!(),
|
||||||
|
// FIXME(emilio): Spec doesn't define any particular specificity for
|
||||||
|
// ::slotted(), so apply the general rule for pseudos per:
|
||||||
|
//
|
||||||
|
// https://github.com/w3c/csswg-drafts/issues/1915
|
||||||
|
//
|
||||||
|
// Though other engines compute it dynamically, so maybe we should
|
||||||
|
// do that instead, eventually.
|
||||||
|
Component::Slotted(..) |
|
||||||
Component::PseudoElement(..) |
|
Component::PseudoElement(..) |
|
||||||
Component::LocalName(..) => {
|
Component::LocalName(..) => {
|
||||||
specificity.element_selectors += 1
|
specificity.element_selectors += 1
|
||||||
|
|
|
@ -393,6 +393,9 @@ where
|
||||||
|
|
||||||
element.parent_element()
|
element.parent_element()
|
||||||
}
|
}
|
||||||
|
Combinator::SlotAssignment => {
|
||||||
|
element.assigned_slot()
|
||||||
|
}
|
||||||
Combinator::PseudoElement => {
|
Combinator::PseudoElement => {
|
||||||
element.pseudo_element_originating_element()
|
element.pseudo_element_originating_element()
|
||||||
}
|
}
|
||||||
|
@ -453,6 +456,7 @@ where
|
||||||
}
|
}
|
||||||
Combinator::Child |
|
Combinator::Child |
|
||||||
Combinator::Descendant |
|
Combinator::Descendant |
|
||||||
|
Combinator::SlotAssignment |
|
||||||
Combinator::PseudoElement => {
|
Combinator::PseudoElement => {
|
||||||
SelectorMatchingResult::NotMatchedGlobally
|
SelectorMatchingResult::NotMatchedGlobally
|
||||||
}
|
}
|
||||||
|
@ -541,6 +545,21 @@ where
|
||||||
{
|
{
|
||||||
match *selector {
|
match *selector {
|
||||||
Component::Combinator(_) => unreachable!(),
|
Component::Combinator(_) => unreachable!(),
|
||||||
|
Component::Slotted(ref selectors) => {
|
||||||
|
context.shared.nesting_level += 1;
|
||||||
|
let result =
|
||||||
|
element.assigned_slot().is_some() &&
|
||||||
|
selectors.iter().any(|s| {
|
||||||
|
matches_complex_selector(
|
||||||
|
s.iter(),
|
||||||
|
element,
|
||||||
|
context.shared,
|
||||||
|
flags_setter,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
context.shared.nesting_level -= 1;
|
||||||
|
result
|
||||||
|
}
|
||||||
Component::PseudoElement(ref pseudo) => {
|
Component::PseudoElement(ref pseudo) => {
|
||||||
element.match_pseudo_element(pseudo, context.shared)
|
element.match_pseudo_element(pseudo, context.shared)
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,11 @@ pub trait Parser<'i> {
|
||||||
is_css2_pseudo_element(name)
|
is_css2_pseudo_element(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether to parse the `::slotted()` pseudo-element.
|
||||||
|
fn parse_slotted(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// This function can return an "Err" pseudo-element in order to support CSS2.1
|
/// This function can return an "Err" pseudo-element in order to support CSS2.1
|
||||||
/// pseudo-elements.
|
/// pseudo-elements.
|
||||||
fn parse_non_ts_pseudo_class(
|
fn parse_non_ts_pseudo_class(
|
||||||
|
@ -353,6 +358,13 @@ impl<Impl: SelectorImpl> SelectorMethods for Component<Impl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
|
Slotted(ref selectors) => {
|
||||||
|
for selector in selectors.iter() {
|
||||||
|
if !selector.visit(visitor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Negation(ref negated) => {
|
Negation(ref negated) => {
|
||||||
for component in negated.iter() {
|
for component in negated.iter() {
|
||||||
if !component.visit(visitor) {
|
if !component.visit(visitor) {
|
||||||
|
@ -658,15 +670,20 @@ pub enum Combinator {
|
||||||
/// combinator for this, we will need to fix up the way hashes are computed
|
/// combinator for this, we will need to fix up the way hashes are computed
|
||||||
/// for revalidation selectors.
|
/// for revalidation selectors.
|
||||||
PseudoElement,
|
PseudoElement,
|
||||||
|
/// Another combinator used for ::slotted(), which represent the jump from
|
||||||
|
/// a node to its assigned slot.
|
||||||
|
SlotAssignment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Combinator {
|
impl Combinator {
|
||||||
/// Returns true if this combinator is a child or descendant combinator.
|
/// Returns true if this combinator is a child or descendant combinator.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_ancestor(&self) -> bool {
|
pub fn is_ancestor(&self) -> bool {
|
||||||
matches!(*self, Combinator::Child |
|
matches!(*self,
|
||||||
|
Combinator::Child |
|
||||||
Combinator::Descendant |
|
Combinator::Descendant |
|
||||||
Combinator::PseudoElement)
|
Combinator::PseudoElement |
|
||||||
|
Combinator::SlotAssignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this combinator is a pseudo-element combinator.
|
/// Returns true if this combinator is a pseudo-element combinator.
|
||||||
|
@ -716,16 +733,16 @@ pub enum Component<Impl: SelectorImpl> {
|
||||||
// Use a Box in the less common cases with more data to keep size_of::<Component>() small.
|
// Use a Box in the less common cases with more data to keep size_of::<Component>() small.
|
||||||
AttributeOther(Box<AttrSelectorWithNamespace<Impl>>),
|
AttributeOther(Box<AttrSelectorWithNamespace<Impl>>),
|
||||||
|
|
||||||
// Pseudo-classes
|
/// Pseudo-classes
|
||||||
//
|
///
|
||||||
// CSS3 Negation only takes a simple simple selector, but we still need to
|
/// CSS3 Negation only takes a simple simple selector, but we still need to
|
||||||
// treat it as a compound selector because it might be a type selector which
|
/// treat it as a compound selector because it might be a type selector
|
||||||
// we represent as a namespace and a localname.
|
/// which we represent as a namespace and a localname.
|
||||||
//
|
///
|
||||||
// Note: if/when we upgrade this to CSS4, which supports combinators, we
|
/// Note: if/when we upgrade this to CSS4, which supports combinators, we
|
||||||
// need to think about how this should interact with visit_complex_selector,
|
/// need to think about how this should interact with
|
||||||
// and what the consumers of those APIs should do about the presence of
|
/// visit_complex_selector, and what the consumers of those APIs should do
|
||||||
// combinators in negation.
|
/// about the presence of combinators in negation.
|
||||||
Negation(Box<[Component<Impl>]>),
|
Negation(Box<[Component<Impl>]>),
|
||||||
FirstChild, LastChild, OnlyChild,
|
FirstChild, LastChild, OnlyChild,
|
||||||
Root,
|
Root,
|
||||||
|
@ -739,6 +756,13 @@ pub enum Component<Impl: SelectorImpl> {
|
||||||
LastOfType,
|
LastOfType,
|
||||||
OnlyOfType,
|
OnlyOfType,
|
||||||
NonTSPseudoClass(Impl::NonTSPseudoClass),
|
NonTSPseudoClass(Impl::NonTSPseudoClass),
|
||||||
|
/// The ::slotted() pseudo-element (which isn't actually a pseudo-element,
|
||||||
|
/// and probably should be a pseudo-class):
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-scoping/#slotted-pseudo
|
||||||
|
///
|
||||||
|
/// The selectors here are compound selectors, that is, no combinators.
|
||||||
|
Slotted(Box<[Selector<Impl>]>),
|
||||||
PseudoElement(Impl::PseudoElement),
|
PseudoElement(Impl::PseudoElement),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,13 +892,15 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
|
||||||
let mut perform_step_2 = true;
|
let mut perform_step_2 = true;
|
||||||
if first_non_namespace == compound.len() - 1 {
|
if first_non_namespace == compound.len() - 1 {
|
||||||
match (combinators.peek(), &compound[first_non_namespace]) {
|
match (combinators.peek(), &compound[first_non_namespace]) {
|
||||||
// We have to be careful here, because if there is a pseudo
|
// We have to be careful here, because if there is a
|
||||||
// element "combinator" there isn't really just the one
|
// pseudo element "combinator" there isn't really just
|
||||||
// simple selector. Technically this compound selector
|
// the one simple selector. Technically this compound
|
||||||
// contains the pseudo element selector as
|
// selector contains the pseudo element selector as well
|
||||||
// well--Combinator::PseudoElement doesn't exist in the
|
// -- Combinator::PseudoElement, just like
|
||||||
|
// Combinator::SlotAssignment, don't exist in the
|
||||||
// spec.
|
// spec.
|
||||||
(Some(&&Component::Combinator(Combinator::PseudoElement)), _) => (),
|
(Some(&&Component::Combinator(Combinator::PseudoElement)), _) |
|
||||||
|
(Some(&&Component::Combinator(Combinator::SlotAssignment)), _) => (),
|
||||||
(_, &Component::ExplicitUniversalType) => {
|
(_, &Component::ExplicitUniversalType) => {
|
||||||
// Iterate over everything so we serialize the namespace
|
// Iterate over everything so we serialize the namespace
|
||||||
// too.
|
// too.
|
||||||
|
@ -942,6 +968,7 @@ impl ToCss for Combinator {
|
||||||
Combinator::NextSibling => dest.write_str(" + "),
|
Combinator::NextSibling => dest.write_str(" + "),
|
||||||
Combinator::LaterSibling => dest.write_str(" ~ "),
|
Combinator::LaterSibling => dest.write_str(" ~ "),
|
||||||
Combinator::PseudoElement => Ok(()),
|
Combinator::PseudoElement => Ok(()),
|
||||||
|
Combinator::SlotAssignment => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -970,6 +997,16 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
Combinator(ref c) => {
|
Combinator(ref c) => {
|
||||||
c.to_css(dest)
|
c.to_css(dest)
|
||||||
}
|
}
|
||||||
|
Slotted(ref selectors) => {
|
||||||
|
dest.write_str("::slotted(")?;
|
||||||
|
let mut iter = selectors.iter();
|
||||||
|
iter.next().expect("At least one selector").to_css(dest)?;
|
||||||
|
for other in iter {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
other.to_css(dest)?;
|
||||||
|
}
|
||||||
|
dest.write_char(')')
|
||||||
|
}
|
||||||
PseudoElement(ref p) => {
|
PseudoElement(ref p) => {
|
||||||
p.to_css(dest)
|
p.to_css(dest)
|
||||||
}
|
}
|
||||||
|
@ -1120,10 +1157,14 @@ where
|
||||||
let mut builder = SelectorBuilder::default();
|
let mut builder = SelectorBuilder::default();
|
||||||
|
|
||||||
let mut has_pseudo_element;
|
let mut has_pseudo_element;
|
||||||
|
let mut slotted;
|
||||||
'outer_loop: loop {
|
'outer_loop: loop {
|
||||||
// Parse a sequence of simple selectors.
|
// Parse a sequence of simple selectors.
|
||||||
has_pseudo_element = match parse_compound_selector(parser, input, &mut builder)? {
|
match parse_compound_selector(parser, input, &mut builder)? {
|
||||||
Some(has_pseudo_element) => has_pseudo_element,
|
Some((has_pseudo, slot)) => {
|
||||||
|
has_pseudo_element = has_pseudo;
|
||||||
|
slotted = slot;
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(input.new_custom_error(if builder.has_combinators() {
|
return Err(input.new_custom_error(if builder.has_combinators() {
|
||||||
SelectorParseErrorKind::DanglingCombinator
|
SelectorParseErrorKind::DanglingCombinator
|
||||||
|
@ -1132,7 +1173,8 @@ where
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if has_pseudo_element {
|
|
||||||
|
if has_pseudo_element || slotted {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1263,6 +1305,7 @@ where
|
||||||
enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
||||||
SimpleSelector(Component<Impl>),
|
SimpleSelector(Component<Impl>),
|
||||||
PseudoElement(Impl::PseudoElement),
|
PseudoElement(Impl::PseudoElement),
|
||||||
|
SlottedPseudo(Box<[Selector<Impl>]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1575,7 +1618,8 @@ where
|
||||||
None => {
|
None => {
|
||||||
return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation));
|
return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation));
|
||||||
},
|
},
|
||||||
Some(SimpleSelectorParseResult::PseudoElement(_)) => {
|
Some(SimpleSelectorParseResult::PseudoElement(_)) |
|
||||||
|
Some(SimpleSelectorParseResult::SlottedPseudo(_)) => {
|
||||||
return Err(input.new_custom_error(SelectorParseErrorKind::NonSimpleSelectorInNegation));
|
return Err(input.new_custom_error(SelectorParseErrorKind::NonSimpleSelectorInNegation));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1592,12 +1636,13 @@ where
|
||||||
/// `Err(())` means invalid selector.
|
/// `Err(())` means invalid selector.
|
||||||
/// `Ok(None)` is an empty selector
|
/// `Ok(None)` is an empty selector
|
||||||
///
|
///
|
||||||
/// The boolean represent whether a pseudo-element has been parsed.
|
/// The booleans represent whether a pseudo-element has been parsed, and whether
|
||||||
|
/// ::slotted() has been parsed, respectively.
|
||||||
fn parse_compound_selector<'i, 't, P, Impl>(
|
fn parse_compound_selector<'i, 't, P, Impl>(
|
||||||
parser: &P,
|
parser: &P,
|
||||||
input: &mut CssParser<'i, 't>,
|
input: &mut CssParser<'i, 't>,
|
||||||
builder: &mut SelectorBuilder<Impl>,
|
builder: &mut SelectorBuilder<Impl>,
|
||||||
) -> Result<Option<bool>, ParseError<'i, P::Error>>
|
) -> Result<Option<(bool, bool)>, ParseError<'i, P::Error>>
|
||||||
where
|
where
|
||||||
P: Parser<'i, Impl=Impl>,
|
P: Parser<'i, Impl=Impl>,
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
|
@ -1605,6 +1650,7 @@ where
|
||||||
input.skip_whitespace();
|
input.skip_whitespace();
|
||||||
|
|
||||||
let mut empty = true;
|
let mut empty = true;
|
||||||
|
let mut slot = false;
|
||||||
if !parse_type_selector(parser, input, builder)? {
|
if !parse_type_selector(parser, input, builder)? {
|
||||||
if let Some(url) = parser.default_namespace() {
|
if let Some(url) = parser.default_namespace() {
|
||||||
// If there was no explicit type selector, but there is a
|
// If there was no explicit type selector, but there is a
|
||||||
|
@ -1618,13 +1664,18 @@ where
|
||||||
|
|
||||||
let mut pseudo = false;
|
let mut pseudo = false;
|
||||||
loop {
|
loop {
|
||||||
|
let parse_result =
|
||||||
match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? {
|
match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? {
|
||||||
None => break,
|
None => break,
|
||||||
Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
|
Some(result) => result,
|
||||||
|
};
|
||||||
|
|
||||||
|
match parse_result {
|
||||||
|
SimpleSelectorParseResult::SimpleSelector(s) => {
|
||||||
builder.push_simple_selector(s);
|
builder.push_simple_selector(s);
|
||||||
empty = false
|
empty = false
|
||||||
}
|
}
|
||||||
Some(SimpleSelectorParseResult::PseudoElement(p)) => {
|
SimpleSelectorParseResult::PseudoElement(p) => {
|
||||||
// Try to parse state to its right. There are only 3 allowable
|
// Try to parse state to its right. There are only 3 allowable
|
||||||
// state selectors that can go on pseudo-elements.
|
// state selectors that can go on pseudo-elements.
|
||||||
let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new();
|
let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new();
|
||||||
|
@ -1673,13 +1724,25 @@ where
|
||||||
empty = false;
|
empty = false;
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
SimpleSelectorParseResult::SlottedPseudo(selectors) => {
|
||||||
|
empty = false;
|
||||||
|
slot = true;
|
||||||
|
if !builder.is_empty() {
|
||||||
|
builder.push_combinator(Combinator::SlotAssignment);
|
||||||
|
}
|
||||||
|
builder.push_simple_selector(Component::Slotted(selectors));
|
||||||
|
// FIXME(emilio): ::slotted() should support ::before and
|
||||||
|
// ::after after it, so we shouldn't break, but we shouldn't
|
||||||
|
// push more type selectors either.
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if empty {
|
if empty {
|
||||||
// An empty selector is invalid.
|
// An empty selector is invalid.
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(pseudo))
|
Ok(Some((pseudo, slot)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1790,14 +1853,33 @@ where
|
||||||
let is_pseudo_element = !is_single_colon ||
|
let is_pseudo_element = !is_single_colon ||
|
||||||
P::pseudo_element_allows_single_colon(&name);
|
P::pseudo_element_allows_single_colon(&name);
|
||||||
if is_pseudo_element {
|
if is_pseudo_element {
|
||||||
let pseudo_element = if is_functional {
|
let parse_result = if is_functional {
|
||||||
|
if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
|
||||||
|
SimpleSelectorParseResult::SlottedPseudo(
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
P::parse_functional_pseudo_element(parser, name, input)
|
parse_compound_selector_list(
|
||||||
|
parser,
|
||||||
|
input,
|
||||||
|
)
|
||||||
})?
|
})?
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
|
SimpleSelectorParseResult::PseudoElement(
|
||||||
|
input.parse_nested_block(|input| {
|
||||||
|
P::parse_functional_pseudo_element(
|
||||||
|
parser,
|
||||||
|
name,
|
||||||
|
input,
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SimpleSelectorParseResult::PseudoElement(
|
||||||
P::parse_pseudo_element(parser, location, name)?
|
P::parse_pseudo_element(parser, location, name)?
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
|
Ok(Some(parse_result))
|
||||||
} else {
|
} else {
|
||||||
let pseudo_class = if is_functional {
|
let pseudo_class = if is_functional {
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
|
@ -1979,6 +2061,10 @@ pub mod tests {
|
||||||
type Impl = DummySelectorImpl;
|
type Impl = DummySelectorImpl;
|
||||||
type Error = SelectorParseErrorKind<'i>;
|
type Error = SelectorParseErrorKind<'i>;
|
||||||
|
|
||||||
|
fn parse_slotted(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_non_ts_pseudo_class(
|
fn parse_non_ts_pseudo_class(
|
||||||
&self,
|
&self,
|
||||||
location: SourceLocation,
|
location: SourceLocation,
|
||||||
|
@ -2394,6 +2480,16 @@ pub mod tests {
|
||||||
].into_boxed_slice()
|
].into_boxed_slice()
|
||||||
)), specificity(0, 0, 0))
|
)), specificity(0, 0, 0))
|
||||||
))));
|
))));
|
||||||
|
|
||||||
|
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());
|
||||||
|
assert!(parse("::slotted(div) + foo").is_err());
|
||||||
|
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("div + slot::slotted(.foo, bar, .baz)").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -84,6 +84,13 @@ pub trait Element: Sized + Clone + Debug {
|
||||||
/// Whether this element is a `link`.
|
/// Whether this element is a `link`.
|
||||||
fn is_link(&self) -> bool;
|
fn is_link(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns the assigned <slot> element this element is assigned to.
|
||||||
|
///
|
||||||
|
/// Necessary for the `::slotted` pseudo-class.
|
||||||
|
fn assigned_slot(&self) -> Option<Self> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn has_id(&self,
|
fn has_id(&self,
|
||||||
id: &<Self::Impl as SelectorImpl>::Identifier,
|
id: &<Self::Impl as SelectorImpl>::Identifier,
|
||||||
case_sensitivity: CaseSensitivity)
|
case_sensitivity: CaseSensitivity)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue