style: Add infrastructure to match :host.

This commit is contained in:
Emilio Cobos Álvarez 2018-03-08 22:03:42 +01:00
parent 1654f297ca
commit 9fa2618197
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
9 changed files with 159 additions and 41 deletions

View file

@ -6,7 +6,7 @@ use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstrai
use bloom::{BLOOM_HASH_MASK, BloomFilter};
use nth_index_cache::NthIndexCacheInner;
use parser::{AncestorHashes, Combinator, Component, LocalName};
use parser::{Selector, SelectorImpl, SelectorIter, SelectorList};
use parser::{Selector, SelectorImpl, SelectorIter, SelectorList, NonTSPseudoClass};
use std::borrow::Borrow;
use std::iter;
use tree::Element;
@ -404,7 +404,7 @@ fn matches_hover_and_active_quirk<Impl: SelectorImpl>(
Component::LastOfType |
Component::OnlyOfType => false,
Component::NonTSPseudoClass(ref pseudo_class) => {
Impl::is_active_or_hover(pseudo_class)
pseudo_class.is_active_or_hover()
},
_ => true,
}
@ -427,6 +427,7 @@ enum Rightmost {
fn next_element_for_combinator<E>(
element: &E,
combinator: Combinator,
selector: &SelectorIter<E::Impl>,
) -> Option<E>
where
E: Element,
@ -442,7 +443,42 @@ where
return None;
}
element.parent_element()
match element.parent_element() {
Some(e) => return Some(e),
None => {}
}
if !element.parent_node_is_shadow_root() {
return None;
}
// https://drafts.csswg.org/css-scoping/#host-element-in-tree:
//
// For the purpose of Selectors, a shadow host also appears in
// its shadow tree, with the contents of the shadow tree treated
// as its children. (In other words, the shadow host is treated as
// replacing the shadow root node.)
//
// and also:
//
// 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.
//
// Since we know that the parent is a shadow root, we necessarily
// are in a shadow tree of the host.
let all_selectors_could_match = selector.clone().all(|component| {
match *component {
Component::NonTSPseudoClass(ref pc) => pc.is_host(),
_ => false,
}
});
if !all_selectors_could_match {
return None;
}
element.containing_shadow_host()
}
Combinator::SlotAssignment => {
debug_assert!(element.assigned_slot().map_or(true, |s| s.is_html_slot_element()));
@ -502,7 +538,8 @@ where
}
};
let mut next_element = next_element_for_combinator(element, combinator);
let mut next_element =
next_element_for_combinator(element, combinator, &selector_iter);
// Stop matching :visited as soon as we find a link, or a combinator for
// something that isn't an ancestor.
@ -565,7 +602,8 @@ where
visited_handling = VisitedHandlingMode::AllLinksUnvisited;
}
next_element = next_element_for_combinator(&element, combinator);
next_element =
next_element_for_combinator(&element, combinator, &selector_iter);
}
}
@ -753,7 +791,7 @@ where
Component::NonTSPseudoClass(ref pc) => {
if context.matches_hover_and_active_quirk == MatchesHoverAndActiveQuirk::Yes &&
!context.shared.is_nested() &&
E::Impl::is_active_or_hover(pc) &&
pc.is_active_or_hover() &&
!element.is_link()
{
return false;

View file

@ -36,6 +36,18 @@ pub trait PseudoElement : Sized + ToCss {
}
}
/// A trait that represents a pseudo-class.
pub trait NonTSPseudoClass : Sized + ToCss {
/// The `SelectorImpl` this pseudo-element is used for.
type Impl: SelectorImpl;
/// Whether this pseudo-class is :active or :hover.
fn is_active_or_hover(&self) -> bool;
/// Whether this pseudo-class is :host.
fn is_host(&self) -> bool;
}
fn to_ascii_lowercase(s: &str) -> Cow<str> {
if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
let mut string = s.to_owned();
@ -96,14 +108,10 @@ macro_rules! with_all_bounds {
/// non tree-structural pseudo-classes
/// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss;
type NonTSPseudoClass: $($CommonBounds)* + NonTSPseudoClass<Impl = Self>;
/// pseudo-elements
type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>;
/// Returns whether the given pseudo class is :active or :hover.
#[inline]
fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool;
}
}
}
@ -1984,6 +1992,20 @@ pub mod tests {
}
}
impl parser::NonTSPseudoClass for PseudoClass {
type Impl = DummySelectorImpl;
#[inline]
fn is_active_or_hover(&self) -> bool {
matches!(*self, PseudoClass::Active | PseudoClass::Hover)
}
#[inline]
fn is_host(&self) -> bool {
false
}
}
impl ToCss for PseudoClass {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
@ -2048,12 +2070,6 @@ pub mod tests {
type BorrowedNamespaceUrl = DummyAtom;
type NonTSPseudoClass = PseudoClass;
type PseudoElement = PseudoElement;
#[inline]
fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool {
matches!(*pseudo_class, PseudoClass::Active |
PseudoClass::Hover)
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]

View file

@ -31,6 +31,12 @@ pub trait Element: Sized + Clone + Debug {
fn parent_element(&self) -> Option<Self>;
/// Whether the parent node of this element is a shadow root.
fn parent_node_is_shadow_root(&self) -> bool;
/// The host of the containing shadow root, if any.
fn containing_shadow_host(&self) -> Option<Self>;
/// The parent of a given pseudo-element, after matching a pseudo-element
/// selector.
///