diff options
-rw-r--r-- | components/selectors/builder.rs | 2 | ||||
-rw-r--r-- | components/selectors/matching.rs | 20 | ||||
-rw-r--r-- | components/selectors/parser.rs | 44 | ||||
-rw-r--r-- | components/style/dom.rs | 8 | ||||
-rw-r--r-- | components/style/invalidation/element/invalidation_map.rs | 19 | ||||
-rw-r--r-- | components/style/invalidation/element/invalidator.rs | 30 | ||||
-rw-r--r-- | components/style/selector_map.rs | 1 | ||||
-rw-r--r-- | components/style/stylist.rs | 5 |
8 files changed, 82 insertions, 47 deletions
diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index b81c729eea5..d4ed2d309b6 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -314,7 +314,7 @@ fn complex_selector_specificity<Impl>(mut iter: slice::Iter<Component<Impl>>) Component::FirstChild | Component::LastChild | Component::OnlyChild | Component::Root | Component::Empty | Component::Scope | - Component::Host | + Component::Host(..) | Component::NthChild(..) | Component::NthLastChild(..) | Component::NthOfType(..) | diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 527d1224e6b..5e78e50d544 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -692,10 +692,10 @@ where match *selector { Component::Combinator(_) => unreachable!(), Component::Slotted(ref selector) => { + // <slots> are never flattened tree slottables. + !element.is_html_slot_element() && + element.assigned_slot().is_some() && context.shared.nest(|context| { - // <slots> are never flattened tree slottables. - !element.is_html_slot_element() && - element.assigned_slot().is_some() && matches_complex_selector( selector.iter(), element, @@ -814,8 +814,18 @@ where flags_setter(element, ElementSelectorFlags::HAS_EMPTY_SELECTOR); element.is_empty() } - Component::Host => { - context.shared.shadow_host().map_or(false, |host| host == element.opaque()) + Component::Host(ref selector) => { + context.shared.shadow_host().map_or(false, |host| host == element.opaque()) && + selector.as_ref().map_or(true, |selector| { + context.shared.nest(|context| { + matches_complex_selector( + selector.iter(), + element, + context, + flags_setter, + ) + }) + }) } Component::Scope => { match context.shared.scope_element { diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index dfcae65a9cc..9fbd711ab78 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -381,11 +381,14 @@ where } match *self { - Slotted(ref selectors) => { - for selector in selectors.iter() { - if !selector.visit(visitor) { - return false; - } + Slotted(ref selector) => { + if !selector.visit(visitor) { + return false; + } + } + Host(Some(ref selector)) => { + if !selector.visit(visitor) { + return false; } } Negation(ref negated) => { @@ -618,7 +621,7 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { /// combinators to the left. #[inline] pub(crate) fn is_featureless_host_selector(&mut self) -> bool { - self.all(|component| matches!(*component, Component::Host)) && + self.all(|component| matches!(*component, Component::Host(..))) && self.next_sequence().is_none() } @@ -793,10 +796,6 @@ pub enum Component<Impl: SelectorImpl> { Root, Empty, Scope, - /// The `:host` pseudo-class: - /// - /// https://drafts.csswg.org/css-scoping/#host-selector - Host, NthChild(i32, i32), NthLastChild(i32, i32), NthOfType(i32, i32), @@ -815,7 +814,19 @@ pub enum Component<Impl: SelectorImpl> { /// 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 `: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>>), PseudoElement(Impl::PseudoElement), } @@ -1119,7 +1130,15 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> { Root => dest.write_str(":root"), Empty => dest.write_str(":empty"), Scope => dest.write_str(":scope"), - Host => dest.write_str(":host"), + Host(ref selector) => { + dest.write_str(":host")?; + if let Some(ref selector) = *selector { + dest.write_char('(')?; + selector.to_css(dest)?; + dest.write_char(')')?; + } + Ok(()) + }, FirstOfType => dest.write_str(":first-of-type"), LastOfType => dest.write_str(":last-of-type"), OnlyOfType => dest.write_str(":only-of-type"), @@ -1816,6 +1835,7 @@ where "nth-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthOfType)?), "nth-last-child" => return Ok(parse_nth_pseudo_class(input, Component::NthLastChild)?), "nth-last-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthLastOfType)?), + "host" => return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input)?))), "not" => { if inside_negation { return Err(input.new_custom_error( @@ -1969,7 +1989,7 @@ where "root" => Ok(Component::Root), "empty" => Ok(Component::Empty), "scope" => Ok(Component::Scope), - "host" if P::parse_host(parser) => Ok(Component::Host), + "host" if P::parse_host(parser) => Ok(Component::Host(None)), "first-of-type" => Ok(Component::FirstOfType), "last-of-type" => Ok(Component::LastOfType), "only-of-type" => Ok(Component::OnlyOfType), diff --git a/components/style/dom.rs b/components/style/dom.rs index 7e63fd8ba50..0c9bcbe3c4d 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -794,6 +794,14 @@ pub trait TElement ); } + if let Some(shadow) = self.shadow_root() { + f( + shadow.style_data(), + self.as_node().owner_doc().quirks_mode(), + Some(shadow.host()), + ); + } + let mut current = self.assigned_slot(); while let Some(slot) = current { // Slots can only have assigned nodes when in a shadow tree. diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs index 05c67bcc383..0a2edd4aec9 100644 --- a/components/style/invalidation/element/invalidation_map.rs +++ b/components/style/invalidation/element/invalidation_map.rs @@ -207,16 +207,6 @@ impl InvalidationMap { }) } - /// Adds a selector to this `InvalidationMap`. Returns Err(..) to - /// signify OOM. - pub fn note_selector( - &mut self, - selector: &Selector<SelectorImpl>, - quirks_mode: QuirksMode, - ) -> Result<(), FailedAllocationError> { - self.collect_invalidations_for(selector, quirks_mode) - } - /// Clears this map, leaving it empty. pub fn clear(&mut self) { self.class_to_selector.clear(); @@ -228,13 +218,14 @@ impl InvalidationMap { self.has_class_attribute_selectors = false; } - // Returns Err(..) to signify OOM. - fn collect_invalidations_for( + /// Adds a selector to this `InvalidationMap`. Returns Err(..) to + /// signify OOM. + pub fn note_selector( &mut self, selector: &Selector<SelectorImpl>, - quirks_mode: QuirksMode + quirks_mode: QuirksMode, ) -> Result<(), FailedAllocationError> { - debug!("InvalidationMap::collect_invalidations_for({:?})", selector); + debug!("InvalidationMap::note_selector({:?})", selector); let mut iter = selector.iter(); let mut combinator; diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index 77e9f875bdd..3392c58e356 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -6,7 +6,7 @@ //! element styles need to be invalidated. use context::StackLimitChecker; -use dom::{TElement, TNode}; +use dom::{TElement, TNode, TShadowRoot}; use selector_parser::SelectorImpl; use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext}; use selectors::matching::matches_compound_selector_from; @@ -534,20 +534,22 @@ where let mut any_descendant = false; - // NOTE(emilio): This should not be needed for Shadow DOM for normal - // element state / attribute invalidations (it's needed for XBL though, - // due to the weird way the anon content there works (it doesn't block - // combinators)). + // NOTE(emilio): This is only needed for Shadow DOM to invalidate + // correctly on :host(..) changes. Instead of doing this, we could add + // a third kind of invalidation list that walks shadow root children, + // but it's not clear it's worth it. // - // However, it's needed as of right now for document state invalidation, - // were we rely on iterating every element that ends up in the composed - // doc. - // - // Also, we could avoid having that special-case for document state - // invalidations if we invalidate for document state changes per - // subtree, though that's kind of annoying because we need to invalidate - // the shadow host subtree (to handle :host and ::slotted), and the - // actual shadow tree (to handle all other rules in the ShadowRoot). + // Also, it's needed as of right now for document state invalidation, + // where we rely on iterating every element that ends up in the composed + // doc, but we could fix that invalidating per subtree. + if let Some(root) = self.element.shadow_root() { + any_descendant |= + self.invalidate_dom_descendants_of(root.as_node(), invalidations); + } + + // This is needed for XBL (technically) unconditionally, because XBL + // bindings do not block combinators in any way. However this is kinda + // broken anyway, since we should be looking at XBL rules too. if let Some(anon_content) = self.element.xbl_binding_anonymous_content() { any_descendant |= self.invalidate_dom_descendants_of(anon_content, invalidations); diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index c1f02b5797f..03979549709 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -457,6 +457,7 @@ fn specific_bucket_for<'a>( // // Meanwhile taking the code path below is slower, but still correct. // Component::Slotted(ref selector) => find_bucket(selector.iter()), + Component::Host(Some(ref selector)) => find_bucket(selector.iter()), _ => Bucket::Universal } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 686ce6a6b6e..6a8ea544c39 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -2238,7 +2238,10 @@ impl CascadeData { ); if rebuild_kind.should_rebuild_invalidation() { - self.invalidation_map.note_selector(&rule.selector, quirks_mode)?; + self.invalidation_map.note_selector( + selector, + quirks_mode, + )?; let mut visitor = StylistSelectorVisitor { needs_revalidation: false, passed_rightmost_selector: false, |