diff --git a/src/servo/css/apply.rs b/src/servo/css/apply.rs deleted file mode 100644 index dbd6f8b70d7..00000000000 --- a/src/servo/css/apply.rs +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Applies the appropriate CSS style to nodes. -*/ - -use au = gfx::geometry; -use css::styles::SpecifiedStyle; -use dom::node::{Node, NodeTree}; -use dom::element::*; -use layout::context::LayoutContext; -use image::ImageHolder; -use resource::image_cache_task::ImageCacheTask; -use std::net::url::Url; - -use newcss::values::*; - -trait ResolveMethods { - pure fn initial() -> T; -} - -impl CSSValue : ResolveMethods { - pure fn initial() -> CSSBackgroundColor { return BgColorTransparent; } -} - -impl CSSValue : ResolveMethods { - pure fn initial() -> CSSDisplay { return DisplayInline; } -} - -impl CSSValue : ResolveMethods { - pure fn initial() -> BoxSizing { return BoxAuto; } -} - -impl CSSValue : ResolveMethods { - pure fn initial() -> CSSFontSize { return AbsoluteSize(Medium); } -} - - -struct StyleApplicator { - node: Node, -} - -// TODO: normalize this into a normal preorder tree traversal function -pub fn apply_style(layout_ctx: &LayoutContext, node: Node) { - let applicator = StyleApplicator { - node: node, - }; - - applicator.apply_css_style(layout_ctx); -} - -// TODO: this is misleadingly-named. It is actually trying to resolve CSS 'inherit' values. - -/** A wrapper around a set of functions that can be applied as a - * top-down traversal of layout boxes. - */ -fn inheritance_wrapper(layout_ctx: &LayoutContext, node : Node) { - let applicator = StyleApplicator { - node: node, - }; - applicator.resolve_style(layout_ctx); -} - -impl StyleApplicator { - fn apply_css_style(layout_ctx: &LayoutContext) { - - for NodeTree.each_child(&self.node) |child| { - inheritance_wrapper(layout_ctx, *child) - } - } - - /** - * Convert the cascaded, specified style for this node into a resolved style: - * one which additionally resolves the values of Initial, Inherit based on - * defaults and node parent style. It also converts Node attributes into - * equivalent inline style declarations (TODO: where is this defined??) - */ - fn resolve_style(_layout_ctx: &LayoutContext) { - // TODO: implement - } -} diff --git a/src/servo/css/matching.rs b/src/servo/css/matching.rs deleted file mode 100644 index a8d2177111b..00000000000 --- a/src/servo/css/matching.rs +++ /dev/null @@ -1,329 +0,0 @@ -/** - Performs CSS selector matching. -*/ - -use dom::node::{LayoutData, Node, Text}; -use dom::element::ElementData; - -use newcss::values::*; -use newcss::{SelectCtx, SelectResults}; -use styles::{SpecifiedStyle}; -use select_handler::NodeSelectHandler; -use node_util::NodeUtil; - -/** - Check if a CSS attribute matches the attribute of an HTML element. -*/ -fn attrs_match(attr: &Attr, elmt: &ElementData) -> bool { - match *attr { - Exists(name) => { - match elmt.get_attr(name) { - Some(_) => true, - None => false - } - } - Exact(name, val) => { - match elmt.get_attr(name) { - Some(value) => value == val, - None => false - } - } - Includes(name, val) => { - // Comply with css spec, if the specified attribute is empty - // it cannot match. - if val == ~"" { return false; } - - match elmt.get_attr(name) { - Some(value) => value.split_char(' ').contains(&val), - None => false - } - } - StartsWith(name, val) => { - match elmt.get_attr(name) { - Some(value) => { - //check that there is only one attribute value and it - //starts with the perscribed value - if !value.starts_with(val) || value.contains(~" ") { return false; } - - // We match on either the exact value or value-foo - if value.len() == val.len() { true } - else { value.starts_with(val + ~"-") } - } - None => { - false - } - } - } - } -} - -trait PrivMatchingMethods { - fn matches_element(sel: &Selector) -> bool; - fn matches_selector(sel: &Selector) -> bool; -} - -impl Node : PrivMatchingMethods { - - /** - Checks if the given CSS selector, which must describe a single - element with no relational information, describes the given HTML - element. - */ - fn matches_element(sel: &Selector) -> bool { - match *sel { - Child(_, _) | Descendant(_, _) | Sibling(_, _) => { return false; } - Element(tag, attrs) => { - match self.read(|n| copy *n.kind) { - dom::node::Element(elmt) => { - if !(tag == ~"*" || tag == elmt.tag_name) { - return false; - } - - let mut i = 0u; - while i < attrs.len() { - if !attrs_match(&attrs[i], &elmt) { return false; } - i += 1u; - } - - return true; - } - _ => { /*fall through, currently unsupported*/ } - } - } - } - - return false; //If we got this far it was because something was - //unsupported. - } - - /** - Checks if a generic CSS selector matches a given HTML element - */ - fn matches_selector(sel : &Selector) -> bool { - match *sel { - Element(*) => { return self.matches_element(sel); } - Child(sel1, sel2) => { - return match self.read(|n| n.tree.parent) { - Some(parent) => self.matches_element(sel2) && parent.matches_selector(sel1), - None => false - } - } - Descendant(sel1, sel2) => { - if !self.matches_element(sel2) { return false; } - - //loop over all ancestors to check if they are the person - //we should be descended from. - let mut cur_parent = match self.read(|n| n.tree.parent) { - Some(parent) => parent, - None => return false - }; - - loop { - if cur_parent.matches_selector(sel1) { return true; } - - cur_parent = match cur_parent.read(|n| n.tree.parent) { - Some(parent) => parent, - None => return false - }; - } - } - Sibling(sel1, sel2) => { - if !self.matches_element(sel2) { return false; } - - // Loop over this node's previous siblings to see if they match. - match self.read(|n| n.tree.prev_sibling) { - Some(sib) => { - let mut cur_sib = sib; - loop { - if cur_sib.matches_selector(sel1) { return true; } - - cur_sib = match cur_sib.read(|n| n.tree.prev_sibling) { - Some(sib) => sib, - None => { break; } - }; - } - } - None => { } - } - - // check the rest of the siblings - match self.read(|n| n.tree.next_sibling) { - Some(sib) => { - let mut cur_sib = sib; - loop { - if cur_sib.matches_selector(sel1) { return true; } - - cur_sib = match cur_sib.read(|n| n.tree.next_sibling) { - Some(sib) => sib, - None => { break; } - }; - } - } - None => { } - } - - return false; - } - } - } -} - -trait MatchingMethods { - fn match_css_style(select_ctx : &SelectCtx); -} - -impl Node : MatchingMethods { - /** - Compare an html element to a list of css rules and update its - style according to the rules matching it. - */ - fn match_css_style(select_ctx : &SelectCtx) { - // Loop over each rule, see if our node matches what is - // described in the rule. If it matches, update its style. As - // we don't currently have priorities of style information, - // the latest rule takes precedence over the others. So we - // just overwrite style information as we go. - - let select_handler = NodeSelectHandler { - node: self - }; - let style = select_ctx.select_style(&self, &select_handler); - self.set_style(move style); - } -} - -#[cfg(test)] -mod test { - use dom::element::{Attr, HTMLDivElement, HTMLHeadElement, HTMLImageElement, UnknownElement}; - use dom::node::NodeScope; - use dvec::DVec; - - #[allow(non_implicitly_copyable_typarams)] - fn new_node_from_attr(scope: &NodeScope, name: ~str, val: ~str) -> Node { - let elmt = ElementData(~"div", ~HTMLDivElement); - let attr = ~Attr(move name, move val); - elmt.attrs.push(move attr); - return scope.new_node(dom::node::Element(move elmt)); - } - - #[test] - fn test_match_pipe1() { - let scope = NodeScope(); - let node = new_node_from_attr(&scope, ~"lang", ~"en-us"); - - let sel = Element(~"*", ~[StartsWith(~"lang", ~"en")]); - - assert node.matches_selector(~move sel); - } - - #[test] - fn test_match_pipe2() { - let scope = NodeScope(); - let node = new_node_from_attr(&scope, ~"lang", ~"en"); - - let sel = Element(~"*", ~[StartsWith(~"lang", ~"en")]); - - assert node.matches_selector(~move sel); - } - - #[test] - fn test_not_match_pipe() { - let scope = NodeScope(); - let node = new_node_from_attr(&scope, ~"lang", ~"english"); - - let sel = Element(~"*", ~[StartsWith(~"lang", ~"en")]); - - assert !node.matches_selector(~move sel); - } - - #[test] - fn test_match_includes() { - let scope = NodeScope(); - let node = new_node_from_attr(&scope, ~"mad", ~"hatter cobler cooper"); - - let sel = Element(~"div", ~[Includes(~"mad", ~"hatter")]); - - assert node.matches_selector(~move sel); - } - - #[test] - fn test_match_exists() { - let scope = NodeScope(); - let node = new_node_from_attr(&scope, ~"mad", ~"hatter cobler cooper"); - - let sel1 = Element(~"div", ~[Exists(~"mad")]); - let sel2 = Element(~"div", ~[Exists(~"hatter")]); - - assert node.matches_selector(~move sel1); - assert !node.matches_selector(~move sel2); - } - - #[test] - fn test_match_exact() { - let scope = NodeScope(); - let node1 = new_node_from_attr(&scope, ~"mad", ~"hatter cobler cooper"); - let node2 = new_node_from_attr(&scope, ~"mad", ~"hatter"); - - let sel = Element(~"div", ~[Exact(~"mad", ~"hatter")]); - - assert !node1.matches_selector(~copy sel); - assert node2.matches_selector(~move sel); - } - - #[test] - fn match_tree() { - let scope = NodeScope(); - - let root = new_node_from_attr(&scope, ~"class", ~"blue"); - let child1 = new_node_from_attr(&scope, ~"id", ~"green"); - let child2 = new_node_from_attr(&scope, ~"flag", ~"black"); - let gchild = new_node_from_attr(&scope, ~"flag", ~"grey"); - let ggchild = new_node_from_attr(&scope, ~"flag", ~"white"); - let gggchild = new_node_from_attr(&scope, ~"flag", ~"purple"); - - scope.add_child(root, child1); - scope.add_child(root, child2); - scope.add_child(child2, gchild); - scope.add_child(gchild, ggchild); - scope.add_child(ggchild, gggchild); - - let sel1 = Descendant(~Element(~"*", ~[Exact(~"class", ~"blue")]), ~Element(~"*", ~[])); - - assert !root.matches_selector(~copy sel1); - assert child1.matches_selector(~copy sel1); - assert child2.matches_selector(~copy sel1); - assert gchild.matches_selector(~copy sel1); - assert ggchild.matches_selector(~copy sel1); - assert gggchild.matches_selector(~move sel1); - - let sel2 = Descendant(~Child(~Element(~"*", ~[Exact(~"class", ~"blue")]), - ~Element(~"*", ~[])), - ~Element(~"div", ~[Exists(~"flag")])); - - assert !root.matches_selector(~copy sel2); - assert !child1.matches_selector(~copy sel2); - assert !child2.matches_selector(~copy sel2); - assert gchild.matches_selector(~copy sel2); - assert ggchild.matches_selector(~copy sel2); - assert gggchild.matches_selector(~move sel2); - - let sel3 = Sibling(~Element(~"*", ~[]), ~Element(~"*", ~[])); - - assert !root.matches_selector(~copy sel3); - assert child1.matches_selector(~copy sel3); - assert child2.matches_selector(~copy sel3); - assert !gchild.matches_selector(~copy sel3); - assert !ggchild.matches_selector(~copy sel3); - assert !gggchild.matches_selector(~move sel3); - - let sel4 = Descendant(~Child(~Element(~"*", ~[Exists(~"class")]), ~Element(~"*", ~[])), - ~Element(~"*", ~[])); - - assert !root.matches_selector(~copy sel4); - assert !child1.matches_selector(~copy sel4); - assert !child2.matches_selector(~copy sel4); - assert gchild.matches_selector(~copy sel4); - assert ggchild.matches_selector(~copy sel4); - assert gggchild.matches_selector(~move sel4); - } -} diff --git a/src/servo/css/styles.rs b/src/servo/css/styles.rs index 1aa85eb9d72..6c6b7e8d5dc 100644 --- a/src/servo/css/styles.rs +++ b/src/servo/css/styles.rs @@ -2,110 +2,19 @@ * High-level interface to CSS selector matching. */ use std::arc::{ARC, get, clone}; - +use dom::node::{Node, LayoutData, NodeTree}; use core::dvec::DVec; use newcss::values::*; use newcss::{SelectCtx, SelectResults}; -use dom::element::{HTMLDivElement, HTMLHeadElement, HTMLImageElement, UnknownElement, HTMLScriptElement}; -use dom::node::{Comment, Doctype, Element, Text, - Node, NodeKind, NodeTree, LayoutData}; use newcss::color::{Color, rgb}; use newcss::color::css_colors::{white, black}; use layout::context::LayoutContext; - -#[allow(non_implicitly_copyable_typarams)] -type SpecifiedStyle = {mut background_color : CSSValue, - mut background_image : CSSValue, - mut display_type : CSSValue, - mut font_size : CSSValue, - mut height : CSSValue, - mut text_color : CSSValue, - mut width : CSSValue, - mut border_color : CSSValue, - mut border_style : CSSValue, - mut border_width : CSSValue, - mut position : CSSValue, - mut top : CSSValue, - mut right : CSSValue, - mut bottom : CSSValue, - mut left : CSSValue - }; - -trait DefaultStyleMethods { - fn default_color() -> Color; - fn default_display_type() -> CSSDisplay; - fn default_width() -> BoxSizing; - fn default_height() -> BoxSizing; -} - -/// Default styles for various attributes in case they don't get initialized from CSS selectors. -impl NodeKind : DefaultStyleMethods { - fn default_color() -> Color { - match self { - Text(*) => white(), - Element(*) => white(), - _ => fail ~"unstyleable node type encountered" - } - } - - /* TODO: this belongs in the UA stylesheet */ - fn default_display_type() -> CSSDisplay { - match self { - Text(*) => DisplayInline, - Element(element) => { - match *element.kind { - HTMLDivElement => DisplayBlock, - HTMLHeadElement => DisplayNone, - HTMLImageElement(*) => DisplayInline, - HTMLScriptElement => DisplayNone, - _ => DisplayInline, - } - }, - Comment(*) | Doctype(*) => DisplayNone - } - } - - fn default_width() -> BoxSizing { - BoxAuto - } - - fn default_height() -> BoxSizing { - BoxAuto - } -} - -/** - * 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 the 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. - */ -#[allow(non_implicitly_copyable_typarams)] -fn empty_style_for_node_kind(kind: &NodeKind) -> SpecifiedStyle { - let display_type = kind.default_display_type(); - - {mut background_color : Initial, - mut background_image: Initial, - mut display_type : Specified(display_type), - mut font_size : Initial, - mut height : Initial, - mut text_color : Initial, - mut width : Initial, - mut border_color : Initial, - mut border_style : Initial, - mut border_width : Initial, - mut position : Initial, - mut top : Initial, - mut right : Initial, - mut bottom : Initial, - mut left : Initial} -} +use select_handler::NodeSelectHandler; trait StyleMethods { fn initialize_layout_data() -> Option<@LayoutData>; fn initialize_style_for_subtree(ctx: &LayoutContext, refs: &DVec<@LayoutData>); - fn recompute_style_for_subtree(ctx: &LayoutContext, styles : &SelectCtx); + fn recompute_style_for_subtree(ctx: &LayoutContext, select_ctx: &SelectCtx); } impl Node : StyleMethods { @@ -145,19 +54,18 @@ impl Node : StyleMethods { * the node (the reader-auxiliary box in the COW model) with the * computed style. */ - fn recompute_style_for_subtree(ctx: &LayoutContext, styles : &SelectCtx) { + fn recompute_style_for_subtree(ctx: &LayoutContext, select_ctx: &SelectCtx) { let mut i = 0u; - // Compute the styles of each of our children in parallel for NodeTree.each_child(&self) |kid| { i = i + 1u; - kid.recompute_style_for_subtree(ctx, styles); + kid.recompute_style_for_subtree(ctx, select_ctx); } - self.match_css_style(styles); + let select_handler = NodeSelectHandler { + node: self + }; + let style = select_ctx.select_style(&self, &select_handler); + self.set_style(move style); } } - -fn apply_style(layout_ctx: &LayoutContext, node: Node) { - apply::apply_style(layout_ctx, node) -} \ No newline at end of file diff --git a/src/servo/layout/box.rs b/src/servo/layout/box.rs index f9ea2e79fe2..b2325bf7cf3 100644 --- a/src/servo/layout/box.rs +++ b/src/servo/layout/box.rs @@ -8,7 +8,6 @@ use au::Au; use core::dvec::DVec; use core::to_str::ToStr; use core::rand; -use css::styles::SpecifiedStyle; use css::compute::ComputeStyles; use newcss::values::{BoxSizing, Length, Px, CSSDisplay, Specified, BgColor, BgColorTransparent}; use newcss::values::{BdrColor, PosAbsolute}; diff --git a/src/servo/layout/box_builder.rs b/src/servo/layout/box_builder.rs index 5e77e97f6ec..1b85a04cd21 100644 --- a/src/servo/layout/box_builder.rs +++ b/src/servo/layout/box_builder.rs @@ -1,7 +1,6 @@ /** Creates CSS boxes from a DOM. */ use au = gfx::geometry; use core::dvec::DVec; -use css::styles::{SpecifiedStyle, empty_style_for_node_kind}; use newcss::values::{CSSDisplay, DisplayBlock, DisplayInline, DisplayInlineBlock, DisplayNone}; use newcss::values::{Inherit, Initial, Specified}; use dom::element::*; diff --git a/src/servo/layout/layout_task.rs b/src/servo/layout/layout_task.rs index eabae411b1e..cd2187ebf44 100644 --- a/src/servo/layout/layout_task.rs +++ b/src/servo/layout/layout_task.rs @@ -7,7 +7,6 @@ use au = gfx::geometry; use au::Au; use content::content_task; use core::dvec::DVec; -use css::styles::apply_style; use newcss::Stylesheet; use dl = gfx::display_list; use dom::event::{Event, ReflowEvent}; @@ -173,13 +172,11 @@ impl Layout { }; let layout_root: @FlowContext = do time("layout: tree construction") { - // TODO: this is dumb. we don't need 3 separate traversals. + // TODO: this is dumb. we don't need 2 separate traversals. node.initialize_style_for_subtree(&layout_ctx, &self.layout_refs); do self.css_select_ctx.borrow_imm |ctx| { node.recompute_style_for_subtree(&layout_ctx, ctx); } - /* resolve styles (convert relative values) down the node tree */ - apply_style(&layout_ctx, *node); let builder = LayoutTreeBuilder::new(); let layout_root: @FlowContext = match builder.construct_trees(&layout_ctx, diff --git a/src/servo/servo.rc b/src/servo/servo.rc index d7447ffbe57..67724a8bf7c 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -46,10 +46,8 @@ pub mod content { pub mod css { pub mod styles; - mod apply; - mod matching; - priv mod select_handler; pub mod compute; + priv mod select_handler; priv mod node_util; }