Revamp how images are handled. HTMLImageElement sends a prefetch message to the image cache during parsing. Other miscellaneous cleanup.

This commit is contained in:
Brian J. Burg 2012-09-21 15:20:37 -07:00
parent b7b3cc8bbb
commit b5e0cee1ab
15 changed files with 174 additions and 131 deletions

View file

@ -20,6 +20,7 @@ use gfx::compositor::Compositor;
use html::lexer::spawn_html_lexer_task; use html::lexer::spawn_html_lexer_task;
use layout::layout_task; use layout::layout_task;
use layout_task::{LayoutTask, BuildMsg}; use layout_task::{LayoutTask, BuildMsg};
use resource::image_cache_task::ImageCacheTask;
use css::styles::Stylesheet; use css::styles::Stylesheet;
@ -59,9 +60,12 @@ enum PingMsg {
type ContentTask = Chan<ControlMsg>; type ContentTask = Chan<ControlMsg>;
fn ContentTask<S: Compositor Send Copy>(layout_task: LayoutTask, +compositor: S, resource_task: ResourceTask) -> ContentTask { fn ContentTask<S: Compositor Send Copy>(layout_task: LayoutTask,
+compositor: S,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask) -> ContentTask {
do task().sched_mode(SingleThreaded).spawn_listener::<ControlMsg> |from_master| { do task().sched_mode(SingleThreaded).spawn_listener::<ControlMsg> |from_master| {
Content(layout_task, compositor, from_master, resource_task).start(); Content(layout_task, compositor, from_master, resource_task, img_cache_task).start();
} }
} }
@ -81,6 +85,7 @@ fn join_layout(scope: NodeScope, layout_task: LayoutTask) {
struct Content<C:Compositor> { struct Content<C:Compositor> {
compositor: C, compositor: C,
layout_task: LayoutTask, layout_task: LayoutTask,
image_cache_task: ImageCacheTask,
from_master: comm::Port<ControlMsg>, from_master: comm::Port<ControlMsg>,
event_port: comm::Port<Event>, event_port: comm::Port<Event>,
@ -100,7 +105,8 @@ struct Content<C:Compositor> {
fn Content<C:Compositor>(layout_task: LayoutTask, fn Content<C:Compositor>(layout_task: LayoutTask,
compositor: C, compositor: C,
from_master: Port<ControlMsg>, from_master: Port<ControlMsg>,
resource_task: ResourceTask) -> Content<C> { resource_task: ResourceTask,
img_cache_task: ImageCacheTask) -> Content<C> {
let jsrt = jsrt(); let jsrt = jsrt();
let cx = jsrt.cx(); let cx = jsrt.cx();
@ -117,6 +123,7 @@ fn Content<C:Compositor>(layout_task: LayoutTask,
Content { Content {
layout_task : layout_task, layout_task : layout_task,
image_cache_task : img_cache_task,
compositor : compositor, compositor : compositor,
from_master : from_master, from_master : from_master,
event_port : event_port, event_port : event_port,
@ -159,7 +166,8 @@ impl<C:Compositor> Content<C> {
let result = html::hubbub_html_parser::parse_html(self.scope, let result = html::hubbub_html_parser::parse_html(self.scope,
url, url,
self.resource_task); self.resource_task,
self.image_cache_task);
let root = result.root; let root = result.root;
let css_rules = result.style_port.recv(); let css_rules = result.style_port.recv();

View file

@ -1,6 +1,9 @@
#[doc="Applies the appropriate CSS style to boxes."] /**
* Applies the appropriate CSS style to nodes.
*/
use au = gfx::geometry; use au = gfx::geometry;
use dom::node::{Node, NodeTree};
use dom::element::*; use dom::element::*;
use layout::base::{RenderBox, SpecifiedStyle, RenderBoxTree}; use layout::base::{RenderBox, SpecifiedStyle, RenderBoxTree};
use layout::context::LayoutContext; use layout::context::LayoutContext;
@ -16,7 +19,7 @@ trait ResolveMethods<T> {
} }
impl CSSValue<CSSBackgroundColor> : ResolveMethods<CSSBackgroundColor> { impl CSSValue<CSSBackgroundColor> : ResolveMethods<CSSBackgroundColor> {
pure fn initial() -> CSSBackgroundColor { return BgTransparent; } pure fn initial() -> CSSBackgroundColor { return BgColorTransparent; }
} }
impl CSSValue<CSSDisplay> : ResolveMethods<CSSDisplay> { impl CSSValue<CSSDisplay> : ResolveMethods<CSSDisplay> {
@ -33,14 +36,14 @@ impl CSSValue<CSSFontSize> : ResolveMethods<CSSFontSize> {
struct StyleApplicator { struct StyleApplicator {
box: @RenderBox, node: Node,
reflow: fn~(), reflow: fn~(),
} }
// TODO: normalize this into a normal preorder tree traversal function // TODO: normalize this into a normal preorder tree traversal function
fn apply_style(layout_ctx: &LayoutContext, box: @RenderBox, reflow: fn~()) { fn apply_style(layout_ctx: &LayoutContext, node: Node, reflow: fn~()) {
let applicator = StyleApplicator { let applicator = StyleApplicator {
box: box, node: node,
reflow: reflow reflow: reflow
}; };
@ -49,14 +52,15 @@ fn apply_style(layout_ctx: &LayoutContext, box: @RenderBox, reflow: fn~()) {
// TODO: this is misleadingly-named. It is actually trying to resolve CSS 'inherit' values. // TODO: this is misleadingly-named. It is actually trying to resolve CSS 'inherit' values.
#[doc="A wrapper around a set of functions that can be applied as a top-down traversal of layout /** A wrapper around a set of functions that can be applied as a
boxes."] * top-down traversal of layout boxes.
fn inheritance_wrapper(layout_ctx: &LayoutContext, box : @RenderBox, reflow: fn~()) { */
fn inheritance_wrapper(layout_ctx: &LayoutContext, node : Node, reflow: fn~()) {
let applicator = StyleApplicator { let applicator = StyleApplicator {
box: box, node: node,
reflow: reflow reflow: reflow
}; };
applicator.apply_style(layout_ctx); applicator.resolve_style(layout_ctx);
} }
/* /*
@ -103,41 +107,19 @@ impl StyleApplicator {
fn apply_css_style(layout_ctx: &LayoutContext) { fn apply_css_style(layout_ctx: &LayoutContext) {
let reflow = copy self.reflow; let reflow = copy self.reflow;
do RenderBoxTree.each_child(self.box) |child| { do NodeTree.each_child(self.node) |child| {
inheritance_wrapper(layout_ctx, child, reflow); true inheritance_wrapper(layout_ctx, child, reflow); true
} }
} }
#[doc="Applies CSS style to a layout box. /**
* Convert the cascaded, specified style for this node into a resolved style:
Get the specified style and apply the existing traits to a * one which additionally resolves the values of Initial, Inherit based on
layout box. If a trait does not exist, calculate the default * defaults and node parent style. It also converts Node attributes into
value for the given type of element and use that instead. * equivalent inline style declarations (TODO: where is this defined??)
*/
"] fn resolve_style(_layout_ctx: &LayoutContext) {
fn apply_style(layout_ctx: &LayoutContext) { // TODO: implement
// Right now, we only handle images.
do self.box.node.read |node| {
match node.kind {
~dom::node::Element(element) => {
match element.kind {
~HTMLImageElement(*) => {
let url = element.get_attr(~"src");
if url.is_some() {
// FIXME: Some sort of BASE HREF support!
// FIXME: Parse URLs!
let new_url = make_url(option::unwrap(url), Some(copy layout_ctx.doc_url));
self.box.data.background_image = Some(ImageHolder(new_url, layout_ctx.image_cache, self.reflow))
};
}
_ => { /* Ignore. */ }
}
}
_ => { /* Ignore. */ }
}
}
} }
} }

View file

@ -14,6 +14,7 @@ use util::color::css_colors::{white, black};
use layout::context::LayoutContext; use layout::context::LayoutContext;
type SpecifiedStyle = {mut background_color : CSSValue<CSSBackgroundColor>, type SpecifiedStyle = {mut background_color : CSSValue<CSSBackgroundColor>,
mut background_image : CSSValue<CSSBackgroundImage>,
mut display_type : CSSValue<CSSDisplay>, mut display_type : CSSValue<CSSDisplay>,
mut font_size : CSSValue<CSSFontSize>, mut font_size : CSSValue<CSSFontSize>,
mut height : CSSValue<BoxSizing>, mut height : CSSValue<BoxSizing>,
@ -75,6 +76,7 @@ fn empty_style_for_node_kind(kind: NodeKind) -> SpecifiedStyle {
let display_type = kind.default_display_type(); let display_type = kind.default_display_type();
{mut background_color : Initial, {mut background_color : Initial,
mut background_image: Initial,
mut display_type : Specified(display_type), mut display_type : Specified(display_type),
mut font_size : Initial, mut font_size : Initial,
mut height : Initial, mut height : Initial,

View file

@ -1,5 +1,6 @@
use SharedColor = util::color::Color; use SharedColor = util::color::Color;
use cmp::Eq; use cmp::Eq;
use std::net::url::Url;
#[doc = " #[doc = "
Defines how css rules, both selectors and style specifications, are Defines how css rules, both selectors and style specifications, are
@ -101,7 +102,7 @@ enum CSSBackgroundAttachment {
enum CSSBackgroundColor { enum CSSBackgroundColor {
BgColor(SharedColor), BgColor(SharedColor),
BgTransparent BgColorTransparent
} }
enum CSSBackgroundRepeat { enum CSSBackgroundRepeat {
@ -111,6 +112,11 @@ enum CSSBackgroundRepeat {
BgNoRepeat BgNoRepeat
} }
enum CSSBackgroundImage {
BgImage(Url),
BgImageNone,
}
enum CSSColor { enum CSSColor {
TextColor(SharedColor) TextColor(SharedColor)
} }
@ -232,7 +238,7 @@ impl CSSBackgroundColor: cmp::Eq {
pure fn eq(other: &CSSBackgroundColor) -> bool { pure fn eq(other: &CSSBackgroundColor) -> bool {
match (self, *other) { match (self, *other) {
(BgColor(a), BgColor(b)) => a == b, (BgColor(a), BgColor(b)) => a == b,
(BgTransparent, BgTransparent) => true, (BgColorTransparent, BgColorTransparent) => true,
(_, _) => false (_, _) => false
} }
} }

View file

@ -2,6 +2,7 @@ use au = gfx::geometry;
use au::au; use au::au;
use dvec::DVec; use dvec::DVec;
use geom::size::Size2D; use geom::size::Size2D;
use std::net::url::Url;
struct ElementData { struct ElementData {
tag_name: ~str, tag_name: ~str,
@ -47,6 +48,16 @@ fn Attr(name: ~str, value: ~str) -> Attr {
} }
} }
fn HTMLImageData() -> HTMLImageData {
HTMLImageData {
image: None
}
}
struct HTMLImageData {
mut image: Option<Url>
}
enum HeadingLevel { enum HeadingLevel {
Heading1, Heading1,
Heading2, Heading2,
@ -69,9 +80,7 @@ enum ElementKind {
HTMLHeadElement, HTMLHeadElement,
HTMLHeadingElement(HeadingLevel), HTMLHeadingElement(HeadingLevel),
HTMLHtmlElement, HTMLHtmlElement,
// TODO: should not take this as argument--it is fetched from HTMLImageElement(HTMLImageData),
// layout task as requested.
HTMLImageElement({mut size: Size2D<au>}),
HTMLInputElement, HTMLInputElement,
HTMLItalicElement, HTMLItalicElement,
HTMLLinkElement, HTMLLinkElement,

View file

@ -71,7 +71,7 @@ impl Node : DebugMethods {
} }
fn debug_str() -> ~str { fn debug_str() -> ~str {
fmt!("%?", self.read(|n| copy n.kind )) do self.read |n| { fmt!("%?", n.kind) }
} }
} }

View file

@ -37,7 +37,7 @@ fn EngineTask_<C: Compositor Send Copy>(
let render_task = RenderTask(compositor); let render_task = RenderTask(compositor);
let layout_task = LayoutTask(render_task, image_cache_task); let layout_task = LayoutTask(render_task, image_cache_task);
let content_task = ContentTask(layout_task, compositor, resource_task); let content_task = ContentTask(layout_task, compositor, resource_task, image_cache_task);
Engine { Engine {
compositor: compositor, compositor: compositor,

View file

@ -1,10 +1,12 @@
use au = gfx::geometry; use au = gfx::geometry;
use dom::element::UnknownElement; use content::content_task::ContentTask;
use css::values::Stylesheet;
use dom::element::*;
use dom::event::{Event, ReflowEvent};
use dom::node::{Comment, Doctype, DoctypeData, Text, use dom::node::{Comment, Doctype, DoctypeData, Text,
Element, Node, NodeScope}; Element, Node, NodeScope};
use dom::element::*; use resource::image_cache_task::ImageCacheTask;
use css::values::Stylesheet; use resource::image_cache_task;
use geom::size::Size2D;
use resource::resource_task::{Done, Load, Payload, ResourceTask}; use resource::resource_task::{Done, Load, Payload, ResourceTask};
use comm::{Chan, Port}; use comm::{Chan, Port};
@ -138,7 +140,7 @@ fn build_element_kind(tag: &str) -> ~ElementKind {
else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) } else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) }
else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) } else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) }
else if tag == ~"html" { ~HTMLHtmlElement } else if tag == ~"html" { ~HTMLHtmlElement }
else if tag == ~"img" { ~HTMLImageElement({ mut size: au::zero_size() }) } else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) }
else if tag == ~"input" { ~HTMLInputElement } else if tag == ~"input" { ~HTMLInputElement }
else if tag == ~"i" { ~HTMLItalicElement } else if tag == ~"i" { ~HTMLItalicElement }
else if tag == ~"link" { ~HTMLLinkElement } else if tag == ~"link" { ~HTMLLinkElement }
@ -162,7 +164,10 @@ fn build_element_kind(tag: &str) -> ~ElementKind {
else { ~UnknownElement } else { ~UnknownElement }
} }
fn parse_html(scope: NodeScope, url: Url, resource_task: ResourceTask) -> HtmlParserResult unsafe { fn parse_html(scope: NodeScope,
url: Url,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask) -> HtmlParserResult unsafe {
// Spawn a CSS parser to receive links to CSS style sheets. // Spawn a CSS parser to receive links to CSS style sheets.
let (css_port, css_chan): (comm::Port<Stylesheet>, comm::Chan<CSSMessage>) = let (css_port, css_chan): (comm::Port<Stylesheet>, comm::Chan<CSSMessage>) =
do task::spawn_conversation |css_port: comm::Port<CSSMessage>, do task::spawn_conversation |css_port: comm::Port<CSSMessage>,
@ -217,10 +222,10 @@ fn parse_html(scope: NodeScope, url: Url, resource_task: ResourceTask) -> HtmlPa
from_slice(attribute.value))); from_slice(attribute.value)));
} }
// Spawn additional parsing, network loads, etc. from opening tag // Spawn additional parsing, network loads, etc. from tag and attrs
match elem.tag_name { match elem.kind {
//Handle CSS style sheets from <link> elements //Handle CSS style sheets from <link> elements
~"link" => { ~HTMLLinkElement => {
match (elem.get_attr(~"rel"), elem.get_attr(~"href")) { match (elem.get_attr(~"rel"), elem.get_attr(~"href")) {
(Some(rel), Some(href)) if rel == ~"stylesheet" => { (Some(rel), Some(href)) if rel == ~"stylesheet" => {
debug!("found CSS stylesheet: %s", href); debug!("found CSS stylesheet: %s", href);
@ -228,6 +233,14 @@ fn parse_html(scope: NodeScope, url: Url, resource_task: ResourceTask) -> HtmlPa
} }
_ => {} _ => {}
} }
},
~HTMLImageElement(d) => {
do elem.get_attr(~"src").iter |img_url_str| {
let img_url = make_url(copy img_url_str, Some(copy *url));
d.image = Some(copy img_url);
// inform the image cache to load this, but don't store a handle.
image_cache_task.send(image_cache_task::Prefetch(move img_url));
}
} }
//TODO: handle inline styles ('style' attr) //TODO: handle inline styles ('style' attr)
_ => {} _ => {}

View file

@ -1,7 +1,8 @@
use std::net::url::Url; use std::net::url::Url;
use std::arc::{ARC, clone}; use std::arc::{ARC, clone, get};
use resource::image_cache_task::ImageCacheTask; use resource::image_cache_task::ImageCacheTask;
use resource::image_cache_task; use resource::image_cache_task;
use geom::size::Size2D;
/** A struct to store image data. The image will be loaded once, the /** A struct to store image data. The image will be loaded once, the
first time it is requested, and an arc will be stored. Clones of first time it is requested, and an arc will be stored. Clones of
@ -17,9 +18,10 @@ pub struct ImageHolder {
} }
fn ImageHolder(-url : Url, image_cache_task: ImageCacheTask, cb: fn~()) -> ImageHolder { fn ImageHolder(url : &Url, image_cache_task: ImageCacheTask, cb: fn~()) -> ImageHolder {
debug!("ImageHolder() %?", url.to_str());
let holder = ImageHolder { let holder = ImageHolder {
url : Some(copy url), url : Some(copy *url),
image : None, image : None,
image_cache_task : image_cache_task, image_cache_task : image_cache_task,
reflow_cb : copy cb, reflow_cb : copy cb,
@ -30,23 +32,34 @@ fn ImageHolder(-url : Url, image_cache_task: ImageCacheTask, cb: fn~()) -> Image
// but they are intended to be spread out in time. Ideally prefetch // but they are intended to be spread out in time. Ideally prefetch
// should be done as early as possible and decode only once we // should be done as early as possible and decode only once we
// are sure that the image will be used. // are sure that the image will be used.
image_cache_task.send(image_cache_task::Prefetch(copy url)); image_cache_task.send(image_cache_task::Prefetch(copy *url));
image_cache_task.send(image_cache_task::Decode(move url)); image_cache_task.send(image_cache_task::Decode(copy *url));
holder holder
} }
impl ImageHolder { impl ImageHolder {
fn get_size() -> Option<Size2D<int>> {
debug!("get_size() %?", self.url);
match self.get_image() {
Some(img) => {
let img_ref = get(&img);
Some(Size2D(img_ref.width as int,
img_ref.height as int))
},
None => None
}
}
// This function should not be called by two tasks at the same time // This function should not be called by two tasks at the same time
fn get_image() -> Option<ARC<~Image>> { fn get_image() -> Option<ARC<~Image>> {
debug!("get_image() %?", self.url);
// If this is the first time we've called this function, load // If this is the first time we've called this function, load
// the image and store it for the future // the image and store it for the future
if self.image.is_none() { if self.image.is_none() {
assert self.url.is_some(); assert self.url.is_some();
let url = copy self.url.get();
let mut temp = None;
temp <-> self.url;
let url = option::unwrap(temp);
let response_port = Port(); let response_port = Port();
self.image_cache_task.send(image_cache_task::GetImage(copy url, response_port.chan())); self.image_cache_task.send(image_cache_task::GetImage(copy url, response_port.chan()));
@ -68,7 +81,7 @@ impl ImageHolder {
None None
} }
image_cache_task::ImageFailed => { image_cache_task::ImageFailed => {
#info("image was not ready for %s", url.to_str()); debug!("image was not ready for %s", url.to_str());
// FIXME: Need to schedule another layout when the image is ready // FIXME: Need to schedule another layout when the image is ready
None None
} }

View file

@ -8,7 +8,7 @@ use core::dvec::DVec;
use core::to_str::ToStr; use core::to_str::ToStr;
use core::rand; use core::rand;
use css::styles::SpecifiedStyle; use css::styles::SpecifiedStyle;
use css::values::{BoxSizing, Length, Px, CSSDisplay, Specified, BgColor, BgTransparent}; use css::values::{BoxSizing, Length, Px, CSSDisplay, Specified, BgColor, BgColorTransparent};
use dl = gfx::display_list; use dl = gfx::display_list;
use dom::element::{ElementKind, HTMLDivElement, HTMLImageElement}; use dom::element::{ElementKind, HTMLDivElement, HTMLImageElement};
use dom::node::{Element, Node, NodeData, NodeKind, NodeTree}; use dom::node::{Element, Node, NodeData, NodeKind, NodeTree};
@ -121,6 +121,7 @@ enum FlowContextData {
struct FlowContext { struct FlowContext {
kind: FlowContextData, kind: FlowContextData,
data: FlowLayoutData, data: FlowLayoutData,
mut node: Option<Node>,
/* reference to parent, children flow contexts */ /* reference to parent, children flow contexts */
tree: tree::Tree<@FlowContext>, tree: tree::Tree<@FlowContext>,
/* TODO: debug only */ /* TODO: debug only */
@ -131,6 +132,7 @@ fn FlowContext(id: int, kind: FlowContextData, tree: tree::Tree<@FlowContext>) -
FlowContext { FlowContext {
kind: kind, kind: kind,
data: FlowLayoutData(), data: FlowLayoutData(),
node: None,
tree: tree, tree: tree,
id: id id: id
} }
@ -230,7 +232,7 @@ fn BoxLayoutData() -> BoxLayoutData {
enum BoxData { enum BoxData {
GenericBox, GenericBox,
ImageBox(Size2D<au>), ImageBox(ImageHolder),
TextBox(TextBoxData) TextBox(TextBoxData)
} }
@ -270,7 +272,11 @@ impl @RenderBox {
} }
} }
pure fn get_min_width() -> au { /** In general, these functions are transitively impure because they
* may cause glyphs to be allocated. For now, it's impure because of
* holder.get_image()
*/
fn get_min_width() -> au {
match self.kind { match self.kind {
// TODO: this should account for min/pref widths of the // TODO: this should account for min/pref widths of the
// box element in isolation. That includes // box element in isolation. That includes
@ -279,18 +285,15 @@ impl @RenderBox {
// that of its children to arrive at the context width. // that of its children to arrive at the context width.
GenericBox => au(0), GenericBox => au(0),
// TODO: consult CSS 'width', margin, border. // TODO: consult CSS 'width', margin, border.
// TODO: If image isn't available, consult Node // TODO: If image isn't available, consult 'width'.
// attrs, etc. to determine intrinsic dimensions. These ImageBox(i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),
// dimensions are not defined by CSS 2.1, but are defined
// by the HTML5 spec in Section 4.8.1
ImageBox(size) => size.width,
TextBox(d) => d.runs.foldl(au(0), |sum, run| { TextBox(d) => d.runs.foldl(au(0), |sum, run| {
au::max(sum, run.min_break_width()) au::max(sum, run.min_break_width())
}) })
} }
} }
pure fn get_pref_width() -> au { fn get_pref_width() -> au {
match self.kind { match self.kind {
// TODO: this should account for min/pref widths of the // TODO: this should account for min/pref widths of the
// box element in isolation. That includes // box element in isolation. That includes
@ -298,11 +301,7 @@ impl @RenderBox {
// FlowContext will combine the width of this element and // FlowContext will combine the width of this element and
// that of its children to arrive at the context width. // that of its children to arrive at the context width.
GenericBox => au(0), GenericBox => au(0),
// TODO: If image isn't available, consult Node ImageBox(i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),
// attrs, etc. to determine intrinsic dimensions. These
// dimensions are not defined by CSS 2.1, but are defined
// by the HTML5 spec in Section 4.8.1
ImageBox(size) => size.width,
// TODO: account for line breaks, etc. The run should know // TODO: account for line breaks, etc. The run should know
// how to compute its own min and pref widths, and should // how to compute its own min and pref widths, and should
// probably cache them. // probably cache them.
@ -330,6 +329,7 @@ impl @RenderBox {
(au(0), au(0)) (au(0), au(0))
} }
// This will be very unhappy if it is getting run in parallel with // This will be very unhappy if it is getting run in parallel with
// anything trying to read the background image // anything trying to read the background image
fn get_image() -> Option<ARC<~Image>> { fn get_image() -> Option<ARC<~Image>> {
@ -408,7 +408,7 @@ impl @RenderBox {
let boxed_color = self.node.style().background_color; let boxed_color = self.node.style().background_color;
let color = match boxed_color { let color = match boxed_color {
Specified(BgColor(c)) => c, Specified(BgColor(c)) => c,
Specified(BgTransparent) | _ => util::color::rgba(0,0,0,0.0) Specified(BgColorTransparent) | _ => util::color::rgba(0,0,0,0.0)
}; };
list.push(~dl::SolidColor(bounds, color.red, color.green, color.blue)); list.push(~dl::SolidColor(bounds, color.red, color.green, color.blue));
} }

View file

@ -6,6 +6,7 @@ use css::values::{CSSDisplay, DisplayBlock, DisplayInline, DisplayInlineBlock, D
use css::values::{Inherit, Initial, Specified}; use css::values::{Inherit, Initial, Specified};
use dom::element::*; use dom::element::*;
use dom::node::{Comment, Doctype, Element, Text, Node, NodeTree, LayoutData}; use dom::node::{Comment, Doctype, Element, Text, Node, NodeTree, LayoutData};
use image::holder::ImageHolder;
use layout::base::{RenderBox, BoxData, GenericBox, ImageBox, TextBox, RenderBoxTree}; use layout::base::{RenderBox, BoxData, GenericBox, ImageBox, TextBox, RenderBoxTree};
use layout::base::{FlowContext, FlowContextData, BlockFlow, InlineFlow, InlineBlockFlow, RootFlow, FlowTree}; use layout::base::{FlowContext, FlowContextData, BlockFlow, InlineFlow, InlineBlockFlow, RootFlow, FlowTree};
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
@ -21,7 +22,6 @@ use servo_text::font_cache::FontCache;
export LayoutTreeBuilder; export LayoutTreeBuilder;
struct LayoutTreeBuilder { struct LayoutTreeBuilder {
mut root_box: Option<@RenderBox>,
mut root_ctx: Option<@FlowContext>, mut root_ctx: Option<@FlowContext>,
mut next_bid: int, mut next_bid: int,
mut next_cid: int mut next_cid: int
@ -29,7 +29,6 @@ struct LayoutTreeBuilder {
fn LayoutTreeBuilder() -> LayoutTreeBuilder { fn LayoutTreeBuilder() -> LayoutTreeBuilder {
LayoutTreeBuilder { LayoutTreeBuilder {
root_box: None,
root_ctx: None, root_ctx: None,
next_bid: -1, next_bid: -1,
next_cid: -1 next_cid: -1
@ -43,7 +42,9 @@ impl LayoutTreeBuilder {
/** Creates necessary box(es) and flow context(s) for the current DOM node, /** Creates necessary box(es) and flow context(s) for the current DOM node,
and recurses on its children. */ and recurses on its children. */
fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node, parent_ctx: @FlowContext, parent_box: @RenderBox) { fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node,
parent_ctx: @FlowContext, parent_box: Option<@RenderBox>) {
let style = cur_node.style(); let style = cur_node.style();
// DEBUG // DEBUG
@ -58,10 +59,7 @@ impl LayoutTreeBuilder {
}; };
// first, create the proper box kind, based on node characteristics // first, create the proper box kind, based on node characteristics
let box_data = match self.create_box_data(layout_ctx, cur_node, simulated_display) { let box_data = self.create_box_data(layout_ctx, cur_node, simulated_display);
None => return,
Some(data) => data
};
// then, figure out its proper context, possibly reorganizing. // then, figure out its proper context, possibly reorganizing.
let next_ctx: @FlowContext = match box_data { let next_ctx: @FlowContext = match box_data {
@ -94,27 +92,40 @@ impl LayoutTreeBuilder {
} }
}; };
// store reference to the flow context which contains any boxes
// that correspond to cur_node
assert cur_node.has_aux();
do cur_node.aux |data| { data.flow = Some(next_ctx) }
// make box, add box to any context-specific list. // make box, add box to any context-specific list.
let mut new_box = self.make_box(cur_node, parent_ctx, box_data); let mut new_box = self.make_box(cur_node, parent_ctx, box_data);
debug!("Assign ^box to flow: %?", next_ctx.debug_str()); debug!("Assign ^box to flow: %?", next_ctx.debug_str());
match next_ctx.kind { match next_ctx.kind {
InlineFlow(d) => { d.boxes.push(new_box) } InlineFlow(d) => {
d.boxes.push(new_box);
if (parent_box.is_some()) {
let parent = parent_box.get();
// connect the box to its parent box
debug!("In inline flow f%?, set child b%? of parent b%?", next_ctx.id, parent.id, new_box.id);
RenderBoxTree.add_child(parent, new_box);
}
}
BlockFlow(d) => { d.box = Some(new_box) } BlockFlow(d) => { d.box = Some(new_box) }
_ => {} // TODO: float lists, etc. _ => {} // TODO: float lists, etc.
}; };
// connect the box to its parent box
debug!("Adding child box b%? of b%?", parent_box.id, new_box.id);
RenderBoxTree.add_child(parent_box, new_box);
if (!next_ctx.eq(&parent_ctx)) { if (!next_ctx.eq(&parent_ctx)) {
debug!("Adding child flow f%? of f%?", parent_ctx.id, next_ctx.id); debug!("Adding child flow f%? of f%?", parent_ctx.id, next_ctx.id);
FlowTree.add_child(parent_ctx, next_ctx); FlowTree.add_child(parent_ctx, next_ctx);
} }
// recurse // recurse
// TODO: don't set parent box unless this is an inline flow?
do NodeTree.each_child(cur_node) |child_node| { do NodeTree.each_child(cur_node) |child_node| {
self.construct_recursively(layout_ctx, child_node, next_ctx, new_box); true self.construct_recursively(layout_ctx, child_node, next_ctx, Some(new_box)); true
} }
// Fixup any irregularities, such as split inlines (CSS 2.1 Section 9.2.1.1) // Fixup any irregularities, such as split inlines (CSS 2.1 Section 9.2.1.1)
@ -164,12 +175,11 @@ impl LayoutTreeBuilder {
/** entry point for box creation. Should only be /** entry point for box creation. Should only be
called on root DOM element. */ called on root DOM element. */
fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@RenderBox, ()> { fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@FlowContext, ()> {
self.root_ctx = Some(self.make_ctx(RootFlow(RootFlowData()), tree::empty())); self.root_ctx = Some(self.make_ctx(RootFlow(RootFlowData()), tree::empty()));
self.root_box = Some(self.make_box(root, self.root_ctx.get(), GenericBox));
self.construct_recursively(layout_ctx, root, self.root_ctx.get(), self.root_box.get()); self.construct_recursively(layout_ctx, root, self.root_ctx.get(), None);
return Ok(self.root_box.get()) return Ok(self.root_ctx.get())
} }
fn make_ctx(kind : FlowContextData, tree: tree::Tree<@FlowContext>) -> @FlowContext { fn make_ctx(kind : FlowContextData, tree: tree::Tree<@FlowContext>) -> @FlowContext {
@ -185,22 +195,27 @@ impl LayoutTreeBuilder {
} }
/* Based on the DOM node type, create a specific type of box */ /* Based on the DOM node type, create a specific type of box */
fn create_box_data(layout_ctx: &LayoutContext, node: Node, display: CSSDisplay) -> Option<BoxData> { fn create_box_data(layout_ctx: &LayoutContext, node: Node, display: CSSDisplay) -> BoxData {
// TODO: handle more types of nodes. // TODO: handle more types of nodes.
do node.read |n| { do node.read |n| {
match n.kind { match n.kind {
~Doctype(*) | ~Comment(*) => None, ~Doctype(*) | ~Comment(*) => fail ~"Hey, doctypes and comments shouldn't get here! They are display:none!",
~Text(string) => { ~Text(string) => {
// TODO: clean this up. Fonts should not be created here. // TODO: clean this up. Fonts should not be created here.
let font = layout_ctx.font_cache.get_test_font(); let font = layout_ctx.font_cache.get_test_font();
let run = TextRun(font, string); let run = TextRun(font, string);
Some(TextBox(TextBoxData(copy string, ~[move run]))) TextBox(TextBoxData(copy string, ~[move run]))
} }
~Element(element) => { ~Element(element) => {
match (element.kind, display) { match (element.kind, display) {
(~HTMLImageElement({size}), _) => Some(ImageBox(size)), (~HTMLImageElement(d), _) if d.image.is_some() => {
// (_, Specified(_)) => Some(GenericBox), let holder = ImageHolder(&d.image.get(),
(_, _) => Some(GenericBox) // TODO: replace this with the commented lines layout_ctx.image_cache,
copy layout_ctx.reflow_cb);
ImageBox(holder)
},
// (_, Specified(_)) => GenericBox,
(_, _) => GenericBox // TODO: replace this with the commented lines
// (_, _) => fail ~"Can't create box for Node with non-specified 'display' type" // (_, _) => fail ~"Can't create box for Node with non-specified 'display' type"
} }
} }

View file

@ -10,5 +10,6 @@ struct LayoutContext {
font_cache: @FontCache, font_cache: @FontCache,
image_cache: ImageCacheTask, image_cache: ImageCacheTask,
doc_url: Url, doc_url: Url,
reflow_cb: fn~(),
screen_size: Rect<au> screen_size: Rect<au>
} }

View file

@ -2,7 +2,7 @@ export DisplayListBuilder;
use au = gfx::geometry; use au = gfx::geometry;
use base::{RenderBox, RenderBoxTree}; use base::{RenderBox, RenderBoxTree};
use css::values::{BgColor, BgTransparent, Specified}; use css::values::{BgColor, BgColorTransparent, Specified};
use dl = gfx::display_list; use dl = gfx::display_list;
use dom::node::{Text, NodeScope}; use dom::node::{Text, NodeScope};
use dom::rcu::Scope; use dom::rcu::Scope;

View file

@ -116,14 +116,14 @@ impl @FlowContext : InlineLayout {
} }
box.data.position.size.width = match box.kind { box.data.position.size.width = match box.kind {
ImageBox(sz) => sz.width, ImageBox(img) => au::from_px(img.get_size().get_default(Size2D(0,0)).width),
TextBox(d) => d.runs[0].size().width, TextBox(d) => d.runs[0].size().width,
// TODO: this should be set to the extents of its children // TODO: this should be set to the extents of its children
GenericBox(*) => au(0) GenericBox(*) => au(0)
}; };
box.data.position.size.height = match box.kind { box.data.position.size.height = match box.kind {
ImageBox(sz) => sz.height, ImageBox(img) => au::from_px(img.get_size().get_default(Size2D(0,0)).height),
TextBox(d) => d.runs[0].size().height, TextBox(d) => d.runs[0].size().height,
// TODO: this should be set to the extents of its children // TODO: this should be set to the extents of its children
GenericBox(*) => au(0) GenericBox(*) => au(0)

View file

@ -60,37 +60,31 @@ fn LayoutTask(render_task: RenderTask, image_cache_task: ImageCacheTask) -> Layo
image_cache: image_cache_task, image_cache: image_cache_task,
font_cache: font_cache, font_cache: font_cache,
doc_url: doc_url, doc_url: doc_url,
reflow_cb: || event_chan.send(ReflowEvent),
// TODO: obtain screen size from a real data source // TODO: obtain screen size from a real data source
screen_size: Rect(Point2D(au(0), au(0)), Size2D(au::from_px(800), au::from_px(600))) screen_size: Rect(Point2D(au(0), au(0)), Size2D(au::from_px(800), au::from_px(600)))
}; };
do util::time::time(~"layout") { do util::time::time(~"layout") {
// TODO: this is dumb. we don't need 3 separate traversals.
node.initialize_style_for_subtree(&layout_ctx, &layout_data_refs); node.initialize_style_for_subtree(&layout_ctx, &layout_data_refs);
node.recompute_style_for_subtree(&layout_ctx, styles); node.recompute_style_for_subtree(&layout_ctx, styles);
/* resolve styles (convert relative values) down the node tree */
// TODO: this should care about root flow, not root box. apply_style(&layout_ctx, node, layout_ctx.reflow_cb);
let root_box: @RenderBox;
let builder = LayoutTreeBuilder(); let builder = LayoutTreeBuilder();
match builder.construct_trees(&layout_ctx, node) { let layout_root: @FlowContext = match builder.construct_trees(&layout_ctx, node) {
Ok(root) => root_box = root, Ok(root) => root,
Err(*) => fail ~"Root node should always exist" Err(*) => fail ~"Root flow should always exist"
} };
debug!("layout: constructed RenderBox tree");
root_box.dump();
debug!("layout: constructed Flow tree"); debug!("layout: constructed Flow tree");
root_box.ctx.dump(); layout_root.dump();
/* resolve styles (convert relative values) down the box tree */
let reflow_cb: fn~() = || event_chan.send(ReflowEvent);
apply_style(&layout_ctx, root_box, reflow_cb);
/* perform layout passes over the flow tree */ /* perform layout passes over the flow tree */
let root_flow = root_box.ctx; do layout_root.traverse_postorder |f| { f.bubble_widths(&layout_ctx) }
do root_flow.traverse_postorder |f| { f.bubble_widths(&layout_ctx) } do layout_root.traverse_preorder |f| { f.assign_widths(&layout_ctx) }
do root_flow.traverse_preorder |f| { f.assign_widths(&layout_ctx) } do layout_root.traverse_postorder |f| { f.assign_height(&layout_ctx) }
do root_flow.traverse_postorder |f| { f.assign_height(&layout_ctx) }
let dlist = DVec(); let dlist = DVec();
let builder = dl::DisplayListBuilder { let builder = dl::DisplayListBuilder {
@ -98,7 +92,7 @@ fn LayoutTask(render_task: RenderTask, image_cache_task: ImageCacheTask) -> Layo
}; };
// TODO: set options on the builder before building // TODO: set options on the builder before building
// TODO: be smarter about what needs painting // TODO: be smarter about what needs painting
root_flow.build_display_list(&builder, &copy root_flow.data.position, &dlist); layout_root.build_display_list(&builder, &copy layout_root.data.position, &dlist);
render_task.send(render_task::RenderMsg(dlist)); render_task.send(render_task::RenderMsg(dlist));
} }
} }