servo: Parse HTML attributes and support images of different widths and heights

Source-Repo: https://github.com/servo/servo
Source-Revision: d4ea1477a764f992ae8faae684287cc2917f9c63
This commit is contained in:
Patrick Walton 2012-05-17 18:21:38 -07:00
Родитель 23e895c155
Коммит a566a859bf
2 изменённых файлов: 126 добавлений и 13 удалений

Просмотреть файл

@ -1,14 +1,22 @@
import comm::{port, chan}; import comm::{port, chan};
enum parse_state {
ps_normal,
ps_tag
}
type parser = { type parser = {
mut lookahead: option<char_or_eof>, mut lookahead: option<char_or_eof>,
mut state: parse_state,
reader: io::reader reader: io::reader
}; };
enum token { enum token {
to_start_tag(str), to_start_opening_tag(str),
to_end_opening_tag,
to_end_tag(str), to_end_tag(str),
to_text(str), to_text(str),
to_attr(str, str),
to_doctype, to_doctype,
to_eof to_eof
} }
@ -18,6 +26,18 @@ enum char_or_eof {
coe_eof coe_eof
} }
impl u8_methods for u8 {
fn is_alpha() -> bool {
ret (self >= ('A' as u8) && self <= ('Z' as u8)) ||
(self >= ('a' as u8) && self <= ('z' as u8));
}
}
impl u8_vec_methods for [u8] {
fn to_str() -> str { ret str::from_bytes(self); }
fn to_str_token() -> token { ret to_text(self.to_str()); }
}
impl methods for parser { impl methods for parser {
fn get() -> char_or_eof { fn get() -> char_or_eof {
alt self.lookahead { alt self.lookahead {
@ -62,8 +82,7 @@ impl methods for parser {
loop { loop {
alt self.get() { alt self.get() {
coe_char(c) { coe_char(c) {
if (c >= ('A' as u8) && c <= ('Z' as u8)) || if (c.is_alpha()) {
(c >= ('a' as u8) && c <= ('z' as u8)) {
result += [c]; result += [c];
} else if result.len() == 0u { } else if result.len() == 0u {
self.parse_err("expected ident"); self.parse_err("expected ident");
@ -112,6 +131,14 @@ impl methods for parser {
coe_eof { ret to_eof; } coe_eof { ret to_eof; }
} }
ret alt self.state {
ps_normal { self.parse_in_normal_state(ch) }
ps_tag { self.parse_in_tag_state(ch) }
}
}
fn parse_in_normal_state(c: u8) -> token {
let mut ch = c;
if ch == ('<' as u8) { if ch == ('<' as u8) {
alt self.get() { alt self.get() {
coe_char(c) { ch = c; } coe_char(c) { ch = c; }
@ -139,8 +166,9 @@ impl methods for parser {
self.eat_whitespace(); self.eat_whitespace();
let ident = self.parse_ident(); let ident = self.parse_ident();
self.eat_whitespace(); self.eat_whitespace();
self.expect('>' as u8);
ret to_start_tag(ident); self.state = ps_tag;
ret to_start_opening_tag(ident);
} }
// Make a text node. // Make a text node.
@ -150,18 +178,66 @@ impl methods for parser {
coe_char(c) { coe_char(c) {
if c == ('<' as u8) { if c == ('<' as u8) {
self.unget(c); self.unget(c);
ret to_text(str::from_bytes(s)); ret s.to_str_token();
} }
s += [c]; s += [c];
} }
coe_eof { ret to_text(str::from_bytes(s)); } coe_eof { ret s.to_str_token(); }
}
} }
} }
} }
fn parse_in_tag_state(c: u8) -> token {
let mut ch = c;
if ch == ('>' as u8) {
self.state = ps_normal;
ret to_end_opening_tag;
}
if !ch.is_alpha() {
fail "expected alphabetical in tag";
}
// Parse an attribute.
let mut attribute_name = [ch];
loop {
alt self.get() {
coe_char(c) {
if c == ('=' as u8) { break; }
attribute_name += [c];
}
coe_eof {
ret to_attr(attribute_name.to_str(),
attribute_name.to_str()); }
}
}
// Parse the attribute value.
self.expect('"' as u8);
let mut attribute_value = [];
loop {
alt self.get() {
coe_char(c) {
if c == ('"' as u8) { break; }
attribute_value += [c];
}
coe_eof {
ret to_attr(attribute_name.to_str(),
attribute_value.to_str());
}
}
}
// Eat whitespace.
self.eat_whitespace();
ret to_attr(attribute_name.to_str(), attribute_value.to_str());
}
}
fn parser(reader: io::reader) -> parser { fn parser(reader: io::reader) -> parser {
ret { mut lookahead: none, reader: reader }; ret { mut lookahead: none, mut state: ps_normal, reader: reader };
} }
fn spawn_parser_task(filename: str) -> port<token> { fn spawn_parser_task(filename: str) -> port<token> {

Просмотреть файл

@ -1,4 +1,4 @@
// Constructs a DOM tree from an incoming token stream. #[doc="Constructs a DOM tree from an incoming token stream."]
import dom::rcu::writer_methods; import dom::rcu::writer_methods;
import dom::base::{methods, rd_tree_ops, wr_tree_ops}; import dom::base::{methods, rd_tree_ops, wr_tree_ops};
@ -7,6 +7,35 @@ import parser = parser::html;
import html::token; import html::token;
import gfx::geom; import gfx::geom;
fn link_up_attribute(scope: dom::node_scope, node: dom::node, key: str,
value: str) {
// TODO: Implement atoms so that we don't always perform string
// comparisons.
// FIXME: This is wrong... we should not have DIV and IMG be separate types
// of nodes and instead have them inherit from Element, obviously.
scope.rd(node) {
|node_contents|
alt node_contents.kind {
dom::nk_img(dims) if key == "width" {
alt int::from_str(value) {
none { /* drop on the floor */ }
some(s) { dims.width = geom::px_to_au(s); }
}
}
dom::nk_img(dims) if key == "height" {
alt int::from_str(value) {
none { /* drop on the floor */ }
some(s) { dims.height = geom::px_to_au(s); }
}
}
dom::nk_div | dom::nk_img(*) { /* drop on the floor */ }
dom::nk_text(*) {
fail "attempt to link up an attribute to a text node"
}
}
}
}
fn build_dom(scope: dom::node_scope, fn build_dom(scope: dom::node_scope,
stream: port<token>) -> dom::node { stream: port<token>) -> dom::node {
// The current reference node. // The current reference node.
@ -16,14 +45,14 @@ fn build_dom(scope: dom::node_scope,
#debug["token=%?", token]; #debug["token=%?", token];
alt token { alt token {
parser::to_eof { break; } parser::to_eof { break; }
parser::to_start_tag("div") { parser::to_start_opening_tag("div") {
#debug["DIV"]; #debug["DIV"];
let new_node = scope.new_node( let new_node = scope.new_node(
dom::nk_div); dom::nk_div);
scope.add_child(cur, new_node); scope.add_child(cur, new_node);
cur = new_node; cur = new_node;
} }
parser::to_start_tag("img") { parser::to_start_opening_tag("img") {
#debug["IMG"]; #debug["IMG"];
let new_node = scope.new_node( let new_node = scope.new_node(
dom::nk_img({mut width: geom::px_to_au(100), dom::nk_img({mut width: geom::px_to_au(100),
@ -31,9 +60,16 @@ fn build_dom(scope: dom::node_scope,
scope.add_child(cur, new_node); scope.add_child(cur, new_node);
cur = new_node; cur = new_node;
} }
parser::to_start_tag(t) { parser::to_start_opening_tag(t) {
fail ("Unrecognized tag: " + t); fail ("Unrecognized tag: " + t);
} }
parser::to_attr(key, value) {
#debug["attr: %? = %?", key, value];
link_up_attribute(scope, cur, key, value);
}
parser::to_end_opening_tag {
#debug("end opening tag");
}
parser::to_end_tag(_) { parser::to_end_tag(_) {
// 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 // TODO: Fail more gracefully (i.e. according to the HTML5
@ -54,3 +90,4 @@ fn build_dom(scope: dom::node_scope,
} }
ret cur; ret cur;
} }