Changed css matching to match nodes in parallel.

This commit is contained in:
Margaret Meyerhofer 2012-06-14 17:20:59 -07:00
parent d7db7a56c3
commit a322542825
11 changed files with 74 additions and 62 deletions

View file

@ -9,6 +9,9 @@ export content;
import dom::base::NodeScope;
import dom::rcu::WriterMethods;
import dom::style;
import parser::lexer::{spawn_css_lexer_task, spawn_html_parser_task};
import parser::css_builder::build_stylesheet;
import parser::html_builder::build_dom;
import layout::layout_task;
import js::rust::methods;
@ -51,26 +54,23 @@ fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
// TODO actually parse where the css sheet should be
// Replace .html with .css and try to open a stylesheet
assert (*filename).ends_with(".html");
let new_file = (*filename).substr(0u, (*filename).len() - 5u)
+ ".css";
let new_file = (*filename).substr(0u, (*filename).len() - 5u) + ".css";
// Send off a task to parse the stylesheet
let css_port = comm::port();
let css_chan = comm::chan(css_port);
task::spawn {||
let new_file <- new_file;
let css_stream = parser::lexer::
spawn_css_lexer_task(~new_file);
let css_rules = parser::css_builder::
build_stylesheet(css_stream);
let css_stream = spawn_css_lexer_task(~new_file);
let css_rules = build_stylesheet(css_stream);
css_chan.send(css_rules);
};
// Note: we can parse the next document in parallel
// with any previous documents.
let stream = parser::lexer::spawn_html_parser_task(filename);
let root = parser::html_builder::build_dom(scope, stream);
let stream = spawn_html_parser_task(filename);
let root = build_dom(scope, stream);
// Collect the css stylesheet
let css_rules = comm::recv(css_port);
@ -92,8 +92,7 @@ fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
}
ExecuteMsg(filename) {
#debug["content: Received filename `%s` to execute",
*filename];
#debug["content: Received filename `%s` to execute", *filename];
alt io::read_whole_file(*filename) {
result::err(msg) {
@ -106,8 +105,7 @@ fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
cx.new_compartment(js::global::global_class).chain {
|compartment|
compartment.define_functions(js::global::debug_fns);
cx.evaluate_script(compartment.global_obj, bytes,
*filename, 1u)
cx.evaluate_script(compartment.global_obj, bytes, *filename, 1u)
};
}
}

View file

@ -51,7 +51,7 @@ class Box {
}
enum layout_data = {
mut computed_style: computed_style,
mut computed_style: ~computed_style,
mut box: option<@Box>
};

View file

@ -55,9 +55,11 @@ fn build_display_list_from_origin(box: @Box, origin: Point2D<au>)
#[doc="
Creates a display list item for a single block.
Args:
-box: the box to build the display list for
-origin: the coordinates of upper-left corner of the passed in box.
# Arguments
* `box` - The box to build the display list for
* `origin` - The coordinates of upper-left corner of the passed in box.
"]
fn box_to_display_items(box: @Box, origin: Point2D<au>) -> [dl::display_item] {

View file

@ -35,6 +35,7 @@ fn layout(to_renderer: chan<renderer::Msg>) -> chan<Msg> {
#debug("layout: received layout request for:");
node.dump();
node.initialize_style_for_subtree();
node.recompute_style_for_subtree(styles);
let this_box = node.construct_boxes();

View file

@ -10,15 +10,6 @@ import style::{computed_style, default_style_for_node_kind};
export matching_methods;
#[doc="Update the computed style of an HTML element with a style specified by CSS."]
fn update_style(style : @computed_style, decl : style_decl) {
alt decl {
display(dis) { (*style).display = dis; }
background_color(col) { (*style).back_color = col; }
text_color(*) | font_size(*) { /* not supported yet */ }
}
}
#[doc="Check if a CSS attribute matches the attribute of an HTML element."]
fn attrs_match(attr: attr, elmt: ElementData) -> bool {
alt attr {
@ -170,14 +161,23 @@ impl priv_matching_methods for Node {
}
}
impl priv_style_methods for Node {
#[doc="Update the computed style of an HTML element with a style specified by CSS."]
fn update_style(decl : style_decl) {
self.aux() { |layout|
alt decl {
display(dis) { layout.computed_style.display = dis; }
background_color(col) { layout.computed_style.back_color = col; }
text_color(*) | font_size(*) { /* not supported yet */ }
}
}
}
}
impl matching_methods for Node {
#[doc="Compare an html element to a list of css rules and update its
style according to the rules matching it."]
fn match_css_style(styles : stylesheet) -> computed_style {
let node_kind = self.read { |n| copy *n.kind };
let style =
@default_style_for_node_kind(node_kind);
fn match_css_style(styles : stylesheet) {
// Loop over each rule, see if our node matches what is described in the rule. If it
// matches, update its style. As we don't currently have priorities of style information,
// the latest rule takes precedence over the others. So we just overwrite style
@ -187,20 +187,18 @@ impl matching_methods for Node {
let (selectors, decls) <- *(copy sty);
for selectors.each { |sel|
if self.matches_selector(sel) {
#debug("Matched selector {%?} with node {%?}", *sel, node_kind);
for decls.each { |decl|
update_style(style, decl);
self.update_style(decl);
}
}
}
}
#debug["Changed the style to: %?", *style];
ret copy *(style);
self.aux() { |a| #debug["Changed the style to: %?", copy *a.computed_style]; }
}
}
#[cfg(test)]
mod test {
import dom::base::{Attr, Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement};
import dom::base::{NodeScope, TreeReadMethods, TreeWriteMethods, UnknownElement};

View file

@ -9,8 +9,7 @@ import matching::matching_methods;
import util::color::{Color, rgb};
import util::color::css_colors::{white, black};
type computed_style = {mut display : display_type,
mut back_color : Color};
type computed_style = {mut display : display_type, mut back_color : Color};
#[doc="Returns the default style for the given node kind."]
fn default_style_for_node_kind(kind: NodeKind) -> computed_style {
@ -34,29 +33,30 @@ fn default_style_for_node_kind(kind: NodeKind) -> computed_style {
}
impl style_priv for Node {
#[doc="
Performs CSS selector matching on a node.
#[doc="Set a default auxilliary data so that other threads can modify it.
This is, importantly, the function that creates the layout data for the node (the reader-
auxiliary box in the RCU model) and populates it with the computed style.
"]
fn recompute_style(styles : stylesheet) {
let style = self.match_css_style(styles);
#debug("recomputing style; parent node:");
auxiliary box in the RCU model) and populates it with the default style.
"]
fn initialize_style() {
let node_kind = self.read { |n| copy *n.kind };
let the_layout_data = @layout_data({
mut computed_style: style,
mut box: none
mut computed_style : ~default_style_for_node_kind(node_kind),
mut box : none
});
#debug("layout data: %?", the_layout_data);
self.set_aux(the_layout_data);
}
}
impl style_methods for Node {
#[doc="Sequentially initialize the nodes' auxilliary data so they can be updated in parallel."]
fn initialize_style_for_subtree() {
self.initialize_style();
for ntree.each_child(self) { |kid| kid.initialize_style_for_subtree(); }
}
#[doc="
Returns the computed style for the given node. If CSS selector matching has not yet been
performed, fails.
@ -67,23 +67,30 @@ impl style_methods for Node {
if !self.has_aux() {
fail "get_computed_style() called on a node without a style!";
}
ret copy self.aux({ |x| copy x }).computed_style;
ret copy *self.aux({ |x| copy x }).computed_style;
}
#[doc="
Performs CSS selector matching on a subtree.
This is, importantly, the function that creates the layout data for the node (the reader-
auxiliary box in the RCU model) and populates it with the computed style.
TODO: compute the style of multiple nodes in parallel.
This is, importantly, the function that updates the layout data for the node (the reader-
auxiliary box in the RCU model) with the computed style.
"]
fn recompute_style_for_subtree(styles : stylesheet) {
self.recompute_style(styles);
for ntree.each_child(self) {
|kid|
kid.recompute_style_for_subtree(styles);
listen { |ack_chan|
// TODO: Don't copy this for every element, look into shared, immutable state
let new_styles = copy styles;
task::spawn { ||
self.match_css_style(new_styles);
ack_chan.send(());
}
for ntree.each_child(self) { |kid| kid.recompute_style_for_subtree(styles); }
// Make sure we finish updating the tree before returning
ack_chan.recv();
}
}
}

View file

@ -174,6 +174,7 @@ mod parsing {
}
}
#[cfg(test)]
mod test {
import css_colors::*;
import parsing::parse_color;

View file

@ -0,0 +1 @@
img {background-color : red }

View file

@ -0,0 +1 @@
<img></img>

View file

@ -4,6 +4,8 @@ p.blue > p.green + p.red { background-color : blue ;color : green }
img[class] .pastoral *[lang|=en] { display:inline}
.book > #novel + *[type=novella] p { color : blue; color : white }
* {background-color : red}
* * {background-color : lime}
* * * {background-color : yellow}
* * * * {background-color : white}
* * * * * {background-color : rgb(200,0,200)}
div div {background-color : green}

1
src/test/tiny_test.html Normal file
View file

@ -0,0 +1 @@