mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
style: Add infrastructure to match :host.
This commit is contained in:
parent
1654f297ca
commit
9fa2618197
9 changed files with 159 additions and 41 deletions
|
@ -659,6 +659,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parent_node_is_shadow_root(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn containing_shadow_host(&self) -> Option<Self> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn first_child_element(&self) -> Option<ServoLayoutElement<'le>> {
|
fn first_child_element(&self) -> Option<ServoLayoutElement<'le>> {
|
||||||
self.as_node().dom_children().filter_map(|n| n.as_element()).next()
|
self.as_node().dom_children().filter_map(|n| n.as_element()).next()
|
||||||
}
|
}
|
||||||
|
@ -1199,6 +1207,14 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parent_node_is_shadow_root(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn containing_shadow_host(&self) -> Option<Self> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn first_child_element(&self) -> Option<Self> {
|
fn first_child_element(&self) -> Option<Self> {
|
||||||
warn!("ServoThreadSafeLayoutElement::first_child_element called");
|
warn!("ServoThreadSafeLayoutElement::first_child_element called");
|
||||||
None
|
None
|
||||||
|
|
|
@ -2570,6 +2570,14 @@ impl<'a> SelectorsElement for DomRoot<Element> {
|
||||||
self.upcast::<Node>().GetParentElement()
|
self.upcast::<Node>().GetParentElement()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parent_node_is_shadow_root(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn containing_shadow_host(&self) -> Option<Self> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn match_pseudo_element(
|
fn match_pseudo_element(
|
||||||
&self,
|
&self,
|
||||||
_pseudo: &PseudoElement,
|
_pseudo: &PseudoElement,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstrai
|
||||||
use bloom::{BLOOM_HASH_MASK, BloomFilter};
|
use bloom::{BLOOM_HASH_MASK, BloomFilter};
|
||||||
use nth_index_cache::NthIndexCacheInner;
|
use nth_index_cache::NthIndexCacheInner;
|
||||||
use parser::{AncestorHashes, Combinator, Component, LocalName};
|
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::borrow::Borrow;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use tree::Element;
|
use tree::Element;
|
||||||
|
@ -404,7 +404,7 @@ fn matches_hover_and_active_quirk<Impl: SelectorImpl>(
|
||||||
Component::LastOfType |
|
Component::LastOfType |
|
||||||
Component::OnlyOfType => false,
|
Component::OnlyOfType => false,
|
||||||
Component::NonTSPseudoClass(ref pseudo_class) => {
|
Component::NonTSPseudoClass(ref pseudo_class) => {
|
||||||
Impl::is_active_or_hover(pseudo_class)
|
pseudo_class.is_active_or_hover()
|
||||||
},
|
},
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
|
@ -427,6 +427,7 @@ enum Rightmost {
|
||||||
fn next_element_for_combinator<E>(
|
fn next_element_for_combinator<E>(
|
||||||
element: &E,
|
element: &E,
|
||||||
combinator: Combinator,
|
combinator: Combinator,
|
||||||
|
selector: &SelectorIter<E::Impl>,
|
||||||
) -> Option<E>
|
) -> Option<E>
|
||||||
where
|
where
|
||||||
E: Element,
|
E: Element,
|
||||||
|
@ -442,7 +443,42 @@ where
|
||||||
return None;
|
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 => {
|
Combinator::SlotAssignment => {
|
||||||
debug_assert!(element.assigned_slot().map_or(true, |s| s.is_html_slot_element()));
|
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
|
// Stop matching :visited as soon as we find a link, or a combinator for
|
||||||
// something that isn't an ancestor.
|
// something that isn't an ancestor.
|
||||||
|
@ -565,7 +602,8 @@ where
|
||||||
visited_handling = VisitedHandlingMode::AllLinksUnvisited;
|
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) => {
|
Component::NonTSPseudoClass(ref pc) => {
|
||||||
if context.matches_hover_and_active_quirk == MatchesHoverAndActiveQuirk::Yes &&
|
if context.matches_hover_and_active_quirk == MatchesHoverAndActiveQuirk::Yes &&
|
||||||
!context.shared.is_nested() &&
|
!context.shared.is_nested() &&
|
||||||
E::Impl::is_active_or_hover(pc) &&
|
pc.is_active_or_hover() &&
|
||||||
!element.is_link()
|
!element.is_link()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -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> {
|
fn to_ascii_lowercase(s: &str) -> Cow<str> {
|
||||||
if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
|
if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
|
||||||
let mut string = s.to_owned();
|
let mut string = s.to_owned();
|
||||||
|
@ -96,14 +108,10 @@ macro_rules! with_all_bounds {
|
||||||
|
|
||||||
/// non tree-structural pseudo-classes
|
/// non tree-structural pseudo-classes
|
||||||
/// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
|
/// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
|
||||||
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss;
|
type NonTSPseudoClass: $($CommonBounds)* + NonTSPseudoClass<Impl = Self>;
|
||||||
|
|
||||||
/// pseudo-elements
|
/// pseudo-elements
|
||||||
type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>;
|
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 {
|
impl ToCss for PseudoClass {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -2048,12 +2070,6 @@ pub mod tests {
|
||||||
type BorrowedNamespaceUrl = DummyAtom;
|
type BorrowedNamespaceUrl = DummyAtom;
|
||||||
type NonTSPseudoClass = PseudoClass;
|
type NonTSPseudoClass = PseudoClass;
|
||||||
type PseudoElement = PseudoElement;
|
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)]
|
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||||
|
|
|
@ -31,6 +31,12 @@ pub trait Element: Sized + Clone + Debug {
|
||||||
|
|
||||||
fn parent_element(&self) -> Option<Self>;
|
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
|
/// The parent of a given pseudo-element, after matching a pseudo-element
|
||||||
/// selector.
|
/// selector.
|
||||||
///
|
///
|
||||||
|
|
|
@ -274,6 +274,20 @@ impl NonTSPseudoClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
|
||||||
|
type Impl = SelectorImpl;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_active_or_hover(&self) -> bool {
|
||||||
|
matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_host(&self) -> bool {
|
||||||
|
false // TODO(emilio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The dummy struct we use to implement our selector parsing.
|
/// The dummy struct we use to implement our selector parsing.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct SelectorImpl;
|
pub struct SelectorImpl;
|
||||||
|
@ -291,12 +305,6 @@ impl ::selectors::SelectorImpl for SelectorImpl {
|
||||||
|
|
||||||
type PseudoElement = PseudoElement;
|
type PseudoElement = PseudoElement;
|
||||||
type NonTSPseudoClass = NonTSPseudoClass;
|
type NonTSPseudoClass = NonTSPseudoClass;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool {
|
|
||||||
matches!(*pseudo_class, NonTSPseudoClass::Active |
|
|
||||||
NonTSPseudoClass::Hover)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SelectorParser<'a> {
|
impl<'a> SelectorParser<'a> {
|
||||||
|
|
|
@ -1822,12 +1822,21 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parent_element(&self) -> Option<Self> {
|
fn parent_element(&self) -> Option<Self> {
|
||||||
// FIXME(emilio): This will need to jump across if the parent node is a
|
|
||||||
// shadow root to get the shadow host.
|
|
||||||
let parent_node = self.as_node().parent_node();
|
let parent_node = self.as_node().parent_node();
|
||||||
parent_node.and_then(|n| n.as_element())
|
parent_node.and_then(|n| n.as_element())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn parent_node_is_shadow_root(&self) -> bool {
|
||||||
|
self.as_node().parent_node().map_or(false, |p| p.is_shadow_root())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn containing_shadow_host(&self) -> Option<Self> {
|
||||||
|
let shadow = self.containing_shadow()?;
|
||||||
|
Some(shadow.host())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||||
debug_assert!(self.implemented_pseudo_element().is_some());
|
debug_assert!(self.implemented_pseudo_element().is_some());
|
||||||
|
|
|
@ -271,28 +271,37 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent_element(&self) -> Option<Self> {
|
fn parent_element(&self) -> Option<Self> {
|
||||||
self.element.parent_element()
|
let parent = self.element.parent_element()?;
|
||||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
Some(Self::new(parent, self.snapshot_map))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_node_is_shadow_root(&self) -> bool {
|
||||||
|
self.element.parent_node_is_shadow_root()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn containing_shadow_host(&self) -> Option<Self> {
|
||||||
|
let host = self.element.containing_shadow_host()?;
|
||||||
|
Some(Self::new(host, self.snapshot_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_child_element(&self) -> Option<Self> {
|
fn first_child_element(&self) -> Option<Self> {
|
||||||
self.element.first_child_element()
|
let child = self.element.first_child_element()?;
|
||||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
Some(Self::new(child, self.snapshot_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_child_element(&self) -> Option<Self> {
|
fn last_child_element(&self) -> Option<Self> {
|
||||||
self.element.last_child_element()
|
let child = self.element.last_child_element()?;
|
||||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
Some(Self::new(child, self.snapshot_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev_sibling_element(&self) -> Option<Self> {
|
fn prev_sibling_element(&self) -> Option<Self> {
|
||||||
self.element.prev_sibling_element()
|
let sibling = self.element.prev_sibling_element()?;
|
||||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
Some(Self::new(sibling, self.snapshot_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_sibling_element(&self) -> Option<Self> {
|
fn next_sibling_element(&self) -> Option<Self> {
|
||||||
self.element.next_sibling_element()
|
let sibling = self.element.next_sibling_element()?;
|
||||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
Some(Self::new(sibling, self.snapshot_map))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -308,6 +308,20 @@ pub enum NonTSPseudoClass {
|
||||||
Visited,
|
Visited,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
|
||||||
|
type Impl = SelectorImpl;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_host(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_active_or_hover(&self) -> bool {
|
||||||
|
matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCss for NonTSPseudoClass {
|
impl ToCss for NonTSPseudoClass {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
use self::NonTSPseudoClass::*;
|
use self::NonTSPseudoClass::*;
|
||||||
|
@ -423,12 +437,6 @@ impl ::selectors::SelectorImpl for SelectorImpl {
|
||||||
type NamespaceUrl = Namespace;
|
type NamespaceUrl = Namespace;
|
||||||
type BorrowedLocalName = LocalName;
|
type BorrowedLocalName = LocalName;
|
||||||
type BorrowedNamespaceUrl = Namespace;
|
type BorrowedNamespaceUrl = Namespace;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool {
|
|
||||||
matches!(*pseudo_class, NonTSPseudoClass::Active |
|
|
||||||
NonTSPseudoClass::Hover)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue