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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,36 +1,38 @@
use dom::node::{Node, NodeData, NodeTree, Doctype, Comment, Element, Text};
///
/// Implementation of the callbacks that the CSS selector engine uses to query the DOM.
///
use dom::node::AbstractNode;
use newcss::select::SelectHandler;
use util::tree;
use core::str::eq_slice;
pub struct NodeSelectHandler {
node: Node
node: AbstractNode
}
fn with_node_name<R>(data: &NodeData, f: &fn(&str) -> R) -> R {
match *data.kind {
Element(ref data) => f(data.tag_name),
_ => fail!(~"attempting to style non-element node")
fn with_node_name<R>(node: AbstractNode, f: &fn(&str) -> R) -> R {
if !node.is_element() {
fail!(~"attempting to style non-element node");
}
do node.with_imm_element |element_n| {
f(element_n.tag_name)
}
}
impl SelectHandler<Node> for NodeSelectHandler {
fn with_node_name<R>(node: &Node, f: &fn(&str) -> R) -> R {
do node.read |data| {
with_node_name(data, f)
}
impl SelectHandler<AbstractNode> for NodeSelectHandler {
fn with_node_name<R>(node: &AbstractNode, f: &fn(&str) -> R) -> R {
with_node_name(*node, f)
}
fn named_parent_node(node: &Node, name: &str) -> Option<Node> {
let parent = tree::parent(&NodeTree, node);
match parent {
fn named_parent_node(node: &AbstractNode, name: &str) -> Option<AbstractNode> {
match node.parent_node() {
Some(parent) => {
do parent.read |data| {
do with_node_name(data) |node_name| {
if name == node_name {
Some(parent)
} else {
None
}
do with_node_name(parent) |node_name| {
if eq_slice(name, node_name) {
Some(parent)
} else {
None
}
}
}
@ -38,23 +40,21 @@ impl SelectHandler<Node> for NodeSelectHandler {
}
}
fn parent_node(node: &Node) -> Option<Node> {
tree::parent(&NodeTree, node)
fn parent_node(node: &AbstractNode) -> Option<AbstractNode> {
node.parent_node()
}
// 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;
loop {
let parent = tree::parent(&NodeTree, &node);
let parent = node.parent_node();
match parent {
Some(parent) => {
let mut found = false;
do parent.read |data| {
do with_node_name(data) |node_name| {
if name == node_name {
found = true;
}
do with_node_name(parent) |node_name| {
if eq_slice(name, node_name) {
found = true;
}
}
if found {
@ -67,31 +67,27 @@ impl SelectHandler<Node> for NodeSelectHandler {
}
}
fn node_is_root(node: &Node) -> bool {
fn node_is_root(node: &AbstractNode) -> bool {
self.parent_node(node).is_none()
}
fn with_node_id<R>(node: &Node, f: &fn(Option<&str>) -> R) -> R {
do node.read |data| {
match *data.kind {
Element(ref data) => data.with_attr("id", f),
_ => fail!(~"attempting to style non-element node")
}
fn with_node_id<R>(node: &AbstractNode, f: &fn(Option<&str>) -> R) -> R {
if !node.is_element() {
fail!(~"attempting to style non-element node");
}
do node.with_imm_element() |element_n| {
f(element_n.get_attr("id"))
}
}
fn node_has_id(node: &Node, id: &str) -> bool {
do node.read |data| {
match *data.kind {
Element(ref data) => {
do data.with_attr("id") |existing_id_opt| {
match existing_id_opt {
None => false,
Some(existing_id) => str::eq_slice(id, existing_id)
}
}
}
_ => fail!(~"attempting to style non-element node")
fn node_has_id(node: &AbstractNode, id: &str) -> bool {
if !node.is_element() {
fail!(~"attempting to style non-element node");
}
do node.with_imm_element |element_n| {
match element_n.get_attr("id") {
None => false,
Some(existing_id) => id == existing_id
}
}
}

View file

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

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

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::{str};
use libc::c_uint;
use ptr::null;
use super::utils;
use dom::node::{AbstractNode, Node};
use super::element;
use super::utils;
use core::cast::transmute;
use core::libc::c_uint;
use core::ptr::null;
use js::glue::bindgen::*;
use js::jsapi::bindgen::*;
use js::jsapi::bindgen::{JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate};
use js::jsapi::bindgen::{JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN};
use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError};
use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec};
use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper};
use js::jsval::{INT_TO_JSVAL, JSVAL_TO_PRIVATE};
use js::rust::{Compartment, jsobj};
use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL};
use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS};
use js;
pub fn init(compartment: @mut Compartment) {
/*
let obj = utils::define_empty_prototype(~"Node", None, compartment);
let attrs = @~[
@ -53,11 +55,12 @@ pub fn init(compartment: @mut Compartment) {
vec::push(&mut compartment.global_props, attrs);
vec::as_imm_buf(*attrs, |specs, _len| {
JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs);
});
});*/
}
#[allow(non_implicitly_copyable_typarams)]
pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj {
pub fn create(cx: *JSContext, node: Node) -> jsobj {
/*
do scope.write(&node) |nd| {
match nd.kind {
~Element(*) => element::create(cx, node, scope),
@ -66,17 +69,19 @@ pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj {
~Doctype(*) => fail!(~"no doctype node bindings yet")
}
}
*/
unsafe {
transmute(0)
}
}
pub struct NodeBundle {
node: Node,
scope: NodeScope,
node: AbstractNode,
}
pub fn NodeBundle(n: Node, s: NodeScope) -> NodeBundle {
pub fn NodeBundle(n: AbstractNode) -> NodeBundle {
NodeBundle {
node : n,
scope : s
node: n,
}
}
@ -87,6 +92,7 @@ pub unsafe fn unwrap(obj: *JSObject) -> *rust_box<NodeBundle> {
#[allow(non_implicitly_copyable_typarams)]
extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
/*
unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() {
@ -105,12 +111,13 @@ extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool
}
}
};
}
}*/
return 1;
}
#[allow(non_implicitly_copyable_typarams)]
extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
/*
unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() {
@ -129,12 +136,13 @@ extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBoo
}
}
};
}
}*/
return 1;
}
impl NodeBundle {
fn getNodeType() -> i32 {
/*
do self.node.read |nd| {
match nd.kind {
~Element(*) => 1,
@ -142,11 +150,13 @@ impl NodeBundle {
~Comment(*) => 8,
~Doctype(*) => 10
}
}
}*/
0
}
}
extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
/*
unsafe {
let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
if obj.is_null() {
@ -156,6 +166,6 @@ extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
let bundle = unwrap(obj);
let nodeType = (*bundle).payload.getNodeType();
*vp = INT_TO_JSVAL(nodeType);
}
}*/
return 1;
}

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 dom::node::{NodeScope, Node};
use std::arc::ARC;
pub struct Document {
root: Node,
scope: NodeScope,
root: AbstractNode,
}
pub fn Document(root: Node, scope: NodeScope) -> Document {
pub fn Document(root: AbstractNode) -> Document {
Document {
root : root,
scope : scope,
root: root,
}
}

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;
pub struct ElementData {
tag_name: ~str,
kind: ~ElementKind,
attrs: DVec<~Attr>,
pub struct Element {
parent: Node,
tag_name: ~str, // TODO: This should be an atom, not a ~str.
attrs: ~[Attr],
}
#[allow(non_implicitly_copyable_typarams)]
impl ElementData {
fn get_attr(name: &str) -> Option<~str> {
let found = do self.attrs.find |attr| { name == attr.name };
match found {
Some(attr) => Some(copy attr.value),
None => None
#[deriving_eq]
pub enum ElementTypeId {
HTMLAnchorElementTypeId,
HTMLAsideElementTypeId,
HTMLBRElementTypeId,
HTMLBodyElementTypeId,
HTMLBoldElementTypeId,
HTMLDivElementTypeId,
HTMLFontElementTypeId,
HTMLFormElementTypeId,
HTMLHRElementTypeId,
HTMLHeadElementTypeId,
HTMLHeadingElementTypeId,
HTMLHtmlElementTypeId,
HTMLImageElementTypeId,
HTMLInputElementTypeId,
HTMLItalicElementTypeId,
HTMLLinkElementTypeId,
HTMLListItemElementTypeId,
HTMLMetaElementTypeId,
HTMLOListElementTypeId,
HTMLOptionElementTypeId,
HTMLParagraphElementTypeId,
HTMLScriptElementTypeId,
HTMLSectionElementTypeId,
HTMLSelectElementTypeId,
HTMLSmallElementTypeId,
HTMLSpanElementTypeId,
HTMLStyleElementTypeId,
HTMLTableBodyElementTypeId,
HTMLTableCellElementTypeId,
HTMLTableElementTypeId,
HTMLTableRowElementTypeId,
HTMLTitleElementTypeId,
HTMLUListElementTypeId,
UnknownElementTypeId,
}
//
// Regular old elements
//
pub struct HTMLAnchorElement { parent: Element }
pub struct HTMLAsideElement { parent: Element }
pub struct HTMLBRElement { parent: Element }
pub struct HTMLBodyElement { parent: Element }
pub struct HTMLBoldElement { parent: Element }
pub struct HTMLDivElement { parent: Element }
pub struct HTMLFontElement { parent: Element }
pub struct HTMLFormElement { parent: Element }
pub struct HTMLHRElement { parent: Element }
pub struct HTMLHeadElement { parent: Element }
pub struct HTMLHtmlElement { parent: Element }
pub struct HTMLInputElement { parent: Element }
pub struct HTMLItalicElement { parent: Element }
pub struct HTMLLinkElement { parent: Element }
pub struct HTMLListItemElement { parent: Element }
pub struct HTMLMetaElement { parent: Element }
pub struct HTMLOListElement { parent: Element }
pub struct HTMLOptionElement { parent: Element }
pub struct HTMLParagraphElement { parent: Element }
pub struct HTMLScriptElement { parent: Element }
pub struct HTMLSectionElement { parent: Element }
pub struct HTMLSelectElement { parent: Element }
pub struct HTMLSmallElement { parent: Element }
pub struct HTMLSpanElement { parent: Element }
pub struct HTMLStyleElement { parent: Element }
pub struct HTMLTableBodyElement { parent: Element }
pub struct HTMLTableCellElement { parent: Element }
pub struct HTMLTableElement { parent: Element }
pub struct HTMLTableRowElement { parent: Element }
pub struct HTMLTitleElement { parent: Element }
pub struct HTMLUListElement { parent: Element }
pub struct UnknownElement { parent: Element }
//
// Fancier elements
//
pub struct HTMLHeadingElement {
parent: Element,
level: HeadingLevel,
}
pub struct HTMLImageElement {
parent: Element,
image: Option<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: ~[]
}
}
// Gets an attribute without copying.
//
// FIXME: Should not take a closure, but we need covariant type parameters for
// that.
fn with_attr<R>(name: &str, f: &fn(Option<&str>) -> R) -> R {
for self.attrs.each |attr| {
if name == attr.name {
let value: &str = attr.value;
return f(Some(value));
fn get_attr(&self, name: &str) -> Option<&self/str> {
// FIXME: Need an each() that links lifetimes in Rust.
for uint::range(0, self.attrs.len()) |i| {
if eq_slice(self.attrs[i].name, name) {
let val: &str = self.attrs[i].value;
return Some(val);
}
}
f(None)
return None;
}
fn set_attr(name: &str, value: ~str) {
let idx = do self.attrs.position |attr| { name == attr.name };
match idx {
Some(idx) => self.attrs.set_elt(idx, ~Attr(name.to_str(), value)),
None => {}
fn set_attr(&mut self, name: &str, value: ~str) {
// FIXME: We need a better each_mut in Rust; this is ugly.
let value_cell = Cell(value);
for uint::range(0, self.attrs.len()) |i| {
if eq_slice(self.attrs[i].name, name) {
self.attrs[i].value = value_cell.take();
return;
}
}
}
}
pub fn ElementData(tag_name: ~str, kind: ~ElementKind) -> ElementData {
ElementData {
tag_name : tag_name,
kind : kind,
attrs : DVec(),
self.attrs.push(Attr::new(name.to_str(), value_cell.take()));
}
}
@ -54,23 +145,15 @@ pub struct Attr {
value: ~str,
}
pub fn Attr(name: ~str, value: ~str) -> Attr {
Attr {
name : name,
value : value,
impl Attr {
static pub fn new(name: ~str, value: ~str) -> Attr {
Attr {
name: name,
value: value
}
}
}
pub fn HTMLImageData() -> HTMLImageData {
HTMLImageData {
image: None
}
}
pub struct HTMLImageData {
mut image: Option<Url>
}
pub enum HeadingLevel {
Heading1,
Heading2,
@ -80,39 +163,3 @@ pub enum HeadingLevel {
Heading6,
}
pub enum ElementKind {
HTMLAnchorElement,
HTMLAsideElement,
HTMLBRElement,
HTMLBodyElement,
HTMLBoldElement,
HTMLDivElement,
HTMLFontElement,
HTMLFormElement,
HTMLHRElement,
HTMLHeadElement,
HTMLHeadingElement(HeadingLevel),
HTMLHtmlElement,
HTMLImageElement(HTMLImageData),
HTMLInputElement,
HTMLItalicElement,
HTMLLinkElement,
HTMLListItemElement,
HTMLMetaElement,
HTMLOListElement,
HTMLOptionElement,
HTMLParagraphElement,
HTMLScriptElement,
HTMLSectionElement,
HTMLSelectElement,
HTMLSmallElement,
HTMLSpanElement,
HTMLStyleElement,
HTMLTableBodyElement,
HTMLTableCellElement,
HTMLTableElement,
HTMLTableRowElement,
HTMLTitleElement,
HTMLUListElement,
UnknownElement,
}

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::document::Document;
use dom::element::{Attr, ElementData};
use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId};
use dom::element::{HTMLStyleElementTypeId};
use dom::window::Window;
use geom::size::Size2D;
use js::crust::*;
use js::glue::bindgen::RUST_OBJECT_TO_JSVAL;
use js::jsapi::{JSClass, JSObject, JSPropertySpec, JSContext, jsid, JSVal, JSBool};
use js::rust::Compartment;
use js::{JSPROP_ENUMERATE, JSPROP_SHARED};
use layout::debug::DebugMethods;
use layout::flow::FlowContext;
use ptr::null;
use newcss::complete::CompleteSelectResults;
use core::cast::transmute;
use core::ptr::null;
use geom::size::Size2D;
use js::crust::*;
use js::rust::Compartment;
use std::arc::ARC;
use util::tree;
use super::cow;
pub struct NodeData {
tree: tree::Tree<Node>,
kind: ~NodeKind,
//
// The basic Node structure
//
/// This is what a Node looks like if you do not know what kind of node it is. To unpack it, use
/// downcast().
///
/// FIXME: This should be replaced with a trait once they can inherit from structs.
pub struct AbstractNode {
priv obj: *mut Node
}
/* The tree holding Nodes (read-only) */
pub enum NodeTree { NodeTree }
impl Eq for AbstractNode {
pure fn eq(&self, other: &AbstractNode) -> bool { self.obj == other.obj }
pure fn ne(&self, other: &AbstractNode) -> bool { self.obj != other.obj }
}
impl NodeTree {
fn each_child(node: &Node, f: fn(&Node) -> bool) {
tree::each_child(&self, node, f)
}
pub struct Node {
type_id: NodeTypeId,
fn get_parent(node: &Node) -> Option<Node> {
tree::get_parent(&self, node)
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,
}
}
}
impl tree::ReadMethods<Node> for NodeTree {
fn with_tree_fields<R>(n: &Node, f: fn(&tree::Tree<Node>) -> R) -> R {
n.read(|n| f(&n.tree))
//
// Basic node types
//
pub struct Doctype {
parent: Node,
name: ~str,
public_id: Option<~str>,
system_id: Option<~str>,
force_quirks: bool
}
impl Doctype {
static pub fn new(name: ~str,
public_id: Option<~str>,
system_id: Option<~str>,
force_quirks: bool)
-> Doctype {
Doctype {
parent: Node::new(DoctypeNodeTypeId),
name: name,
public_id: public_id,
system_id: system_id,
force_quirks: force_quirks,
}
}
}
impl Node {
fn traverse_preorder(preorder_cb: &fn(Node)) {
preorder_cb(self);
do NodeTree.each_child(&self) |child| { child.traverse_preorder(preorder_cb); true }
}
pub struct Comment {
parent: Node,
text: ~str,
}
fn traverse_postorder(postorder_cb: &fn(Node)) {
do NodeTree.each_child(&self) |child| { child.traverse_postorder(postorder_cb); true }
postorder_cb(self);
impl Comment {
static pub fn new(text: ~str) -> Comment {
Comment {
parent: Node::new(CommentNodeTypeId),
text: text
}
}
}
impl DebugMethods for Node {
/* Dumps the subtree rooted at this node, for debugging. */
pub struct Text {
parent: Node,
text: ~str,
}
impl Text {
static pub fn new(text: ~str) -> Text {
Text {
parent: Node::new(CommentNodeTypeId),
text: text
}
}
}
impl AbstractNode {
//
// Convenience accessors
//
// FIXME: Fold these into util::tree.
fn type_id(self) -> NodeTypeId { self.with_imm_node(|n| n.type_id) }
fn parent_node(self) -> Option<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) {
self.dump_indent(0u);
self.dump_indent(0);
}
/* Dumps the node tree, for debugging, with indentation. */
// Dumps the node tree, for debugging, with indentation.
pure fn dump_indent(&self, indent: uint) {
let mut s = ~"";
for uint::range(0u, indent) |_i| {
@ -70,116 +328,46 @@ impl DebugMethods for Node {
// FIXME: this should have a pure version?
unsafe {
for NodeTree.each_child(self) |kid| {
for self.each_child() |kid| {
kid.dump_indent(indent + 1u)
}
}
}
pure fn debug_str(&self) -> ~str {
// Unsafe due to the call to type_id().
unsafe {
do self.read |n| { fmt!("%?", n.kind) }
fmt!("%?", self.type_id())
}
}
}
impl Node {
fn is_element(&self) -> bool {
self.read(|n| match *n.kind { Element(*) => true, _ => false } )
static pub unsafe fn as_abstract_node<N>(node: ~N) -> AbstractNode {
// This surrenders memory management of the node!
AbstractNode {
obj: transmute(node)
}
}
static pub fn new(type_id: NodeTypeId) -> Node {
Node {
type_id: type_id,
parent_node: None,
first_child: None,
last_child: None,
next_sibling: None,
prev_sibling: None,
layout_data: None,
}
}
}
pub enum NodeKind {
Doctype(DoctypeData),
Comment(~str),
Element(ElementData),
Text(~str)
}
pub struct DoctypeData {
name: ~str,
public_id: Option<~str>,
system_id: Option<~str>,
force_quirks: bool
}
pub fn DoctypeData(name: ~str, public_id: Option<~str>,
system_id: Option<~str>, force_quirks: bool) -> DoctypeData {
DoctypeData {
name : name,
public_id : public_id,
system_id : system_id,
force_quirks : force_quirks,
}
}
pub fn define_bindings(compartment: @mut Compartment, doc: @Document, win: @Window) {
bindings::window::init(compartment, win);
bindings::document::init(compartment, doc);
bindings::node::init(compartment);
bindings::element::init(compartment);
}
/** The COW rd_aux data is a (weak) pointer to the layout data,
defined by this `LayoutData` struct. It contains the CSS style object
as well as the primary `RenderBox`.
Note that there may be multiple boxes per DOM node. */
pub struct LayoutData {
mut style: Option<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 dom::cow;
use dom::element::*;
use dom::event::{Event, ReflowEvent};
use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, NodeScopeExtensions};
use dom::node::{Text};
use dom::node::{AbstractNode, Comment, Doctype, Element, ElementNodeTypeId, Node, Text};
use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use newcss::stylesheet::Stylesheet;
use resource::image_cache_task::ImageCacheTask;
use resource::image_cache_task;
use resource::resource_task::{Done, Load, Payload, ResourceTask};
use util::task::{spawn_listener, spawn_conversation};
use core::comm::{Chan, Port, SharedChan};
use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use core::str::eq_slice;
use gfx::util::url::make_url;
use hubbub::hubbub::Attribute;
use hubbub::hubbub;
use newcss::stylesheet::Stylesheet;
use std::cell::Cell;
use std::net::url::Url;
use std::net::url;
macro_rules! handle_element(
($tag:expr, $string:expr, $ctor:ident, $type_id:expr) => (
if eq_slice($tag, $string) {
let element = ~$ctor {
parent: Element::new($type_id, ($tag).to_str())
};
unsafe {
return Node::as_abstract_node(element);
}
}
)
)
macro_rules! handle_heading_element(
($tag:expr, $string:expr, $ctor:ident, $type_id:expr, $level:expr) => (
if eq_slice($tag, $string) {
let element = ~HTMLHeadingElement {
parent: Element::new($type_id, ($tag).to_str()),
level: $level
};
unsafe {
return Node::as_abstract_node(element);
}
}
)
)
type JSResult = ~[~[u8]];
enum CSSMessage {
@ -32,11 +58,25 @@ enum JSMessage {
}
struct HtmlParserResult {
root: Node,
root: AbstractNode,
style_port: Port<Option<Stylesheet>>,
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.
@ -119,52 +159,57 @@ fn js_script_listener(to_parent: Chan<~[~[u8]]>,
to_parent.send(js_scripts);
}
fn build_element_kind(tag: &str) -> ~ElementKind {
// Silly macros to handle constructing DOM nodes. This produces bad code and should be optimized
// via atomization (issue #85).
fn build_element_from_tag(tag: &str) -> AbstractNode {
// TODO (Issue #85): use atoms
if tag == ~"a" { ~HTMLAnchorElement }
else if tag == ~"aside" { ~HTMLAsideElement }
else if tag == ~"br" { ~HTMLBRElement }
else if tag == ~"body" { ~HTMLBodyElement }
else if tag == ~"bold" { ~HTMLBoldElement }
else if tag == ~"div" { ~HTMLDivElement }
else if tag == ~"font" { ~HTMLFontElement }
else if tag == ~"form" { ~HTMLFormElement }
else if tag == ~"hr" { ~HTMLHRElement }
else if tag == ~"head" { ~HTMLHeadElement }
else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) }
else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) }
else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) }
else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) }
else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) }
else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) }
else if tag == ~"html" { ~HTMLHtmlElement }
else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) }
else if tag == ~"input" { ~HTMLInputElement }
else if tag == ~"i" { ~HTMLItalicElement }
else if tag == ~"link" { ~HTMLLinkElement }
else if tag == ~"li" { ~HTMLListItemElement }
else if tag == ~"meta" { ~HTMLMetaElement }
else if tag == ~"ol" { ~HTMLOListElement }
else if tag == ~"option" { ~HTMLOptionElement }
else if tag == ~"p" { ~HTMLParagraphElement }
else if tag == ~"script" { ~HTMLScriptElement }
else if tag == ~"section" { ~HTMLSectionElement }
else if tag == ~"select" { ~HTMLSelectElement }
else if tag == ~"small" { ~HTMLSmallElement }
else if tag == ~"span" { ~HTMLSpanElement }
else if tag == ~"style" { ~HTMLStyleElement }
else if tag == ~"tbody" { ~HTMLTableBodyElement }
else if tag == ~"td" { ~HTMLTableCellElement }
else if tag == ~"table" { ~HTMLTableElement }
else if tag == ~"tr" { ~HTMLTableRowElement }
else if tag == ~"title" { ~HTMLTitleElement }
else if tag == ~"ul" { ~HTMLUListElement }
else { ~UnknownElement }
handle_element!(tag, "a", HTMLAnchorElement, HTMLAnchorElementTypeId);
handle_element!(tag, "aside", HTMLAsideElement, HTMLAsideElementTypeId);
handle_element!(tag, "br", HTMLBRElement, HTMLBRElementTypeId);
handle_element!(tag, "body", HTMLBodyElement, HTMLBodyElementTypeId);
handle_element!(tag, "bold", HTMLBoldElement, HTMLBoldElementTypeId);
handle_element!(tag, "div", HTMLDivElement, HTMLDivElementTypeId);
handle_element!(tag, "font", HTMLFontElement, HTMLFontElementTypeId);
handle_element!(tag, "form", HTMLFormElement, HTMLFormElementTypeId);
handle_element!(tag, "hr", HTMLHRElement, HTMLHRElementTypeId);
handle_element!(tag, "head", HTMLHeadElement, HTMLHeadElementTypeId);
handle_element!(tag, "html", HTMLHtmlElement, HTMLHtmlElementTypeId);
handle_element!(tag, "input", HTMLInputElement, HTMLInputElementTypeId);
handle_element!(tag, "i", HTMLItalicElement, HTMLItalicElementTypeId);
handle_element!(tag, "link", HTMLLinkElement, HTMLLinkElementTypeId);
handle_element!(tag, "li", HTMLListItemElement, HTMLListItemElementTypeId);
handle_element!(tag, "meta", HTMLMetaElement, HTMLMetaElementTypeId);
handle_element!(tag, "ol", HTMLOListElement, HTMLOListElementTypeId);
handle_element!(tag, "option", HTMLOptionElement, HTMLOptionElementTypeId);
handle_element!(tag, "p", HTMLParagraphElement, HTMLParagraphElementTypeId);
handle_element!(tag, "script", HTMLScriptElement, HTMLScriptElementTypeId);
handle_element!(tag, "section", HTMLSectionElement, HTMLSectionElementTypeId);
handle_element!(tag, "select", HTMLSelectElement, HTMLSelectElementTypeId);
handle_element!(tag, "small", HTMLSmallElement, HTMLSmallElementTypeId);
handle_element!(tag, "span", HTMLSpanElement, HTMLSpanElementTypeId);
handle_element!(tag, "style", HTMLStyleElement, HTMLStyleElementTypeId);
handle_element!(tag, "tbody", HTMLTableBodyElement, HTMLTableBodyElementTypeId);
handle_element!(tag, "td", HTMLTableCellElement, HTMLTableCellElementTypeId);
handle_element!(tag, "table", HTMLTableElement, HTMLTableElementTypeId);
handle_element!(tag, "tr", HTMLTableRowElement, HTMLTableRowElementTypeId);
handle_element!(tag, "title", HTMLTitleElement, HTMLTitleElementTypeId);
handle_element!(tag, "ul", HTMLUListElement, HTMLUListElementTypeId);
handle_heading_element!(tag, "h1", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading1);
handle_heading_element!(tag, "h2", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading2);
handle_heading_element!(tag, "h3", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading3);
handle_heading_element!(tag, "h4", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading4);
handle_heading_element!(tag, "h5", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading5);
handle_heading_element!(tag, "h6", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading6);
unsafe {
Node::as_abstract_node(~Element::new(UnknownElementTypeId, tag.to_str()))
}
}
#[allow(non_implicitly_copyable_typarams)]
pub fn parse_html(scope: NodeScope,
url: Url,
pub fn parse_html(url: Url,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask) -> HtmlParserResult {
// Spawn a CSS parser to receive links to CSS style sheets.
@ -185,38 +230,31 @@ pub fn parse_html(scope: NodeScope,
};
let js_chan = SharedChan(js_chan);
let (scope, url) = (@copy scope, @url);
let url = @url;
unsafe {
// Build the root node.
let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
let root = ~HTMLHtmlElement { parent: Element::new(HTMLHtmlElementTypeId, ~"html") };
let root = unsafe { Node::as_abstract_node(root) };
debug!("created new node");
let parser = hubbub::Parser("UTF-8", false);
debug!("created parser");
parser.set_document_node(cast::transmute(cow::unwrap(root)));
parser.set_document_node(root.to_hubbub_node());
parser.enable_scripting(true);
// Performs various actions necessary after appending has taken place. Currently, this consists
// of processing inline stylesheets, but in the future it might perform prefetching, etc.
// Performs various actions necessary after appending has taken place. Currently, this
// consists of processing inline stylesheets, but in the future it might perform
// prefetching, etc.
let css_chan2 = css_chan.clone();
let append_hook: @fn(Node, Node) = |parent_node, child_node| {
do scope.read(&parent_node) |parent_node_contents| {
do scope.read(&child_node) |child_node_contents| {
match (&parent_node_contents.kind, &child_node_contents.kind) {
(&~Element(ref element), &~Text(ref data)) => {
match element.kind {
~HTMLStyleElement => {
debug!("found inline CSS stylesheet");
let url = url::from_str("http://example.com/"); // FIXME
let provenance = InlineProvenance(result::unwrap(url),
copy *data);
css_chan2.send(CSSTaskNewFile(provenance));
}
_ => {} // Nothing to do.
}
}
_ => {} // Nothing to do.
}
let append_hook: @fn(AbstractNode, AbstractNode) = |parent_node, child_node| {
if parent_node.is_style_element() && child_node.is_text() {
debug!("found inline CSS stylesheet");
let url = url::from_str("http://example.com/"); // FIXME
let url_cell = Cell(url);
do child_node.with_imm_text |text_node| {
let data = text_node.text.to_str(); // FIXME: Bad copy.
let provenance = InlineProvenance(result::unwrap(url_cell.take()), data);
css_chan2.send(CSSTaskNewFile(provenance));
}
}
};
@ -225,8 +263,9 @@ pub fn parse_html(scope: NodeScope,
parser.set_tree_handler(@hubbub::TreeHandler {
create_comment: |data: ~str| {
debug!("create comment");
let new_node = scope.new_node(Comment(data));
unsafe { cast::transmute(cow::unwrap(new_node)) }
unsafe {
Node::as_abstract_node(~Comment::new(data)).to_hubbub_node()
}
},
create_doctype: |doctype: ~hubbub::Doctype| {
debug!("create doctype");
@ -240,69 +279,83 @@ pub fn parse_html(scope: NodeScope,
&None => None,
&Some(ref id) => Some(copy *id)
};
let data = DoctypeData(copy doctype.name,
public_id,
system_id,
copy doctype.force_quirks);
let new_node = scope.new_node(Doctype(data));
unsafe { cast::transmute(cow::unwrap(new_node)) }
let node = ~Doctype::new(copy doctype.name,
public_id,
system_id,
doctype.force_quirks);
unsafe {
Node::as_abstract_node(node).to_hubbub_node()
}
},
create_element: |tag: ~hubbub::Tag| {
debug!("create element");
// TODO: remove copying here by using struct pattern matching to
// move all ~strs at once (blocked on Rust #3845, #3846, #3847)
let elem_kind = build_element_kind(tag.name);
let elem = ElementData(copy tag.name, elem_kind);
let node = build_element_from_tag(tag.name);
debug!("-- attach attrs");
for tag.attributes.each |attr| {
elem.attrs.push(~Attr(copy attr.name, copy attr.value));
do node.as_mut_element |element| {
for tag.attributes.each |attr| {
element.attrs.push(Attr::new(copy attr.name, copy attr.value));
}
}
// Spawn additional parsing, network loads, etc. from tag and attrs
match elem.kind {
//Handle CSS style sheets from <link> elements
~HTMLLinkElement => {
match (elem.get_attr(~"rel"), elem.get_attr(~"href")) {
(Some(rel), Some(href)) => {
if rel == ~"stylesheet" {
debug!("found CSS stylesheet: %s", href);
css_chan2.send(CSSTaskNewFile(UrlProvenance(make_url(
href, Some(copy *url)))));
match node.type_id() {
// Handle CSS style sheets from <link> elements
ElementNodeTypeId(HTMLLinkElementTypeId) => {
do node.with_imm_element |element| {
match (element.get_attr(~"rel"), element.get_attr(~"href")) {
(Some(rel), Some(href)) => {
if rel == ~"stylesheet" {
debug!("found CSS stylesheet: %s", href);
let url = make_url(href.to_str(), Some(copy *url));
css_chan2.send(CSSTaskNewFile(UrlProvenance(url)));
}
}
_ => {}
}
_ => {}
}
},
~HTMLImageElement(ref d) => {
do elem.get_attr(~"src").iter |img_url_str| {
let img_url = make_url(copy *img_url_str, Some(copy *url));
d.image = Some(copy img_url);
// inform the image cache to load this, but don't store a handle.
// TODO (Issue #84): don't prefetch if we are within a <noscript> tag.
image_cache_task.send(image_cache_task::Prefetch(img_url));
ElementNodeTypeId(HTMLImageElementTypeId) => {
do node.with_mut_image_element |image_element| {
let src_opt = image_element.parent.get_attr(~"src").map(|x| x.to_str());
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.
// TODO (Issue #84): don't prefetch if we are within a <noscript>
// tag.
image_cache_task.send(image_cache_task::Prefetch(img_url));
}
}
}
}
//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| {
debug!("create text");
let new_node = scope.new_node(Text(data));
unsafe { cast::transmute(cow::unwrap(new_node)) }
unsafe {
Node::as_abstract_node(~Text::new(data)).to_hubbub_node()
}
},
ref_node: |_node| {},
unref_node: |_node| {},
ref_node: |_| {},
unref_node: |_| {},
append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| {
unsafe {
debug!("append child %x %x", cast::transmute(parent), cast::transmute(child));
let p: Node = cow::wrap(cast::transmute(parent));
let c: Node = cow::wrap(cast::transmute(child));
scope.add_child(p, c);
append_hook(p, c);
let parent: AbstractNode = NodeWrapping::from_hubbub_node(parent);
let child: AbstractNode = NodeWrapping::from_hubbub_node(child);
parent.append_child(child);
append_hook(parent, child);
}
child
},
@ -318,10 +371,7 @@ pub fn parse_html(scope: NodeScope,
debug!("clone node");
unsafe {
if deep { error!("-- deep clone unimplemented"); }
let n: Node = cow::wrap(cast::transmute(node));
let data = n.read(|read_data| copy *read_data.kind);
let new_node = scope.new_node(data);
cast::transmute(cow::unwrap(new_node))
fail!(~"clone node unimplemented")
}
},
reparent_children: |_node, _new_parent| {
@ -351,29 +401,24 @@ pub fn parse_html(scope: NodeScope,
complete_script: |script| {
// A little function for holding this lint attr
#[allow(non_implicitly_copyable_typarams)]
fn complete_script(scope: &NodeScope,
script: hubbub::NodeDataPtr,
fn complete_script(script: hubbub::NodeDataPtr,
url: &Url,
js_chan: SharedChan<JSMessage>) {
unsafe {
do scope.read(&cow::wrap(cast::transmute(script))) |node_contents| {
match *node_contents.kind {
Element(ref element) if element.tag_name == ~"script" => {
match element.get_attr(~"src") {
Some(src) => {
debug!("found script: %s", src);
let new_url = make_url(src, Some(copy *url));
js_chan.send(JSTaskNewFile(new_url));
}
None => {}
}
let script: AbstractNode = NodeWrapping::from_hubbub_node(script);
do script.with_imm_element |script| {
match script.get_attr(~"src") {
Some(src) => {
debug!("found script: %s", src);
let new_url = make_url(src.to_str(), Some(copy *url));
js_chan.send(JSTaskNewFile(new_url));
}
_ => {}
None => {}
}
}
}
}
complete_script(scope, script, url, js_chan2.clone());
complete_script(script, url, js_chan2.clone());
debug!("complete script");
}
});

View file

@ -2,42 +2,36 @@
Code for managing the DOM aux pointer
*/
use dom::node::{Node, LayoutData};
use dom::node::{AbstractNode, LayoutData};
use core::dvec::DVec;
pub trait LayoutAuxMethods {
fn initialize_layout_data() -> Option<@LayoutData>;
fn initialize_style_for_subtree(refs: &DVec<@LayoutData>);
fn initialize_layout_data(self) -> Option<@mut LayoutData>;
fn initialize_style_for_subtree(self, refs: &DVec<@mut LayoutData>);
}
impl LayoutAuxMethods for Node {
/** 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.
*/
fn initialize_layout_data() -> Option<@LayoutData> {
match self.has_aux() {
false => {
let data = @LayoutData {
mut style : None,
mut flow : None
};
self.set_aux(data); Some(data)
},
true => None
impl LayoutAuxMethods for AbstractNode {
/// 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.
fn initialize_layout_data(self) -> Option<@mut LayoutData> {
if self.has_layout_data() {
None
} else {
let data = @mut LayoutData::new();
self.set_layout_data(data);
Some(data)
}
}
/**
* 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.
*/
fn initialize_style_for_subtree(refs: &DVec<@LayoutData>) {
do self.traverse_preorder |n| {
/// 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.
fn initialize_style_for_subtree(self, refs: &DVec<@mut LayoutData>) {
let _ = for self.traverse_preorder |n| {
match n.initialize_layout_data() {
Some(r) => refs.push(r),
None => {}
}
}
};
}
}

View file

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

View file

@ -1,10 +1,11 @@
/** Creates CSS boxes from a DOM. */
use dom;
use dom::element::*;
use dom::node::{Comment, Doctype, Element, Text, Node, LayoutData};
use layout::box::*;
use dom::node::{AbstractNode, Comment, CommentNodeTypeId, Doctype, DoctypeNodeTypeId, Element};
use dom::node::{ElementNodeTypeId, Node, Text, TextNodeTypeId};
use dom;
use layout::block::BlockFlowData;
use layout::box::*;
use layout::context::LayoutContext;
use layout::debug::{BoxedDebugMethods, DebugMethods};
use layout::flow::*;
@ -46,8 +47,7 @@ enum InlineSpacerSide {
LogicalAfter,
}
priv fn simulate_UA_display_rules(node: Node) -> CSSDisplay {
priv fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay {
// FIXME
/*let resolved = do node.aux |nd| {
match nd.style.display_type {
@ -55,28 +55,25 @@ priv fn simulate_UA_display_rules(node: Node) -> CSSDisplay {
Specified(v) => v
}
};*/
let resolved = CSSDisplayInline;
if (resolved == CSSDisplayNone) { return resolved; }
do node.read |n| {
let kind: &dom::node::NodeKind = n.kind;
match kind {
&Doctype(*) | &Comment(*) => CSSDisplayNone,
&Text(*) => CSSDisplayInline,
&Element(ref e) => {
let kind: &dom::element::ElementKind = e.kind;
match kind {
&HTMLHeadElement(*) => CSSDisplayNone,
&HTMLScriptElement(*) => CSSDisplayNone,
&HTMLParagraphElement(*) => CSSDisplayBlock,
&HTMLDivElement(*) => CSSDisplayBlock,
&HTMLBodyElement(*) => CSSDisplayBlock,
&HTMLHeadingElement(*) => CSSDisplayBlock,
&HTMLHtmlElement(*) => CSSDisplayBlock,
&HTMLUListElement(*) => CSSDisplayBlock,
&HTMLOListElement(*) => CSSDisplayBlock,
_ => resolved
}
match node.type_id() {
DoctypeNodeTypeId | CommentNodeTypeId => CSSDisplayNone,
TextNodeTypeId => CSSDisplayInline,
ElementNodeTypeId(element_type_id) => {
match element_type_id {
HTMLHeadElementTypeId |
HTMLScriptElementTypeId => CSSDisplayNone,
HTMLParagraphElementTypeId |
HTMLDivElementTypeId |
HTMLBodyElementTypeId |
HTMLHeadingElementTypeId |
HTMLHtmlElementTypeId |
HTMLUListElementTypeId |
HTMLOListElementTypeId => CSSDisplayBlock,
_ => resolved
}
}
}
@ -92,17 +89,19 @@ impl BoxGenerator {
}
/* 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;
}
// TODO: implement this, generating spacer
fn make_inline_spacer_for_node_side(_ctx: &LayoutContext, _node: Node,
_side: InlineSpacerSide) -> Option<@RenderBox> {
fn make_inline_spacer_for_node_side(_: &LayoutContext,
_: AbstractNode,
_: InlineSpacerSide)
-> Option<@RenderBox> {
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());
// first, determine the box type, based on node characteristics
@ -122,12 +121,13 @@ impl BoxGenerator {
self.range_stack.push(node_range_start);
// 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);
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) {
do self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).iter |spacer: &@RenderBox| {
} 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| {
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());
match self.flow {
@ -241,8 +241,9 @@ impl BuilderContext {
// 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'
// should just not generate any flows or boxes.
fn containing_context_for_node(node: Node,
builder: &LayoutTreeBuilder) -> Option<BuilderContext> {
fn containing_context_for_node(node: AbstractNode,
builder: &LayoutTreeBuilder)
-> Option<BuilderContext> {
// TODO: remove this once UA styles work
// TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7)
let simulated_display = match simulate_UA_display_rules(node) {
@ -254,7 +255,7 @@ impl BuilderContext {
(CSSDisplayBlock, @RootFlow(*)) => {
// If this is the root node, then use the root flow's
// 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) }
None => { self.clone() },
}
@ -276,28 +277,31 @@ impl BuilderContext {
impl LayoutTreeBuilder {
/* Debug-only ids */
fn next_box_id() -> int { self.next_bid += 1; self.next_bid }
fn next_flow_id() -> int { self.next_cid += 1; self.next_cid }
fn next_box_id(&self) -> int { self.next_bid += 1; self.next_bid }
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,
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());
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,
None => { return; } // no context because of display: none. Stop building subtree.
};
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());
// recurse on child nodes.
for tree::each_child(&NodeTree, &cur_node) |child_node| {
self.construct_recursively(layout_ctx, *child_node, &this_ctx);
for cur_node.each_child |child_node| {
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);
// store reference to the flow context which contains any
@ -306,8 +310,8 @@ impl LayoutTreeBuilder {
// nodes and FlowContexts should not change during layout.
for tree::each_child(&FlowTree, &this_ctx.default_collector.flow) |child_flow: &@FlowContext| {
do (copy child_flow.d().node).iter |node| {
assert node.has_aux();
do node.aux |data| { data.flow = Some(*child_flow) }
assert node.has_layout_data();
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
// beginning or end of a block flow. Otherwise, the whitespace
// 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 {
InlineFlow(*) => {
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.
fail!(~"TODO: handle case where an inline is split by a block")
}
/** entry point for box creation. Should only be
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_generator = @BoxGenerator::new(new_flow);
let root_ctx = BuilderContext::new(new_generator);
@ -386,7 +391,7 @@ impl LayoutTreeBuilder {
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 ret = match ty {
Flow_Absolute => @AbsoluteFlow(data),
@ -405,7 +410,12 @@ impl LayoutTreeBuilder {
disambiguate between different methods here instead of inlining, since each
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 {
RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx),
RenderBox_Text => self.make_text_box(layout_ctx, node, ctx),
@ -415,63 +425,65 @@ impl LayoutTreeBuilder {
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()))
}
fn make_image_box(layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox {
do node.read |n| {
match n.kind {
~Element(ref ed) => match ed.kind {
~HTMLImageElement(ref d) => {
// TODO: this could be written as a pattern guard, but it triggers
// an ICE (mozilla/rust issue #3601)
if d.image.is_some() {
let holder = ImageHolder::new({copy *d.image.get_ref()},
layout_ctx.image_cache);
@ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder)
} else {
info!("Tried to make image box, but couldn't find image. Made generic box instead.");
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_image_box(&self,
layout_ctx: &LayoutContext,
node: AbstractNode,
ctx: @FlowContext)
-> @RenderBox {
if !node.is_image_element() {
fail!(~"WAT error: why couldn't we make an image box?");
}
}
fn make_text_box(_layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox {
do node.read |n| {
match n.kind {
~Text(ref string) => @UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), copy *string),
_ => fail!(~"WAT error: why couldn't we make a text box?")
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)
} else {
info!("Tried to make image box, but couldn't find image. Made generic box instead.");
self.make_generic_box(layout_ctx, node, ctx)
}
}
}
fn decide_box_type(node: Node, display: CSSDisplay) -> RenderBoxType {
do node.read |n| {
match n.kind {
~Doctype(*) | ~Comment(*) => {
fail!(~"Hey, doctypes and comments shouldn't get here! \
They are display:none!")
}
~Text(*) => RenderBox_Text,
~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")
//}
}
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?");
}
// FIXME: Don't copy text. I guess it should be atomically reference counted?
do node.with_imm_text |text_node| {
let string = text_node.text.to_str();
@UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), string)
}
}
fn decide_box_type(&self, node: AbstractNode, display: CSSDisplay) -> RenderBoxType {
if node.is_text() {
RenderBox_Text
} else if node.is_image_element() {
do node.with_imm_image_element |image_element| {
match image_element.image {
Some(_) => RenderBox_Image,
None => RenderBox_Generic,
}
}
} 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};
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;
///
/// Constructs display lists from render boxes.
///
use layout::box::{RenderBox, TextBox};
use layout::context::LayoutContext;
use layout::flow::FlowContext;
use layout::text::TextBoxData;
use newcss::values::Specified;
use newcss::values::{CSSBackgroundColorColor, CSSBackgroundColorTransparent};
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::geometry::Au;
use core::mutable::Mut;
use gfx;
/** A builder object that manages display list builder should mainly
hold information about the initial request and desired result---for

View file

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

View file

@ -1,5 +1,5 @@
use core;
use dom::node::Node;
use dom::node::AbstractNode;
use layout::box::*;
use layout::context::LayoutContext;
use layout::debug::{BoxedDebugMethods, DebugMethods};
@ -41,12 +41,12 @@ hard to try out that alternative.
*/
pub struct NodeRange {
node: Node,
node: AbstractNode,
range: Range,
}
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 }
}
}
@ -60,7 +60,7 @@ impl ElementMapping {
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))
}

View file

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

View file

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