diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs index 4afd9a77815..fc5e7aadb14 100644 --- a/components/layout/css/matching.rs +++ b/components/layout/css/matching.rs @@ -31,7 +31,7 @@ use util::arc_ptr_eq; use util::cache::{LRUCache, SimpleHashCache}; use util::opts; use util::vec::ForgetfulSink; -use wrapper::{LayoutElement, LayoutNode}; +use wrapper::{LayoutElement, LayoutNode, ServoLayoutElement, ServoLayoutNode}; pub struct ApplicableDeclarations { pub normal: SmallVec<[DeclarationBlock; 16]>, @@ -160,7 +160,7 @@ pub struct StyleSharingCandidateCache { cache: LRUCache, } -fn create_common_style_affecting_attributes_from_element(element: &LayoutElement) +fn create_common_style_affecting_attributes_from_element(element: &ServoLayoutElement) -> CommonStyleAffectingAttributes { let mut flags = CommonStyleAffectingAttributes::empty(); for attribute_info in &common_style_affecting_attributes() { @@ -211,7 +211,7 @@ impl StyleSharingCandidate { /// Attempts to create a style sharing candidate from this node. Returns /// the style sharing candidate or `None` if this node is ineligible for /// style sharing. - fn new(element: &LayoutElement) -> Option { + fn new(element: &ServoLayoutElement) -> Option { let parent_element = match element.parent_element() { None => return None, Some(parent_element) => parent_element, @@ -257,7 +257,7 @@ impl StyleSharingCandidate { }) } - fn can_share_style_with(&self, element: &LayoutElement) -> bool { + fn can_share_style_with(&self, element: &ServoLayoutElement) -> bool { if *element.get_local_name() != self.local_name { return false } @@ -342,7 +342,7 @@ impl StyleSharingCandidateCache { self.cache.iter() } - pub fn insert_if_possible(&mut self, element: &LayoutElement) { + pub fn insert_if_possible(&mut self, element: &ServoLayoutElement) { match StyleSharingCandidate::new(element) { None => {} Some(candidate) => self.cache.insert(candidate, ()) @@ -376,7 +376,7 @@ pub trait ElementMatchMethods { unsafe fn share_style_if_possible(&self, style_sharing_candidate_cache: &mut StyleSharingCandidateCache, - parent: Option) + parent: Option) -> StyleSharingResult; } @@ -396,7 +396,7 @@ pub trait MatchMethods { unsafe fn cascade_node(&self, layout_context: &SharedLayoutContext, - parent: Option, + parent: Option, applicable_declarations: &ApplicableDeclarations, applicable_declarations_cache: &mut ApplicableDeclarationsCache, new_animations_sender: &Mutex>); @@ -418,12 +418,12 @@ trait PrivateMatchMethods { trait PrivateElementMatchMethods { fn share_style_with_candidate_if_possible(&self, - parent_node: Option, + parent_node: Option, candidate: &StyleSharingCandidate) -> Option>; } -impl<'ln> PrivateMatchMethods for LayoutNode<'ln> { +impl<'ln> PrivateMatchMethods for ServoLayoutNode<'ln> { fn cascade_node_pseudo_element(&self, layout_context: &SharedLayoutContext, parent_style: Option<&Arc>, @@ -502,9 +502,9 @@ impl<'ln> PrivateMatchMethods for LayoutNode<'ln> { } } -impl<'ln> PrivateElementMatchMethods for LayoutElement<'ln> { +impl<'ln> PrivateElementMatchMethods for ServoLayoutElement<'ln> { fn share_style_with_candidate_if_possible(&self, - parent_node: Option, + parent_node: Option, candidate: &StyleSharingCandidate) -> Option> { let parent_node = match parent_node { @@ -537,7 +537,7 @@ impl<'ln> PrivateElementMatchMethods for LayoutElement<'ln> { } } -impl<'ln> ElementMatchMethods for LayoutElement<'ln> { +impl<'ln> ElementMatchMethods for ServoLayoutElement<'ln> { fn match_element(&self, stylist: &Stylist, parent_bf: Option<&BloomFilter>, @@ -570,7 +570,7 @@ impl<'ln> ElementMatchMethods for LayoutElement<'ln> { unsafe fn share_style_if_possible(&self, style_sharing_candidate_cache: &mut StyleSharingCandidateCache, - parent: Option) + parent: Option) -> StyleSharingResult { if opts::get().disable_share_style_cache { return StyleSharingResult::CannotShare @@ -603,7 +603,7 @@ impl<'ln> ElementMatchMethods for LayoutElement<'ln> { } } -impl<'ln> MatchMethods for LayoutNode<'ln> { +impl<'ln> MatchMethods for ServoLayoutNode<'ln> { // The below two functions are copy+paste because I can't figure out how to // write a function which takes a generic function. I don't think it can // be done. @@ -645,7 +645,7 @@ impl<'ln> MatchMethods for LayoutNode<'ln> { unsafe fn cascade_node(&self, layout_context: &SharedLayoutContext, - parent: Option, + parent: Option, applicable_declarations: &ApplicableDeclarations, applicable_declarations_cache: &mut ApplicableDeclarationsCache, new_animations_sender: &Mutex>) { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 0aed4e6c9c0..0ce3744d9ac 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -82,7 +82,7 @@ use util::opts; use util::task::spawn_named_with_send_on_failure; use util::task_state; use util::workqueue::WorkQueue; -use wrapper::{LayoutNode, ThreadSafeLayoutNode}; +use wrapper::{LayoutDocument, LayoutElement, LayoutNode, ServoLayoutNode, ThreadSafeLayoutNode}; /// The number of screens of data we're allowed to generate display lists for in each direction. pub const DISPLAY_PORT_SIZE_FACTOR: i32 = 8; @@ -750,7 +750,7 @@ impl LayoutTask { possibly_locked_rw_data.block(rw_data); } - fn try_get_layout_root(&self, node: LayoutNode) -> Option { + fn try_get_layout_root(&self, node: ServoLayoutNode) -> Option { let mut layout_data_ref = node.mutate_layout_data(); let layout_data = match layout_data_ref.as_mut() { @@ -828,7 +828,7 @@ impl LayoutTask { property: &Atom, layout_root: &mut FlowRef) -> Option { - let node = unsafe { LayoutNode::new(&requested_node) }; + let node = unsafe { ServoLayoutNode::new(&requested_node) }; let layout_node = ThreadSafeLayoutNode::new(&node); let layout_node = match pseudo { @@ -1063,14 +1063,14 @@ impl LayoutTask { fn handle_reflow<'a, 'b>(&mut self, data: &ScriptReflow, possibly_locked_rw_data: &mut RwData<'a, 'b>) { - let document = unsafe { LayoutNode::new(&data.document) }; + let document = unsafe { ServoLayoutNode::new(&data.document) }; let document = document.as_document().unwrap(); debug!("layout: received layout request for: {}", self.url.serialize()); let mut rw_data = possibly_locked_rw_data.lock(); - let node: LayoutNode = match document.root_node() { + let node: ServoLayoutNode = match document.root_node() { None => { // Since we cannot compute anything, give spec-required placeholders. debug!("layout: No root node: bailing"); @@ -1418,7 +1418,7 @@ impl LayoutTask { } } - unsafe fn dirty_all_nodes(node: LayoutNode) { + unsafe fn dirty_all_nodes(node: ServoLayoutNode) { for node in node.traverse_preorder() { // TODO(cgaebel): mark nodes which are sensitive to media queries as // "changed": diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 1b1f4056706..c8549c37eb6 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -23,7 +23,7 @@ use traversal::{PostorderDomTraversal, PreorderDomTraversal}; use util::opts; use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; use wrapper::UnsafeLayoutNode; -use wrapper::{LayoutNode, layout_node_from_unsafe_layout_node, layout_node_to_unsafe_layout_node}; +use wrapper::{LayoutNode, ServoLayoutNode, layout_node_from_unsafe_layout_node, layout_node_to_unsafe_layout_node}; const CHUNK_SIZE: usize = 64; @@ -103,7 +103,7 @@ pub trait ParallelPreorderDomTraversal : PreorderDomTraversal { let mut discovered_child_nodes = Vec::new(); for unsafe_node in *unsafe_nodes.0 { // Get a real layout node. - let node: LayoutNode = unsafe { + let node: ServoLayoutNode = unsafe { layout_node_from_unsafe_layout_node(&unsafe_node) }; @@ -157,7 +157,7 @@ trait ParallelPostorderDomTraversal : PostorderDomTraversal { /// fetch-and-subtract the parent's children count. fn run_parallel(&self, unsafe_node: UnsafeLayoutNode) { // Get a real layout node. - let mut node: LayoutNode = unsafe { + let mut node: ServoLayoutNode = unsafe { layout_node_from_unsafe_layout_node(&unsafe_node) }; loop { @@ -440,7 +440,7 @@ fn run_queue_with_custom_work_data_type( queue.run(shared_layout_context); } -pub fn traverse_dom_preorder(root: LayoutNode, +pub fn traverse_dom_preorder(root: ServoLayoutNode, shared_layout_context: &SharedLayoutContext, queue: &mut WorkQueue) { run_queue_with_custom_work_data_type(queue, |queue| { diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 9202ef2ea8a..36f754a2cf5 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -19,11 +19,11 @@ use traversal::{BuildDisplayList, ComputeAbsolutePositions}; use traversal::{PostorderDomTraversal, PreorderDomTraversal}; use util::geometry::ZERO_POINT; use util::opts; -use wrapper::LayoutNode; +use wrapper::{LayoutNode, ServoLayoutNode}; -pub fn traverse_dom_preorder(root: LayoutNode, +pub fn traverse_dom_preorder(root: ServoLayoutNode, shared_layout_context: &SharedLayoutContext) { - fn doit(node: LayoutNode, recalc_style: RecalcStyleForNode, construct_flows: ConstructFlows) { + fn doit(node: ServoLayoutNode, recalc_style: RecalcStyleForNode, construct_flows: ConstructFlows) { recalc_style.process(node); for kid in node.children() { diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 3a1fcf38bc6..18fb3e19124 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -17,7 +17,7 @@ use std::cell::RefCell; use std::mem; use util::opts; use util::tid::tid; -use wrapper::{LayoutNode, layout_node_to_unsafe_layout_node}; +use wrapper::{LayoutNode, ServoLayoutNode, layout_node_to_unsafe_layout_node}; use wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode}; /// Every time we do another layout, the old bloom filters are invalid. This is @@ -51,7 +51,7 @@ thread_local!( /// /// If one does not exist, a new one will be made for you. If it is out of date, /// it will be cleared and reused. -fn take_task_local_bloom_filter(parent_node: Option, +fn take_task_local_bloom_filter(parent_node: Option, root: OpaqueNode, layout_context: &LayoutContext) -> Box { @@ -98,7 +98,7 @@ fn put_task_local_bloom_filter(bf: Box, /// "Ancestors" in this context is inclusive of ourselves. fn insert_ancestors_into_bloom_filter(bf: &mut Box, - mut n: LayoutNode, + mut n: ServoLayoutNode, root: OpaqueNode) { debug!("[{}] Inserting ancestors.", tid()); let mut ancestors = 0; @@ -118,13 +118,13 @@ fn insert_ancestors_into_bloom_filter(bf: &mut Box, /// A top-down traversal. pub trait PreorderDomTraversal { /// The operation to perform. Return true to continue or false to stop. - fn process(&self, node: LayoutNode); + fn process(&self, node: ServoLayoutNode); } /// A bottom-up traversal, with a optional in-order pass. pub trait PostorderDomTraversal { /// The operation to perform. Return true to continue or false to stop. - fn process(&self, node: LayoutNode); + fn process(&self, node: ServoLayoutNode); } /// A bottom-up, parallelizable traversal. @@ -144,7 +144,7 @@ pub struct RecalcStyleForNode<'a> { impl<'a> PreorderDomTraversal for RecalcStyleForNode<'a> { #[inline] #[allow(unsafe_code)] - fn process(&self, node: LayoutNode) { + fn process(&self, node: ServoLayoutNode) { // Initialize layout data. // // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML @@ -249,7 +249,7 @@ pub struct ConstructFlows<'a> { impl<'a> PostorderDomTraversal for ConstructFlows<'a> { #[inline] #[allow(unsafe_code)] - fn process(&self, node: LayoutNode) { + fn process(&self, node: ServoLayoutNode) { // Construct flows for this node. { let tnode = ThreadSafeLayoutNode::new(&node); diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 11a4a0e514e..a38009f7a52 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -73,8 +73,170 @@ use util::str::{is_whitespace, search_index}; /// A wrapper so that layout can access only the methods that it should have access to. Layout must /// only ever see these and must never see instances of `LayoutJS`. + +pub trait WrapperTypes<'a> : Sized { + type Node: LayoutNode<'a, Self>; + type Document: LayoutDocument<'a, Self>; + type Element: LayoutElement<'a, Self>; +} + +pub trait LayoutNode<'ln, Wrappers> : Sized + Copy + Clone + where Wrappers: WrapperTypes<'ln> { + /// Returns the type ID of this node. + fn type_id(&self) -> NodeTypeId; + + fn is_element(&self) -> bool; + + fn dump(self); + + fn traverse_preorder(self) -> LayoutTreeIterator<'ln, Wrappers>; + + /// Returns an iterator over this node's children. + fn children(self) -> LayoutNodeChildrenIterator<'ln, Wrappers>; + + fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln, Wrappers>; + + /// Converts self into an `OpaqueNode`. + fn opaque(&self) -> OpaqueNode; + + /// Resets layout data and styles for the node. + /// + /// FIXME(pcwalton): Do this as part of fragment building instead of in a traversal. + fn initialize_layout_data(self); + + /// While doing a reflow, the node at the root has no parent, as far as we're + /// concerned. This method returns `None` at the reflow root. + fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option; + + fn debug_id(self) -> usize; + + fn as_element(&self) -> Option; + + fn as_document(&self) -> Option; + + fn children_count(&self) -> u32; + + fn has_changed(&self) -> bool; + + unsafe fn set_changed(&self, value: bool); + + fn is_dirty(&self) -> bool; + + unsafe fn set_dirty(&self, value: bool); + + fn has_dirty_descendants(&self) -> bool; + + unsafe fn set_dirty_descendants(&self, value: bool); + + fn dirty_self(&self) { + unsafe { + self.set_dirty(true); + self.set_dirty_descendants(true); + } + } + + fn dirty_descendants(&self) { + for ref child in self.children() { + child.dirty_self(); + child.dirty_descendants(); + } + } + + /// Borrows the layout data without checks. + #[inline(always)] + unsafe fn borrow_layout_data_unchecked(&self) -> *const Option; + + /// Borrows the layout data immutably. Fails on a conflicting borrow. + #[inline(always)] + fn borrow_layout_data(&self) -> Ref>; + + /// Borrows the layout data mutably. Fails on a conflicting borrow. + #[inline(always)] + fn mutate_layout_data(&self) -> RefMut>; + + fn parent_node(&self) -> Option; + + fn first_child(&self) -> Option; + + fn last_child(&self) -> Option; + + fn prev_sibling(&self) -> Option; + + fn next_sibling(&self) -> Option; +} + +pub trait LayoutDocument<'ld, Wrappers> : Sized + Copy + Clone + where Wrappers: WrapperTypes<'ld> { + fn as_node(&self) -> Wrappers::Node; + + fn root_node(&self) -> Option; + + fn drain_modified_elements(&self) -> Vec<(Wrappers::Element, ElementSnapshot)>; +} + +pub trait LayoutElement<'le, Wrappers> : Sized + Copy + Clone + ::selectors::Element + where Wrappers: WrapperTypes<'le> { + fn as_node(&self) -> Wrappers::Node; + + fn style_attribute(&self) -> &'le Option; + + fn get_state(&self) -> ElementState; + + /// Properly marks nodes as dirty in response to restyle hints. + fn note_restyle_hint(&self, mut hint: RestyleHint) { + // Bail early if there's no restyling to do. + if hint.is_empty() { + return; + } + + // If the restyle hint is non-empty, we need to restyle either this element + // or one of its siblings. Mark our ancestor chain as having dirty descendants. + let node = self.as_node(); + let mut curr = node; + while let Some(parent) = curr.parent_node() { + if parent.has_dirty_descendants() { break } + unsafe { parent.set_dirty_descendants(true); } + curr = parent; + } + + // Process hints. + if hint.contains(RESTYLE_SELF) { + node.dirty_self(); + + // FIXME(bholley, #8438): We currently need to RESTYLE_DESCENDANTS in the + // RESTYLE_SELF case in order to make sure "inherit" style structs propagate + // properly. See the explanation in the github issue. + hint.insert(RESTYLE_DESCENDANTS); + } + if hint.contains(RESTYLE_DESCENDANTS) { + unsafe { node.set_dirty_descendants(true); } + node.dirty_descendants(); + } + if hint.contains(RESTYLE_LATER_SIBLINGS) { + let mut next = ::selectors::Element::next_sibling_element(self); + while let Some(sib) = next { + let sib_node = sib.as_node(); + sib_node.dirty_self(); + sib_node.dirty_descendants(); + next = ::selectors::Element::next_sibling_element(&sib); + } + } + } +} + +pub struct ServoWrapperTypes<'a> { + // Satisfy the compiler about the unused lifetime. + phantom: PhantomData<&'a ()>, +} + +impl<'a> WrapperTypes<'a> for ServoWrapperTypes<'a> { + type Node = ServoLayoutNode<'a>; + type Element = ServoLayoutElement<'a>; + type Document = ServoLayoutDocument<'a>; +} + #[derive(Copy, Clone)] -pub struct LayoutNode<'a> { +pub struct ServoLayoutNode<'a> { /// The wrapped node. node: LayoutJS, @@ -82,50 +244,180 @@ pub struct LayoutNode<'a> { chain: PhantomData<&'a ()>, } -impl<'a> PartialEq for LayoutNode<'a> { +impl<'a> PartialEq for ServoLayoutNode<'a> { #[inline] - fn eq(&self, other: &LayoutNode) -> bool { + fn eq(&self, other: &ServoLayoutNode) -> bool { self.node == other.node } } -impl<'ln> LayoutNode<'ln> { - fn from_layout_js(n: LayoutJS) -> LayoutNode<'ln> { - LayoutNode { +impl<'ln> ServoLayoutNode<'ln> { + fn from_layout_js(n: LayoutJS) -> ServoLayoutNode<'ln> { + ServoLayoutNode { node: n, chain: PhantomData, } } - pub unsafe fn new(address: &TrustedNodeAddress) -> LayoutNode { - LayoutNode::from_layout_js(LayoutJS::from_trusted_node_address(*address)) + pub unsafe fn new(address: &TrustedNodeAddress) -> ServoLayoutNode { + ServoLayoutNode::from_layout_js(LayoutJS::from_trusted_node_address(*address)) } /// Creates a new layout node with the same lifetime as this layout node. - pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS) -> LayoutNode<'ln> { - LayoutNode { + pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS) -> ServoLayoutNode<'ln> { + ServoLayoutNode { node: *node, chain: self.chain, } } +} - /// Returns the type ID of this node. - pub fn type_id(&self) -> NodeTypeId { +impl<'ln> LayoutNode<'ln, ServoWrapperTypes<'ln>> for ServoLayoutNode<'ln> { + fn type_id(&self) -> NodeTypeId { unsafe { self.node.type_id_for_layout() } } - pub fn is_element(&self) -> bool { + fn is_element(&self) -> bool { unsafe { self.node.is_element_for_layout() } } - pub fn dump(self) { + fn dump(self) { self.dump_indent(0); } + fn traverse_preorder(self) -> LayoutTreeIterator<'ln, ServoWrapperTypes<'ln>> { + LayoutTreeIterator::new(self) + } + + fn children(self) -> LayoutNodeChildrenIterator<'ln, ServoWrapperTypes<'ln>> { + LayoutNodeChildrenIterator { + current: self.first_child(), + } + } + + fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln, ServoWrapperTypes<'ln>> { + LayoutNodeReverseChildrenIterator { + current: self.last_child() + } + } + + fn opaque(&self) -> OpaqueNode { + OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() }) + } + + fn initialize_layout_data(self) { + let mut layout_data_ref = self.mutate_layout_data(); + match *layout_data_ref { + None => { + *layout_data_ref = Some(LayoutDataWrapper { + shared_data: SharedLayoutData { style: None }, + data: box PrivateLayoutData::new(), + }); + } + Some(_) => {} + } + } + + fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option> { + if self.opaque() == reflow_root { + None + } else { + self.parent_node() + } + } + + fn debug_id(self) -> usize { + self.opaque().to_untrusted_node_address().0 as usize + } + + fn children_count(&self) -> u32 { + unsafe { self.node.children_count() } + } + + fn as_element(&self) -> Option> { + as_element(self.node) + } + + fn as_document(&self) -> Option> { + self.node.downcast().map(|document| ServoLayoutDocument::from_layout_js(document)) + } + + fn has_changed(&self) -> bool { + unsafe { self.node.get_flag(HAS_CHANGED) } + } + + unsafe fn set_changed(&self, value: bool) { + self.node.set_flag(HAS_CHANGED, value) + } + + fn is_dirty(&self) -> bool { + unsafe { self.node.get_flag(IS_DIRTY) } + } + + unsafe fn set_dirty(&self, value: bool) { + self.node.set_flag(IS_DIRTY, value) + } + + fn has_dirty_descendants(&self) -> bool { + unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) } + } + + unsafe fn set_dirty_descendants(&self, value: bool) { + self.node.set_flag(HAS_DIRTY_DESCENDANTS, value) + } + + unsafe fn borrow_layout_data_unchecked(&self) -> *const Option { + mem::transmute(self.get_jsmanaged().layout_data_unchecked()) + } + + fn borrow_layout_data(&self) -> Ref> { + unsafe { + mem::transmute(self.get_jsmanaged().layout_data()) + } + } + + fn mutate_layout_data(&self) -> RefMut> { + unsafe { + mem::transmute(self.get_jsmanaged().layout_data_mut()) + } + } + + fn parent_node(&self) -> Option> { + unsafe { + self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn first_child(&self) -> Option> { + unsafe { + self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn last_child(&self) -> Option> { + unsafe { + self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn prev_sibling(&self) -> Option> { + unsafe { + self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } + + fn next_sibling(&self) -> Option> { + unsafe { + self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) + } + } +} + +impl<'ln> ServoLayoutNode<'ln> { fn dump_indent(self, indent: u32) { let mut s = String::new(); for _ in 0..indent { @@ -153,188 +445,45 @@ impl<'ln> LayoutNode<'ln> { } } - pub fn traverse_preorder(self) -> LayoutTreeIterator<'ln> { - LayoutTreeIterator::new(self) - } - - /// Returns an iterator over this node's children. - pub fn children(self) -> LayoutNodeChildrenIterator<'ln> { - LayoutNodeChildrenIterator { - current: self.first_child(), - } - } - - pub fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln> { - LayoutNodeReverseChildrenIterator { - current: self.last_child() - } - - } - /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to /// call and as such is marked `unsafe`. unsafe fn get_jsmanaged(&self) -> &LayoutJS { &self.node } - - /// Converts self into an `OpaqueNode`. - pub fn opaque(&self) -> OpaqueNode { - OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() }) - } - - /// Resets layout data and styles for the node. - /// - /// FIXME(pcwalton): Do this as part of fragment building instead of in a traversal. - pub fn initialize_layout_data(self) { - let mut layout_data_ref = self.mutate_layout_data(); - match *layout_data_ref { - None => { - *layout_data_ref = Some(LayoutDataWrapper { - shared_data: SharedLayoutData { style: None }, - data: box PrivateLayoutData::new(), - }); - } - Some(_) => {} - } - } - - /// While doing a reflow, the node at the root has no parent, as far as we're - /// concerned. This method returns `None` at the reflow root. - pub fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option> { - if self.opaque() == reflow_root { - None - } else { - self.parent_node() - } - } - - pub fn debug_id(self) -> usize { - self.opaque().to_untrusted_node_address().0 as usize - } - - pub fn as_element(&self) -> Option> { - as_element(self.node) - } - - pub fn as_document(&self) -> Option> { - self.node.downcast().map(|document| LayoutDocument::from_layout_js(document)) - } - - fn parent_node(&self) -> Option> { - unsafe { - self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn first_child(&self) -> Option> { - unsafe { - self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn last_child(&self) -> Option> { - unsafe { - self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn prev_sibling(&self) -> Option> { - unsafe { - self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - fn next_sibling(&self) -> Option> { - unsafe { - self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node)) - } - } - - pub fn children_count(&self) -> u32 { - unsafe { self.node.children_count() } - } } -impl<'ln> LayoutNode<'ln> { - pub fn has_changed(&self) -> bool { - unsafe { self.node.get_flag(HAS_CHANGED) } - } - - pub unsafe fn set_changed(&self, value: bool) { - self.node.set_flag(HAS_CHANGED, value) - } - - pub fn is_dirty(&self) -> bool { - unsafe { self.node.get_flag(IS_DIRTY) } - } - - pub unsafe fn set_dirty(&self, value: bool) { - self.node.set_flag(IS_DIRTY, value) - } - - pub fn has_dirty_descendants(&self) -> bool { - unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) } - } - - pub unsafe fn set_dirty_descendants(&self, value: bool) { - self.node.set_flag(HAS_DIRTY_DESCENDANTS, value) - } - - /// Borrows the layout data without checks. - #[inline(always)] - pub unsafe fn borrow_layout_data_unchecked(&self) -> *const Option { - mem::transmute(self.get_jsmanaged().layout_data_unchecked()) - } - - /// Borrows the layout data immutably. Fails on a conflicting borrow. - #[inline(always)] - pub fn borrow_layout_data(&self) -> Ref> { - unsafe { - mem::transmute(self.get_jsmanaged().layout_data()) - } - } - - /// Borrows the layout data mutably. Fails on a conflicting borrow. - #[inline(always)] - pub fn mutate_layout_data(&self) -> RefMut> { - unsafe { - mem::transmute(self.get_jsmanaged().layout_data_mut()) - } - } +pub struct LayoutNodeChildrenIterator<'a, Wrappers: WrapperTypes<'a>> { + current: Option, } -pub struct LayoutNodeChildrenIterator<'a> { - current: Option>, -} - -impl<'a> Iterator for LayoutNodeChildrenIterator<'a> { - type Item = LayoutNode<'a>; - fn next(&mut self) -> Option> { +impl<'a, Wrappers: WrapperTypes<'a>> Iterator for LayoutNodeChildrenIterator<'a, Wrappers> { + type Item = Wrappers::Node; + fn next(&mut self) -> Option { let node = self.current; self.current = node.and_then(|node| node.next_sibling()); node } } -pub struct LayoutNodeReverseChildrenIterator<'a> { - current: Option>, +pub struct LayoutNodeReverseChildrenIterator<'a, Wrappers: WrapperTypes<'a>> { + current: Option, } -impl<'a> Iterator for LayoutNodeReverseChildrenIterator<'a> { - type Item = LayoutNode<'a>; - fn next(&mut self) -> Option> { +impl<'a, Wrappers: WrapperTypes<'a>> Iterator for LayoutNodeReverseChildrenIterator<'a, Wrappers> { + type Item = Wrappers::Node; + fn next(&mut self) -> Option { let node = self.current; self.current = node.and_then(|node| node.prev_sibling()); node } } -pub struct LayoutTreeIterator<'a> { - stack: Vec>, +pub struct LayoutTreeIterator<'a, Wrappers: WrapperTypes<'a>> { + stack: Vec, } -impl<'a> LayoutTreeIterator<'a> { - fn new(root: LayoutNode<'a>) -> LayoutTreeIterator<'a> { +impl<'a, Wrappers> LayoutTreeIterator<'a, Wrappers> where Wrappers: WrapperTypes<'a> { + fn new(root: Wrappers::Node) -> LayoutTreeIterator<'a, Wrappers> { let mut stack = vec!(); stack.push(root); LayoutTreeIterator { @@ -343,9 +492,9 @@ impl<'a> LayoutTreeIterator<'a> { } } -impl<'a> Iterator for LayoutTreeIterator<'a> { - type Item = LayoutNode<'a>; - fn next(&mut self) -> Option> { +impl<'a, Wrappers> Iterator for LayoutTreeIterator<'a, Wrappers> where Wrappers: WrapperTypes<'a> { + type Item = Wrappers::Node; + fn next(&mut self) -> Option { let ret = self.stack.pop(); ret.map(|node| self.stack.extend(node.rev_children())); ret @@ -354,120 +503,70 @@ impl<'a> Iterator for LayoutTreeIterator<'a> { // A wrapper around documents that ensures ayout can only ever access safe properties. #[derive(Copy, Clone)] -pub struct LayoutDocument<'le> { +pub struct ServoLayoutDocument<'ld> { document: LayoutJS, - chain: PhantomData<&'le ()>, + chain: PhantomData<&'ld ()>, } -impl<'le> LayoutDocument<'le> { - fn from_layout_js(doc: LayoutJS) -> LayoutDocument<'le> { - LayoutDocument { +impl<'ld> LayoutDocument<'ld, ServoWrapperTypes<'ld>> for ServoLayoutDocument<'ld> { + fn as_node(&self) -> ServoLayoutNode<'ld> { + ServoLayoutNode::from_layout_js(self.document.upcast()) + } + + fn root_node(&self) -> Option> { + self.as_node().children().find(ServoLayoutNode::is_element) + } + + fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, ElementSnapshot)> { + let elements = unsafe { self.document.drain_modified_elements() }; + elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect() + } +} + +impl<'ld> ServoLayoutDocument<'ld> { + fn from_layout_js(doc: LayoutJS) -> ServoLayoutDocument<'ld> { + ServoLayoutDocument { document: doc, chain: PhantomData, } } - - pub fn as_node(&self) -> LayoutNode<'le> { - LayoutNode::from_layout_js(self.document.upcast()) - } - - pub fn root_node(&self) -> Option> { - self.as_node().children().find(LayoutNode::is_element) - } - - pub fn drain_modified_elements(&self) -> Vec<(LayoutElement, ElementSnapshot)> { - let elements = unsafe { self.document.drain_modified_elements() }; - elements.into_iter().map(|(el, snapshot)| (LayoutElement::from_layout_js(el), snapshot)).collect() - } } /// A wrapper around elements that ensures layout can only ever access safe properties. #[derive(Copy, Clone)] -pub struct LayoutElement<'le> { +pub struct ServoLayoutElement<'le> { element: LayoutJS, chain: PhantomData<&'le ()>, } -impl<'le> LayoutElement<'le> { - fn from_layout_js(el: LayoutJS) -> LayoutElement<'le> { - LayoutElement { - element: el, - chain: PhantomData, - } +impl<'le> LayoutElement<'le, ServoWrapperTypes<'le>> for ServoLayoutElement<'le> { + fn as_node(&self) -> ServoLayoutNode<'le> { + ServoLayoutNode::from_layout_js(self.element.upcast()) } - pub fn style_attribute(&self) -> &'le Option { + fn style_attribute(&self) -> &'le Option { unsafe { &*self.element.style_attribute() } } - pub fn as_node(&self) -> LayoutNode<'le> { - LayoutNode::from_layout_js(self.element.upcast()) - } - - pub fn get_state(&self) -> ElementState { + fn get_state(&self) -> ElementState { self.element.get_state_for_layout() } +} - /// Properly marks nodes as dirty in response to restyle hints. - pub fn note_restyle_hint(&self, mut hint: RestyleHint) { - // Bail early if there's no restyling to do. - if hint.is_empty() { - return; - } - // If the restyle hint is non-empty, we need to restyle either this element - // or one of its siblings. Mark our ancestor chain as having dirty descendants. - let node = self.as_node(); - let mut curr = node; - while let Some(parent) = curr.parent_node() { - if parent.has_dirty_descendants() { break } - unsafe { parent.set_dirty_descendants(true); } - curr = parent; - } - - // Set up our helpers. - fn dirty_node(node: &LayoutNode) { - unsafe { - node.set_dirty(true); - node.set_dirty_descendants(true); - } - } - fn dirty_descendants(node: &LayoutNode) { - for ref child in node.children() { - dirty_node(child); - dirty_descendants(child); - } - } - - // Process hints. - if hint.contains(RESTYLE_SELF) { - dirty_node(&node); - - // FIXME(bholley, #8438): We currently need to RESTYLE_DESCENDANTS in the - // RESTYLE_SELF case in order to make sure "inherit" style structs propagate - // properly. See the explanation in the github issue. - hint.insert(RESTYLE_DESCENDANTS); - } - if hint.contains(RESTYLE_DESCENDANTS) { - unsafe { node.set_dirty_descendants(true); } - dirty_descendants(&node); - } - if hint.contains(RESTYLE_LATER_SIBLINGS) { - let mut next = ::selectors::Element::next_sibling_element(self); - while let Some(sib) = next { - let sib_node = sib.as_node(); - dirty_node(&sib_node); - dirty_descendants(&sib_node); - next = ::selectors::Element::next_sibling_element(&sib); - } +impl<'le> ServoLayoutElement<'le> { + fn from_layout_js(el: LayoutJS) -> ServoLayoutElement<'le> { + ServoLayoutElement { + element: el, + chain: PhantomData, } } } -fn as_element<'le>(node: LayoutJS) -> Option> { - node.downcast().map(|element| LayoutElement::from_layout_js(element)) +fn as_element<'le>(node: LayoutJS) -> Option> { + node.downcast().map(|element| ServoLayoutElement::from_layout_js(element)) } macro_rules! state_getter { @@ -480,22 +579,22 @@ macro_rules! state_getter { } } -impl<'le> ::selectors::Element for LayoutElement<'le> { - fn parent_element(&self) -> Option> { +impl<'le> ::selectors::Element for ServoLayoutElement<'le> { + fn parent_element(&self) -> Option> { unsafe { self.element.upcast().parent_node_ref().and_then(as_element) } } - fn first_child_element(&self) -> Option> { + fn first_child_element(&self) -> Option> { self.as_node().children().filter_map(|n| n.as_element()).next() } - fn last_child_element(&self) -> Option> { + fn last_child_element(&self) -> Option> { self.as_node().rev_children().filter_map(|n| n.as_element()).next() } - fn prev_sibling_element(&self) -> Option> { + fn prev_sibling_element(&self) -> Option> { let mut node = self.as_node(); while let Some(sibling) = node.prev_sibling() { if let Some(element) = sibling.as_element() { @@ -506,7 +605,7 @@ impl<'le> ::selectors::Element for LayoutElement<'le> { None } - fn next_sibling_element(&self) -> Option> { + fn next_sibling_element(&self) -> Option> { let mut node = self.as_node(); while let Some(sibling) = node.next_sibling() { if let Some(element) = sibling.as_element() { @@ -633,7 +732,7 @@ impl<'le> ::selectors::Element for LayoutElement<'le> { } } -impl<'le> TElementAttributes for LayoutElement<'le> { +impl<'le> TElementAttributes for ServoLayoutElement<'le> { fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) where V: VecLike>> { @@ -686,7 +785,7 @@ impl PseudoElementType { #[derive(Copy, Clone)] pub struct ThreadSafeLayoutNode<'ln> { /// The wrapped node. - node: LayoutNode<'ln>, + node: ServoLayoutNode<'ln>, pseudo: PseudoElementType, } @@ -701,7 +800,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { } /// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`. - pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> { + pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ThreadSafeLayoutNode<'a> { ThreadSafeLayoutNode { node: node.clone(), pseudo: PseudoElementType::Normal, @@ -1105,14 +1204,14 @@ impl<'le> ThreadSafeLayoutElement<'le> { /// Must be transmutable to and from LayoutNode. pub type UnsafeLayoutNode = (usize, usize); -pub fn layout_node_to_unsafe_layout_node(node: &LayoutNode) -> UnsafeLayoutNode { +pub fn layout_node_to_unsafe_layout_node(node: &ServoLayoutNode) -> UnsafeLayoutNode { unsafe { let ptr: usize = mem::transmute_copy(node); (ptr, 0) } } -pub unsafe fn layout_node_from_unsafe_layout_node(node: &UnsafeLayoutNode) -> LayoutNode { +pub unsafe fn layout_node_from_unsafe_layout_node(node: &UnsafeLayoutNode) -> ServoLayoutNode { let (node, _) = *node; mem::transmute(node) }