separate into tasks that actually parse

This commit is contained in:
Niko Matsakis 2012-05-04 16:55:07 -07:00
parent 480d36137e
commit 275185ab26
10 changed files with 166 additions and 104 deletions

View file

@ -1,29 +1,59 @@
export msg; export msg, ping;
export content; export content;
import gfx::geom::*; import dom::rcu::writer_methods;
import dom::rcu::*; import dom=dom::base;
import dom::base::*; import layout::layout;
import layout::base::{rd_tree_ops, wr_tree_ops};
enum msg { enum msg {
parse(str),
exit exit
} }
fn content(layout: chan<layout::layout::msg>) -> chan<msg> { enum ping {
pong
}
task::spawn_listener::<msg> {|po| // sends a ping to layout and awaits the response.
// TODO: Get a DOM from the parser fn join_layout(scope: dom::node_scope,
// let s: int = scope(); to_layout: chan<layout::msg>) {
if scope.is_reader_forked() {
comm::listen { |ch|
to_layout.send(layout::ping(ch));
ch.recv();
}
scope.reader_joined();
}
}
// TODO: RCU this stuff over to layout fn content(to_layout: chan<layout::msg>) -> chan<msg> {
task::spawn_listener::<msg> {|from_master|
let scope = dom::node_scope();
loop { loop {
if po.peek() { alt from_master.recv() {
parse(filename) {
#debug["content: Received filename `%s`", filename];
// Note: we can parse the next document in parallel
// with any previous documents.
let stream = html::spawn_parser_task(filename);
let root = parser::html_builder::build_dom(scope, stream);
// Now, join the layout so that they will see the latest
// changes we have made.
join_layout(scope, to_layout);
// Send new document to layout.
to_layout.send(layout::build(root));
// Indicate that reader was forked so any further
// changes will be isolated.
scope.reader_forked();
}
exit {
to_layout.send(layout::exit);
break; break;
} else { }
#debug("content: requesting layout");
layout.send(layout::layout::build);
std::timer::sleep(1000u);
} }
} }
} }

View file

@ -1,4 +1,4 @@
import dom::rcu::{scope, writer_methods}; import dom::rcu::{writer_methods};
import gfx::geom::{au, size}; import gfx::geom::{au, size};
import layout::base::box; import layout::base::box;
import util::tree; import util::tree;
@ -16,30 +16,37 @@ enum node_kind {
// The rd_aux data is a (weak) pointer to the primary box. Note that // The rd_aux data is a (weak) pointer to the primary box. Note that
// there may be multiple boxes per DOM node. // there may be multiple boxes per DOM node.
type node = rcu::handle<node_data, box>; type node = rcu::handle<node_data, box>;
type node_scope = rcu::scope<node_data, box>;
impl methods for scope<node_data, box> { fn node_scope() -> node_scope { rcu::scope() }
impl methods for node_scope {
fn new_node(+k: node_kind) -> node { fn new_node(+k: node_kind) -> node {
self.handle(node_data({tree: tree::empty(), self.handle(node_data({tree: tree::empty(),
kind: k})) kind: k}))
} }
} }
impl of tree::rd_tree_ops<node> for scope<node_data, box> { impl of tree::rd_tree_ops<node> for node_scope {
fn each_child(node: node, f: fn(node) -> bool) { fn each_child(node: node, f: fn(node) -> bool) {
tree::each_child(self, node, f) tree::each_child(self, node, f)
} }
fn get_parent(node: node) -> option<node> {
tree::get_parent(self, node)
}
fn with_tree_fields<R>(node: node, f: fn(tree::fields<node>) -> R) -> R { fn with_tree_fields<R>(node: node, f: fn(tree::fields<node>) -> R) -> R {
f(self.rd(node) { |f| f.tree }) self.rd(node) { |n| f(n.tree) }
} }
} }
impl of tree::wr_tree_ops<node> for scope<node_data, box> { impl of tree::wr_tree_ops<node> for node_scope {
fn add_child(node: node, child: node) { fn add_child(node: node, child: node) {
tree::add_child(self, node, child) tree::add_child(self, node, child)
} }
fn with_tree_fields<R>(node: node, f: fn(tree::fields<node>) -> R) -> R { fn with_tree_fields<R>(node: node, f: fn(tree::fields<node>) -> R) -> R {
f(self.wr(node) { |f| f.tree }) self.wr(node) { |n| f(n.tree) }
} }
} }

View file

@ -98,6 +98,10 @@ fn scope<T:send,A>() -> scope<T,A> {
} }
impl writer_methods<T:send,A> for scope<T,A> { impl writer_methods<T:send,A> for scope<T,A> {
fn is_reader_forked() -> bool {
self.layout_active
}
fn reader_forked() { fn reader_forked() {
assert !self.layout_active; assert !self.layout_active;
assert self.first_dirty.is_null(); assert self.first_dirty.is_null();
@ -120,6 +124,7 @@ impl writer_methods<T:send,A> for scope<T,A> {
self.first_dirty = null_handle(); self.first_dirty = null_handle();
} }
assert self.first_dirty.is_null();
self.layout_active = false; self.layout_active = false;
} }
@ -130,12 +135,11 @@ impl writer_methods<T:send,A> for scope<T,A> {
} }
fn wr<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe { fn wr<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe {
if self.layout_active { if self.layout_active && h.rd_ptr() == h.wr_ptr() {
if h.rd_ptr() == h.wr_ptr() { #debug["marking handle %? as dirty", h];
h.set_wr_ptr(self.clone(h.rd_ptr())); h.set_wr_ptr(self.clone(h.rd_ptr()));
h.set_next_dirty(self.first_dirty); h.set_next_dirty(self.first_dirty);
self.first_dirty = h; self.first_dirty = h;
}
} }
f(*h.wr_ptr()) f(*h.wr_ptr())
} }

View file

@ -1,12 +1,12 @@
import platform::osmain; import platform::osmain;
import geom::*; import geom::*;
import comm::*; import comm::*;
import layout::display_list::*; import dl = layout::display_list;
import azure::*; import azure::*;
import azure::bindgen::*; import azure::bindgen::*;
enum msg { enum msg {
render(display_list), render(dl::display_list),
exit(comm::chan<()>) exit(comm::chan<()>)
} }
@ -15,7 +15,7 @@ fn renderer(osmain: chan<osmain::msg>) -> chan<msg> {
listen {|draw_target_ch| listen {|draw_target_ch|
#debug("renderer: beginning rendering loop"); #debug("renderer: beginning rendering loop");
osmain.send(osmain::begin_drawing(draw_target_ch)); osmain.send(osmain::begin_drawing(draw_target_ch));
loop { loop {
alt po.recv() { alt po.recv() {
render(display_list) { render(display_list) {
@ -38,13 +38,13 @@ fn renderer(osmain: chan<osmain::msg>) -> chan<msg> {
fn draw_display_list( fn draw_display_list(
draw_target: AzDrawTargetRef, draw_target: AzDrawTargetRef,
display_list: display_list display_list: dl::display_list
) { ) {
clear(draw_target); clear(draw_target);
for display_list.each {|item| for display_list.each {|item|
let (r, g, b) = alt check item.item_type { let (r, g, b) = alt check item.item_type {
solid_color(r, g, b) { (r, g, b) } dl::solid_color(r, g, b) { (r, g, b) }
}; };
let bounds = (*item).bounds; let bounds = (*item).bounds;

View file

@ -55,6 +55,15 @@ fn linked_box(n: node) -> @box {
ret b; ret b;
} }
fn linked_subtree(p: node) -> @box {
let p_box = linked_box(p);
for ntree.each_child(p) { |c|
let c_box = linked_box(c);
btree.add_child(p_box, c_box);
}
ret p_box;
}
fn reflow_block(root: @box, available_width: au) { fn reflow_block(root: @box, available_width: au) {
// Root here is the root of the reflow, not necessarily the doc as // Root here is the root of the reflow, not necessarily the doc as
// a whole. // a whole.

View file

@ -8,56 +8,34 @@ them to be rendered
import task::*; import task::*;
import comm::*; import comm::*;
import gfx::geom; import gfx::geom;
import gfx::geom::*;
import gfx::renderer; import gfx::renderer;
import dom::base::*; import dom::base::node;
import display_list::*;
import dom::rcu::scope; import dom::rcu::scope;
import base::{btree, rd_tree_ops, wr_tree_ops}; import base::{btree, rd_tree_ops, wr_tree_ops, linked_subtree};
import dl = display_list;
enum msg { enum msg {
build, build(node),
ping(chan<content::ping>),
exit exit
} }
fn layout(renderer: chan<renderer::msg>) -> chan<msg> { fn layout(to_renderer: chan<renderer::msg>) -> chan<msg> {
spawn_listener::<msg> { |po|
spawn_listener::<msg> {|po|
let r = rand::rng();
loop { loop {
alt po.recv() {
let s = scope(); ping(ch) { ch.send(content::pong); }
let ndiv = s.new_node(nk_div); exit { break; }
let bdiv = base::linked_box(ndiv); build(node) {
iter::repeat(100u) {||
let node = s.new_node(nk_img(
size(
int_to_au(r.next() as int % 800),
int_to_au(r.next() as int % 200)
)));
s.add_child(ndiv, node);
let b = base::linked_box(node);
btree.add_child(bdiv, b);
}
alt recv(po) {
build {
#debug("layout: received layout request"); #debug("layout: received layout request");
base::reflow_block(bdiv, int_to_au(800)); let box = linked_subtree(node);
let dlist = build_display_list(bdiv); base::reflow_block(box, geom::int_to_au(800));
let dlist = build_display_list(box);
send(renderer, gfx::renderer::render(dlist)); to_renderer.send(renderer::render(dlist));
}
exit {
break;
} }
} }
} }
} }
} }
fn build_display_list(box: @base::box) -> display_list::display_list { fn build_display_list(box: @base::box) -> display_list::display_list {
@ -71,10 +49,12 @@ fn build_display_list(box: @base::box) -> display_list::display_list {
ret list; ret list;
} }
fn box_to_display_item(box: @base::box) -> display_item { fn box_to_display_item(box: @base::box) -> dl::display_item {
let r = rand::rng(); let r = rand::rng();
let item = display_item({ let item = dl::display_item({
item_type: solid_color(r.next() as u8, r.next() as u8, r.next() as u8), item_type: dl::solid_color(r.next() as u8,
r.next() as u8,
r.next() as u8),
bounds: box.bounds bounds: box.bounds
}); });
#debug("layout: display item: %?", item); #debug("layout: display item: %?", item);

View file

@ -0,0 +1,38 @@
// Constructs a DOM tree from an incoming token stream.
import dom::rcu::writer_methods;
import dom::base::{methods, rd_tree_ops, wr_tree_ops};
import dom = dom::base;
import parser = parser::html;
import html::token;
fn build_dom(scope: dom::node_scope,
stream: port<token>) -> dom::node {
// The current reference node.
let mut cur = scope.new_node(dom::nk_div);
loop {
let token = stream.recv();
#debug["token=%?", token];
alt token {
parser::to_eof { break; }
parser::to_start_tag(_) {
let new_node = scope.new_node(dom::nk_div);
scope.add_child(cur, new_node);
cur = new_node;
}
parser::to_end_tag(_) {
// TODO: Assert that the closing tag has the right name.
// TODO: Fail more gracefully (i.e. according to the HTML5
// spec) if we close more tags than we open.
cur = scope.get_parent(cur).get();
}
parser::to_text(_) {
// TODO
}
parser::to_doctype {
// TODO: Do something here...
}
}
}
ret cur;
}

View file

@ -35,6 +35,7 @@ mod layout {
mod parser { mod parser {
mod html; mod html;
mod html_builder;
} }
mod platform { mod platform {

View file

@ -3,27 +3,7 @@ import parser::html;
import parser::html::methods; import parser::html::methods;
import result::extensions; import result::extensions;
fn parse(filename: str) {
let file_data = io::read_whole_file(filename).get();
let reader = io::bytes_reader(file_data);
let parser = html::parser(reader);
loop {
let t = parser.parse();
log(error, #fmt("token: %?", t));
if t == html::to_eof { break; }
}
}
fn main(args: [str]) { fn main(args: [str]) {
if args.len() >= 2u {
let p = html::spawn_parser_task(args[1]);
loop {
let token = p.recv();
io::println(#fmt("token: %?", token));
if token == html::to_eof { break; }
}
}
// The platform event handler thread // The platform event handler thread
let osmain = platform::osmain::osmain(); let osmain = platform::osmain::osmain();
@ -36,14 +16,19 @@ fn main(args: [str]) {
// The content task // The content task
let content = content::content(layout); let content = content::content(layout);
// Wait for keypress // Send each file to render then wait for keypress
listen {|key_ch| for args.tail().each { |filename|
osmain.send(platform::osmain::add_key_handler(key_ch)); #debug["master: Sending filename `%s`", filename];
content.send(content::parse(filename));
key_ch.recv(); #debug["master: Waiting for keypress"];
listen {|key_ch|
osmain.send(platform::osmain::add_key_handler(key_ch));
key_ch.recv();
}
} }
// Shut everything down // Shut everything down
#debug["master: Shut down"];
content.send(content::exit); content.send(content::exit);
layout.send(layout::layout::exit); layout.send(layout::layout::exit);

View file

@ -1,3 +1,7 @@
// A generic tree datatype.
//
// TODO: Use traits.
type fields<T> = { type fields<T> = {
mut parent: option<T>, mut parent: option<T>,
mut first_child: option<T>, mut first_child: option<T>,
@ -38,21 +42,21 @@ fn empty<T>() -> fields<T> {
} }
fn add_child<T:copy,O:wr_tree_ops<T>>( fn add_child<T:copy,O:wr_tree_ops<T>>(
ops: O, node: T, child: T) { ops: O, parent: T, child: T) {
ops.with_tree_fields(child) { |child_tf| ops.with_tree_fields(child) { |child_tf|
alt child_tf.parent { alt child_tf.parent {
some(_) { fail "Already has a parent"; } some(_) { fail "Already has a parent"; }
none { child_tf.parent = some(node); } none { child_tf.parent = some(parent); }
} }
assert child_tf.prev_sibling == none; assert child_tf.prev_sibling == none;
assert child_tf.next_sibling == none; assert child_tf.next_sibling == none;
ops.with_tree_fields(node) { |node_tf| ops.with_tree_fields(parent) { |parent_tf|
alt node_tf.last_child { alt parent_tf.last_child {
none { none {
node_tf.first_child = some(child); parent_tf.first_child = some(child);
} }
some(lc) { some(lc) {
@ -65,11 +69,15 @@ fn add_child<T:copy,O:wr_tree_ops<T>>(
} }
} }
node_tf.last_child = some(child); parent_tf.last_child = some(child);
} }
} }
} }
fn get_parent<T:copy,O:rd_tree_ops<T>>(ops: O, node: T) -> option<T> {
ops.with_tree_fields(node) { |tf| tf.parent }
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
enum dummy = @{ enum dummy = @{
@ -129,4 +137,4 @@ mod test {
} }
assert i == 1u; assert i == 1u;
} }
} }