aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-12-17 13:41:45 -0800
committerPatrick Walton <pcwalton@mimiga.net>2013-12-17 18:07:41 -0800
commit9e2b63ddd3c3881e3a6a9a444e47fb87b8bbbcf2 (patch)
treed4adb1ac4b1efb877a4a6f30bb4950f1704f5e9e /src
parentc506e52c7c6e80ea875e9d1ce95597cf3bc8bcc1 (diff)
downloadservo-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.rs2
-rw-r--r--src/components/main/css/node_style.rs2
-rw-r--r--src/components/main/css/node_util.rs2
-rw-r--r--src/components/main/layout/box.rs2
-rw-r--r--src/components/main/layout/construct.rs4
-rw-r--r--src/components/main/layout/extra.rs3
-rw-r--r--src/components/main/layout/flow.rs2
-rw-r--r--src/components/main/layout/layout_task.rs3
-rw-r--r--src/components/main/layout/text.rs5
-rw-r--r--src/components/main/layout/util.rs3
-rw-r--r--src/components/main/layout/wrapper.rs352
-rwxr-xr-xsrc/components/main/servo.rc1
-rw-r--r--src/components/script/dom/node.rs332
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)
- }
-
-}