diff options
-rw-r--r-- | components/selectors/parser.rs | 26 | ||||
-rw-r--r-- | components/style/bloom.rs | 17 | ||||
-rw-r--r-- | components/style/dom.rs | 5 | ||||
-rw-r--r-- | components/style/gecko/selector_parser.rs | 5 | ||||
-rw-r--r-- | components/style/gecko/wrapper.rs | 51 |
5 files changed, 96 insertions, 8 deletions
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index e99b1b18412..73c1239035c 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -212,6 +212,12 @@ macro_rules! with_all_bounds { /// pseudo-elements type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>; + + /// Whether attribute hashes should be collected for filtering + /// purposes. + fn should_collect_attr_hash(_name: &Self::LocalName) -> bool { + false + } } } } @@ -482,6 +488,26 @@ where Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => { class.precomputed_hash() }, + Component::AttributeInNoNamespace { ref local_name, .. } if Impl::should_collect_attr_hash(local_name) => { + // AttributeInNoNamespace is only used when local_name == + // local_name_lower. + local_name.precomputed_hash() + }, + Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower, .. } => { + // Only insert the local-name into the filter if it's all + // lowercase. Otherwise we would need to test both hashes, and + // our data structures aren't really set up for that. + if local_name != local_name_lower || !Impl::should_collect_attr_hash(local_name) { + continue; + } + local_name.precomputed_hash() + }, + Component::AttributeOther(ref selector) => { + if selector.local_name != selector.local_name_lower || !Impl::should_collect_attr_hash(&selector.local_name) { + continue; + } + selector.local_name.precomputed_hash() + }, Component::Is(ref list) | Component::Where(ref list) => { // :where and :is OR their selectors, so we can't put any hash // in the filter if there's more than one selector, as that'd diff --git a/components/style/bloom.rs b/components/style/bloom.rs index c17b31d1bee..d75abaa4f93 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -102,6 +102,15 @@ impl<E: TElement> PushedElement<E> { } } +/// Returns whether the attribute name is excluded from the bloom filter. +/// +/// We do this for attributes that are very common but not commonly used in +/// selectors. +#[inline] +pub fn is_attr_name_excluded_from_filter(atom: &crate::Atom) -> bool { + *atom == atom!("class") || *atom == atom!("id") || *atom == atom!("style") +} + fn each_relevant_element_hash<E, F>(element: E, mut f: F) where E: TElement, @@ -115,6 +124,14 @@ where } element.each_class(|class| f(class.get_hash())); + + if static_prefs::pref!("layout.css.bloom-filter-attribute-names.enabled") { + element.each_attr_name(|name| { + if !is_attr_name_excluded_from_filter(name) { + f(name.get_hash()) + } + }); + } } impl<E: TElement> Drop for StyleBloom<E> { diff --git a/components/style/dom.rs b/components/style/dom.rs index c23b985a628..f16e45ce681 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -519,6 +519,11 @@ pub trait TElement: { } + /// Internal iterator for the attribute names of this element. + fn each_attr_name<F>(&self, callback: F) + where + F: FnMut(&AtomIdent); + /// Internal iterator for the part names that this element exports for a /// given part name. fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F) diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index a9083581234..b2fe46a9a2c 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -247,6 +247,11 @@ impl ::selectors::SelectorImpl for SelectorImpl { type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; + + fn should_collect_attr_hash(name: &AtomIdent) -> bool { + static_prefs::pref!("layout.css.bloom-filter-attribute-names.enabled") && + !crate::bloom::is_attr_name_excluded_from_filter(name) + } } impl<'a> SelectorParser<'a> { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 7d4b4e03f73..3f23981a823 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -570,7 +570,7 @@ impl<'le> GeckoElement<'le> { } #[inline(always)] - fn attrs(&self) -> &[structs::AttrArray_InternalAttr] { + fn non_mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] { unsafe { let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() { Some(attrs) => attrs, @@ -582,11 +582,28 @@ impl<'le> GeckoElement<'le> { } #[inline(always)] + fn mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] { + unsafe { + let attrs = match self.0.mAttrs.mImpl.mPtr.as_ref() { + Some(attrs) => attrs, + None => return &[], + }; + + let attrs = match attrs.mMappedAttrs.as_ref() { + Some(attrs) => attrs, + None => return &[], + }; + + attrs.mBuffer.as_slice(attrs.mAttrCount as usize) + } + } + + #[inline(always)] fn get_part_attr(&self) -> Option<&structs::nsAttrValue> { if !self.has_part_attr() { return None; } - snapshot_helpers::find_attr(self.attrs(), &atom!("part")) + snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("part")) } #[inline(always)] @@ -602,7 +619,7 @@ impl<'le> GeckoElement<'le> { } } - snapshot_helpers::find_attr(self.attrs(), &atom!("class")) + snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class")) } #[inline] @@ -1167,7 +1184,7 @@ impl<'le> TElement for GeckoElement<'le> { #[inline] fn exports_any_part(&self) -> bool { - snapshot_helpers::find_attr(self.attrs(), &atom!("exportparts")).is_some() + snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("exportparts")).is_some() } // FIXME(emilio): we should probably just return a reference to the Atom. @@ -1177,7 +1194,25 @@ impl<'le> TElement for GeckoElement<'le> { return None; } - snapshot_helpers::get_id(self.attrs()) + snapshot_helpers::get_id(self.non_mapped_attrs()) + } + + fn each_attr_name<F>(&self, mut callback: F) + where + F: FnMut(&AtomIdent), + { + for attr in self.non_mapped_attrs().iter().chain(self.mapped_attrs().iter()) { + let is_nodeinfo = attr.mName.mBits & 1 != 0; + unsafe { + let atom = if is_nodeinfo { + let node_info = &*((attr.mName.mBits & !1) as *const structs::NodeInfo); + node_info.mInner.mName + } else { + attr.mName.mBits as *const nsAtom + }; + AtomIdent::with(atom, |a| callback(a)) + } + } } fn each_class<F>(&self, callback: F) @@ -1197,7 +1232,7 @@ impl<'le> TElement for GeckoElement<'le> { where F: FnMut(&AtomIdent), { - snapshot_helpers::each_exported_part(self.attrs(), name, callback) + snapshot_helpers::each_exported_part(self.non_mapped_attrs(), name, callback) } fn each_part<F>(&self, callback: F) @@ -2058,7 +2093,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { return false; } - let element_id = match snapshot_helpers::get_id(self.attrs()) { + let element_id = match snapshot_helpers::get_id(self.non_mapped_attrs()) { Some(id) => id, None => return false, }; @@ -2078,7 +2113,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { #[inline] fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> { - snapshot_helpers::imported_part(self.attrs(), name) + snapshot_helpers::imported_part(self.non_mapped_attrs(), name) } #[inline(always)] |