diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2013-12-17 13:41:45 -0800 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2013-12-17 18:07:41 -0800 |
commit | 9e2b63ddd3c3881e3a6a9a444e47fb87b8bbbcf2 (patch) | |
tree | d4adb1ac4b1efb877a4a6f30bb4950f1704f5e9e /src | |
parent | c506e52c7c6e80ea875e9d1ce95597cf3bc8bcc1 (diff) | |
download | servo-9e2b63ddd3c3881e3a6a9a444e47fb87b8bbbcf2.tar.gz servo-9e2b63ddd3c3881e3a6a9a444e47fb87b8bbbcf2.zip |
layout: Move the `LayoutNode` wrapper from script into layout.
Diffstat (limited to 'src')
-rw-r--r-- | src/components/main/css/matching.rs | 2 | ||||
-rw-r--r-- | src/components/main/css/node_style.rs | 2 | ||||
-rw-r--r-- | src/components/main/css/node_util.rs | 2 | ||||
-rw-r--r-- | src/components/main/layout/box.rs | 2 | ||||
-rw-r--r-- | src/components/main/layout/construct.rs | 4 | ||||
-rw-r--r-- | src/components/main/layout/extra.rs | 3 | ||||
-rw-r--r-- | src/components/main/layout/flow.rs | 2 | ||||
-rw-r--r-- | src/components/main/layout/layout_task.rs | 3 | ||||
-rw-r--r-- | src/components/main/layout/text.rs | 5 | ||||
-rw-r--r-- | src/components/main/layout/util.rs | 3 | ||||
-rw-r--r-- | src/components/main/layout/wrapper.rs | 352 | ||||
-rwxr-xr-x | src/components/main/servo.rc | 1 | ||||
-rw-r--r-- | src/components/script/dom/node.rs | 332 |
13 files changed, 370 insertions, 343 deletions
diff --git a/src/components/main/css/matching.rs b/src/components/main/css/matching.rs index efd84f36b7e..ea1b6d30a37 100644 --- a/src/components/main/css/matching.rs +++ b/src/components/main/css/matching.rs @@ -7,9 +7,9 @@ use css::node_style::StyledNode; use layout::incremental; use layout::util::LayoutDataAccess; +use layout::wrapper::LayoutNode; use extra::arc::{Arc, RWArc}; -use script::dom::node::LayoutNode; use std::cast; use std::cell::Cell; use std::comm; diff --git a/src/components/main/css/node_style.rs b/src/components/main/css/node_style.rs index 824b50e991a..6810d9187ea 100644 --- a/src/components/main/css/node_style.rs +++ b/src/components/main/css/node_style.rs @@ -6,10 +6,10 @@ use css::node_util::NodeUtil; use layout::incremental::RestyleDamage; +use layout::wrapper::LayoutNode; use extra::arc::Arc; use style::ComputedValues; -use script::dom::node::LayoutNode; /// Node mixin providing `style` method that returns a `NodeStyle` pub trait StyledNode { diff --git a/src/components/main/css/node_util.rs b/src/components/main/css/node_util.rs index 72acf5dc08c..a84fb5cc061 100644 --- a/src/components/main/css/node_util.rs +++ b/src/components/main/css/node_util.rs @@ -4,11 +4,11 @@ use layout::incremental::RestyleDamage; use layout::util::LayoutDataAccess; +use layout::wrapper::LayoutNode; use extra::arc::Arc; use std::cast; use style::{ComputedValues, TNode}; -use script::dom::node::LayoutNode; pub trait NodeUtil { fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>; diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index c2dfa213c9e..70cda196fc9 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -15,7 +15,6 @@ use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags, ClipDisplayI use gfx::display_list::{ClipDisplayItemClass}; use gfx::font::{FontStyle, FontWeight300}; use gfx::text::text_run::TextRun; -use script::dom::node::LayoutNode; use servo_msg::constellation_msg::{FrameRectMsg, PipelineId, SubpageId}; use servo_net::image::holder::ImageHolder; use servo_net::local_image_cache::LocalImageCache; @@ -40,6 +39,7 @@ use layout::flow::Flow; use layout::flow; use layout::model::{MaybeAuto, specified}; use layout::util::OpaqueNode; +use layout::wrapper::LayoutNode; /// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In /// general, boxes do not have a simple correspondence with CSS boxes in the specification: diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index 9c6f285047e..80a8ffb8a1b 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -30,11 +30,11 @@ use layout::flow::{Flow, FlowData, MutableFlowUtils}; use layout::inline::InlineFlow; use layout::text::TextRunScanner; use layout::util::LayoutDataAccess; +use layout::wrapper::{LayoutNode, PostorderNodeMutTraversal}; use script::dom::element::{HTMLIframeElementTypeId, HTMLImageElementTypeId}; use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId}; -use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, LayoutNode}; -use script::dom::node::{PostorderNodeMutTraversal, TextNodeTypeId}; +use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, TextNodeTypeId}; use servo_util::slot::Slot; use std::util; use style::computed_values::{display, float}; diff --git a/src/components/main/layout/extra.rs b/src/components/main/layout/extra.rs index bef668c6788..c17bfc4e0e4 100644 --- a/src/components/main/layout/extra.rs +++ b/src/components/main/layout/extra.rs @@ -5,8 +5,7 @@ //! Code for managing the layout data in the DOM. use layout::util::{LayoutData, LayoutDataAccess}; - -use script::dom::node::LayoutNode; +use layout::wrapper::LayoutNode; /// Functionality useful for querying the layout-specific data on DOM nodes. pub trait LayoutAuxMethods { diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 4062e13a30c..d7d1f602f29 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -33,13 +33,13 @@ use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::float_context::{FloatContext, Invalid}; use layout::incremental::RestyleDamage; use layout::inline::InlineFlow; +use layout::wrapper::LayoutNode; use extra::dlist::{DList, DListIterator, MutDListIterator}; use extra::container::Deque; use geom::point::Point2D; use geom::rect::Rect; use gfx::display_list::{ClipDisplayItemClass, DisplayList}; -use script::dom::node::LayoutNode; use servo_util::geometry::Au; use std::cast; use std::cell::Cell; diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 2d83e0f1a6a..8f5edcfb3ab 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -17,6 +17,7 @@ use layout::flow::{PostorderFlowTraversal}; use layout::flow; use layout::incremental::{RestyleDamage}; use layout::util::{LayoutData, LayoutDataAccess, OpaqueNode}; +use layout::wrapper::LayoutNode; use extra::arc::{Arc, RWArc, MutexArc}; use geom::point::Point2D; @@ -28,7 +29,7 @@ use gfx::opts::Opts; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer}; use gfx::{render_task, color}; use script::dom::event::ReflowEvent; -use script::dom::node::{AbstractNode, ElementNodeTypeId, LayoutDataRef, LayoutNode}; +use script::dom::node::{AbstractNode, ElementNodeTypeId, LayoutDataRef}; use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId}; use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery}; use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitNowMsg, LayoutQuery}; diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs index 457b014ed9c..bc3059695d6 100644 --- a/src/components/main/layout/text.rs +++ b/src/components/main/layout/text.rs @@ -3,15 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Text layout. -use extra::arc::Arc; + use layout::box::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox}; use layout::context::LayoutContext; use layout::flow::Flow; +use extra::arc::Arc; use gfx::text::text_run::TextRun; use gfx::text::util::{CompressWhitespaceNewline, transform_text}; -use std::vec; use servo_util::range::Range; +use std::vec; /// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es. struct TextRunScanner { diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs index 36168117e0a..90c85acb1aa 100644 --- a/src/components/main/layout/util.rs +++ b/src/components/main/layout/util.rs @@ -4,9 +4,10 @@ use layout::box::Box; use layout::construct::{ConstructionResult, NoConstructionResult}; +use layout::wrapper::LayoutNode; use extra::arc::Arc; -use script::dom::node::{AbstractNode, LayoutNode}; +use script::dom::node::AbstractNode; use servo_util::range::Range; use servo_util::slot::{MutSlotRef, SlotRef}; use std::cast; diff --git a/src/components/main/layout/wrapper.rs b/src/components/main/layout/wrapper.rs new file mode 100644 index 00000000000..0f700552300 --- /dev/null +++ b/src/components/main/layout/wrapper.rs @@ -0,0 +1,352 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes +//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via +//! a simple whitelist of allowed operations. +//! +//! As a security wrapper is only as good as its whitelist, be careful when adding operations to +//! this list. The cardinal rules are: +//! +//! (1) Layout is not allowed to mutate the DOM. +//! +//! (2) Layout is not allowed to see anything with `Abstract` in the name, because it could hang +//! onto these objects and cause use-after-free. + +use extra::url::Url; +use script::dom::element::Element; +use script::dom::htmliframeelement::HTMLIFrameElement; +use script::dom::htmlimageelement::HTMLImageElement; +use script::dom::node::{AbstractNode, DocumentNodeTypeId, ElementNodeTypeId, LayoutView, Node}; +use script::dom::node::{NodeTypeId}; +use script::dom::text::Text; +use servo_msg::constellation_msg::{PipelineId, SubpageId}; +use std::cast; +use style::TNode; + +/// 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<'self> { + /// The wrapped node. + priv node: AbstractNode, + + /// Being chained to a value prevents `LayoutNode`s from escaping. + priv chain: &'self (), +} + +impl<'self> LayoutNode<'self> { + /// Creates a new layout node, scoped to the given closure. + pub unsafe fn with_layout_node<R>(node: AbstractNode, f: &fn<'a>(LayoutNode<'a>) -> R) -> R { + let heavy_iron_ball = (); + f(LayoutNode { + node: node, + chain: &heavy_iron_ball, + }) + } + + /// Creates a new layout node with the same lifetime as this layout node. + unsafe fn new_with_this_lifetime(&self, node: AbstractNode) -> LayoutNode<'self> { + LayoutNode { + node: node, + chain: self.chain, + } + } + + /// Returns the interior of this node as a `Node`. This is highly unsafe for layout to call + /// and as such is marked `unsafe`. + pub unsafe fn get<'a>(&'a self) -> &'a Node<LayoutView> { + cast::transmute(self.node.node()) + } + + /// Returns the first child of this node. + pub fn first_child(&self) -> Option<LayoutNode<'self>> { + unsafe { + self.node.first_child().map(|node| self.new_with_this_lifetime(node)) + } + } + + /// Returns the first child of this node. + pub fn last_child(&self) -> Option<LayoutNode<'self>> { + unsafe { + self.node.last_child().map(|node| self.new_with_this_lifetime(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<'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<'self> { + LayoutNodeChildrenIterator { + current_node: self.first_child(), + } + } + + /// Returns the type ID of this node. Fails if this node is borrowed mutably. + pub fn type_id(&self) -> NodeTypeId { + self.node.type_id() + } + + /// If this is an image element, returns its URL. If this is not an image element, fails. + /// + /// FIXME(pcwalton): Don't copy URLs. + pub fn image_url(&self) -> Option<Url> { + unsafe { + self.with_image_element(|image_element| { + image_element.image.as_ref().map(|url| (*url).clone()) + }) + } + } + + /// Downcasts this node to an image element and calls the given closure. + /// + /// FIXME(pcwalton): RAII. + unsafe fn with_image_element<R>(self, f: &fn(&HTMLImageElement) -> R) -> R { + if !self.node.is_image_element() { + fail!(~"node is not an image element"); + } + self.node.transmute(f) + } + + /// If this node is an iframe element, returns its pipeline and subpage IDs. If this node is + /// not an iframe element, fails. + pub fn iframe_pipeline_and_subpage_ids(&self) -> (PipelineId, SubpageId) { + unsafe { + self.with_iframe_element(|iframe_element| { + let size = iframe_element.size.unwrap(); + (size.pipeline_id, size.subpage_id) + }) + } + } + + /// Downcasts this node to an iframe element and calls the given closure. + /// + /// FIXME(pcwalton): RAII. + unsafe fn with_iframe_element<R>(self, f: &fn(&HTMLIFrameElement) -> R) -> R { + if !self.node.is_iframe_element() { + fail!(~"node is not an iframe element"); + } + self.node.transmute(f) + } + + /// Returns true if this node is a text node or false otherwise. + #[inline] + pub fn is_text(self) -> bool { + self.node.is_text() + } + + /// Returns true if this node consists entirely of ignorable whitespace and false otherwise. + /// Ignorable whitespace is defined as whitespace that would be removed per CSS 2.1 § 16.6.1. + pub fn is_ignorable_whitespace(&self) -> bool { + unsafe { + self.is_text() && self.with_text(|text| text.element.data.is_whitespace()) + } + } + + /// If this is a text node, copies out the text. If this is not a text node, fails. + /// + /// FIXME(pcwalton): Don't copy text. Atomically reference count instead. + pub fn text(&self) -> ~str { + unsafe { + self.with_text(|text| text.element.data.to_str()) + } + } + + /// Downcasts this node to a text node and calls the given closure. + /// + /// FIXME(pcwalton): RAII. + unsafe fn with_text<R>(self, f: &fn(&Text) -> R) -> R { + self.node.with_imm_text(f) + } + + /// Dumps this node tree, for debugging. + pub fn dump(&self) { + self.node.dump() + } + + /// Returns a string that describes this node, for debugging. + pub fn debug_str(&self) -> ~str { + self.node.debug_str() + } + + /// Traverses the tree in postorder. + /// + /// TODO(pcwalton): Offer a parallel version with a compatible API. + pub fn traverse_postorder<T:PostorderNodeTraversal>(self, traversal: &T) -> bool { + if traversal.should_prune(self) { + return true + } + + let mut opt_kid = self.first_child(); + loop { + match opt_kid { + None => break, + Some(kid) => { + if !kid.traverse_postorder(traversal) { + return false + } + opt_kid = kid.next_sibling() + } + } + } + + traversal.process(self) + } + + /// Traverses the tree in postorder. + /// + /// TODO(pcwalton): Offer a parallel version with a compatible API. + pub fn traverse_postorder_mut<T:PostorderNodeMutTraversal>(mut self, traversal: &mut T) + -> bool { + if traversal.should_prune(self) { + return true + } + + let mut opt_kid = self.first_child(); + loop { + match opt_kid { + None => break, + Some(kid) => { + if !kid.traverse_postorder_mut(traversal) { + return false + } + opt_kid = kid.next_sibling() + } + } + } + + traversal.process(self) + } +} + +impl<'self> TNode<Element> for LayoutNode<'self> { + fn parent_node(&self) -> Option<LayoutNode<'self>> { + unsafe { + self.node.node().parent_node.map(|node| self.new_with_this_lifetime(node)) + } + } + + fn prev_sibling(&self) -> Option<LayoutNode<'self>> { + unsafe { + self.node.node().prev_sibling.map(|node| self.new_with_this_lifetime(node)) + } + } + + fn next_sibling(&self) -> Option<LayoutNode<'self>> { + unsafe { + self.node.node().next_sibling.map(|node| self.new_with_this_lifetime(node)) + } + } + + fn is_element(&self) -> bool { + match self.node.type_id() { + ElementNodeTypeId(*) => true, + _ => false + } + } + + fn is_document(&self) -> bool { + match self.node.type_id() { + DocumentNodeTypeId(*) => true, + _ => false + } + } + + /// FIXME(pcwalton): Unsafe! + #[inline] + fn with_element<R>(&self, f: &fn(&Element) -> R) -> R { + self.node.with_imm_element(f) + } +} + +pub struct LayoutNodeChildrenIterator<'self> { + priv current_node: Option<LayoutNode<'self>>, +} + +impl<'self> Iterator<LayoutNode<'self>> for LayoutNodeChildrenIterator<'self> { + fn next(&mut self) -> Option<LayoutNode<'self>> { + let node = self.current_node; + self.current_node = do self.current_node.and_then |node| { + node.next_sibling() + }; + node + } +} + +// FIXME: Do this without precomputing a vector of refs. +// Easy for preorder; harder for postorder. +// +// FIXME(pcwalton): Parallelism! Eventually this should just be nuked. +pub struct LayoutTreeIterator<'self> { + priv nodes: ~[LayoutNode<'self>], + priv index: uint, +} + +impl<'self> LayoutTreeIterator<'self> { + fn new(nodes: ~[LayoutNode<'self>]) -> LayoutTreeIterator<'self> { + LayoutTreeIterator { + nodes: nodes, + index: 0, + } + } +} + +impl<'self> Iterator<LayoutNode<'self>> for LayoutTreeIterator<'self> { + fn next(&mut self) -> Option<LayoutNode<'self>> { + if self.index >= self.nodes.len() { + None + } else { + let v = self.nodes[self.index].clone(); + self.index += 1; + Some(v) + } + } +} + +/// FIXME(pcwalton): This is super inefficient. +fn gather_layout_nodes<'a>(cur: &LayoutNode<'a>, refs: &mut ~[LayoutNode<'a>], postorder: bool) { + if !postorder { + refs.push(cur.clone()); + } + for kid in cur.children() { + gather_layout_nodes(&kid, refs, postorder) + } + if postorder { + refs.push(cur.clone()); + } +} + +/// A bottom-up, parallelizable traversal. +pub trait PostorderNodeTraversal { + /// The operation to perform. Return true to continue or false to stop. + 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<'a>(&'a self, _node: LayoutNode<'a>) -> bool { + false + } +} + +/// A bottom-up, parallelizable traversal. +pub trait PostorderNodeMutTraversal { + /// The operation to perform. Return true to continue or false to stop. + 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<'a>(&'a self, _node: LayoutNode<'a>) -> bool { + false + } +} + diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc index e6584df38c7..cc451b4e7ce 100755 --- a/src/components/main/servo.rc +++ b/src/components/main/servo.rc @@ -88,6 +88,7 @@ pub mod layout { pub mod text; pub mod util; pub mod incremental; + pub mod wrapper; mod extra; } diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index cbc7595b609..2d4e15ef8c8 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -14,14 +14,12 @@ use dom::documenttype::DocumentType; use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId}; use dom::element::{HTMLAnchorElementTypeId, HTMLStyleElementTypeId}; use dom::eventtarget::{AbstractEventTarget, EventTarget, NodeTypeId}; -use dom::nodelist::{NodeList}; -use dom::htmlimageelement::HTMLImageElement; use dom::htmliframeelement::HTMLIFrameElement; +use dom::htmlimageelement::HTMLImageElement; +use dom::nodelist::{NodeList}; use dom::text::Text; -use extra::url::Url; use js::jsapi::{JSObject, JSContext}; -use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_util::slot::{MutSlotRef, Slot, SlotRef}; use std::cast::transmute; use std::cast; @@ -30,198 +28,6 @@ use std::util; use style::TNode; // -// Layout's immutable, sanitized view of nodes. -// - -/// 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<'self> { - /// The wrapped node. - priv node: AbstractNode, - - /// Being chained to a `HEAVY_IRON_BALL` prevents `LayoutNode`s from escaping. - priv chain: &'self (), -} - -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<'static> { - LayoutNode { - node: node, - chain: &HEAVY_IRON_BALL, - } - } - - /// Returns the interior of this node as a `Node`. This is highly unsafe for layout to call - /// and as such is marked `unsafe`. - pub unsafe fn get<'a>(&'a self) -> &'a Node<LayoutView> { - cast::transmute(self.node.node()) - } - - /// Returns the first child of this node. - pub fn first_child(&self) -> Option<LayoutNode<'self>> { - self.node.first_child().map(|node| LayoutNode::new(node)) - } - - /// Returns the first child of this node. - pub fn last_child(&self) -> Option<LayoutNode<'self>> { - 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<'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<'self> { - LayoutNodeChildrenIterator { - current_node: self.first_child(), - } - } - - /// Returns the type ID of this node. Fails if this node is borrowed mutably. - pub fn type_id(&self) -> NodeTypeId { - self.node.type_id() - } - - /// If this is an image element, returns its URL. If this is not an image element, fails. - /// - /// FIXME(pcwalton): Don't copy URLs. - pub fn image_url(&self) -> Option<Url> { - self.with_image_element(|image_element| { - image_element.image.as_ref().map(|url| (*url).clone()) - }) - } - - /// Downcasts this node to an image element and calls the given closure. - /// - /// NB: Do not make this public, as layout will be able to do unsafe things with `AbstractNode` - /// otherwise. - /// - /// FIXME(pcwalton): RAII. - /*PRIVATE-FOR-SECURITY-REASONS*/ fn with_image_element<R>( - self, - f: &fn(&HTMLImageElement) -> R) - -> R { - if !self.node.is_image_element() { - fail!(~"node is not an image element"); - } - self.node.transmute(f) - } - - /// If this node is an iframe element, returns its pipeline and subpage IDs. If this node is - /// not an iframe element, fails. - pub fn iframe_pipeline_and_subpage_ids(&self) -> (PipelineId, SubpageId) { - self.with_iframe_element(|iframe_element| { - let size = iframe_element.size.unwrap(); - (size.pipeline_id, size.subpage_id) - }) - } - - /// Downcasts this node to an iframe element and calls the given closure. - /// - /// NB: Do not make this public, as layout will be able to do unsafe things with `AbstractNode` - /// otherwise. - /// - /// FIXME(pcwalton): RAII. - /*PRIVATE-FOR-SECURITY-REASONS*/ fn with_iframe_element<R>( - self, - f: &fn(&HTMLIFrameElement) -> R) - -> R { - if !self.node.is_iframe_element() { - fail!(~"node is not an iframe element"); - } - self.node.transmute(f) - } - - /// Returns true if this node is a text node or false otherwise. - #[inline] - pub fn is_text(self) -> bool { - self.node.is_text() - } - - /// Returns true if this node consists entirely of ignorable whitespace and false otherwise. - /// Ignorable whitespace is defined as whitespace that would be removed per CSS 2.1 § 16.6.1. - pub fn is_ignorable_whitespace(&self) -> bool { - self.is_text() && self.with_text(|text| text.element.data.is_whitespace()) - } - - /// If this is a text node, copies out the text. If this is not a text node, fails. - /// - /// FIXME(pcwalton): Don't copy text. Atomically reference count instead. - pub fn text(&self) -> ~str { - self.with_text(|text| text.element.data.to_str()) - } - - /// Downcasts this node to a text node and calls the given closure. - /// - /// NB: Do not make this public, as layout will be able to do unsafe things with `AbstractNode` - /// otherwise. - /// - /// FIXME(pcwalton): RAII. - /*PRIVATE-FOR-SECURITY-REASONS*/ fn with_text<R>(self, f: &fn(&Text) -> R) -> R { - self.node.with_imm_text(f) - } - - /// Dumps this node tree, for debugging. - pub fn dump(&self) { - self.node.dump() - } - - /// Returns a string that describes this node, for debugging. - pub fn debug_str(&self) -> ~str { - self.node.debug_str() - } -} - -impl<'self> TNode<Element> for LayoutNode<'self> { - fn parent_node(&self) -> Option<LayoutNode<'self>> { - self.node.node().parent_node.map(|node| LayoutNode::new(node)) - } - - fn prev_sibling(&self) -> Option<LayoutNode<'self>> { - self.node.node().prev_sibling.map(|node| LayoutNode::new(node)) - } - - fn next_sibling(&self) -> Option<LayoutNode<'self>> { - self.node.node().next_sibling.map(|node| LayoutNode::new(node)) - } - - fn is_element(&self) -> bool { - match self.node.type_id() { - ElementNodeTypeId(*) => true, - _ => false - } - } - - fn is_document(&self) -> bool { - match self.node.type_id() { - DocumentNodeTypeId(*) => true, - _ => false - } - } - - /// FIXME(pcwalton): Unsafe! - #[inline] - fn with_element<R>(&self, f: &fn(&Element) -> R) -> R { - self.node.with_imm_element(f) - } -} - -// // The basic Node structure // @@ -851,20 +657,6 @@ impl Iterator<AbstractNode> for AbstractNodeChildrenIterator { } } -pub struct LayoutNodeChildrenIterator<'self> { - priv current_node: Option<LayoutNode<'self>>, -} - -impl<'self> Iterator<LayoutNode<'self>> for LayoutNodeChildrenIterator<'self> { - fn next(&mut self) -> Option<LayoutNode<'self>> { - let node = self.current_node; - self.current_node = do self.current_node.and_then |node| { - node.next_sibling() - }; - node - } -} - pub struct AncestorIterator { priv current: Option<AbstractNode>, } @@ -922,49 +714,6 @@ fn gather_abstract_nodes(cur: &AbstractNode, refs: &mut ~[AbstractNode], postord } } -// FIXME: Do this without precomputing a vector of refs. -// Easy for preorder; harder for postorder. -// -// FIXME(pcwalton): Parallelism! Eventually this should just be nuked. -pub struct LayoutTreeIterator<'self> { - priv nodes: ~[LayoutNode<'self>], - priv index: uint, -} - -impl<'self> LayoutTreeIterator<'self> { - fn new(nodes: ~[LayoutNode<'self>]) -> LayoutTreeIterator<'self> { - LayoutTreeIterator { - nodes: nodes, - index: 0, - } - } -} - -impl<'self> Iterator<LayoutNode<'self>> for LayoutTreeIterator<'self> { - fn next(&mut self) -> Option<LayoutNode<'self>> { - if self.index >= self.nodes.len() { - None - } else { - let v = self.nodes[self.index].clone(); - self.index += 1; - Some(v) - } - } -} - -/// FIXME(pcwalton): This is super inefficient. -fn gather_layout_nodes<'a>(cur: &LayoutNode<'a>, refs: &mut ~[LayoutNode<'a>], postorder: bool) { - if !postorder { - refs.push(cur.clone()); - } - for kid in cur.children() { - gather_layout_nodes(&kid, refs, postorder) - } - if postorder { - refs.push(cur.clone()); - } -} - impl AbstractNode { /// Iterates over all ancestors of this node. pub fn ancestors(&self) -> AncestorIterator { @@ -1629,80 +1378,3 @@ impl Reflectable for Node<ScriptView> { } } -/// A bottom-up, parallelizable traversal. -pub trait PostorderNodeTraversal { - /// The operation to perform. Return true to continue or false to stop. - 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<'a>(&'a self, _node: LayoutNode<'a>) -> bool { - false - } -} - -/// A bottom-up, parallelizable traversal. -pub trait PostorderNodeMutTraversal { - /// The operation to perform. Return true to continue or false to stop. - 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<'a>(&'a self, _node: LayoutNode<'a>) -> bool { - false - } -} - -impl<'self> LayoutNode<'self> { - /// Traverses the tree in postorder. - /// - /// TODO(pcwalton): Offer a parallel version with a compatible API. - pub fn traverse_postorder<T:PostorderNodeTraversal>(self, traversal: &T) -> bool { - if traversal.should_prune(self) { - return true - } - - let mut opt_kid = self.first_child(); - loop { - match opt_kid { - None => break, - Some(kid) => { - if !kid.traverse_postorder(traversal) { - return false - } - opt_kid = kid.next_sibling() - } - } - } - - traversal.process(self) - } - - /// Traverses the tree in postorder. - /// - /// TODO(pcwalton): Offer a parallel version with a compatible API. - pub fn traverse_postorder_mut<T:PostorderNodeMutTraversal>(mut self, traversal: &mut T) - -> bool { - if traversal.should_prune(self) { - return true - } - - let mut opt_kid = self.first_child(); - loop { - match opt_kid { - None => break, - Some(kid) => { - if !kid.traverse_postorder_mut(traversal) { - return false - } - opt_kid = kid.next_sibling() - } - } - } - - traversal.process(self) - } - -} |