зеркало из 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 dom::style::Stylesheet;
|
||||||
import gfx::renderer::Sink;
|
import gfx::renderer::Sink;
|
||||||
import parser::html_lexer::spawn_html_lexer_task;
|
import parser::html_lexer::spawn_html_lexer_task;
|
||||||
import parser::css_builder::build_stylesheet;
|
|
||||||
import parser::html_builder::build_dom;
|
import parser::html_builder::build_dom;
|
||||||
import layout::layout_task;
|
import layout::layout_task;
|
||||||
import layout_task::{Layout, BuildMsg};
|
import layout_task::{Layout, BuildMsg};
|
||||||
|
|
|
@ -1,16 +1,37 @@
|
||||||
import util::color::Color;
|
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,
|
DisBlock,
|
||||||
DisInline,
|
DisInline,
|
||||||
DisNone
|
DisNone
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StyleDeclaration{
|
enum Unit {
|
||||||
FontSize(uint), // Currently assumes format '# pt'
|
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),
|
Display(DisplayType),
|
||||||
|
FontSize(Unit),
|
||||||
|
Height(Unit),
|
||||||
TextColor(Color),
|
TextColor(Color),
|
||||||
BackgroundColor(Color)
|
Width(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Attr{
|
enum Attr{
|
||||||
|
|
|
@ -13,9 +13,9 @@ import image::base::image;
|
||||||
import layout::block::block_layout_methods;
|
import layout::block::block_layout_methods;
|
||||||
import layout::inline::inline_layout_methods;
|
import layout::inline::inline_layout_methods;
|
||||||
import util::tree;
|
import util::tree;
|
||||||
import util::color::Color;
|
import util::color::{Color, css_colors};
|
||||||
import text::text_box;
|
import text::text_box;
|
||||||
import style::style::computed_style;
|
import style::style::SpecifiedStyle;
|
||||||
import text::text_layout_methods;
|
import text::text_layout_methods;
|
||||||
import vec::{push, push_all};
|
import vec::{push, push_all};
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ enum BoxKind {
|
||||||
|
|
||||||
class Appearance {
|
class Appearance {
|
||||||
let mut background_image: option<@image>;
|
let mut background_image: option<@image>;
|
||||||
let mut background_color: option<Color>;
|
let mut background_color: Color;
|
||||||
|
|
||||||
new() {
|
new() {
|
||||||
self.background_image = none;
|
self.background_image = none;
|
||||||
self.background_color = none;
|
self.background_color = css_colors::black();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class Box {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LayoutData = {
|
enum LayoutData = {
|
||||||
mut computed_style: ~computed_style,
|
mut specified_style: ~SpecifiedStyle,
|
||||||
mut box: option<@Box>
|
mut box: option<@Box>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,9 +39,8 @@ fn create_context(parent_node: Node, parent_box: @Box) -> ctxt {
|
||||||
|
|
||||||
impl methods for ctxt {
|
impl methods for ctxt {
|
||||||
#[doc="
|
#[doc="
|
||||||
Constructs boxes for the parent's children, when the parent's 'display'
|
Constructs boxes for the parent's children, when the parent's 'display' attribute is 'block'.
|
||||||
attribute is 'block'.
|
"]
|
||||||
"]
|
|
||||||
fn construct_boxes_for_block_children() {
|
fn construct_boxes_for_block_children() {
|
||||||
for NTree.each_child(self.parent_node) |kid| {
|
for NTree.each_child(self.parent_node) |kid| {
|
||||||
|
|
||||||
|
@ -49,46 +48,48 @@ impl methods for ctxt {
|
||||||
let kid_box = kid.construct_boxes();
|
let kid_box = kid.construct_boxes();
|
||||||
|
|
||||||
// Determine the child's display.
|
// Determine the child's display.
|
||||||
let disp = kid.get_computed_style().display;
|
let disp = kid.get_specified_style().display_type;
|
||||||
if disp != DisInline {
|
if disp != some(DisInline) {
|
||||||
self.finish_anonymous_box_if_necessary();
|
self.finish_anonymous_box_if_necessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the child's box to the current enclosing box or the current anonymous box.
|
// Add the child's box to the current enclosing box or the current anonymous box.
|
||||||
alt kid.get_computed_style().display {
|
alt kid.get_specified_style().display_type {
|
||||||
DisBlock {
|
some(DisBlock) {
|
||||||
BTree.add_child(self.parent_box, kid_box);
|
BTree.add_child(self.parent_box, kid_box);
|
||||||
}
|
}
|
||||||
DisInline {
|
some(DisInline) {
|
||||||
let anon_box = alt self.anon_box {
|
let anon_box = alt self.anon_box {
|
||||||
none {
|
none {
|
||||||
//
|
//
|
||||||
// The anonymous box inherits the attributes of its parents for now, so
|
// The anonymous box inherits the attributes of its parents for now, so
|
||||||
// that properties of intrinsic boxes are not spread to their parenting
|
// that properties of intrinsic boxes are not spread to their parenting
|
||||||
// anonymous box.
|
// anonymous box.
|
||||||
//
|
//
|
||||||
// TODO: check what CSS actually specifies
|
// TODO: check what CSS actually specifies
|
||||||
//
|
//
|
||||||
|
|
||||||
let b = @Box(self.parent_node, InlineBox);
|
let b = @Box(self.parent_node, InlineBox);
|
||||||
self.anon_box = some(b);
|
self.anon_box = some(b);
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
some(b) { b }
|
some(b) { b }
|
||||||
};
|
};
|
||||||
BTree.add_child(anon_box, kid_box);
|
BTree.add_child(anon_box, kid_box);
|
||||||
}
|
}
|
||||||
DisNone {
|
some(DisNone) {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
_ { //hack for now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc="
|
#[doc="
|
||||||
Constructs boxes for the parent's children, when the parent's 'display'
|
Constructs boxes for the parent's children, when the parent's 'display'
|
||||||
attribute is 'inline'.
|
attribute is 'inline'.
|
||||||
"]
|
"]
|
||||||
fn construct_boxes_for_inline_children() {
|
fn construct_boxes_for_inline_children() {
|
||||||
for NTree.each_child(self.parent_node) |kid| {
|
for NTree.each_child(self.parent_node) |kid| {
|
||||||
|
|
||||||
|
@ -96,24 +97,26 @@ impl methods for ctxt {
|
||||||
let kid_box = kid.construct_boxes();
|
let kid_box = kid.construct_boxes();
|
||||||
|
|
||||||
// Determine the child's display.
|
// Determine the child's display.
|
||||||
let disp = kid.get_computed_style().display;
|
let disp = kid.get_specified_style().display_type;
|
||||||
if disp != DisInline {
|
if disp != some(DisInline) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the child's box to the current enclosing box.
|
// Add the child's box to the current enclosing box.
|
||||||
alt kid.get_computed_style().display {
|
alt kid.get_specified_style().display_type {
|
||||||
DisBlock {
|
some(DisBlock) {
|
||||||
// TODO
|
// TODO
|
||||||
#warn("TODO: non-inline display found inside inline box");
|
#warn("TODO: non-inline display found inside inline box");
|
||||||
BTree.add_child(self.parent_box, kid_box);
|
BTree.add_child(self.parent_box, kid_box);
|
||||||
}
|
}
|
||||||
DisInline {
|
some(DisInline) {
|
||||||
BTree.add_child(self.parent_box, kid_box);
|
BTree.add_child(self.parent_box, kid_box);
|
||||||
}
|
}
|
||||||
DisNone {
|
some(DisNone) {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
_ { //hack for now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,10 +126,12 @@ impl methods for ctxt {
|
||||||
#debug("parent node:");
|
#debug("parent node:");
|
||||||
self.parent_node.dump();
|
self.parent_node.dump();
|
||||||
|
|
||||||
alt self.parent_node.get_computed_style().display {
|
alt self.parent_node.get_specified_style().display_type {
|
||||||
DisBlock { self.construct_boxes_for_block_children(); }
|
some(DisBlock) { self.construct_boxes_for_block_children(); }
|
||||||
DisInline { self.construct_boxes_for_inline_children(); }
|
some(DisInline) { self.construct_boxes_for_inline_children(); }
|
||||||
DisNone { /* Nothing to do. */ }
|
some(DisNone) { /* Nothing to do. */ }
|
||||||
|
_ { //hack for now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_anonymous_box_if_necessary();
|
self.finish_anonymous_box_if_necessary();
|
||||||
|
@ -134,13 +139,13 @@ impl methods for ctxt {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc="
|
#[doc="
|
||||||
Flushes the anonymous box we're creating if it exists. This appends the
|
Flushes the anonymous box we're creating if it exists. This appends the
|
||||||
anonymous box to the block.
|
anonymous box to the block.
|
||||||
"]
|
"]
|
||||||
fn finish_anonymous_box_if_necessary() {
|
fn finish_anonymous_box_if_necessary() {
|
||||||
alt copy self.anon_box {
|
alt copy self.anon_box {
|
||||||
none { /* Nothing to do. */ }
|
none { /* Nothing to do. */ }
|
||||||
some(b) { BTree.add_child(self.parent_box, b); }
|
some(b) { BTree.add_child(self.parent_box, b); }
|
||||||
}
|
}
|
||||||
self.anon_box = none;
|
self.anon_box = none;
|
||||||
}
|
}
|
||||||
|
@ -152,21 +157,21 @@ trait box_builder_priv {
|
||||||
|
|
||||||
impl box_builder_priv of box_builder_priv for Node {
|
impl box_builder_priv of box_builder_priv for Node {
|
||||||
#[doc="
|
#[doc="
|
||||||
Determines the kind of box that this node needs. Also, for images, computes the intrinsic
|
Determines the kind of box that this node needs. Also, for images, computes the intrinsic
|
||||||
size.
|
size.
|
||||||
"]
|
"]
|
||||||
fn determine_box_kind() -> BoxKind {
|
fn determine_box_kind() -> BoxKind {
|
||||||
alt self.read(|n| copy n.kind) {
|
alt self.read(|n| copy n.kind) {
|
||||||
~Text(string) {
|
~Text(string) {
|
||||||
TextBox(@text_box(copy string))
|
TextBox(@text_box(copy string))
|
||||||
}
|
}
|
||||||
~Element(element) {
|
~Element(element) {
|
||||||
alt *element.kind {
|
alt *element.kind {
|
||||||
HTMLDivElement { BlockBox }
|
HTMLDivElement { BlockBox }
|
||||||
HTMLImageElement({size}) { IntrinsicBox(@size) }
|
HTMLImageElement({size}) { IntrinsicBox(@size) }
|
||||||
UnknownElement { InlineBox }
|
UnknownElement { InlineBox }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,13 +186,13 @@ impl box_builder_methods of box_builder_methods for Node {
|
||||||
let box_kind = self.determine_box_kind();
|
let box_kind = self.determine_box_kind();
|
||||||
let my_box = @Box(self, box_kind);
|
let my_box = @Box(self, box_kind);
|
||||||
alt box_kind {
|
alt box_kind {
|
||||||
BlockBox | InlineBox {
|
BlockBox | InlineBox {
|
||||||
let cx = create_context(self, my_box);
|
let cx = create_context(self, my_box);
|
||||||
cx.construct_boxes_for_children();
|
cx.construct_boxes_for_children();
|
||||||
}
|
}
|
||||||
_ {
|
_ {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret my_box;
|
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);
|
#debug("request to display a box from origin %?", origin);
|
||||||
|
|
||||||
let bounds = Rect(origin, copy box.bounds.size);
|
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) {
|
alt (box.kind, box.appearance.background_image) {
|
||||||
(TextBox(subbox), _, _) {
|
(TextBox(subbox), _) {
|
||||||
let run = copy subbox.run;
|
let run = copy subbox.run;
|
||||||
assert run.is_some();
|
assert run.is_some();
|
||||||
push(items, dl::display_item({
|
push(items, dl::display_item({
|
||||||
|
@ -84,28 +85,20 @@ fn box_to_display_items(box: @Box, origin: Point2D<au>) -> ~[dl::display_item] {
|
||||||
bounds: bounds
|
bounds: bounds
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
(_, some(image), some(*)) | (_, some(image), none) {
|
(_, some(image)) {
|
||||||
push(items, dl::display_item({
|
push(items, dl::display_item({
|
||||||
item_type: dl::display_item_image(~copy *image),
|
item_type: dl::display_item_image(~copy *image),
|
||||||
bounds: bounds
|
bounds: bounds
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
(_, none, some(col)) {
|
(_, none) {
|
||||||
#debug("Assigning color %? to box with bounds %?", col, bounds);
|
#debug("Assigning color %? to box with bounds %?", col, bounds);
|
||||||
|
let col = box.appearance.background_color;
|
||||||
push(items, dl::display_item({
|
push(items, dl::display_item({
|
||||||
item_type: dl::display_item_solid_color(col.red, col.green, col.blue),
|
item_type: dl::display_item_solid_color(col.red, col.green, col.blue),
|
||||||
bounds: bounds
|
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);
|
#debug("layout: display items: %?", items);
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
import dom::base::{Element, HTMLImageElement, Node};
|
import dom::base::{Element, HTMLImageElement, Node};
|
||||||
import dom::rcu::ReaderMethods;
|
import dom::rcu::ReaderMethods;
|
||||||
import image::base::load;
|
import image::base::load;
|
||||||
import base::{Box, BTree, NTree, LayoutData, BoxTreeReadMethods};
|
import base::{Box, BTree, NTree, LayoutData, BoxTreeReadMethods, SpecifiedStyle};
|
||||||
import style::style_methods;
|
import style::{default_style_methods, style_methods};
|
||||||
|
|
||||||
trait ApplyStyleBoxMethods {
|
trait ApplyStyleBoxMethods {
|
||||||
fn apply_style_for_subtree();
|
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() {
|
fn apply_style() {
|
||||||
// Right now, we only handle images.
|
// Right now, we only handle images.
|
||||||
self.node.read(|node| {
|
self.node.read(|node| {
|
||||||
alt node.kind {
|
alt node.kind {
|
||||||
~Element(element) {
|
~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 {
|
alt element.kind {
|
||||||
~HTMLImageElement(*) {
|
~HTMLImageElement(*) {
|
||||||
|
|
|
@ -5,9 +5,9 @@ import dom::base;
|
||||||
import base::{ElementData, Node, Text};
|
import base::{ElementData, Node, Text};
|
||||||
import dom::style::{Selector, StyleDeclaration, FontSize, Display, TextColor, BackgroundColor,
|
import dom::style::{Selector, StyleDeclaration, FontSize, Display, TextColor, BackgroundColor,
|
||||||
Stylesheet, Element, Child, Descendant, Sibling, Attr, Exact, Exists, Includes,
|
Stylesheet, Element, Child, Descendant, Sibling, Attr, Exact, Exists, Includes,
|
||||||
StartsWith};
|
StartsWith, Width, Height};
|
||||||
import dom::rcu::ReaderMethods;
|
import dom::rcu::ReaderMethods;
|
||||||
import style::{computed_style, default_style_for_node_kind};
|
import style::{SpecifiedStyle};
|
||||||
|
|
||||||
export matching_methods;
|
export matching_methods;
|
||||||
|
|
||||||
|
@ -70,20 +70,20 @@ impl priv_matching_methods of priv_matching_methods for Node {
|
||||||
Child(_, _) | Descendant(_, _) | Sibling(_, _) { ret false; }
|
Child(_, _) | Descendant(_, _) | Sibling(_, _) { ret false; }
|
||||||
Element(tag, attrs) {
|
Element(tag, attrs) {
|
||||||
alt self.read(|n| copy *n.kind) {
|
alt self.read(|n| copy *n.kind) {
|
||||||
base::Element(elmt) {
|
base::Element(elmt) {
|
||||||
if !(tag == ~"*" || tag == elmt.tag_name) {
|
if !(tag == ~"*" || tag == elmt.tag_name) {
|
||||||
ret false;
|
ret false;
|
||||||
}
|
|
||||||
|
|
||||||
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*/ }
|
|
||||||
|
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
|
//loop over all ancestors to check if they are the person
|
||||||
//we should be descended from.
|
//we should be descended from.
|
||||||
let mut cur_parent = alt self.read(|n| n.tree.parent) {
|
let mut cur_parent = alt self.read(|n| n.tree.parent) {
|
||||||
some(parent) { parent }
|
some(parent) { parent }
|
||||||
none { ret false; }
|
none { ret false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if cur_parent.matches_selector(sel1) { ret true; }
|
if cur_parent.matches_selector(sel1) { ret true; }
|
||||||
|
|
||||||
cur_parent = alt cur_parent.read(|n| n.tree.parent) {
|
cur_parent = alt cur_parent.read(|n| n.tree.parent) {
|
||||||
some(parent) { parent }
|
some(parent) { parent }
|
||||||
none { ret false; }
|
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.
|
// Loop over this node's previous siblings to see if they match.
|
||||||
alt self.read(|n| n.tree.prev_sibling) {
|
alt self.read(|n| n.tree.prev_sibling) {
|
||||||
some(sib) {
|
some(sib) {
|
||||||
let mut cur_sib = sib;
|
let mut cur_sib = sib;
|
||||||
loop {
|
loop {
|
||||||
if cur_sib.matches_selector(sel1) { ret true; }
|
if cur_sib.matches_selector(sel1) { ret true; }
|
||||||
|
|
||||||
cur_sib = alt cur_sib.read(|n| n.tree.prev_sibling) {
|
cur_sib = alt cur_sib.read(|n| n.tree.prev_sibling) {
|
||||||
some(sib) { sib }
|
some(sib) { sib }
|
||||||
none { break; }
|
none { break; }
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
none { }
|
}
|
||||||
|
none { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the rest of the siblings
|
// 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) {
|
fn update_style(decl : StyleDeclaration) {
|
||||||
self.aux(|layout| {
|
self.aux(|layout| {
|
||||||
alt decl {
|
alt decl {
|
||||||
Display(dis) { layout.computed_style.display = dis; }
|
BackgroundColor(col) { layout.specified_style.background_color = some(col); }
|
||||||
BackgroundColor(col) { layout.computed_style.back_color = col; }
|
Display(dis) { layout.specified_style.display_type = some(dis); }
|
||||||
TextColor(*) | FontSize(*) { /* not supported yet */ }
|
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(gchild, ggchild);
|
||||||
scope.add_child(ggchild, gggchild);
|
scope.add_child(ggchild, gggchild);
|
||||||
|
|
||||||
let sel1 = Descendant(~Element(~"*", ~[Exact(~"class", ~"blue")]),
|
let sel1 = Descendant(~Element(~"*", ~[Exact(~"class", ~"blue")]), ~Element(~"*", ~[]));
|
||||||
~Element(~"*", ~[]));
|
|
||||||
|
|
||||||
assert !root.matches_selector(~copy sel1);
|
assert !root.matches_selector(~copy sel1);
|
||||||
assert child1.matches_selector(~copy sel1);
|
assert child1.matches_selector(~copy sel1);
|
||||||
|
@ -338,8 +340,7 @@ mod test {
|
||||||
assert !ggchild.matches_selector(~copy sel3);
|
assert !ggchild.matches_selector(~copy sel3);
|
||||||
assert !gggchild.matches_selector(~sel3);
|
assert !gggchild.matches_selector(~sel3);
|
||||||
|
|
||||||
let sel4 = Descendant(~Child(~Element(~"*", ~[Exists(~"class")]),
|
let sel4 = Descendant(~Child(~Element(~"*", ~[Exists(~"class")]), ~Element(~"*", ~[])),
|
||||||
~Element(~"*", ~[])),
|
|
||||||
~Element(~"*", ~[]));
|
~Element(~"*", ~[]));
|
||||||
|
|
||||||
assert !root.matches_selector(~copy sel4);
|
assert !root.matches_selector(~copy sel4);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import arc::{arc, get, clone};
|
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::{Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement, Node, NodeKind};
|
||||||
import dom::base::{Text};
|
import dom::base::{Text};
|
||||||
import dom::rcu::ReaderMethods;
|
import dom::rcu::ReaderMethods;
|
||||||
|
@ -11,26 +11,63 @@ import util::color::{Color, rgb};
|
||||||
import util::color::css_colors::{white, black};
|
import util::color::css_colors::{white, black};
|
||||||
import base::{LayoutData, NTree, NodeTreeReadMethods};
|
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."]
|
trait default_style_methods {
|
||||||
fn default_style_for_node_kind(kind: NodeKind) -> computed_style {
|
fn default_color() -> Color;
|
||||||
alt kind {
|
fn default_display_type() -> DisplayType;
|
||||||
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);
|
|
||||||
|
|
||||||
alt *element.kind {
|
#[doc="Default stylesfor various attributes in case they don't get initialized from css selectors"]
|
||||||
HTMLDivElement { {mut display: DisBlock, mut back_color: rand_color} }
|
impl default_style_methods of default_style_methods for NodeKind {
|
||||||
HTMLHeadElement { {mut display: DisNone, mut back_color: rand_color} }
|
fn default_color() -> Color {
|
||||||
HTMLImageElement(*) { {mut display: DisInline, mut back_color: rand_color} }
|
alt self {
|
||||||
UnknownElement { {mut display: DisInline, mut back_color: rand_color} }
|
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 {
|
trait style_priv {
|
||||||
|
@ -40,13 +77,17 @@ trait style_priv {
|
||||||
impl style_priv of style_priv for Node {
|
impl style_priv of style_priv for Node {
|
||||||
#[doc="Set a default auxilliary data so that other threads can modify it.
|
#[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-
|
This is, importantly, the function that creates the layout
|
||||||
auxiliary box in the RCU model) and populates it with the default style.
|
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() {
|
fn initialize_style() {
|
||||||
let node_kind = self.read(|n| copy *n.kind);
|
let node_kind = self.read(|n| copy *n.kind);
|
||||||
let the_layout_data = @LayoutData({
|
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
|
mut box : none
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -56,7 +97,7 @@ impl style_priv of style_priv for Node {
|
||||||
|
|
||||||
trait style_methods {
|
trait style_methods {
|
||||||
fn initialize_style_for_subtree();
|
fn initialize_style_for_subtree();
|
||||||
fn get_computed_style() -> computed_style;
|
fn get_specified_style() -> SpecifiedStyle;
|
||||||
fn recompute_style_for_subtree(styles : arc<Stylesheet>);
|
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.
|
TODO: Return a safe reference; don't copy.
|
||||||
"]
|
"]
|
||||||
fn get_computed_style() -> computed_style {
|
fn get_specified_style() -> SpecifiedStyle {
|
||||||
if !self.has_aux() {
|
if !self.has_aux() {
|
||||||
fail ~"get_computed_style() called on a node without a style!";
|
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="
|
#[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
|
// TODO: fail according to the css spec instead of failing when things
|
||||||
// are not as expected
|
// are not as expected
|
||||||
|
|
||||||
import dom::style;
|
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,
|
import parser::css_lexer::{Token, StartDescription, EndDescription,
|
||||||
Descendant, Child, Sibling,
|
Descendant, Child, Sibling,
|
||||||
Comma, Element, Attr, Description,
|
Comma, Element, Attr, Description,
|
||||||
Eof};
|
Eof};
|
||||||
import comm::recv;
|
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 util::color::parsing::parse_color;
|
||||||
import vec::push;
|
import vec::push;
|
||||||
|
|
||||||
type TokenReader = {stream : port<Token>, mut lookahead : option<Token>};
|
type TokenReader = {stream : port<Token>, mut lookahead : option<Token>};
|
||||||
|
|
||||||
trait methods {
|
trait util_methods {
|
||||||
fn get() -> Token;
|
fn get() -> Token;
|
||||||
fn unget(-tok : Token);
|
fn unget(-tok : Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl methods of methods for TokenReader {
|
impl util_methods of util_methods for TokenReader {
|
||||||
fn get() -> Token {
|
fn get() -> Token {
|
||||||
alt copy self.lookahead {
|
alt copy self.lookahead {
|
||||||
some(tok) { self.lookahead = none; copy tok }
|
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> {
|
trait parser_methods {
|
||||||
// Get the current element type
|
fn parse_element() -> option<~style::Selector>;
|
||||||
let elmt_name = alt reader.get() {
|
fn parse_selector() -> option<~[~Selector]>;
|
||||||
Element(tag) { copy tag }
|
fn parse_description() -> option<~[StyleDeclaration]>;
|
||||||
Eof { ret none; }
|
fn parse_rule() -> option<~style::Rule>;
|
||||||
_ { 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_rule(reader : TokenReader) -> option<~style::Rule> {
|
impl parser_methods of parser_methods for TokenReader {
|
||||||
let mut sel_list = ~[];
|
fn parse_element() -> option<~style::Selector> {
|
||||||
let mut desc_list = ~[];
|
// 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
|
let mut attr_list = ~[];
|
||||||
loop {
|
|
||||||
let mut cur_sel;
|
|
||||||
|
|
||||||
alt parse_element(reader) {
|
// Get the attributes associated with that element
|
||||||
some(elmt) { cur_sel = copy elmt; }
|
loop {
|
||||||
none { ret none; } // we hit an eof in the middle of a rule
|
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 {
|
loop {
|
||||||
let tok = reader.get();
|
let mut cur_sel;
|
||||||
alt tok {
|
|
||||||
Descendant {
|
alt self.parse_element() {
|
||||||
alt parse_element(reader) {
|
some(elmt) { cur_sel = copy elmt; }
|
||||||
some(elmt) {
|
none { ret none; } // we hit an eof in the middle of a rule
|
||||||
let built_sel <- cur_sel;
|
}
|
||||||
let new_sel = copy elmt;
|
|
||||||
cur_sel <- ~style::Descendant(built_sel, new_sel)
|
loop {
|
||||||
}
|
let tok = self.get();
|
||||||
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 built_sel <- cur_sel;
|
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) {
|
alt tok {
|
||||||
some(n) { push(desc_list, FontSize(n)); }
|
Descendant {
|
||||||
none { fail ~"Nonnumber provided as font size"; }
|
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; }
|
// check if we should break out of the nesting loop as well
|
||||||
StartDescription | Descendant | Child | Sibling
|
// TODO: fix this when rust gets labelled loops
|
||||||
| Comma | Element(_) | Attr(_) {
|
let tok = self.get();
|
||||||
fail #fmt["Unexpected token %? in description", tok];
|
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] {
|
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};
|
let reader = {stream : stream, mut lookahead : none};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
alt parse_rule(reader) {
|
alt reader.parse_rule() {
|
||||||
some(rule) { push(rule_list, copy rule); }
|
some(rule) { push(rule_list, copy rule); }
|
||||||
none { break; }
|
none { break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#[doc = "Code to lex and tokenize css files."]
|
||||||
|
|
||||||
import comm::{port, chan};
|
import comm::{port, chan};
|
||||||
import dom::style;
|
import dom::style;
|
||||||
import option::is_none;
|
import option::is_none;
|
||||||
|
@ -48,9 +50,9 @@ impl css_methods of css_methods for CssLexer {
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = alt self.parser_state {
|
let token = alt self.parser_state {
|
||||||
CssDescription { self.parse_css_description(ch) }
|
CssDescription { self.parse_css_description(ch) }
|
||||||
CssAttribute { self.parse_css_attribute(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) }
|
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 {
|
fn parser(reader: io::reader, state : ParserState) -> CssLexer {
|
||||||
ret { input_state: {mut lookahead: none, reader: reader},
|
ret { input_state: {mut lookahead: none, reader: reader}, mut parser_state: state };
|
||||||
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)]
|
#[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_port = port();
|
||||||
let result_chan = chan(result_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);
|
let file_try = io::read_whole_file(filename);
|
||||||
|
|
||||||
// Check if the given css file existed, if it does, parse it,
|
// Check if the given css file existed, if it does, parse it,
|
||||||
// otherwise just send an eof. This is a hack to allow
|
// otherwise just send an eof.
|
||||||
// guessing that if foo.html exists, foo.css is the
|
|
||||||
// corresponding stylesheet.
|
|
||||||
if file_try.is_ok() {
|
if file_try.is_ok() {
|
||||||
#debug["Lexing css sheet %s", copy filename];
|
#debug["Lexing css sheet %s", copy filename];
|
||||||
let file_data = file_try.get();
|
let file_data = file_try.get();
|
||||||
let reader = io::bytes_reader(file_data);
|
lex_css_from_bytes(file_data, result_chan);
|
||||||
|
|
||||||
let lexer = parser(reader, CssElement);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let token = lexer.parse_css();
|
|
||||||
let should_break = token == Eof;
|
|
||||||
result_chan.send(token);
|
|
||||||
if should_break { break; }
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
#debug["Failed to open css sheet %s", copy filename];
|
#debug["Failed to open css sheet %s", copy filename];
|
||||||
result_chan.send(Eof);
|
result_chan.send(Eof);
|
||||||
|
|
|
@ -11,11 +11,12 @@ import parser = parser::html_lexer;
|
||||||
import parser::Token;
|
import parser::Token;
|
||||||
import dom::style::Stylesheet;
|
import dom::style::Stylesheet;
|
||||||
import vec::{push, push_all_move, flat_map};
|
import vec::{push, push_all_move, flat_map};
|
||||||
|
|
||||||
import dvec::extensions;
|
import dvec::extensions;
|
||||||
|
|
||||||
enum css_message {
|
enum CSSMessage {
|
||||||
file(~str),
|
File(~str),
|
||||||
exit
|
Exit
|
||||||
}
|
}
|
||||||
|
|
||||||
#[warn(no_non_implicitly_copyable_typarams)]
|
#[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 {
|
fn build_element_kind(tag_name: ~str) -> ~ElementKind {
|
||||||
alt tag_name {
|
alt tag_name {
|
||||||
~"div" { ~HTMLDivElement }
|
~"div" { ~HTMLDivElement }
|
||||||
~"img" {
|
~"img" {
|
||||||
~HTMLImageElement({
|
~HTMLImageElement({
|
||||||
mut size: Size2D(geometry::px_to_au(100),
|
mut size: Size2D(geometry::px_to_au(100),
|
||||||
geometry::px_to_au(100))
|
geometry::px_to_au(100))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
~"head" { ~HTMLHeadElement }
|
~"head" { ~HTMLHeadElement }
|
||||||
_ { ~UnknownElement }
|
_ { ~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.
|
* `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 = ~[];
|
let mut result_vec = ~[];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
alt from_parent.recv() {
|
alt from_parent.recv() {
|
||||||
file(filename) {
|
File(filename) {
|
||||||
let result_port = comm::port();
|
let result_port = comm::port();
|
||||||
let result_chan = comm::chan(result_port);
|
let result_chan = comm::chan(result_port);
|
||||||
let filename = copy filename;
|
let filename = copy filename;
|
||||||
task::spawn(|| {
|
task::spawn(|| {
|
||||||
//TODO: deal with extraneous copies
|
//TODO: deal with extraneous copies
|
||||||
let filename <- copy filename;
|
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);
|
let mut css_rules = css_builder::build_stylesheet(css_stream);
|
||||||
result_chan.send(css_rules);
|
result_chan.send(css_rules);
|
||||||
});
|
});
|
||||||
|
|
||||||
push(result_vec, result_port);
|
push(result_vec, result_port);
|
||||||
}
|
}
|
||||||
exit {
|
Exit {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +161,7 @@ fn build_dom(scope: NodeScope, stream: port<Token>) -> (Node, port<Stylesheet>)
|
||||||
alt elmt.get_attr(~"href") {
|
alt elmt.get_attr(~"href") {
|
||||||
some(filename) {
|
some(filename) {
|
||||||
#debug["Linking to a css sheet named: %s", 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*/ }
|
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);
|
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 option::is_none;
|
||||||
import str::from_bytes;
|
import str::from_bytes;
|
||||||
import vec::push;
|
import vec::push;
|
||||||
|
@ -17,7 +19,6 @@ trait u8_methods {
|
||||||
fn is_alpha() -> bool;
|
fn is_alpha() -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl u8_methods of u8_methods for u8 {
|
impl u8_methods of u8_methods for u8 {
|
||||||
fn is_whitespace() -> bool {
|
fn is_whitespace() -> bool {
|
||||||
ret self == ' ' as u8 || self == '\n' as u8 || self == '\t' as u8;
|
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) {
|
fn expect(ch: u8) {
|
||||||
alt self.get() {
|
alt self.get() {
|
||||||
CoeChar(c) {
|
CoeChar(c) { if c != ch { self.parse_err(#fmt("expected '%c'", ch as char)); } }
|
||||||
if c != ch {
|
CoeEof { self.parse_err(#fmt("expected '%c' at eof", ch as char)); }
|
||||||
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 {
|
fn parse_ident() -> ~str {
|
||||||
let mut result: ~[u8] = ~[];
|
let mut result: ~[u8] = ~[];
|
||||||
loop {
|
loop {
|
||||||
alt self.get() {
|
alt self.get() {
|
||||||
CoeChar(c) {
|
CoeChar(c) {
|
||||||
if (c.is_alpha()) {
|
if (c.is_alpha()) { push(result, c); }
|
||||||
push(result, c);
|
else if result.len() == 0u { self.parse_err(~"expected ident"); }
|
||||||
} else if result.len() == 0u {
|
else {
|
||||||
self.parse_err(~"expected ident");
|
self.unget(c);
|
||||||
} else {
|
break;
|
||||||
self.unget(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CoeEof {
|
|
||||||
self.parse_err(~"expected ident");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
CoeEof {
|
||||||
|
self.parse_err(~"expected ident");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret str::from_bytes(result);
|
ret str::from_bytes(result);
|
||||||
|
@ -110,15 +103,15 @@ impl util_methods of util_methods for InputState {
|
||||||
fn eat_whitespace() {
|
fn eat_whitespace() {
|
||||||
loop {
|
loop {
|
||||||
alt self.get() {
|
alt self.get() {
|
||||||
CoeChar(c) {
|
CoeChar(c) {
|
||||||
if !c.is_whitespace() {
|
if !c.is_whitespace() {
|
||||||
self.unget(c);
|
self.unget(c);
|
||||||
ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CoeEof {
|
|
||||||
ret;
|
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 parser {
|
||||||
mod lexer_util;
|
mod lexer_util;
|
||||||
|
mod parser_util;
|
||||||
mod css_lexer;
|
mod css_lexer;
|
||||||
mod html_lexer;
|
mod html_lexer;
|
||||||
mod html_builder;
|
mod html_builder;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче