mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
style: Make style sharing look at XBL / Shadow DOM rules.
Bug: 1412251 Reviewed-by: bz MozReview-Commit-ID: II6lk6OmSZU
This commit is contained in:
parent
76a71996f4
commit
0df912be93
5 changed files with 148 additions and 47 deletions
|
@ -388,6 +388,15 @@ pub trait TElement
|
||||||
depth
|
depth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The style scope of this element is a node that represents which rules
|
||||||
|
/// apply to the element.
|
||||||
|
///
|
||||||
|
/// In Servo, where we don't know about Shadow DOM or XBL, the style scope
|
||||||
|
/// is always the document.
|
||||||
|
fn style_scope(&self) -> Self::ConcreteNode {
|
||||||
|
self.as_node().owner_doc().as_node()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get this node's parent element from the perspective of a restyle
|
/// Get this node's parent element from the perspective of a restyle
|
||||||
/// traversal.
|
/// traversal.
|
||||||
fn traversal_parent(&self) -> Option<Self> {
|
fn traversal_parent(&self) -> Option<Self> {
|
||||||
|
@ -738,14 +747,16 @@ pub trait TElement
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the rule hash target given an element.
|
/// Return the element which we can use to look up rules in the selector
|
||||||
|
/// maps.
|
||||||
|
///
|
||||||
|
/// This is always the element itself, except in the case where we are an
|
||||||
|
/// element-backed pseudo-element, in which case we return the originating
|
||||||
|
/// element.
|
||||||
fn rule_hash_target(&self) -> Self {
|
fn rule_hash_target(&self) -> Self {
|
||||||
let is_implemented_pseudo =
|
let is_implemented_pseudo =
|
||||||
self.implemented_pseudo_element().is_some();
|
self.implemented_pseudo_element().is_some();
|
||||||
|
|
||||||
// NB: This causes use to rule has pseudo selectors based on the
|
|
||||||
// properties of the originating element (which is fine, given the
|
|
||||||
// find_first_from_right usage).
|
|
||||||
if is_implemented_pseudo {
|
if is_implemented_pseudo {
|
||||||
self.closest_non_native_anonymous_ancestor().unwrap()
|
self.closest_non_native_anonymous_ancestor().unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -518,9 +518,14 @@ impl<'le> GeckoElement<'le> {
|
||||||
.and_then(|s| unsafe { s.mExtendedSlots.mPtr.as_ref() })
|
.and_then(|s| unsafe { s.mExtendedSlots.mPtr.as_ref() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn may_be_in_binding_manager(&self) -> bool {
|
||||||
|
self.flags() & (structs::NODE_MAY_BE_IN_BINDING_MNGR as u32) != 0
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_xbl_binding(&self) -> Option<GeckoXBLBinding<'le>> {
|
fn get_xbl_binding(&self) -> Option<GeckoXBLBinding<'le>> {
|
||||||
if self.flags() & (structs::NODE_MAY_BE_IN_BINDING_MNGR as u32) == 0 {
|
if !self.may_be_in_binding_manager() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,9 +558,11 @@ impl<'le> GeckoElement<'le> {
|
||||||
} else {
|
} else {
|
||||||
let binding_parent = unsafe {
|
let binding_parent = unsafe {
|
||||||
self.get_non_xul_xbl_binding_parent_raw_content().as_ref()
|
self.get_non_xul_xbl_binding_parent_raw_content().as_ref()
|
||||||
}.map(GeckoNode::from_content)
|
}.map(GeckoNode::from_content).and_then(|n| n.as_element());
|
||||||
.and_then(|n| n.as_element());
|
|
||||||
debug_assert!(binding_parent == unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) });
|
debug_assert!(binding_parent == unsafe {
|
||||||
|
bindings::Gecko_GetBindingParent(self.0).map(GeckoElement)
|
||||||
|
});
|
||||||
binding_parent
|
binding_parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -925,6 +932,28 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
self.get_before_or_after_pseudo(/* is_before = */ false)
|
self.get_before_or_after_pseudo(/* is_before = */ false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensure this accurately represents the rules that an element may ever
|
||||||
|
/// match, even in the native anonymous content case.
|
||||||
|
fn style_scope(&self) -> Self::ConcreteNode {
|
||||||
|
if self.implemented_pseudo_element().is_some() {
|
||||||
|
return self.closest_non_native_anonymous_ancestor().unwrap().style_scope();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_in_native_anonymous_subtree() {
|
||||||
|
return self.as_node().owner_doc().as_node();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.get_xbl_binding().is_some() {
|
||||||
|
return self.as_node();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parent) = self.get_xbl_binding_parent() {
|
||||||
|
return parent.as_node();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.as_node().owner_doc().as_node()
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute `f` for each anonymous content child element (apart from
|
/// Execute `f` for each anonymous content child element (apart from
|
||||||
/// ::before and ::after) whose originating element is `self`.
|
/// ::before and ::after) whose originating element is `self`.
|
||||||
fn each_anonymous_content_child<F>(&self, mut f: F)
|
fn each_anonymous_content_child<F>(&self, mut f: F)
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
//! quickly whether it's worth to share style, and whether two different
|
//! quickly whether it's worth to share style, and whether two different
|
||||||
//! elements can indeed share the same style.
|
//! elements can indeed share the same style.
|
||||||
|
|
||||||
use Atom;
|
|
||||||
use bloom::StyleBloom;
|
use bloom::StyleBloom;
|
||||||
use context::{SelectorFlagsMap, SharedStyleContext};
|
use context::{SelectorFlagsMap, SharedStyleContext};
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
|
@ -96,14 +95,16 @@ pub fn have_same_class<E>(target: &mut StyleSharingTarget<E>,
|
||||||
/// :first-child, etc, or on attributes that we don't check off-hand (pretty
|
/// :first-child, etc, or on attributes that we don't check off-hand (pretty
|
||||||
/// much every attribute selector except `id` and `class`.
|
/// much every attribute selector except `id` and `class`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn revalidate<E>(target: &mut StyleSharingTarget<E>,
|
pub fn revalidate<E>(
|
||||||
candidate: &mut StyleSharingCandidate<E>,
|
target: &mut StyleSharingTarget<E>,
|
||||||
shared_context: &SharedStyleContext,
|
candidate: &mut StyleSharingCandidate<E>,
|
||||||
bloom: &StyleBloom<E>,
|
shared_context: &SharedStyleContext,
|
||||||
nth_index_cache: &mut NthIndexCache,
|
bloom: &StyleBloom<E>,
|
||||||
selector_flags_map: &mut SelectorFlagsMap<E>)
|
nth_index_cache: &mut NthIndexCache,
|
||||||
-> bool
|
selector_flags_map: &mut SelectorFlagsMap<E>,
|
||||||
where E: TElement,
|
) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
{
|
{
|
||||||
let stylist = &shared_context.stylist;
|
let stylist = &shared_context.stylist;
|
||||||
|
|
||||||
|
@ -130,16 +131,25 @@ pub fn revalidate<E>(target: &mut StyleSharingTarget<E>,
|
||||||
|
|
||||||
/// Checks whether we might have rules for either of the two ids.
|
/// Checks whether we might have rules for either of the two ids.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn may_have_rules_for_ids(shared_context: &SharedStyleContext,
|
pub fn may_match_different_id_rules<E>(
|
||||||
element_id: Option<&Atom>,
|
shared_context: &SharedStyleContext,
|
||||||
candidate_id: Option<&Atom>) -> bool
|
element: E,
|
||||||
|
candidate: E,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
{
|
{
|
||||||
// We shouldn't be called unless the ids are different.
|
let element_id = element.get_id();
|
||||||
debug_assert!(element_id.is_some() || candidate_id.is_some());
|
let candidate_id = candidate.get_id();
|
||||||
|
|
||||||
|
if element_id == candidate_id {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let stylist = &shared_context.stylist;
|
let stylist = &shared_context.stylist;
|
||||||
|
|
||||||
let may_have_rules_for_element = match element_id {
|
let may_have_rules_for_element = match element_id {
|
||||||
Some(id) => stylist.may_have_rules_for_id(id),
|
Some(ref id) => stylist.may_have_rules_for_id(id, element),
|
||||||
None => false
|
None => false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,7 +158,7 @@ pub fn may_have_rules_for_ids(shared_context: &SharedStyleContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
match candidate_id {
|
match candidate_id {
|
||||||
Some(id) => stylist.may_have_rules_for_id(id),
|
Some(ref id) => stylist.may_have_rules_for_id(id, candidate),
|
||||||
None => false
|
None => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,10 +206,11 @@ impl ValidationData {
|
||||||
bloom: &StyleBloom<E>,
|
bloom: &StyleBloom<E>,
|
||||||
nth_index_cache: &mut NthIndexCache,
|
nth_index_cache: &mut NthIndexCache,
|
||||||
bloom_known_valid: bool,
|
bloom_known_valid: bool,
|
||||||
flags_setter: &mut F
|
flags_setter: &mut F,
|
||||||
) -> &SmallBitVec
|
) -> &SmallBitVec
|
||||||
where E: TElement,
|
where
|
||||||
F: FnMut(&E, ElementSelectorFlags),
|
E: TElement,
|
||||||
|
F: FnMut(&E, ElementSelectorFlags),
|
||||||
{
|
{
|
||||||
if self.revalidation_match_results.is_none() {
|
if self.revalidation_match_results.is_none() {
|
||||||
// The bloom filter may already be set up for our element.
|
// The bloom filter may already be set up for our element.
|
||||||
|
@ -230,10 +231,12 @@ impl ValidationData {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.revalidation_match_results =
|
self.revalidation_match_results =
|
||||||
Some(stylist.match_revalidation_selectors(&element,
|
Some(stylist.match_revalidation_selectors(
|
||||||
bloom_to_use,
|
element,
|
||||||
nth_index_cache,
|
bloom_to_use,
|
||||||
flags_setter));
|
nth_index_cache,
|
||||||
|
flags_setter,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.revalidation_match_results.as_ref().unwrap()
|
self.revalidation_match_results.as_ref().unwrap()
|
||||||
|
@ -661,6 +664,15 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that in the XBL case, we should be able to assert that the
|
||||||
|
// scopes are different, since two elements with different XBL bindings
|
||||||
|
// need to necessarily have different style (and thus children of them
|
||||||
|
// would never pass the parent check).
|
||||||
|
if target.element.style_scope() != candidate.element.style_scope() {
|
||||||
|
trace!("Miss: Different style scopes");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if *target.get_local_name() != *candidate.element.get_local_name() {
|
if *target.get_local_name() != *candidate.element.get_local_name() {
|
||||||
trace!("Miss: Local Name");
|
trace!("Miss: Local Name");
|
||||||
return None;
|
return None;
|
||||||
|
@ -690,15 +702,17 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let element_id = target.element.get_id();
|
// It's possible that there are no styles for either id.
|
||||||
let candidate_id = candidate.element.get_id();
|
let may_match_different_id_rules =
|
||||||
if element_id != candidate_id {
|
checks::may_match_different_id_rules(
|
||||||
// It's possible that there are no styles for either id.
|
shared,
|
||||||
if checks::may_have_rules_for_ids(shared, element_id.as_ref(),
|
target.element,
|
||||||
candidate_id.as_ref()) {
|
candidate.element,
|
||||||
trace!("Miss: ID Attr");
|
);
|
||||||
return None;
|
|
||||||
}
|
if may_match_different_id_rules {
|
||||||
|
trace!("Miss: ID Attr");
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !checks::have_same_style_attribute(target, candidate) {
|
if !checks::have_same_style_attribute(target, candidate) {
|
||||||
|
|
|
@ -1409,10 +1409,29 @@ impl Stylist {
|
||||||
/// Given an id, returns whether there might be any rules for that id in any
|
/// Given an id, returns whether there might be any rules for that id in any
|
||||||
/// of our rule maps.
|
/// of our rule maps.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn may_have_rules_for_id(&self, id: &Atom) -> bool {
|
pub fn may_have_rules_for_id<E>(
|
||||||
self.cascade_data
|
&self,
|
||||||
.iter_origins()
|
id: &Atom,
|
||||||
.any(|(d, _)| d.mapped_ids.might_contain_hash(id.get_hash()))
|
element: E,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
|
let hash = id.get_hash();
|
||||||
|
for (data, _) in self.cascade_data.iter_origins() {
|
||||||
|
if data.mapped_ids.might_contain_hash(hash) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut xbl_rules_may_contain = false;
|
||||||
|
|
||||||
|
element.each_xbl_stylist(|stylist| {
|
||||||
|
xbl_rules_may_contain = xbl_rules_may_contain ||
|
||||||
|
stylist.cascade_data.author.mapped_ids.might_contain_hash(hash)
|
||||||
|
});
|
||||||
|
|
||||||
|
xbl_rules_may_contain
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the registered `@keyframes` animation for the specified name.
|
/// Returns the registered `@keyframes` animation for the specified name.
|
||||||
|
@ -1428,7 +1447,7 @@ impl Stylist {
|
||||||
/// revalidation selectors.
|
/// revalidation selectors.
|
||||||
pub fn match_revalidation_selectors<E, F>(
|
pub fn match_revalidation_selectors<E, F>(
|
||||||
&self,
|
&self,
|
||||||
element: &E,
|
element: E,
|
||||||
bloom: Option<&BloomFilter>,
|
bloom: Option<&BloomFilter>,
|
||||||
nth_index_cache: &mut NthIndexCache,
|
nth_index_cache: &mut NthIndexCache,
|
||||||
flags_setter: &mut F
|
flags_setter: &mut F
|
||||||
|
@ -1454,14 +1473,14 @@ impl Stylist {
|
||||||
let mut results = SmallBitVec::new();
|
let mut results = SmallBitVec::new();
|
||||||
for (data, _) in self.cascade_data.iter_origins() {
|
for (data, _) in self.cascade_data.iter_origins() {
|
||||||
data.selectors_for_cache_revalidation.lookup(
|
data.selectors_for_cache_revalidation.lookup(
|
||||||
*element,
|
element,
|
||||||
self.quirks_mode,
|
self.quirks_mode,
|
||||||
&mut |selector_and_hashes| {
|
&mut |selector_and_hashes| {
|
||||||
results.push(matches_selector(
|
results.push(matches_selector(
|
||||||
&selector_and_hashes.selector,
|
&selector_and_hashes.selector,
|
||||||
selector_and_hashes.selector_offset,
|
selector_and_hashes.selector_offset,
|
||||||
Some(&selector_and_hashes.hashes),
|
Some(&selector_and_hashes.hashes),
|
||||||
element,
|
&element,
|
||||||
&mut matching_context,
|
&mut matching_context,
|
||||||
flags_setter
|
flags_setter
|
||||||
));
|
));
|
||||||
|
@ -1470,6 +1489,24 @@ impl Stylist {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
element.each_xbl_stylist(|stylist| {
|
||||||
|
stylist.cascade_data.author.selectors_for_cache_revalidation.lookup(
|
||||||
|
element,
|
||||||
|
stylist.quirks_mode,
|
||||||
|
&mut |selector_and_hashes| {
|
||||||
|
results.push(matches_selector(
|
||||||
|
&selector_and_hashes.selector,
|
||||||
|
selector_and_hashes.selector_offset,
|
||||||
|
Some(&selector_and_hashes.hashes),
|
||||||
|
&element,
|
||||||
|
&mut matching_context,
|
||||||
|
flags_setter
|
||||||
|
));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue