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

View file

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

View file

@ -55,9 +55,11 @@ fn build_display_list_from_origin(box: @Box, origin: Point2D<au>)
#[doc=" #[doc="
Creates a display list item for a single block. Creates a display list item for a single block.
Args:
-box: the box to build the display list for # Arguments
-origin: the coordinates of upper-left corner of the passed in box.
* `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] { 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:"); #debug("layout: received layout request for:");
node.dump(); node.dump();
node.initialize_style_for_subtree();
node.recompute_style_for_subtree(styles); node.recompute_style_for_subtree(styles);
let this_box = node.construct_boxes(); let this_box = node.construct_boxes();

View file

@ -10,15 +10,6 @@ import style::{computed_style, default_style_for_node_kind};
export matching_methods; 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."] #[doc="Check if a CSS attribute matches the attribute of an HTML element."]
fn attrs_match(attr: attr, elmt: ElementData) -> bool { fn attrs_match(attr: attr, elmt: ElementData) -> bool {
alt attr { 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 { impl matching_methods for Node {
#[doc="Compare an html element to a list of css rules and update its #[doc="Compare an html element to a list of css rules and update its
style according to the rules matching it."] style according to the rules matching it."]
fn match_css_style(styles : stylesheet) -> computed_style { fn match_css_style(styles : stylesheet) {
let node_kind = self.read { |n| copy *n.kind };
let style =
@default_style_for_node_kind(node_kind);
// Loop over each rule, see if our node matches what is described in the rule. If it // 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, // 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 // 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); let (selectors, decls) <- *(copy sty);
for selectors.each { |sel| for selectors.each { |sel|
if self.matches_selector(sel) { if self.matches_selector(sel) {
#debug("Matched selector {%?} with node {%?}", *sel, node_kind);
for decls.each { |decl| for decls.each { |decl|
update_style(style, decl); self.update_style(decl);
} }
} }
} }
} }
#debug["Changed the style to: %?", *style]; self.aux() { |a| #debug["Changed the style to: %?", copy *a.computed_style]; }
ret copy *(style);
} }
} }
#[cfg(test)]
mod test { mod test {
import dom::base::{Attr, Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement}; import dom::base::{Attr, Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement};
import dom::base::{NodeScope, TreeReadMethods, TreeWriteMethods, UnknownElement}; 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::{Color, rgb};
import util::color::css_colors::{white, black}; import util::color::css_colors::{white, black};
type computed_style = {mut display : display_type, type computed_style = {mut display : display_type, mut back_color : Color};
mut back_color : Color};
#[doc="Returns the default style for the given node kind."] #[doc="Returns the default style for the given node kind."]
fn default_style_for_node_kind(kind: NodeKind) -> computed_style { 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 { impl style_priv for Node {
#[doc=" #[doc="Set a default auxilliary data so that other threads can modify it.
Performs CSS selector matching on a node.
This is, importantly, the function that creates the layout data for the node (the reader- 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. auxiliary box in the RCU model) and populates it with the default style.
"] "]
fn recompute_style(styles : stylesheet) { fn initialize_style() {
let style = self.match_css_style(styles); let node_kind = self.read { |n| copy *n.kind };
#debug("recomputing style; parent node:");
let the_layout_data = @layout_data({ let the_layout_data = @layout_data({
mut computed_style: style, mut computed_style : ~default_style_for_node_kind(node_kind),
mut box: none mut box : none
}); });
#debug("layout data: %?", the_layout_data);
self.set_aux(the_layout_data); self.set_aux(the_layout_data);
} }
} }
impl style_methods for Node { 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=" #[doc="
Returns the computed style for the given node. If CSS selector matching has not yet been Returns the computed style for the given node. If CSS selector matching has not yet been
performed, fails. performed, fails.
@ -67,23 +67,30 @@ impl style_methods for Node {
if !self.has_aux() { if !self.has_aux() {
fail "get_computed_style() called on a node without a style!"; 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=" #[doc="
Performs CSS selector matching on a subtree. Performs CSS selector matching on a subtree.
This is, importantly, the function that creates the layout data for the node (the reader- This is, importantly, the function that updates the layout data for the node (the reader-
auxiliary box in the RCU model) and populates it with the computed style. auxiliary box in the RCU model) with the computed style.
TODO: compute the style of multiple nodes in parallel.
"] "]
fn recompute_style_for_subtree(styles : stylesheet) { fn recompute_style_for_subtree(styles : stylesheet) {
self.recompute_style(styles); listen { |ack_chan|
for ntree.each_child(self) {
|kid| // TODO: Don't copy this for every element, look into shared, immutable state
kid.recompute_style_for_subtree(styles); 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 { mod test {
import css_colors::*; import css_colors::*;
import parsing::parse_color; 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} img[class] .pastoral *[lang|=en] { display:inline}
.book > #novel + *[type=novella] p { color : blue; color : white } .book > #novel + *[type=novella] p { color : blue; color : white }
* {background-color : red} * {background-color : red}
* * {background-color : lime}
* * * {background-color : yellow}
* * * * {background-color : white} * * * * {background-color : white}
* * * * * {background-color : rgb(200,0,200)} * * * * * {background-color : rgb(200,0,200)}
div div {background-color : green} div div {background-color : green}

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

@ -0,0 +1 @@