aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout_thread/dom_wrapper.rs16
-rw-r--r--components/script/dom/element.rs9
-rw-r--r--components/selectors/matching.rs50
-rw-r--r--components/selectors/parser.rs43
-rw-r--r--components/selectors/tree.rs6
-rw-r--r--components/style/gecko/selector_parser.rs20
-rw-r--r--components/style/gecko/wrapper.rs13
-rw-r--r--components/style/invalidation/element/element_wrapper.rs29
-rw-r--r--components/style/servo/selector_parser.rs43
9 files changed, 178 insertions, 51 deletions
diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs
index 5c5fc4be052..55bacd037cb 100644
--- a/components/layout_thread/dom_wrapper.rs
+++ b/components/layout_thread/dom_wrapper.rs
@@ -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>> {
self.as_node().dom_children().filter_map(|n| n.as_element()).next()
}
@@ -1199,6 +1207,14 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
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> {
warn!("ServoThreadSafeLayoutElement::first_child_element called");
None
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 5060492160b..a53446fd4a3 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -2570,6 +2570,14 @@ impl<'a> SelectorsElement for DomRoot<Element> {
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(
&self,
_pseudo: &PseudoElement,
@@ -2578,7 +2586,6 @@ impl<'a> SelectorsElement for DomRoot<Element> {
false
}
-
fn first_child_element(&self) -> Option<DomRoot<Element>> {
self.node.child_elements().next()
}
diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs
index 6cb9acf59fe..7244868285b 100644
--- a/components/selectors/matching.rs
+++ b/components/selectors/matching.rs
@@ -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;
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs
index 91981562366..5fd73ce8d73 100644
--- a/components/selectors/parser.rs
+++ b/components/selectors/parser.rs
@@ -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;
}
}
}
@@ -356,7 +364,10 @@ impl<Impl: SelectorImpl> Visit for Selector<Impl> where Impl::NonTSPseudoClass:
}
}
-impl<Impl: SelectorImpl> Visit for Component<Impl> where Impl::NonTSPseudoClass: Visit<Impl=Impl> {
+impl<Impl: SelectorImpl> Visit for Component<Impl>
+where
+ Impl::NonTSPseudoClass: Visit<Impl=Impl>
+{
type Impl = Impl;
fn visit<V>(&self, visitor: &mut V) -> bool
@@ -1981,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 {
@@ -2045,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)]
diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs
index c9baeb1316d..00799f92edd 100644
--- a/components/selectors/tree.rs
+++ b/components/selectors/tree.rs
@@ -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.
///
diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs
index 434d2fef414..b6e99b2083f 100644
--- a/components/style/gecko/selector_parser.rs
+++ b/components/style/gecko/selector_parser.rs
@@ -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.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SelectorImpl;
@@ -291,12 +305,6 @@ impl ::selectors::SelectorImpl for SelectorImpl {
type PseudoElement = PseudoElement;
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> {
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index e774b1cfb33..db3188bd0bf 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -1822,13 +1822,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
#[inline]
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();
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]
fn pseudo_element_originating_element(&self) -> Option<Self> {
debug_assert!(self.implemented_pseudo_element().is_some());
self.closest_non_native_anonymous_ancestor()
diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs
index 5483c275697..29da4d7cb2f 100644
--- a/components/style/invalidation/element/element_wrapper.rs
+++ b/components/style/invalidation/element/element_wrapper.rs
@@ -271,28 +271,37 @@ where
}
fn parent_element(&self) -> Option<Self> {
- self.element.parent_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ let parent = self.element.parent_element()?;
+ 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> {
- self.element.first_child_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ let child = self.element.first_child_element()?;
+ Some(Self::new(child, self.snapshot_map))
}
fn last_child_element(&self) -> Option<Self> {
- self.element.last_child_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ let child = self.element.last_child_element()?;
+ Some(Self::new(child, self.snapshot_map))
}
fn prev_sibling_element(&self) -> Option<Self> {
- self.element.prev_sibling_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ let sibling = self.element.prev_sibling_element()?;
+ Some(Self::new(sibling, self.snapshot_map))
}
fn next_sibling_element(&self) -> Option<Self> {
- self.element.next_sibling_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ let sibling = self.element.next_sibling_element()?;
+ Some(Self::new(sibling, self.snapshot_map))
}
#[inline]
diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs
index 08d3401cebd..dd2e4f20c9f 100644
--- a/components/style/servo/selector_parser.rs
+++ b/components/style/servo/selector_parser.rs
@@ -308,6 +308,20 @@ pub enum NonTSPseudoClass {
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 {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use self::NonTSPseudoClass::*;
@@ -423,20 +437,17 @@ impl ::selectors::SelectorImpl for SelectorImpl {
type NamespaceUrl = Namespace;
type BorrowedLocalName = LocalName;
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> {
type Impl = SelectorImpl;
type Error = StyleParseErrorKind<'i>;
- fn parse_non_ts_pseudo_class(&self, location: SourceLocation, name: CowRcStr<'i>)
- -> Result<NonTSPseudoClass, ParseError<'i>> {
+ fn parse_non_ts_pseudo_class(
+ &self,
+ location: SourceLocation,
+ name: CowRcStr<'i>,
+ ) -> Result<NonTSPseudoClass, ParseError<'i>> {
use self::NonTSPseudoClass::*;
let pseudo_class = match_ignore_ascii_case! { &name,
"active" => Active,
@@ -468,10 +479,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
Ok(pseudo_class)
}
- fn parse_non_ts_functional_pseudo_class<'t>(&self,
- name: CowRcStr<'i>,
- parser: &mut CssParser<'i, 't>)
- -> Result<NonTSPseudoClass, ParseError<'i>> {
+ fn parse_non_ts_functional_pseudo_class<'t>(
+ &self,
+ name: CowRcStr<'i>,
+ parser: &mut CssParser<'i, 't>,
+ ) -> Result<NonTSPseudoClass, ParseError<'i>> {
use self::NonTSPseudoClass::*;
let pseudo_class = match_ignore_ascii_case!{ &name,
"lang" => {
@@ -489,8 +501,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
Ok(pseudo_class)
}
- fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
- -> Result<PseudoElement, ParseError<'i>> {
+ fn parse_pseudo_element(
+ &self,
+ location: SourceLocation,
+ name: CowRcStr<'i>,
+ ) -> Result<PseudoElement, ParseError<'i>> {
use self::PseudoElement::*;
let pseudo_element = match_ignore_ascii_case! { &name,
"before" => Before,