mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
separate into tasks that actually parse
This commit is contained in:
parent
480d36137e
commit
275185ab26
10 changed files with 166 additions and 104 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
38
src/servo/parser/html_builder.rs
Normal file
38
src/servo/parser/html_builder.rs
Normal 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;
|
||||
}
|
|
@ -35,6 +35,7 @@ mod layout {
|
|||
|
||||
mod parser {
|
||||
mod html;
|
||||
mod html_builder;
|
||||
}
|
||||
|
||||
mod platform {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue