From c506e52c7c6e80ea875e9d1ce95597cf3bc8bcc1 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 17 Dec 2013 10:08:01 -0800 Subject: layout: Add a lifetime to `LayoutNode` to prevent layout from stuffing them into evil places. --- src/components/main/css/matching.rs | 32 ++++++++++---- src/components/main/css/node_style.rs | 2 +- src/components/main/css/node_util.rs | 2 +- src/components/main/layout/construct.rs | 2 +- src/components/main/layout/extra.rs | 2 +- src/components/main/layout/inline.rs | 3 +- src/components/main/layout/util.rs | 2 +- src/components/script/dom/node.rs | 78 ++++++++++++++++++++------------- 8 files changed, 77 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/components/main/css/matching.rs b/src/components/main/css/matching.rs index 4ca64a8a138..efd84f36b7e 100644 --- a/src/components/main/css/matching.rs +++ b/src/components/main/css/matching.rs @@ -4,18 +4,19 @@ // High-level interface to CSS selector matching. -use std::cell::Cell; -use std::comm; -use std::task; -use std::vec; -use std::rt; -use extra::arc::{Arc, RWArc}; - use css::node_style::StyledNode; use layout::incremental; use layout::util::LayoutDataAccess; +use extra::arc::{Arc, RWArc}; use script::dom::node::LayoutNode; +use std::cast; +use std::cell::Cell; +use std::comm; +use std::libc::uintptr_t; +use std::rt; +use std::task; +use std::vec; use style::{TNode, Stylist, cascade}; pub trait MatchMethods { @@ -26,7 +27,7 @@ pub trait MatchMethods { fn cascade_subtree(&self, parent: Option); } -impl MatchMethods for LayoutNode { +impl<'self> MatchMethods for LayoutNode<'self> { fn match_node(&self, stylist: &Stylist) { let applicable_declarations = do self.with_element |element| { let style_attribute = match element.style_attribute { @@ -63,7 +64,20 @@ impl MatchMethods for LayoutNode { if nodes.len() > 0 { let chan = chan.clone(); let stylist = stylist.clone(); - do task::spawn_with((nodes, stylist)) |(nodes, stylist)| { + + // FIXME(pcwalton): This transmute is to work around the fact that we have no + // mechanism for safe fork/join parallelism. If we had such a thing, then we could + // close over the lifetime-bounded `LayoutNode`. But we can't, so we force it with + // a transmute. + let evil: uintptr_t = unsafe { + cast::transmute(nodes) + }; + + do task::spawn_with((evil, stylist)) |(evil, stylist)| { + let nodes: ~[LayoutNode] = unsafe { + cast::transmute(evil) + }; + let nodes = Cell::new(nodes); do stylist.read |stylist| { for node in nodes.take().move_iter() { diff --git a/src/components/main/css/node_style.rs b/src/components/main/css/node_style.rs index 6e2abaa81d1..824b50e991a 100644 --- a/src/components/main/css/node_style.rs +++ b/src/components/main/css/node_style.rs @@ -17,7 +17,7 @@ pub trait StyledNode { fn restyle_damage(&self) -> RestyleDamage; } -impl StyledNode for LayoutNode { +impl<'self> StyledNode for LayoutNode<'self> { #[inline] fn style<'a>(&'a self) -> &'a Arc { self.get_css_select_results() diff --git a/src/components/main/css/node_util.rs b/src/components/main/css/node_util.rs index 3b24f85e9de..72acf5dc08c 100644 --- a/src/components/main/css/node_util.rs +++ b/src/components/main/css/node_util.rs @@ -18,7 +18,7 @@ pub trait NodeUtil { fn set_restyle_damage(self, damage: RestyleDamage); } -impl NodeUtil for LayoutNode { +impl<'self> NodeUtil for LayoutNode<'self> { /** * Provides the computed style for the given node. If CSS selector * Returns the style results for the given node. If CSS selector diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index e2182886f29..9c6f285047e 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -528,7 +528,7 @@ trait NodeUtils { fn swap_out_construction_result(self) -> ConstructionResult; } -impl NodeUtils for LayoutNode { +impl<'self> NodeUtils for LayoutNode<'self> { fn is_replaced_content(self) -> bool { match self.type_id() { TextNodeTypeId | diff --git a/src/components/main/layout/extra.rs b/src/components/main/layout/extra.rs index 7bc4847f5d3..bef668c6788 100644 --- a/src/components/main/layout/extra.rs +++ b/src/components/main/layout/extra.rs @@ -14,7 +14,7 @@ pub trait LayoutAuxMethods { fn initialize_style_for_subtree(self); } -impl LayoutAuxMethods for LayoutNode { +impl<'self> LayoutAuxMethods for LayoutNode<'self> { /// Resets layout data and styles for the node. /// /// FIXME(pcwalton): Do this as part of box building instead of in a traversal. diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index c270d90df60..5d5022eaf30 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -441,10 +441,11 @@ pub struct InlineFlow { // these ranges are disjoint, and are the result of inline layout. // also some metadata used for positioning lines lines: ~[LineBox], + // vec of ranges into boxes that represent elements. These ranges // must be well-nested, and are only related to the content of // boxes (not lines). Ranges are only kept for non-leaf elements. - elems: ElementMapping + elems: ElementMapping, } impl InlineFlow { diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs index 4621578e481..36168117e0a 100644 --- a/src/components/main/layout/util.rs +++ b/src/components/main/layout/util.rs @@ -163,7 +163,7 @@ pub trait LayoutDataAccess { fn mutate_layout_data<'a>(&'a self) -> MutSlotRef<'a,Option<~LayoutData>>; } -impl LayoutDataAccess for LayoutNode { +impl<'self> LayoutDataAccess for LayoutNode<'self> { #[inline(always)] unsafe fn borrow_layout_data_unchecked<'a>(&'a self) -> &'a Option<~LayoutData> { cast::transmute(self.get().layout_data.borrow_unchecked()) diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index e9c60433a61..cbc7595b609 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -30,23 +30,33 @@ use std::util; use style::TNode; // -// The basic Node structure +// Layout's immutable, sanitized view of nodes. // -/// A wrapper so that layout can access only the properties that it should have access to. Layout -/// should only ever see this. +/// A nonsense constant for layout nodes to point to just to establish a lifetime. +/// +/// FIXME(pcwalton): Phantom lifetimes in Rust would be useful... +static HEAVY_IRON_BALL: () = (); + +/// 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 `AbstractNode`. #[deriving(Clone, Eq)] -pub struct LayoutNode { +pub struct LayoutNode<'self> { + /// The wrapped node. priv node: AbstractNode, + + /// Being chained to a `HEAVY_IRON_BALL` prevents `LayoutNode`s from escaping. + priv chain: &'self (), } -impl LayoutNode { +impl<'self> LayoutNode<'self> { /// NB: Do not make this public. /// /// FIXME(pcwalton): Probably this should be marked `unsafe`. - /*PRIVATE-FOR-SECURITY-REASONS*/ fn new(node: AbstractNode) -> LayoutNode { + /*PRIVATE-FOR-SECURITY-REASONS*/ fn new(node: AbstractNode) -> LayoutNode<'static> { LayoutNode { node: node, + chain: &HEAVY_IRON_BALL, } } @@ -57,26 +67,26 @@ impl LayoutNode { } /// Returns the first child of this node. - pub fn first_child(&self) -> Option { + pub fn first_child(&self) -> Option> { self.node.first_child().map(|node| LayoutNode::new(node)) } /// Returns the first child of this node. - pub fn last_child(&self) -> Option { + pub fn last_child(&self) -> Option> { self.node.last_child().map(|node| LayoutNode::new(node)) } /// Iterates over this node and all its descendants, in preorder. /// /// FIXME(pcwalton): Terribly inefficient. We should use parallelism. - pub fn traverse_preorder(&self) -> LayoutTreeIterator { + pub fn traverse_preorder(&self) -> LayoutTreeIterator<'self> { let mut nodes = ~[]; gather_layout_nodes(self, &mut nodes, false); LayoutTreeIterator::new(nodes) } /// Returns an iterator over this node's children. - pub fn children(&self) -> LayoutNodeChildrenIterator { + pub fn children(&self) -> LayoutNodeChildrenIterator<'self> { LayoutNodeChildrenIterator { current_node: self.first_child(), } @@ -177,16 +187,16 @@ impl LayoutNode { } } -impl TNode for LayoutNode { - fn parent_node(&self) -> Option { +impl<'self> TNode for LayoutNode<'self> { + fn parent_node(&self) -> Option> { self.node.node().parent_node.map(|node| LayoutNode::new(node)) } - fn prev_sibling(&self) -> Option { + fn prev_sibling(&self) -> Option> { self.node.node().prev_sibling.map(|node| LayoutNode::new(node)) } - fn next_sibling(&self) -> Option { + fn next_sibling(&self) -> Option> { self.node.node().next_sibling.map(|node| LayoutNode::new(node)) } @@ -204,12 +214,17 @@ impl TNode for LayoutNode { } } + /// FIXME(pcwalton): Unsafe! #[inline] fn with_element(&self, f: &fn(&Element) -> R) -> R { self.node.with_imm_element(f) } } +// +// The basic Node structure +// + /// This is what a Node looks like if you do not know what kind of node it is. To unpack it, use /// downcast(). /// @@ -836,12 +851,12 @@ impl Iterator for AbstractNodeChildrenIterator { } } -pub struct LayoutNodeChildrenIterator { - priv current_node: Option, +pub struct LayoutNodeChildrenIterator<'self> { + priv current_node: Option>, } -impl Iterator for LayoutNodeChildrenIterator { - fn next(&mut self) -> Option { +impl<'self> Iterator> for LayoutNodeChildrenIterator<'self> { + fn next(&mut self) -> Option> { let node = self.current_node; self.current_node = do self.current_node.and_then |node| { node.next_sibling() @@ -911,13 +926,13 @@ fn gather_abstract_nodes(cur: &AbstractNode, refs: &mut ~[AbstractNode], postord // Easy for preorder; harder for postorder. // // FIXME(pcwalton): Parallelism! Eventually this should just be nuked. -pub struct LayoutTreeIterator { - priv nodes: ~[LayoutNode], +pub struct LayoutTreeIterator<'self> { + priv nodes: ~[LayoutNode<'self>], priv index: uint, } -impl LayoutTreeIterator { - fn new(nodes: ~[LayoutNode]) -> LayoutTreeIterator { +impl<'self> LayoutTreeIterator<'self> { + fn new(nodes: ~[LayoutNode<'self>]) -> LayoutTreeIterator<'self> { LayoutTreeIterator { nodes: nodes, index: 0, @@ -925,8 +940,8 @@ impl LayoutTreeIterator { } } -impl Iterator for LayoutTreeIterator { - fn next(&mut self) -> Option { +impl<'self> Iterator> for LayoutTreeIterator<'self> { + fn next(&mut self) -> Option> { if self.index >= self.nodes.len() { None } else { @@ -938,7 +953,7 @@ impl Iterator for LayoutTreeIterator { } /// FIXME(pcwalton): This is super inefficient. -fn gather_layout_nodes(cur: &LayoutNode, refs: &mut ~[LayoutNode], postorder: bool) { +fn gather_layout_nodes<'a>(cur: &LayoutNode<'a>, refs: &mut ~[LayoutNode<'a>], postorder: bool) { if !postorder { refs.push(cur.clone()); } @@ -1617,12 +1632,12 @@ impl Reflectable for Node { /// A bottom-up, parallelizable traversal. pub trait PostorderNodeTraversal { /// The operation to perform. Return true to continue or false to stop. - fn process(&self, node: LayoutNode) -> bool; + fn process<'a>(&'a self, node: LayoutNode<'a>) -> bool; /// Returns true if this node should be pruned. If this returns true, we skip the operation /// entirely and do not process any descendant nodes. This is called *before* child nodes are /// visited. The default implementation never prunes any nodes. - fn should_prune(&self, _node: LayoutNode) -> bool { + fn should_prune<'a>(&'a self, _node: LayoutNode<'a>) -> bool { false } } @@ -1630,17 +1645,17 @@ pub trait PostorderNodeTraversal { /// A bottom-up, parallelizable traversal. pub trait PostorderNodeMutTraversal { /// The operation to perform. Return true to continue or false to stop. - fn process(&mut self, node: LayoutNode) -> bool; + fn process<'a>(&'a mut self, node: LayoutNode<'a>) -> bool; /// Returns true if this node should be pruned. If this returns true, we skip the operation /// entirely and do not process any descendant nodes. This is called *before* child nodes are /// visited. The default implementation never prunes any nodes. - fn should_prune(&self, _node: LayoutNode) -> bool { + fn should_prune<'a>(&'a self, _node: LayoutNode<'a>) -> bool { false } } -impl LayoutNode { +impl<'self> LayoutNode<'self> { /// Traverses the tree in postorder. /// /// TODO(pcwalton): Offer a parallel version with a compatible API. @@ -1668,7 +1683,8 @@ impl LayoutNode { /// Traverses the tree in postorder. /// /// TODO(pcwalton): Offer a parallel version with a compatible API. - pub fn traverse_postorder_mut(mut self, traversal: &mut T) -> bool { + pub fn traverse_postorder_mut(mut self, traversal: &mut T) + -> bool { if traversal.should_prune(self) { return true } -- cgit v1.2.3