aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2017-10-28 01:48:08 +0200
committerEmilio Cobos Álvarez <emilio@crisal.io>2017-10-28 12:43:43 +0200
commit0df912be93848ec03fd43f19d922a33b60e162bb (patch)
tree27fc44451d49361c850ac5993c683a7d9a5f63ee
parent76a71996f4dc78997dc5d1bd0718d1970715732e (diff)
downloadservo-0df912be93848ec03fd43f19d922a33b60e162bb.tar.gz
servo-0df912be93848ec03fd43f19d922a33b60e162bb.zip
style: Make style sharing look at XBL / Shadow DOM rules.
Bug: 1412251 Reviewed-by: bz MozReview-Commit-ID: II6lk6OmSZU
-rw-r--r--components/style/dom.rs19
-rw-r--r--components/style/gecko/wrapper.rs37
-rw-r--r--components/style/sharing/checks.rs42
-rw-r--r--components/style/sharing/mod.rs46
-rw-r--r--components/style/stylist.rs51
5 files changed, 148 insertions, 47 deletions
diff --git a/components/style/dom.rs b/components/style/dom.rs
index c58433fe377..4f2ef48b1ae 100644
--- a/components/style/dom.rs
+++ b/components/style/dom.rs
@@ -388,6 +388,15 @@ pub trait TElement
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
/// traversal.
fn traversal_parent(&self) -> Option<Self> {
@@ -738,14 +747,16 @@ pub trait TElement
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 {
let is_implemented_pseudo =
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 {
self.closest_non_native_anonymous_ancestor().unwrap()
} else {
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index c10cb3875b9..7b76a355a71 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -519,8 +519,13 @@ impl<'le> GeckoElement<'le> {
}
#[inline]
+ fn may_be_in_binding_manager(&self) -> bool {
+ self.flags() & (structs::NODE_MAY_BE_IN_BINDING_MNGR as u32) != 0
+ }
+
+ #[inline]
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;
}
@@ -553,9 +558,11 @@ impl<'le> GeckoElement<'le> {
} else {
let binding_parent = unsafe {
self.get_non_xul_xbl_binding_parent_raw_content().as_ref()
- }.map(GeckoNode::from_content)
- .and_then(|n| n.as_element());
- debug_assert!(binding_parent == unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) });
+ }.map(GeckoNode::from_content).and_then(|n| n.as_element());
+
+ debug_assert!(binding_parent == unsafe {
+ bindings::Gecko_GetBindingParent(self.0).map(GeckoElement)
+ });
binding_parent
}
}
@@ -925,6 +932,28 @@ impl<'le> TElement for GeckoElement<'le> {
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
/// ::before and ::after) whose originating element is `self`.
fn each_anonymous_content_child<F>(&self, mut f: F)
diff --git a/components/style/sharing/checks.rs b/components/style/sharing/checks.rs
index 20020b9497b..9cba67ca0f3 100644
--- a/components/style/sharing/checks.rs
+++ b/components/style/sharing/checks.rs
@@ -6,7 +6,6 @@
//! quickly whether it's worth to share style, and whether two different
//! elements can indeed share the same style.
-use Atom;
use bloom::StyleBloom;
use context::{SelectorFlagsMap, SharedStyleContext};
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
/// 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,
+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;
@@ -130,16 +131,25 @@ pub fn revalidate<E>(target: &mut StyleSharingTarget<E>,
/// Checks whether we might have rules for either of the two ids.
#[inline]
-pub fn may_have_rules_for_ids(shared_context: &SharedStyleContext,
- element_id: Option<&Atom>,
- candidate_id: Option<&Atom>) -> bool
+pub fn may_match_different_id_rules<E>(
+ shared_context: &SharedStyleContext,
+ element: E,
+ candidate: E,
+) -> bool
+where
+ E: TElement,
{
- // We shouldn't be called unless the ids are different.
- debug_assert!(element_id.is_some() || candidate_id.is_some());
+ 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(id) => stylist.may_have_rules_for_id(id),
+ Some(ref id) => stylist.may_have_rules_for_id(id, element),
None => false
};
@@ -148,7 +158,7 @@ pub fn may_have_rules_for_ids(shared_context: &SharedStyleContext,
}
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
}
}
diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs
index e99da60f4b9..d48ddc94017 100644
--- a/components/style/sharing/mod.rs
+++ b/components/style/sharing/mod.rs
@@ -206,10 +206,11 @@ impl ValidationData {
bloom: &StyleBloom<E>,
nth_index_cache: &mut NthIndexCache,
bloom_known_valid: bool,
- flags_setter: &mut F
+ flags_setter: &mut F,
) -> &SmallBitVec
- where E: TElement,
- F: FnMut(&E, ElementSelectorFlags),
+ where
+ E: TElement,
+ F: FnMut(&E, ElementSelectorFlags),
{
if self.revalidation_match_results.is_none() {
// The bloom filter may already be set up for our element.
@@ -230,10 +231,12 @@ impl ValidationData {
}
};
self.revalidation_match_results =
- Some(stylist.match_revalidation_selectors(&element,
- bloom_to_use,
- nth_index_cache,
- flags_setter));
+ Some(stylist.match_revalidation_selectors(
+ element,
+ bloom_to_use,
+ nth_index_cache,
+ flags_setter,
+ ));
}
self.revalidation_match_results.as_ref().unwrap()
@@ -661,6 +664,15 @@ impl<E: TElement> StyleSharingCache<E> {
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() {
trace!("Miss: Local Name");
return None;
@@ -690,15 +702,17 @@ impl<E: TElement> StyleSharingCache<E> {
return None;
}
- let element_id = target.element.get_id();
- let candidate_id = candidate.element.get_id();
- if element_id != candidate_id {
- // It's possible that there are no styles for either id.
- if checks::may_have_rules_for_ids(shared, element_id.as_ref(),
- candidate_id.as_ref()) {
- trace!("Miss: ID Attr");
- return None;
- }
+ // It's possible that there are no styles for either id.
+ let may_match_different_id_rules =
+ checks::may_match_different_id_rules(
+ shared,
+ target.element,
+ candidate.element,
+ );
+
+ if may_match_different_id_rules {
+ trace!("Miss: ID Attr");
+ return None;
}
if !checks::have_same_style_attribute(target, candidate) {
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index f819d7f7da1..acdb19da98b 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -1409,10 +1409,29 @@ impl Stylist {
/// Given an id, returns whether there might be any rules for that id in any
/// of our rule maps.
#[inline]
- pub fn may_have_rules_for_id(&self, id: &Atom) -> bool {
- self.cascade_data
- .iter_origins()
- .any(|(d, _)| d.mapped_ids.might_contain_hash(id.get_hash()))
+ pub fn may_have_rules_for_id<E>(
+ &self,
+ id: &Atom,
+ 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.
@@ -1428,7 +1447,7 @@ impl Stylist {
/// revalidation selectors.
pub fn match_revalidation_selectors<E, F>(
&self,
- element: &E,
+ element: E,
bloom: Option<&BloomFilter>,
nth_index_cache: &mut NthIndexCache,
flags_setter: &mut F
@@ -1454,14 +1473,14 @@ impl Stylist {
let mut results = SmallBitVec::new();
for (data, _) in self.cascade_data.iter_origins() {
data.selectors_for_cache_revalidation.lookup(
- *element,
+ element,
self.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,
+ &element,
&mut matching_context,
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
}