diff --git a/src/servo/content/content_task.rs b/src/servo/content/content_task.rs index b17b0e26fbf..12baf34a56c 100644 --- a/src/servo/content/content_task.rs +++ b/src/servo/content/content_task.rs @@ -5,7 +5,7 @@ tasks. use dom::bindings::utils::rust_box; use dom::document::Document; -use dom::node::{Node, NodeScope, define_bindings}; +use dom::node::define_bindings; use dom::event::{Event, ResizeEvent, ReflowEvent}; use dom::window::Window; use layout::layout_task; @@ -90,7 +90,6 @@ pub struct Content { event_port: comm::Port, event_chan: comm::SharedChan, - scope: NodeScope, jsrt: jsrt, cx: @Cx, @@ -135,19 +134,18 @@ pub fn Content(layout_task: LayoutTask, event_port: event_port, event_chan: event_chan, - scope: NodeScope(), - jsrt: jsrt, - cx: cx, + jsrt : jsrt, + cx : cx, - document: None, - window: None, - doc_url: None, - window_size: Size2D(800u, 600u), + document : None, + window : None, + doc_url : None, + window_size : Size2D(800u, 600u), - resource_task: resource_task, - compartment: compartment, + resource_task : resource_task, + compartment : compartment, - damage: MatchSelectorsDamage, + damage : MatchSelectorsDamage, }; cx.set_cx_private(ptr::to_unsafe_ptr(&*content) as *()); @@ -184,8 +182,7 @@ impl Content { // Note: we can parse the next document in parallel // with any previous documents. - let result = html::hubbub_html_parser::parse_html(self.scope, - copy url, + let result = html::hubbub_html_parser::parse_html(copy url, self.resource_task.clone(), self.image_cache_task.clone()); @@ -206,7 +203,7 @@ impl Content { let js_scripts = result.js_port.recv(); debug!("js_scripts: %?", js_scripts); - let document = Document(root, self.scope); + let document = Document(root); let window = Window(self.control_chan.clone()); self.damage.add(MatchSelectorsDamage); @@ -272,13 +269,9 @@ impl Content { Sends a ping to layout and waits for the response (i.e., it has finished any pending layout request messages). */ - fn join_layout() { - assert self.scope.is_reader_forked() == self.layout_join_port.is_some(); - - if self.scope.is_reader_forked() { - + fn join_layout(&self) { + if self.layout_join_port.is_some() { let join_port = replace(&mut self.layout_join_port, None); - match join_port { Some(ref join_port) => { if !join_port.peek() { @@ -289,8 +282,6 @@ impl Content { } None => fail!(~"reader forked but no join port?") } - - self.scope.reader_joined(); } } @@ -312,7 +303,7 @@ impl Content { // Send new document and relevant styles to layout - let data = BuildData { + let data = ~BuildData { node: document.root, url: copy *doc_url, dom_event_chan: self.event_chan.clone(), @@ -323,10 +314,6 @@ impl Content { self.layout_task.send(BuildMsg(data)); - // Indicate that reader was forked so any further - // changes will be isolated. - self.scope.reader_forked(); - debug!("content: layout forked"); } diff --git a/src/servo/css/matching.rs b/src/servo/css/matching.rs index 25e5a530973..2646aca4aad 100644 --- a/src/servo/css/matching.rs +++ b/src/servo/css/matching.rs @@ -2,7 +2,7 @@ use css::node_util::NodeUtil; use css::select_handler::NodeSelectHandler; -use dom::node::{Node, NodeTree}; +use dom::node::AbstractNode; use layout::context::LayoutContext; use newcss::complete::CompleteSelectResults; use newcss::select::{SelectCtx, SelectResults}; @@ -10,40 +10,34 @@ use newcss::select::{SelectCtx, SelectResults}; use std::arc::{ARC, get, clone}; pub trait MatchMethods { - fn restyle_subtree(select_ctx: &SelectCtx); + fn restyle_subtree(&self, select_ctx: &SelectCtx); } -impl MatchMethods for Node { +impl MatchMethods for AbstractNode { /** * Performs CSS selector matching on a subtree. - + * * This is, importantly, the function that updates the layout data for * the node (the reader-auxiliary box in the COW model) with the * computed style. */ - fn restyle_subtree(select_ctx: &SelectCtx) { - + fn restyle_subtree(&self, select_ctx: &SelectCtx) { // Only elements have styles if self.is_element() { - let select_handler = NodeSelectHandler { - node: self - }; - let incomplete_results = select_ctx.select_style(&self, &select_handler); + let select_handler = NodeSelectHandler { node: *self }; + let incomplete_results = select_ctx.select_style(self, &select_handler); // Combine this node's results with its parent's to resolve all inherited values - let complete_results = compose_results(&self, incomplete_results); + let complete_results = compose_results(*self, incomplete_results); self.set_css_select_results(complete_results); } - let mut i = 0u; - - for NodeTree.each_child(&self) |kid| { - i = i + 1u; + for self.each_child |kid| { kid.restyle_subtree(select_ctx); } } } -fn compose_results(node: &Node, results: SelectResults) -> CompleteSelectResults { +fn compose_results(node: AbstractNode, results: SelectResults) -> CompleteSelectResults { match find_parent_element_node(node) { None => CompleteSelectResults::new_root(results), Some(parent_node) => { @@ -53,17 +47,11 @@ fn compose_results(node: &Node, results: SelectResults) -> CompleteSelectResults } } -fn find_parent_element_node(node: &Node) -> Option { - use util::tree::parent; - - match parent(&NodeTree, node) { - Some(ref parent) => { - if parent.is_element() { - Some(*parent) - } else { - find_parent_element_node(parent) - } - } - None => None +fn find_parent_element_node(node: AbstractNode) -> Option { + match node.parent_node() { + Some(parent) if parent.is_element() => Some(parent), + Some(parent) => find_parent_element_node(parent), + None => None, } } + diff --git a/src/servo/css/node_style.rs b/src/servo/css/node_style.rs index 1ca324c1967..c62d141ac8f 100644 --- a/src/servo/css/node_style.rs +++ b/src/servo/css/node_style.rs @@ -1,7 +1,7 @@ // Style retrieval from DOM elements. use css::node_util::NodeUtil; -use dom::node::Node; +use dom::node::AbstractNode; use newcss::complete::CompleteStyle; /// Node mixin providing `style` method that returns a `NodeStyle` @@ -9,7 +9,7 @@ pub trait StyledNode { fn style(&self) -> CompleteStyle/&self; } -impl StyledNode for Node { +impl StyledNode for AbstractNode { fn style(&self) -> CompleteStyle/&self { assert self.is_element(); // Only elements can have styles let results = self.get_css_select_results(); diff --git a/src/servo/css/node_util.rs b/src/servo/css/node_util.rs index acc32027377..2eb0ffddb94 100644 --- a/src/servo/css/node_util.rs +++ b/src/servo/css/node_util.rs @@ -1,13 +1,14 @@ -use dom::node::Node; +use dom::node::AbstractNode; use newcss::complete::CompleteSelectResults; -use std::cell::Cell; + +use core::cast::transmute; pub trait NodeUtil { - fn get_css_select_results() -> &self/CompleteSelectResults; - fn set_css_select_results(decl : CompleteSelectResults); + fn get_css_select_results(self) -> &self/CompleteSelectResults; + fn set_css_select_results(self, decl: CompleteSelectResults); } -impl NodeUtil for Node { +impl NodeUtil for AbstractNode { /** * Provides the computed style for the given node. If CSS selector * Returns the style results for the given node. If CSS selector @@ -15,25 +16,23 @@ impl NodeUtil for Node { * FIXME: This isn't completely memory safe since the style is * stored in a box that can be overwritten */ - fn get_css_select_results() -> &self/CompleteSelectResults { - if !self.has_aux() { + fn get_css_select_results(self) -> &self/CompleteSelectResults { + if !self.has_layout_data() { fail!(~"style() called on a node without aux data!"); } - unsafe { &*self.aux( |x| { - match x.style { - Some(ref style) => ptr::to_unsafe_ptr(style), - None => fail!(~"style() called on node without a style!") - } - })} - } - /** - Update the computed style of an HTML element with a style specified by CSS. - */ - fn set_css_select_results(decl : CompleteSelectResults) { - let decl = Cell(decl); - do self.aux |data| { - data.style = Some(decl.take()) + match self.layout_data().style { + None => fail!(~"style() called on node without a style!"), + Some(ref style) => unsafe { transmute(style) } } } + + /// Update the computed style of an HTML element with a style specified by CSS. + fn set_css_select_results(self, decl: CompleteSelectResults) { + if !self.has_layout_data() { + fail!(~"set_css_select_results() called on a node without aux data!"); + } + + self.layout_data().style = Some(decl); + } } diff --git a/src/servo/css/node_void_ptr.rs b/src/servo/css/node_void_ptr.rs index 686265e5cd6..f436164b0ac 100644 --- a/src/servo/css/node_void_ptr.rs +++ b/src/servo/css/node_void_ptr.rs @@ -1,19 +1,24 @@ //! CSS library requires that DOM nodes be convertable to *c_void through this trait -use dom::node::Node; +use dom::node::AbstractNode; + +use core::cast; // FIXME: Rust #3908. rust-css can't reexport VoidPtrLike extern mod netsurfcss; use css::node_void_ptr::netsurfcss::util::VoidPtrLike; -impl VoidPtrLike for Node { - static fn from_void_ptr(node: *libc::c_void) -> Node { +impl VoidPtrLike for AbstractNode { + static fn from_void_ptr(node: *libc::c_void) -> AbstractNode { assert node.is_not_null(); - unsafe { cast::reinterpret_cast(&node) } + unsafe { + cast::transmute(node) + } } fn to_void_ptr(&self) -> *libc::c_void { - let node: *libc::c_void = unsafe { cast::reinterpret_cast(self) }; - node + unsafe { + cast::transmute(*self) + } } } diff --git a/src/servo/css/select_handler.rs b/src/servo/css/select_handler.rs index 39cbe4a9b89..29ae574e7ef 100644 --- a/src/servo/css/select_handler.rs +++ b/src/servo/css/select_handler.rs @@ -1,36 +1,38 @@ -use dom::node::{Node, NodeData, NodeTree, Doctype, Comment, Element, Text}; +/// +/// Implementation of the callbacks that the CSS selector engine uses to query the DOM. +/// + +use dom::node::AbstractNode; use newcss::select::SelectHandler; -use util::tree; + +use core::str::eq_slice; pub struct NodeSelectHandler { - node: Node + node: AbstractNode } -fn with_node_name(data: &NodeData, f: &fn(&str) -> R) -> R { - match *data.kind { - Element(ref data) => f(data.tag_name), - _ => fail!(~"attempting to style non-element node") +fn with_node_name(node: AbstractNode, f: &fn(&str) -> R) -> R { + if !node.is_element() { + fail!(~"attempting to style non-element node"); + } + do node.with_imm_element |element_n| { + f(element_n.tag_name) } } -impl SelectHandler for NodeSelectHandler { - fn with_node_name(node: &Node, f: &fn(&str) -> R) -> R { - do node.read |data| { - with_node_name(data, f) - } +impl SelectHandler for NodeSelectHandler { + fn with_node_name(node: &AbstractNode, f: &fn(&str) -> R) -> R { + with_node_name(*node, f) } - fn named_parent_node(node: &Node, name: &str) -> Option { - let parent = tree::parent(&NodeTree, node); - match parent { + fn named_parent_node(node: &AbstractNode, name: &str) -> Option { + match node.parent_node() { Some(parent) => { - do parent.read |data| { - do with_node_name(data) |node_name| { - if name == node_name { - Some(parent) - } else { - None - } + do with_node_name(parent) |node_name| { + if eq_slice(name, node_name) { + Some(parent) + } else { + None } } } @@ -38,23 +40,21 @@ impl SelectHandler for NodeSelectHandler { } } - fn parent_node(node: &Node) -> Option { - tree::parent(&NodeTree, node) + fn parent_node(node: &AbstractNode) -> Option { + node.parent_node() } // TODO: Use a Bloom filter. - fn named_ancestor_node(node: &Node, name: &str) -> Option { + fn named_ancestor_node(node: &AbstractNode, name: &str) -> Option { let mut node = *node; loop { - let parent = tree::parent(&NodeTree, &node); + let parent = node.parent_node(); match parent { Some(parent) => { let mut found = false; - do parent.read |data| { - do with_node_name(data) |node_name| { - if name == node_name { - found = true; - } + do with_node_name(parent) |node_name| { + if eq_slice(name, node_name) { + found = true; } } if found { @@ -67,31 +67,27 @@ impl SelectHandler for NodeSelectHandler { } } - fn node_is_root(node: &Node) -> bool { + fn node_is_root(node: &AbstractNode) -> bool { self.parent_node(node).is_none() } - fn with_node_id(node: &Node, f: &fn(Option<&str>) -> R) -> R { - do node.read |data| { - match *data.kind { - Element(ref data) => data.with_attr("id", f), - _ => fail!(~"attempting to style non-element node") - } + fn with_node_id(node: &AbstractNode, f: &fn(Option<&str>) -> R) -> R { + if !node.is_element() { + fail!(~"attempting to style non-element node"); + } + do node.with_imm_element() |element_n| { + f(element_n.get_attr("id")) } } - fn node_has_id(node: &Node, id: &str) -> bool { - do node.read |data| { - match *data.kind { - Element(ref data) => { - do data.with_attr("id") |existing_id_opt| { - match existing_id_opt { - None => false, - Some(existing_id) => str::eq_slice(id, existing_id) - } - } - } - _ => fail!(~"attempting to style non-element node") + fn node_has_id(node: &AbstractNode, id: &str) -> bool { + if !node.is_element() { + fail!(~"attempting to style non-element node"); + } + do node.with_imm_element |element_n| { + match element_n.get_attr("id") { + None => false, + Some(existing_id) => id == existing_id } } } diff --git a/src/servo/dom/bindings/document.rs b/src/servo/dom/bindings/document.rs index 165c6d48375..f02f81a58f9 100644 --- a/src/servo/dom/bindings/document.rs +++ b/src/servo/dom/bindings/document.rs @@ -64,6 +64,7 @@ enum Element = int; }*/ extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { + /* unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); if obj.is_null() { @@ -75,7 +76,8 @@ extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> J let scope = (*box).payload.scope; *vp = RUST_OBJECT_TO_JSVAL(node::create(cx, node, scope).ptr); return 1; - } + }*/ + return 1; } unsafe fn unwrap(obj: *JSObject) -> *rust_box { @@ -85,14 +87,17 @@ unsafe fn unwrap(obj: *JSObject) -> *rust_box { } extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { + /* debug!("document finalize!"); unsafe { let val = JS_GetReservedSlot(obj, 0); let _doc: @Document = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); } + */ } pub fn init(compartment: @mut Compartment, doc: @Document) { + /* let obj = utils::define_empty_prototype(~"Document", None, compartment); let attrs = @~[ @@ -128,4 +133,5 @@ pub fn init(compartment: @mut Compartment, doc: @Document) { GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8, JSPROP_ENUMERATE); + */ } diff --git a/src/servo/dom/bindings/element.rs b/src/servo/dom/bindings/element.rs index 8e624e5b806..1253e5be1ab 100644 --- a/src/servo/dom/bindings/element.rs +++ b/src/servo/dom/bindings/element.rs @@ -1,36 +1,35 @@ -use js::rust::{Compartment, jsobj}; -use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL, - JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS}; -use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec, - JSPropertyOpWrapper, JSStrictPropertyOpWrapper}; -use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError, - JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN, - JS_DefineFunctions, JS_DefineProperty}; -use js::jsapi::bindgen::*; -use js::glue::bindgen::*; -use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub}; - use content::content_task::{Content, task_from_context}; -use layout::layout_task; -use dom::node::{Node, NodeScope, Element}; -use dom::element::*; -use dom::bindings::node::NodeBundle; +use dom::bindings::node::{NodeBundle, unwrap}; use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval}; use dom::bindings::utils::{str}; -use libc::c_uint; -use ptr::null; -use dom::bindings::node::unwrap; +use dom::element::*; +use dom::node::{Node, Element}; +use layout::layout_task; use super::utils; +use core::libc::c_uint; +use core::ptr::null; +use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub}; +use js::glue::bindgen::*; +use js::jsapi::bindgen::*; +use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec}; +use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper}; +use js::rust::{Compartment, jsobj}; +use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL}; +use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS}; + extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { +/* debug!("element finalize!"); unsafe { let val = JS_GetReservedSlot(obj, 0); let _node: ~NodeBundle = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); } + */ } pub fn init(compartment: @mut Compartment) { +/* let obj = utils::define_empty_prototype(~"Element", Some(~"Node"), compartment); let attrs = @~[ JSPropertySpec { @@ -74,11 +73,12 @@ pub fn init(compartment: @mut Compartment) { vec::as_imm_buf(*attrs, |specs, _len| { JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs); }); + */ } #[allow(non_implicitly_copyable_typarams)] -extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) - -> JSBool { +extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { + /* unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); if obj.is_null() { @@ -109,12 +109,13 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa *vp = RUST_INT_TO_JSVAL( (width & (i32::max_value as int)) as libc::c_int); return 1; - } + }*/ + return 0; } #[allow(non_implicitly_copyable_typarams)] -extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) - -> JSBool { +extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { + /* unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); if obj.is_null() { @@ -137,12 +138,13 @@ extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa } }; return 1; - } + }*/ + return 0; } #[allow(non_implicitly_copyable_typarams)] -extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) - -> JSBool { +extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { +/* unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); if obj.is_null() { @@ -162,12 +164,13 @@ extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) } } }; - } + }*/ return 1; } #[allow(non_implicitly_copyable_typarams)] -pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj { +pub fn create(cx: *JSContext, node: Node) -> jsobj { + /* let proto = scope.write(&node, |nd| { match &nd.kind { &~Element(ref ed) => { @@ -195,5 +198,7 @@ pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj { cast::reinterpret_cast(&squirrel_away_unique(~NodeBundle(node, scope))); JS_SetReservedSlot(obj.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr)); } - return obj; + + return obj;*/ + fail!(~"stub"); } diff --git a/src/servo/dom/bindings/node.rs b/src/servo/dom/bindings/node.rs index a1a5d580c08..74c382ece5c 100644 --- a/src/servo/dom/bindings/node.rs +++ b/src/servo/dom/bindings/node.rs @@ -1,25 +1,27 @@ -use js::rust::{Compartment, jsobj}; -use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL, - JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS}; -use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec, - JSPropertyOpWrapper, JSStrictPropertyOpWrapper}; -use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError, - JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN, - JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate}; -use js::jsval::{INT_TO_JSVAL, JSVAL_TO_PRIVATE}; -use js::jsapi::bindgen::*; -use js::glue::bindgen::*; - -use dom::node::{Node, NodeScope, Text, Doctype, Comment, Element}; use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval}; use dom::bindings::utils::{str}; -use libc::c_uint; -use ptr::null; -use super::utils; +use dom::node::{AbstractNode, Node}; use super::element; +use super::utils; + +use core::cast::transmute; +use core::libc::c_uint; +use core::ptr::null; +use js::glue::bindgen::*; +use js::jsapi::bindgen::*; +use js::jsapi::bindgen::{JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate}; +use js::jsapi::bindgen::{JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN}; +use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError}; +use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec}; +use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper}; +use js::jsval::{INT_TO_JSVAL, JSVAL_TO_PRIVATE}; +use js::rust::{Compartment, jsobj}; +use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL}; +use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS}; use js; pub fn init(compartment: @mut Compartment) { +/* let obj = utils::define_empty_prototype(~"Node", None, compartment); let attrs = @~[ @@ -53,11 +55,12 @@ pub fn init(compartment: @mut Compartment) { vec::push(&mut compartment.global_props, attrs); vec::as_imm_buf(*attrs, |specs, _len| { JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs); - }); + });*/ } #[allow(non_implicitly_copyable_typarams)] -pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj { +pub fn create(cx: *JSContext, node: Node) -> jsobj { + /* do scope.write(&node) |nd| { match nd.kind { ~Element(*) => element::create(cx, node, scope), @@ -66,17 +69,19 @@ pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj { ~Doctype(*) => fail!(~"no doctype node bindings yet") } } + */ + unsafe { + transmute(0) + } } pub struct NodeBundle { - node: Node, - scope: NodeScope, + node: AbstractNode, } -pub fn NodeBundle(n: Node, s: NodeScope) -> NodeBundle { +pub fn NodeBundle(n: AbstractNode) -> NodeBundle { NodeBundle { - node : n, - scope : s + node: n, } } @@ -87,6 +92,7 @@ pub unsafe fn unwrap(obj: *JSObject) -> *rust_box { #[allow(non_implicitly_copyable_typarams)] extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { + /* unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); if obj.is_null() { @@ -105,12 +111,13 @@ extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool } } }; - } + }*/ return 1; } #[allow(non_implicitly_copyable_typarams)] extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { + /* unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); if obj.is_null() { @@ -129,12 +136,13 @@ extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBoo } } }; - } + }*/ return 1; } impl NodeBundle { fn getNodeType() -> i32 { + /* do self.node.read |nd| { match nd.kind { ~Element(*) => 1, @@ -142,11 +150,13 @@ impl NodeBundle { ~Comment(*) => 8, ~Doctype(*) => 10 } - } + }*/ + 0 } } extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { + /* unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); if obj.is_null() { @@ -156,6 +166,6 @@ extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { let bundle = unwrap(obj); let nodeType = (*bundle).payload.getNodeType(); *vp = INT_TO_JSVAL(nodeType); - } + }*/ return 1; } diff --git a/src/servo/dom/cow.rs b/src/servo/dom/cow.rs deleted file mode 100644 index 4d4311925a3..00000000000 --- a/src/servo/dom/cow.rs +++ /dev/null @@ -1,366 +0,0 @@ -/*! - -Implements the copy-on-write DOM-sharing model. This model allows for -a single writer and any number of readers, but the writer must be able -to control and manage the lifetimes of the reader(s). For simplicity -I will describe the implementation as though there were a single -reader. - -The basic idea is that every object in the COW pool has both a reader -view and a writer view. The writer always sees the writer view, which -contains the most up-to-date values. The reader uses the reader view, -which contains the values as of the point where the reader was forked. -When the writer joins the reader, the reader view will be synchronized -with the writer view. - -Internally, the way this works is using a copy-on-write scheme. Each -COW node maintains two pointers (`read_ptr` and `write_ptr`). -Assuming that readers are active, when a writer wants to modify a -node, it first copies the reader's data into a new pointer. Any -writes that occur after that point (but before the reader is joined) -will operate on this same copy. When the reader is joined, any nodes -which the writer modified will free the stale reader data and update -the reader pointer to be the same as the writer pointer. - -# Using the COW APIs as a writer - -You must first create a `scope` object. The scope object manages the -memory and the COW operations. COW'd objects of some sendable type -`T` are not referenced directly but rather through a `handle`. To -create a new COW object, you use `scope.handle(t)` where `t` is some -initial value of type `T`. To write to an COW object, use -`scope.write()` and to read from it use `scope.read()`. Be sure not to -use the various `ReaderMethods`. - -Handles can be freely sent between tasks but the COW scope cannot. It must stay with the writer -task. You are responsible for correctly invoking `reader_forked()` and `reader_joined()` to keep -the COW scope abreast of when the reader is active. Failure to do so will lead to race conditions -or worse. - -# Using the COW APIs as a reader - -Import the `ReaderMethods` impl. When you receive a handle, you can -invoke `h.read { |v| ... }` and so forth. There is also a piece of -auxiliary data that can be optionally associated with each handle. - -Note: if the type `T` contains mutable fields, then there is nothing -to stop the reader from mutating those fields in the `read()` method. -Do not do this. It will lead to race conditions. - -FIXME: We can enforce that this is not done by ensuring that the type -`T` contains no mutable fields. - -# Auxiliary data - -Readers can associate a piece of auxiliary data of type `A` along with -main nodes. This is convenient but dangerous: it is the reader's job -to ensure that this data remains live independent of the COW nodes -themselves. - -*/ - -use core::libc::types::os::arch::c95::size_t; -use ptr::Ptr; -use vec::push; - -struct ScopeData { - mut layout_active: bool, - mut free_list: ~[Handle], - mut first_dirty: Handle -} - -struct ScopeResource { - d : ScopeData, - - drop { - unsafe { - for self.d.free_list.each |h| { free_handle(*h); } - } - } -} - -fn ScopeResource(d : ScopeData) -> ScopeResource { - ScopeResource { d: d } -} - -pub type Scope = @ScopeResource; - -type HandleData = {mut read_ptr: *T, - mut write_ptr: *mut T, - mut read_aux: *A, - mut next_dirty: Handle}; -pub enum Handle { - _Handle(*HandleData) -} - -// Private methods -impl Handle { - fn read_ptr() -> *T { unsafe { (**self).read_ptr } } - fn write_ptr() -> *mut T { unsafe { (**self).write_ptr } } - fn read_aux() -> *A { unsafe { (**self).read_aux } } - fn next_dirty() -> Handle { unsafe { (**self).next_dirty } } - - fn set_read_ptr(t: *T) { unsafe { (**self).read_ptr = t; } } - fn set_write_ptr(t: *mut T) { unsafe { (**self).write_ptr = t; } } - fn set_read_aux(t: *A) { unsafe { (**self).read_aux = t; } } - fn set_next_dirty(h: Handle) { unsafe { (**self).next_dirty = h; } } - - pure fn is_null() -> bool { (*self).is_null() } - fn is_not_null() -> bool { (*self).is_not_null() } -} - -impl Handle { - /// Access the reader's view of the handle's data - fn read(f: fn(&T) -> U) -> U { - unsafe { - f(&*self.read_ptr()) - } - } - - /// True if auxiliary data is associated with this handle - fn has_aux() -> bool { - unsafe { - self.read_aux().is_not_null() - } - } - - /** Set the auxiliary data associated with this handle. - - **Warning:** the reader is responsible for keeping this data live! - */ - fn set_aux(p: @A) { - unsafe { - (**self).read_aux = ptr::to_unsafe_ptr(&*p); - } - } - - /// Access the auxiliary data associated with this handle - fn aux(f: fn(&A) -> U) -> U { - unsafe { - assert self.has_aux(); - f(&*self.read_aux()) - } - } -} - -impl cmp::Eq for Handle { - pure fn eq(&self, other: &Handle) -> bool { **self == **other } - pure fn ne(&self, other: &Handle) -> bool { **self != **other } -} - -// Private methods -impl Scope { - fn clone(v: *T) -> *T { - unsafe { - let n: *mut T = - cast::reinterpret_cast(&libc::calloc(sys::size_of::() as size_t, 1u as size_t)); - - // n.b.: this assignment will run the drop glue for . *Hopefully* the fact that - // everything is initialized to NULL by calloc will make this ok. We may have to make the - // take glue be tolerant of this. - *n = unsafe{*v}; - - return cast::reinterpret_cast(&n); - } - } -} - -unsafe fn free(t: *T) { - let _x = *cast::reinterpret_cast::<*T,*mut T>(&t); - libc::free(cast::reinterpret_cast(&t)); -} - -unsafe fn free_handle(h: Handle) { - free(h.read_ptr()); - if h.write_ptr() != cast::reinterpret_cast(&h.read_ptr()) { - free(cast::reinterpret_cast::<*mut T,*T>(&h.write_ptr())); - } -} - -pub unsafe fn unwrap(handle: Handle) -> *HandleData { - *handle -} - -pub unsafe fn wrap(data: *HandleData) -> Handle { - _Handle(data) -} - -fn null_handle() -> Handle { - _Handle(ptr::null()) -} - -pub fn Scope() -> Scope { - @ScopeResource( - ScopeData { - mut layout_active: false, - mut free_list: ~[], - mut first_dirty: null_handle() - }) -} - -// Writer methods -impl Scope { - fn is_reader_forked() -> bool { - self.d.layout_active - } - - fn reader_forked() { - assert !self.d.layout_active; - assert self.d.first_dirty.is_null(); - self.d.layout_active = true; - } - - fn reader_joined() { - assert self.d.layout_active; - - if (/*bad*/copy self.d.first_dirty).is_not_null() { - let mut handle = self.d.first_dirty; - while (*handle).is_not_null() { - unsafe { - free(handle.read_ptr()); - - handle.set_read_ptr(cast::reinterpret_cast(&handle.write_ptr())); - let next_handle = handle.next_dirty(); - handle.set_next_dirty(null_handle()); - handle = next_handle; - } - } - self.d.first_dirty = null_handle(); - } - - assert self.d.first_dirty.is_null(); - self.d.layout_active = false; - } - - fn read(h: &Handle, f: fn(&T) -> U) -> U { - // Use the write_ptr, which may be more up to date than the read_ptr or may not - unsafe { - f(&*h.write_ptr()) - } - } - - fn write(h: &Handle, f: fn(&T) -> U) -> U { - unsafe { - let const_read_ptr = ptr::const_offset(h.read_ptr(), 0); - let const_write_ptr = ptr::const_offset(h.write_ptr(), 0); - if self.d.layout_active && const_read_ptr == const_write_ptr { - debug!("marking handle %? as dirty", h); - h.set_write_ptr(cast::reinterpret_cast(&self.clone(h.read_ptr()))); - h.set_next_dirty(self.d.first_dirty); - self.d.first_dirty = *h; - } - f(&*h.write_ptr()) - } - } - - // FIXME: This could avoid a deep copy by taking ownership of `v` - #[allow(non_implicitly_copyable_typarams)] - fn handle(v: &T) -> Handle { - unsafe { - debug!("vv: %?", *v); - let d: *HandleData = - cast::reinterpret_cast( - &libc::malloc(sys::size_of::>() as size_t)); - (*d).read_ptr = self.clone(ptr::to_unsafe_ptr(v)); - (*d).write_ptr = cast::reinterpret_cast(&(*d).read_ptr); - (*d).read_aux = ptr::null(); - (*d).next_dirty = null_handle(); - let h = _Handle(d); - push(&mut self.d.free_list, h); - do self.read(&h) |v| { - debug!("vv: %?", *v); - } - return h; - } - } -} - -#[cfg(test)] -#[allow(non_implicitly_copyable_typarams)] -mod test { - use pipes::{SharedChan, stream}; - use super::Scope; - - type animal = {name: ~str, species: species}; - enum species { - chicken(~chicken), - bull(~bull) - } - type chicken = {mut eggs_per_day:uint}; - type bull = {mut horns:uint}; - - type processed = {flag: bool}; - - type animal_scope = Scope; - - #[test] - fn handles_get_freed() { - let s: animal_scope = Scope(); - s.handle(&{name:~"henrietta", species:chicken(~{mut eggs_per_day:22u})}); - s.handle(&{name:~"ferdinand", species:bull(~{mut horns:3u})}); - } - - fn mutate(a: &animal) { - match &a.species { - &chicken(ref c) => c.eggs_per_day += 1u, - &bull(ref c) => c.horns += 1u - } - } - - fn read_characteristic(a: &animal) -> uint { - match &a.species { - &chicken(ref c) => c.eggs_per_day, - &bull(ref c) => c.horns - } - } - - #[test] - fn interspersed_execution() { - let s: animal_scope = Scope(); - let henrietta = - s.handle(&{name:~"henrietta", - species:chicken(~{mut eggs_per_day:0u})}); - let ferdinand = - s.handle(&{name:~"ferdinand", - species:bull(~{mut horns:0u})}); - - let iter1 = 3u; - let iter2 = 22u; - let (read_port, read_chan) = stream(); - let read_chan = SharedChan(read_chan); - - // fire up a reader task - for uint::range(0u, iter1) |i| { - s.reader_forked(); - let (wait_port, wait_chan) = stream(); - - let read_chan = read_chan.clone(); - do task::spawn { - let read_chan = read_chan.clone(); - for uint::range(0u, iter2) |_i| { - read_chan.send(henrietta.read(read_characteristic)); - read_chan.send(ferdinand.read(read_characteristic)); - wait_port.recv(); - } - } - - let hrc = henrietta.read(read_characteristic); - assert hrc == (i * iter2); - - let frc = ferdinand.read(read_characteristic); - assert frc == i * iter2; - - for uint::range(0u, iter2) |_i| { - assert hrc == read_port.recv(); - s.write(&henrietta, mutate); - assert frc == read_port.recv(); - s.write(&ferdinand, mutate); - wait_chan.send(()); - } - s.reader_joined(); - } - - assert henrietta.read(read_characteristic) == iter1 * iter2; - assert ferdinand.read(read_characteristic) == iter1 * iter2; - } -} diff --git a/src/servo/dom/document.rs b/src/servo/dom/document.rs index d010b6e19d2..18b033a4934 100644 --- a/src/servo/dom/document.rs +++ b/src/servo/dom/document.rs @@ -1,15 +1,14 @@ +use dom::node::AbstractNode; use newcss::stylesheet::Stylesheet; -use dom::node::{NodeScope, Node}; + use std::arc::ARC; pub struct Document { - root: Node, - scope: NodeScope, + root: AbstractNode, } -pub fn Document(root: Node, scope: NodeScope) -> Document { +pub fn Document(root: AbstractNode) -> Document { Document { - root : root, - scope : scope, + root: root, } } diff --git a/src/servo/dom/element.rs b/src/servo/dom/element.rs index af88a367fdd..6b9a39d2b8d 100644 --- a/src/servo/dom/element.rs +++ b/src/servo/dom/element.rs @@ -1,51 +1,142 @@ -use core::dvec::DVec; -use geom::size::Size2D; +// +// Element nodes. +// + +use dom::node::{ElementNodeTypeId, Node}; + +use core::str::eq_slice; +use std::cell::Cell; use std::net::url::Url; -pub struct ElementData { - tag_name: ~str, - kind: ~ElementKind, - attrs: DVec<~Attr>, +pub struct Element { + parent: Node, + tag_name: ~str, // TODO: This should be an atom, not a ~str. + attrs: ~[Attr], } -#[allow(non_implicitly_copyable_typarams)] -impl ElementData { - fn get_attr(name: &str) -> Option<~str> { - let found = do self.attrs.find |attr| { name == attr.name }; - match found { - Some(attr) => Some(copy attr.value), - None => None +#[deriving_eq] +pub enum ElementTypeId { + HTMLAnchorElementTypeId, + HTMLAsideElementTypeId, + HTMLBRElementTypeId, + HTMLBodyElementTypeId, + HTMLBoldElementTypeId, + HTMLDivElementTypeId, + HTMLFontElementTypeId, + HTMLFormElementTypeId, + HTMLHRElementTypeId, + HTMLHeadElementTypeId, + HTMLHeadingElementTypeId, + HTMLHtmlElementTypeId, + HTMLImageElementTypeId, + HTMLInputElementTypeId, + HTMLItalicElementTypeId, + HTMLLinkElementTypeId, + HTMLListItemElementTypeId, + HTMLMetaElementTypeId, + HTMLOListElementTypeId, + HTMLOptionElementTypeId, + HTMLParagraphElementTypeId, + HTMLScriptElementTypeId, + HTMLSectionElementTypeId, + HTMLSelectElementTypeId, + HTMLSmallElementTypeId, + HTMLSpanElementTypeId, + HTMLStyleElementTypeId, + HTMLTableBodyElementTypeId, + HTMLTableCellElementTypeId, + HTMLTableElementTypeId, + HTMLTableRowElementTypeId, + HTMLTitleElementTypeId, + HTMLUListElementTypeId, + UnknownElementTypeId, +} + +// +// Regular old elements +// + +pub struct HTMLAnchorElement { parent: Element } +pub struct HTMLAsideElement { parent: Element } +pub struct HTMLBRElement { parent: Element } +pub struct HTMLBodyElement { parent: Element } +pub struct HTMLBoldElement { parent: Element } +pub struct HTMLDivElement { parent: Element } +pub struct HTMLFontElement { parent: Element } +pub struct HTMLFormElement { parent: Element } +pub struct HTMLHRElement { parent: Element } +pub struct HTMLHeadElement { parent: Element } +pub struct HTMLHtmlElement { parent: Element } +pub struct HTMLInputElement { parent: Element } +pub struct HTMLItalicElement { parent: Element } +pub struct HTMLLinkElement { parent: Element } +pub struct HTMLListItemElement { parent: Element } +pub struct HTMLMetaElement { parent: Element } +pub struct HTMLOListElement { parent: Element } +pub struct HTMLOptionElement { parent: Element } +pub struct HTMLParagraphElement { parent: Element } +pub struct HTMLScriptElement { parent: Element } +pub struct HTMLSectionElement { parent: Element } +pub struct HTMLSelectElement { parent: Element } +pub struct HTMLSmallElement { parent: Element } +pub struct HTMLSpanElement { parent: Element } +pub struct HTMLStyleElement { parent: Element } +pub struct HTMLTableBodyElement { parent: Element } +pub struct HTMLTableCellElement { parent: Element } +pub struct HTMLTableElement { parent: Element } +pub struct HTMLTableRowElement { parent: Element } +pub struct HTMLTitleElement { parent: Element } +pub struct HTMLUListElement { parent: Element } +pub struct UnknownElement { parent: Element } + +// +// Fancier elements +// + +pub struct HTMLHeadingElement { + parent: Element, + level: HeadingLevel, +} + +pub struct HTMLImageElement { + parent: Element, + image: Option, +} + +// +// Element methods +// + +impl Element { + static pub fn new(type_id: ElementTypeId, tag_name: ~str) -> Element { + Element { + parent: Node::new(ElementNodeTypeId(type_id)), + tag_name: tag_name, + attrs: ~[] } } - // Gets an attribute without copying. - // - // FIXME: Should not take a closure, but we need covariant type parameters for - // that. - fn with_attr(name: &str, f: &fn(Option<&str>) -> R) -> R { - for self.attrs.each |attr| { - if name == attr.name { - let value: &str = attr.value; - return f(Some(value)); + fn get_attr(&self, name: &str) -> Option<&self/str> { + // FIXME: Need an each() that links lifetimes in Rust. + for uint::range(0, self.attrs.len()) |i| { + if eq_slice(self.attrs[i].name, name) { + let val: &str = self.attrs[i].value; + return Some(val); } } - f(None) + return None; } - fn set_attr(name: &str, value: ~str) { - let idx = do self.attrs.position |attr| { name == attr.name }; - match idx { - Some(idx) => self.attrs.set_elt(idx, ~Attr(name.to_str(), value)), - None => {} + fn set_attr(&mut self, name: &str, value: ~str) { + // FIXME: We need a better each_mut in Rust; this is ugly. + let value_cell = Cell(value); + for uint::range(0, self.attrs.len()) |i| { + if eq_slice(self.attrs[i].name, name) { + self.attrs[i].value = value_cell.take(); + return; + } } - } -} - -pub fn ElementData(tag_name: ~str, kind: ~ElementKind) -> ElementData { - ElementData { - tag_name : tag_name, - kind : kind, - attrs : DVec(), + self.attrs.push(Attr::new(name.to_str(), value_cell.take())); } } @@ -54,23 +145,15 @@ pub struct Attr { value: ~str, } -pub fn Attr(name: ~str, value: ~str) -> Attr { - Attr { - name : name, - value : value, +impl Attr { + static pub fn new(name: ~str, value: ~str) -> Attr { + Attr { + name: name, + value: value + } } } -pub fn HTMLImageData() -> HTMLImageData { - HTMLImageData { - image: None - } -} - -pub struct HTMLImageData { - mut image: Option -} - pub enum HeadingLevel { Heading1, Heading2, @@ -80,39 +163,3 @@ pub enum HeadingLevel { Heading6, } -pub enum ElementKind { - HTMLAnchorElement, - HTMLAsideElement, - HTMLBRElement, - HTMLBodyElement, - HTMLBoldElement, - HTMLDivElement, - HTMLFontElement, - HTMLFormElement, - HTMLHRElement, - HTMLHeadElement, - HTMLHeadingElement(HeadingLevel), - HTMLHtmlElement, - HTMLImageElement(HTMLImageData), - HTMLInputElement, - HTMLItalicElement, - HTMLLinkElement, - HTMLListItemElement, - HTMLMetaElement, - HTMLOListElement, - HTMLOptionElement, - HTMLParagraphElement, - HTMLScriptElement, - HTMLSectionElement, - HTMLSelectElement, - HTMLSmallElement, - HTMLSpanElement, - HTMLStyleElement, - HTMLTableBodyElement, - HTMLTableCellElement, - HTMLTableElement, - HTMLTableRowElement, - HTMLTitleElement, - HTMLUListElement, - UnknownElement, -} diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index a5c95d9efdc..a496d4ff6ab 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -1,64 +1,322 @@ -/* The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. */ -use newcss::complete::CompleteSelectResults; +// +// The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. +// + use dom::bindings; use dom::document::Document; -use dom::element::{Attr, ElementData}; +use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId}; +use dom::element::{HTMLStyleElementTypeId}; use dom::window::Window; -use geom::size::Size2D; -use js::crust::*; -use js::glue::bindgen::RUST_OBJECT_TO_JSVAL; -use js::jsapi::{JSClass, JSObject, JSPropertySpec, JSContext, jsid, JSVal, JSBool}; -use js::rust::Compartment; -use js::{JSPROP_ENUMERATE, JSPROP_SHARED}; use layout::debug::DebugMethods; use layout::flow::FlowContext; -use ptr::null; +use newcss::complete::CompleteSelectResults; + +use core::cast::transmute; +use core::ptr::null; +use geom::size::Size2D; +use js::crust::*; +use js::rust::Compartment; use std::arc::ARC; -use util::tree; -use super::cow; -pub struct NodeData { - tree: tree::Tree, - kind: ~NodeKind, +// +// 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(). +/// +/// FIXME: This should be replaced with a trait once they can inherit from structs. +pub struct AbstractNode { + priv obj: *mut Node } -/* The tree holding Nodes (read-only) */ -pub enum NodeTree { NodeTree } +impl Eq for AbstractNode { + pure fn eq(&self, other: &AbstractNode) -> bool { self.obj == other.obj } + pure fn ne(&self, other: &AbstractNode) -> bool { self.obj != other.obj } +} -impl NodeTree { - fn each_child(node: &Node, f: fn(&Node) -> bool) { - tree::each_child(&self, node, f) - } +pub struct Node { + type_id: NodeTypeId, - fn get_parent(node: &Node) -> Option { - tree::get_parent(&self, node) + parent_node: Option, + first_child: Option, + last_child: Option, + next_sibling: Option, + prev_sibling: Option, + + // You must not touch this if you are not layout. + priv layout_data: Option<@mut LayoutData> +} + +#[deriving_eq] +pub enum NodeTypeId { + DoctypeNodeTypeId, + CommentNodeTypeId, + ElementNodeTypeId(ElementTypeId), + TextNodeTypeId, +} + +// +// Auxiliary layout data +// + +pub struct LayoutData { + style: Option, + flow: Option<@FlowContext>, +} + +impl LayoutData { + static pub fn new() -> LayoutData { + LayoutData { + style: None, + flow: None, + } } } -impl tree::ReadMethods for NodeTree { - fn with_tree_fields(n: &Node, f: fn(&tree::Tree) -> R) -> R { - n.read(|n| f(&n.tree)) +// +// Basic node types +// + +pub struct Doctype { + parent: Node, + name: ~str, + public_id: Option<~str>, + system_id: Option<~str>, + force_quirks: bool +} + +impl Doctype { + static pub fn new(name: ~str, + public_id: Option<~str>, + system_id: Option<~str>, + force_quirks: bool) + -> Doctype { + Doctype { + parent: Node::new(DoctypeNodeTypeId), + name: name, + public_id: public_id, + system_id: system_id, + force_quirks: force_quirks, + } } } -impl Node { - fn traverse_preorder(preorder_cb: &fn(Node)) { - preorder_cb(self); - do NodeTree.each_child(&self) |child| { child.traverse_preorder(preorder_cb); true } - } +pub struct Comment { + parent: Node, + text: ~str, +} - fn traverse_postorder(postorder_cb: &fn(Node)) { - do NodeTree.each_child(&self) |child| { child.traverse_postorder(postorder_cb); true } - postorder_cb(self); +impl Comment { + static pub fn new(text: ~str) -> Comment { + Comment { + parent: Node::new(CommentNodeTypeId), + text: text + } } } -impl DebugMethods for Node { - /* Dumps the subtree rooted at this node, for debugging. */ +pub struct Text { + parent: Node, + text: ~str, +} + +impl Text { + static pub fn new(text: ~str) -> Text { + Text { + parent: Node::new(CommentNodeTypeId), + text: text + } + } +} + +impl AbstractNode { + // + // Convenience accessors + // + // FIXME: Fold these into util::tree. + + fn type_id(self) -> NodeTypeId { self.with_imm_node(|n| n.type_id) } + fn parent_node(self) -> Option { self.with_imm_node(|n| n.parent_node) } + fn first_child(self) -> Option { self.with_imm_node(|n| n.first_child) } + fn last_child(self) -> Option { self.with_imm_node(|n| n.last_child) } + fn prev_sibling(self) -> Option { self.with_imm_node(|n| n.prev_sibling) } + fn next_sibling(self) -> Option { 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 + // ensure this. + fn layout_data(self) -> @mut LayoutData { + self.with_imm_node(|n| n.layout_data.get()) + } + fn has_layout_data(self) -> bool { + self.with_imm_node(|n| n.layout_data.is_some()) + } + fn set_layout_data(self, data: @mut LayoutData) { + 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; + } + } + } + + // + // 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 !f(kid) { + return false; + } + } + true + } + + fn traverse_postorder(self, f: &fn(AbstractNode) -> bool) -> bool { + for self.each_child |kid| { + if !f(kid) { + return false; + } + } + f(self) + } + + // + // Downcasting borrows + // + + fn with_imm_node(self, f: &fn(&Node) -> R) -> R { + unsafe { + f(transmute(self.obj)) + } + } + + fn with_mut_node(self, f: &fn(&mut Node) -> R) -> R { + unsafe { + f(transmute(self.obj)) + } + } + + fn is_text(self) -> bool { self.type_id() == TextNodeTypeId } + + // FIXME: This should be doing dynamic borrow checking for safety. + fn with_imm_text(self, f: &fn(&Text) -> R) -> R { + if !self.is_text() { + fail!(~"node is not text"); + } + unsafe { + f(transmute(self.obj)) + } + } + + fn is_element(self) -> bool { + match self.type_id() { + ElementNodeTypeId(*) => true, + _ => false + } + } + + // FIXME: This should be doing dynamic borrow checking for safety. + fn with_imm_element(self, f: &fn(&Element) -> R) -> R { + if !self.is_element() { + fail!(~"node is not an element"); + } + unsafe { + f(transmute(self.obj)) + } + } + + // FIXME: This should be doing dynamic borrow checking for safety. + fn as_mut_element(self, f: &fn(&mut Element) -> R) -> R { + if !self.is_element() { + fail!(~"node is not an element"); + } + unsafe { + f(transmute(self.obj)) + } + } + + fn is_image_element(self) -> bool { + self.type_id() == ElementNodeTypeId(HTMLImageElementTypeId) + } + + fn with_imm_image_element(self, f: &fn(&HTMLImageElement) -> R) -> R { + if !self.is_image_element() { + fail!(~"node is not an image element"); + } + unsafe { + f(transmute(self.obj)) + } + } + + fn with_mut_image_element(self, f: &fn(&mut HTMLImageElement) -> R) -> R { + if !self.is_image_element() { + fail!(~"node is not an image element"); + } + unsafe { + f(transmute(self.obj)) + } + } + + fn is_style_element(self) -> bool { + self.type_id() == ElementNodeTypeId(HTMLStyleElementTypeId) + } +} + +impl DebugMethods for AbstractNode { + // Dumps the subtree rooted at this node, for debugging. pure fn dump(&self) { - self.dump_indent(0u); + self.dump_indent(0); } - /* Dumps the node tree, for debugging, with indentation. */ + + // Dumps the node tree, for debugging, with indentation. pure fn dump_indent(&self, indent: uint) { let mut s = ~""; for uint::range(0u, indent) |_i| { @@ -70,116 +328,46 @@ impl DebugMethods for Node { // FIXME: this should have a pure version? unsafe { - for NodeTree.each_child(self) |kid| { + for self.each_child() |kid| { kid.dump_indent(indent + 1u) } } } pure fn debug_str(&self) -> ~str { + // Unsafe due to the call to type_id(). unsafe { - do self.read |n| { fmt!("%?", n.kind) } + fmt!("%?", self.type_id()) } } } impl Node { - fn is_element(&self) -> bool { - self.read(|n| match *n.kind { Element(*) => true, _ => false } ) + static pub unsafe fn as_abstract_node(node: ~N) -> AbstractNode { + // This surrenders memory management of the node! + AbstractNode { + obj: transmute(node) + } + } + + static pub fn new(type_id: NodeTypeId) -> Node { + Node { + type_id: type_id, + + parent_node: None, + first_child: None, + last_child: None, + next_sibling: None, + prev_sibling: None, + + layout_data: None, + } } } -pub enum NodeKind { - Doctype(DoctypeData), - Comment(~str), - Element(ElementData), - Text(~str) -} - -pub struct DoctypeData { - name: ~str, - public_id: Option<~str>, - system_id: Option<~str>, - force_quirks: bool -} - -pub fn DoctypeData(name: ~str, public_id: Option<~str>, - system_id: Option<~str>, force_quirks: bool) -> DoctypeData { - DoctypeData { - name : name, - public_id : public_id, - system_id : system_id, - force_quirks : force_quirks, - } -} - - - pub fn define_bindings(compartment: @mut Compartment, doc: @Document, win: @Window) { bindings::window::init(compartment, win); bindings::document::init(compartment, doc); bindings::node::init(compartment); bindings::element::init(compartment); } - - -/** The COW rd_aux data is a (weak) pointer to the layout data, - defined by this `LayoutData` struct. It contains the CSS style object - as well as the primary `RenderBox`. - - Note that there may be multiple boxes per DOM node. */ -pub struct LayoutData { - mut style: Option, - mut flow: Option<@FlowContext> -} - -pub type Node = cow::Handle; - -pub type NodeScope = cow::Scope; - -pub fn NodeScope() -> NodeScope { - cow::Scope() -} - -pub trait NodeScopeExtensions { - fn new_node(+k: NodeKind) -> Node; -} - -#[allow(non_implicitly_copyable_typarams)] -impl NodeScopeExtensions for NodeScope { - fn new_node(k: NodeKind) -> Node { - self.handle(&NodeData {tree: tree::empty(), kind: ~k}) - } -} - -impl NodeScope { - fn each_child(node: &Node, f: fn(&Node) -> bool) { - tree::each_child(&self, node, f) - } - - fn get_parent(node: &Node) -> Option { - tree::get_parent(&self, node) - } -} - -#[allow(non_implicitly_copyable_typarams)] -impl tree::ReadMethods for NodeScope { - fn with_tree_fields(node: &Node, f: fn(&tree::Tree) -> R) -> R { - self.read(node, |n| f(&n.tree)) - } -} - -impl NodeScope { - fn add_child(node: Node, child: Node) { - tree::add_child(&self, node, child) - } -} - -#[allow(non_implicitly_copyable_typarams)] -impl tree::WriteMethods for NodeScope { - pure fn tree_eq(a: &Node, b: &Node) -> bool { a == b } - - fn with_tree_fields(node: &Node, f: fn(&tree::Tree) -> R) -> R { - self.write(node, |n| f(&n.tree)) - } -} diff --git a/src/servo/html/hubbub_html_parser.rs b/src/servo/html/hubbub_html_parser.rs index d1565c63ecd..d309090edab 100644 --- a/src/servo/html/hubbub_html_parser.rs +++ b/src/servo/html/hubbub_html_parser.rs @@ -1,24 +1,50 @@ -use gfx::util::url::make_url; -use au = gfx::geometry; use content::content_task::ContentTask; -use dom::cow; use dom::element::*; use dom::event::{Event, ReflowEvent}; -use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, NodeScopeExtensions}; -use dom::node::{Text}; +use dom::node::{AbstractNode, Comment, Doctype, Element, ElementNodeTypeId, Node, Text}; +use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser}; +use newcss::stylesheet::Stylesheet; use resource::image_cache_task::ImageCacheTask; use resource::image_cache_task; use resource::resource_task::{Done, Load, Payload, ResourceTask}; use util::task::{spawn_listener, spawn_conversation}; use core::comm::{Chan, Port, SharedChan}; -use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser}; +use core::str::eq_slice; +use gfx::util::url::make_url; use hubbub::hubbub::Attribute; use hubbub::hubbub; -use newcss::stylesheet::Stylesheet; +use std::cell::Cell; use std::net::url::Url; use std::net::url; +macro_rules! handle_element( + ($tag:expr, $string:expr, $ctor:ident, $type_id:expr) => ( + if eq_slice($tag, $string) { + let element = ~$ctor { + parent: Element::new($type_id, ($tag).to_str()) + }; + unsafe { + return Node::as_abstract_node(element); + } + } + ) +) + +macro_rules! handle_heading_element( + ($tag:expr, $string:expr, $ctor:ident, $type_id:expr, $level:expr) => ( + if eq_slice($tag, $string) { + let element = ~HTMLHeadingElement { + parent: Element::new($type_id, ($tag).to_str()), + level: $level + }; + unsafe { + return Node::as_abstract_node(element); + } + } + ) +) + type JSResult = ~[~[u8]]; enum CSSMessage { @@ -32,11 +58,25 @@ enum JSMessage { } struct HtmlParserResult { - root: Node, + root: AbstractNode, style_port: Port>, js_port: Port, } +trait NodeWrapping { + unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr; + static unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> Self; +} + +impl NodeWrapping for AbstractNode { + unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr { + cast::transmute(self) + } + static unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> AbstractNode { + cast::transmute(n) + } +} + /** Runs a task that coordinates parsing links to css stylesheets. @@ -119,52 +159,57 @@ fn js_script_listener(to_parent: Chan<~[~[u8]]>, to_parent.send(js_scripts); } -fn build_element_kind(tag: &str) -> ~ElementKind { +// Silly macros to handle constructing DOM nodes. This produces bad code and should be optimized +// via atomization (issue #85). + +fn build_element_from_tag(tag: &str) -> AbstractNode { // TODO (Issue #85): use atoms - if tag == ~"a" { ~HTMLAnchorElement } - else if tag == ~"aside" { ~HTMLAsideElement } - else if tag == ~"br" { ~HTMLBRElement } - else if tag == ~"body" { ~HTMLBodyElement } - else if tag == ~"bold" { ~HTMLBoldElement } - else if tag == ~"div" { ~HTMLDivElement } - else if tag == ~"font" { ~HTMLFontElement } - else if tag == ~"form" { ~HTMLFormElement } - else if tag == ~"hr" { ~HTMLHRElement } - else if tag == ~"head" { ~HTMLHeadElement } - else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) } - else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) } - else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) } - else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) } - else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) } - else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) } - else if tag == ~"html" { ~HTMLHtmlElement } - else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) } - else if tag == ~"input" { ~HTMLInputElement } - else if tag == ~"i" { ~HTMLItalicElement } - else if tag == ~"link" { ~HTMLLinkElement } - else if tag == ~"li" { ~HTMLListItemElement } - else if tag == ~"meta" { ~HTMLMetaElement } - else if tag == ~"ol" { ~HTMLOListElement } - else if tag == ~"option" { ~HTMLOptionElement } - else if tag == ~"p" { ~HTMLParagraphElement } - else if tag == ~"script" { ~HTMLScriptElement } - else if tag == ~"section" { ~HTMLSectionElement } - else if tag == ~"select" { ~HTMLSelectElement } - else if tag == ~"small" { ~HTMLSmallElement } - else if tag == ~"span" { ~HTMLSpanElement } - else if tag == ~"style" { ~HTMLStyleElement } - else if tag == ~"tbody" { ~HTMLTableBodyElement } - else if tag == ~"td" { ~HTMLTableCellElement } - else if tag == ~"table" { ~HTMLTableElement } - else if tag == ~"tr" { ~HTMLTableRowElement } - else if tag == ~"title" { ~HTMLTitleElement } - else if tag == ~"ul" { ~HTMLUListElement } - else { ~UnknownElement } + handle_element!(tag, "a", HTMLAnchorElement, HTMLAnchorElementTypeId); + handle_element!(tag, "aside", HTMLAsideElement, HTMLAsideElementTypeId); + handle_element!(tag, "br", HTMLBRElement, HTMLBRElementTypeId); + handle_element!(tag, "body", HTMLBodyElement, HTMLBodyElementTypeId); + handle_element!(tag, "bold", HTMLBoldElement, HTMLBoldElementTypeId); + handle_element!(tag, "div", HTMLDivElement, HTMLDivElementTypeId); + handle_element!(tag, "font", HTMLFontElement, HTMLFontElementTypeId); + handle_element!(tag, "form", HTMLFormElement, HTMLFormElementTypeId); + handle_element!(tag, "hr", HTMLHRElement, HTMLHRElementTypeId); + handle_element!(tag, "head", HTMLHeadElement, HTMLHeadElementTypeId); + handle_element!(tag, "html", HTMLHtmlElement, HTMLHtmlElementTypeId); + handle_element!(tag, "input", HTMLInputElement, HTMLInputElementTypeId); + handle_element!(tag, "i", HTMLItalicElement, HTMLItalicElementTypeId); + handle_element!(tag, "link", HTMLLinkElement, HTMLLinkElementTypeId); + handle_element!(tag, "li", HTMLListItemElement, HTMLListItemElementTypeId); + handle_element!(tag, "meta", HTMLMetaElement, HTMLMetaElementTypeId); + handle_element!(tag, "ol", HTMLOListElement, HTMLOListElementTypeId); + handle_element!(tag, "option", HTMLOptionElement, HTMLOptionElementTypeId); + handle_element!(tag, "p", HTMLParagraphElement, HTMLParagraphElementTypeId); + handle_element!(tag, "script", HTMLScriptElement, HTMLScriptElementTypeId); + handle_element!(tag, "section", HTMLSectionElement, HTMLSectionElementTypeId); + handle_element!(tag, "select", HTMLSelectElement, HTMLSelectElementTypeId); + handle_element!(tag, "small", HTMLSmallElement, HTMLSmallElementTypeId); + handle_element!(tag, "span", HTMLSpanElement, HTMLSpanElementTypeId); + handle_element!(tag, "style", HTMLStyleElement, HTMLStyleElementTypeId); + handle_element!(tag, "tbody", HTMLTableBodyElement, HTMLTableBodyElementTypeId); + handle_element!(tag, "td", HTMLTableCellElement, HTMLTableCellElementTypeId); + handle_element!(tag, "table", HTMLTableElement, HTMLTableElementTypeId); + handle_element!(tag, "tr", HTMLTableRowElement, HTMLTableRowElementTypeId); + handle_element!(tag, "title", HTMLTitleElement, HTMLTitleElementTypeId); + handle_element!(tag, "ul", HTMLUListElement, HTMLUListElementTypeId); + + handle_heading_element!(tag, "h1", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading1); + handle_heading_element!(tag, "h2", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading2); + handle_heading_element!(tag, "h3", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading3); + handle_heading_element!(tag, "h4", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading4); + handle_heading_element!(tag, "h5", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading5); + handle_heading_element!(tag, "h6", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading6); + + unsafe { + Node::as_abstract_node(~Element::new(UnknownElementTypeId, tag.to_str())) + } } #[allow(non_implicitly_copyable_typarams)] -pub fn parse_html(scope: NodeScope, - url: Url, +pub fn parse_html(url: Url, resource_task: ResourceTask, image_cache_task: ImageCacheTask) -> HtmlParserResult { // Spawn a CSS parser to receive links to CSS style sheets. @@ -185,38 +230,31 @@ pub fn parse_html(scope: NodeScope, }; let js_chan = SharedChan(js_chan); - let (scope, url) = (@copy scope, @url); + let url = @url; unsafe { // Build the root node. - let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement))); + let root = ~HTMLHtmlElement { parent: Element::new(HTMLHtmlElementTypeId, ~"html") }; + let root = unsafe { Node::as_abstract_node(root) }; debug!("created new node"); let parser = hubbub::Parser("UTF-8", false); debug!("created parser"); - parser.set_document_node(cast::transmute(cow::unwrap(root))); + parser.set_document_node(root.to_hubbub_node()); parser.enable_scripting(true); - // Performs various actions necessary after appending has taken place. Currently, this consists - // of processing inline stylesheets, but in the future it might perform prefetching, etc. + // Performs various actions necessary after appending has taken place. Currently, this + // consists of processing inline stylesheets, but in the future it might perform + // prefetching, etc. let css_chan2 = css_chan.clone(); - let append_hook: @fn(Node, Node) = |parent_node, child_node| { - do scope.read(&parent_node) |parent_node_contents| { - do scope.read(&child_node) |child_node_contents| { - match (&parent_node_contents.kind, &child_node_contents.kind) { - (&~Element(ref element), &~Text(ref data)) => { - match element.kind { - ~HTMLStyleElement => { - debug!("found inline CSS stylesheet"); - let url = url::from_str("http://example.com/"); // FIXME - let provenance = InlineProvenance(result::unwrap(url), - copy *data); - css_chan2.send(CSSTaskNewFile(provenance)); - } - _ => {} // Nothing to do. - } - } - _ => {} // Nothing to do. - } + let append_hook: @fn(AbstractNode, AbstractNode) = |parent_node, child_node| { + if parent_node.is_style_element() && child_node.is_text() { + debug!("found inline CSS stylesheet"); + let url = url::from_str("http://example.com/"); // FIXME + let url_cell = Cell(url); + do child_node.with_imm_text |text_node| { + let data = text_node.text.to_str(); // FIXME: Bad copy. + let provenance = InlineProvenance(result::unwrap(url_cell.take()), data); + css_chan2.send(CSSTaskNewFile(provenance)); } } }; @@ -225,8 +263,9 @@ pub fn parse_html(scope: NodeScope, parser.set_tree_handler(@hubbub::TreeHandler { create_comment: |data: ~str| { debug!("create comment"); - let new_node = scope.new_node(Comment(data)); - unsafe { cast::transmute(cow::unwrap(new_node)) } + unsafe { + Node::as_abstract_node(~Comment::new(data)).to_hubbub_node() + } }, create_doctype: |doctype: ~hubbub::Doctype| { debug!("create doctype"); @@ -240,69 +279,83 @@ pub fn parse_html(scope: NodeScope, &None => None, &Some(ref id) => Some(copy *id) }; - let data = DoctypeData(copy doctype.name, - public_id, - system_id, - copy doctype.force_quirks); - let new_node = scope.new_node(Doctype(data)); - unsafe { cast::transmute(cow::unwrap(new_node)) } + let node = ~Doctype::new(copy doctype.name, + public_id, + system_id, + doctype.force_quirks); + unsafe { + Node::as_abstract_node(node).to_hubbub_node() + } }, create_element: |tag: ~hubbub::Tag| { debug!("create element"); // TODO: remove copying here by using struct pattern matching to // move all ~strs at once (blocked on Rust #3845, #3846, #3847) - let elem_kind = build_element_kind(tag.name); - let elem = ElementData(copy tag.name, elem_kind); + let node = build_element_from_tag(tag.name); debug!("-- attach attrs"); - for tag.attributes.each |attr| { - elem.attrs.push(~Attr(copy attr.name, copy attr.value)); + do node.as_mut_element |element| { + for tag.attributes.each |attr| { + element.attrs.push(Attr::new(copy attr.name, copy attr.value)); + } } // Spawn additional parsing, network loads, etc. from tag and attrs - match elem.kind { - //Handle CSS style sheets from elements - ~HTMLLinkElement => { - match (elem.get_attr(~"rel"), elem.get_attr(~"href")) { - (Some(rel), Some(href)) => { - if rel == ~"stylesheet" { - debug!("found CSS stylesheet: %s", href); - css_chan2.send(CSSTaskNewFile(UrlProvenance(make_url( - href, Some(copy *url))))); + match node.type_id() { + // Handle CSS style sheets from elements + ElementNodeTypeId(HTMLLinkElementTypeId) => { + do node.with_imm_element |element| { + match (element.get_attr(~"rel"), element.get_attr(~"href")) { + (Some(rel), Some(href)) => { + if rel == ~"stylesheet" { + debug!("found CSS stylesheet: %s", href); + let url = make_url(href.to_str(), Some(copy *url)); + css_chan2.send(CSSTaskNewFile(UrlProvenance(url))); + } } + _ => {} } - _ => {} } }, - ~HTMLImageElement(ref d) => { - do elem.get_attr(~"src").iter |img_url_str| { - let img_url = make_url(copy *img_url_str, Some(copy *url)); - d.image = Some(copy img_url); - // inform the image cache to load this, but don't store a handle. - // TODO (Issue #84): don't prefetch if we are within a