mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
style: Get restyle hints right in presence of XBL.
Bug: 1371130 Reviewed-By: heycam MozReview-Commit-ID: 56lMyXEYT1I
This commit is contained in:
parent
459b22a985
commit
ae5a6c9c69
3 changed files with 158 additions and 113 deletions
|
@ -587,6 +587,14 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
return data.restyle.hint.has_animation_hint()
|
return data.restyle.hint.has_animation_hint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the anonymous content for the current element's XBL binding,
|
||||||
|
/// given if any.
|
||||||
|
///
|
||||||
|
/// This is used in Gecko for XBL and shadow DOM.
|
||||||
|
fn xbl_binding_anonymous_content(&self) -> Option<Self::ConcreteNode> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets declarations from XBL bindings from the element. Only gecko element could have this.
|
/// Gets declarations from XBL bindings from the element. Only gecko element could have this.
|
||||||
fn get_declarations_from_xbl_bindings<V>(&self,
|
fn get_declarations_from_xbl_bindings<V>(&self,
|
||||||
_pseudo_element: Option<&PseudoElement>,
|
_pseudo_element: Option<&PseudoElement>,
|
||||||
|
|
|
@ -36,7 +36,6 @@ use gecko_bindings::bindings::{Gecko_IsRootElement, Gecko_MatchesElement, Gecko_
|
||||||
use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
|
use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
|
||||||
use gecko_bindings::bindings::Gecko_ClassOrClassList;
|
use gecko_bindings::bindings::Gecko_ClassOrClassList;
|
||||||
use gecko_bindings::bindings::Gecko_ElementHasAnimations;
|
use gecko_bindings::bindings::Gecko_ElementHasAnimations;
|
||||||
use gecko_bindings::bindings::Gecko_ElementHasBindingWithAnonymousContent;
|
|
||||||
use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
|
use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
|
||||||
use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
|
use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
|
||||||
use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
|
use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
|
||||||
|
@ -275,7 +274,10 @@ impl<'ln> TNode for GeckoNode<'ln> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children_and_traversal_children_might_differ(&self) -> bool {
|
fn children_and_traversal_children_might_differ(&self) -> bool {
|
||||||
self.as_element().map_or(false, |e| unsafe { Gecko_ElementHasBindingWithAnonymousContent(e.0) })
|
match self.as_element() {
|
||||||
|
Some(e) => e.xbl_binding_anonymous_content().is_some(),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opaque(&self) -> OpaqueNode {
|
fn opaque(&self) -> OpaqueNode {
|
||||||
|
@ -376,6 +378,10 @@ impl<'lb> GeckoXBLBinding<'lb> {
|
||||||
unsafe { self.0.mNextBinding.mRawPtr.as_ref().map(GeckoXBLBinding) }
|
unsafe { self.0.mNextBinding.mRawPtr.as_ref().map(GeckoXBLBinding) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn anon_content(&self) -> *const nsIContent {
|
||||||
|
unsafe { self.0.mContent.raw::<nsIContent>() }
|
||||||
|
}
|
||||||
|
|
||||||
fn inherits_style(&self) -> bool {
|
fn inherits_style(&self) -> bool {
|
||||||
unsafe { bindings::Gecko_XBLBinding_InheritsStyle(self.0) }
|
unsafe { bindings::Gecko_XBLBinding_InheritsStyle(self.0) }
|
||||||
}
|
}
|
||||||
|
@ -1039,6 +1045,19 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
current.is_some()
|
current.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn xbl_binding_anonymous_content(&self) -> Option<GeckoNode<'le>> {
|
||||||
|
if self.flags() & (structs::NODE_MAY_BE_IN_BINDING_MNGR as u32) == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let anon_content = match self.get_xbl_binding() {
|
||||||
|
Some(binding) => binding.anon_content(),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { anon_content.as_ref().map(GeckoNode::from_content) }
|
||||||
|
}
|
||||||
|
|
||||||
fn get_css_transitions_info(&self)
|
fn get_css_transitions_info(&self)
|
||||||
-> HashMap<TransitionProperty, Arc<AnimationValue>> {
|
-> HashMap<TransitionProperty, Arc<AnimationValue>> {
|
||||||
use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
|
use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
|
||||||
|
@ -1380,6 +1399,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
type Impl = SelectorImpl;
|
type Impl = SelectorImpl;
|
||||||
|
|
||||||
fn parent_element(&self) -> Option<Self> {
|
fn parent_element(&self) -> Option<Self> {
|
||||||
|
// FIXME(emilio): This will need to jump across if the parent node is a
|
||||||
|
// shadow root to get the shadow host.
|
||||||
let parent_node = self.as_node().parent_node();
|
let parent_node = self.as_node().parent_node();
|
||||||
parent_node.and_then(|n| n.as_element())
|
parent_node.and_then(|n| n.as_element())
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,6 +274,34 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
|
||||||
&mut self,
|
&mut self,
|
||||||
child: E,
|
child: E,
|
||||||
invalidations: &InvalidationVector
|
invalidations: &InvalidationVector
|
||||||
|
) -> bool {
|
||||||
|
let mut sibling_invalidations = InvalidationVector::new();
|
||||||
|
|
||||||
|
let result = self.invalidate_child(
|
||||||
|
child,
|
||||||
|
invalidations,
|
||||||
|
&mut sibling_invalidations
|
||||||
|
);
|
||||||
|
|
||||||
|
// Roots of NAC subtrees can indeed generate sibling invalidations, but
|
||||||
|
// they can be just ignored, since they have no siblings.
|
||||||
|
debug_assert!(child.implemented_pseudo_element().is_none() ||
|
||||||
|
sibling_invalidations.is_empty(),
|
||||||
|
"pseudos can't generate sibling invalidations, since \
|
||||||
|
using them in other position that isn't the \
|
||||||
|
rightmost part of the selector is invalid \
|
||||||
|
(for now at least)");
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invalidate a child and recurse down invalidating its descendants if
|
||||||
|
/// needed.
|
||||||
|
fn invalidate_child(
|
||||||
|
&mut self,
|
||||||
|
child: E,
|
||||||
|
invalidations: &InvalidationVector,
|
||||||
|
sibling_invalidations: &mut InvalidationVector,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut child_data = child.mutate_data();
|
let mut child_data = child.mutate_data();
|
||||||
let child_data = child_data.as_mut().map(|d| &mut **d);
|
let child_data = child_data.as_mut().map(|d| &mut **d);
|
||||||
|
@ -285,69 +313,93 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut invalidations_for_descendants = InvalidationVector::new();
|
let mut invalidations_for_descendants = InvalidationVector::new();
|
||||||
let mut sibling_invalidations = InvalidationVector::new();
|
let mut invalidated_child = false;
|
||||||
|
|
||||||
let invalidated = child_invalidator.process_descendant_invalidations(
|
invalidated_child |=
|
||||||
invalidations,
|
child_invalidator.process_sibling_invalidations(
|
||||||
&mut invalidations_for_descendants,
|
&mut invalidations_for_descendants,
|
||||||
&mut sibling_invalidations,
|
sibling_invalidations,
|
||||||
);
|
|
||||||
|
|
||||||
debug_assert!(child.implemented_pseudo_element().is_none() ||
|
|
||||||
sibling_invalidations.is_empty(),
|
|
||||||
"pseudos can't generate sibling invalidations, since \
|
|
||||||
using them in other position that isn't the \
|
|
||||||
rightmost part of the selector is invalid \
|
|
||||||
(for now at least)");
|
|
||||||
|
|
||||||
// For NAC roots, we can ignore sibling invalidations, since they don't
|
|
||||||
// have any siblings.
|
|
||||||
|
|
||||||
let invalidated_children =
|
|
||||||
child_invalidator.invalidate_descendants(
|
|
||||||
&invalidations_for_descendants
|
|
||||||
);
|
);
|
||||||
|
|
||||||
invalidated || invalidated_children
|
invalidated_child |=
|
||||||
|
child_invalidator.process_descendant_invalidations(
|
||||||
|
invalidations,
|
||||||
|
&mut invalidations_for_descendants,
|
||||||
|
sibling_invalidations,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The child may not be a flattened tree child of the current element,
|
||||||
|
// but may be arbitrarily deep.
|
||||||
|
//
|
||||||
|
// Since we keep the traversal flags in terms of the flattened tree,
|
||||||
|
// we need to propagate it as appropriate.
|
||||||
|
if invalidated_child && child.parent_element() != Some(self.element) {
|
||||||
|
let mut current = child.traversal_parent();
|
||||||
|
while let Some(parent) = current.take() {
|
||||||
|
if parent == self.element {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { parent.set_dirty_descendants() };
|
||||||
|
current = parent.traversal_parent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let invalidated_descendants = child_invalidator.invalidate_descendants(
|
||||||
|
&invalidations_for_descendants
|
||||||
|
);
|
||||||
|
|
||||||
|
invalidated_child || invalidated_descendants
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate_pseudo_elements_and_nac(
|
fn invalidate_nac(
|
||||||
&mut self,
|
&mut self,
|
||||||
invalidations: &InvalidationVector
|
invalidations: &InvalidationVector,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut any_pseudo = false;
|
let mut any_nac_root = false;
|
||||||
|
|
||||||
if let Some(before) = self.element.before_pseudo_element() {
|
|
||||||
any_pseudo |=
|
|
||||||
self.invalidate_pseudo_element_or_nac(before, invalidations);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(after) = self.element.after_pseudo_element() {
|
|
||||||
any_pseudo |=
|
|
||||||
self.invalidate_pseudo_element_or_nac(after, invalidations);
|
|
||||||
}
|
|
||||||
|
|
||||||
let element = self.element;
|
let element = self.element;
|
||||||
element.each_anonymous_content_child(|pseudo| {
|
element.each_anonymous_content_child(|nac| {
|
||||||
let invalidated =
|
any_nac_root |=
|
||||||
self.invalidate_pseudo_element_or_nac(pseudo, invalidations);
|
self.invalidate_pseudo_element_or_nac(nac, invalidations);
|
||||||
|
|
||||||
if invalidated {
|
|
||||||
let mut current = pseudo.traversal_parent();
|
|
||||||
while let Some(parent) = current.take() {
|
|
||||||
if parent == self.element {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { parent.set_dirty_descendants() };
|
|
||||||
current = parent.traversal_parent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
any_pseudo |= invalidated;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
any_pseudo
|
any_nac_root
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: It's important that this operates on DOM children, which is what
|
||||||
|
// selector-matching operates on.
|
||||||
|
fn invalidate_dom_descendants_of(
|
||||||
|
&mut self,
|
||||||
|
parent: E::ConcreteNode,
|
||||||
|
invalidations: &InvalidationVector,
|
||||||
|
) -> bool {
|
||||||
|
let mut any_descendant = false;
|
||||||
|
|
||||||
|
let mut sibling_invalidations = InvalidationVector::new();
|
||||||
|
for child in parent.children() {
|
||||||
|
// TODO(emilio): We handle <xbl:children> fine, because they appear
|
||||||
|
// in selector-matching (note bug 1374247, though).
|
||||||
|
//
|
||||||
|
// This probably needs a shadow root check on `child` here, and
|
||||||
|
// recursing if that's the case.
|
||||||
|
//
|
||||||
|
// Also, what's the deal with HTML <content>? We don't need to
|
||||||
|
// support that for now, though we probably need to recurse into the
|
||||||
|
// distributed children too.
|
||||||
|
let child = match child.as_element() {
|
||||||
|
Some(e) => e,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
any_descendant |= self.invalidate_child(
|
||||||
|
child,
|
||||||
|
invalidations,
|
||||||
|
&mut sibling_invalidations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
any_descendant
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a descendant invalidation list, go through the current element's
|
/// Given a descendant invalidation list, go through the current element's
|
||||||
|
@ -373,72 +425,36 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sibling_invalidations = InvalidationVector::new();
|
let mut any_descendant = false;
|
||||||
|
|
||||||
let mut any_children = false;
|
if let Some(anon_content) = self.element.xbl_binding_anonymous_content() {
|
||||||
|
any_descendant |=
|
||||||
// NB: DOM children!
|
self.invalidate_dom_descendants_of(anon_content, invalidations);
|
||||||
for child in self.element.as_node().children() {
|
|
||||||
let child = match child.as_element() {
|
|
||||||
Some(e) => e,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut child_data = child.mutate_data();
|
|
||||||
let child_data = child_data.as_mut().map(|d| &mut **d);
|
|
||||||
|
|
||||||
let mut child_invalidator = TreeStyleInvalidator::new(
|
|
||||||
child,
|
|
||||||
child_data,
|
|
||||||
self.shared_context
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut invalidations_for_descendants = InvalidationVector::new();
|
|
||||||
let mut invalidated_child = false;
|
|
||||||
|
|
||||||
invalidated_child |=
|
|
||||||
child_invalidator.process_sibling_invalidations(
|
|
||||||
&mut invalidations_for_descendants,
|
|
||||||
&mut sibling_invalidations,
|
|
||||||
);
|
|
||||||
|
|
||||||
invalidated_child |=
|
|
||||||
child_invalidator.process_descendant_invalidations(
|
|
||||||
invalidations,
|
|
||||||
&mut invalidations_for_descendants,
|
|
||||||
&mut sibling_invalidations,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The child may not be a flattened tree child of the current
|
|
||||||
// element, but may be arbitrarily deep.
|
|
||||||
//
|
|
||||||
// Since we keep the traversal flags in terms of the flattened tree,
|
|
||||||
// we need to propagate it as appropriate.
|
|
||||||
if invalidated_child {
|
|
||||||
let mut current = child.traversal_parent();
|
|
||||||
while let Some(parent) = current.take() {
|
|
||||||
if parent == self.element {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { parent.set_dirty_descendants() };
|
|
||||||
current = parent.traversal_parent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
any_children |= invalidated_child;
|
|
||||||
any_children |= child_invalidator.invalidate_descendants(
|
|
||||||
&invalidations_for_descendants
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
any_children |= self.invalidate_pseudo_elements_and_nac(invalidations);
|
// TODO(emilio): Having a list of invalidations just for pseudo-elements
|
||||||
|
// may save some work here and there.
|
||||||
|
if let Some(before) = self.element.before_pseudo_element() {
|
||||||
|
any_descendant |=
|
||||||
|
self.invalidate_pseudo_element_or_nac(before, invalidations);
|
||||||
|
}
|
||||||
|
|
||||||
if any_children {
|
let node = self.element.as_node();
|
||||||
|
any_descendant |=
|
||||||
|
self.invalidate_dom_descendants_of(node, invalidations);
|
||||||
|
|
||||||
|
if let Some(after) = self.element.after_pseudo_element() {
|
||||||
|
any_descendant |=
|
||||||
|
self.invalidate_pseudo_element_or_nac(after, invalidations);
|
||||||
|
}
|
||||||
|
|
||||||
|
any_descendant |= self.invalidate_nac(invalidations);
|
||||||
|
|
||||||
|
if any_descendant {
|
||||||
unsafe { self.element.set_dirty_descendants() };
|
unsafe { self.element.set_dirty_descendants() };
|
||||||
}
|
}
|
||||||
|
|
||||||
any_children
|
any_descendant
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process the given sibling invalidations coming from our previous
|
/// Process the given sibling invalidations coming from our previous
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue