stylo: Fix pseudo element matching for XBL stylesheets (
Bug 1371577)
MozReview-Commit-ID: GzWbztqW0V1
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -568,17 +568,18 @@ pub trait TElement : Eq + PartialEq + De
None => return false,
};
return data.get_restyle()
.map_or(false, |r| r.hint.has_animation_hint());
}
/// Gets declarations from XBL bindings from the element. Only gecko element could have this.
fn get_declarations_from_xbl_bindings<V>(&self,
- _: &mut V)
+ _pseudo_element: Option<&PseudoElement>,
+ _applicable_declarations: &mut V)
-> bool
where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
false
}
/// Gets the current existing CSS transitions, by |property, end value| pairs in a HashMap.
#[cfg(feature = "gecko")]
fn get_css_transitions_info(&self)
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -377,27 +377,29 @@ impl<'lb> GeckoXBLBinding<'lb> {
fn inherits_style(&self) -> bool {
unsafe { bindings::Gecko_XBLBinding_InheritsStyle(self.0) }
}
// Implements Gecko's nsXBLBinding::WalkRules().
fn get_declarations_for<E, V>(&self,
element: &E,
+ pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V)
where E: TElement,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
if let Some(base_binding) = self.base_binding() {
- base_binding.get_declarations_for(element, applicable_declarations);
+ base_binding.get_declarations_for(element, pseudo_element, applicable_declarations);
}
let raw_data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyleSet(self.0) };
if let Some(raw_data) = raw_data {
let data = PerDocumentStyleData::from_ffi(&*raw_data).borrow();
data.stylist.push_applicable_declarations_as_xbl_only_stylist(element,
+ pseudo_element,
applicable_declarations);
}
}
}
/// A simple wrapper over a non-null Gecko `Element` pointer.
#[derive(Clone, Copy)]
pub struct GeckoElement<'le>(pub &'le RawGeckoElement);
@@ -937,26 +939,29 @@ impl<'le> TElement for GeckoElement<'le>
fn has_css_transitions(&self) -> bool {
self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
}
// Implements Gecko's nsBindingManager::WalkRules(). Returns whether to cut off the
// inheritance.
fn get_declarations_from_xbl_bindings<V>(&self,
+ pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V)
-> bool
where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
// Walk the binding scope chain, starting with the binding attached to our content, up
// till we run out of scopes or we get cut off.
let mut current = Some(*self);
while let Some(element) = current {
if let Some(binding) = element.get_xbl_binding() {
- binding.get_declarations_for(self, applicable_declarations);
+ binding.get_declarations_for(self,
+ pseudo_element,
+ applicable_declarations);
// If we're not looking at our original element, allow the binding to cut off
// style inheritance.
if element != *self {
if !binding.inherits_style() {
// Go no further; we're not inheriting style from anything above here.
break;
}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -928,35 +928,77 @@ impl Stylist {
// during multiple layout passes, but this is totally bogus, in the
// sense that it's updated asynchronously.
//
// This should probably be an argument to `update`, and use the quirks
// mode info in the `SharedLayoutContext`.
self.quirks_mode = quirks_mode;
}
+ /// Returns the correspond PerPseudoElementSelectorMap given PseudoElement.
+ fn get_element_map(&self,
+ pseudo_element: Option<&PseudoElement>) -> &PerPseudoElementSelectorMap
+ {
+ debug_assert!(pseudo_element.is_none() ||
+ self.pseudos_map.get(pseudo_element.unwrap()).is_some());
+ match pseudo_element {
+ Some(pseudo) => self.pseudos_map.get(pseudo).unwrap(),
+ None => &self.element_map,
+ }
+ }
+
+ /// Returns the rule hash target given an element.
+ fn rule_hash_target<E>(&self,
+ element: &E) -> E
+ where E: TElement {
+ let is_implemented_pseudo =
+ element.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).
+ let rule_hash_target = if is_implemented_pseudo {
+ element.closest_non_native_anonymous_ancestor().unwrap()
+ } else {
+ *element
+ };
+
+ rule_hash_target
+ }
+
/// Returns the applicable CSS declarations for the given element by
/// treating us as an XBL stylesheet-only stylist.
pub fn push_applicable_declarations_as_xbl_only_stylist<E, V>(&self,
element: &E,
+ pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V)
where E: TElement,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
{
let mut matching_context =
MatchingContext::new(MatchingMode::Normal, None, self.quirks_mode);
let mut dummy_flag_setter = |_: &E, _: ElementSelectorFlags| {};
- self.element_map.author.get_all_matching_rules(element,
- element,
- applicable_declarations,
- &mut matching_context,
- self.quirks_mode,
- &mut dummy_flag_setter,
- CascadeLevel::XBL);
+ // Bail out if we don't have the selector map of the pseudo element.
+ if pseudo_element.is_some() && self.pseudos_map.get(pseudo_element.unwrap()).is_none() {
+ return;
+ }
+
+ let map = self.get_element_map(pseudo_element);
+ let rule_hash_target = self.rule_hash_target(element);
+
+ // nsXBLPrototypeResources::ComputeServoStyleSet() added XBL stylesheets under author
+ // (doc) level.
+ map.author.get_all_matching_rules(element,
+ &rule_hash_target,
+ applicable_declarations,
+ &mut matching_context,
+ self.quirks_mode,
+ &mut dummy_flag_setter,
+ CascadeLevel::XBL);
}
/// Returns the applicable CSS declarations for the given element.
///
/// This corresponds to `ElementRuleCollector` in WebKit.
///
/// The `StyleRelations` recorded in `MatchingContext` indicate hints about
/// which kind of rules have matched.
@@ -978,32 +1020,18 @@ impl Stylist {
debug_assert!(!self.is_device_dirty);
// Gecko definitely has pseudo-elements with style attributes, like
// ::-moz-color-swatch.
debug_assert!(cfg!(feature = "gecko") ||
style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.map_or(true, |p| !p.is_precomputed()));
- let map = match pseudo_element {
- Some(pseudo) => self.pseudos_map.get(pseudo).unwrap(),
- None => &self.element_map,
- };
-
- let is_implemented_pseudo =
- element.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).
- let rule_hash_target = if is_implemented_pseudo {
- element.closest_non_native_anonymous_ancestor().unwrap()
- } else {
- *element
- };
+ let map = self.get_element_map(pseudo_element);
+ let rule_hash_target = self.rule_hash_target(element);
debug!("Determining if style is shareable: pseudo: {}",
pseudo_element.is_some());
let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
// Step 1: Normal user-agent rules.
map.user_agent.get_all_matching_rules(element,
@@ -1055,17 +1083,18 @@ impl Stylist {
CascadeLevel::UserNormal);
debug!("user normal: {:?}", context.relations);
} else {
debug!("skipping user rules");
}
// Step 3b: XBL rules.
let cut_off_inheritance =
- rule_hash_target.get_declarations_from_xbl_bindings(applicable_declarations);
+ rule_hash_target.get_declarations_from_xbl_bindings(pseudo_element,
+ applicable_declarations);
debug!("XBL: {:?}", context.relations);
if rule_hash_target.matches_user_and_author_rules() && !only_default_rules {
// Gecko skips author normal rules if cutting off inheritance.
// See nsStyleSet::FileRules().
if !cut_off_inheritance {
// Step 3c: Author normal rules.
map.author.get_all_matching_rules(element,