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;
import gfx::geom::*;
import dom::rcu::*;
import dom::base::*;
import layout::base::{rd_tree_ops, wr_tree_ops};
import dom::rcu::writer_methods;
import dom=dom::base;
import layout::layout;
enum msg {
parse(str),
exit
}
fn content(layout: chan<layout::layout::msg>) -> chan<msg> {
enum ping {
pong
}
task::spawn_listener::<msg> {|po|
// TODO: Get a DOM from the parser
// let s: int = scope();
// sends a ping to layout and awaits the response.
fn join_layout(scope: dom::node_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 {
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;
} 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 layout::base::box;
import util::tree;
@ -16,30 +16,37 @@ enum node_kind {
// The rd_aux data is a (weak) pointer to the primary box. Note that
// there may be multiple boxes per DOM node.
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 {
self.handle(node_data({tree: tree::empty(),
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) {
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 {
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) {
tree::add_child(self, node, child)
}
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> {
fn is_reader_forked() -> bool {
self.layout_active
}
fn reader_forked() {
assert !self.layout_active;
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();
}
assert self.first_dirty.is_null();
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 {
if self.layout_active {
if h.rd_ptr() == h.wr_ptr() {
h.set_wr_ptr(self.clone(h.rd_ptr()));
h.set_next_dirty(self.first_dirty);
self.first_dirty = h;
}
if self.layout_active && h.rd_ptr() == h.wr_ptr() {
#debug["marking handle %? as dirty", h];
h.set_wr_ptr(self.clone(h.rd_ptr()));
h.set_next_dirty(self.first_dirty);
self.first_dirty = h;
}
f(*h.wr_ptr())
}

View file

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

View file

@ -55,6 +55,15 @@ fn linked_box(n: node) -> @box {
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) {
// Root here is the root of the reflow, not necessarily the doc as
// a whole.

View file

@ -8,56 +8,34 @@ them to be rendered
import task::*;
import comm::*;
import gfx::geom;
import gfx::geom::*;
import gfx::renderer;
import dom::base::*;
import display_list::*;
import dom::base::node;
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 {
build,
build(node),
ping(chan<content::ping>),
exit
}
fn layout(renderer: chan<renderer::msg>) -> chan<msg> {
spawn_listener::<msg> {|po|
let r = rand::rng();
fn layout(to_renderer: chan<renderer::msg>) -> chan<msg> {
spawn_listener::<msg> { |po|
loop {
let s = scope();
let ndiv = s.new_node(nk_div);
let bdiv = base::linked_box(ndiv);
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 {
alt po.recv() {
ping(ch) { ch.send(content::pong); }
exit { break; }
build(node) {
#debug("layout: received layout request");
base::reflow_block(bdiv, int_to_au(800));
let dlist = build_display_list(bdiv);
send(renderer, gfx::renderer::render(dlist));
}
exit {
break;
let box = linked_subtree(node);
base::reflow_block(box, geom::int_to_au(800));
let dlist = build_display_list(box);
to_renderer.send(renderer::render(dlist));
}
}
}
}
}
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;
}
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 item = display_item({
item_type: solid_color(r.next() as u8, r.next() as u8, r.next() as u8),
let item = dl::display_item({
item_type: dl::solid_color(r.next() as u8,
r.next() as u8,
r.next() as u8),
bounds: box.bounds
});
#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 html;
mod html_builder;
}
mod platform {

View file

@ -3,27 +3,7 @@ import parser::html;
import parser::html::methods;
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]) {
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
let osmain = platform::osmain::osmain();
@ -36,14 +16,19 @@ fn main(args: [str]) {
// The content task
let content = content::content(layout);
// Wait for keypress
listen {|key_ch|
osmain.send(platform::osmain::add_key_handler(key_ch));
key_ch.recv();
// Send each file to render then wait for keypress
for args.tail().each { |filename|
#debug["master: Sending filename `%s`", filename];
content.send(content::parse(filename));
#debug["master: Waiting for keypress"];
listen {|key_ch|
osmain.send(platform::osmain::add_key_handler(key_ch));
key_ch.recv();
}
}
// Shut everything down
#debug["master: Shut down"];
content.send(content::exit);
layout.send(layout::layout::exit);

View file

@ -1,3 +1,7 @@
// A generic tree datatype.
//
// TODO: Use traits.
type fields<T> = {
mut parent: 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>>(
ops: O, node: T, child: T) {
ops: O, parent: T, child: T) {
ops.with_tree_fields(child) { |child_tf|
alt child_tf.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.next_sibling == none;
ops.with_tree_fields(node) { |node_tf|
alt node_tf.last_child {
ops.with_tree_fields(parent) { |parent_tf|
alt parent_tf.last_child {
none {
node_tf.first_child = some(child);
parent_tf.first_child = some(child);
}
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)]
mod test {
enum dummy = @{
@ -129,4 +137,4 @@ mod test {
}
assert i == 1u;
}
}
}