Bug 1883017: Handle resizable buffers in ArrayBufferObject::ensureNonInline. r=sfink
[gecko.git] / servo / components / style / sharing / mod.rs
blob28592a02c5f2379e04e228fa4c496be5cdba93e8
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 //! Code related to the style sharing cache, an optimization that allows similar
6 //! nodes to share style without having to run selector matching twice.
7 //!
8 //! The basic setup is as follows.  We have an LRU cache of style sharing
9 //! candidates.  When we try to style a target element, we first check whether
10 //! we can quickly determine that styles match something in this cache, and if
11 //! so we just use the cached style information.  This check is done with a
12 //! StyleBloom filter set up for the target element, which may not be a correct
13 //! state for the cached candidate element if they're cousins instead of
14 //! siblings.
15 //!
16 //! The complicated part is determining that styles match.  This is subject to
17 //! the following constraints:
18 //!
19 //! 1) The target and candidate must be inheriting the same styles.
20 //! 2) The target and candidate must have exactly the same rules matching them.
21 //! 3) The target and candidate must have exactly the same non-selector-based
22 //!    style information (inline styles, presentation hints).
23 //! 4) The target and candidate must have exactly the same rules matching their
24 //!    pseudo-elements, because an element's style data points to the style
25 //!    data for its pseudo-elements.
26 //!
27 //! These constraints are satisfied in the following ways:
28 //!
29 //! * We check that the parents of the target and the candidate have the same
30 //!   computed style.  This addresses constraint 1.
31 //!
32 //! * We check that the target and candidate have the same inline style and
33 //!   presentation hint declarations.  This addresses constraint 3.
34 //!
35 //! * We ensure that a target matches a candidate only if they have the same
36 //!   matching result for all selectors that target either elements or the
37 //!   originating elements of pseudo-elements.  This addresses constraint 4
38 //!   (because it prevents a target that has pseudo-element styles from matching
39 //!   a candidate that has different pseudo-element styles) as well as
40 //!   constraint 2.
41 //!
42 //! The actual checks that ensure that elements match the same rules are
43 //! conceptually split up into two pieces.  First, we do various checks on
44 //! elements that make sure that the set of possible rules in all selector maps
45 //! in the stylist (for normal styling and for pseudo-elements) that might match
46 //! the two elements is the same.  For example, we enforce that the target and
47 //! candidate must have the same localname and namespace.  Second, we have a
48 //! selector map of "revalidation selectors" that the stylist maintains that we
49 //! actually match against the target and candidate and then check whether the
50 //! two sets of results were the same.  Due to the up-front selector map checks,
51 //! we know that the target and candidate will be matched against the same exact
52 //! set of revalidation selectors, so the match result arrays can be compared
53 //! directly.
54 //!
55 //! It's very important that a selector be added to the set of revalidation
56 //! selectors any time there are two elements that could pass all the up-front
57 //! checks but match differently against some ComplexSelector in the selector.
58 //! If that happens, then they can have descendants that might themselves pass
59 //! the up-front checks but would have different matching results for the
60 //! selector in question.  In this case, "descendants" includes pseudo-elements,
61 //! so there is a single selector map of revalidation selectors that includes
62 //! both selectors targeting elements and selectors targeting pseudo-element
63 //! originating elements.  We ensure that the pseudo-element parts of all these
64 //! selectors are effectively stripped off, so that matching them all against
65 //! elements makes sense.
67 use crate::applicable_declarations::ApplicableDeclarationBlock;
68 use crate::bloom::StyleBloom;
69 use crate::computed_value_flags::ComputedValueFlags;
70 use crate::context::{SharedStyleContext, StyleContext};
71 use crate::dom::{SendElement, TElement};
72 use crate::properties::ComputedValues;
73 use crate::rule_tree::StrongRuleNode;
74 use crate::selector_map::RelevantAttributes;
75 use crate::style_resolver::{PrimaryStyle, ResolvedElementStyles};
76 use crate::stylist::Stylist;
77 use crate::values::AtomIdent;
78 use atomic_refcell::{AtomicRefCell, AtomicRefMut};
79 use owning_ref::OwningHandle;
80 use selectors::matching::{NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode};
81 use servo_arc::Arc;
82 use smallbitvec::SmallBitVec;
83 use smallvec::SmallVec;
84 use std::marker::PhantomData;
85 use std::mem::{self, ManuallyDrop};
86 use std::ops::Deref;
87 use std::ptr::NonNull;
88 use uluru::LRUCache;
90 mod checks;
92 /// The amount of nodes that the style sharing candidate cache should hold at
93 /// most.
94 ///
95 /// The cache size was chosen by measuring style sharing and resulting
96 /// performance on a few pages; sizes up to about 32 were giving good sharing
97 /// improvements (e.g. 3x fewer styles having to be resolved than at size 8) and
98 /// slight performance improvements.  Sizes larger than 32 haven't really been
99 /// tested.
100 pub const SHARING_CACHE_SIZE: usize = 32;
102 /// Opaque pointer type to compare ComputedValues identities.
103 #[derive(Clone, Debug, Eq, PartialEq)]
104 pub struct OpaqueComputedValues(NonNull<()>);
106 unsafe impl Send for OpaqueComputedValues {}
107 unsafe impl Sync for OpaqueComputedValues {}
109 impl OpaqueComputedValues {
110     fn from(cv: &ComputedValues) -> Self {
111         let p =
112             unsafe { NonNull::new_unchecked(cv as *const ComputedValues as *const () as *mut ()) };
113         OpaqueComputedValues(p)
114     }
116     fn eq(&self, cv: &ComputedValues) -> bool {
117         Self::from(cv) == *self
118     }
121 /// The results from the revalidation step.
123 /// Rather than either:
125 ///  * Plainly rejecting sharing for elements with different attributes (which would be unfortunate
126 ///    because a lot of elements have different attributes yet those attributes are not
127 ///    style-relevant).
129 ///  * Having to give up on per-attribute bucketing, which would be unfortunate because it
130 ///    increases the cost of revalidation for pages with lots of global attribute selectors (see
131 ///    bug 1868316).
133 ///  * We also store the style-relevant attributes for these elements, in order to guarantee that
134 ///    we end up looking at the same selectors.
136 #[derive(Debug, Default)]
137 pub struct RevalidationResult {
138     /// A bit for each selector matched. This is sound because we guarantee we look up into the
139     /// same buckets via the pre-revalidation checks and relevant_attributes.
140     pub selectors_matched: SmallBitVec,
141     /// The set of attributes of this element that were relevant for its style.
142     pub relevant_attributes: RelevantAttributes,
145 impl PartialEq for RevalidationResult {
146     fn eq(&self, other: &Self) -> bool {
147         if self.relevant_attributes != other.relevant_attributes {
148             return false;
149         }
151         // This assert "ensures", to some extent, that the two candidates have matched the
152         // same rulehash buckets, and as such, that the bits we're comparing represent the
153         // same set of selectors.
154         debug_assert_eq!(self.selectors_matched.len(), other.selectors_matched.len());
155         self.selectors_matched == other.selectors_matched
156     }
159 /// Some data we want to avoid recomputing all the time while trying to share
160 /// style.
161 #[derive(Debug, Default)]
162 pub struct ValidationData {
163     /// The class list of this element.
164     ///
165     /// TODO(emilio): Maybe check whether rules for these classes apply to the
166     /// element?
167     class_list: Option<SmallVec<[AtomIdent; 5]>>,
169     /// The part list of this element.
170     ///
171     /// TODO(emilio): Maybe check whether rules with these part names apply to
172     /// the element?
173     part_list: Option<SmallVec<[AtomIdent; 5]>>,
175     /// The list of presentational attributes of the element.
176     pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,
178     /// The pointer identity of the parent ComputedValues.
179     parent_style_identity: Option<OpaqueComputedValues>,
181     /// The cached result of matching this entry against the revalidation
182     /// selectors.
183     revalidation_match_results: Option<RevalidationResult>,
186 impl ValidationData {
187     /// Move the cached data to a new instance, and return it.
188     pub fn take(&mut self) -> Self {
189         mem::replace(self, Self::default())
190     }
192     /// Get or compute the list of presentational attributes associated with
193     /// this element.
194     pub fn pres_hints<E>(&mut self, element: E) -> &[ApplicableDeclarationBlock]
195     where
196         E: TElement,
197     {
198         self.pres_hints.get_or_insert_with(|| {
199             let mut pres_hints = SmallVec::new();
200             element.synthesize_presentational_hints_for_legacy_attributes(
201                 VisitedHandlingMode::AllLinksUnvisited,
202                 &mut pres_hints,
203             );
204             pres_hints
205         })
206     }
208     /// Get or compute the part-list associated with this element.
209     pub fn part_list<E>(&mut self, element: E) -> &[AtomIdent]
210     where
211         E: TElement,
212     {
213         if !element.has_part_attr() {
214             return &[];
215         }
216         self.part_list.get_or_insert_with(|| {
217             let mut list = SmallVec::<[_; 5]>::new();
218             element.each_part(|p| list.push(p.clone()));
219             // See below for the reasoning.
220             if !list.spilled() {
221                 list.sort_unstable_by_key(|a| a.get_hash());
222             }
223             list
224         })
225     }
227     /// Get or compute the class-list associated with this element.
228     pub fn class_list<E>(&mut self, element: E) -> &[AtomIdent]
229     where
230         E: TElement,
231     {
232         self.class_list.get_or_insert_with(|| {
233             let mut list = SmallVec::<[_; 5]>::new();
234             element.each_class(|c| list.push(c.clone()));
235             // Assuming there are a reasonable number of classes (we use the
236             // inline capacity as "reasonable number"), sort them to so that
237             // we don't mistakenly reject sharing candidates when one element
238             // has "foo bar" and the other has "bar foo".
239             if !list.spilled() {
240                 list.sort_unstable_by_key(|a| a.get_hash());
241             }
242             list
243         })
244     }
246     /// Get or compute the parent style identity.
247     pub fn parent_style_identity<E>(&mut self, el: E) -> OpaqueComputedValues
248     where
249         E: TElement,
250     {
251         self.parent_style_identity
252             .get_or_insert_with(|| {
253                 let parent = el.inheritance_parent().unwrap();
254                 let values =
255                     OpaqueComputedValues::from(parent.borrow_data().unwrap().styles.primary());
256                 values
257             })
258             .clone()
259     }
261     /// Computes the revalidation results if needed, and returns it.
262     /// Inline so we know at compile time what bloom_known_valid is.
263     #[inline]
264     fn revalidation_match_results<E>(
265         &mut self,
266         element: E,
267         stylist: &Stylist,
268         bloom: &StyleBloom<E>,
269         selector_caches: &mut SelectorCaches,
270         bloom_known_valid: bool,
271         needs_selector_flags: NeedsSelectorFlags,
272     ) -> &RevalidationResult
273     where
274         E: TElement,
275     {
276         self.revalidation_match_results.get_or_insert_with(|| {
277             // The bloom filter may already be set up for our element.
278             // If it is, use it.  If not, we must be in a candidate
279             // (i.e. something in the cache), and the element is one
280             // of our cousins, not a sibling.  In that case, we'll
281             // just do revalidation selector matching without a bloom
282             // filter, to avoid thrashing the filter.
283             let bloom_to_use = if bloom_known_valid {
284                 debug_assert_eq!(bloom.current_parent(), element.traversal_parent());
285                 Some(bloom.filter())
286             } else {
287                 if bloom.current_parent() == element.traversal_parent() {
288                     Some(bloom.filter())
289                 } else {
290                     None
291                 }
292             };
293             stylist.match_revalidation_selectors(
294                 element,
295                 bloom_to_use,
296                 selector_caches,
297                 needs_selector_flags,
298             )
299         })
300     }
303 /// Information regarding a style sharing candidate, that is, an entry in the
304 /// style sharing cache.
306 /// Note that this information is stored in TLS and cleared after the traversal,
307 /// and once here, the style information of the element is immutable, so it's
308 /// safe to access.
310 /// Important: If you change the members/layout here, You need to do the same for
311 /// FakeCandidate below.
312 #[derive(Debug)]
313 pub struct StyleSharingCandidate<E: TElement> {
314     /// The element.
315     element: E,
316     validation_data: ValidationData,
319 struct FakeCandidate {
320     _element: usize,
321     _validation_data: ValidationData,
324 impl<E: TElement> Deref for StyleSharingCandidate<E> {
325     type Target = E;
327     fn deref(&self) -> &Self::Target {
328         &self.element
329     }
332 impl<E: TElement> StyleSharingCandidate<E> {
333     /// Get the classlist of this candidate.
334     fn class_list(&mut self) -> &[AtomIdent] {
335         self.validation_data.class_list(self.element)
336     }
338     /// Get the part list of this candidate.
339     fn part_list(&mut self) -> &[AtomIdent] {
340         self.validation_data.part_list(self.element)
341     }
343     /// Get the pres hints of this candidate.
344     fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
345         self.validation_data.pres_hints(self.element)
346     }
348     /// Get the parent style identity.
349     fn parent_style_identity(&mut self) -> OpaqueComputedValues {
350         self.validation_data.parent_style_identity(self.element)
351     }
353     /// Compute the bit vector of revalidation selector match results
354     /// for this candidate.
355     fn revalidation_match_results(
356         &mut self,
357         stylist: &Stylist,
358         bloom: &StyleBloom<E>,
359         selector_caches: &mut SelectorCaches,
360     ) -> &RevalidationResult {
361         self.validation_data.revalidation_match_results(
362             self.element,
363             stylist,
364             bloom,
365             selector_caches,
366             /* bloom_known_valid = */ false,
367             // The candidate must already have the right bits already, if
368             // needed.
369             NeedsSelectorFlags::No,
370         )
371     }
374 impl<E: TElement> PartialEq<StyleSharingCandidate<E>> for StyleSharingCandidate<E> {
375     fn eq(&self, other: &Self) -> bool {
376         self.element == other.element
377     }
380 /// An element we want to test against the style sharing cache.
381 pub struct StyleSharingTarget<E: TElement> {
382     element: E,
383     validation_data: ValidationData,
386 impl<E: TElement> Deref for StyleSharingTarget<E> {
387     type Target = E;
389     fn deref(&self) -> &Self::Target {
390         &self.element
391     }
394 impl<E: TElement> StyleSharingTarget<E> {
395     /// Trivially construct a new StyleSharingTarget to test against the cache.
396     pub fn new(element: E) -> Self {
397         Self {
398             element: element,
399             validation_data: ValidationData::default(),
400         }
401     }
403     fn class_list(&mut self) -> &[AtomIdent] {
404         self.validation_data.class_list(self.element)
405     }
407     fn part_list(&mut self) -> &[AtomIdent] {
408         self.validation_data.part_list(self.element)
409     }
411     /// Get the pres hints of this candidate.
412     fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
413         self.validation_data.pres_hints(self.element)
414     }
416     /// Get the parent style identity.
417     fn parent_style_identity(&mut self) -> OpaqueComputedValues {
418         self.validation_data.parent_style_identity(self.element)
419     }
421     fn revalidation_match_results(
422         &mut self,
423         stylist: &Stylist,
424         bloom: &StyleBloom<E>,
425         selector_caches: &mut SelectorCaches,
426     ) -> &RevalidationResult {
427         // It's important to set the selector flags. Otherwise, if we succeed in
428         // sharing the style, we may not set the slow selector flags for the
429         // right elements (which may not necessarily be |element|), causing
430         // missed restyles after future DOM mutations.
431         //
432         // Gecko's test_bug534804.html exercises this. A minimal testcase is:
433         // <style> #e:empty + span { ... } </style>
434         // <span id="e">
435         //   <span></span>
436         // </span>
437         // <span></span>
438         //
439         // The style sharing cache will get a hit for the second span. When the
440         // child span is subsequently removed from the DOM, missing selector
441         // flags would cause us to miss the restyle on the second span.
442         self.validation_data.revalidation_match_results(
443             self.element,
444             stylist,
445             bloom,
446             selector_caches,
447             /* bloom_known_valid = */ true,
448             NeedsSelectorFlags::Yes,
449         )
450     }
452     /// Attempts to share a style with another node.
453     pub fn share_style_if_possible(
454         &mut self,
455         context: &mut StyleContext<E>,
456     ) -> Option<ResolvedElementStyles> {
457         let cache = &mut context.thread_local.sharing_cache;
458         let shared_context = &context.shared;
459         let bloom_filter = &context.thread_local.bloom_filter;
460         let selector_caches = &mut context.thread_local.selector_caches;
462         if cache.dom_depth != bloom_filter.matching_depth() {
463             debug!(
464                 "Can't share style, because DOM depth changed from {:?} to {:?}, element: {:?}",
465                 cache.dom_depth,
466                 bloom_filter.matching_depth(),
467                 self.element
468             );
469             return None;
470         }
471         debug_assert_eq!(
472             bloom_filter.current_parent(),
473             self.element.traversal_parent()
474         );
476         cache.share_style_if_possible(shared_context, bloom_filter, selector_caches, self)
477     }
479     /// Gets the validation data used to match against this target, if any.
480     pub fn take_validation_data(&mut self) -> ValidationData {
481         self.validation_data.take()
482     }
485 struct SharingCacheBase<Candidate> {
486     entries: LRUCache<Candidate, SHARING_CACHE_SIZE>,
489 impl<Candidate> Default for SharingCacheBase<Candidate> {
490     fn default() -> Self {
491         Self {
492             entries: LRUCache::default(),
493         }
494     }
497 impl<Candidate> SharingCacheBase<Candidate> {
498     fn clear(&mut self) {
499         self.entries.clear();
500     }
502     fn is_empty(&self) -> bool {
503         self.entries.len() == 0
504     }
507 impl<E: TElement> SharingCache<E> {
508     fn insert(
509         &mut self,
510         element: E,
511         validation_data_holder: Option<&mut StyleSharingTarget<E>>,
512     ) {
513         let validation_data = match validation_data_holder {
514             Some(v) => v.take_validation_data(),
515             None => ValidationData::default(),
516         };
517         self.entries.insert(StyleSharingCandidate {
518             element,
519             validation_data,
520         });
521     }
524 /// Style sharing caches are are large allocations, so we store them in thread-local
525 /// storage such that they can be reused across style traversals. Ideally, we'd just
526 /// stack-allocate these buffers with uninitialized memory, but right now rustc can't
527 /// avoid memmoving the entire cache during setup, which gets very expensive. See
528 /// issues like [1] and [2].
530 /// Given that the cache stores entries of type TElement, we transmute to usize
531 /// before storing in TLS. This is safe as long as we make sure to empty the cache
532 /// before we let it go.
534 /// [1] https://github.com/rust-lang/rust/issues/42763
535 /// [2] https://github.com/rust-lang/rust/issues/13707
536 type SharingCache<E> = SharingCacheBase<StyleSharingCandidate<E>>;
537 type TypelessSharingCache = SharingCacheBase<FakeCandidate>;
538 type StoredSharingCache = Arc<AtomicRefCell<TypelessSharingCache>>;
540 thread_local! {
541     // See the comment on bloom.rs about why do we leak this.
542     static SHARING_CACHE_KEY: ManuallyDrop<StoredSharingCache> =
543         ManuallyDrop::new(Arc::new_leaked(Default::default()));
546 /// An LRU cache of the last few nodes seen, so that we can aggressively try to
547 /// reuse their styles.
549 /// Note that this cache is flushed every time we steal work from the queue, so
550 /// storing nodes here temporarily is safe.
551 pub struct StyleSharingCache<E: TElement> {
552     /// The LRU cache, with the type cast away to allow persisting the allocation.
553     cache_typeless: OwningHandle<StoredSharingCache, AtomicRefMut<'static, TypelessSharingCache>>,
554     /// Bind this structure to the lifetime of E, since that's what we effectively store.
555     marker: PhantomData<SendElement<E>>,
556     /// The DOM depth we're currently at.  This is used as an optimization to
557     /// clear the cache when we change depths, since we know at that point
558     /// nothing in the cache will match.
559     dom_depth: usize,
562 impl<E: TElement> Drop for StyleSharingCache<E> {
563     fn drop(&mut self) {
564         self.clear();
565     }
568 impl<E: TElement> StyleSharingCache<E> {
569     #[allow(dead_code)]
570     fn cache(&self) -> &SharingCache<E> {
571         let base: &TypelessSharingCache = &*self.cache_typeless;
572         unsafe { mem::transmute(base) }
573     }
575     fn cache_mut(&mut self) -> &mut SharingCache<E> {
576         let base: &mut TypelessSharingCache = &mut *self.cache_typeless;
577         unsafe { mem::transmute(base) }
578     }
580     /// Create a new style sharing candidate cache.
582     // Forced out of line to limit stack frame sizes after extra inlining from
583     // https://github.com/rust-lang/rust/pull/43931
584     //
585     // See https://github.com/servo/servo/pull/18420#issuecomment-328769322
586     #[inline(never)]
587     pub fn new() -> Self {
588         assert_eq!(
589             mem::size_of::<SharingCache<E>>(),
590             mem::size_of::<TypelessSharingCache>()
591         );
592         assert_eq!(
593             mem::align_of::<SharingCache<E>>(),
594             mem::align_of::<TypelessSharingCache>()
595         );
596         let cache_arc = SHARING_CACHE_KEY.with(|c| Arc::clone(&*c));
597         let cache =
598             OwningHandle::new_with_fn(cache_arc, |x| unsafe { x.as_ref() }.unwrap().borrow_mut());
599         debug_assert!(cache.is_empty());
601         StyleSharingCache {
602             cache_typeless: cache,
603             marker: PhantomData,
604             dom_depth: 0,
605         }
606     }
608     /// Tries to insert an element in the style sharing cache.
609     ///
610     /// Fails if we know it should never be in the cache.
611     ///
612     /// NB: We pass a source for the validation data, rather than the data itself,
613     /// to avoid memmoving at each function call. See rust issue #42763.
614     pub fn insert_if_possible(
615         &mut self,
616         element: &E,
617         style: &PrimaryStyle,
618         validation_data_holder: Option<&mut StyleSharingTarget<E>>,
619         dom_depth: usize,
620         shared_context: &SharedStyleContext,
621     ) {
622         let parent = match element.traversal_parent() {
623             Some(element) => element,
624             None => {
625                 debug!("Failing to insert to the cache: no parent element");
626                 return;
627             },
628         };
630         if !element.matches_user_and_content_rules() {
631             debug!("Failing to insert into the cache: no tree rules:");
632             return;
633         }
635         // We can't share style across shadow hosts right now, because they may
636         // match different :host rules.
637         //
638         // TODO(emilio): We could share across the ones that don't have :host
639         // rules or have the same.
640         if element.shadow_root().is_some() {
641             debug!("Failing to insert into the cache: Shadow Host");
642             return;
643         }
645         // If the element has running animations, we can't share style.
646         //
647         // This is distinct from the specifies_{animations,transitions} check below,
648         // because:
649         //   * Animations can be triggered directly via the Web Animations API.
650         //   * Our computed style can still be affected by animations after we no
651         //     longer match any animation rules, since removing animations involves
652         //     a sequential task and an additional traversal.
653         if element.has_animations(shared_context) {
654             debug!("Failing to insert to the cache: running animations");
655             return;
656         }
658         // In addition to the above running animations check, we also need to
659         // check CSS animation and transition styles since it's possible that
660         // we are about to create CSS animations/transitions.
661         //
662         // These are things we don't check in the candidate match because they
663         // are either uncommon or expensive.
664         let ui_style = style.style().get_ui();
665         if ui_style.specifies_transitions() {
666             debug!("Failing to insert to the cache: transitions");
667             return;
668         }
670         if ui_style.specifies_animations() {
671             debug!("Failing to insert to the cache: animations");
672             return;
673         }
675         debug!(
676             "Inserting into cache: {:?} with parent {:?}",
677             element, parent
678         );
680         if self.dom_depth != dom_depth {
681             debug!(
682                 "Clearing cache because depth changed from {:?} to {:?}, element: {:?}",
683                 self.dom_depth, dom_depth, element
684             );
685             self.clear();
686             self.dom_depth = dom_depth;
687         }
688         self.cache_mut().insert(
689             *element,
690             validation_data_holder,
691         );
692     }
694     /// Clear the style sharing candidate cache.
695     pub fn clear(&mut self) {
696         self.cache_mut().clear();
697     }
699     /// Attempts to share a style with another node.
700     fn share_style_if_possible(
701         &mut self,
702         shared_context: &SharedStyleContext,
703         bloom_filter: &StyleBloom<E>,
704         selector_caches: &mut SelectorCaches,
705         target: &mut StyleSharingTarget<E>,
706     ) -> Option<ResolvedElementStyles> {
707         if shared_context.options.disable_style_sharing_cache {
708             debug!(
709                 "{:?} Cannot share style: style sharing cache disabled",
710                 target.element
711             );
712             return None;
713         }
715         if target.inheritance_parent().is_none() {
716             debug!(
717                 "{:?} Cannot share style: element has no parent",
718                 target.element
719             );
720             return None;
721         }
723         if !target.matches_user_and_content_rules() {
724             debug!("{:?} Cannot share style: content rules", target.element);
725             return None;
726         }
728         self.cache_mut().entries.lookup(|candidate| {
729             Self::test_candidate(
730                 target,
731                 candidate,
732                 &shared_context,
733                 bloom_filter,
734                 selector_caches,
735                 shared_context,
736             )
737         })
738     }
740     fn test_candidate(
741         target: &mut StyleSharingTarget<E>,
742         candidate: &mut StyleSharingCandidate<E>,
743         shared: &SharedStyleContext,
744         bloom: &StyleBloom<E>,
745         selector_caches: &mut SelectorCaches,
746         shared_context: &SharedStyleContext,
747     ) -> Option<ResolvedElementStyles> {
748         debug_assert!(target.matches_user_and_content_rules());
750         // Check that we have the same parent, or at least that the parents
751         // share styles and permit sharing across their children. The latter
752         // check allows us to share style between cousins if the parents
753         // shared style.
754         if !checks::parents_allow_sharing(target, candidate) {
755             trace!("Miss: Parent");
756             return None;
757         }
759         if target.local_name() != candidate.element.local_name() {
760             trace!("Miss: Local Name");
761             return None;
762         }
764         if target.namespace() != candidate.element.namespace() {
765             trace!("Miss: Namespace");
766             return None;
767         }
769         // We do not ignore visited state here, because Gecko needs to store
770         // extra bits on visited styles, so these contexts cannot be shared.
771         if target.element.state() != candidate.state() {
772             trace!("Miss: User and Author State");
773             return None;
774         }
776         if target.is_link() != candidate.element.is_link() {
777             trace!("Miss: Link");
778             return None;
779         }
781         // If two elements belong to different shadow trees, different rules may
782         // apply to them, from the respective trees.
783         if target.element.containing_shadow() != candidate.element.containing_shadow() {
784             trace!("Miss: Different containing shadow roots");
785             return None;
786         }
788         // If the elements are not assigned to the same slot they could match
789         // different ::slotted() rules in the slot scope.
790         //
791         // If two elements are assigned to different slots, even within the same
792         // shadow root, they could match different rules, due to the slot being
793         // assigned to yet another slot in another shadow root.
794         if target.element.assigned_slot() != candidate.element.assigned_slot() {
795             // TODO(emilio): We could have a look at whether the shadow roots
796             // actually have slotted rules and such.
797             trace!("Miss: Different assigned slots");
798             return None;
799         }
801         if target.element.shadow_root().is_some() {
802             trace!("Miss: Shadow host");
803             return None;
804         }
806         if target.element.has_animations(shared_context) {
807             trace!("Miss: Has Animations");
808             return None;
809         }
811         if target.matches_user_and_content_rules() !=
812             candidate.element.matches_user_and_content_rules()
813         {
814             trace!("Miss: User and Author Rules");
815             return None;
816         }
818         // It's possible that there are no styles for either id.
819         if checks::may_match_different_id_rules(shared, target.element, candidate.element) {
820             trace!("Miss: ID Attr");
821             return None;
822         }
824         if !checks::have_same_style_attribute(target, candidate) {
825             trace!("Miss: Style Attr");
826             return None;
827         }
829         if !checks::have_same_class(target, candidate) {
830             trace!("Miss: Class");
831             return None;
832         }
834         if !checks::have_same_presentational_hints(target, candidate) {
835             trace!("Miss: Pres Hints");
836             return None;
837         }
839         if !checks::have_same_parts(target, candidate) {
840             trace!("Miss: Shadow parts");
841             return None;
842         }
844         if !checks::revalidate(target, candidate, shared, bloom, selector_caches) {
845             trace!("Miss: Revalidation");
846             return None;
847         }
849         debug!(
850             "Sharing allowed between {:?} and {:?}",
851             target.element, candidate.element
852         );
853         Some(candidate.element.borrow_data().unwrap().share_styles())
854     }
856     /// Attempts to find an element in the cache with the given primary rule
857     /// node and parent.
858     ///
859     /// FIXME(emilio): re-measure this optimization, and remove if it's not very
860     /// useful... It's probably not worth the complexity / obscure bugs.
861     pub fn lookup_by_rules(
862         &mut self,
863         shared_context: &SharedStyleContext,
864         inherited: &ComputedValues,
865         rules: &StrongRuleNode,
866         visited_rules: Option<&StrongRuleNode>,
867         target: E,
868     ) -> Option<PrimaryStyle> {
869         if shared_context.options.disable_style_sharing_cache {
870             return None;
871         }
873         self.cache_mut().entries.lookup(|candidate| {
874             debug_assert_ne!(candidate.element, target);
875             if !candidate.parent_style_identity().eq(inherited) {
876                 return None;
877             }
878             let data = candidate.element.borrow_data().unwrap();
879             let style = data.styles.primary();
880             if style.rules.as_ref() != Some(&rules) {
881                 return None;
882             }
883             if style.visited_rules() != visited_rules {
884                 return None;
885             }
886             // NOTE(emilio): We only need to check name / namespace because we
887             // do name-dependent style adjustments, like the display: contents
888             // to display: none adjustment.
889             if target.namespace() != candidate.element.namespace() ||
890                 target.local_name() != candidate.element.local_name()
891             {
892                 return None;
893             }
894             // When using container units, inherited style + rules matched aren't enough to
895             // determine whether the style is the same. We could actually do a full container
896             // lookup but for now we just check that our actual traversal parent matches.
897             if data
898                 .styles
899                 .primary()
900                 .flags
901                 .intersects(ComputedValueFlags::USES_CONTAINER_UNITS) &&
902                 candidate.element.traversal_parent() != target.traversal_parent()
903             {
904                 return None;
905             }
906             // Rule nodes and styles are computed independent of the element's actual visitedness,
907             // but at the end of the cascade (in `adjust_for_visited`) we do store the
908             // RELEVANT_LINK_VISITED flag, so we can't share by rule node between visited and
909             // unvisited styles. We don't check for visitedness and just refuse to share for links
910             // entirely, so that visitedness doesn't affect timing.
911             if target.is_link() || candidate.element.is_link() {
912                 return None;
913             }
915             Some(data.share_primary_style())
916         })
917     }