gecko-dev/servo/components/style/sharing/checks.rs
Emilio Cobos Álvarez bfd5d2191a servo: Merge #19493 - style: Some stylo-chrome perf tweaks (from emilio:chrome-perf); r=xidorn,Manishearth
Source-Repo: https://github.com/servo/servo
Source-Revision: 8f30880d362ef54a2c6881c4927b96303ff763ef

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 680fd60a85bcdf4b28e86c397d2e39c02bead9cb
2017-12-05 06:05:25 -06:00

171 lines
5.3 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Different checks done during the style sharing process in order to determine
//! quickly whether it's worth to share style, and whether two different
//! elements can indeed share the same style.
use bloom::StyleBloom;
use context::{SelectorFlagsMap, SharedStyleContext};
use dom::TElement;
use selectors::NthIndexCache;
use sharing::{StyleSharingCandidate, StyleSharingTarget};
/// Determines whether a target and a candidate have compatible parents for
/// sharing.
pub fn parents_allow_sharing<E>(
target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>
) -> bool
where
E: TElement,
{
// If the identity of the parent style isn't equal, we can't share. We check
// this first, because the result is cached.
if target.parent_style_identity() != candidate.parent_style_identity() {
return false;
}
// Siblings can always share.
let parent = target.inheritance_parent().unwrap();
let candidate_parent = candidate.element.inheritance_parent().unwrap();
if parent == candidate_parent {
return true;
}
// Cousins are a bit more complicated.
//
// If a parent element was already styled and we traversed past it without
// restyling it, that may be because our clever invalidation logic was able
// to prove that the styles of that element would remain unchanged despite
// changes to the id or class attributes. However, style sharing relies in
// the strong guarantee that all the classes and ids up the respective parent
// chains are identical. As such, if we skipped styling for one (or both) of
// the parents on this traversal, we can't share styles across cousins.
//
// This is a somewhat conservative check. We could tighten it by having the
// invalidation logic explicitly flag elements for which it ellided styling.
let parent_data = parent.borrow_data().unwrap();
let candidate_parent_data = candidate_parent.borrow_data().unwrap();
if !parent_data.safe_for_cousin_sharing() || !candidate_parent_data.safe_for_cousin_sharing() {
return false;
}
true
}
/// Whether two elements have the same same style attribute (by pointer identity).
pub fn have_same_style_attribute<E>(
target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>
) -> bool
where
E: TElement,
{
match (target.style_attribute(), candidate.style_attribute()) {
(None, None) => true,
(Some(_), None) | (None, Some(_)) => false,
(Some(a), Some(b)) => &*a as *const _ == &*b as *const _
}
}
/// Whether two elements have the same same presentational attributes.
pub fn have_same_presentational_hints<E>(
target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>
) -> bool
where
E: TElement,
{
target.pres_hints() == candidate.pres_hints()
}
/// Whether a given element has the same class attribute than a given candidate.
///
/// We don't try to share style across elements with different class attributes.
pub fn have_same_class<E>(
target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>,
) -> bool
where
E: TElement,
{
target.class_list() == candidate.class_list()
}
/// Whether a given element and a candidate match the same set of "revalidation"
/// selectors.
///
/// Revalidation selectors are those that depend on the DOM structure, like
/// :first-child, etc, or on attributes that we don't check off-hand (pretty
/// much every attribute selector except `id` and `class`.
#[inline]
pub fn revalidate<E>(
target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>,
shared_context: &SharedStyleContext,
bloom: &StyleBloom<E>,
nth_index_cache: &mut NthIndexCache,
selector_flags_map: &mut SelectorFlagsMap<E>,
) -> bool
where
E: TElement,
{
let stylist = &shared_context.stylist;
let for_element = target.revalidation_match_results(
stylist,
bloom,
nth_index_cache,
selector_flags_map,
);
let for_candidate = candidate.revalidation_match_results(
stylist,
bloom,
nth_index_cache,
);
// This assert "ensures", to some extent, that the two candidates have
// matched the same rulehash buckets, and as such, that the bits we're
// comparing represent the same set of selectors.
debug_assert_eq!(for_element.len(), for_candidate.len());
for_element == for_candidate
}
/// Checks whether we might have rules for either of the two ids.
#[inline]
pub fn may_match_different_id_rules<E>(
shared_context: &SharedStyleContext,
element: E,
candidate: E,
) -> bool
where
E: TElement,
{
let element_id = element.get_id();
let candidate_id = candidate.get_id();
if element_id == candidate_id {
return false;
}
let stylist = &shared_context.stylist;
let may_have_rules_for_element = match element_id {
Some(ref id) => stylist.may_have_rules_for_id(id, element),
None => false
};
if may_have_rules_for_element {
return true;
}
match candidate_id {
Some(ref id) => stylist.may_have_rules_for_id(id, candidate),
None => false
}
}