Added width and height parsing

This commit is contained in:
Margaret Meyerhofer 2012-06-28 16:01:36 -07:00
parent 0c5aefa88f
commit ed99449f52
14 changed files with 550 additions and 362 deletions

View file

@ -20,7 +20,6 @@ import dom::style;
import dom::style::Stylesheet;
import gfx::renderer::Sink;
import parser::html_lexer::spawn_html_lexer_task;
import parser::css_builder::build_stylesheet;
import parser::html_builder::build_dom;
import layout::layout_task;
import layout_task::{Layout, BuildMsg};

View file

@ -1,16 +1,37 @@
import util::color::Color;
enum DisplayType{
#[doc = "
Defines how css rules, both selectors and style specifications, are
stored. CSS selector-matching rules, as presented by
http://www.w3.org/TR/CSS2/selector.html are represented by nested, structural types,
"]
enum DisplayType {
DisBlock,
DisInline,
DisNone
}
enum StyleDeclaration{
FontSize(uint), // Currently assumes format '# pt'
enum Unit {
Auto,
Percent(float),
In(float),
Mm(float),
Cm(float),
Em(float),
Ex(float),
Pt(float),
Pc(float),
Px(float)
}
enum StyleDeclaration {
BackgroundColor(Color),
Display(DisplayType),
FontSize(Unit),
Height(Unit),
TextColor(Color),
BackgroundColor(Color)
Width(Unit)
}
enum Attr{

View file

@ -13,9 +13,9 @@ import image::base::image;
import layout::block::block_layout_methods;
import layout::inline::inline_layout_methods;
import util::tree;
import util::color::Color;
import util::color::{Color, css_colors};
import text::text_box;
import style::style::computed_style;
import style::style::SpecifiedStyle;
import text::text_layout_methods;
import vec::{push, push_all};
@ -28,11 +28,11 @@ enum BoxKind {
class Appearance {
let mut background_image: option<@image>;
let mut background_color: option<Color>;
let mut background_color: Color;
new() {
self.background_image = none;
self.background_color = none;
self.background_color = css_colors::black();
}
}
@ -53,7 +53,7 @@ class Box {
}
enum LayoutData = {
mut computed_style: ~computed_style,
mut specified_style: ~SpecifiedStyle,
mut box: option<@Box>
};

View file

@ -39,8 +39,7 @@ fn create_context(parent_node: Node, parent_box: @Box) -> ctxt {
impl methods for ctxt {
#[doc="
Constructs boxes for the parent's children, when the parent's 'display'
attribute is 'block'.
Constructs boxes for the parent's children, when the parent's 'display' attribute is 'block'.
"]
fn construct_boxes_for_block_children() {
for NTree.each_child(self.parent_node) |kid| {
@ -49,17 +48,17 @@ impl methods for ctxt {
let kid_box = kid.construct_boxes();
// Determine the child's display.
let disp = kid.get_computed_style().display;
if disp != DisInline {
let disp = kid.get_specified_style().display_type;
if disp != some(DisInline) {
self.finish_anonymous_box_if_necessary();
}
// Add the child's box to the current enclosing box or the current anonymous box.
alt kid.get_computed_style().display {
DisBlock {
alt kid.get_specified_style().display_type {
some(DisBlock) {
BTree.add_child(self.parent_box, kid_box);
}
DisInline {
some(DisInline) {
let anon_box = alt self.anon_box {
none {
//
@ -78,9 +77,11 @@ impl methods for ctxt {
};
BTree.add_child(anon_box, kid_box);
}
DisNone {
some(DisNone) {
// Nothing to do.
}
_ { //hack for now
}
}
}
}
@ -96,24 +97,26 @@ impl methods for ctxt {
let kid_box = kid.construct_boxes();
// Determine the child's display.
let disp = kid.get_computed_style().display;
if disp != DisInline {
let disp = kid.get_specified_style().display_type;
if disp != some(DisInline) {
// TODO
}
// Add the child's box to the current enclosing box.
alt kid.get_computed_style().display {
DisBlock {
alt kid.get_specified_style().display_type {
some(DisBlock) {
// TODO
#warn("TODO: non-inline display found inside inline box");
BTree.add_child(self.parent_box, kid_box);
}
DisInline {
some(DisInline) {
BTree.add_child(self.parent_box, kid_box);
}
DisNone {
some(DisNone) {
// Nothing to do.
}
_ { //hack for now
}
}
}
}
@ -123,10 +126,12 @@ impl methods for ctxt {
#debug("parent node:");
self.parent_node.dump();
alt self.parent_node.get_computed_style().display {
DisBlock { self.construct_boxes_for_block_children(); }
DisInline { self.construct_boxes_for_inline_children(); }
DisNone { /* Nothing to do. */ }
alt self.parent_node.get_specified_style().display_type {
some(DisBlock) { self.construct_boxes_for_block_children(); }
some(DisInline) { self.construct_boxes_for_inline_children(); }
some(DisNone) { /* Nothing to do. */ }
_ { //hack for now
}
}
self.finish_anonymous_box_if_necessary();

View file

@ -70,9 +70,10 @@ fn box_to_display_items(box: @Box, origin: Point2D<au>) -> ~[dl::display_item] {
#debug("request to display a box from origin %?", origin);
let bounds = Rect(origin, copy box.bounds.size);
let col = box.appearance.background_color;
alt (box.kind, box.appearance.background_image, box.appearance.background_color) {
(TextBox(subbox), _, _) {
alt (box.kind, box.appearance.background_image) {
(TextBox(subbox), _) {
let run = copy subbox.run;
assert run.is_some();
push(items, dl::display_item({
@ -84,28 +85,20 @@ fn box_to_display_items(box: @Box, origin: Point2D<au>) -> ~[dl::display_item] {
bounds: bounds
}));
}
(_, some(image), some(*)) | (_, some(image), none) {
(_, some(image)) {
push(items, dl::display_item({
item_type: dl::display_item_image(~copy *image),
bounds: bounds
}));
}
(_, none, some(col)) {
(_, none) {
#debug("Assigning color %? to box with bounds %?", col, bounds);
let col = box.appearance.background_color;
push(items, dl::display_item({
item_type: dl::display_item_solid_color(col.red, col.green, col.blue),
bounds: bounds
}));
}
(_, none, none) {
let r = rand::rng();
push(items, dl::display_item({
item_type: dl::display_item_solid_color(r.next() as u8,
r.next() as u8,
r.next() as u8),
bounds: bounds
}));
}
}
#debug("layout: display items: %?", items);

View file

@ -3,8 +3,8 @@
import dom::base::{Element, HTMLImageElement, Node};
import dom::rcu::ReaderMethods;
import image::base::load;
import base::{Box, BTree, NTree, LayoutData, BoxTreeReadMethods};
import style::style_methods;
import base::{Box, BTree, NTree, LayoutData, BoxTreeReadMethods, SpecifiedStyle};
import style::{default_style_methods, style_methods};
trait ApplyStyleBoxMethods {
fn apply_style_for_subtree();
@ -19,15 +19,24 @@ impl ApplyStyleBoxMethods of ApplyStyleBoxMethods for @Box {
}
}
#[doc="Applies CSS style."]
#[doc="Applies CSS style to a layout box.
Get the specified style and apply the existing traits to a
layout box. If a trait does not exist, calculate the default
value for the given type of element and use that instead.
"]
fn apply_style() {
// Right now, we only handle images.
self.node.read(|node| {
alt node.kind {
~Element(element) {
let style = self.node.get_computed_style();
let style = self.node.get_specified_style();
self.appearance.background_color = some(style.back_color);
self.appearance.background_color = alt style.background_color {
some(col) { col }
none { node.kind.default_color() }
};
alt element.kind {
~HTMLImageElement(*) {

View file

@ -5,9 +5,9 @@ import dom::base;
import base::{ElementData, Node, Text};
import dom::style::{Selector, StyleDeclaration, FontSize, Display, TextColor, BackgroundColor,
Stylesheet, Element, Child, Descendant, Sibling, Attr, Exact, Exists, Includes,
StartsWith};
StartsWith, Width, Height};
import dom::rcu::ReaderMethods;
import style::{computed_style, default_style_for_node_kind};
import style::{SpecifiedStyle};
export matching_methods;
@ -176,9 +176,12 @@ impl priv_style_methods of priv_style_methods for Node {
fn update_style(decl : StyleDeclaration) {
self.aux(|layout| {
alt decl {
Display(dis) { layout.computed_style.display = dis; }
BackgroundColor(col) { layout.computed_style.back_color = col; }
TextColor(*) | FontSize(*) { /* not supported yet */ }
BackgroundColor(col) { layout.specified_style.background_color = some(col); }
Display(dis) { layout.specified_style.display_type = some(dis); }
FontSize(size) { layout.specified_style.font_size = some(size); }
Height(size) { layout.specified_style.height = some(size); }
TextColor(col) { layout.specified_style.text_color = some(col); }
Width(size) { layout.specified_style.width = some(size); }
}
})
}
@ -208,7 +211,7 @@ impl matching_methods of matching_methods for Node {
}
}
self.aux(|a| #debug["Changed the style to: %?", copy *a.computed_style]);
self.aux(|a| #debug["Changed the style to: %?", copy *a.specified_style]);
}
}
@ -308,8 +311,7 @@ mod test {
scope.add_child(gchild, ggchild);
scope.add_child(ggchild, gggchild);
let sel1 = Descendant(~Element(~"*", ~[Exact(~"class", ~"blue")]),
~Element(~"*", ~[]));
let sel1 = Descendant(~Element(~"*", ~[Exact(~"class", ~"blue")]), ~Element(~"*", ~[]));
assert !root.matches_selector(~copy sel1);
assert child1.matches_selector(~copy sel1);
@ -338,8 +340,7 @@ mod test {
assert !ggchild.matches_selector(~copy sel3);
assert !gggchild.matches_selector(~sel3);
let sel4 = Descendant(~Child(~Element(~"*", ~[Exists(~"class")]),
~Element(~"*", ~[])),
let sel4 = Descendant(~Child(~Element(~"*", ~[Exists(~"class")]), ~Element(~"*", ~[])),
~Element(~"*", ~[]));
assert !root.matches_selector(~copy sel4);

View file

@ -2,7 +2,7 @@
import arc::{arc, get, clone};
import dom::style::{DisplayType, DisBlock, DisInline, DisNone, Stylesheet};
import dom::style::{DisplayType, DisBlock, DisInline, DisNone, Stylesheet, Unit};
import dom::base::{Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement, Node, NodeKind};
import dom::base::{Text};
import dom::rcu::ReaderMethods;
@ -11,26 +11,63 @@ import util::color::{Color, rgb};
import util::color::css_colors::{white, black};
import base::{LayoutData, NTree, NodeTreeReadMethods};
type computed_style = {mut display : DisplayType, mut back_color : Color};
type SpecifiedStyle = {mut background_color : option<Color>,
mut display_type : option<DisplayType>,
mut font_size : option<Unit>,
mut height : option<Unit>,
mut text_color : option<Color>,
mut width : option<Unit>
};
#[doc="Returns the default style for the given node kind."]
fn default_style_for_node_kind(kind: NodeKind) -> computed_style {
alt kind {
Text(*) {
{mut display: DisInline, mut back_color: white()}
}
Element(element) {
trait default_style_methods {
fn default_color() -> Color;
fn default_display_type() -> DisplayType;
}
#[doc="Default stylesfor various attributes in case they don't get initialized from css selectors"]
impl default_style_methods of default_style_methods for NodeKind {
fn default_color() -> Color {
alt self {
Text(*) { white() }
Element(*) {
let r = rand::rng();
let rand_color = rgb(r.next() as u8, r.next() as u8, r.next() as u8);
rgb(r.next() as u8, r.next() as u8, r.next() as u8)
}
}
}
fn default_display_type() -> DisplayType {
alt self {
Text(*) { DisInline }
Element(element) {
alt *element.kind {
HTMLDivElement { {mut display: DisBlock, mut back_color: rand_color} }
HTMLHeadElement { {mut display: DisNone, mut back_color: rand_color} }
HTMLImageElement(*) { {mut display: DisInline, mut back_color: rand_color} }
UnknownElement { {mut display: DisInline, mut back_color: rand_color} }
HTMLDivElement { DisBlock }
HTMLHeadElement { DisNone }
HTMLImageElement(*) { DisInline }
UnknownElement { DisInline }
}
}
}
}
}
#[doc="Create a specified style that can be used to initialize a node before selector matching.
Everything is initialized to none except the display style. The
default value of thee display style is computed so that it can be
used to short-circuit selector matching to avoid computing style
for children of display:none objects.
"]
fn empty_style_for_node_kind(kind: NodeKind) -> SpecifiedStyle {
let display_type = kind.default_display_type();
{mut background_color : none,
mut display_type : some(display_type),
mut font_size : none,
mut height : none,
mut text_color : none,
mut width : none}
}
trait style_priv {
@ -40,13 +77,17 @@ trait style_priv {
impl style_priv of style_priv for Node {
#[doc="Set a default auxilliary data so that other threads can modify it.
This is, importantly, the function that creates the layout data for the node (the reader-
auxiliary box in the RCU model) and populates it with the default style.
This is, importantly, the function that creates the layout
data for the node (the reader-auxiliary box in the RCU model)
and populates it with the default style.
"]
// TODO: we should look into folding this into building the dom,
// instead of doing a linear sweep afterwards.
fn initialize_style() {
let node_kind = self.read(|n| copy *n.kind);
let the_layout_data = @LayoutData({
mut computed_style : ~default_style_for_node_kind(node_kind),
mut specified_style : ~empty_style_for_node_kind(node_kind),
mut box : none
});
@ -56,7 +97,7 @@ impl style_priv of style_priv for Node {
trait style_methods {
fn initialize_style_for_subtree();
fn get_computed_style() -> computed_style;
fn get_specified_style() -> SpecifiedStyle;
fn recompute_style_for_subtree(styles : arc<Stylesheet>);
}
@ -76,11 +117,11 @@ impl style_methods of style_methods for Node {
TODO: Return a safe reference; don't copy.
"]
fn get_computed_style() -> computed_style {
fn get_specified_style() -> SpecifiedStyle {
if !self.has_aux() {
fail ~"get_computed_style() called on a node without a style!";
}
ret copy *self.aux(|x| copy x).computed_style;
ret copy *self.aux(|x| copy x).specified_style;
}
#[doc="

View file

@ -1,27 +1,30 @@
#[doc="Constructs a list of style rules from a token stream"]
#[doc="Constructs a list of css style rules from a token stream"]
// TODO: fail according to the css spec instead of failing when things
// are not as expected
import dom::style;
import style::{DisInline, DisBlock, DisNone, Display, TextColor, BackgroundColor, FontSize};
import style::{DisInline, DisBlock, DisNone, Display, TextColor, BackgroundColor, FontSize,
Height, Width, StyleDeclaration, Selector};
import parser::css_lexer::{Token, StartDescription, EndDescription,
Descendant, Child, Sibling,
Comma, Element, Attr, Description,
Eof};
import comm::recv;
import option::is_none;
import option::{map, is_none};
import vec::push;
import parser::parser_util::{parse_display_type, parse_font_size, parse_size};
import util::color::parsing::parse_color;
import vec::push;
type TokenReader = {stream : port<Token>, mut lookahead : option<Token>};
trait methods {
trait util_methods {
fn get() -> Token;
fn unget(-tok : Token);
}
impl methods of methods for TokenReader {
impl util_methods of util_methods for TokenReader {
fn get() -> Token {
alt copy self.lookahead {
some(tok) { self.lookahead = none; copy tok }
@ -35,9 +38,17 @@ impl methods of methods for TokenReader {
}
}
fn parse_element(reader : TokenReader) -> option<~style::Selector> {
trait parser_methods {
fn parse_element() -> option<~style::Selector>;
fn parse_selector() -> option<~[~Selector]>;
fn parse_description() -> option<~[StyleDeclaration]>;
fn parse_rule() -> option<~style::Rule>;
}
impl parser_methods of parser_methods for TokenReader {
fn parse_element() -> option<~style::Selector> {
// Get the current element type
let elmt_name = alt reader.get() {
let elmt_name = alt self.get() {
Element(tag) { copy tag }
Eof { ret none; }
_ { fail ~"Expected an element" }
@ -47,44 +58,42 @@ fn parse_element(reader : TokenReader) -> option<~style::Selector> {
// Get the attributes associated with that element
loop {
let tok = reader.get();
let tok = self.get();
alt tok {
Attr(attr) { push(attr_list, copy attr); }
StartDescription | Descendant | Child | Sibling | Comma {
reader.unget(tok);
self.unget(tok);
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"; }
}
}
ret some(~style::Element(elmt_name, attr_list));
}
}
fn parse_rule(reader : TokenReader) -> option<~style::Rule> {
fn parse_selector() -> option<~[~Selector]> {
let mut sel_list = ~[];
let mut desc_list = ~[];
// Collect all the selectors that this rule applies to
loop {
let mut cur_sel;
alt parse_element(reader) {
alt self.parse_element() {
some(elmt) { cur_sel = copy elmt; }
none { ret none; } // we hit an eof in the middle of a rule
}
loop {
let tok = reader.get();
let tok = self.get();
let built_sel <- cur_sel;
alt tok {
Descendant {
alt parse_element(reader) {
alt self.parse_element() {
some(elmt) {
let built_sel <- cur_sel;
let new_sel = copy elmt;
cur_sel <- ~style::Descendant(built_sel, new_sel)
}
@ -92,9 +101,8 @@ fn parse_rule(reader : TokenReader) -> option<~style::Rule> {
}
}
Child {
alt parse_element(reader) {
alt self.parse_element() {
some(elmt) {
let built_sel <- cur_sel;
let new_sel = copy elmt;
cur_sel <- ~style::Child(built_sel, new_sel)
}
@ -102,9 +110,8 @@ fn parse_rule(reader : TokenReader) -> option<~style::Rule> {
}
}
Sibling {
alt parse_element(reader) {
alt self.parse_element() {
some(elmt) {
let built_sel <- cur_sel;
let new_sel = copy elmt;
cur_sel <- ~style::Sibling(built_sel, new_sel)
}
@ -112,15 +119,13 @@ fn parse_rule(reader : TokenReader) -> option<~style::Rule> {
}
}
StartDescription {
let built_sel <- cur_sel;
push(sel_list, built_sel);
reader.unget(StartDescription);
self.unget(StartDescription);
break;
}
Comma {
let built_sel <- cur_sel;
push(sel_list, built_sel);
reader.unget(Comma);
self.unget(Comma);
break;
}
Attr(_) | EndDescription | Element(_) | Description(_, _) {
@ -131,57 +136,62 @@ fn parse_rule(reader : TokenReader) -> option<~style::Rule> {
}
// check if we should break out of the nesting loop as well
let tok = reader.get();
// TODO: fix this when rust gets labelled loops
let tok = self.get();
alt tok {
StartDescription { break; }
Comma { }
_ { reader.unget(tok); }
_ { self.unget(tok); }
}
}
ret some(sel_list);
}
fn parse_description() -> option<~[StyleDeclaration]> {
let mut desc_list : ~[StyleDeclaration]= ~[];
// Get the description to be applied to the selector
loop {
let tok = reader.get();
let tok = self.get();
alt tok {
EndDescription { break; }
Description(prop, val) {
alt prop {
~"font-size" {
// TODO, support more ways to declare a font size than # pt
assert val.ends_with(~"pt");
let num = val.substr(0u, val.len() - 2u);
alt uint::from_str(num) {
some(n) { push(desc_list, FontSize(n)); }
none { fail ~"Nonnumber provided as font size"; }
}
}
~"display" {
alt val {
~"inline" { push(desc_list, Display(DisInline)); }
~"block" { push(desc_list, Display(DisBlock)); }
~"none" { push(desc_list, Display(DisNone)); }
_ { #debug["Recieved unknown display value '%s'", val]; }
}
}
~"color" {
push(desc_list, TextColor(parse_color(val)));
}
~"background-color" {
push(desc_list, BackgroundColor(parse_color(val)));
}
// TODO: have color parsing return an option instead of a real value
~"background-color" { push(desc_list, BackgroundColor(parse_color(val))); }
~"color" { push(desc_list, TextColor(parse_color(val))); }
~"display" { parse_display_type(val).map(|res| push(desc_list, Display(res))); }
~"font-size" { parse_font_size(val).map(|res| push(desc_list, FontSize(res))); }
~"height" { parse_size(val).map(|res| push(desc_list, Height(res))); }
~"width" { parse_size(val).map(|res| push(desc_list, Width(res))); }
_ { #debug["Recieved unknown style property '%s'", val]; }
}
}
Eof { ret none; }
StartDescription | Descendant | Child | Sibling
| Comma | Element(_) | Attr(_) {
StartDescription | Descendant | Child | Sibling | Comma | Element(_) | Attr(_) {
fail #fmt["Unexpected token %? in description", tok];
}
}
}
ret some(desc_list);
}
fn parse_rule() -> option<~style::Rule> {
let sel_list = alt self.parse_selector() {
some(list){ copy list }
none { ret none; }
};
// Get the description to be applied to the selector
let desc_list = alt self.parse_description() {
some(list) { copy list }
none { ret none; }
};
ret some(~(sel_list, desc_list));
}
}
fn build_stylesheet(stream : port<Token>) -> ~[~style::Rule] {
@ -189,7 +199,7 @@ fn build_stylesheet(stream : port<Token>) -> ~[~style::Rule] {
let reader = {stream : stream, mut lookahead : none};
loop {
alt parse_rule(reader) {
alt reader.parse_rule() {
some(rule) { push(rule_list, copy rule); }
none { break; }
}

View file

@ -1,3 +1,5 @@
#[doc = "Code to lex and tokenize css files."]
import comm::{port, chan};
import dom::style;
import option::is_none;
@ -221,12 +223,36 @@ impl css_methods of css_methods for CssLexer {
}
fn parser(reader: io::reader, state : ParserState) -> CssLexer {
ret { input_state: {mut lookahead: none, reader: reader},
mut parser_state: state };
ret { input_state: {mut lookahead: none, reader: reader}, mut parser_state: state };
}
fn lex_css_from_bytes(-content : ~[u8], result_chan : chan<Token>) {
let reader = io::bytes_reader(content);
let lexer = parser(reader, CssElement);
loop {
let token = lexer.parse_css();
let should_break = (token == Eof);
result_chan.send(token);
if should_break {
break;
}
}
}
fn spawn_css_lexer_from_string(-content : ~str) -> port<Token> {
let result_port = port();
let result_chan = chan(result_port);
task::spawn(|| lex_css_from_bytes(str::bytes(content), result_chan));
ret result_port;
}
#[warn(no_non_implicitly_copyable_typarams)]
fn spawn_css_lexer_task(-filename: ~str) -> port<Token> {
fn spawn_css_lexer_from_file(-filename: ~str) -> port<Token> {
let result_port = port();
let result_chan = chan(result_port);
@ -235,22 +261,11 @@ fn spawn_css_lexer_task(-filename: ~str) -> port<Token> {
let file_try = io::read_whole_file(filename);
// Check if the given css file existed, if it does, parse it,
// otherwise just send an eof. This is a hack to allow
// guessing that if foo.html exists, foo.css is the
// corresponding stylesheet.
// otherwise just send an eof.
if file_try.is_ok() {
#debug["Lexing css sheet %s", copy filename];
let file_data = file_try.get();
let reader = io::bytes_reader(file_data);
let lexer = parser(reader, CssElement);
loop {
let token = lexer.parse_css();
let should_break = token == Eof;
result_chan.send(token);
if should_break { break; }
}
lex_css_from_bytes(file_data, result_chan);
} else {
#debug["Failed to open css sheet %s", copy filename];
result_chan.send(Eof);

View file

@ -11,11 +11,12 @@ import parser = parser::html_lexer;
import parser::Token;
import dom::style::Stylesheet;
import vec::{push, push_all_move, flat_map};
import dvec::extensions;
enum css_message {
file(~str),
exit
enum CSSMessage {
File(~str),
Exit
}
#[warn(no_non_implicitly_copyable_typarams)]
@ -85,25 +86,26 @@ spawned, collates them, and sends them to the given result channel.
* `from_parent` - A port on which to receive new links.
"]
fn css_link_listener(to_parent : chan<Stylesheet>, from_parent : port<css_message>) {
fn css_link_listener(to_parent : chan<Stylesheet>, from_parent : port<CSSMessage>) {
let mut result_vec = ~[];
loop {
alt from_parent.recv() {
file(filename) {
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 css_stream = css_lexer::spawn_css_lexer_from_file(filename);
let mut css_rules = css_builder::build_stylesheet(css_stream);
result_chan.send(css_rules);
});
push(result_vec, result_port);
}
exit {
Exit {
break;
}
}
@ -159,7 +161,7 @@ fn build_dom(scope: NodeScope, stream: port<Token>) -> (Node, port<Stylesheet>)
alt elmt.get_attr(~"href") {
some(filename) {
#debug["Linking to a css sheet named: %s", filename];
style_chan.send(file(copy filename));
style_chan.send(File(copy filename));
}
none { /* fall through*/ }
}
@ -189,7 +191,7 @@ fn build_dom(scope: NodeScope, stream: port<Token>) -> (Node, port<Stylesheet>)
}
}
style_chan.send(exit);
style_chan.send(Exit);
ret (cur_node, style_port);
}

View file

@ -1,3 +1,5 @@
#[doc = "A collection of functions that are useful for both css and html parsing."]
import option::is_none;
import str::from_bytes;
import vec::push;
@ -17,7 +19,6 @@ trait u8_methods {
fn is_alpha() -> bool;
}
impl u8_methods of u8_methods for u8 {
fn is_whitespace() -> bool {
ret self == ' ' as u8 || self == '\n' as u8 || self == '\t' as u8;
@ -67,14 +68,8 @@ impl util_methods of util_methods for InputState {
fn expect(ch: u8) {
alt self.get() {
CoeChar(c) {
if c != ch {
self.parse_err(#fmt("expected '%c'", ch as char));
}
}
CoeEof {
self.parse_err(#fmt("expected '%c' at eof", ch as char));
}
CoeChar(c) { if c != ch { self.parse_err(#fmt("expected '%c'", ch as char)); } }
CoeEof { self.parse_err(#fmt("expected '%c' at eof", ch as char)); }
}
}
@ -83,11 +78,9 @@ impl util_methods of util_methods for InputState {
loop {
alt self.get() {
CoeChar(c) {
if (c.is_alpha()) {
push(result, c);
} else if result.len() == 0u {
self.parse_err(~"expected ident");
} else {
if (c.is_alpha()) { push(result, c); }
else if result.len() == 0u { self.parse_err(~"expected ident"); }
else {
self.unget(c);
break;
}

View file

@ -0,0 +1,98 @@
#[doc = "Helper functions to parse values of specific attributes."]
import dom::style::*;
import str::{pop_char, from_chars};
import float::from_str;
import option::map;
export parse_font_size;
export parse_size;
export parse_display_type;
fn parse_unit(str : ~str) -> option<Unit> {
alt str {
s if s.ends_with(~"%") { from_str(str.substr(0, str.len() - 1)).map(|f| Percent(f)) }
s if s.ends_with(~"in") { from_str(str.substr(0, str.len() - 2)).map(|f| In(f)) }
s if s.ends_with(~"cm") { from_str(str.substr(0, str.len() - 2)).map(|f| Cm(f)) }
s if s.ends_with(~"mm") { from_str(str.substr(0, str.len() - 2)).map(|f| Mm(f)) }
s if s.ends_with(~"pt") { from_str(str.substr(0, str.len() - 2)).map(|f| Pt(f)) }
s if s.ends_with(~"pc") { from_str(str.substr(0, str.len() - 2)).map(|f| Pc(f)) }
s if s.ends_with(~"px") { from_str(str.substr(0, str.len() - 2)).map(|f| Px(f)) }
s if s.ends_with(~"em") { from_str(str.substr(0, str.len() - 2)).map(|f| Em(f)) }
s if s.ends_with(~"ex") { from_str(str.substr(0, str.len() - 2)).map(|f| Ex(f)) }
_ { none }
}
}
fn parse_font_size(str : ~str) -> option<Unit> {
// The default pixel size, not sure if this is accurate.
let default = 16.0;
alt str {
~"xx-small" { some(Px(0.6*default)) }
~"x-small" { some(Px(0.75*default)) }
~"small" { some(Px(8.0/9.0*default)) }
~"medium" { some(Px(default)) }
~"large" { some(Px(1.2*default)) }
~"x-large" { some(Px(1.5*default)) }
~"xx-large" { some(Px(2.0*default)) }
~"smaller" { some(Em(0.8)) }
~"larger" { some(Em(1.25)) }
~"inherit" { some(Em(1.0)) }
_ { parse_unit(str) }
}
}
// For width / height, and anything else with the same attribute values
fn parse_size(str : ~str) -> option<Unit> {
alt str {
~"auto" { some(Auto) }
~"inherit" { some(Em(1.0)) }
_ { parse_unit(str) }
}
}
fn parse_display_type(str : ~str) -> option<DisplayType> {
alt str {
~"inline" { some(DisInline) }
~"block" { some(DisBlock) }
~"none" { some(DisNone) }
_ { #debug["Recieved unknown display value '%s'", str]; none }
}
}
#[cfg(test)]
mod test {
import css_lexer::spawn_css_lexer_from_string;
import css_builder::build_stylesheet;
#[test]
fn should_match_font_sizes() {
let input = ~"* {font-size:12pt; font-size:inherit; font-size:2em; font-size:x-small}";
let token_port = spawn_css_lexer_from_string(input);
let actual_rule = build_stylesheet(token_port);
let expected_rule : Stylesheet = ~[~(~[~Element(~"*", ~[])],
~[FontSize(Pt(12.0)),
FontSize(Em(1.0)),
FontSize(Em(2.0)),
FontSize(Px(12.0))])];
assert actual_rule == expected_rule;
}
#[test]
fn should_match_width_height() {
let input = ~"* {width:20%; height:auto; width:20px; width:3in; height:70mm; height:3cm}";
let token_port = spawn_css_lexer_from_string(input);
let actual_rule = build_stylesheet(token_port);
let expected_rule : Stylesheet = ~[~(~[~Element(~"*", ~[])],
~[Width(Percent(20.0)),
Height(Auto),
Width(Px(20.0)),
Width(In(3.0)),
Height(Mm(70.0)),
Height(Cm(3.0))])];
assert actual_rule == expected_rule;
}
}

View file

@ -57,6 +57,7 @@ mod layout {
mod parser {
mod lexer_util;
mod parser_util;
mod css_lexer;
mod html_lexer;
mod html_builder;