From 03ec0860fa0d7c2e0a41b2a46db4ed326c8db7e6 Mon Sep 17 00:00:00 2001 From: Margaret Meyerhofer Date: Fri, 22 Jun 2012 11:32:16 -0700 Subject: [PATCH] Changed the css parsing to be called when a is used instead of guessing the filename. --- src/servo/content.rs | 25 +--- src/servo/parser/css_builder.rs | 4 +- src/servo/parser/html_builder.rs | 116 ++++++++++++++++-- src/servo/parser/html_lexer.rs | 36 ++++-- src/test/head_link_test.html | 6 + ...test.css => lots_of_background_colors.css} | 8 +- src/test/small-layout-test.html | 9 +- src/test/small_color_test.html | 7 +- src/test/test_bg_color.css | 16 --- src/test/test_bg_color.html | 9 +- src/test/test_inline_boxes.css | 16 --- src/test/test_inline_boxes.html | 15 ++- src/test/test_linking.css | 1 + 13 files changed, 175 insertions(+), 93 deletions(-) create mode 100644 src/test/head_link_test.html rename src/test/{small-layout-test.css => lots_of_background_colors.css} (73%) delete mode 100644 src/test/test_bg_color.css delete mode 100644 src/test/test_inline_boxes.css create mode 100644 src/test/test_linking.css diff --git a/src/servo/content.rs b/src/servo/content.rs index d5adf45745c..2b6ecc0bd99 100644 --- a/src/servo/content.rs +++ b/src/servo/content.rs @@ -63,39 +63,22 @@ fn Content(layout: Layout) -> Content { ParseMsg(filename) { #debug["content: Received filename `%s` to parse", *filename]; - // 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"; - - // Send off a task to parse the stylesheet - let css_port = port(); - let css_chan = chan(css_port); - spawn { || - let new_file = copy new_file; - 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 = spawn_html_lexer_task(copy filename); - let root = build_dom(scope, stream); + let (root, style_port) = build_dom(scope, stream); // Collect the css stylesheet - let css_rules = css_port.recv(); + let css_rules = style_port.recv(); // Apply the css rules to the dom tree: - // TODO #debug["%s", print_sheet(css_rules)]; - - + // Now, join the layout so that they will see the latest // changes we have made. join_layout(scope, layout); - // Send new document to layout. + // Send new document and relevant styles to layout layout.send(BuildMsg(root, css_rules)); // Indicate that reader was forked so any further diff --git a/src/servo/parser/css_builder.rs b/src/servo/parser/css_builder.rs index 5f5f0aac1d3..aa8e2ebe493 100644 --- a/src/servo/parser/css_builder.rs +++ b/src/servo/parser/css_builder.rs @@ -48,8 +48,8 @@ fn parse_element(reader : TokenReader) -> option<~selector> { break; } Eof { ret none; } - Element(_) { fail "Unexpected second element without " + - "relation to first element"; } + Element(_) { fail "Unexpected second element without " + + "relation to first element"; } EndDescription { fail "Unexpected '}'"; } Description(_, _) { fail "Unexpected description"; } } diff --git a/src/servo/parser/html_builder.rs b/src/servo/parser/html_builder.rs index a495f4c2ddb..661ea650f01 100644 --- a/src/servo/parser/html_builder.rs +++ b/src/servo/parser/html_builder.rs @@ -9,9 +9,15 @@ import gfx::geometry; import gfx::geometry::au; import parser = parser::html_lexer; import parser::Token; +import dom::style::stylesheet; import dvec::extensions; +enum css_message { + file(~str), + exit +} + #[warn(no_non_implicitly_copyable_typarams)] fn link_up_attribute(scope: NodeScope, node: Node, -key: str, -value: str) { // TODO: Implement atoms so that we don't always perform string comparisons. @@ -66,9 +72,69 @@ fn build_element_kind(tag_name: str) -> ~ElementKind { } } -fn build_dom(scope: NodeScope, stream: port) -> Node { +#[doc="Runs a task that coordinates parsing links to css stylesheets. + +This function should be spawned in a separate task and spins waiting +for the html builder to find links to css stylesheets and sends off +tasks to parse each link. When the html process finishes, it notifies +the listener, who then collects the css rules from each task it +spawned, collates them, and sends them to the given result channel. + +# Arguments + +* `to_parent` - A channel on which to send back the full set of rules. +* `from_parent` - A port on which to receive new links. + +"] +fn css_link_listener(to_parent : chan, from_parent : port) { + let mut result_vec = []; + + loop { + alt from_parent.recv() { + file(filename) { + let result_port = comm::port(); + let result_chan = comm::chan(result_port); + let filename = copy filename; + task::spawn{ || + //TODO: deal with extraneous copies + let filename <- copy filename; + let css_stream = css_lexer::spawn_css_lexer_task(filename); + let mut css_rules = css_builder::build_stylesheet(css_stream); + result_chan.send(css_rules); + } + result_vec += [result_port]; + } + exit { + break; + } + } + } + + let css_rules = []; + + let css_rules = result_vec.foldl(css_rules) { |rules, result_port| + let new_rules = result_port.recv(); + rules + new_rules + }; + + to_parent.send(css_rules); +} + +#[warn(no_non_implicitly_copyable_typarams)] +fn build_dom(scope: NodeScope, stream: port) -> (Node, port) { // The current reference node. - let mut cur = scope.new_node(Element(ElementData("html", ~HTMLDivElement))); + let mut cur_node = scope.new_node(Element(ElementData("html", ~HTMLDivElement))); + // We will spawn a separate task to parse any css that is + // encountered, each link to a stylesheet is sent to the waiting + // task. After the html sheet has been fully read, the spawned + // task will collect the results of all linked style data and send + // it along the returned port. + let style_port = comm::port(); + let child_chan = comm::chan(style_port); + let style_chan = task::spawn_listener { |child_port| + css_link_listener(child_chan, child_port); + }; + loop { let token = stream.recv(); alt token { @@ -77,26 +143,48 @@ fn build_dom(scope: NodeScope, stream: port) -> Node { #debug["starting tag %s", tag_name]; let element_kind = build_element_kind(tag_name); let new_node = scope.new_node(Element(ElementData(copy tag_name, element_kind))); - scope.add_child(cur, new_node); - cur = new_node; + scope.add_child(cur_node, new_node); + cur_node = new_node; } parser::Attr(key, value) { #debug["attr: %? = %?", key, value]; - link_up_attribute(scope, cur, copy key, copy value); + link_up_attribute(scope, cur_node, copy key, copy value); } parser::EndOpeningTag { #debug("end opening tag"); } - - parser::EndTag(_) | parser::SelfCloseTag { + // TODO: Fail more gracefully (i.e. according to the HTML5 + // spec) if we close more tags than we open. + parser::SelfCloseTag { + //TODO: check for things other than the link tag + scope.read(cur_node) { |n| + alt *n.kind { + Element(elmt) if elmt.tag_name == "link" { + alt elmt.get_attr("rel") { + some(r) if r == "stylesheet" { + alt elmt.get_attr("href") { + some(filename) { + #debug["Linking to a css sheet named: %s", filename]; + style_chan.send(file(~copy filename)); + } + none { /* fall through*/ } + } + } + _ { /* fall through*/ } + } + } + _ { /* fall through*/ } + } + } + cur_node = scope.get_parent(cur_node).get(); + } + parser::EndTag(_) { // 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(); + cur_node = scope.get_parent(cur_node).get(); } parser::Text(s) if !s.is_whitespace() { let new_node = scope.new_node(Text(copy s)); - scope.add_child(cur, new_node); + scope.add_child(cur_node, new_node); } parser::Text(_) { // FIXME: Whitespace should not be ignored. @@ -106,6 +194,8 @@ fn build_dom(scope: NodeScope, stream: port) -> Node { } } } - ret cur; -} + style_chan.send(exit); + + ret (cur_node, style_port); +} diff --git a/src/servo/parser/html_lexer.rs b/src/servo/parser/html_lexer.rs index 75a1f7e966a..de203a9ad12 100644 --- a/src/servo/parser/html_lexer.rs +++ b/src/servo/parser/html_lexer.rs @@ -99,8 +99,19 @@ impl html_methods for HtmlLexer { } if ch == ('/' as u8) { - self.parser_state = NormalHtml; - ret SelfCloseTag; + alt self.input_state.get() { + CoeChar(c) { + if c == ('>' as u8) { + self.parser_state = NormalHtml; + ret SelfCloseTag; + } else { + #warn["/ not followed by > in a tag"]; + } + } + CoeEof { + #warn["/ not followed by > at end of file"]; + } + } } if !ch.is_alpha() { @@ -116,8 +127,8 @@ impl html_methods for HtmlLexer { attribute_name += [c]; } CoeEof { - ret Attr(attribute_name.to_str(), - attribute_name.to_str()); } + ret Attr(attribute_name.to_str(), attribute_name.to_str()); + } } } @@ -131,8 +142,7 @@ impl html_methods for HtmlLexer { attribute_value += [c]; } CoeEof { - ret Attr(attribute_name.to_str(), - attribute_value.to_str()); + ret Attr(attribute_name.to_str(), attribute_value.to_str()); } } } @@ -151,10 +161,13 @@ fn lexer(reader: io::reader, state : ParseState) -> HtmlLexer { #[warn(no_non_implicitly_copyable_typarams)] fn spawn_html_lexer_task(-filename: ~str) -> port { - let result_port = port(); - let result_chan = chan(result_port); + let html_port = port(); + let html_chan = chan(html_port); + let html_file = copy filename; + task::spawn {|| - assert (*copy filename).ends_with(".html"); + let filename = copy html_file; + assert (copy *filename).ends_with(".html"); let file_data = io::read_whole_file(*filename).get(); let reader = io::bytes_reader(file_data); @@ -163,9 +176,10 @@ fn spawn_html_lexer_task(-filename: ~str) -> port { loop { let token = lexer.parse_html(); let should_break = token == Eof; - result_chan.send(token); + html_chan.send(token); if should_break { break; } } }; - ret result_port; + + ret html_port; } diff --git a/src/test/head_link_test.html b/src/test/head_link_test.html new file mode 100644 index 00000000000..cbab0074406 --- /dev/null +++ b/src/test/head_link_test.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/test/small-layout-test.css b/src/test/lots_of_background_colors.css similarity index 73% rename from src/test/small-layout-test.css rename to src/test/lots_of_background_colors.css index a987a0264b2..6c2d2f10c57 100644 --- a/src/test/small-layout-test.css +++ b/src/test/lots_of_background_colors.css @@ -4,13 +4,13 @@ .white {background-color : white} .black {background-color : black} .brown {background-color : rgb(200,100,0)} -.gray {background-color : rgb(100,100,100)} +.gray {background-color : gray} .lightgray {background-color : rgb(200,200,200)} .darkgray {background-color : rgb(50,50,50)} -.cyan {background-color : rgb(0,255,255)} -.maroon {background-color : rgb(100,0,20)} +.cyan {background-color : aqua)} +.maroon {background-color : maroon} .pink {background-color : rgb(255,0,255)} .orange {background-color : rgb(255,175,0)} .violet {background-color : rgb(100,0,150)} .darkgreen {background-color : rgb(0,100,0)} -.darkblue {background-color : rgb(0,0,100)} +.darkblue {background-color : navy} diff --git a/src/test/small-layout-test.html b/src/test/small-layout-test.html index ae1dc439fd8..4bc0ee50bbf 100644 --- a/src/test/small-layout-test.html +++ b/src/test/small-layout-test.html @@ -1,4 +1,9 @@ -
+ + + + +
-
+
+ diff --git a/src/test/small_color_test.html b/src/test/small_color_test.html index e78e3375f98..883bb081ba6 100644 --- a/src/test/small_color_test.html +++ b/src/test/small_color_test.html @@ -1 +1,6 @@ - + + + + + + diff --git a/src/test/test_bg_color.css b/src/test/test_bg_color.css deleted file mode 100644 index a987a0264b2..00000000000 --- a/src/test/test_bg_color.css +++ /dev/null @@ -1,16 +0,0 @@ -.green {background-color : green} -.blue {background-color : blue} -.red {background-color : red} -.white {background-color : white} -.black {background-color : black} -.brown {background-color : rgb(200,100,0)} -.gray {background-color : rgb(100,100,100)} -.lightgray {background-color : rgb(200,200,200)} -.darkgray {background-color : rgb(50,50,50)} -.cyan {background-color : rgb(0,255,255)} -.maroon {background-color : rgb(100,0,20)} -.pink {background-color : rgb(255,0,255)} -.orange {background-color : rgb(255,175,0)} -.violet {background-color : rgb(100,0,150)} -.darkgreen {background-color : rgb(0,100,0)} -.darkblue {background-color : rgb(0,0,100)} diff --git a/src/test/test_bg_color.html b/src/test/test_bg_color.html index 1e92be72497..7672b166b70 100644 --- a/src/test/test_bg_color.html +++ b/src/test/test_bg_color.html @@ -1,4 +1,8 @@ -
+ + + + +
@@ -15,4 +19,5 @@
-
+
+ diff --git a/src/test/test_inline_boxes.css b/src/test/test_inline_boxes.css deleted file mode 100644 index a987a0264b2..00000000000 --- a/src/test/test_inline_boxes.css +++ /dev/null @@ -1,16 +0,0 @@ -.green {background-color : green} -.blue {background-color : blue} -.red {background-color : red} -.white {background-color : white} -.black {background-color : black} -.brown {background-color : rgb(200,100,0)} -.gray {background-color : rgb(100,100,100)} -.lightgray {background-color : rgb(200,200,200)} -.darkgray {background-color : rgb(50,50,50)} -.cyan {background-color : rgb(0,255,255)} -.maroon {background-color : rgb(100,0,20)} -.pink {background-color : rgb(255,0,255)} -.orange {background-color : rgb(255,175,0)} -.violet {background-color : rgb(100,0,150)} -.darkgreen {background-color : rgb(0,100,0)} -.darkblue {background-color : rgb(0,0,100)} diff --git a/src/test/test_inline_boxes.html b/src/test/test_inline_boxes.html index 2a7d2cf9909..0d1bf947688 100644 --- a/src/test/test_inline_boxes.html +++ b/src/test/test_inline_boxes.html @@ -1,6 +1,11 @@ -
-
- + + + + +
+
+ +
+
- -
+ diff --git a/src/test/test_linking.css b/src/test/test_linking.css new file mode 100644 index 00000000000..3445c564d9e --- /dev/null +++ b/src/test/test_linking.css @@ -0,0 +1 @@ +img {background-color : green}