mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Start work on new DOM representation
This commit is contained in:
parent
ce514f2785
commit
1b030480ab
22 changed files with 1004 additions and 1088 deletions
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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 => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue