diff --git a/src/servo/content.rs b/src/servo/content.rs index 7655e6e216f..3e9264dfb1c 100644 --- a/src/servo/content.rs +++ b/src/servo/content.rs @@ -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) -> chan { +enum ping { + pong +} - task::spawn_listener:: {|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) { + 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) -> chan { + task::spawn_listener:: {|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); + } } } } diff --git a/src/servo/dom/base.rs b/src/servo/dom/base.rs index 30c3f9eb210..d1c15c9eff2 100644 --- a/src/servo/dom/base.rs +++ b/src/servo/dom/base.rs @@ -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; +type node_scope = rcu::scope; -impl methods for scope { +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 for scope { +impl of tree::rd_tree_ops for node_scope { fn each_child(node: node, f: fn(node) -> bool) { tree::each_child(self, node, f) } + fn get_parent(node: node) -> option { + tree::get_parent(self, node) + } + fn with_tree_fields(node: node, f: fn(tree::fields) -> R) -> R { - f(self.rd(node) { |f| f.tree }) + self.rd(node) { |n| f(n.tree) } } } -impl of tree::wr_tree_ops for scope { +impl of tree::wr_tree_ops for node_scope { fn add_child(node: node, child: node) { tree::add_child(self, node, child) } fn with_tree_fields(node: node, f: fn(tree::fields) -> R) -> R { - f(self.wr(node) { |f| f.tree }) + self.wr(node) { |n| f(n.tree) } } } diff --git a/src/servo/dom/rcu.rs b/src/servo/dom/rcu.rs index 02233b6ed0c..f54fe0c5a6e 100644 --- a/src/servo/dom/rcu.rs +++ b/src/servo/dom/rcu.rs @@ -98,6 +98,10 @@ fn scope() -> scope { } impl writer_methods for scope { + 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 for scope { self.first_dirty = null_handle(); } + assert self.first_dirty.is_null(); self.layout_active = false; } @@ -130,12 +135,11 @@ impl writer_methods for scope { } fn wr(h: handle, 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()) } diff --git a/src/servo/gfx/renderer.rs b/src/servo/gfx/renderer.rs index 5a7386ce6e3..2a3ef9d991d 100644 --- a/src/servo/gfx/renderer.rs +++ b/src/servo/gfx/renderer.rs @@ -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) -> chan { 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) -> chan { 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; diff --git a/src/servo/layout/base.rs b/src/servo/layout/base.rs index f1e34053903..6fc12c7c730 100644 --- a/src/servo/layout/base.rs +++ b/src/servo/layout/base.rs @@ -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. diff --git a/src/servo/layout/layout.rs b/src/servo/layout/layout.rs index 755ec5ef506..e4fe2e5ac60 100644 --- a/src/servo/layout/layout.rs +++ b/src/servo/layout/layout.rs @@ -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), exit } -fn layout(renderer: chan) -> chan { - - spawn_listener:: {|po| - - let r = rand::rng(); - +fn layout(to_renderer: chan) -> chan { + spawn_listener:: { |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); diff --git a/src/servo/parser/html_builder.rs b/src/servo/parser/html_builder.rs new file mode 100644 index 00000000000..0d4d1f2a4dc --- /dev/null +++ b/src/servo/parser/html_builder.rs @@ -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) -> 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; +} diff --git a/src/servo/servo.rc b/src/servo/servo.rc index 1b3e3464472..a2f25852963 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -35,6 +35,7 @@ mod layout { mod parser { mod html; + mod html_builder; } mod platform { diff --git a/src/servo/servo.rs b/src/servo/servo.rs index 0318854e2e6..740b3b9f41d 100644 --- a/src/servo/servo.rs +++ b/src/servo/servo.rs @@ -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); diff --git a/src/servo/util/tree.rs b/src/servo/util/tree.rs index 8805853a105..a82f822470a 100644 --- a/src/servo/util/tree.rs +++ b/src/servo/util/tree.rs @@ -1,3 +1,7 @@ +// A generic tree datatype. +// +// TODO: Use traits. + type fields = { mut parent: option, mut first_child: option, @@ -38,21 +42,21 @@ fn empty() -> fields { } fn add_child>( - 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>( } } - node_tf.last_child = some(child); + parent_tf.last_child = some(child); } } } +fn get_parent>(ops: O, node: T) -> option { + ops.with_tree_fields(node) { |tf| tf.parent } +} + #[cfg(test)] mod test { enum dummy = @{ @@ -129,4 +137,4 @@ mod test { } assert i == 1u; } -} \ No newline at end of file +}