aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/stylist.rs
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2019-06-11 17:42:32 +0000
committerEmilio Cobos Álvarez <emilio@crisal.io>2019-06-25 13:11:28 +0200
commit39de0a068ea3b1ff021ddf979575ce2feeaa6b32 (patch)
treeefb3ee5dd0f5dd970c88d55ee82ad4b371a7100d /components/style/stylist.rs
parentfac050325c35729e6f5ff137e259bfe0b301b925 (diff)
downloadservo-39de0a068ea3b1ff021ddf979575ce2feeaa6b32.tar.gz
servo-39de0a068ea3b1ff021ddf979575ce2feeaa6b32.zip
style: Collect ::part() rules during CascadeData rebuilds.
Unlike for :host() or ::slotted(), or regular rules, we don't need a whole SelectorMap<>, so gotta make the code a bit more generic. Differential Revision: https://phabricator.services.mozilla.com/D32646
Diffstat (limited to 'components/style/stylist.rs')
-rw-r--r--components/style/stylist.rs114
1 files changed, 79 insertions, 35 deletions
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index a98b8a89eb5..c2601515002 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -33,6 +33,7 @@ use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule,
use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
use crate::thread_state::{self, ThreadState};
use crate::{Atom, LocalName, Namespace, WeakAtom};
+use fallible::FallibleVec;
use hashglobe::FailedAllocationError;
#[cfg(feature = "gecko")]
use malloc_size_of::MallocUnconditionalShallowSizeOf;
@@ -1641,9 +1642,9 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
/// A set of rules for element and pseudo-elements.
#[derive(Debug, Default, MallocSizeOf)]
-struct ElementAndPseudoRules {
+struct GenericElementAndPseudoRules<Map> {
/// Rules from stylesheets at this `CascadeData`'s origin.
- element_map: SelectorMap<Rule>,
+ element_map: Map,
/// Rules from stylesheets at this `CascadeData`'s origin that correspond
/// to a given pseudo-element.
@@ -1651,39 +1652,30 @@ struct ElementAndPseudoRules {
/// FIXME(emilio): There are a bunch of wasted entries here in practice.
/// Figure out a good way to do a `PerNonAnonBox` and `PerAnonBox` (for
/// `precomputed_values_for_pseudo`) without duplicating a lot of code.
- pseudos_map: PerPseudoElementMap<Box<SelectorMap<Rule>>>,
+ pseudos_map: PerPseudoElementMap<Box<Map>>,
}
-impl ElementAndPseudoRules {
+impl<Map: Default + MallocSizeOf> GenericElementAndPseudoRules<Map> {
#[inline(always)]
- fn insert(
- &mut self,
- rule: Rule,
- pseudo_element: Option<&PseudoElement>,
- quirks_mode: QuirksMode,
- ) -> Result<(), FailedAllocationError> {
+ fn for_insertion(&mut self, pseudo_element: Option<&PseudoElement>) -> &mut Map {
debug_assert!(
- pseudo_element.map_or(true, |pseudo| !pseudo.is_precomputed() &&
- !pseudo.is_unknown_webkit_pseudo_element())
+ pseudo_element.map_or(true, |pseudo| {
+ !pseudo.is_precomputed() && !pseudo.is_unknown_webkit_pseudo_element()
+ }),
+ "Precomputed pseudos should end up in precomputed_pseudo_element_decls, \
+ and unknown webkit pseudos should be discarded before getting here"
);
- let map = match pseudo_element {
+ match pseudo_element {
None => &mut self.element_map,
Some(pseudo) => self
.pseudos_map
- .get_or_insert_with(pseudo, || Box::new(SelectorMap::new())),
- };
-
- map.insert(rule, quirks_mode)
- }
-
- fn clear(&mut self) {
- self.element_map.clear();
- self.pseudos_map.clear();
+ .get_or_insert_with(pseudo, || Box::new(Default::default())),
+ }
}
#[inline]
- fn rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
+ fn rules(&self, pseudo: Option<&PseudoElement>) -> Option<&Map> {
match pseudo {
Some(pseudo) => self.pseudos_map.get(pseudo).map(|p| &**p),
None => Some(&self.element_map),
@@ -1703,6 +1695,26 @@ impl ElementAndPseudoRules {
}
}
+type ElementAndPseudoRules = GenericElementAndPseudoRules<SelectorMap<Rule>>;
+type PartMap = PrecomputedHashMap<Atom, SmallVec<[Rule; 1]>>;
+type PartElementAndPseudoRules = GenericElementAndPseudoRules<PartMap>;
+
+impl ElementAndPseudoRules {
+ // TODO(emilio): Should we retain storage of these?
+ fn clear(&mut self) {
+ self.element_map.clear();
+ self.pseudos_map.clear();
+ }
+}
+
+impl PartElementAndPseudoRules {
+ // TODO(emilio): Should we retain storage of these?
+ fn clear(&mut self) {
+ self.element_map.clear();
+ self.pseudos_map.clear();
+ }
+}
+
/// Data resulting from performing the CSS cascade that is specific to a given
/// origin.
///
@@ -1727,6 +1739,12 @@ pub struct CascadeData {
/// containing style scopes starting from the closest assigned slot.
slotted_rules: Option<Box<ElementAndPseudoRules>>,
+ /// The data coming from ::part() pseudo-element rules.
+ ///
+ /// We need to store them separately because an element needs to match
+ /// ::part() pseudo-element rules in different shadow roots.
+ part_rules: Option<Box<PartElementAndPseudoRules>>,
+
/// The invalidation map for these rules.
invalidation_map: InvalidationMap,
@@ -1786,6 +1804,7 @@ impl CascadeData {
normal_rules: ElementAndPseudoRules::default(),
host_rules: None,
slotted_rules: None,
+ part_rules: None,
invalidation_map: InvalidationMap::new(),
attribute_dependencies: PrecomputedHashSet::default(),
state_dependencies: ElementState::empty(),
@@ -1876,6 +1895,12 @@ impl CascadeData {
self.slotted_rules.as_ref().and_then(|d| d.rules(pseudo))
}
+ /// Returns the parts rule map for a given pseudo-element.
+ #[inline]
+ pub fn part_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&PartMap> {
+ self.part_rules.as_ref().and_then(|d| d.rules(pseudo))
+ }
+
/// Collects all the applicable media query results into `results`.
///
/// This duplicates part of the logic in `add_stylesheet`, which is
@@ -2005,20 +2030,33 @@ impl CascadeData {
}
}
- // NOTE(emilio): It's fine to look at :host and then at
- // ::slotted(..), since :host::slotted(..) could never
- // possibly match, as <slot> is not a valid shadow host.
- let rules = if selector.is_featureless_host_selector_or_pseudo_element() {
- self.host_rules
- .get_or_insert_with(|| Box::new(Default::default()))
- } else if selector.is_slotted() {
- self.slotted_rules
+ // Part is special, since given it doesn't have any
+ // selectors inside, it's not worth using a whole
+ // SelectorMap for it.
+ if let Some(part) = selector.part() {
+ self.part_rules
.get_or_insert_with(|| Box::new(Default::default()))
+ .for_insertion(pseudo_element)
+ .try_entry(part.clone())?
+ .or_insert_with(SmallVec::new)
+ .try_push(rule)?;
} else {
- &mut self.normal_rules
- };
-
- rules.insert(rule, pseudo_element, quirks_mode)?;
+ // NOTE(emilio): It's fine to look at :host and then at
+ // ::slotted(..), since :host::slotted(..) could never
+ // possibly match, as <slot> is not a valid shadow host.
+ let rules =
+ if selector.is_featureless_host_selector_or_pseudo_element() {
+ self.host_rules
+ .get_or_insert_with(|| Box::new(Default::default()))
+ } else if selector.is_slotted() {
+ self.slotted_rules
+ .get_or_insert_with(|| Box::new(Default::default()))
+ } else {
+ &mut self.normal_rules
+ }
+ .for_insertion(pseudo_element);
+ rules.insert(rule, quirks_mode)?;
+ }
}
self.rules_source_order += 1;
},
@@ -2184,6 +2222,9 @@ impl CascadeData {
if let Some(ref mut slotted_rules) = self.slotted_rules {
slotted_rules.clear();
}
+ if let Some(ref mut part_rules) = self.part_rules {
+ part_rules.clear();
+ }
if let Some(ref mut host_rules) = self.host_rules {
host_rules.clear();
}
@@ -2212,6 +2253,9 @@ impl CascadeData {
if let Some(ref slotted_rules) = self.slotted_rules {
slotted_rules.add_size_of(ops, sizes);
}
+ if let Some(ref part_rules) = self.part_rules {
+ part_rules.add_size_of(ops, sizes);
+ }
if let Some(ref host_rules) = self.host_rules {
host_rules.add_size_of(ops, sizes);
}