Start work on new DOM representation

This commit is contained in:
Patrick Walton 2013-02-14 19:13:56 -08:00 committed by Josh Matthews
parent ce514f2785
commit 1b030480ab
22 changed files with 1004 additions and 1088 deletions

View file

@ -5,7 +5,7 @@ tasks.
use dom::bindings::utils::rust_box; use dom::bindings::utils::rust_box;
use dom::document::Document; use dom::document::Document;
use dom::node::{Node, NodeScope, define_bindings}; use dom::node::define_bindings;
use dom::event::{Event, ResizeEvent, ReflowEvent}; use dom::event::{Event, ResizeEvent, ReflowEvent};
use dom::window::Window; use dom::window::Window;
use layout::layout_task; use layout::layout_task;
@ -90,7 +90,6 @@ pub struct Content {
event_port: comm::Port<Event>, event_port: comm::Port<Event>,
event_chan: comm::SharedChan<Event>, event_chan: comm::SharedChan<Event>,
scope: NodeScope,
jsrt: jsrt, jsrt: jsrt,
cx: @Cx, cx: @Cx,
@ -135,19 +134,18 @@ pub fn Content(layout_task: LayoutTask,
event_port: event_port, event_port: event_port,
event_chan: event_chan, event_chan: event_chan,
scope: NodeScope(), jsrt : jsrt,
jsrt: jsrt, cx : cx,
cx: cx,
document: None, document : None,
window: None, window : None,
doc_url: None, doc_url : None,
window_size: Size2D(800u, 600u), window_size : Size2D(800u, 600u),
resource_task: resource_task, resource_task : resource_task,
compartment: compartment, compartment : compartment,
damage: MatchSelectorsDamage, damage : MatchSelectorsDamage,
}; };
cx.set_cx_private(ptr::to_unsafe_ptr(&*content) as *()); 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 // Note: we can parse the next document in parallel
// with any previous documents. // with any previous documents.
let result = html::hubbub_html_parser::parse_html(self.scope, let result = html::hubbub_html_parser::parse_html(copy url,
copy url,
self.resource_task.clone(), self.resource_task.clone(),
self.image_cache_task.clone()); self.image_cache_task.clone());
@ -206,7 +203,7 @@ impl Content {
let js_scripts = result.js_port.recv(); let js_scripts = result.js_port.recv();
debug!("js_scripts: %?", js_scripts); debug!("js_scripts: %?", js_scripts);
let document = Document(root, self.scope); let document = Document(root);
let window = Window(self.control_chan.clone()); let window = Window(self.control_chan.clone());
self.damage.add(MatchSelectorsDamage); 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 Sends a ping to layout and waits for the response (i.e., it has finished any
pending layout request messages). pending layout request messages).
*/ */
fn join_layout() { fn join_layout(&self) {
assert self.scope.is_reader_forked() == self.layout_join_port.is_some(); if self.layout_join_port.is_some() {
if self.scope.is_reader_forked() {
let join_port = replace(&mut self.layout_join_port, None); let join_port = replace(&mut self.layout_join_port, None);
match join_port { match join_port {
Some(ref join_port) => { Some(ref join_port) => {
if !join_port.peek() { if !join_port.peek() {
@ -289,8 +282,6 @@ impl Content {
} }
None => fail!(~"reader forked but no join port?") 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 // Send new document and relevant styles to layout
let data = BuildData { let data = ~BuildData {
node: document.root, node: document.root,
url: copy *doc_url, url: copy *doc_url,
dom_event_chan: self.event_chan.clone(), dom_event_chan: self.event_chan.clone(),
@ -323,10 +314,6 @@ impl Content {
self.layout_task.send(BuildMsg(data)); 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"); debug!("content: layout forked");
} }

View file

@ -2,7 +2,7 @@
use css::node_util::NodeUtil; use css::node_util::NodeUtil;
use css::select_handler::NodeSelectHandler; use css::select_handler::NodeSelectHandler;
use dom::node::{Node, NodeTree}; use dom::node::AbstractNode;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use newcss::complete::CompleteSelectResults; use newcss::complete::CompleteSelectResults;
use newcss::select::{SelectCtx, SelectResults}; use newcss::select::{SelectCtx, SelectResults};
@ -10,40 +10,34 @@ use newcss::select::{SelectCtx, SelectResults};
use std::arc::{ARC, get, clone}; use std::arc::{ARC, get, clone};
pub trait MatchMethods { 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. * Performs CSS selector matching on a subtree.
*
* This is, importantly, the function that updates the layout data for * This is, importantly, the function that updates the layout data for
* the node (the reader-auxiliary box in the COW model) with the * the node (the reader-auxiliary box in the COW model) with the
* computed style. * computed style.
*/ */
fn restyle_subtree(select_ctx: &SelectCtx) { fn restyle_subtree(&self, select_ctx: &SelectCtx) {
// Only elements have styles // Only elements have styles
if self.is_element() { if self.is_element() {
let select_handler = NodeSelectHandler { let select_handler = NodeSelectHandler { node: *self };
node: self let incomplete_results = select_ctx.select_style(self, &select_handler);
};
let incomplete_results = select_ctx.select_style(&self, &select_handler);
// Combine this node's results with its parent's to resolve all inherited values // 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); self.set_css_select_results(complete_results);
} }
let mut i = 0u; for self.each_child |kid| {
for NodeTree.each_child(&self) |kid| {
i = i + 1u;
kid.restyle_subtree(select_ctx); 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) { match find_parent_element_node(node) {
None => CompleteSelectResults::new_root(results), None => CompleteSelectResults::new_root(results),
Some(parent_node) => { Some(parent_node) => {
@ -53,17 +47,11 @@ fn compose_results(node: &Node, results: SelectResults) -> CompleteSelectResults
} }
} }
fn find_parent_element_node(node: &Node) -> Option<Node> { fn find_parent_element_node(node: AbstractNode) -> Option<AbstractNode> {
use util::tree::parent; match node.parent_node() {
Some(parent) if parent.is_element() => Some(parent),
match parent(&NodeTree, node) { Some(parent) => find_parent_element_node(parent),
Some(ref parent) => { None => None,
if parent.is_element() {
Some(*parent)
} else {
find_parent_element_node(parent)
}
}
None => None
} }
} }

View file

@ -1,7 +1,7 @@
// Style retrieval from DOM elements. // Style retrieval from DOM elements.
use css::node_util::NodeUtil; use css::node_util::NodeUtil;
use dom::node::Node; use dom::node::AbstractNode;
use newcss::complete::CompleteStyle; use newcss::complete::CompleteStyle;
/// Node mixin providing `style` method that returns a `NodeStyle` /// Node mixin providing `style` method that returns a `NodeStyle`
@ -9,7 +9,7 @@ pub trait StyledNode {
fn style(&self) -> CompleteStyle/&self; fn style(&self) -> CompleteStyle/&self;
} }
impl StyledNode for Node { impl StyledNode for AbstractNode {
fn style(&self) -> CompleteStyle/&self { fn style(&self) -> CompleteStyle/&self {
assert self.is_element(); // Only elements can have styles assert self.is_element(); // Only elements can have styles
let results = self.get_css_select_results(); let results = self.get_css_select_results();

View file

@ -1,13 +1,14 @@
use dom::node::Node; use dom::node::AbstractNode;
use newcss::complete::CompleteSelectResults; use newcss::complete::CompleteSelectResults;
use std::cell::Cell;
use core::cast::transmute;
pub trait NodeUtil { pub trait NodeUtil {
fn get_css_select_results() -> &self/CompleteSelectResults; fn get_css_select_results(self) -> &self/CompleteSelectResults;
fn set_css_select_results(decl : 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 * Provides the computed style for the given node. If CSS selector
* Returns the style results 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 * FIXME: This isn't completely memory safe since the style is
* stored in a box that can be overwritten * stored in a box that can be overwritten
*/ */
fn get_css_select_results() -> &self/CompleteSelectResults { fn get_css_select_results(self) -> &self/CompleteSelectResults {
if !self.has_aux() { if !self.has_layout_data() {
fail!(~"style() called on a node without aux data!"); fail!(~"style() called on a node without aux data!");
} }
unsafe { &*self.aux( |x| {
match x.style { match self.layout_data().style {
Some(ref style) => ptr::to_unsafe_ptr(style), None => fail!(~"style() called on node without a 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.
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() {
fn set_css_select_results(decl : CompleteSelectResults) { fail!(~"set_css_select_results() called on a node without aux data!");
let decl = Cell(decl);
do self.aux |data| {
data.style = Some(decl.take())
} }
self.layout_data().style = Some(decl);
} }
} }

View file

@ -1,19 +1,24 @@
//! CSS library requires that DOM nodes be convertable to *c_void through this trait //! 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 // FIXME: Rust #3908. rust-css can't reexport VoidPtrLike
extern mod netsurfcss; extern mod netsurfcss;
use css::node_void_ptr::netsurfcss::util::VoidPtrLike; use css::node_void_ptr::netsurfcss::util::VoidPtrLike;
impl VoidPtrLike for Node { impl VoidPtrLike for AbstractNode {
static fn from_void_ptr(node: *libc::c_void) -> Node { static fn from_void_ptr(node: *libc::c_void) -> AbstractNode {
assert node.is_not_null(); assert node.is_not_null();
unsafe { cast::reinterpret_cast(&node) } unsafe {
cast::transmute(node)
}
} }
fn to_void_ptr(&self) -> *libc::c_void { fn to_void_ptr(&self) -> *libc::c_void {
let node: *libc::c_void = unsafe { cast::reinterpret_cast(self) }; unsafe {
node cast::transmute(*self)
}
} }
} }

View file

@ -1,62 +1,62 @@
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 newcss::select::SelectHandler;
use util::tree;
use core::str::eq_slice;
pub struct NodeSelectHandler { pub struct NodeSelectHandler {
node: Node node: AbstractNode
} }
fn with_node_name<R>(data: &NodeData, f: &fn(&str) -> R) -> R { fn with_node_name<R>(node: AbstractNode, f: &fn(&str) -> R) -> R {
match *data.kind { if !node.is_element() {
Element(ref data) => f(data.tag_name), fail!(~"attempting to style non-element node");
_ => fail!(~"attempting to style non-element node") }
do node.with_imm_element |element_n| {
f(element_n.tag_name)
} }
} }
impl SelectHandler<Node> for NodeSelectHandler { impl SelectHandler<AbstractNode> for NodeSelectHandler {
fn with_node_name<R>(node: &Node, f: &fn(&str) -> R) -> R { fn with_node_name<R>(node: &AbstractNode, f: &fn(&str) -> R) -> R {
do node.read |data| { with_node_name(*node, f)
with_node_name(data, f)
}
} }
fn named_parent_node(node: &Node, name: &str) -> Option<Node> { fn named_parent_node(node: &AbstractNode, name: &str) -> Option<AbstractNode> {
let parent = tree::parent(&NodeTree, node); match node.parent_node() {
match parent {
Some(parent) => { Some(parent) => {
do parent.read |data| { do with_node_name(parent) |node_name| {
do with_node_name(data) |node_name| { if eq_slice(name, node_name) {
if name == node_name {
Some(parent) Some(parent)
} else { } else {
None None
} }
} }
} }
}
None => None None => None
} }
} }
fn parent_node(node: &Node) -> Option<Node> { fn parent_node(node: &AbstractNode) -> Option<AbstractNode> {
tree::parent(&NodeTree, node) node.parent_node()
} }
// TODO: Use a Bloom filter. // TODO: Use a Bloom filter.
fn named_ancestor_node(node: &Node, name: &str) -> Option<Node> { fn named_ancestor_node(node: &AbstractNode, name: &str) -> Option<AbstractNode> {
let mut node = *node; let mut node = *node;
loop { loop {
let parent = tree::parent(&NodeTree, &node); let parent = node.parent_node();
match parent { match parent {
Some(parent) => { Some(parent) => {
let mut found = false; let mut found = false;
do parent.read |data| { do with_node_name(parent) |node_name| {
do with_node_name(data) |node_name| { if eq_slice(name, node_name) {
if name == node_name {
found = true; found = true;
} }
} }
}
if found { if found {
return Some(parent); return Some(parent);
} }
@ -67,31 +67,27 @@ impl SelectHandler<Node> for NodeSelectHandler {
} }
} }
fn node_is_root(node: &Node) -> bool { fn node_is_root(node: &AbstractNode) -> bool {
self.parent_node(node).is_none() self.parent_node(node).is_none()
} }
fn with_node_id<R>(node: &Node, f: &fn(Option<&str>) -> R) -> R { fn with_node_id<R>(node: &AbstractNode, f: &fn(Option<&str>) -> R) -> R {
do node.read |data| { if !node.is_element() {
match *data.kind { fail!(~"attempting to style non-element node");
Element(ref data) => data.with_attr("id", f),
_ => 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 { fn node_has_id(node: &AbstractNode, id: &str) -> bool {
do node.read |data| { if !node.is_element() {
match *data.kind { fail!(~"attempting to style non-element node");
Element(ref data) => { }
do data.with_attr("id") |existing_id_opt| { do node.with_imm_element |element_n| {
match existing_id_opt { match element_n.get_attr("id") {
None => false, None => false,
Some(existing_id) => str::eq_slice(id, existing_id) Some(existing_id) => id == existing_id
}
}
}
_ => fail!(~"attempting to style non-element node")
} }
} }
} }

View file

@ -64,6 +64,7 @@ enum Element = int;
}*/ }*/
extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
/*
unsafe { unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() { 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; let scope = (*box).payload.scope;
*vp = RUST_OBJECT_TO_JSVAL(node::create(cx, node, scope).ptr); *vp = RUST_OBJECT_TO_JSVAL(node::create(cx, node, scope).ptr);
return 1; return 1;
} }*/
return 1;
} }
unsafe fn unwrap(obj: *JSObject) -> *rust_box<Document> { unsafe fn unwrap(obj: *JSObject) -> *rust_box<Document> {
@ -85,14 +87,17 @@ unsafe fn unwrap(obj: *JSObject) -> *rust_box<Document> {
} }
extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
/*
debug!("document finalize!"); debug!("document finalize!");
unsafe { unsafe {
let val = JS_GetReservedSlot(obj, 0); let val = JS_GetReservedSlot(obj, 0);
let _doc: @Document = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); let _doc: @Document = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
} }
*/
} }
pub fn init(compartment: @mut Compartment, doc: @Document) { pub fn init(compartment: @mut Compartment, doc: @Document) {
/*
let obj = utils::define_empty_prototype(~"Document", None, compartment); let obj = utils::define_empty_prototype(~"Document", None, compartment);
let attrs = @~[ let attrs = @~[
@ -128,4 +133,5 @@ pub fn init(compartment: @mut Compartment, doc: @Document) {
GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, GetJSClassHookStubPointer(PROPERTY_STUB) as *u8,
GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8, GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8,
JSPROP_ENUMERATE); JSPROP_ENUMERATE);
*/
} }

View file

@ -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 content::content_task::{Content, task_from_context};
use layout::layout_task; use dom::bindings::node::{NodeBundle, unwrap};
use dom::node::{Node, NodeScope, Element};
use dom::element::*;
use dom::bindings::node::NodeBundle;
use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval}; use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval};
use dom::bindings::utils::{str}; use dom::bindings::utils::{str};
use libc::c_uint; use dom::element::*;
use ptr::null; use dom::node::{Node, Element};
use dom::bindings::node::unwrap; use layout::layout_task;
use super::utils; 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) { extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
/*
debug!("element finalize!"); debug!("element finalize!");
unsafe { unsafe {
let val = JS_GetReservedSlot(obj, 0); let val = JS_GetReservedSlot(obj, 0);
let _node: ~NodeBundle = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); let _node: ~NodeBundle = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
} }
*/
} }
pub fn init(compartment: @mut Compartment) { pub fn init(compartment: @mut Compartment) {
/*
let obj = utils::define_empty_prototype(~"Element", Some(~"Node"), compartment); let obj = utils::define_empty_prototype(~"Element", Some(~"Node"), compartment);
let attrs = @~[ let attrs = @~[
JSPropertySpec { JSPropertySpec {
@ -74,11 +73,12 @@ pub fn init(compartment: @mut Compartment) {
vec::as_imm_buf(*attrs, |specs, _len| { vec::as_imm_buf(*attrs, |specs, _len| {
JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs); JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs);
}); });
*/
} }
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
-> JSBool { /*
unsafe { unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() { 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( *vp = RUST_INT_TO_JSVAL(
(width & (i32::max_value as int)) as libc::c_int); (width & (i32::max_value as int)) as libc::c_int);
return 1; return 1;
} }*/
return 0;
} }
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
-> JSBool { /*
unsafe { unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() { if obj.is_null() {
@ -137,12 +138,13 @@ extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa
} }
}; };
return 1; return 1;
} }*/
return 0;
} }
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
-> JSBool { /*
unsafe { unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() { if obj.is_null() {
@ -162,12 +164,13 @@ extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal)
} }
} }
}; };
} }*/
return 1; return 1;
} }
#[allow(non_implicitly_copyable_typarams)] #[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| { let proto = scope.write(&node, |nd| {
match &nd.kind { match &nd.kind {
&~Element(ref ed) => { &~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))); cast::reinterpret_cast(&squirrel_away_unique(~NodeBundle(node, scope)));
JS_SetReservedSlot(obj.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr)); JS_SetReservedSlot(obj.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr));
} }
return obj;
return obj;*/
fail!(~"stub");
} }

View file

@ -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::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval};
use dom::bindings::utils::{str}; use dom::bindings::utils::{str};
use libc::c_uint; use dom::node::{AbstractNode, Node};
use ptr::null;
use super::utils;
use super::element; 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; use js;
pub fn init(compartment: @mut Compartment) { pub fn init(compartment: @mut Compartment) {
/*
let obj = utils::define_empty_prototype(~"Node", None, compartment); let obj = utils::define_empty_prototype(~"Node", None, compartment);
let attrs = @~[ let attrs = @~[
@ -53,11 +55,12 @@ pub fn init(compartment: @mut Compartment) {
vec::push(&mut compartment.global_props, attrs); vec::push(&mut compartment.global_props, attrs);
vec::as_imm_buf(*attrs, |specs, _len| { vec::as_imm_buf(*attrs, |specs, _len| {
JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs); JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs);
}); });*/
} }
#[allow(non_implicitly_copyable_typarams)] #[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| { do scope.write(&node) |nd| {
match nd.kind { match nd.kind {
~Element(*) => element::create(cx, node, scope), ~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") ~Doctype(*) => fail!(~"no doctype node bindings yet")
} }
} }
*/
unsafe {
transmute(0)
}
} }
pub struct NodeBundle { pub struct NodeBundle {
node: Node, node: AbstractNode,
scope: NodeScope,
} }
pub fn NodeBundle(n: Node, s: NodeScope) -> NodeBundle { pub fn NodeBundle(n: AbstractNode) -> NodeBundle {
NodeBundle { NodeBundle {
node : n, node: n,
scope : s
} }
} }
@ -87,6 +92,7 @@ pub unsafe fn unwrap(obj: *JSObject) -> *rust_box<NodeBundle> {
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
/*
unsafe { unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() { if obj.is_null() {
@ -105,12 +111,13 @@ extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool
} }
} }
}; };
} }*/
return 1; return 1;
} }
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
/*
unsafe { unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() { if obj.is_null() {
@ -129,12 +136,13 @@ extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBoo
} }
} }
}; };
} }*/
return 1; return 1;
} }
impl NodeBundle { impl NodeBundle {
fn getNodeType() -> i32 { fn getNodeType() -> i32 {
/*
do self.node.read |nd| { do self.node.read |nd| {
match nd.kind { match nd.kind {
~Element(*) => 1, ~Element(*) => 1,
@ -142,11 +150,13 @@ impl NodeBundle {
~Comment(*) => 8, ~Comment(*) => 8,
~Doctype(*) => 10 ~Doctype(*) => 10
} }
} }*/
0
} }
} }
extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
/*
unsafe { unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() { 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 bundle = unwrap(obj);
let nodeType = (*bundle).payload.getNodeType(); let nodeType = (*bundle).payload.getNodeType();
*vp = INT_TO_JSVAL(nodeType); *vp = INT_TO_JSVAL(nodeType);
} }*/
return 1; return 1;
} }

View file

@ -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<T>`. 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<T,A> {
mut layout_active: bool,
mut free_list: ~[Handle<T,A>],
mut first_dirty: Handle<T,A>
}
struct ScopeResource<T,A> {
d : ScopeData<T,A>,
drop {
unsafe {
for self.d.free_list.each |h| { free_handle(*h); }
}
}
}
fn ScopeResource<T:Owned,A>(d : ScopeData<T,A>) -> ScopeResource<T,A> {
ScopeResource { d: d }
}
pub type Scope<T,A> = @ScopeResource<T,A>;
type HandleData<T,A> = {mut read_ptr: *T,
mut write_ptr: *mut T,
mut read_aux: *A,
mut next_dirty: Handle<T,A>};
pub enum Handle<T,A> {
_Handle(*HandleData<T,A>)
}
// Private methods
impl<T,A> Handle<T,A> {
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<T,A> { 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<T,A>) { unsafe { (**self).next_dirty = h; } }
pure fn is_null() -> bool { (*self).is_null() }
fn is_not_null() -> bool { (*self).is_not_null() }
}
impl<T:Owned,A> Handle<T,A> {
/// Access the reader's view of the handle's data
fn read<U>(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<U>(f: fn(&A) -> U) -> U {
unsafe {
assert self.has_aux();
f(&*self.read_aux())
}
}
}
impl<T:Owned,A> cmp::Eq for Handle<T,A> {
pure fn eq(&self, other: &Handle<T,A>) -> bool { **self == **other }
pure fn ne(&self, other: &Handle<T,A>) -> bool { **self != **other }
}
// Private methods
impl<T: Copy + Owned,A> Scope<T,A> {
fn clone(v: *T) -> *T {
unsafe {
let n: *mut T =
cast::reinterpret_cast(&libc::calloc(sys::size_of::<T>() as size_t, 1u as size_t));
// n.b.: this assignment will run the drop glue for <T,A>. *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: *T) {
let _x = *cast::reinterpret_cast::<*T,*mut T>(&t);
libc::free(cast::reinterpret_cast(&t));
}
unsafe fn free_handle<T,A>(h: Handle<T,A>) {
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<T:Owned, A>(handle: Handle<T,A>) -> *HandleData<T,A> {
*handle
}
pub unsafe fn wrap<T:Owned, A>(data: *HandleData<T,A>) -> Handle<T,A> {
_Handle(data)
}
fn null_handle<T:Owned,A>() -> Handle<T,A> {
_Handle(ptr::null())
}
pub fn Scope<T:Owned,A>() -> Scope<T,A> {
@ScopeResource(
ScopeData {
mut layout_active: false,
mut free_list: ~[],
mut first_dirty: null_handle()
})
}
// Writer methods
impl<T:Copy + Owned,A> Scope<T,A> {
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<U>(h: &Handle<T,A>, 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<U>(h: &Handle<T,A>, 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<T,A> {
unsafe {
debug!("vv: %?", *v);
let d: *HandleData<T,A> =
cast::reinterpret_cast(
&libc::malloc(sys::size_of::<HandleData<T,A>>() 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<animal, processed>;
#[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;
}
}

View file

@ -1,15 +1,14 @@
use dom::node::AbstractNode;
use newcss::stylesheet::Stylesheet; use newcss::stylesheet::Stylesheet;
use dom::node::{NodeScope, Node};
use std::arc::ARC; use std::arc::ARC;
pub struct Document { pub struct Document {
root: Node, root: AbstractNode,
scope: NodeScope,
} }
pub fn Document(root: Node, scope: NodeScope) -> Document { pub fn Document(root: AbstractNode) -> Document {
Document { Document {
root : root, root: root,
scope : scope,
} }
} }

View file

@ -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; use std::net::url::Url;
pub struct ElementData { pub struct Element {
tag_name: ~str, parent: Node,
kind: ~ElementKind, tag_name: ~str, // TODO: This should be an atom, not a ~str.
attrs: DVec<~Attr>, attrs: ~[Attr],
} }
#[allow(non_implicitly_copyable_typarams)] #[deriving_eq]
impl ElementData { pub enum ElementTypeId {
fn get_attr(name: &str) -> Option<~str> { HTMLAnchorElementTypeId,
let found = do self.attrs.find |attr| { name == attr.name }; HTMLAsideElementTypeId,
match found { HTMLBRElementTypeId,
Some(attr) => Some(copy attr.value), HTMLBodyElementTypeId,
None => None HTMLBoldElementTypeId,
} HTMLDivElementTypeId,
} HTMLFontElementTypeId,
HTMLFormElementTypeId,
// Gets an attribute without copying. HTMLHRElementTypeId,
// HTMLHeadElementTypeId,
// FIXME: Should not take a closure, but we need covariant type parameters for HTMLHeadingElementTypeId,
// that. HTMLHtmlElementTypeId,
fn with_attr<R>(name: &str, f: &fn(Option<&str>) -> R) -> R { HTMLImageElementTypeId,
for self.attrs.each |attr| { HTMLInputElementTypeId,
if name == attr.name { HTMLItalicElementTypeId,
let value: &str = attr.value; HTMLLinkElementTypeId,
return f(Some(value)); HTMLListItemElementTypeId,
} HTMLMetaElementTypeId,
} HTMLOListElementTypeId,
f(None) HTMLOptionElementTypeId,
} HTMLParagraphElementTypeId,
HTMLScriptElementTypeId,
fn set_attr(name: &str, value: ~str) { HTMLSectionElementTypeId,
let idx = do self.attrs.position |attr| { name == attr.name }; HTMLSelectElementTypeId,
match idx { HTMLSmallElementTypeId,
Some(idx) => self.attrs.set_elt(idx, ~Attr(name.to_str(), value)), HTMLSpanElementTypeId,
None => {} HTMLStyleElementTypeId,
} HTMLTableBodyElementTypeId,
} HTMLTableCellElementTypeId,
HTMLTableElementTypeId,
HTMLTableRowElementTypeId,
HTMLTitleElementTypeId,
HTMLUListElementTypeId,
UnknownElementTypeId,
} }
pub fn ElementData(tag_name: ~str, kind: ~ElementKind) -> ElementData { //
ElementData { // Regular old elements
tag_name : tag_name, //
kind : kind,
attrs : DVec(), 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<Url>,
}
//
// 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: ~[]
}
}
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);
}
}
return 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;
}
}
self.attrs.push(Attr::new(name.to_str(), value_cell.take()));
} }
} }
@ -54,23 +145,15 @@ pub struct Attr {
value: ~str, value: ~str,
} }
pub fn Attr(name: ~str, value: ~str) -> Attr { impl Attr {
static pub fn new(name: ~str, value: ~str) -> Attr {
Attr { Attr {
name : name, name: name,
value : value, value: value
} }
}
pub fn HTMLImageData() -> HTMLImageData {
HTMLImageData {
image: None
} }
} }
pub struct HTMLImageData {
mut image: Option<Url>
}
pub enum HeadingLevel { pub enum HeadingLevel {
Heading1, Heading1,
Heading2, Heading2,
@ -80,39 +163,3 @@ pub enum HeadingLevel {
Heading6, 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,
}

View file

@ -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::bindings;
use dom::document::Document; 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 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::debug::DebugMethods;
use layout::flow::FlowContext; 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 std::arc::ARC;
use util::tree;
use super::cow;
pub struct NodeData { //
tree: tree::Tree<Node>, // The basic Node structure
kind: ~NodeKind, //
/// 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) */ impl Eq for AbstractNode {
pub enum NodeTree { NodeTree } pure fn eq(&self, other: &AbstractNode) -> bool { self.obj == other.obj }
pure fn ne(&self, other: &AbstractNode) -> bool { self.obj != other.obj }
}
impl NodeTree { pub struct Node {
fn each_child(node: &Node, f: fn(&Node) -> bool) { type_id: NodeTypeId,
tree::each_child(&self, node, f)
parent_node: Option<AbstractNode>,
first_child: Option<AbstractNode>,
last_child: Option<AbstractNode>,
next_sibling: Option<AbstractNode>,
prev_sibling: Option<AbstractNode>,
// 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<CompleteSelectResults>,
flow: Option<@FlowContext>,
}
impl LayoutData {
static pub fn new() -> LayoutData {
LayoutData {
style: None,
flow: None,
} }
fn get_parent(node: &Node) -> Option<Node> {
tree::get_parent(&self, node)
} }
} }
impl tree::ReadMethods<Node> for NodeTree { //
fn with_tree_fields<R>(n: &Node, f: fn(&tree::Tree<Node>) -> R) -> R { // Basic node types
n.read(|n| f(&n.tree)) //
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 { pub struct Comment {
fn traverse_preorder(preorder_cb: &fn(Node)) { parent: Node,
preorder_cb(self); text: ~str,
do NodeTree.each_child(&self) |child| { child.traverse_preorder(preorder_cb); true } }
}
fn traverse_postorder(postorder_cb: &fn(Node)) { impl Comment {
do NodeTree.each_child(&self) |child| { child.traverse_postorder(postorder_cb); true } static pub fn new(text: ~str) -> Comment {
postorder_cb(self); Comment {
parent: Node::new(CommentNodeTypeId),
text: text
}
} }
} }
impl DebugMethods for Node { pub struct Text {
/* Dumps the subtree rooted at this node, for debugging. */ 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<AbstractNode> { self.with_imm_node(|n| n.parent_node) }
fn first_child(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.first_child) }
fn last_child(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.last_child) }
fn prev_sibling(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.prev_sibling) }
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
// 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<R>(self, f: &fn(&Node) -> R) -> R {
unsafe {
f(transmute(self.obj))
}
}
fn with_mut_node<R>(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<R>(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<R>(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<R>(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<R>(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<R>(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) { 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) { pure fn dump_indent(&self, indent: uint) {
let mut s = ~""; let mut s = ~"";
for uint::range(0u, indent) |_i| { for uint::range(0u, indent) |_i| {
@ -70,116 +328,46 @@ impl DebugMethods for Node {
// FIXME: this should have a pure version? // FIXME: this should have a pure version?
unsafe { unsafe {
for NodeTree.each_child(self) |kid| { for self.each_child() |kid| {
kid.dump_indent(indent + 1u) kid.dump_indent(indent + 1u)
} }
} }
} }
pure fn debug_str(&self) -> ~str { pure fn debug_str(&self) -> ~str {
// Unsafe due to the call to type_id().
unsafe { unsafe {
do self.read |n| { fmt!("%?", n.kind) } fmt!("%?", self.type_id())
} }
} }
} }
impl Node { impl Node {
fn is_element(&self) -> bool { static pub unsafe fn as_abstract_node<N>(node: ~N) -> AbstractNode {
self.read(|n| match *n.kind { Element(*) => true, _ => false } ) // 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) { pub fn define_bindings(compartment: @mut Compartment, doc: @Document, win: @Window) {
bindings::window::init(compartment, win); bindings::window::init(compartment, win);
bindings::document::init(compartment, doc); bindings::document::init(compartment, doc);
bindings::node::init(compartment); bindings::node::init(compartment);
bindings::element::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<CompleteSelectResults>,
mut flow: Option<@FlowContext>
}
pub type Node = cow::Handle<NodeData, LayoutData>;
pub type NodeScope = cow::Scope<NodeData, LayoutData>;
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<Node> {
tree::get_parent(&self, node)
}
}
#[allow(non_implicitly_copyable_typarams)]
impl tree::ReadMethods<Node> for NodeScope {
fn with_tree_fields<R>(node: &Node, f: fn(&tree::Tree<Node>) -> 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<Node> for NodeScope {
pure fn tree_eq(a: &Node, b: &Node) -> bool { a == b }
fn with_tree_fields<R>(node: &Node, f: fn(&tree::Tree<Node>) -> R) -> R {
self.write(node, |n| f(&n.tree))
}
}

View file

@ -1,24 +1,50 @@
use gfx::util::url::make_url;
use au = gfx::geometry;
use content::content_task::ContentTask; use content::content_task::ContentTask;
use dom::cow;
use dom::element::*; use dom::element::*;
use dom::event::{Event, ReflowEvent}; use dom::event::{Event, ReflowEvent};
use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, NodeScopeExtensions}; use dom::node::{AbstractNode, Comment, Doctype, Element, ElementNodeTypeId, Node, Text};
use dom::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::ImageCacheTask;
use resource::image_cache_task; use resource::image_cache_task;
use resource::resource_task::{Done, Load, Payload, ResourceTask}; use resource::resource_task::{Done, Load, Payload, ResourceTask};
use util::task::{spawn_listener, spawn_conversation}; use util::task::{spawn_listener, spawn_conversation};
use core::comm::{Chan, Port, SharedChan}; 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::Attribute;
use hubbub::hubbub; use hubbub::hubbub;
use newcss::stylesheet::Stylesheet; use std::cell::Cell;
use std::net::url::Url; use std::net::url::Url;
use std::net::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]]; type JSResult = ~[~[u8]];
enum CSSMessage { enum CSSMessage {
@ -32,11 +58,25 @@ enum JSMessage {
} }
struct HtmlParserResult { struct HtmlParserResult {
root: Node, root: AbstractNode,
style_port: Port<Option<Stylesheet>>, style_port: Port<Option<Stylesheet>>,
js_port: Port<JSResult>, js_port: Port<JSResult>,
} }
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. 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); 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 // TODO (Issue #85): use atoms
if tag == ~"a" { ~HTMLAnchorElement } handle_element!(tag, "a", HTMLAnchorElement, HTMLAnchorElementTypeId);
else if tag == ~"aside" { ~HTMLAsideElement } handle_element!(tag, "aside", HTMLAsideElement, HTMLAsideElementTypeId);
else if tag == ~"br" { ~HTMLBRElement } handle_element!(tag, "br", HTMLBRElement, HTMLBRElementTypeId);
else if tag == ~"body" { ~HTMLBodyElement } handle_element!(tag, "body", HTMLBodyElement, HTMLBodyElementTypeId);
else if tag == ~"bold" { ~HTMLBoldElement } handle_element!(tag, "bold", HTMLBoldElement, HTMLBoldElementTypeId);
else if tag == ~"div" { ~HTMLDivElement } handle_element!(tag, "div", HTMLDivElement, HTMLDivElementTypeId);
else if tag == ~"font" { ~HTMLFontElement } handle_element!(tag, "font", HTMLFontElement, HTMLFontElementTypeId);
else if tag == ~"form" { ~HTMLFormElement } handle_element!(tag, "form", HTMLFormElement, HTMLFormElementTypeId);
else if tag == ~"hr" { ~HTMLHRElement } handle_element!(tag, "hr", HTMLHRElement, HTMLHRElementTypeId);
else if tag == ~"head" { ~HTMLHeadElement } handle_element!(tag, "head", HTMLHeadElement, HTMLHeadElementTypeId);
else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) } handle_element!(tag, "html", HTMLHtmlElement, HTMLHtmlElementTypeId);
else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) } handle_element!(tag, "input", HTMLInputElement, HTMLInputElementTypeId);
else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) } handle_element!(tag, "i", HTMLItalicElement, HTMLItalicElementTypeId);
else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) } handle_element!(tag, "link", HTMLLinkElement, HTMLLinkElementTypeId);
else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) } handle_element!(tag, "li", HTMLListItemElement, HTMLListItemElementTypeId);
else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) } handle_element!(tag, "meta", HTMLMetaElement, HTMLMetaElementTypeId);
else if tag == ~"html" { ~HTMLHtmlElement } handle_element!(tag, "ol", HTMLOListElement, HTMLOListElementTypeId);
else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) } handle_element!(tag, "option", HTMLOptionElement, HTMLOptionElementTypeId);
else if tag == ~"input" { ~HTMLInputElement } handle_element!(tag, "p", HTMLParagraphElement, HTMLParagraphElementTypeId);
else if tag == ~"i" { ~HTMLItalicElement } handle_element!(tag, "script", HTMLScriptElement, HTMLScriptElementTypeId);
else if tag == ~"link" { ~HTMLLinkElement } handle_element!(tag, "section", HTMLSectionElement, HTMLSectionElementTypeId);
else if tag == ~"li" { ~HTMLListItemElement } handle_element!(tag, "select", HTMLSelectElement, HTMLSelectElementTypeId);
else if tag == ~"meta" { ~HTMLMetaElement } handle_element!(tag, "small", HTMLSmallElement, HTMLSmallElementTypeId);
else if tag == ~"ol" { ~HTMLOListElement } handle_element!(tag, "span", HTMLSpanElement, HTMLSpanElementTypeId);
else if tag == ~"option" { ~HTMLOptionElement } handle_element!(tag, "style", HTMLStyleElement, HTMLStyleElementTypeId);
else if tag == ~"p" { ~HTMLParagraphElement } handle_element!(tag, "tbody", HTMLTableBodyElement, HTMLTableBodyElementTypeId);
else if tag == ~"script" { ~HTMLScriptElement } handle_element!(tag, "td", HTMLTableCellElement, HTMLTableCellElementTypeId);
else if tag == ~"section" { ~HTMLSectionElement } handle_element!(tag, "table", HTMLTableElement, HTMLTableElementTypeId);
else if tag == ~"select" { ~HTMLSelectElement } handle_element!(tag, "tr", HTMLTableRowElement, HTMLTableRowElementTypeId);
else if tag == ~"small" { ~HTMLSmallElement } handle_element!(tag, "title", HTMLTitleElement, HTMLTitleElementTypeId);
else if tag == ~"span" { ~HTMLSpanElement } handle_element!(tag, "ul", HTMLUListElement, HTMLUListElementTypeId);
else if tag == ~"style" { ~HTMLStyleElement }
else if tag == ~"tbody" { ~HTMLTableBodyElement } handle_heading_element!(tag, "h1", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading1);
else if tag == ~"td" { ~HTMLTableCellElement } handle_heading_element!(tag, "h2", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading2);
else if tag == ~"table" { ~HTMLTableElement } handle_heading_element!(tag, "h3", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading3);
else if tag == ~"tr" { ~HTMLTableRowElement } handle_heading_element!(tag, "h4", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading4);
else if tag == ~"title" { ~HTMLTitleElement } handle_heading_element!(tag, "h5", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading5);
else if tag == ~"ul" { ~HTMLUListElement } handle_heading_element!(tag, "h6", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading6);
else { ~UnknownElement }
unsafe {
Node::as_abstract_node(~Element::new(UnknownElementTypeId, tag.to_str()))
}
} }
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
pub fn parse_html(scope: NodeScope, pub fn parse_html(url: Url,
url: Url,
resource_task: ResourceTask, resource_task: ResourceTask,
image_cache_task: ImageCacheTask) -> HtmlParserResult { image_cache_task: ImageCacheTask) -> HtmlParserResult {
// Spawn a CSS parser to receive links to CSS style sheets. // Spawn a CSS parser to receive links to CSS style sheets.
@ -185,39 +230,32 @@ pub fn parse_html(scope: NodeScope,
}; };
let js_chan = SharedChan(js_chan); let js_chan = SharedChan(js_chan);
let (scope, url) = (@copy scope, @url); let url = @url;
unsafe { unsafe {
// Build the root node. // 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"); debug!("created new node");
let parser = hubbub::Parser("UTF-8", false); let parser = hubbub::Parser("UTF-8", false);
debug!("created parser"); debug!("created parser");
parser.set_document_node(cast::transmute(cow::unwrap(root))); parser.set_document_node(root.to_hubbub_node());
parser.enable_scripting(true); parser.enable_scripting(true);
// Performs various actions necessary after appending has taken place. Currently, this consists // Performs various actions necessary after appending has taken place. Currently, this
// of processing inline stylesheets, but in the future it might perform prefetching, etc. // consists of processing inline stylesheets, but in the future it might perform
// prefetching, etc.
let css_chan2 = css_chan.clone(); let css_chan2 = css_chan.clone();
let append_hook: @fn(Node, Node) = |parent_node, child_node| { let append_hook: @fn(AbstractNode, AbstractNode) = |parent_node, child_node| {
do scope.read(&parent_node) |parent_node_contents| { if parent_node.is_style_element() && child_node.is_text() {
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"); debug!("found inline CSS stylesheet");
let url = url::from_str("http://example.com/"); // FIXME let url = url::from_str("http://example.com/"); // FIXME
let provenance = InlineProvenance(result::unwrap(url), let url_cell = Cell(url);
copy *data); 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)); css_chan2.send(CSSTaskNewFile(provenance));
} }
_ => {} // Nothing to do.
}
}
_ => {} // Nothing to do.
}
}
} }
}; };
@ -225,8 +263,9 @@ pub fn parse_html(scope: NodeScope,
parser.set_tree_handler(@hubbub::TreeHandler { parser.set_tree_handler(@hubbub::TreeHandler {
create_comment: |data: ~str| { create_comment: |data: ~str| {
debug!("create comment"); debug!("create comment");
let new_node = scope.new_node(Comment(data)); unsafe {
unsafe { cast::transmute(cow::unwrap(new_node)) } Node::as_abstract_node(~Comment::new(data)).to_hubbub_node()
}
}, },
create_doctype: |doctype: ~hubbub::Doctype| { create_doctype: |doctype: ~hubbub::Doctype| {
debug!("create doctype"); debug!("create doctype");
@ -240,69 +279,83 @@ pub fn parse_html(scope: NodeScope,
&None => None, &None => None,
&Some(ref id) => Some(copy *id) &Some(ref id) => Some(copy *id)
}; };
let data = DoctypeData(copy doctype.name, let node = ~Doctype::new(copy doctype.name,
public_id, public_id,
system_id, system_id,
copy doctype.force_quirks); doctype.force_quirks);
let new_node = scope.new_node(Doctype(data)); unsafe {
unsafe { cast::transmute(cow::unwrap(new_node)) } Node::as_abstract_node(node).to_hubbub_node()
}
}, },
create_element: |tag: ~hubbub::Tag| { create_element: |tag: ~hubbub::Tag| {
debug!("create element"); debug!("create element");
// TODO: remove copying here by using struct pattern matching to // TODO: remove copying here by using struct pattern matching to
// move all ~strs at once (blocked on Rust #3845, #3846, #3847) // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
let elem_kind = build_element_kind(tag.name); let node = build_element_from_tag(tag.name);
let elem = ElementData(copy tag.name, elem_kind);
debug!("-- attach attrs"); debug!("-- attach attrs");
do node.as_mut_element |element| {
for tag.attributes.each |attr| { for tag.attributes.each |attr| {
elem.attrs.push(~Attr(copy attr.name, copy attr.value)); element.attrs.push(Attr::new(copy attr.name, copy attr.value));
}
} }
// Spawn additional parsing, network loads, etc. from tag and attrs // Spawn additional parsing, network loads, etc. from tag and attrs
match elem.kind { match node.type_id() {
//Handle CSS style sheets from <link> elements // Handle CSS style sheets from <link> elements
~HTMLLinkElement => { ElementNodeTypeId(HTMLLinkElementTypeId) => {
match (elem.get_attr(~"rel"), elem.get_attr(~"href")) { do node.with_imm_element |element| {
match (element.get_attr(~"rel"), element.get_attr(~"href")) {
(Some(rel), Some(href)) => { (Some(rel), Some(href)) => {
if rel == ~"stylesheet" { if rel == ~"stylesheet" {
debug!("found CSS stylesheet: %s", href); debug!("found CSS stylesheet: %s", href);
css_chan2.send(CSSTaskNewFile(UrlProvenance(make_url( let url = make_url(href.to_str(), Some(copy *url));
href, Some(copy *url))))); css_chan2.send(CSSTaskNewFile(UrlProvenance(url)));
} }
} }
_ => {} _ => {}
} }
}
}, },
~HTMLImageElement(ref d) => { ElementNodeTypeId(HTMLImageElementTypeId) => {
do elem.get_attr(~"src").iter |img_url_str| { do node.with_mut_image_element |image_element| {
let img_url = make_url(copy *img_url_str, Some(copy *url)); let src_opt = image_element.parent.get_attr(~"src").map(|x| x.to_str());
d.image = Some(copy img_url); match src_opt {
None => {}
Some(src) => {
let img_url = make_url(src, Some(copy *url));
image_element.image = Some(copy img_url);
// inform the image cache to load this, but don't store a handle. // inform the image cache to load this, but don't store a handle.
// TODO (Issue #84): don't prefetch if we are within a <noscript> tag. // TODO (Issue #84): don't prefetch if we are within a <noscript>
// tag.
image_cache_task.send(image_cache_task::Prefetch(img_url)); image_cache_task.send(image_cache_task::Prefetch(img_url));
} }
} }
}
}
//TODO (Issue #86): handle inline styles ('style' attr) //TODO (Issue #86): handle inline styles ('style' attr)
_ => {} _ => {}
} }
let node = scope.new_node(Element(elem));
unsafe { cast::transmute(cow::unwrap(node)) } unsafe {
node.to_hubbub_node()
}
}, },
create_text: |data: ~str| { create_text: |data: ~str| {
debug!("create text"); debug!("create text");
let new_node = scope.new_node(Text(data)); unsafe {
unsafe { cast::transmute(cow::unwrap(new_node)) } Node::as_abstract_node(~Text::new(data)).to_hubbub_node()
}
}, },
ref_node: |_node| {}, ref_node: |_| {},
unref_node: |_node| {}, unref_node: |_| {},
append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| { append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| {
unsafe { unsafe {
debug!("append child %x %x", cast::transmute(parent), cast::transmute(child)); debug!("append child %x %x", cast::transmute(parent), cast::transmute(child));
let p: Node = cow::wrap(cast::transmute(parent)); let parent: AbstractNode = NodeWrapping::from_hubbub_node(parent);
let c: Node = cow::wrap(cast::transmute(child)); let child: AbstractNode = NodeWrapping::from_hubbub_node(child);
scope.add_child(p, c); parent.append_child(child);
append_hook(p, c); append_hook(parent, child);
} }
child child
}, },
@ -318,10 +371,7 @@ pub fn parse_html(scope: NodeScope,
debug!("clone node"); debug!("clone node");
unsafe { unsafe {
if deep { error!("-- deep clone unimplemented"); } if deep { error!("-- deep clone unimplemented"); }
let n: Node = cow::wrap(cast::transmute(node)); fail!(~"clone node unimplemented")
let data = n.read(|read_data| copy *read_data.kind);
let new_node = scope.new_node(data);
cast::transmute(cow::unwrap(new_node))
} }
}, },
reparent_children: |_node, _new_parent| { reparent_children: |_node, _new_parent| {
@ -351,29 +401,24 @@ pub fn parse_html(scope: NodeScope,
complete_script: |script| { complete_script: |script| {
// A little function for holding this lint attr // A little function for holding this lint attr
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
fn complete_script(scope: &NodeScope, fn complete_script(script: hubbub::NodeDataPtr,
script: hubbub::NodeDataPtr,
url: &Url, url: &Url,
js_chan: SharedChan<JSMessage>) { js_chan: SharedChan<JSMessage>) {
unsafe { unsafe {
do scope.read(&cow::wrap(cast::transmute(script))) |node_contents| { let script: AbstractNode = NodeWrapping::from_hubbub_node(script);
match *node_contents.kind { do script.with_imm_element |script| {
Element(ref element) if element.tag_name == ~"script" => { match script.get_attr(~"src") {
match element.get_attr(~"src") {
Some(src) => { Some(src) => {
debug!("found script: %s", src); debug!("found script: %s", src);
let new_url = make_url(src, Some(copy *url)); let new_url = make_url(src.to_str(), Some(copy *url));
js_chan.send(JSTaskNewFile(new_url)); js_chan.send(JSTaskNewFile(new_url));
} }
None => {} None => {}
} }
} }
_ => {}
} }
} }
} complete_script(script, url, js_chan2.clone());
}
complete_script(scope, script, url, js_chan2.clone());
debug!("complete script"); debug!("complete script");
} }
}); });

View file

@ -2,42 +2,36 @@
Code for managing the DOM aux pointer Code for managing the DOM aux pointer
*/ */
use dom::node::{Node, LayoutData}; use dom::node::{AbstractNode, LayoutData};
use core::dvec::DVec; use core::dvec::DVec;
pub trait LayoutAuxMethods { pub trait LayoutAuxMethods {
fn initialize_layout_data() -> Option<@LayoutData>; fn initialize_layout_data(self) -> Option<@mut LayoutData>;
fn initialize_style_for_subtree(refs: &DVec<@LayoutData>); fn initialize_style_for_subtree(self, refs: &DVec<@mut LayoutData>);
} }
impl LayoutAuxMethods for Node { impl LayoutAuxMethods for AbstractNode {
/** If none exists, creates empty layout data for the node (the reader-auxiliary /// If none exists, creates empty layout data for the node (the reader-auxiliary
* box in the COW model) and populates it with an empty style object. /// box in the COW model) and populates it with an empty style object.
*/ fn initialize_layout_data(self) -> Option<@mut LayoutData> {
fn initialize_layout_data() -> Option<@LayoutData> { if self.has_layout_data() {
match self.has_aux() { None
false => { } else {
let data = @LayoutData { let data = @mut LayoutData::new();
mut style : None, self.set_layout_data(data);
mut flow : None Some(data)
};
self.set_aux(data); Some(data)
},
true => None
} }
} }
/** /// Initializes layout data and styles for a Node tree, if any nodes do not have
* Initializes layout data and styles for a Node tree, if any nodes do not have /// this data already. Append created layout data to the task's GC roots.
* this data already. Append created layout data to the task's GC roots. fn initialize_style_for_subtree(self, refs: &DVec<@mut LayoutData>) {
*/ let _ = for self.traverse_preorder |n| {
fn initialize_style_for_subtree(refs: &DVec<@LayoutData>) {
do self.traverse_preorder |n| {
match n.initialize_layout_data() { match n.initialize_layout_data() {
Some(r) => refs.push(r), Some(r) => refs.push(r),
None => {} None => {}
} }
} };
} }
} }

View file

@ -1,8 +1,7 @@
/* Fundamental layout structures and algorithms. */ /* Fundamental layout structures and algorithms. */
use css::node_style::StyledNode; use css::node_style::StyledNode;
use dom::element::{ElementKind, HTMLDivElement, HTMLImageElement}; use dom::node::AbstractNode;
use dom::node::{Element, Node, NodeData, NodeKind, NodeTree};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::BoxedDebugMethods; use layout::debug::BoxedDebugMethods;
use layout::display_list_builder::DisplayListBuilder; use layout::display_list_builder::DisplayListBuilder;
@ -78,7 +77,7 @@ padding, backgrounds. It is analogous to a CSS nonreplaced content box.
*/ */
pub struct RenderBoxData { pub struct RenderBoxData {
/* originating DOM node */ /* originating DOM node */
node : Node, node : AbstractNode,
/* reference to containing flow context, which this box /* reference to containing flow context, which this box
participates in */ participates in */
ctx : @FlowContext, ctx : @FlowContext,
@ -110,7 +109,7 @@ pub enum SplitBoxResult {
SplitDidNotFit(Option<@RenderBox>, Option<@RenderBox>) SplitDidNotFit(Option<@RenderBox>, Option<@RenderBox>)
} }
pub fn RenderBoxData(node: Node, ctx: @FlowContext, id: int) -> RenderBoxData { pub fn RenderBoxData(node: AbstractNode, ctx: @FlowContext, id: int) -> RenderBoxData {
RenderBoxData { RenderBoxData {
node : node, node : node,
mut ctx : ctx, mut ctx : ctx,
@ -361,7 +360,7 @@ impl RenderBox {
fn with_style_of_nearest_element<R>(@self, f: &fn(CompleteStyle) -> R) -> R { fn with_style_of_nearest_element<R>(@self, f: &fn(CompleteStyle) -> R) -> R {
let mut node = self.d().node; let mut node = self.d().node;
while !node.is_element() { while !node.is_element() {
node = NodeTree.get_parent(&node).get(); node = node.parent_node().get();
} }
f(node.style()) f(node.style())
} }
@ -604,10 +603,10 @@ impl BoxedDebugMethods for RenderBox {
// Other methods // Other methods
impl RenderBox { impl RenderBox {
/// Returns the nearest ancestor-or-self element node. Infallible. /// Returns the nearest ancestor-or-self element node. Infallible.
fn nearest_ancestor_element(@self) -> Node { fn nearest_ancestor_element(@self) -> AbstractNode {
let mut node = self.d().node; let mut node = self.d().node;
while !node.is_element() { while !node.is_element() {
match NodeTree.get_parent(&node) { match node.parent_node() {
None => fail!(~"no nearest element?!"), None => fail!(~"no nearest element?!"),
Some(parent) => node = parent, Some(parent) => node = parent,
} }

View file

@ -1,10 +1,11 @@
/** Creates CSS boxes from a DOM. */ /** Creates CSS boxes from a DOM. */
use dom;
use dom::element::*; use dom::element::*;
use dom::node::{Comment, Doctype, Element, Text, Node, LayoutData}; use dom::node::{AbstractNode, Comment, CommentNodeTypeId, Doctype, DoctypeNodeTypeId, Element};
use layout::box::*; use dom::node::{ElementNodeTypeId, Node, Text, TextNodeTypeId};
use dom;
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::box::*;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::{BoxedDebugMethods, DebugMethods}; use layout::debug::{BoxedDebugMethods, DebugMethods};
use layout::flow::*; use layout::flow::*;
@ -46,8 +47,7 @@ enum InlineSpacerSide {
LogicalAfter, LogicalAfter,
} }
priv fn simulate_UA_display_rules(node: Node) -> CSSDisplay { priv fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay {
// FIXME // FIXME
/*let resolved = do node.aux |nd| { /*let resolved = do node.aux |nd| {
match nd.style.display_type { match nd.style.display_type {
@ -55,31 +55,28 @@ priv fn simulate_UA_display_rules(node: Node) -> CSSDisplay {
Specified(v) => v Specified(v) => v
} }
};*/ };*/
let resolved = CSSDisplayInline; let resolved = CSSDisplayInline;
if (resolved == CSSDisplayNone) { return resolved; } if (resolved == CSSDisplayNone) { return resolved; }
do node.read |n| { match node.type_id() {
let kind: &dom::node::NodeKind = n.kind; DoctypeNodeTypeId | CommentNodeTypeId => CSSDisplayNone,
match kind { TextNodeTypeId => CSSDisplayInline,
&Doctype(*) | &Comment(*) => CSSDisplayNone, ElementNodeTypeId(element_type_id) => {
&Text(*) => CSSDisplayInline, match element_type_id {
&Element(ref e) => { HTMLHeadElementTypeId |
let kind: &dom::element::ElementKind = e.kind; HTMLScriptElementTypeId => CSSDisplayNone,
match kind { HTMLParagraphElementTypeId |
&HTMLHeadElement(*) => CSSDisplayNone, HTMLDivElementTypeId |
&HTMLScriptElement(*) => CSSDisplayNone, HTMLBodyElementTypeId |
&HTMLParagraphElement(*) => CSSDisplayBlock, HTMLHeadingElementTypeId |
&HTMLDivElement(*) => CSSDisplayBlock, HTMLHtmlElementTypeId |
&HTMLBodyElement(*) => CSSDisplayBlock, HTMLUListElementTypeId |
&HTMLHeadingElement(*) => CSSDisplayBlock, HTMLOListElementTypeId => CSSDisplayBlock,
&HTMLHtmlElement(*) => CSSDisplayBlock,
&HTMLUListElement(*) => CSSDisplayBlock,
&HTMLOListElement(*) => CSSDisplayBlock,
_ => resolved _ => resolved
} }
} }
} }
}
} }
impl BoxGenerator { impl BoxGenerator {
@ -92,17 +89,19 @@ impl BoxGenerator {
} }
/* Whether "spacer" boxes are needed to stand in for this DOM node */ /* Whether "spacer" boxes are needed to stand in for this DOM node */
pure fn inline_spacers_needed_for_node(_node: Node) -> bool { pure fn inline_spacers_needed_for_node(_: AbstractNode) -> bool {
return false; return false;
} }
// TODO: implement this, generating spacer // TODO: implement this, generating spacer
fn make_inline_spacer_for_node_side(_ctx: &LayoutContext, _node: Node, fn make_inline_spacer_for_node_side(_: &LayoutContext,
_side: InlineSpacerSide) -> Option<@RenderBox> { _: AbstractNode,
_: InlineSpacerSide)
-> Option<@RenderBox> {
None None
} }
pub fn push_node(ctx: &LayoutContext, builder: &LayoutTreeBuilder, node: Node) { pub fn push_node(ctx: &LayoutContext, builder: &LayoutTreeBuilder, node: AbstractNode) {
debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.d().id, node.debug_str()); debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.d().id, node.debug_str());
// first, determine the box type, based on node characteristics // first, determine the box type, based on node characteristics
@ -122,12 +121,13 @@ impl BoxGenerator {
self.range_stack.push(node_range_start); self.range_stack.push(node_range_start);
// if a leaf, make a box. // if a leaf, make a box.
if tree::is_leaf(&NodeTree, &node) { if node.is_leaf() {
let new_box = builder.make_box(ctx, box_type, node, self.flow); let new_box = builder.make_box(ctx, box_type, node, self.flow);
self.flow.inline().boxes.push(new_box); self.flow.inline().boxes.push(new_box);
} // else, maybe make a spacer for "left" margin, border, padding } else if self.inline_spacers_needed_for_node(node) {
else if self.inline_spacers_needed_for_node(node) { // else, maybe make a spacer for "left" margin, border, padding
do self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).iter |spacer: &@RenderBox| { do self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).iter
|spacer: &@RenderBox| {
self.flow.inline().boxes.push(*spacer); self.flow.inline().boxes.push(*spacer);
} }
} }
@ -156,7 +156,7 @@ impl BoxGenerator {
} }
} }
pub fn pop_node(ctx: &LayoutContext, _builder: &LayoutTreeBuilder, node: Node) { pub fn pop_node(ctx: &LayoutContext, _builder: &LayoutTreeBuilder, node: AbstractNode) {
debug!("BoxGenerator[f%d]: popping node: %s", self.flow.d().id, node.debug_str()); debug!("BoxGenerator[f%d]: popping node: %s", self.flow.d().id, node.debug_str());
match self.flow { match self.flow {
@ -241,8 +241,9 @@ impl BuilderContext {
// returns a context for the current node, or None if the document subtree rooted // returns a context for the current node, or None if the document subtree rooted
// by the node should not generate a layout tree. For example, nodes with style 'display:none' // by the node should not generate a layout tree. For example, nodes with style 'display:none'
// should just not generate any flows or boxes. // should just not generate any flows or boxes.
fn containing_context_for_node(node: Node, fn containing_context_for_node(node: AbstractNode,
builder: &LayoutTreeBuilder) -> Option<BuilderContext> { builder: &LayoutTreeBuilder)
-> Option<BuilderContext> {
// TODO: remove this once UA styles work // TODO: remove this once UA styles work
// TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7) // TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7)
let simulated_display = match simulate_UA_display_rules(node) { let simulated_display = match simulate_UA_display_rules(node) {
@ -254,7 +255,7 @@ impl BuilderContext {
(CSSDisplayBlock, @RootFlow(*)) => { (CSSDisplayBlock, @RootFlow(*)) => {
// If this is the root node, then use the root flow's // If this is the root node, then use the root flow's
// context. Otherwise, make a child block context. // context. Otherwise, make a child block context.
match NodeTree.get_parent(&node) { match node.parent_node() {
Some(_) => { self.create_child_flow_of_type(Flow_Block, builder) } Some(_) => { self.create_child_flow_of_type(Flow_Block, builder) }
None => { self.clone() }, None => { self.clone() },
} }
@ -276,28 +277,31 @@ impl BuilderContext {
impl LayoutTreeBuilder { impl LayoutTreeBuilder {
/* Debug-only ids */ /* Debug-only ids */
fn next_box_id() -> int { self.next_bid += 1; self.next_bid } fn next_box_id(&self) -> int { self.next_bid += 1; self.next_bid }
fn next_flow_id() -> int { self.next_cid += 1; self.next_cid } fn next_flow_id(&self) -> int { self.next_cid += 1; self.next_cid }
/** Creates necessary box(es) and flow context(s) for the current DOM node, /** Creates necessary box(es) and flow context(s) for the current DOM node,
and recurses on its children. */ and recurses on its children. */
fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node, parent_ctx: &BuilderContext) { fn construct_recursively(&self,
layout_ctx: &LayoutContext,
cur_node: AbstractNode,
parent_ctx: &BuilderContext) {
debug!("Considering node: %s", cur_node.debug_str()); debug!("Considering node: %s", cur_node.debug_str());
let this_ctx = match parent_ctx.containing_context_for_node(cur_node, &self) { let this_ctx = match parent_ctx.containing_context_for_node(cur_node, self) {
Some(ctx) => ctx, Some(ctx) => ctx,
None => { return; } // no context because of display: none. Stop building subtree. None => { return; } // no context because of display: none. Stop building subtree.
}; };
debug!("point a: %s", cur_node.debug_str()); debug!("point a: %s", cur_node.debug_str());
this_ctx.default_collector.push_node(layout_ctx, &self, cur_node); this_ctx.default_collector.push_node(layout_ctx, self, cur_node);
debug!("point b: %s", cur_node.debug_str()); debug!("point b: %s", cur_node.debug_str());
// recurse on child nodes. // recurse on child nodes.
for tree::each_child(&NodeTree, &cur_node) |child_node| { for cur_node.each_child |child_node| {
self.construct_recursively(layout_ctx, *child_node, &this_ctx); self.construct_recursively(layout_ctx, child_node, &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);
// store reference to the flow context which contains any // store reference to the flow context which contains any
@ -306,8 +310,8 @@ impl LayoutTreeBuilder {
// nodes and FlowContexts should not change during layout. // nodes and FlowContexts should not change during layout.
for tree::each_child(&FlowTree, &this_ctx.default_collector.flow) |child_flow: &@FlowContext| { for tree::each_child(&FlowTree, &this_ctx.default_collector.flow) |child_flow: &@FlowContext| {
do (copy child_flow.d().node).iter |node| { do (copy child_flow.d().node).iter |node| {
assert node.has_aux(); assert node.has_layout_data();
do node.aux |data| { data.flow = Some(*child_flow) } node.layout_data().flow = Some(*child_flow);
} }
} }
} }
@ -321,7 +325,7 @@ impl LayoutTreeBuilder {
// The latter can only be done immediately adjacent to, or at the // The latter can only be done immediately adjacent to, or at the
// beginning or end of a block flow. Otherwise, the whitespace // beginning or end of a block flow. Otherwise, the whitespace
// might affect whitespace collapsing with adjacent text. // might affect whitespace collapsing with adjacent text.
fn simplify_children_of_flow(_layout_ctx: &LayoutContext, parent_ctx: &BuilderContext) { fn simplify_children_of_flow(&self, _: &LayoutContext, parent_ctx: &BuilderContext) {
match *parent_ctx.default_collector.flow { match *parent_ctx.default_collector.flow {
InlineFlow(*) => { InlineFlow(*) => {
let mut found_child_inline = false; let mut found_child_inline = false;
@ -369,14 +373,15 @@ impl LayoutTreeBuilder {
} }
} }
fn fixup_split_inline(_foo: @FlowContext) { fn fixup_split_inline(&self, _: @FlowContext) {
// TODO: finish me. // TODO: finish me.
fail!(~"TODO: handle case where an inline is split by a block") fail!(~"TODO: handle case where an inline is split by a block")
} }
/** entry point for box creation. Should only be /** entry point for box creation. Should only be
called on root DOM element. */ called on root DOM element. */
fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@FlowContext, ()> { fn construct_trees(&self, layout_ctx: &LayoutContext, root: AbstractNode)
-> Result<@FlowContext, ()> {
let new_flow = self.make_flow(Flow_Root); let new_flow = self.make_flow(Flow_Root);
let new_generator = @BoxGenerator::new(new_flow); let new_generator = @BoxGenerator::new(new_flow);
let root_ctx = BuilderContext::new(new_generator); let root_ctx = BuilderContext::new(new_generator);
@ -386,7 +391,7 @@ impl LayoutTreeBuilder {
return Ok(new_flow) return Ok(new_flow)
} }
fn make_flow(ty : FlowContextType) -> @FlowContext { fn make_flow(&self, ty: FlowContextType) -> @FlowContext {
let data = FlowData(self.next_flow_id()); let data = FlowData(self.next_flow_id());
let ret = match ty { let ret = match ty {
Flow_Absolute => @AbsoluteFlow(data), Flow_Absolute => @AbsoluteFlow(data),
@ -405,7 +410,12 @@ impl LayoutTreeBuilder {
disambiguate between different methods here instead of inlining, since each disambiguate between different methods here instead of inlining, since each
case has very different complexity case has very different complexity
*/ */
fn make_box(layout_ctx: &LayoutContext, ty: RenderBoxType, node: Node, ctx: @FlowContext) -> @RenderBox { fn make_box(&self,
layout_ctx: &LayoutContext,
ty: RenderBoxType,
node: AbstractNode,
ctx: @FlowContext)
-> @RenderBox {
let ret = match ty { let ret = match ty {
RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx), RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx),
RenderBox_Text => self.make_text_box(layout_ctx, node, ctx), RenderBox_Text => self.make_text_box(layout_ctx, node, ctx),
@ -415,63 +425,65 @@ impl LayoutTreeBuilder {
ret ret
} }
fn make_generic_box(_layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox { fn make_generic_box(&self,
_: &LayoutContext,
node: AbstractNode,
ctx: @FlowContext)
-> @RenderBox {
@GenericBox(RenderBoxData(copy node, ctx, self.next_box_id())) @GenericBox(RenderBoxData(copy node, ctx, self.next_box_id()))
} }
fn make_image_box(layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox { fn make_image_box(&self,
do node.read |n| { layout_ctx: &LayoutContext,
match n.kind { node: AbstractNode,
~Element(ref ed) => match ed.kind { ctx: @FlowContext)
~HTMLImageElement(ref d) => { -> @RenderBox {
// TODO: this could be written as a pattern guard, but it triggers if !node.is_image_element() {
// an ICE (mozilla/rust issue #3601) fail!(~"WAT error: why couldn't we make an image box?");
if d.image.is_some() { }
let holder = ImageHolder::new({copy *d.image.get_ref()},
layout_ctx.image_cache);
do node.with_imm_image_element |image_element| {
if image_element.image.is_some() {
let holder = ImageHolder::new(copy *image_element.image.get_ref(),
layout_ctx.image_cache);
@ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder) @ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder)
} else { } else {
info!("Tried to make image box, but couldn't find image. Made generic box instead."); info!("Tried to make image box, but couldn't find image. Made generic box instead.");
self.make_generic_box(layout_ctx, node, ctx) self.make_generic_box(layout_ctx, node, ctx)
} }
},
_ => fail!(~"WAT error: why couldn't we make an image box?")
},
_ => fail!(~"WAT error: why couldn't we make an image box?")
} }
} }
fn make_text_box(&self,
_: &LayoutContext,
node: AbstractNode,
ctx: @FlowContext)
-> @RenderBox {
if !node.is_text() {
fail!(~"WAT error: why couldn't we make a text box?");
} }
fn make_text_box(_layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox { // FIXME: Don't copy text. I guess it should be atomically reference counted?
do node.read |n| { do node.with_imm_text |text_node| {
match n.kind { let string = text_node.text.to_str();
~Text(ref string) => @UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), copy *string), @UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), string)
_ => fail!(~"WAT error: why couldn't we make a text box?")
}
} }
} }
fn decide_box_type(node: Node, display: CSSDisplay) -> RenderBoxType { fn decide_box_type(&self, node: AbstractNode, display: CSSDisplay) -> RenderBoxType {
do node.read |n| { if node.is_text() {
match n.kind { RenderBox_Text
~Doctype(*) | ~Comment(*) => { } else if node.is_image_element() {
fail!(~"Hey, doctypes and comments shouldn't get here! \ do node.with_imm_image_element |image_element| {
They are display:none!") match image_element.image {
} Some(_) => RenderBox_Image,
~Text(*) => RenderBox_Text, None => RenderBox_Generic,
~Element(ref element) => {
match (&element.kind, display) {
(&~HTMLImageElement(ref d), _) if d.image.is_some() => RenderBox_Image,
// (_, Specified(_)) => GenericBox,
(_, _) => RenderBox_Generic // TODO: replace this with the commented lines
//(_, _) => {
// fail!(~"Can't create box for Node with non-specified 'display' type")
//}
}
} }
} }
} else if node.is_element() {
RenderBox_Generic
} else {
fail!(~"Hey, doctypes and comments shouldn't get here! They are display:none!")
} }
} }
} }

View file

@ -1,22 +1,25 @@
use newcss::values::Specified; ///
use newcss::values::{CSSBackgroundColorColor, CSSBackgroundColorTransparent}; /// Constructs display lists from render boxes.
use dom::node::{Text, NodeScope}; ///
use dom::cow::Scope;
use dvec::DVec;
use either::{Left, Right};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layout::box::{RenderBox, TextBox}; use layout::box::{RenderBox, TextBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::flow::FlowContext; use layout::flow::FlowContext;
use layout::text::TextBoxData; use layout::text::TextBoxData;
use newcss::values::Specified;
use newcss::values::{CSSBackgroundColorColor, CSSBackgroundColorTransparent};
use util::tree; use util::tree;
use vec::push;
use gfx; use core::dvec::DVec;
use core::either::{Left, Right};
use core::mutable::Mut;
use core::vec::push;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use gfx::geometry::Au; use gfx::geometry::Au;
use core::mutable::Mut; use gfx;
/** A builder object that manages display list builder should mainly /** A builder object that manages display list builder should mainly
hold information about the initial request and desired result---for hold information about the initial request and desired result---for

View file

@ -1,5 +1,5 @@
use core; use core;
use dom::node::Node; use dom::node::AbstractNode;
use layout::block::{BlockFlowData, BlockLayout}; use layout::block::{BlockFlowData, BlockLayout};
use layout::box::RenderBox; use layout::box::RenderBox;
use layout::context::LayoutContext; use layout::context::LayoutContext;
@ -67,7 +67,7 @@ enum FlowContextType {
/* A particular kind of layout context. It manages the positioning of /* A particular kind of layout context. It manages the positioning of
render boxes within the context. */ render boxes within the context. */
struct FlowData { struct FlowData {
mut node: Option<Node>, mut node: Option<AbstractNode>,
/* reference to parent, children flow contexts */ /* reference to parent, children flow contexts */
tree: tree::Tree<@FlowContext>, tree: tree::Tree<@FlowContext>,
/* TODO (Issue #87): debug only */ /* TODO (Issue #87): debug only */
@ -177,8 +177,10 @@ impl FlowContext {
} }
} }
pure fn foldl_boxes_for_node<B: Copy>(node: Node, seed: B, pure fn foldl_boxes_for_node<B: Copy>(node: AbstractNode,
cb: pure fn&(a: B,@RenderBox) -> B) -> B { seed: B,
cb: pure fn&(a: B,@RenderBox) -> B)
-> B {
do self.foldl_all_boxes(seed) |acc, box| { do self.foldl_all_boxes(seed) |acc, box| {
if box.d().node == node { cb(acc, box) } if box.d().node == node { cb(acc, box) }
else { acc } else { acc }
@ -194,7 +196,7 @@ impl FlowContext {
} }
} }
pure fn iter_boxes_for_node<T>(node: Node, pure fn iter_boxes_for_node<T>(node: AbstractNode,
cb: pure fn&(@RenderBox) -> T) { cb: pure fn&(@RenderBox) -> T) {
do self.iter_all_boxes |box| { do self.iter_all_boxes |box| {
if box.d().node == node { cb(box); } if box.d().node == node { cb(box); }

View file

@ -1,5 +1,5 @@
use core; use core;
use dom::node::Node; use dom::node::AbstractNode;
use layout::box::*; use layout::box::*;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::{BoxedDebugMethods, DebugMethods}; use layout::debug::{BoxedDebugMethods, DebugMethods};
@ -41,12 +41,12 @@ hard to try out that alternative.
*/ */
pub struct NodeRange { pub struct NodeRange {
node: Node, node: AbstractNode,
range: Range, range: Range,
} }
pub impl NodeRange { pub impl NodeRange {
static pure fn new(node: Node, range: &const Range) -> NodeRange { static pure fn new(node: AbstractNode, range: &const Range) -> NodeRange {
NodeRange { node: node, range: copy *range } NodeRange { node: node, range: copy *range }
} }
} }
@ -60,7 +60,7 @@ impl ElementMapping {
ElementMapping { entries: DVec() } ElementMapping { entries: DVec() }
} }
fn add_mapping(node: Node, range: &const Range) { fn add_mapping(node: AbstractNode, range: &const Range) {
self.entries.push(NodeRange::new(node, range)) self.entries.push(NodeRange::new(node, range))
} }

View file

@ -5,7 +5,7 @@ use content::content_task;
use css::matching::MatchMethods; use css::matching::MatchMethods;
use css::select::new_css_select_ctx; use css::select::new_css_select_ctx;
use dom::event::{Event, ReflowEvent}; use dom::event::{Event, ReflowEvent};
use dom::node::{Node, LayoutData}; use dom::node::{AbstractNode, LayoutData};
use layout::aux::LayoutAuxMethods; use layout::aux::LayoutAuxMethods;
use layout::box::RenderBox; use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder; use layout::box_builder::LayoutTreeBuilder;
@ -42,7 +42,7 @@ use std::net::url::Url;
pub type LayoutTask = SharedChan<Msg>; pub type LayoutTask = SharedChan<Msg>;
pub enum LayoutQuery { pub enum LayoutQuery {
ContentBox(Node) ContentBox(AbstractNode)
} }
pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>; pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;
@ -53,7 +53,7 @@ enum LayoutQueryResponse_ {
pub enum Msg { pub enum Msg {
AddStylesheet(Stylesheet), AddStylesheet(Stylesheet),
BuildMsg(BuildData), BuildMsg(~BuildData),
QueryMsg(LayoutQuery, Chan<LayoutQueryResponse>), QueryMsg(LayoutQuery, Chan<LayoutQueryResponse>),
ExitMsg ExitMsg
} }
@ -77,7 +77,7 @@ impl Damage {
} }
pub struct BuildData { pub struct BuildData {
node: Node, node: AbstractNode,
url: Url, url: Url,
dom_event_chan: comm::SharedChan<Event>, dom_event_chan: comm::SharedChan<Event>,
window_size: Size2D<uint>, window_size: Size2D<uint>,
@ -100,8 +100,8 @@ struct Layout {
from_content: Port<Msg>, from_content: Port<Msg>,
font_ctx: @FontContext, font_ctx: @FontContext,
// This is used to root auxilliary RCU reader data // This is used to root reader data
layout_refs: DVec<@LayoutData>, layout_refs: DVec<@mut LayoutData>,
css_select_ctx: Mut<SelectCtx>, css_select_ctx: Mut<SelectCtx>,
} }
@ -167,7 +167,7 @@ impl Layout {
} }
} }
fn handle_build(data: BuildData) { fn handle_build(data: &BuildData) {
let node = &data.node; let node = &data.node;
// FIXME: Bad copy // FIXME: Bad copy
let doc_url = copy data.url; let doc_url = copy data.url;
@ -261,11 +261,10 @@ impl Layout {
reply_chan: Chan<LayoutQueryResponse>) { reply_chan: Chan<LayoutQueryResponse>) {
match query { match query {
ContentBox(node) => { ContentBox(node) => {
let response = do node.aux |a| { let response = match node.layout_data().flow {
match a.flow {
None => Err(()), None => Err(()),
Some(flow) => { Some(flow) => {
let start_val : Option<Rect<Au>> = None; let start_val: Option<Rect<Au>> = None;
let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| { let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
match acc { match acc {
Some(acc) => Some(acc.union(&box.content_box())), Some(acc) => Some(acc.union(&box.content_box())),
@ -282,7 +281,6 @@ impl Layout {
} }
} }
} }
}
}; };
reply_chan.send(response) reply_chan.send(response)

View file

@ -46,7 +46,6 @@ pub mod dom {
pub mod utils; pub mod utils;
pub mod window; pub mod window;
} }
pub mod cow;
pub mod document; pub mod document;
pub mod element; pub mod element;
pub mod event; pub mod event;