diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index 2220477b553..b81c729eea5 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -314,6 +314,7 @@ fn complex_selector_specificity(mut iter: slice::Iter>) Component::FirstChild | Component::LastChild | Component::OnlyChild | Component::Root | Component::Empty | Component::Scope | + Component::Host | Component::NthChild(..) | Component::NthLastChild(..) | Component::NthOfType(..) | diff --git a/components/selectors/context.rs b/components/selectors/context.rs index 257a29afc44..b3b147f6e0e 100644 --- a/components/selectors/context.rs +++ b/components/selectors/context.rs @@ -6,7 +6,7 @@ use attr::CaseSensitivity; use bloom::BloomFilter; use nth_index_cache::NthIndexCache; use parser::SelectorImpl; -use tree::OpaqueElement; +use tree::{Element, OpaqueElement}; /// What kind of selector matching mode we should use. /// @@ -118,6 +118,9 @@ where /// See https://drafts.csswg.org/selectors-4/#scope-pseudo pub scope_element: Option, + /// The current shadow host we're collecting :host rules for. + pub current_host: Option, + /// Controls how matching for links is handled. visited_handling: VisitedHandlingMode, @@ -178,6 +181,7 @@ where quirks_mode, classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(), scope_element: None, + current_host: None, nesting_level: 0, in_negation: false, pseudo_element_matching_fn: None, @@ -186,6 +190,16 @@ where } } + /// Override the quirks mode we're matching against. + /// + /// FIXME(emilio): This is a hack for XBL quirks-mode mismatches. + #[inline] + pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) { + self.quirks_mode = quirks_mode; + self.classes_and_ids_case_sensitivity = + quirks_mode.classes_and_ids_case_sensitivity(); + } + /// Whether we're matching a nested selector. #[inline] pub fn is_nested(&self) -> bool { @@ -266,4 +280,30 @@ where self.visited_handling = original_handling_mode; result } + + /// Runs F with a given shadow host which is the root of the tree whose + /// rules we're matching. + #[inline] + pub fn with_shadow_host( + &mut self, + host: Option, + f: F, + ) -> R + where + E: Element, + F: FnOnce(&mut Self) -> R, + { + let original_host = self.current_host.take(); + self.current_host = host.map(|h| h.opaque()); + let result = f(self); + self.current_host = original_host; + result + } + + /// Returns the current shadow host whose shadow root we're matching rules + /// against. + #[inline] + pub fn shadow_host(&self) -> Option { + self.current_host.clone() + } } diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 7244868285b..527d1224e6b 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -466,15 +466,9 @@ where // 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 { + // are in a shadow tree of the host, and the next selector will only + // match if the selector is a featureless :host selector. + if !selector.clone().is_featureless_host_selector() { return None; } @@ -820,6 +814,9 @@ 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::Scope => { match context.shared.scope_element { Some(ref scope_element) => element.opaque() == *scope_element, diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 9f33683b950..dfcae65a9cc 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -42,9 +42,6 @@ pub trait NonTSPseudoClass : Sized + ToCss { /// 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 { @@ -145,6 +142,11 @@ pub trait Parser<'i> { false } + /// Whether to parse the `:host` pseudo-class. + fn parse_host(&self) -> bool { + false + } + /// This function can return an "Err" pseudo-element in order to support CSS2.1 /// pseudo-elements. fn parse_non_ts_pseudo_class( @@ -513,6 +515,13 @@ impl Selector { } } + /// Whether this selector is a featureless :host selector, with no + /// combinators to the left. + #[inline] + pub fn is_featureless_host_selector(&self) -> bool { + self.iter().is_featureless_host_selector() + } + /// Returns an iterator over this selector in matching order (right-to-left), /// skipping the rightmost |offset| Components. #[inline] @@ -605,6 +614,14 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { self.next_combinator.take() } + /// Whether this selector is a featureless host selector, with no + /// combinators to the left. + #[inline] + pub(crate) fn is_featureless_host_selector(&mut self) -> bool { + self.all(|component| matches!(*component, Component::Host)) && + self.next_sequence().is_none() + } + /// Returns remaining count of the simple selectors and combinators in the Selector. #[inline] pub fn selector_length(&self) -> usize { @@ -776,6 +793,10 @@ pub enum Component { 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), @@ -1098,6 +1119,7 @@ impl ToCss for Component { Root => dest.write_str(":root"), Empty => dest.write_str(":empty"), Scope => dest.write_str(":scope"), + Host => dest.write_str(":host"), FirstOfType => dest.write_str(":first-of-type"), LastOfType => dest.write_str(":last-of-type"), OnlyOfType => dest.write_str(":only-of-type"), @@ -1947,9 +1969,10 @@ where "root" => Ok(Component::Root), "empty" => Ok(Component::Empty), "scope" => Ok(Component::Scope), + "host" if P::parse_host(parser) => Ok(Component::Host), "first-of-type" => Ok(Component::FirstOfType), - "last-of-type" => Ok(Component::LastOfType), - "only-of-type" => Ok(Component::OnlyOfType), + "last-of-type" => Ok(Component::LastOfType), + "only-of-type" => Ok(Component::OnlyOfType), _ => Err(()) }).or_else(|()| { P::parse_non_ts_pseudo_class(parser, location, name) @@ -1999,11 +2022,6 @@ pub mod tests { fn is_active_or_hover(&self) -> bool { matches!(*self, PseudoClass::Active | PseudoClass::Hover) } - - #[inline] - fn is_host(&self) -> bool { - false - } } impl ToCss for PseudoClass { diff --git a/components/style/data.rs b/components/style/data.rs index e1af10308ad..3cc56b19f0c 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -262,8 +262,8 @@ impl ElementData { let mut non_document_styles = SmallVec::<[_; 3]>::new(); let matches_doc_author_rules = - element.each_applicable_non_document_style_rule_data(|data, quirks_mode| { - non_document_styles.push((data, quirks_mode)) + element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| { + non_document_styles.push((data, quirks_mode, host.map(|h| h.opaque()))) }); let mut processor = StateAndAttrInvalidationProcessor::new( diff --git a/components/style/dom.rs b/components/style/dom.rs index 93c587257a6..7e63fd8ba50 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -772,24 +772,37 @@ pub trait TElement /// Executes the callback for each applicable style rule data which isn't /// the main document's data (which stores UA / author rules). /// + /// The element passed to the callback is the containing shadow host for the + /// data if it comes from Shadow DOM, None if it comes from XBL. + /// /// Returns whether normal document author rules should apply. fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool where Self: 'a, - F: FnMut(&'a CascadeData, QuirksMode), + F: FnMut(&'a CascadeData, QuirksMode, Option), { - let mut doc_rules_apply = !self.each_xbl_cascade_data(&mut f); + let mut doc_rules_apply = !self.each_xbl_cascade_data(|data, quirks_mode| { + f(data, quirks_mode, None); + }); if let Some(shadow) = self.containing_shadow() { doc_rules_apply = false; - f(shadow.style_data(), self.as_node().owner_doc().quirks_mode()); + 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. - let data = slot.containing_shadow().unwrap().style_data(); - f(data, self.as_node().owner_doc().quirks_mode()); + let shadow = slot.containing_shadow().unwrap(); + f( + shadow.style_data(), + self.as_node().owner_doc().quirks_mode(), + Some(shadow.host()), + ); current = slot.assigned_slot(); } diff --git a/components/style/dom_apis.rs b/components/style/dom_apis.rs index 1a775853f90..e73f651a6f4 100644 --- a/components/style/dom_apis.rs +++ b/components/style/dom_apis.rs @@ -7,7 +7,7 @@ use Atom; use context::QuirksMode; -use dom::{TDocument, TElement, TNode}; +use dom::{TDocument, TElement, TNode, TShadowRoot}; use invalidation::element::invalidator::{DescendantInvalidationLists, Invalidation}; use invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector}; use selectors::{Element, NthIndexCache, SelectorList}; @@ -33,6 +33,7 @@ where quirks_mode, ); context.scope_element = Some(element.opaque()); + context.current_host = element.containing_shadow_host().map(|e| e.opaque()); matching::matches_selector_list(selector_list, element, &mut context) } @@ -54,6 +55,7 @@ where quirks_mode, ); context.scope_element = Some(element.opaque()); + context.current_host = element.containing_shadow_host().map(|e| e.opaque()); let mut current = Some(element); while let Some(element) = current.take() { @@ -547,6 +549,10 @@ where let root_element = root.as_element(); matching_context.scope_element = root_element.map(|e| e.opaque()); + matching_context.current_host = match root_element { + Some(root) => root.containing_shadow_host().map(|host| host.opaque()), + None => root.as_shadow_root().map(|root| root.host().opaque()), + }; let fast_result = query_selector_fast::( root, diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index d7a9e0af425..41f33269d7d 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -117,7 +117,8 @@ impl Visit for NonTSPseudoClass { type Impl = SelectorImpl; fn visit(&self, visitor: &mut V) -> bool - where V: SelectorVisitor, + where + V: SelectorVisitor, { if let NonTSPseudoClass::MozAny(ref selectors) = *self { for selector in selectors.iter() { @@ -161,8 +162,6 @@ impl NonTSPseudoClass { match *self { $(NonTSPseudoClass::$name => check_flag!($flags),)* $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)* - // TODO(emilio): Maybe -moz-locale-dir shouldn't be - // content-exposed. NonTSPseudoClass::MozLocaleDir(_) | NonTSPseudoClass::Dir(_) | NonTSPseudoClass::MozAny(_) => false, @@ -275,11 +274,6 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass { 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. @@ -349,6 +343,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { type Impl = SelectorImpl; type Error = StyleParseErrorKind<'i>; + #[inline] fn parse_slotted(&self) -> bool { // NOTE(emilio): Slot assignment and such works per-document, but // getting a document around here is not trivial, and it's not worth @@ -356,6 +351,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { unsafe { structs::nsContentUtils_sIsShadowDOMEnabled } } + #[inline] + fn parse_host(&self) -> bool { + self.parse_slotted() + } + fn pseudo_element_allows_single_colon(name: &str) -> bool { // FIXME: -moz-tree check should probably be ascii-case-insensitive. ::selectors::parser::is_css2_pseudo_element(name) || diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index 0c0bba24e89..c133243abfd 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -18,6 +18,7 @@ use invalidation::element::restyle_hints::RestyleHint; use selector_map::SelectorMap; use selector_parser::Snapshot; use selectors::NthIndexCache; +use selectors::OpaqueElement; use selectors::attr::CaseSensitivity; use selectors::matching::{MatchingContext, MatchingMode, VisitedHandlingMode}; use selectors::matching::matches_selector; @@ -38,9 +39,8 @@ where { element: E, wrapper: ElementWrapper<'b, E>, - nth_index_cache: Option<&'a mut NthIndexCache>, snapshot: &'a Snapshot, - quirks_mode: QuirksMode, + matching_context: &'a mut MatchingContext<'b, E::Impl>, lookup_element: E, removed_id: Option<&'a WeakAtom>, added_id: Option<&'a WeakAtom>, @@ -56,7 +56,7 @@ where /// changes. pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> { shared_context: &'a SharedStyleContext<'b>, - shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode)], + shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode, Option)], matches_document_author_rules: bool, element: E, data: &'a mut ElementData, @@ -67,7 +67,7 @@ impl<'a, 'b: 'a, E: TElement> StateAndAttrInvalidationProcessor<'a, 'b, E> { /// Creates a new StateAndAttrInvalidationProcessor. pub fn new( shared_context: &'a SharedStyleContext<'b>, - shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode)], + shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode, Option)], matches_document_author_rules: bool, element: E, data: &'a mut ElementData, @@ -237,8 +237,7 @@ where state_changes, element, snapshot: &snapshot, - quirks_mode: self.shared_context.quirks_mode(), - nth_index_cache: self.matching_context.nth_index_cache.as_mut().map(|c| &mut **c), + matching_context: &mut self.matching_context, removed_id: id_removed, added_id: id_added, classes_removed: &classes_removed, @@ -260,11 +259,12 @@ where } } - for &(ref data, quirks_mode) in self.shadow_rule_datas { + for &(ref data, quirks_mode, ref host) in self.shadow_rule_datas { // FIXME(emilio): Replace with assert / remove when we figure // out what to do with the quirks mode mismatches // (that is, when bug 1406875 is properly fixed). - collector.quirks_mode = quirks_mode; + collector.matching_context.set_quirks_mode(quirks_mode); + collector.matching_context.current_host = host.clone(); collector.collect_dependencies_in_invalidation_map(data.invalidation_map()); } @@ -333,7 +333,7 @@ where &mut self, map: &'selectors InvalidationMap, ) { - let quirks_mode = self.quirks_mode; + let quirks_mode = self.matching_context.quirks_mode(); let removed_id = self.removed_id; if let Some(ref id) = removed_id { if let Some(deps) = map.id_to_selector.get(id, quirks_mode) { @@ -386,7 +386,7 @@ where ) { map.lookup_with_additional( self.lookup_element, - self.quirks_mode, + self.matching_context.quirks_mode(), self.removed_id, self.classes_removed, |dependency| { @@ -403,7 +403,7 @@ where ) { map.lookup_with_additional( self.lookup_element, - self.quirks_mode, + self.matching_context.quirks_mode(), self.removed_id, self.classes_removed, |dependency| { @@ -429,51 +429,29 @@ where visited_handling_mode: VisitedHandlingMode, dependency: &Dependency, ) -> bool { - let matches_now = { - let mut context = MatchingContext::new_for_visited( - MatchingMode::Normal, - None, - self.nth_index_cache.as_mut().map(|c| &mut **c), - visited_handling_mode, - self.quirks_mode, - ); - + let element = &self.element; + let wrapper = &self.wrapper; + self.matching_context.with_visited_handling_mode(visited_handling_mode, |mut context| { let matches_now = matches_selector( &dependency.selector, dependency.selector_offset, None, - &self.element, + element, &mut context, &mut |_, _| {}, ); - matches_now - }; - - let matched_then = { - let mut context = MatchingContext::new_for_visited( - MatchingMode::Normal, - None, - self.nth_index_cache.as_mut().map(|c| &mut **c), - visited_handling_mode, - self.quirks_mode, - ); - let matched_then = matches_selector( &dependency.selector, dependency.selector_offset, None, - &self.wrapper, + wrapper, &mut context, &mut |_, _| {}, ); - matched_then - }; - - // Check for mismatches in both the match result and also the status - // of whether a relevant link was found. - matched_then != matches_now + matched_then != matches_now + }) } fn scan_dependency( diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 91dce27a7d3..dde2c329f9c 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -311,11 +311,6 @@ pub enum NonTSPseudoClass { 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) diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index ae35d734b91..cf8345b5b47 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -553,6 +553,16 @@ impl StyleSharingCache { return; } + // We can't share style across shadow hosts right now, because they may + // match different :host rules. + // + // TODO(emilio): We could share across the ones that don't have :host + // rules or have the same. + if element.shadow_root().is_some() { + debug!("Failing to insert into the cache: Shadow Host"); + return; + } + // If the element has running animations, we can't share style. // // This is distinct from the specifies_{animations,transitions} check below, @@ -716,6 +726,11 @@ impl StyleSharingCache { return None; } + if target.element.shadow_root().is_some() { + trace!("Miss: Shadow host"); + return None; + } + if target.matches_user_and_author_rules() != candidate.element.matches_user_and_author_rules() { trace!("Miss: User and Author Rules"); diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 59461d152a2..686ce6a6b6e 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -591,25 +591,24 @@ impl Stylist { pub fn any_applicable_rule_data(&self, element: E, mut f: F) -> bool where E: TElement, - F: FnMut(&CascadeData, QuirksMode) -> bool, + F: FnMut(&CascadeData) -> bool, { - if f(&self.cascade_data.user_agent.cascade_data, self.quirks_mode()) { + if f(&self.cascade_data.user_agent.cascade_data) { return true; } let mut maybe = false; let doc_author_rules_apply = - element.each_applicable_non_document_style_rule_data(|data, quirks_mode| { - maybe = maybe || f(&*data, quirks_mode); + element.each_applicable_non_document_style_rule_data(|data, _, _| { + maybe = maybe || f(&*data); }); if maybe || !doc_author_rules_apply { return maybe; } - f(&self.cascade_data.author, self.quirks_mode()) || - f(&self.cascade_data.user, self.quirks_mode()) + f(&self.cascade_data.author) || f(&self.cascade_data.user) } /// Computes the style for a given "precomputed" pseudo-element, taking the @@ -1261,6 +1260,21 @@ impl Stylist { // for !important it should be the other way around. So probably we need // to add some sort of AuthorScoped cascade level or something. if matches_author_rules && !only_default_rules { + if let Some(shadow) = rule_hash_target.shadow_root() { + if let Some(map) = shadow.style_data().host_rules(pseudo_element) { + context.with_shadow_host(Some(rule_hash_target), |context| { + map.get_all_matching_rules( + element, + rule_hash_target, + applicable_declarations, + context, + flags_setter, + CascadeLevel::AuthorNormal, + ); + }); + } + } + // Match slotted rules in reverse order, so that the outer slotted // rules come before the inner rules (and thus have less priority). let mut slots = SmallVec::<[_; 3]>::new(); @@ -1271,32 +1285,35 @@ impl Stylist { } for slot in slots.iter().rev() { - let styles = slot.containing_shadow().unwrap().style_data(); + let shadow = slot.containing_shadow().unwrap(); + let styles = shadow.style_data(); if let Some(map) = styles.slotted_rules(pseudo_element) { - map.get_all_matching_rules( - element, - rule_hash_target, - applicable_declarations, - context, - flags_setter, - CascadeLevel::AuthorNormal, - ); + context.with_shadow_host(Some(shadow.host()), |context| { + map.get_all_matching_rules( + element, + rule_hash_target, + applicable_declarations, + context, + flags_setter, + CascadeLevel::AuthorNormal, + ); + }); } } - // TODO(emilio): We need to look up :host rules if the element is a - // shadow host, when we implement that. if let Some(containing_shadow) = rule_hash_target.containing_shadow() { let cascade_data = containing_shadow.style_data(); if let Some(map) = cascade_data.normal_rules(pseudo_element) { - map.get_all_matching_rules( - element, - rule_hash_target, - applicable_declarations, - context, - flags_setter, - CascadeLevel::AuthorNormal, - ); + context.with_shadow_host(Some(containing_shadow.host()), |context| { + map.get_all_matching_rules( + element, + rule_hash_target, + applicable_declarations, + context, + flags_setter, + CascadeLevel::AuthorNormal, + ); + }); } match_document_author_rules = false; @@ -1421,7 +1438,7 @@ impl Stylist { } let hash = id.get_hash(); - self.any_applicable_rule_data(element, |data, _| { + self.any_applicable_rule_data(element, |data| { data.mapped_ids.might_contain_hash(hash) }) } @@ -1483,22 +1500,24 @@ impl Stylist { ); } - element.each_applicable_non_document_style_rule_data(|data, quirks_mode| { - data.selectors_for_cache_revalidation.lookup( - element, - quirks_mode, - |selector_and_hashes| { - results.push(matches_selector( - &selector_and_hashes.selector, - selector_and_hashes.selector_offset, - Some(&selector_and_hashes.hashes), - &element, - &mut matching_context, - flags_setter - )); - true - } - ); + element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| { + matching_context.with_shadow_host(host, |matching_context| { + data.selectors_for_cache_revalidation.lookup( + element, + quirks_mode, + |selector_and_hashes| { + results.push(matches_selector( + &selector_and_hashes.selector, + selector_and_hashes.selector_offset, + Some(&selector_and_hashes.hashes), + &element, + matching_context, + flags_setter + )); + true + } + ); + }) }); results @@ -1929,6 +1948,15 @@ pub struct CascadeData { /// cascade level. normal_rules: ElementAndPseudoRules, + /// The `:host` pseudo rules that are the rightmost selector. + /// + /// Note that as of right now these can't affect invalidation in any way, + /// until we support the :host() notation. + /// + /// Also, note that other engines don't accept stuff like :host::before / + /// :host::after, so we don't need to store pseudo rules at all. + host_rules: Option>>, + /// The data coming from ::slotted() pseudo-element rules. /// /// We need to store them separately because an element needs to match @@ -2005,6 +2033,7 @@ impl CascadeData { pub fn new() -> Self { Self { normal_rules: ElementAndPseudoRules::default(), + host_rules: None, slotted_rules: None, invalidation_map: InvalidationMap::new(), attribute_dependencies: NonCountingBloomFilter::new(), @@ -2091,6 +2120,15 @@ impl CascadeData { self.normal_rules.rules(pseudo) } + #[inline] + fn host_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap> { + if pseudo.is_some() { + return None; + } + + self.host_rules.as_ref().map(|rules| &**rules) + } + #[inline] fn slotted_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap> { self.slotted_rules.as_ref().and_then(|d| d.rules(pseudo)) @@ -2224,19 +2262,23 @@ impl CascadeData { } } - let rules = if selector.is_slotted() { - self.slotted_rules.get_or_insert_with(|| { - Box::new(Default::default()) - }) + if selector.is_featureless_host_selector() { + let host_rules = + self.host_rules.get_or_insert_with(|| { + Box::new(Default::default()) + }); + host_rules.insert(rule, quirks_mode)?; } else { - &mut self.normal_rules - }; + let rules = if selector.is_slotted() { + self.slotted_rules.get_or_insert_with(|| { + Box::new(Default::default()) + }) + } else { + &mut self.normal_rules + }; - rules.insert( - rule, - pseudo_element, - quirks_mode, - )?; + rules.insert(rule, pseudo_element, quirks_mode)?; + } } self.rules_source_order += 1; } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 41905bfe55e..ce51c3b5d74 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -4914,7 +4914,7 @@ pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency( unsafe { Atom::with(local_name, |atom| { - data.stylist.any_applicable_rule_data(element, |data, _| { + data.stylist.any_applicable_rule_data(element, |data| { data.might_have_attribute_dependency(atom) }) }) @@ -4932,7 +4932,7 @@ pub extern "C" fn Servo_StyleSet_HasStateDependency( let state = ElementState::from_bits_truncate(state); let data = PerDocumentStyleData::from_ffi(raw_data).borrow(); - data.stylist.any_applicable_rule_data(element, |data, _| { + data.stylist.any_applicable_rule_data(element, |data| { data.has_state_dependency(state) }) }