diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 80f87eb85040..f8ddbc485a10 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -165,12 +165,26 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { transmute(node) } - fn children(self) -> LayoutIterator> { + fn parent_node(&self) -> Option { + unsafe { + self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn children(&self) -> LayoutIterator> { LayoutIterator(ServoChildrenIterator { current: self.first_child(), }) } + fn traversal_parent(&self) -> Option> { + self.parent_element() + } + + fn traversal_children(&self) -> LayoutIterator> { + self.children() + } + fn opaque(&self) -> OpaqueNode { unsafe { self.get_jsmanaged().opaque() } } @@ -199,12 +213,6 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { self.node.set_flag(CAN_BE_FRAGMENTED, value) } - fn parent_node(&self) -> Option> { - unsafe { - self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - fn is_in_doc(&self) -> bool { unsafe { (*self.node.unsafe_get()).is_in_doc() } } diff --git a/components/style/bloom.rs b/components/style/bloom.rs index 46df21261bc8..c63cf88a76e8 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -89,7 +89,7 @@ impl StyleBloom { pub fn push(&mut self, element: E) { if cfg!(debug_assertions) { if self.elements.is_empty() { - assert!(element.parent_element().is_none()); + assert!(element.traversal_parent().is_none()); } } self.push_internal(element); @@ -139,7 +139,7 @@ impl StyleBloom { pub fn rebuild(&mut self, mut element: E) { self.clear(); - while let Some(parent) = element.parent_element() { + while let Some(parent) = element.traversal_parent() { self.push_internal(parent); element = parent; } @@ -155,7 +155,7 @@ impl StyleBloom { pub fn assert_complete(&self, mut element: E) { if cfg!(debug_assertions) { let mut checked = 0; - while let Some(parent) = element.parent_element() { + while let Some(parent) = element.traversal_parent() { assert_eq!(parent, *self.elements[self.elements.len() - 1 - checked]); element = parent; checked += 1; @@ -190,7 +190,7 @@ impl StyleBloom { return; } - let parent_element = match element.parent_element() { + let traversal_parent = match element.traversal_parent() { Some(parent) => parent, None => { // Yay, another easy case. @@ -199,7 +199,7 @@ impl StyleBloom { } }; - if self.current_parent() == Some(parent_element) { + if self.current_parent() == Some(traversal_parent) { // Ta da, cache hit, we're all done. return; } @@ -232,8 +232,8 @@ impl StyleBloom { } // Now let's try to find a common parent in the bloom filter chain, - // starting with parent_element. - let mut common_parent = parent_element; + // starting with traversal_parent. + let mut common_parent = traversal_parent; let mut common_parent_depth = element_depth - 1; // Let's collect the parents we are going to need to insert once we've @@ -247,7 +247,7 @@ impl StyleBloom { // reverse the slice. parents_to_insert.push(common_parent); common_parent = - common_parent.parent_element().expect("We were lied"); + common_parent.traversal_parent().expect("We were lied to"); common_parent_depth -= 1; } @@ -269,7 +269,7 @@ impl StyleBloom { while **self.elements.last().unwrap() != common_parent { parents_to_insert.push(common_parent); self.pop().unwrap(); - common_parent = match common_parent.parent_element() { + common_parent = match common_parent.traversal_parent() { Some(parent) => parent, None => { debug_assert!(self.elements.is_empty()); diff --git a/components/style/dom.rs b/components/style/dom.rs index 1d9462a63351..c50a613d2534 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -108,17 +108,27 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo { /// Get a node back from an `UnsafeNode`. unsafe fn from_unsafe(n: &UnsafeNode) -> Self; - /// Returns an iterator over this node's children. - fn children(self) -> LayoutIterator; - - /// Converts self into an `OpaqueNode`. - fn opaque(&self) -> OpaqueNode; + /// Get this node's parent node. + fn parent_node(&self) -> Option; /// Get this node's parent element if present. fn parent_element(&self) -> Option { self.parent_node().and_then(|n| n.as_element()) } + /// Returns an iterator over this node's children. + fn children(&self) -> LayoutIterator; + + /// Get this node's parent element from the perspective of a restyle + /// traversal. + fn traversal_parent(&self) -> Option; + + /// Get this node's children from the perspective of a restyle traversal. + fn traversal_children(&self) -> LayoutIterator; + + /// Converts self into an `OpaqueNode`. + fn opaque(&self) -> OpaqueNode; + /// A debug id, only useful, mm... for debugging. fn debug_id(self) -> usize; @@ -138,9 +148,6 @@ pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo { /// Set whether this node can be fragmented. unsafe fn set_can_be_fragmented(&self, value: bool); - /// Get this node's parent node. - fn parent_node(&self) -> Option; - /// Whether this node is in the document right now needed to clear the /// restyle data appropriately on some forced restyles. fn is_in_doc(&self) -> bool; @@ -222,7 +229,7 @@ fn fmt_subtree(f: &mut fmt::Formatter, stringify: &F, n: N, indent: try!(write!(f, " ")); } try!(stringify(f, n)); - for kid in n.children() { + for kid in n.traversal_children() { try!(writeln!(f, "")); try!(fmt_subtree(f, stringify, kid, indent + 1)); } @@ -256,7 +263,7 @@ pub unsafe fn raw_note_descendants(element: E) -> bool break; } B::set(el); - curr = el.parent_element(); + curr = el.traversal_parent(); } // Note: We disable this assertion on servo because of bugs. See the @@ -297,7 +304,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + fn depth(&self) -> usize { let mut depth = 0; let mut curr = *self; - while let Some(parent) = curr.parent_element() { + while let Some(parent) = curr.traversal_parent() { depth += 1; curr = parent; } @@ -315,11 +322,18 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + } } + /// Get this node's parent element from the perspective of a restyle + /// traversal. + fn traversal_parent(&self) -> Option { + self.as_node().traversal_parent() + } + /// Returns the parent element we should inherit from. /// /// This is pretty much always the parent element itself, except in the case - /// of Gecko's Native Anonymous Content, which may need to find the closest - /// non-NAC ancestor. + /// of Gecko's Native Anonymous Content, which uses the traversal parent + /// (i.e. the flattened tree parent) and which also may need to find the + /// closest non-NAC ancestor. fn inheritance_parent(&self) -> Option { self.parent_element() } @@ -442,7 +456,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + let mut current = Some(*self); while let Some(el) = current { if !B::has(el) { return false; } - current = el.parent_element(); + current = el.traversal_parent(); } true diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 2964fd6dd5ec..70b36aff5334 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -204,6 +204,16 @@ impl<'ln> GeckoNode<'ln> { true } + fn flattened_tree_parent(&self) -> Option { + let fast_path = self.flattened_tree_parent_is_parent(); + debug_assert!(fast_path == unsafe { bindings::Gecko_FlattenedTreeParentIsParent(self.0) }); + if fast_path { + unsafe { self.0.mParent.as_ref().map(GeckoNode) } + } else { + unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) } + } + } + /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree. fn is_root_of_native_anonymous_subtree(&self) -> bool { use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT; @@ -240,12 +250,24 @@ impl<'ln> TNode for GeckoNode<'ln> { GeckoNode(&*(n.0 as *mut RawGeckoNode)) } - fn children(self) -> LayoutIterator> { + fn parent_node(&self) -> Option { + unsafe { self.0.mParent.as_ref().map(GeckoNode) } + } + + fn children(&self) -> LayoutIterator> { + LayoutIterator(self.dom_children()) + } + + fn traversal_parent(&self) -> Option> { + self.flattened_tree_parent().and_then(|n| n.as_element()) + } + + fn traversal_children(&self) -> LayoutIterator> { let maybe_iter = unsafe { Gecko_MaybeCreateStyleChildrenIterator(self.0) }; if let Some(iter) = maybe_iter.into_owned_opt() { LayoutIterator(GeckoChildrenIterator::GeckoIterator(iter)) } else { - LayoutIterator(GeckoChildrenIterator::Current(self.first_child())) + LayoutIterator(self.dom_children()) } } @@ -277,16 +299,6 @@ impl<'ln> TNode for GeckoNode<'ln> { // Maybe this isn’t useful for Gecko? } - fn parent_node(&self) -> Option { - let fast_path = self.flattened_tree_parent_is_parent(); - debug_assert!(fast_path == unsafe { bindings::Gecko_FlattenedTreeParentIsParent(self.0) }); - if fast_path { - unsafe { self.0.mParent.as_ref().map(GeckoNode) } - } else { - unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) } - } - } - fn is_in_doc(&self) -> bool { unsafe { bindings::Gecko_IsInDocument(self.0) } } @@ -644,9 +656,10 @@ impl<'le> TElement for GeckoElement<'le> { fn inheritance_parent(&self) -> Option { if self.is_native_anonymous() { - return self.closest_non_native_anonymous_ancestor(); + self.closest_non_native_anonymous_ancestor() + } else { + self.as_node().flattened_tree_parent().and_then(|n| n.as_element()) } - return self.parent_element(); } fn closest_non_native_anonymous_ancestor(&self) -> Option { diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs index a222b611d448..2991867cb3ba 100644 --- a/components/style/invalidation/stylesheets.rs +++ b/components/style/invalidation/stylesheets.rs @@ -166,7 +166,7 @@ impl StylesheetInvalidationSet { let mut any_children_invalid = false; - for child in element.as_node().children() { + for child in element.as_node().traversal_children() { let child = match child.as_element() { Some(e) => e, None => continue, diff --git a/components/style/matching.rs b/components/style/matching.rs index 19a731f138c5..c801c295aa3e 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -230,7 +230,7 @@ trait PrivateMatchMethods: TElement { fn layout_parent(&self) -> Self { let mut current = self.clone(); loop { - current = match current.parent_element() { + current = match current.traversal_parent() { Some(el) => el, None => return current, }; diff --git a/components/style/parallel.rs b/components/style/parallel.rs index 0af6eed469c1..ea39a0c15d71 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -64,7 +64,7 @@ pub fn traverse_dom(traversal: &D, // it on the context to make it available to the bottom-up phase. let depth = if token.traverse_unstyled_children_only() { debug_assert!(!D::needs_postorder_traversal()); - for kid in root.as_node().children() { + for kid in root.as_node().traversal_children() { if kid.as_element().map_or(false, |el| el.get_data().is_none()) { nodes.push(unsafe { SendNode::new(kid) }); } diff --git a/components/style/sequential.rs b/components/style/sequential.rs index 74c20d5bf01a..f2fa63c95f3f 100644 --- a/components/style/sequential.rs +++ b/components/style/sequential.rs @@ -32,7 +32,7 @@ pub fn traverse_dom(traversal: &D, let root_depth = root.depth(); if token.traverse_unstyled_children_only() { - for kid in root.as_node().children() { + for kid in root.as_node().traversal_children() { if kid.as_element().map_or(false, |el| el.get_data().is_none()) { discovered.push_back(WorkItem(kid, root_depth + 1)); } diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 7cced786444c..3c17f576156b 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -185,10 +185,10 @@ impl ValidationData { // filter, to avoid thrashing the filter. let bloom_to_use = if bloom_known_valid { debug_assert_eq!(bloom.current_parent(), - element.parent_element()); + element.traversal_parent()); Some(bloom.filter()) } else { - if bloom.current_parent() == element.parent_element() { + if bloom.current_parent() == element.traversal_parent() { Some(bloom.filter()) } else { None @@ -344,7 +344,7 @@ impl StyleSharingTarget { return StyleSharingResult::CannotShare; } debug_assert_eq!(bloom_filter.current_parent(), - self.element.parent_element()); + self.element.traversal_parent()); let result = cache .share_style_if_possible(shared_context, @@ -500,7 +500,7 @@ impl StyleSharingCandidateCache { dom_depth: usize) { use selectors::matching::AFFECTED_BY_PRESENTATIONAL_HINTS; - let parent = match element.parent_element() { + let parent = match element.traversal_parent() { Some(element) => element, None => { debug!("Failing to insert to the cache: no parent element"); @@ -572,7 +572,7 @@ impl StyleSharingCandidateCache { return StyleSharingResult::CannotShare } - if target.parent_element().is_none() { + if target.traversal_parent().is_none() { debug!("{:?} Cannot share style: element has no parent", target.element); return StyleSharingResult::CannotShare @@ -644,8 +644,8 @@ impl StyleSharingCandidateCache { // Check that we have the same parent, or at least the same pointer // identity for parent computed style. The latter check allows us to // share style between cousins if the parents shared style. - let parent = target.parent_element(); - let candidate_parent = candidate.element.parent_element(); + let parent = target.traversal_parent(); + let candidate_parent = candidate.element.traversal_parent(); if parent != candidate_parent && !checks::same_computed_values(parent, candidate_parent) { miss!(Parent) diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 484bce5faada..32a525d4b451 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -192,7 +192,7 @@ pub trait DomTraversal : Sync { if node.opaque() == root { break; } - let parent = node.parent_element().unwrap(); + let parent = node.traversal_parent().unwrap(); let remaining = parent.did_process_child(); if remaining != 0 { // The parent has other unprocessed descendants. We only @@ -308,7 +308,7 @@ pub trait DomTraversal : Sync { // But it may be that we no longer match, so detect that case // and act appropriately here. if el.is_native_anonymous() { - if let Some(parent) = el.parent_element() { + if let Some(parent) = el.traversal_parent() { let parent_data = parent.borrow_data().unwrap(); let going_to_reframe = parent_data.get_restyle().map_or(false, |r| { (r.damage | r.damage_handled()) @@ -467,7 +467,7 @@ pub trait DomTraversal : Sync { return; } - for kid in parent.as_node().children() { + for kid in parent.as_node().traversal_children() { if Self::node_needs_traversal(kid, self.shared_context().traversal_flags) { // If we are in a restyle for reconstruction, there is no need to // perform a post-traversal, so we don't need to set the dirty @@ -527,7 +527,7 @@ fn resolve_style_internal(context: &mut StyleContext, // If the Element isn't styled, we need to compute its style. if data.get_styles().is_none() { // Compute the parent style if necessary. - let parent = element.parent_element(); + let parent = element.traversal_parent(); if let Some(p) = parent { display_none_root = resolve_style_internal(context, p, ensure_data); } @@ -604,7 +604,7 @@ pub fn resolve_style(context: &mut StyleContext, element: E, break; } clear_data(curr); - curr = match curr.parent_element() { + curr = match curr.traversal_parent() { Some(parent) => parent, None => break, }; @@ -632,7 +632,7 @@ pub fn resolve_default_style(context: &mut StyleContext, let mut e = element; loop { old_data.push((e, set_data(e, None))); - match e.parent_element() { + match e.traversal_parent() { Some(parent) => e = parent, None => break, } @@ -862,8 +862,8 @@ fn preprocess_children(context: &mut StyleContext, { trace!("preprocess_children: {:?}", element); - // Loop over all the children. - for child in element.as_node().children() { + // Loop over all the traversal children. + for child in element.as_node().traversal_children() { // FIXME(bholley): Add TElement::element_children instead of this. let child = match child.as_element() { Some(el) => el, @@ -919,7 +919,7 @@ fn preprocess_children(context: &mut StyleContext, /// Clear style data for all the subtree under `el`. pub fn clear_descendant_data(el: E, clear_data: &F) { - for kid in el.as_node().children() { + for kid in el.as_node().traversal_children() { if let Some(kid) = kid.as_element() { // We maintain an invariant that, if an element has data, all its ancestors // have data as well. By consequence, any element without data has no diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index df404c1043e4..45399c255a36 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -198,7 +198,7 @@ fn traverse_subtree(element: GeckoElement, snapshots: &ServoElementSnapshotTable) { // When new content is inserted in a display:none subtree, we will call into // servo to try to style it. Detect that here and bail out. - if let Some(parent) = element.parent_element() { + if let Some(parent) = element.traversal_parent() { if parent.borrow_data().map_or(true, |d| d.styles().is_display_none()) { debug!("{:?} has unstyled parent {:?} - ignoring call to traverse_subtree", element, parent); return; @@ -2398,7 +2398,7 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut, } // Propagate the bit up the chain. - if let Some(p) = element.parent_element() { + if let Some(p) = element.traversal_parent() { if animation_only { p.note_descendants::(); } else { @@ -2735,7 +2735,7 @@ pub extern "C" fn Servo_AssertTreeIsClean(root: RawGeckoElementBorrowed) { let root = GeckoElement(root); fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) { debug_assert!(!el.has_dirty_descendants() && !el.has_animation_only_dirty_descendants()); - for child in el.as_node().children() { + for child in el.as_node().traversal_children() { if let Some(child) = child.as_element() { assert_subtree_is_clean(child); }