mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01: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;
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
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 parser {
|
||||||
mod html;
|
mod html;
|
||||||
|
mod html_builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod platform {
|
mod platform {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue