Changed the css parsing to be called when a <link /> is used instead of guessing the filename.

This commit is contained in:
Margaret Meyerhofer 2012-06-22 11:32:16 -07:00
parent 7508d6d2a1
commit 03ec0860fa
13 changed files with 175 additions and 93 deletions

View file

@ -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

View file

@ -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"; }
}

View file

@ -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<Token>) -> 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<stylesheet>, from_parent : port<css_message>) {
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<Token>) -> (Node, port<stylesheet>) {
// 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<Token>) -> 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<Token>) -> Node {
}
}
}
ret cur;
}
style_chan.send(exit);
ret (cur_node, style_port);
}

View file

@ -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<Token> {
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<Token> {
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;
}

View file

@ -0,0 +1,6 @@
<head>
<link rel="stylesheet" href="test_linking.css" />
</head>
<body>
<img></img>
</body>

View file

@ -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}

View file

@ -1,4 +1,9 @@
<div class="gray">
<head>
<link rel="stylesheet" href="lots_of_background_colors.css" />
</head>
<body>
<div class="gray">
<img class="green"></img>
<div class="blue"><img class="red"></img></div>
</div>
</div>
</body>

View file

@ -1 +1,6 @@
<img></img>
<head>
<link rel="stylesheet" href="small_color_test.css" />
</head>
<body>
<img></img>
</body>

View file

@ -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)}

View file

@ -1,4 +1,8 @@
<div class="darkgray">
<head>
<link rel="stylesheet" href="lots_of_background_colors.css" />
</head>
<body>
<div class="darkgray">
<div class="darkblue">
<img class="maroon"></img>
<div class="darkgreen">
@ -15,4 +19,5 @@
<img class="blue"></img>
<img class="red"></img>
</div>
</div>
</div>
</body>

View file

@ -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)}

View file

@ -1,6 +1,11 @@
<div class="darkgray">
<div class="blue">
<img class="red"></img>
<head>
<link rel="stylesheet" href="lots_of_background_colors.css" />
</head>
<body>
<div class="darkgray">
<div class="blue">
<img class="red"></img>
</div>
<img class="green"></img>
</div>
<img class="green"></img>
</div>
</body>

View file

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