зеркало из https://github.com/mozilla/gecko-dev.git
servo: Added width and height parsing
Source-Repo: https://github.com/servo/servo Source-Revision: ed99449f52f5d927893408448a56f9c247f8e098
This commit is contained in:
Родитель
d6080bd836
Коммит
97da506abd
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче