mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +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) {
|
ParseMsg(filename) {
|
||||||
#debug["content: Received filename `%s` to parse", *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
|
// Note: we can parse the next document in parallel
|
||||||
// with any previous documents.
|
// with any previous documents.
|
||||||
let stream = spawn_html_lexer_task(copy filename);
|
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
|
// Collect the css stylesheet
|
||||||
let css_rules = css_port.recv();
|
let css_rules = style_port.recv();
|
||||||
|
|
||||||
// Apply the css rules to the dom tree:
|
// Apply the css rules to the dom tree:
|
||||||
// TODO
|
|
||||||
#debug["%s", print_sheet(css_rules)];
|
#debug["%s", print_sheet(css_rules)];
|
||||||
|
|
||||||
|
|
||||||
// Now, join the layout so that they will see the latest
|
// Now, join the layout so that they will see the latest
|
||||||
// changes we have made.
|
// changes we have made.
|
||||||
join_layout(scope, layout);
|
join_layout(scope, layout);
|
||||||
|
|
||||||
// Send new document to layout.
|
// Send new document and relevant styles to layout
|
||||||
layout.send(BuildMsg(root, css_rules));
|
layout.send(BuildMsg(root, css_rules));
|
||||||
|
|
||||||
// Indicate that reader was forked so any further
|
// Indicate that reader was forked so any further
|
||||||
|
|
|
@ -48,8 +48,8 @@ fn parse_element(reader : TokenReader) -> option<~selector> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Eof { ret none; }
|
Eof { ret none; }
|
||||||
Element(_) { fail "Unexpected second element without " +
|
Element(_) { fail "Unexpected second element without "
|
||||||
"relation to first element"; }
|
+ "relation to first element"; }
|
||||||
EndDescription { fail "Unexpected '}'"; }
|
EndDescription { fail "Unexpected '}'"; }
|
||||||
Description(_, _) { fail "Unexpected description"; }
|
Description(_, _) { fail "Unexpected description"; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,15 @@ import gfx::geometry;
|
||||||
import gfx::geometry::au;
|
import gfx::geometry::au;
|
||||||
import parser = parser::html_lexer;
|
import parser = parser::html_lexer;
|
||||||
import parser::Token;
|
import parser::Token;
|
||||||
|
import dom::style::stylesheet;
|
||||||
|
|
||||||
import dvec::extensions;
|
import dvec::extensions;
|
||||||
|
|
||||||
|
enum css_message {
|
||||||
|
file(~str),
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
#[warn(no_non_implicitly_copyable_typarams)]
|
#[warn(no_non_implicitly_copyable_typarams)]
|
||||||
fn link_up_attribute(scope: NodeScope, node: Node, -key: str, -value: str) {
|
fn link_up_attribute(scope: NodeScope, node: Node, -key: str, -value: str) {
|
||||||
// TODO: Implement atoms so that we don't always perform string comparisons.
|
// 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.
|
// 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 {
|
loop {
|
||||||
let token = stream.recv();
|
let token = stream.recv();
|
||||||
alt token {
|
alt token {
|
||||||
|
@ -77,26 +143,48 @@ fn build_dom(scope: NodeScope, stream: port<Token>) -> Node {
|
||||||
#debug["starting tag %s", tag_name];
|
#debug["starting tag %s", tag_name];
|
||||||
let element_kind = build_element_kind(tag_name);
|
let element_kind = build_element_kind(tag_name);
|
||||||
let new_node = scope.new_node(Element(ElementData(copy tag_name, element_kind)));
|
let new_node = scope.new_node(Element(ElementData(copy tag_name, element_kind)));
|
||||||
scope.add_child(cur, new_node);
|
scope.add_child(cur_node, new_node);
|
||||||
cur = new_node;
|
cur_node = new_node;
|
||||||
}
|
}
|
||||||
parser::Attr(key, value) {
|
parser::Attr(key, value) {
|
||||||
#debug["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 {
|
parser::EndOpeningTag {
|
||||||
#debug("end opening tag");
|
#debug("end opening tag");
|
||||||
}
|
}
|
||||||
|
// TODO: Fail more gracefully (i.e. according to the HTML5
|
||||||
parser::EndTag(_) | parser::SelfCloseTag {
|
// 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: Assert that the closing tag has the right name.
|
||||||
// TODO: Fail more gracefully (i.e. according to the HTML5
|
cur_node = scope.get_parent(cur_node).get();
|
||||||
// spec) if we close more tags than we open.
|
|
||||||
cur = scope.get_parent(cur).get();
|
|
||||||
}
|
}
|
||||||
parser::Text(s) if !s.is_whitespace() {
|
parser::Text(s) if !s.is_whitespace() {
|
||||||
let new_node = scope.new_node(Text(copy s));
|
let new_node = scope.new_node(Text(copy s));
|
||||||
scope.add_child(cur, new_node);
|
scope.add_child(cur_node, new_node);
|
||||||
}
|
}
|
||||||
parser::Text(_) {
|
parser::Text(_) {
|
||||||
// FIXME: Whitespace should not be ignored.
|
// 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) {
|
if ch == ('/' as u8) {
|
||||||
self.parser_state = NormalHtml;
|
alt self.input_state.get() {
|
||||||
ret SelfCloseTag;
|
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() {
|
if !ch.is_alpha() {
|
||||||
|
@ -116,8 +127,8 @@ impl html_methods for HtmlLexer {
|
||||||
attribute_name += [c];
|
attribute_name += [c];
|
||||||
}
|
}
|
||||||
CoeEof {
|
CoeEof {
|
||||||
ret Attr(attribute_name.to_str(),
|
ret Attr(attribute_name.to_str(), attribute_name.to_str());
|
||||||
attribute_name.to_str()); }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,8 +142,7 @@ impl html_methods for HtmlLexer {
|
||||||
attribute_value += [c];
|
attribute_value += [c];
|
||||||
}
|
}
|
||||||
CoeEof {
|
CoeEof {
|
||||||
ret Attr(attribute_name.to_str(),
|
ret Attr(attribute_name.to_str(), attribute_value.to_str());
|
||||||
attribute_value.to_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,10 +161,13 @@ fn lexer(reader: io::reader, state : ParseState) -> HtmlLexer {
|
||||||
|
|
||||||
#[warn(no_non_implicitly_copyable_typarams)]
|
#[warn(no_non_implicitly_copyable_typarams)]
|
||||||
fn spawn_html_lexer_task(-filename: ~str) -> port<Token> {
|
fn spawn_html_lexer_task(-filename: ~str) -> port<Token> {
|
||||||
let result_port = port();
|
let html_port = port();
|
||||||
let result_chan = chan(result_port);
|
let html_chan = chan(html_port);
|
||||||
|
let html_file = copy filename;
|
||||||
|
|
||||||
task::spawn {||
|
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 file_data = io::read_whole_file(*filename).get();
|
||||||
let reader = io::bytes_reader(file_data);
|
let reader = io::bytes_reader(file_data);
|
||||||
|
|
||||||
|
@ -163,9 +176,10 @@ fn spawn_html_lexer_task(-filename: ~str) -> port<Token> {
|
||||||
loop {
|
loop {
|
||||||
let token = lexer.parse_html();
|
let token = lexer.parse_html();
|
||||||
let should_break = token == Eof;
|
let should_break = token == Eof;
|
||||||
result_chan.send(token);
|
html_chan.send(token);
|
||||||
if should_break { break; }
|
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}
|
.white {background-color : white}
|
||||||
.black {background-color : black}
|
.black {background-color : black}
|
||||||
.brown {background-color : rgb(200,100,0)}
|
.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)}
|
.lightgray {background-color : rgb(200,200,200)}
|
||||||
.darkgray {background-color : rgb(50,50,50)}
|
.darkgray {background-color : rgb(50,50,50)}
|
||||||
.cyan {background-color : rgb(0,255,255)}
|
.cyan {background-color : aqua)}
|
||||||
.maroon {background-color : rgb(100,0,20)}
|
.maroon {background-color : maroon}
|
||||||
.pink {background-color : rgb(255,0,255)}
|
.pink {background-color : rgb(255,0,255)}
|
||||||
.orange {background-color : rgb(255,175,0)}
|
.orange {background-color : rgb(255,175,0)}
|
||||||
.violet {background-color : rgb(100,0,150)}
|
.violet {background-color : rgb(100,0,150)}
|
||||||
.darkgreen {background-color : rgb(0,100,0)}
|
.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>
|
<img class="green"></img>
|
||||||
<div class="blue"><img class="red"></img></div>
|
<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">
|
<div class="darkblue">
|
||||||
<img class="maroon"></img>
|
<img class="maroon"></img>
|
||||||
<div class="darkgreen">
|
<div class="darkgreen">
|
||||||
|
@ -15,4 +19,5 @@
|
||||||
<img class="blue"></img>
|
<img class="blue"></img>
|
||||||
<img class="red"></img>
|
<img class="red"></img>
|
||||||
</div>
|
</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">
|
<head>
|
||||||
<div class="blue">
|
<link rel="stylesheet" href="lots_of_background_colors.css" />
|
||||||
<img class="red"></img>
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="darkgray">
|
||||||
|
<div class="blue">
|
||||||
|
<img class="red"></img>
|
||||||
|
</div>
|
||||||
|
<img class="green"></img>
|
||||||
</div>
|
</div>
|
||||||
<img class="green"></img>
|
</body>
|
||||||
</div>
|
|
||||||
|
|
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