diff --git a/components/style/dom.rs b/components/style/dom.rs index 91e7324afd4..545463f4bd0 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -587,6 +587,14 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + 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 { + None + } + /// Gets declarations from XBL bindings from the element. Only gecko element could have this. fn get_declarations_from_xbl_bindings(&self, _pseudo_element: Option<&PseudoElement>, diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 9d17f442edf..355863d94ba 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -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_ClassOrClassList; use gecko_bindings::bindings::Gecko_ElementHasAnimations; -use gecko_bindings::bindings::Gecko_ElementHasBindingWithAnonymousContent; use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations; use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions; 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 { - 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 { @@ -376,6 +378,10 @@ impl<'lb> GeckoXBLBinding<'lb> { unsafe { self.0.mNextBinding.mRawPtr.as_ref().map(GeckoXBLBinding) } } + fn anon_content(&self) -> *const nsIContent { + unsafe { self.0.mContent.raw::() } + } + fn inherits_style(&self) -> bool { unsafe { bindings::Gecko_XBLBinding_InheritsStyle(self.0) } } @@ -1039,6 +1045,19 @@ impl<'le> TElement for GeckoElement<'le> { current.is_some() } + fn xbl_binding_anonymous_content(&self) -> Option> { + 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) -> HashMap> { use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt; @@ -1380,6 +1399,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { type Impl = SelectorImpl; fn parent_element(&self) -> Option { + // 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(); parent_node.and_then(|n| n.as_element()) } diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index efaa96cdbf4..12cb10f7172 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -274,6 +274,34 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E> &mut self, child: E, 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 { let mut child_data = child.mutate_data(); 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 sibling_invalidations = InvalidationVector::new(); + let mut invalidated_child = false; - let invalidated = child_invalidator.process_descendant_invalidations( - invalidations, - &mut invalidations_for_descendants, - &mut 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_child |= + child_invalidator.process_sibling_invalidations( + &mut invalidations_for_descendants, + sibling_invalidations, ); - 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, - invalidations: &InvalidationVector + invalidations: &InvalidationVector, ) -> bool { - let mut any_pseudo = 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 mut any_nac_root = false; let element = self.element; - element.each_anonymous_content_child(|pseudo| { - let invalidated = - self.invalidate_pseudo_element_or_nac(pseudo, 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; + element.each_anonymous_content_child(|nac| { + any_nac_root |= + self.invalidate_pseudo_element_or_nac(nac, invalidations); }); - 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 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 ? 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 @@ -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; - - // NB: DOM children! - 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 - ); + if let Some(anon_content) = self.element.xbl_binding_anonymous_content() { + any_descendant |= + self.invalidate_dom_descendants_of(anon_content, invalidations); } - 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() }; } - any_children + any_descendant } /// Process the given sibling invalidations coming from our previous