mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Changed the css parsing to be called when a <link /> is used instead of guessing the filename.
This commit is contained in:
parent
7508d6d2a1
commit
03ec0860fa
13 changed files with 175 additions and 93 deletions
|
@ -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
|
||||
|
|
|
@ -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"; }
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
6
src/test/head_link_test.html
Normal file
6
src/test/head_link_test.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<head>
|
||||
<link rel="stylesheet" href="test_linking.css" />
|
||||
</head>
|
||||
<body>
|
||||
<img></img>
|
||||
</body>
|
|
@ -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}
|
|
@ -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>
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
<img></img>
|
||||
<head>
|
||||
<link rel="stylesheet" href="small_color_test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<img></img>
|
||||
</body>
|
||||
|
|
|
@ -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)}
|
|
@ -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>
|
||||
|
|
|
@ -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)}
|
|
@ -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>
|
||||
|
|
1
src/test/test_linking.css
Normal file
1
src/test/test_linking.css
Normal file
|
@ -0,0 +1 @@
|
|||
img {background-color : green}
|
Loading…
Add table
Add a link
Reference in a new issue