dom: Document the node structure better and remove the node pointer stitching routines

This commit is contained in:
Patrick Walton 2013-05-06 19:26:52 -07:00
parent 58679216b3
commit 6a6cad1e39
4 changed files with 94 additions and 119 deletions

View file

@ -2,9 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
// The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
//
use content::content_task::global_content; use content::content_task::global_content;
use dom::bindings::codegen; use dom::bindings::codegen;
@ -36,28 +34,47 @@ pub struct AbstractNode {
} }
impl Eq for AbstractNode { impl Eq for AbstractNode {
fn eq(&self, other: &AbstractNode) -> bool { self.obj == other.obj } fn eq(&self, other: &AbstractNode) -> bool {
fn ne(&self, other: &AbstractNode) -> bool { self.obj != other.obj } self.obj == other.obj
}
fn ne(&self, other: &AbstractNode) -> bool {
self.obj != other.obj
}
} }
/// An HTML node.
pub struct Node { pub struct Node {
/// The JavaScript wrapper for this node.
wrapper: WrapperCache, wrapper: WrapperCache,
/// The type of node that this is.
type_id: NodeTypeId, type_id: NodeTypeId,
abstract: Option<AbstractNode>, abstract: Option<AbstractNode>,
/// The parent of this node.
parent_node: Option<AbstractNode>, parent_node: Option<AbstractNode>,
/// The first child of this node.
first_child: Option<AbstractNode>, first_child: Option<AbstractNode>,
/// The last child of this node.
last_child: Option<AbstractNode>, last_child: Option<AbstractNode>,
/// The next sibling of this node.
next_sibling: Option<AbstractNode>, next_sibling: Option<AbstractNode>,
/// The previous sibling of this node.
prev_sibling: Option<AbstractNode>, prev_sibling: Option<AbstractNode>,
/// The document that this node belongs to.
owner_doc: Option<@mut Document>, owner_doc: Option<@mut Document>,
// You must not touch this if you are not layout. /// Layout information. You must not touch this if you are not layout.
priv layout_data: Option<@mut LayoutData> priv layout_data: Option<@mut LayoutData>
} }
/// The different types of nodes.
#[deriving(Eq)] #[deriving(Eq)]
pub enum NodeTypeId { pub enum NodeTypeId {
DoctypeNodeTypeId, DoctypeNodeTypeId,
@ -70,12 +87,17 @@ pub enum NodeTypeId {
// Auxiliary layout data // Auxiliary layout data
// //
/// Data that layout associates with a node.
pub struct LayoutData { pub struct LayoutData {
/// The results of CSS styling for this node.
style: Option<CompleteSelectResults>, style: Option<CompleteSelectResults>,
/// The CSS flow that this node is associated with.
flow: Option<FlowContext>, flow: Option<FlowContext>,
} }
impl LayoutData { impl LayoutData {
/// Creates new layout data.
pub fn new() -> LayoutData { pub fn new() -> LayoutData {
LayoutData { LayoutData {
style: None, style: None,
@ -88,6 +110,7 @@ impl LayoutData {
// Basic node types // Basic node types
// //
/// The `DOCTYPE` tag.
pub struct Doctype { pub struct Doctype {
parent: Node, parent: Node,
name: ~str, name: ~str,
@ -97,6 +120,7 @@ pub struct Doctype {
} }
impl Doctype { impl Doctype {
/// Creates a new `DOCTYPE` tag.
pub fn new(name: ~str, pub fn new(name: ~str,
public_id: Option<~str>, public_id: Option<~str>,
system_id: Option<~str>, system_id: Option<~str>,
@ -112,11 +136,13 @@ impl Doctype {
} }
} }
/// An HTML comment.
pub struct Comment { pub struct Comment {
parent: CharacterData, parent: CharacterData,
} }
impl Comment { impl Comment {
/// Creates a new HTML comment.
pub fn new(text: ~str) -> Comment { pub fn new(text: ~str) -> Comment {
Comment { Comment {
parent: CharacterData::new(CommentNodeTypeId, text) parent: CharacterData::new(CommentNodeTypeId, text)
@ -124,11 +150,13 @@ impl Comment {
} }
} }
/// An HTML text node.
pub struct Text { pub struct Text {
parent: CharacterData, parent: CharacterData,
} }
impl Text { impl Text {
/// Creates a new HTML text node.
pub fn new(text: ~str) -> Text { pub fn new(text: ~str) -> Text {
Text { Text {
parent: CharacterData::new(TextNodeTypeId, text) parent: CharacterData::new(TextNodeTypeId, text)
@ -187,118 +215,56 @@ impl TreeNodeRef<Node> for AbstractNode {
} }
} }
pub impl AbstractNode { impl AbstractNode {
// Convenience accessors // Convenience accessors
/// Returns the type ID of this node. /// Returns the type ID of this node. Fails if this node is borrowed mutably.
fn type_id(self) -> NodeTypeId { self.with_imm_node(|n| n.type_id) } pub fn type_id(self) -> NodeTypeId {
self.with_imm_node(|n| n.type_id)
}
/// Returns the parent node of this node. /// Returns the parent node of this node. Fails if this node is borrowed mutably.
fn parent_node(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.parent_node) } pub fn parent_node(self) -> Option<AbstractNode> {
self.with_imm_node(|n| n.parent_node)
}
/// Returns the first child of this node. /// Returns the first child of this node. Fails if this node is borrowed mutably.
fn first_child(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.first_child) } pub fn first_child(self) -> Option<AbstractNode> {
self.with_imm_node(|n| n.first_child)
}
/// Returns the last child of this node. /// Returns the last child of this node. Fails if this node is borrowed mutably.
fn last_child(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.last_child) } pub fn last_child(self) -> Option<AbstractNode> {
self.with_imm_node(|n| n.last_child)
}
/// Returns the previous sibling of this node. /// Returns the previous sibling of this node. Fails if this node is borrowed mutably.
fn prev_sibling(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.prev_sibling) } pub fn prev_sibling(self) -> Option<AbstractNode> {
self.with_imm_node(|n| n.prev_sibling)
}
/// Returns the next sibling of this node. /// Returns the next sibling of this node. Fails if this node is borrowed mutably.
fn next_sibling(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.next_sibling) } pub fn next_sibling(self) -> Option<AbstractNode> {
self.with_imm_node(|n| n.next_sibling)
}
// NB: You must not call these if you are not layout. We should do something with scoping to // NB: You must not call these if you are not layout. We should do something with scoping to
// ensure this. // ensure this.
fn layout_data(self) -> @mut LayoutData { pub fn layout_data(self) -> @mut LayoutData {
self.with_imm_node(|n| n.layout_data.get()) self.with_imm_node(|n| n.layout_data.get())
} }
fn has_layout_data(self) -> bool { pub fn has_layout_data(self) -> bool {
self.with_imm_node(|n| n.layout_data.is_some()) self.with_imm_node(|n| n.layout_data.is_some())
} }
fn set_layout_data(self, data: @mut LayoutData) { pub fn set_layout_data(self, data: @mut LayoutData) {
self.with_mut_node(|n| n.layout_data = Some(data)) self.with_mut_node(|n| n.layout_data = Some(data))
} }
//
// Tree operations
//
// FIXME: Fold this into util::tree.
//
fn is_leaf(self) -> bool { self.first_child().is_none() }
// Invariant: `child` is disconnected from the document.
fn append_child(self, child: AbstractNode) {
assert!(self != child);
do self.with_mut_node |parent_n| {
do child.with_mut_node |child_n| {
assert!(child_n.parent_node.is_none());
assert!(child_n.prev_sibling.is_none());
assert!(child_n.next_sibling.is_none());
child_n.parent_node = Some(self);
match parent_n.last_child {
None => parent_n.first_child = Some(child),
Some(last_child) => {
do last_child.with_mut_node |last_child_n| {
assert!(last_child_n.next_sibling.is_none());
last_child_n.next_sibling = Some(child);
}
}
}
child_n.prev_sibling = parent_n.last_child;
parent_n.last_child = Some(child);
}
}
}
//
// Tree traversal
//
// FIXME: Fold this into util::tree.
//
fn each_child(self, f: &fn(AbstractNode) -> bool) {
let mut current_opt = self.first_child();
while !current_opt.is_none() {
let current = current_opt.get();
if !f(current) {
break;
}
current_opt = current.next_sibling();
}
}
fn traverse_preorder(self, f: &fn(AbstractNode) -> bool) -> bool {
if !f(self) {
return false;
}
for self.each_child |kid| {
if !kid.traverse_preorder(f) {
return false;
}
}
true
}
fn traverse_postorder(self, f: &fn(AbstractNode) -> bool) -> bool {
for self.each_child |kid| {
if !kid.traverse_postorder(f) {
return false;
}
}
f(self)
}
// //
// Downcasting borrows // Downcasting borrows
// //
fn transmute<T, R>(self, f: &fn(&T) -> R) -> R { pub fn transmute<T, R>(self, f: &fn(&T) -> R) -> R {
unsafe { unsafe {
let node_box: *mut bindings::utils::rust_box<Node> = transmute(self.obj); let node_box: *mut bindings::utils::rust_box<Node> = transmute(self.obj);
let node = &mut (*node_box).payload; let node = &mut (*node_box).payload;
@ -311,7 +277,7 @@ pub impl AbstractNode {
} }
} }
fn transmute_mut<T, R>(self, f: &fn(&mut T) -> R) -> R { pub fn transmute_mut<T, R>(self, f: &fn(&mut T) -> R) -> R {
unsafe { unsafe {
let node_box: *mut bindings::utils::rust_box<Node> = transmute(self.obj); let node_box: *mut bindings::utils::rust_box<Node> = transmute(self.obj);
let node = &mut (*node_box).payload; let node = &mut (*node_box).payload;
@ -324,25 +290,27 @@ pub impl AbstractNode {
} }
} }
fn with_imm_node<R>(self, f: &fn(&Node) -> R) -> R { pub fn with_imm_node<R>(self, f: &fn(&Node) -> R) -> R {
self.transmute(f) self.transmute(f)
} }
fn with_mut_node<R>(self, f: &fn(&mut Node) -> R) -> R { pub fn with_mut_node<R>(self, f: &fn(&mut Node) -> R) -> R {
self.transmute_mut(f) self.transmute_mut(f)
} }
fn is_text(self) -> bool { self.type_id() == TextNodeTypeId } pub fn is_text(self) -> bool {
self.type_id() == TextNodeTypeId
}
// FIXME: This should be doing dynamic borrow checking for safety. // FIXME: This should be doing dynamic borrow checking for safety.
fn with_imm_text<R>(self, f: &fn(&Text) -> R) -> R { pub fn with_imm_text<R>(self, f: &fn(&Text) -> R) -> R {
if !self.is_text() { if !self.is_text() {
fail!(~"node is not text"); fail!(~"node is not text");
} }
self.transmute(f) self.transmute(f)
} }
fn is_element(self) -> bool { pub fn is_element(self) -> bool {
match self.type_id() { match self.type_id() {
ElementNodeTypeId(*) => true, ElementNodeTypeId(*) => true,
_ => false _ => false
@ -350,7 +318,7 @@ pub impl AbstractNode {
} }
// FIXME: This should be doing dynamic borrow checking for safety. // FIXME: This should be doing dynamic borrow checking for safety.
fn with_imm_element<R>(self, f: &fn(&Element) -> R) -> R { pub fn with_imm_element<R>(self, f: &fn(&Element) -> R) -> R {
if !self.is_element() { if !self.is_element() {
fail!(~"node is not an element"); fail!(~"node is not an element");
} }
@ -358,40 +326,40 @@ pub impl AbstractNode {
} }
// FIXME: This should be doing dynamic borrow checking for safety. // FIXME: This should be doing dynamic borrow checking for safety.
fn as_mut_element<R>(self, f: &fn(&mut Element) -> R) -> R { pub fn as_mut_element<R>(self, f: &fn(&mut Element) -> R) -> R {
if !self.is_element() { if !self.is_element() {
fail!(~"node is not an element"); fail!(~"node is not an element");
} }
self.transmute_mut(f) self.transmute_mut(f)
} }
fn is_image_element(self) -> bool { pub fn is_image_element(self) -> bool {
self.type_id() == ElementNodeTypeId(HTMLImageElementTypeId) self.type_id() == ElementNodeTypeId(HTMLImageElementTypeId)
} }
fn with_imm_image_element<R>(self, f: &fn(&HTMLImageElement) -> R) -> R { pub fn with_imm_image_element<R>(self, f: &fn(&HTMLImageElement) -> R) -> R {
if !self.is_image_element() { if !self.is_image_element() {
fail!(~"node is not an image element"); fail!(~"node is not an image element");
} }
self.transmute(f) self.transmute(f)
} }
fn with_mut_image_element<R>(self, f: &fn(&mut HTMLImageElement) -> R) -> R { pub fn with_mut_image_element<R>(self, f: &fn(&mut HTMLImageElement) -> R) -> R {
if !self.is_image_element() { if !self.is_image_element() {
fail!(~"node is not an image element"); fail!(~"node is not an image element");
} }
self.transmute_mut(f) self.transmute_mut(f)
} }
fn is_style_element(self) -> bool { pub fn is_style_element(self) -> bool {
self.type_id() == ElementNodeTypeId(HTMLStyleElementTypeId) self.type_id() == ElementNodeTypeId(HTMLStyleElementTypeId)
} }
unsafe fn raw_object(self) -> *mut Node { pub unsafe fn raw_object(self) -> *mut Node {
self.obj self.obj
} }
fn from_raw(raw: *mut Node) -> AbstractNode { pub fn from_raw(raw: *mut Node) -> AbstractNode {
AbstractNode { AbstractNode {
obj: raw obj: raw
} }

View file

@ -14,8 +14,9 @@ use util::task::spawn_conversation;
use core::cell::Cell; use core::cell::Cell;
use core::comm::{Chan, Port, SharedChan}; use core::comm::{Chan, Port, SharedChan};
use core::str::eq_slice; use core::str::eq_slice;
use servo_util::url::make_url;
use hubbub::hubbub; use hubbub::hubbub;
use servo_util::tree::TreeUtils;
use servo_util::url::make_url;
use std::net::url::Url; use std::net::url::Url;
use std::net::url; use std::net::url;
@ -336,7 +337,7 @@ pub fn parse_html(url: Url,
debug!("append child %x %x", cast::transmute(parent), cast::transmute(child)); debug!("append child %x %x", cast::transmute(parent), cast::transmute(child));
let parent: AbstractNode = NodeWrapping::from_hubbub_node(parent); let parent: AbstractNode = NodeWrapping::from_hubbub_node(parent);
let child: AbstractNode = NodeWrapping::from_hubbub_node(child); let child: AbstractNode = NodeWrapping::from_hubbub_node(child);
parent.append_child(child); parent.add_child(child);
append_hook(parent, child); append_hook(parent, child);
} }
child child

View file

@ -2,12 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** //! Code for managing the layout data in the DOM.
Code for managing the DOM aux pointer
*/
use dom::node::{AbstractNode, LayoutData}; use dom::node::{AbstractNode, LayoutData};
use servo_util::tree::TreeUtils;
pub trait LayoutAuxMethods { pub trait LayoutAuxMethods {
fn initialize_layout_data(self) -> Option<@mut LayoutData>; fn initialize_layout_data(self) -> Option<@mut LayoutData>;
fn initialize_style_for_subtree(self, refs: &mut ~[@mut LayoutData]); fn initialize_style_for_subtree(self, refs: &mut ~[@mut LayoutData]);
@ -36,5 +36,5 @@ impl LayoutAuxMethods for AbstractNode {
} }
}; };
} }
} }

View file

@ -121,6 +121,8 @@ impl BoxGenerator {
// depending on flow, make a box for this node. // depending on flow, make a box for this node.
match self.flow { match self.flow {
InlineFlow(inline) => { InlineFlow(inline) => {
use servo_util::tree::TreeUtils; // For `is_leaf()`.
let mut inline = &mut *inline; let mut inline = &mut *inline;
let node_range_start = inline.boxes.len(); let node_range_start = inline.boxes.len();
self.range_stack.push(node_range_start); self.range_stack.push(node_range_start);
@ -336,9 +338,13 @@ pub impl LayoutTreeBuilder {
debug!("point b: %s", cur_node.debug_str()); debug!("point b: %s", cur_node.debug_str());
// recurse on child nodes. // recurse on child nodes.
{
use servo_util::tree::TreeUtils; // For `each_child()`.
for cur_node.each_child |child_node| { for cur_node.each_child |child_node| {
self.construct_recursively(layout_ctx, child_node, &mut this_ctx); self.construct_recursively(layout_ctx, child_node, &mut this_ctx);
} }
}
this_ctx.default_collector.pop_node(layout_ctx, self, cur_node); this_ctx.default_collector.pop_node(layout_ctx, self, cur_node);
self.simplify_children_of_flow(layout_ctx, &this_ctx); self.simplify_children_of_flow(layout_ctx, &this_ctx);