checks.rs (6241B)
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/. */ 4 5 //! Different checks done during the style sharing process in order to determine 6 //! quickly whether it's worth to share style, and whether two different 7 //! elements can indeed share the same style. 8 9 use crate::bloom::StyleBloom; 10 use crate::context::SharedStyleContext; 11 use crate::dom::TElement; 12 use crate::sharing::{StyleSharingCandidate, StyleSharingTarget}; 13 use selectors::matching::SelectorCaches; 14 15 /// Determines whether a target and a candidate have compatible parents for 16 /// sharing. 17 pub fn parents_allow_sharing<E>( 18 target: &mut StyleSharingTarget<E>, 19 candidate: &mut StyleSharingCandidate<E>, 20 ) -> bool 21 where 22 E: TElement, 23 { 24 // If the identity of the parent style isn't equal, we can't share. We check 25 // this first, because the result is cached. 26 if target.parent_style_identity() != candidate.parent_style_identity() { 27 return false; 28 } 29 30 // Siblings can always share. 31 let parent = target.inheritance_parent().unwrap(); 32 let candidate_parent = candidate.element.inheritance_parent().unwrap(); 33 if parent == candidate_parent { 34 return true; 35 } 36 37 // If a parent element was already styled and we traversed past it without 38 // restyling it, that may be because our clever invalidation logic was able 39 // to prove that the styles of that element would remain unchanged despite 40 // changes to the id or class attributes. However, style sharing relies in 41 // the strong guarantee that all the classes and ids up the respective parent 42 // chains are identical. As such, if we skipped styling for one (or both) of 43 // the parents on this traversal, we can't share styles across cousins. 44 // 45 // This is a somewhat conservative check. We could tighten it by having the 46 // invalidation logic explicitly flag elements for which it ellided styling. 47 let parent_data = parent.borrow_data().unwrap(); 48 let candidate_parent_data = candidate_parent.borrow_data().unwrap(); 49 if !parent_data.safe_for_cousin_sharing() || !candidate_parent_data.safe_for_cousin_sharing() { 50 return false; 51 } 52 53 true 54 } 55 56 /// Whether two elements have the same style attribute. 57 /// 58 /// First checks pointer identity (fast path), then falls back to value comparison. 59 pub fn have_same_style_attribute<E>( 60 target: &mut StyleSharingTarget<E>, 61 candidate: &mut StyleSharingCandidate<E>, 62 shared_context: &SharedStyleContext, 63 ) -> bool 64 where 65 E: TElement, 66 { 67 match (target.style_attribute(), candidate.style_attribute()) { 68 (None, None) => true, 69 (Some(_), None) | (None, Some(_)) => false, 70 (Some(a), Some(b)) => { 71 if std::ptr::eq(&*a, &*b) { 72 return true; 73 } 74 let guard = shared_context.guards.author; 75 *a.read_with(guard) == *b.read_with(guard) 76 }, 77 } 78 } 79 80 /// Whether two elements have the same same presentational attributes. 81 pub fn have_same_presentational_hints<E>( 82 target: &mut StyleSharingTarget<E>, 83 candidate: &mut StyleSharingCandidate<E>, 84 ) -> bool 85 where 86 E: TElement, 87 { 88 target.pres_hints() == candidate.pres_hints() 89 } 90 91 /// Whether a given element has the same class attribute as a given candidate. 92 /// 93 /// We don't try to share style across elements with different class attributes. 94 pub fn have_same_class<E>( 95 target: &mut StyleSharingTarget<E>, 96 candidate: &mut StyleSharingCandidate<E>, 97 ) -> bool 98 where 99 E: TElement, 100 { 101 target.class_list() == candidate.class_list() 102 } 103 104 /// Whether a given element has the same part attribute as a given candidate. 105 /// 106 /// We don't try to share style across elements with different part attributes. 107 pub fn have_same_parts<E>( 108 target: &mut StyleSharingTarget<E>, 109 candidate: &mut StyleSharingCandidate<E>, 110 ) -> bool 111 where 112 E: TElement, 113 { 114 target.part_list() == candidate.part_list() 115 } 116 117 /// Whether a given element and a candidate match the same set of "revalidation" 118 /// selectors. 119 /// 120 /// Revalidation selectors are those that depend on the DOM structure, like 121 /// :first-child, etc, or on attributes that we don't check off-hand (pretty 122 /// much every attribute selector except `id` and `class`. 123 #[inline] 124 pub fn revalidate<E>( 125 target: &mut StyleSharingTarget<E>, 126 candidate: &mut StyleSharingCandidate<E>, 127 shared_context: &SharedStyleContext, 128 bloom: &StyleBloom<E>, 129 selector_caches: &mut SelectorCaches, 130 ) -> bool 131 where 132 E: TElement, 133 { 134 let stylist = &shared_context.stylist; 135 136 let for_element = target.revalidation_match_results(stylist, bloom, selector_caches); 137 138 let for_candidate = candidate.revalidation_match_results(stylist, bloom, selector_caches); 139 140 for_element == for_candidate 141 } 142 143 /// Whether a given element and a candidate share a set of scope activations 144 /// for revalidation. 145 #[inline] 146 pub fn revalidate_scope<E>( 147 target: &mut StyleSharingTarget<E>, 148 candidate: &mut StyleSharingCandidate<E>, 149 shared_context: &SharedStyleContext, 150 selector_caches: &mut SelectorCaches, 151 ) -> bool 152 where 153 E: TElement, 154 { 155 let stylist = &shared_context.stylist; 156 let for_element = target.scope_revalidation_results(stylist, selector_caches); 157 let for_candidate = candidate.scope_revalidation_results(stylist, selector_caches); 158 159 for_element == for_candidate 160 } 161 162 /// Checks whether we might have rules for either of the two ids. 163 #[inline] 164 pub fn may_match_different_id_rules<E>( 165 shared_context: &SharedStyleContext, 166 element: E, 167 candidate: E, 168 ) -> bool 169 where 170 E: TElement, 171 { 172 let element_id = element.id(); 173 let candidate_id = candidate.id(); 174 175 if element_id == candidate_id { 176 return false; 177 } 178 179 let stylist = &shared_context.stylist; 180 181 let may_have_rules_for_element = match element_id { 182 Some(id) => stylist.may_have_rules_for_id(id, element), 183 None => false, 184 }; 185 186 if may_have_rules_for_element { 187 return true; 188 } 189 190 match candidate_id { 191 Some(id) => stylist.may_have_rules_for_id(id, candidate), 192 None => false, 193 } 194 }