diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 8993a5ea003..e8fd8afebca 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -676,19 +676,22 @@ where None => return false, }; - loop { - let outer_host = host.containing_shadow_host(); - if outer_host.as_ref().map(|h| h.opaque()) == context.shared.current_host { - break; + let current_host = context.shared.current_host; + if current_host != Some(host.opaque()) { + loop { + let outer_host = host.containing_shadow_host(); + if outer_host.as_ref().map(|h| h.opaque()) == current_host { + break; + } + let outer_host = match outer_host { + Some(h) => h, + None => return false, + }; + // TODO(emilio): if worth it, we could early return if + // host doesn't have the exportparts attribute. + hosts.push(host); + host = outer_host; } - let outer_host = match outer_host { - Some(h) => h, - None => return false, - }; - // TODO(emilio): if worth it, we could early return if - // host doesn't have the exportparts attribute. - hosts.push(host); - host = outer_host; } // Translate the part into the right scope. diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs index 7b7b0b48787..26a85d9dc2a 100644 --- a/components/style/rule_collector.rs +++ b/components/style/rule_collector.rs @@ -55,11 +55,6 @@ pub fn containing_shadow_ignoring_svg_use( } } -#[inline] -fn sort_rules_from(rules: &mut ApplicableDeclarationList, start: usize) { - rules[start..].sort_unstable_by_key(|block| (block.specificity, block.source_order())); -} - /// An object that we use with all the intermediate state needed for the /// cascade. /// @@ -82,6 +77,7 @@ where flags_setter: &'a mut F, matches_user_and_author_rules: bool, matches_document_author_rules: bool, + in_sort_scope: bool, } impl<'a, 'b: 'a, E, F: 'a> RuleCollector<'a, 'b, E, F> @@ -134,9 +130,35 @@ where rules, matches_user_and_author_rules, matches_document_author_rules: matches_user_and_author_rules, + in_sort_scope: false, } } + /// Sets up the state necessary to collect rules from a given DOM tree + /// (either the document tree, or a shadow tree). + /// + /// All rules in the same tree need to be matched together, and this + /// function takes care of sorting them by specificity and source order. + #[inline] + fn in_tree(&mut self, host: Option, f: impl FnOnce(&mut Self)) { + debug_assert!(!self.in_sort_scope, "Nested sorting makes no sense"); + let start = self.rules.len(); + self.in_sort_scope = true; + let old_host = self.context.current_host.take(); + self.context.current_host = host.map(|e| e.opaque()); + f(self); + if start != self.rules.len() { + self.rules[start..].sort_unstable_by_key(|block| (block.specificity, block.source_order())); + } + self.context.current_host = old_host; + self.in_sort_scope = false; + } + + #[inline] + fn in_shadow_tree(&mut self, host: E, f: impl FnOnce(&mut Self)) { + self.in_tree(Some(host), f); + } + fn collect_stylist_rules(&mut self, origin: Origin) { let cascade_level = match origin { Origin::UserAgent => CascadeLevel::UANormal, @@ -150,7 +172,9 @@ where None => return, }; - self.collect_rules_internal(None, map, cascade_level); + self.in_tree(None, |collector| { + collector.collect_rules_in_map(map, cascade_level); + }); } fn collect_user_agent_rules(&mut self) { @@ -189,39 +213,38 @@ where } } - fn collect_rules_in_shadow_tree( + #[inline] + fn collect_rules_in_list( &mut self, - shadow_host: E, - map: &SelectorMap, + part_rules: &[Rule], cascade_level: CascadeLevel, ) { - debug_assert!(shadow_host.shadow_root().is_some()); - self.collect_rules_internal(Some(shadow_host), map, cascade_level); + debug_assert!(self.in_sort_scope, "Rules gotta be sorted"); + SelectorMap::get_matching_rules( + self.element, + part_rules, + &mut self.rules, + &mut self.context, + &mut self.flags_setter, + cascade_level, + ); } #[inline] - fn collect_rules_internal( + fn collect_rules_in_map( &mut self, - shadow_host: Option, map: &SelectorMap, cascade_level: CascadeLevel, ) { - let element = self.element; - let rule_hash_target = self.rule_hash_target; - let rules = &mut self.rules; - let flags_setter = &mut self.flags_setter; - let start = rules.len(); - self.context.with_shadow_host(shadow_host, |context| { - map.get_all_matching_rules( - element, - rule_hash_target, - rules, - context, - flags_setter, - cascade_level, - ); - }); - sort_rules_from(rules, start); + debug_assert!(self.in_sort_scope, "Rules gotta be sorted"); + map.get_all_matching_rules( + self.element, + self.rule_hash_target, + &mut self.rules, + &mut self.context, + &mut self.flags_setter, + cascade_level, + ); } /// Collects the rules for the ::slotted pseudo-element and the :host @@ -258,17 +281,16 @@ where None => continue, }; - self.collect_rules_in_shadow_tree( - shadow.host(), - slotted_rules, - CascadeLevel::AuthorNormal { + self.in_shadow_tree(shadow.host(), |collector| { + let cascade_level = CascadeLevel::AuthorNormal { shadow_cascade_order, - }, - ); + }; + collector.collect_rules_in_map(slotted_rules, cascade_level); + }); } } - fn collect_normal_rules_from_containing_shadow_tree(&mut self) { + fn collect_rules_from_containing_shadow_tree(&mut self) { if !self.matches_user_and_author_rules { return; } @@ -281,11 +303,34 @@ where self.matches_document_author_rules = false; - let cascade_data = containing_shadow.style_data(); - let host = containing_shadow.host(); - if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) { - self.collect_rules_in_shadow_tree(host, map, CascadeLevel::same_tree_author_normal()); - } + let cascade_data = match containing_shadow.style_data() { + Some(c) => c, + None => return, + }; + + let cascade_level = CascadeLevel::same_tree_author_normal(); + self.in_shadow_tree(containing_shadow.host(), |collector| { + if let Some(map) = cascade_data.normal_rules(collector.pseudo_element) { + collector.collect_rules_in_map(map, cascade_level); + } + + // Collect rules from :host::part() and such + let hash_target = collector.rule_hash_target; + if !hash_target.has_part_attr() { + return; + } + + let part_rules = match cascade_data.part_rules(collector.pseudo_element) { + Some(p) => p, + None => return, + }; + + hash_target.each_part(|part| { + if let Some(part_rules) = part_rules.get(part) { + collector.collect_rules_in_list(part_rules, cascade_level); + } + }); + }); } /// Collects the rules for the :host pseudo-class. @@ -311,13 +356,12 @@ where }; let rule_hash_target = self.rule_hash_target; - self.collect_rules_in_shadow_tree( - rule_hash_target, - host_rules, - CascadeLevel::AuthorNormal { + self.in_shadow_tree(rule_hash_target, |collector| { + let cascade_level = CascadeLevel::AuthorNormal { shadow_cascade_order, - }, - ); + }; + collector.collect_rules_in_map(host_rules, cascade_level); + }); } fn collect_document_author_rules(&mut self) { @@ -328,7 +372,7 @@ where self.collect_stylist_rules(Origin::Author); } - fn collect_part_rules(&mut self) { + fn collect_part_rules_from_outer_trees(&mut self) { if !self.rule_hash_target.has_part_attr() { return; } @@ -363,28 +407,19 @@ where if let Some(part_rules) = part_rules { let containing_host = outer_shadow.map(|s| s.host()); - let element = self.element; - let rules = &mut self.rules; - let flags_setter = &mut self.flags_setter; let cascade_level = CascadeLevel::AuthorNormal { shadow_cascade_order, }; - let start = rules.len(); - self.context.with_shadow_host(containing_host, |context| { + self.in_tree(containing_host, |collector| { for p in &parts { if let Some(part_rules) = part_rules.get(p) { - SelectorMap::get_matching_rules( - element, - &part_rules, - rules, - context, - flags_setter, + collector.collect_rules_in_list( + part_rules, cascade_level, ); } } }); - sort_rules_from(rules, start); shadow_cascade_order.inc(); } @@ -460,10 +495,10 @@ where return; } self.collect_host_and_slotted_rules(); - self.collect_normal_rules_from_containing_shadow_tree(); + self.collect_rules_from_containing_shadow_tree(); self.collect_document_author_rules(); self.collect_style_attribute(); - self.collect_part_rules(); + self.collect_part_rules_from_outer_trees(); self.collect_animation_rules(); } }