servo: Added width and height parsing

Source-Repo: https://github.com/servo/servo
Source-Revision: ed99449f52f5d927893408448a56f9c247f8e098
This commit is contained in:
Margaret Meyerhofer 2012-06-28 16:01:36 -07:00
Родитель d6080bd836
Коммит 97da506abd
14 изменённых файлов: 550 добавлений и 362 удалений

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

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

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

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

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

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

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

@ -39,9 +39,8 @@ 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,46 +48,48 @@ 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 {
BTree.add_child(self.parent_box, kid_box);
}
DisInline {
let anon_box = alt self.anon_box {
none {
//
// The anonymous box inherits the attributes of its parents for now, so
// that properties of intrinsic boxes are not spread to their parenting
// anonymous box.
//
// TODO: check what CSS actually specifies
//
alt kid.get_specified_style().display_type {
some(DisBlock) {
BTree.add_child(self.parent_box, kid_box);
}
some(DisInline) {
let anon_box = alt self.anon_box {
none {
//
// The anonymous box inherits the attributes of its parents for now, so
// that properties of intrinsic boxes are not spread to their parenting
// anonymous box.
//
// TODO: check what CSS actually specifies
//
let b = @Box(self.parent_node, InlineBox);
self.anon_box = some(b);
b
}
some(b) { b }
};
BTree.add_child(anon_box, kid_box);
}
DisNone {
// Nothing to do.
}
let b = @Box(self.parent_node, InlineBox);
self.anon_box = some(b);
b
}
some(b) { b }
};
BTree.add_child(anon_box, kid_box);
}
some(DisNone) {
// Nothing to do.
}
_ { //hack for now
}
}
}
}
#[doc="
Constructs boxes for the parent's children, when the parent's 'display'
attribute is 'inline'.
"]
Constructs boxes for the parent's children, when the parent's 'display'
attribute is 'inline'.
"]
fn construct_boxes_for_inline_children() {
for NTree.each_child(self.parent_node) |kid| {
@ -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();
@ -134,13 +139,13 @@ impl methods for ctxt {
}
#[doc="
Flushes the anonymous box we're creating if it exists. This appends the
anonymous box to the block.
Flushes the anonymous box we're creating if it exists. This appends the
anonymous box to the block.
"]
fn finish_anonymous_box_if_necessary() {
alt copy self.anon_box {
none { /* Nothing to do. */ }
some(b) { BTree.add_child(self.parent_box, b); }
none { /* Nothing to do. */ }
some(b) { BTree.add_child(self.parent_box, b); }
}
self.anon_box = none;
}
@ -152,21 +157,21 @@ trait box_builder_priv {
impl box_builder_priv of box_builder_priv for Node {
#[doc="
Determines the kind of box that this node needs. Also, for images, computes the intrinsic
size.
"]
Determines the kind of box that this node needs. Also, for images, computes the intrinsic
size.
"]
fn determine_box_kind() -> BoxKind {
alt self.read(|n| copy n.kind) {
~Text(string) {
TextBox(@text_box(copy string))
}
~Element(element) {
alt *element.kind {
HTMLDivElement { BlockBox }
HTMLImageElement({size}) { IntrinsicBox(@size) }
UnknownElement { InlineBox }
}
~Text(string) {
TextBox(@text_box(copy string))
}
~Element(element) {
alt *element.kind {
HTMLDivElement { BlockBox }
HTMLImageElement({size}) { IntrinsicBox(@size) }
UnknownElement { InlineBox }
}
}
}
}
}
@ -181,13 +186,13 @@ impl box_builder_methods of box_builder_methods for Node {
let box_kind = self.determine_box_kind();
let my_box = @Box(self, box_kind);
alt box_kind {
BlockBox | InlineBox {
let cx = create_context(self, my_box);
cx.construct_boxes_for_children();
}
_ {
// Nothing to do.
}
BlockBox | InlineBox {
let cx = create_context(self, my_box);
cx.construct_boxes_for_children();
}
_ {
// Nothing to do.
}
}
ret my_box;
}

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

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

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

@ -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(*) {

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

@ -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;
@ -70,20 +70,20 @@ impl priv_matching_methods of priv_matching_methods for Node {
Child(_, _) | Descendant(_, _) | Sibling(_, _) { ret false; }
Element(tag, attrs) {
alt self.read(|n| copy *n.kind) {
base::Element(elmt) {
if !(tag == ~"*" || tag == elmt.tag_name) {
ret false;
}
let mut i = 0u;
while i < attrs.len() {
if !attrs_match(attrs[i], elmt) { ret false; }
i += 1u;
}
ret true;
base::Element(elmt) {
if !(tag == ~"*" || tag == elmt.tag_name) {
ret false;
}
Text(str) { /*fall through, currently unsupported*/ }
let mut i = 0u;
while i < attrs.len() {
if !attrs_match(attrs[i], elmt) { ret false; }
i += 1u;
}
ret true;
}
Text(str) { /*fall through, currently unsupported*/ }
}
}
}
@ -113,16 +113,16 @@ impl priv_matching_methods of priv_matching_methods for Node {
//loop over all ancestors to check if they are the person
//we should be descended from.
let mut cur_parent = alt self.read(|n| n.tree.parent) {
some(parent) { parent }
none { ret false; }
some(parent) { parent }
none { ret false; }
};
loop {
if cur_parent.matches_selector(sel1) { ret true; }
cur_parent = alt cur_parent.read(|n| n.tree.parent) {
some(parent) { parent }
none { ret false; }
some(parent) { parent }
none { ret false; }
};
}
}
@ -131,18 +131,18 @@ impl priv_matching_methods of priv_matching_methods for Node {
// Loop over this node's previous siblings to see if they match.
alt self.read(|n| n.tree.prev_sibling) {
some(sib) {
let mut cur_sib = sib;
loop {
if cur_sib.matches_selector(sel1) { ret true; }
cur_sib = alt cur_sib.read(|n| n.tree.prev_sibling) {
some(sib) { sib }
none { break; }
};
}
some(sib) {
let mut cur_sib = sib;
loop {
if cur_sib.matches_selector(sel1) { ret true; }
cur_sib = alt cur_sib.read(|n| n.tree.prev_sibling) {
some(sib) { sib }
none { break; }
};
}
none { }
}
none { }
}
// check the rest of the siblings
@ -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);

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

@ -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) {
let r = rand::rng();
let rand_color = rgb(r.next() as u8, r.next() as u8, r.next() as u8);
trait default_style_methods {
fn default_color() -> Color;
fn default_display_type() -> DisplayType;
}
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} }
#[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();
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 { 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="

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

@ -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,153 +38,160 @@ impl methods of methods for TokenReader {
}
}
fn parse_element(reader : TokenReader) -> option<~style::Selector> {
// Get the current element type
let elmt_name = alt reader.get() {
Element(tag) { copy tag }
Eof { ret none; }
_ { fail ~"Expected an element" }
};
let mut attr_list = ~[];
// Get the attributes associated with that element
loop {
let tok = reader.get();
alt tok {
Attr(attr) { push(attr_list, copy attr); }
StartDescription | Descendant | Child | Sibling | Comma {
reader.unget(tok);
break;
}
Eof { ret none; }
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));
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>;
}
fn parse_rule(reader : TokenReader) -> option<~style::Rule> {
let mut sel_list = ~[];
let mut desc_list = ~[];
impl parser_methods of parser_methods for TokenReader {
fn parse_element() -> option<~style::Selector> {
// Get the current element type
let elmt_name = alt self.get() {
Element(tag) { copy tag }
Eof { ret none; }
_ { fail ~"Expected an element" }
};
// Collect all the selectors that this rule applies to
loop {
let mut cur_sel;
let mut attr_list = ~[];
alt parse_element(reader) {
some(elmt) { cur_sel = copy elmt; }
none { ret none; } // we hit an eof in the middle of a rule
}
// Get the attributes associated with that element
loop {
let tok = self.get();
alt tok {
Attr(attr) { push(attr_list, copy attr); }
StartDescription | Descendant | Child | Sibling | Comma {
self.unget(tok);
break;
}
Eof { ret none; }
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_selector() -> option<~[~Selector]> {
let mut sel_list = ~[];
// Collect all the selectors that this rule applies to
loop {
let tok = reader.get();
alt tok {
Descendant {
alt parse_element(reader) {
some(elmt) {
let built_sel <- cur_sel;
let new_sel = copy elmt;
cur_sel <- ~style::Descendant(built_sel, new_sel)
}
none { ret none; }
}
}
Child {
alt parse_element(reader) {
some(elmt) {
let built_sel <- cur_sel;
let new_sel = copy elmt;
cur_sel <- ~style::Child(built_sel, new_sel)
}
none { ret none; }
}
}
Sibling {
alt parse_element(reader) {
some(elmt) {
let built_sel <- cur_sel;
let new_sel = copy elmt;
cur_sel <- ~style::Sibling(built_sel, new_sel)
}
none { ret none; }
}
}
StartDescription {
let built_sel <- cur_sel;
push(sel_list, built_sel);
reader.unget(StartDescription);
break;
}
Comma {
let mut cur_sel;
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 = self.get();
let built_sel <- cur_sel;
push(sel_list, built_sel);
reader.unget(Comma);
break;
}
Attr(_) | EndDescription | Element(_) | Description(_, _) {
fail #fmt["Unexpected token %? in elements", tok];
}
Eof { ret none; }
}
}
// check if we should break out of the nesting loop as well
let tok = reader.get();
alt tok {
StartDescription { break; }
Comma { }
_ { reader.unget(tok); }
}
}
// Get the description to be applied to the selector
loop {
let tok = reader.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"; }
alt tok {
Descendant {
alt self.parse_element() {
some(elmt) {
let new_sel = copy elmt;
cur_sel <- ~style::Descendant(built_sel, new_sel)
}
none { ret none; }
}
}
Child {
alt self.parse_element() {
some(elmt) {
let new_sel = copy elmt;
cur_sel <- ~style::Child(built_sel, new_sel)
}
none { ret none; }
}
}
Sibling {
alt self.parse_element() {
some(elmt) {
let new_sel = copy elmt;
cur_sel <- ~style::Sibling(built_sel, new_sel)
}
none { ret none; }
}
}
StartDescription {
push(sel_list, built_sel);
self.unget(StartDescription);
break;
}
Comma {
push(sel_list, built_sel);
self.unget(Comma);
break;
}
Attr(_) | EndDescription | Element(_) | Description(_, _) {
fail #fmt["Unexpected token %? in elements", tok];
}
Eof { ret none; }
}
}
~"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)));
}
_ { #debug["Recieved unknown style property '%s'", val]; }
}
}
Eof { ret none; }
StartDescription | Descendant | Child | Sibling
| Comma | Element(_) | Attr(_) {
fail #fmt["Unexpected token %? in description", tok];
}
// check if we should break out of the nesting loop as well
// TODO: fix this when rust gets labelled loops
let tok = self.get();
alt tok {
StartDescription { break; }
Comma { }
_ { self.unget(tok); }
}
}
ret some(sel_list);
}
ret some(~(sel_list, desc_list));
fn parse_description() -> option<~[StyleDeclaration]> {
let mut desc_list : ~[StyleDeclaration]= ~[];
// Get the description to be applied to the selector
loop {
let tok = self.get();
alt tok {
EndDescription { break; }
Description(prop, val) {
alt prop {
// 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(_) {
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,9 +199,9 @@ fn build_stylesheet(stream : port<Token>) -> ~[~style::Rule] {
let reader = {stream : stream, mut lookahead : none};
loop {
alt parse_rule(reader) {
some(rule) { push(rule_list, copy rule); }
none { break; }
alt reader.parse_rule() {
some(rule) { push(rule_list, copy rule); }
none { break; }
}
}

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

@ -1,3 +1,5 @@
#[doc = "Code to lex and tokenize css files."]
import comm::{port, chan};
import dom::style;
import option::is_none;
@ -48,9 +50,9 @@ impl css_methods of css_methods for CssLexer {
}
let token = alt self.parser_state {
CssDescription { self.parse_css_description(ch) }
CssDescription { self.parse_css_description(ch) }
CssAttribute { self.parse_css_attribute(ch) }
CssElement { self.parse_css_element(ch) }
CssElement { self.parse_css_element(ch) }
CssRelation { self.parse_css_relation(ch) }
};
@ -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);

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

@ -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)]
@ -59,15 +60,15 @@ fn link_up_attribute(scope: NodeScope, node: Node, -key: ~str, -value: ~str) {
fn build_element_kind(tag_name: ~str) -> ~ElementKind {
alt tag_name {
~"div" { ~HTMLDivElement }
~"img" {
~HTMLImageElement({
mut size: Size2D(geometry::px_to_au(100),
geometry::px_to_au(100))
})
}
~"head" { ~HTMLHeadElement }
_ { ~UnknownElement }
~"div" { ~HTMLDivElement }
~"img" {
~HTMLImageElement({
mut size: Size2D(geometry::px_to_au(100),
geometry::px_to_au(100))
})
}
~"head" { ~HTMLHeadElement }
_ { ~UnknownElement }
}
}
@ -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);
}

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

@ -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,34 +68,26 @@ 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)); }
}
}
fn parse_ident() -> ~str {
let mut result: ~[u8] = ~[];
loop {
alt self.get() {
CoeChar(c) {
if (c.is_alpha()) {
push(result, c);
} else if result.len() == 0u {
self.parse_err(~"expected ident");
} else {
self.unget(c);
break;
}
}
CoeEof {
self.parse_err(~"expected ident");
CoeChar(c) {
if (c.is_alpha()) { push(result, c); }
else if result.len() == 0u { self.parse_err(~"expected ident"); }
else {
self.unget(c);
break;
}
}
CoeEof {
self.parse_err(~"expected ident");
}
}
}
ret str::from_bytes(result);
@ -110,15 +103,15 @@ impl util_methods of util_methods for InputState {
fn eat_whitespace() {
loop {
alt self.get() {
CoeChar(c) {
if !c.is_whitespace() {
self.unget(c);
ret;
}
}
CoeEof {
CoeChar(c) {
if !c.is_whitespace() {
self.unget(c);
ret;
}
}
CoeEof {
ret;
}
}
}
}

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

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

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

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