servo: Refactor the Flow type to save memory and allow upcasting and downcasting more naturally

This commit is contained in:
Patrick Walton 2013-05-06 15:41:47 -07:00
parent 5749ffcf7a
commit 1d7a3f916d
10 changed files with 603 additions and 511 deletions

View file

@ -71,7 +71,7 @@ pub enum NodeTypeId {
pub struct LayoutData { pub struct LayoutData {
style: Option<CompleteSelectResults>, style: Option<CompleteSelectResults>,
flow: Option<@mut FlowContext>, flow: Option<FlowContext>,
} }
impl LayoutData { impl LayoutData {

View file

@ -2,12 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Block layout. //! CSS block layout.
use layout::box::{RenderBox}; use layout::box::{RenderBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods}; use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
use layout::flow::{BlockFlow, FlowContext, InlineBlockFlow, RootFlow}; use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow, RootFlow};
use layout::inline::InlineLayout; use layout::inline::InlineLayout;
use au = gfx::geometry; use au = gfx::geometry;
@ -18,23 +18,33 @@ use gfx::display_list::DisplayList;
use gfx::geometry::Au; use gfx::geometry::Au;
pub struct BlockFlowData { pub struct BlockFlowData {
/// Data common to all flows.
common: FlowData,
/// The associated render box.
box: Option<@mut RenderBox> box: Option<@mut RenderBox>
} }
pub fn BlockFlowData() -> BlockFlowData { impl BlockFlowData {
pub fn new(common: FlowData) -> BlockFlowData {
BlockFlowData { BlockFlowData {
box: None common: common,
box: None,
}
} }
} }
/// NB: These are part of FlowContext, not part of BlockFlowData, because the root flow calls these
/// as well. It is not clear to me whether this needs to be the case, or whether `RootFlow` can be
/// merged into this.
pub trait BlockLayout { pub trait BlockLayout {
fn starts_block_flow(&self) -> bool; fn starts_block_flow(&self) -> bool;
fn with_block_box(@mut self, &fn(box: &@mut RenderBox) -> ()) -> (); fn with_block_box(&self, &fn(box: &@mut RenderBox) -> ()) -> ();
fn bubble_widths_block(@mut self, ctx: &LayoutContext); fn bubble_widths_block(&self, ctx: &LayoutContext);
fn assign_widths_block(@mut self, ctx: &LayoutContext); fn assign_widths_block(&self, ctx: &LayoutContext);
fn assign_height_block(@mut self, ctx: &LayoutContext); fn assign_height_block(&self, ctx: &LayoutContext);
fn build_display_list_block(@mut self, fn build_display_list_block(&self,
a: &DisplayListBuilder, a: &DisplayListBuilder,
b: &Rect<Au>, b: &Rect<Au>,
c: &Point2D<Au>, c: &Point2D<Au>,
@ -49,17 +59,21 @@ impl BlockLayout for FlowContext {
} }
} }
/* Get the current flow's corresponding block box, if it exists, and do something with it. /// Get the current flow's corresponding block box, if it exists, and do something with it.
This works on both BlockFlow and RootFlow, since they are mostly the same. */ /// This works on both BlockFlow and RootFlow, since they are mostly the same.
fn with_block_box(@mut self, cb: &fn(box: &@mut RenderBox) -> ()) -> () { fn with_block_box(&self, callback: &fn(box: &@mut RenderBox) -> ()) -> () {
match *self { match *self {
BlockFlow(*) => { BlockFlow(*) => {
let box = self.block().box; let box = self.block().box;
for box.each |b| { cb(b); } for box.each |b| {
callback(b);
}
}, },
RootFlow(*) => { RootFlow(*) => {
let mut box = self.root().box; let mut box = self.root().box;
for box.each |b| { cb(b); } for box.each |b| {
callback(b);
}
}, },
_ => fail!(fmt!("Tried to do something with_block_box(), but this is a %?", self)) _ => fail!(fmt!("Tried to do something with_block_box(), but this is a %?", self))
} }
@ -74,7 +88,7 @@ impl BlockLayout for FlowContext {
/* TODO: floats */ /* TODO: floats */
/* TODO: absolute contexts */ /* TODO: absolute contexts */
/* TODO: inline-blocks */ /* TODO: inline-blocks */
fn bubble_widths_block(@mut self, ctx: &LayoutContext) { fn bubble_widths_block(&self, ctx: &LayoutContext) {
assert!(self.starts_block_flow()); assert!(self.starts_block_flow());
let mut min_width = Au(0); let mut min_width = Au(0);
@ -84,8 +98,10 @@ impl BlockLayout for FlowContext {
for self.each_child |child_ctx| { for self.each_child |child_ctx| {
assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
min_width = au::max(min_width, child_ctx.d().min_width); do child_ctx.with_common_info |child_info| {
pref_width = au::max(pref_width, child_ctx.d().pref_width); min_width = au::max(min_width, child_info.min_width);
pref_width = au::max(pref_width, child_info.pref_width);
}
} }
/* if not an anonymous block context, add in block box's widths. /* if not an anonymous block context, add in block box's widths.
@ -95,8 +111,10 @@ impl BlockLayout for FlowContext {
pref_width = pref_width.add(&box.get_pref_width(ctx)); pref_width = pref_width.add(&box.get_pref_width(ctx));
} }
self.d().min_width = min_width; do self.with_common_info |info| {
self.d().pref_width = pref_width; info.min_width = min_width;
info.pref_width = pref_width;
}
} }
/* Recursively (top-down) determines the actual width of child /* Recursively (top-down) determines the actual width of child
@ -106,10 +124,10 @@ impl BlockLayout for FlowContext {
Dual boxes consume some width first, and the remainder is assigned to Dual boxes consume some width first, and the remainder is assigned to
all child (block) contexts. */ all child (block) contexts. */
fn assign_widths_block(@mut self, _ctx: &LayoutContext) { fn assign_widths_block(&self, _ctx: &LayoutContext) {
assert!(self.starts_block_flow()); assert!(self.starts_block_flow());
let mut remaining_width = self.d().position.size.width; let mut remaining_width = self.with_common_info(|info| info.position.size.width);
let mut _right_used = Au(0); let mut _right_used = Au(0);
let mut left_used = Au(0); let mut left_used = Au(0);
@ -123,22 +141,28 @@ impl BlockLayout for FlowContext {
for self.each_child |child_ctx| { for self.each_child |child_ctx| {
assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
child_ctx.d().position.origin.x = left_used; do child_ctx.with_common_info |child_info| {
child_ctx.d().position.size.width = remaining_width; child_info.position.origin.x = left_used;
child_info.position.size.width = remaining_width;
}
} }
} }
fn assign_height_block(@mut self, _ctx: &LayoutContext) { fn assign_height_block(&self, _ctx: &LayoutContext) {
assert!(self.starts_block_flow()); assert!(self.starts_block_flow());
let mut cur_y = Au(0); let mut cur_y = Au(0);
for self.each_child |child_ctx| { for self.each_child |child_ctx| {
child_ctx.d().position.origin.y = cur_y; do child_ctx.with_common_info |child_info| {
cur_y += child_ctx.d().position.size.height; child_info.position.origin.y = cur_y;
cur_y += child_info.position.size.height;
}
} }
self.d().position.size.height = cur_y; do self.with_common_info |info| {
info.position.size.height = cur_y;
}
let _used_top = Au(0); let _used_top = Au(0);
let _used_bot = Au(0); let _used_bot = Au(0);
@ -150,9 +174,11 @@ impl BlockLayout for FlowContext {
} }
} }
fn build_display_list_block(@mut self, builder: &DisplayListBuilder, dirty: &Rect<Au>, fn build_display_list_block(&self,
offset: &Point2D<Au>, list: &Cell<DisplayList>) { builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
assert!(self.starts_block_flow()); assert!(self.starts_block_flow());
// add box that starts block context // add box that starts block context

View file

@ -74,7 +74,7 @@ pub struct RenderBoxData {
node: AbstractNode, node: AbstractNode,
/* reference to containing flow context, which this box /* reference to containing flow context, which this box
participates in */ participates in */
ctx : @mut FlowContext, ctx: FlowContext,
/* position of this box relative to owning flow */ /* position of this box relative to owning flow */
position: Rect<Au>, position: Rect<Au>,
font_size: Length, font_size: Length,
@ -103,7 +103,7 @@ pub enum SplitBoxResult {
SplitDidNotFit(Option<@mut RenderBox>, Option<@mut RenderBox>) SplitDidNotFit(Option<@mut RenderBox>, Option<@mut RenderBox>)
} }
pub fn RenderBoxData(node: AbstractNode, ctx: @mut FlowContext, id: int) -> RenderBoxData { pub fn RenderBoxData(node: AbstractNode, ctx: FlowContext, id: int) -> RenderBoxData {
RenderBoxData { RenderBoxData {
node : node, node : node,
ctx : ctx, ctx : ctx,

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** Creates CSS boxes from a DOM. */ //! Creates CSS boxes from a DOM tree.
use dom::element::*; use dom::element::*;
use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId}; use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
@ -16,12 +16,12 @@ use layout::inline::{InlineFlowData, InlineLayout};
use layout::root::RootFlowData; use layout::root::RootFlowData;
use gfx::image::holder::ImageHolder; use gfx::image::holder::ImageHolder;
use servo_util::range::Range;
use newcss::values::{CSSDisplay, CSSDisplayBlock, CSSDisplayInline, CSSDisplayInlineBlock}; use newcss::values::{CSSDisplay, CSSDisplayBlock, CSSDisplayInline, CSSDisplayInlineBlock};
use newcss::values::{CSSDisplayNone}; use newcss::values::{CSSDisplayNone};
use servo_util::range::Range;
pub struct LayoutTreeBuilder { pub struct LayoutTreeBuilder {
root_flow: Option<@mut FlowContext>, root_flow: Option<FlowContext>,
next_bid: int, next_bid: int,
next_cid: int next_cid: int
} }
@ -39,7 +39,7 @@ pub impl LayoutTreeBuilder {
// helper object for building the initial box list and making the // helper object for building the initial box list and making the
// mapping between DOM nodes and boxes. // mapping between DOM nodes and boxes.
struct BoxGenerator { struct BoxGenerator {
flow: @mut FlowContext, flow: FlowContext,
range_stack: ~[uint], range_stack: ~[uint],
} }
@ -58,7 +58,9 @@ priv fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay {
};*/ };*/
let resolved = CSSDisplayInline; let resolved = CSSDisplayInline;
if (resolved == CSSDisplayNone) { return resolved; } if resolved == CSSDisplayNone {
return resolved;
}
match node.type_id() { match node.type_id() {
DoctypeNodeTypeId | CommentNodeTypeId => CSSDisplayNone, DoctypeNodeTypeId | CommentNodeTypeId => CSSDisplayNone,
@ -81,7 +83,7 @@ priv fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay {
} }
impl BoxGenerator { impl BoxGenerator {
fn new(flow: @mut FlowContext) -> BoxGenerator { fn new(flow: FlowContext) -> BoxGenerator {
debug!("Creating box generator for flow: %s", flow.debug_str()); debug!("Creating box generator for flow: %s", flow.debug_str());
BoxGenerator { BoxGenerator {
flow: flow, flow: flow,
@ -103,99 +105,106 @@ impl BoxGenerator {
None None
} }
pub fn push_node(@mut self, ctx: &LayoutContext, builder: &mut LayoutTreeBuilder, node: AbstractNode) { pub fn push_node(&mut self,
debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.d().id, node.debug_str()); ctx: &LayoutContext,
builder: &mut LayoutTreeBuilder,
node: AbstractNode) {
debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.id(), node.debug_str());
// first, determine the box type, based on node characteristics // first, determine the box type, based on node characteristics
let simulated_display = simulate_UA_display_rules(node); let simulated_display = simulate_UA_display_rules(node);
// TODO: remove this once UA styles work // TODO: remove this once UA styles work
let box_type = builder.decide_box_type(node, simulated_display); let box_type = builder.decide_box_type(node, simulated_display);
debug!("BoxGenerator[f%d]: point a", self.flow.d().id); debug!("BoxGenerator[f%d]: point a", self.flow.id());
// depending on flow, make a box for this node. // depending on flow, make a box for this node.
match self.flow { match self.flow {
@InlineFlow(*) => { InlineFlow(inline) => {
let node_range_start = match self.flow { let mut inline = &mut *inline;
@InlineFlow(*) => { let node_range_start = inline.boxes.len();
let inline_flow = self.flow.inline();
inline_flow.boxes.len()
}
_ => 0
};
self.range_stack.push(node_range_start); self.range_stack.push(node_range_start);
// if a leaf, make a box. // if a leaf, make a box.
if node.is_leaf() { if node.is_leaf() {
let new_box = builder.make_box(ctx, box_type, node, self.flow); let new_box = builder.make_box(ctx, box_type, node, self.flow);
let boxes = &mut self.flow.inline().boxes; inline.boxes.push(new_box);
boxes.push(new_box);
} else if self.inline_spacers_needed_for_node(node) { } else if self.inline_spacers_needed_for_node(node) {
// else, maybe make a spacer for "left" margin, border, padding // else, maybe make a spacer for "left" margin, border, padding
for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each
|spacer: &@mut RenderBox| { |spacer: &@mut RenderBox| {
let boxes = &mut self.flow.inline().boxes; inline.boxes.push(*spacer);
boxes.push(*spacer);
} }
} }
// TODO: cases for inline-block, etc. // TODO: cases for inline-block, etc.
}, },
@BlockFlow(*) => { BlockFlow(*) => {
debug!("BoxGenerator[f%d]: point b", self.flow.d().id); do self.flow.with_common_info |flow_info| {
debug!("BoxGenerator[f%d]: point b", flow_info.id);
let new_box = builder.make_box(ctx, box_type, node, self.flow); let new_box = builder.make_box(ctx, box_type, node, self.flow);
debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)", debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)",
self.flow.d().id, new_box.d().id, node.debug_str()); flow_info.id,
new_box.d().id,
node.debug_str());
assert!(self.flow.block().box.is_none()); assert!(self.flow.block().box.is_none());
//XXXjdm We segfault when returning without this temporary. //XXXjdm We segfault when returning without this temporary.
let block = self.flow.block(); let block = self.flow.block();
block.box = Some(new_box); block.box = Some(new_box);
}
}, },
@RootFlow(*) => { RootFlow(*) => {
debug!("BoxGenerator[f%d]: point c", self.flow.d().id); do self.flow.with_common_info |info| {
debug!("BoxGenerator[f%d]: point c", info.id);
let new_box = builder.make_box(ctx, box_type, node, self.flow); let new_box = builder.make_box(ctx, box_type, node, self.flow);
debug!("BoxGenerator[f%d]: (node is: %s)", self.flow.d().id, node.debug_str()); debug!("BoxGenerator[f%d]: (node is: %s)", info.id, node.debug_str());
debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)", debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)",
self.flow.d().id, new_box.d().id, node.debug_str()); info.id,
new_box.d().id,
node.debug_str());
assert!(self.flow.root().box.is_none()); assert!(self.flow.root().box.is_none());
//XXXjdm We segfault when returning without this temporary. //XXXjdm We segfault when returning without this temporary.
let root = self.flow.root(); let root = self.flow.root();
root.box = Some(new_box); root.box = Some(new_box);
}
}, },
_ => { warn!("push_node() not implemented for flow f%d", self.flow.d().id) } _ => {
do self.flow.with_common_info |flow_info| {
warn!("push_node() not implemented for flow f%d", flow_info.id)
}
}
} }
} }
pub fn pop_node(&mut self, ctx: &LayoutContext, _builder: &LayoutTreeBuilder, node: AbstractNode) { pub fn pop_node(&mut self,
debug!("BoxGenerator[f%d]: popping node: %s", self.flow.d().id, node.debug_str()); ctx: &LayoutContext,
_builder: &LayoutTreeBuilder,
node: AbstractNode) {
debug!("BoxGenerator[f%d]: popping node: %s", self.flow.id(), node.debug_str());
match self.flow { match self.flow {
@InlineFlow(*) => { InlineFlow(inline) => {
let inline = &mut *inline;
if self.inline_spacers_needed_for_node(node) { if self.inline_spacers_needed_for_node(node) {
// if this non-leaf box generates extra horizontal // If this non-leaf box generates extra horizontal spacing, add a SpacerBox for
// spacing, add a SpacerBox for it. // it.
for self.make_inline_spacer_for_node_side(ctx, node, LogicalAfter).each |spacer: &@mut RenderBox| { let result = self.make_inline_spacer_for_node_side(ctx, node, LogicalAfter);
for result.each |spacer| {
let boxes = &mut self.flow.inline().boxes; let boxes = &mut self.flow.inline().boxes;
boxes.push(*spacer); boxes.push(*spacer);
} }
} }
let mut node_range: Range = Range::new(self.range_stack.pop(), 0); let mut node_range: Range = Range::new(self.range_stack.pop(), 0);
let inline_flow = self.flow.inline(); // FIXME: borrow checker workaround node_range.extend_to(inline.boxes.len());
node_range.extend_to(inline_flow.boxes.len());
assert!(node_range.length() > 0); assert!(node_range.length() > 0);
debug!("BoxGenerator: adding element range=%?", node_range); debug!("BoxGenerator: adding element range=%?", node_range);
let elems = &mut inline_flow.elems; inline.elems.add_mapping(node, &node_range);
elems.add_mapping(node, &node_range);
}, },
@BlockFlow(*) | @RootFlow(*) => { BlockFlow(*) | RootFlow(*) => assert!(self.range_stack.len() == 0),
assert!(self.range_stack.len() == 0); _ => warn!("pop_node() not implemented for flow %?", self.flow.id()),
},
_ => {
let d = self.flow.d(); // FIXME: borrow checker workaround
warn!("pop_node() not implemented for flow %?", d.id)
}
} }
} }
} }
@ -207,7 +216,11 @@ struct BuilderContext {
impl BuilderContext { impl BuilderContext {
fn new(collector: @mut BoxGenerator) -> BuilderContext { fn new(collector: @mut BoxGenerator) -> BuilderContext {
{
let collector = &mut *collector;
debug!("Creating new BuilderContext for flow: %s", collector.flow.debug_str()); debug!("Creating new BuilderContext for flow: %s", collector.flow.debug_str());
}
BuilderContext { BuilderContext {
default_collector: collector, default_collector: collector,
inline_collector: None, inline_collector: None,
@ -219,11 +232,16 @@ impl BuilderContext {
copy self copy self
} }
priv fn attach_child_flow(&self, child: @mut FlowContext) { priv fn attach_child_flow(&self, child: FlowContext) {
let d = self.default_collector.flow.d(); // FIXME: borrow checker workaround let default_collector = &mut *self.default_collector;
let cd = child.d(); // FIXME: borrow checker workaround do default_collector.flow.with_common_info |flow_info| {
debug!("BuilderContext: Adding child flow f%? of f%?", d.id, cd.id); do child.with_common_info |child_flow_info| {
self.default_collector.flow.add_child(child); debug!("BuilderContext: Adding child flow f%? of f%?",
flow_info.id,
child_flow_info.id);
default_collector.flow.add_child(child);
}
}
} }
priv fn create_child_flow_of_type(&self, priv fn create_child_flow_of_type(&self,
@ -236,7 +254,8 @@ impl BuilderContext {
BuilderContext::new(@mut BoxGenerator::new(new_flow)) BuilderContext::new(@mut BoxGenerator::new(new_flow))
} }
priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext { priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode)
-> BuilderContext {
debug!("BuilderContext: making new inline collector flow"); debug!("BuilderContext: making new inline collector flow");
let new_flow = builder.make_flow(Flow_Inline, node); let new_flow = builder.make_flow(Flow_Inline, node);
let new_generator = @mut BoxGenerator::new(new_flow); let new_generator = @mut BoxGenerator::new(new_flow);
@ -247,7 +266,8 @@ impl BuilderContext {
BuilderContext::new(new_generator) BuilderContext::new(new_generator)
} }
priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext { priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode)
-> BuilderContext {
match copy self.inline_collector { match copy self.inline_collector {
Some(collector) => BuilderContext::new(collector), Some(collector) => BuilderContext::new(collector),
None => self.make_inline_collector(builder, node) None => self.make_inline_collector(builder, node)
@ -261,9 +281,7 @@ impl BuilderContext {
// returns a context for the current node, or None if the document subtree rooted // returns a context for the current node, or None if the document subtree rooted
// by the node should not generate a layout tree. For example, nodes with style 'display:none' // by the node should not generate a layout tree. For example, nodes with style 'display:none'
// should just not generate any flows or boxes. // should just not generate any flows or boxes.
fn containing_context_for_node(&mut self, fn containing_context_for_node(&mut self, node: AbstractNode, builder: &mut LayoutTreeBuilder)
node: AbstractNode,
builder: &mut LayoutTreeBuilder)
-> Option<BuilderContext> { -> Option<BuilderContext> {
// TODO: remove this once UA styles work // TODO: remove this once UA styles work
// TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7) // TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7)
@ -273,7 +291,7 @@ impl BuilderContext {
}; };
let containing_context = match (simulated_display, self.default_collector.flow) { let containing_context = match (simulated_display, self.default_collector.flow) {
(CSSDisplayBlock, @RootFlow(*)) => { (CSSDisplayBlock, RootFlow(*)) => {
// If this is the root node, then use the root flow's // If this is the root node, then use the root flow's
// context. Otherwise, make a child block context. // context. Otherwise, make a child block context.
match node.parent_node() { match node.parent_node() {
@ -281,14 +299,14 @@ impl BuilderContext {
None => { self.clone() }, None => { self.clone() },
} }
}, },
(CSSDisplayBlock, @BlockFlow(*)) => { (CSSDisplayBlock, BlockFlow(*)) => {
self.clear_inline_collector(); self.clear_inline_collector();
self.create_child_flow_of_type(Flow_Block, builder, node) self.create_child_flow_of_type(Flow_Block, builder, node)
}, },
(CSSDisplayInline, @InlineFlow(*)) => self.clone(), (CSSDisplayInline, InlineFlow(*)) => self.clone(),
(CSSDisplayInlineBlock, @InlineFlow(*)) => self.clone(), (CSSDisplayInlineBlock, InlineFlow(*)) => self.clone(),
(CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder, node), (CSSDisplayInline, BlockFlow(*)) => self.get_inline_collector(builder, node),
(CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder, node), (CSSDisplayInlineBlock, BlockFlow(*)) => self.get_inline_collector(builder, node),
_ => self.clone() _ => self.clone()
}; };
@ -330,12 +348,14 @@ pub impl LayoutTreeBuilder {
// eventually be elided or split, but the mapping between // eventually be elided or split, but the mapping between
// nodes and FlowContexts should not change during layout. // nodes and FlowContexts should not change during layout.
let flow = &mut this_ctx.default_collector.flow; let flow = &mut this_ctx.default_collector.flow;
for flow.each_child |child_flow: @mut FlowContext| { for flow.each_child |child_flow| {
let node = child_flow.d().node; do child_flow.with_common_info |child_flow_info| {
let node = child_flow_info.node;
assert!(node.has_layout_data()); assert!(node.has_layout_data());
node.layout_data().flow = Some(child_flow); node.layout_data().flow = Some(child_flow);
} }
} }
}
// Fixup any irregularities such as: // Fixup any irregularities such as:
// //
@ -347,14 +367,14 @@ pub impl LayoutTreeBuilder {
// beginning or end of a block flow. Otherwise, the whitespace // beginning or end of a block flow. Otherwise, the whitespace
// might affect whitespace collapsing with adjacent text. // might affect whitespace collapsing with adjacent text.
fn simplify_children_of_flow(&self, _: &LayoutContext, parent_ctx: &BuilderContext) { fn simplify_children_of_flow(&self, _: &LayoutContext, parent_ctx: &BuilderContext) {
match *parent_ctx.default_collector.flow { match parent_ctx.default_collector.flow {
InlineFlow(*) => { InlineFlow(*) => {
let mut found_child_inline = false; let mut found_child_inline = false;
let mut found_child_block = false; let mut found_child_block = false;
let flow = &mut parent_ctx.default_collector.flow; let flow = &mut parent_ctx.default_collector.flow;
for flow.each_child |child_ctx: @mut FlowContext| { for flow.each_child |child_ctx| {
match *child_ctx { match child_ctx {
InlineFlow(*) | InlineBlockFlow(*) => found_child_inline = true, InlineFlow(*) | InlineBlockFlow(*) => found_child_inline = true,
BlockFlow(*) => found_child_block = true, BlockFlow(*) => found_child_block = true,
_ => {} _ => {}
@ -370,34 +390,32 @@ pub impl LayoutTreeBuilder {
// of its RenderBox or FlowContext children, and possibly keep alive other junk // of its RenderBox or FlowContext children, and possibly keep alive other junk
let parent_flow = parent_ctx.default_collector.flow; let parent_flow = parent_ctx.default_collector.flow;
// FIXME: Workaround for the borrow check. let (first_child, last_child) =
let (first_child, last_child) = { do parent_flow.with_common_info |parent_flow_info| {
let parent_flow: &mut FlowContext = parent_flow; (parent_flow_info.first_child, parent_flow_info.last_child)
let parent_flow_data = parent_flow.d();
(parent_flow_data.first_child, parent_flow_data.last_child)
}; };
// check first/last child for whitespace-ness // check first/last child for whitespace-ness
for first_child.each |first_flow: &@mut FlowContext| { for first_child.each |first_flow| {
if first_flow.starts_inline_flow() { if first_flow.starts_inline_flow() {
let boxes = &mut first_flow.inline().boxes; let boxes = &mut first_flow.inline().boxes;
if boxes.len() == 1 && boxes[0].is_whitespace_only() { if boxes.len() == 1 && boxes[0].is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only first child flow \ debug!("LayoutTreeBuilder: pruning whitespace-only first child flow \
f%d from parent f%d", f%d from parent f%d",
first_flow.d().id, first_flow.id(),
parent_flow.d().id); parent_flow.id());
parent_flow.remove_child(*first_flow); parent_flow.remove_child(*first_flow);
} }
} }
} }
for last_child.each |last_flow: &@mut FlowContext| { for last_child.each |last_flow| {
if last_flow.starts_inline_flow() { if last_flow.starts_inline_flow() {
let boxes = &mut last_flow.inline().boxes; let boxes = &mut last_flow.inline().boxes;
if boxes.len() == 1 && boxes.last().is_whitespace_only() { if boxes.len() == 1 && boxes.last().is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only last child flow \ debug!("LayoutTreeBuilder: pruning whitespace-only last child flow \
f%d from parent f%d", f%d from parent f%d",
last_flow.d().id, last_flow.id(),
parent_flow.d().id); parent_flow.id());
parent_flow.remove_child(*last_flow); parent_flow.remove_child(*last_flow);
} }
} }
@ -407,15 +425,14 @@ pub impl LayoutTreeBuilder {
} }
} }
fn fixup_split_inline(&self, _: @mut FlowContext) { fn fixup_split_inline(&self, _: FlowContext) {
// TODO: finish me. // TODO: finish me.
fail!(~"TODO: handle case where an inline is split by a block") fail!(~"TODO: handle case where an inline is split by a block")
} }
/** entry point for box creation. Should only be /// Entry point for box creation. Should only be called on the root DOM element.
called on root DOM element. */
fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode) fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode)
-> Result<@mut FlowContext, ()> { -> Result<FlowContext, ()> {
let new_flow = self.make_flow(Flow_Root, root); let new_flow = self.make_flow(Flow_Root, root);
let new_generator = @mut BoxGenerator::new(new_flow); let new_generator = @mut BoxGenerator::new(new_flow);
let mut root_ctx = BuilderContext::new(new_generator); let mut root_ctx = BuilderContext::new(new_generator);
@ -425,52 +442,45 @@ pub impl LayoutTreeBuilder {
return Ok(new_flow) return Ok(new_flow)
} }
fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> @mut FlowContext { /// Creates a flow of the given type for the supplied node.
let data = FlowData::new(self.next_flow_id(), node); fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> FlowContext {
let ret = match ty { let info = FlowData::new(self.next_flow_id(), node);
Flow_Absolute => @mut AbsoluteFlow(data), let result = match ty {
Flow_Block => @mut BlockFlow(data, BlockFlowData()), Flow_Absolute => AbsoluteFlow(@mut info),
Flow_Float => @mut FloatFlow(data), Flow_Block => BlockFlow(@mut BlockFlowData::new(info)),
Flow_InlineBlock => @mut InlineBlockFlow(data), Flow_Float => FloatFlow(@mut info),
Flow_Inline => @mut InlineFlow(data, InlineFlowData()), Flow_InlineBlock => InlineBlockFlow(@mut info),
Flow_Root => @mut RootFlow(data, RootFlowData()), Flow_Inline => InlineFlow(@mut InlineFlowData::new(info)),
Flow_Table => @mut TableFlow(data) Flow_Root => RootFlow(@mut RootFlowData::new(info)),
Flow_Table => TableFlow(@mut info),
}; };
debug!("LayoutTreeBuilder: created flow: %s", ret.debug_str()); debug!("LayoutTreeBuilder: created flow: %s", result.debug_str());
ret result
} }
/** /// Disambiguate between different methods here instead of inlining, since each case has very
disambiguate between different methods here instead of inlining, since each /// different complexity.
case has very different complexity
*/
fn make_box(&mut self, fn make_box(&mut self,
layout_ctx: &LayoutContext, layout_ctx: &LayoutContext,
ty: RenderBoxType, ty: RenderBoxType,
node: AbstractNode, node: AbstractNode,
ctx: @mut FlowContext) ctx: FlowContext)
-> @mut RenderBox { -> @mut RenderBox {
let ret = match ty { let result = match ty {
RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx), RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx),
RenderBox_Text => self.make_text_box(layout_ctx, node, ctx), RenderBox_Text => self.make_text_box(layout_ctx, node, ctx),
RenderBox_Image => self.make_image_box(layout_ctx, node, ctx), RenderBox_Image => self.make_image_box(layout_ctx, node, ctx),
}; };
debug!("LayoutTreeBuilder: created box: %s", ret.debug_str()); debug!("LayoutTreeBuilder: created box: %s", result.debug_str());
ret result
} }
fn make_generic_box(&mut self, fn make_generic_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext)
_: &LayoutContext,
node: AbstractNode,
ctx: @mut FlowContext)
-> @mut RenderBox { -> @mut RenderBox {
@mut GenericBox(RenderBoxData(copy node, ctx, self.next_box_id())) @mut GenericBox(RenderBoxData(copy node, ctx, self.next_box_id()))
} }
fn make_image_box(&mut self, fn make_image_box(&mut self, layout_ctx: &LayoutContext, node: AbstractNode, ctx: FlowContext)
layout_ctx: &LayoutContext,
node: AbstractNode,
ctx: @mut FlowContext)
-> @mut RenderBox { -> @mut RenderBox {
if !node.is_image_element() { if !node.is_image_element() {
fail!(~"WAT error: why couldn't we make an image box?"); fail!(~"WAT error: why couldn't we make an image box?");
@ -482,16 +492,14 @@ pub impl LayoutTreeBuilder {
layout_ctx.image_cache); layout_ctx.image_cache);
@mut ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder) @mut ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder)
} else { } else {
info!("Tried to make image box, but couldn't find image. Made generic box instead."); info!("Tried to make image box, but couldn't find image. Made generic box \
instead.");
self.make_generic_box(layout_ctx, node, ctx) self.make_generic_box(layout_ctx, node, ctx)
} }
} }
} }
fn make_text_box(&mut self, fn make_text_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext)
_: &LayoutContext,
node: AbstractNode,
ctx: @mut FlowContext)
-> @mut RenderBox { -> @mut RenderBox {
if !node.is_text() { if !node.is_text() {
fail!(~"WAT error: why couldn't we make a text box?"); fail!(~"WAT error: why couldn't we make a text box?");

View file

@ -29,17 +29,17 @@ pub struct DisplayListBuilder<'self> {
} }
pub trait FlowDisplayListBuilderMethods { pub trait FlowDisplayListBuilderMethods {
fn build_display_list(@mut self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Cell<DisplayList>); fn build_display_list(&self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Cell<DisplayList>);
fn build_display_list_for_child(@mut self, fn build_display_list_for_child(&self,
a: &DisplayListBuilder, a: &DisplayListBuilder,
b: @mut FlowContext, b: FlowContext,
c: &Rect<Au>, c: &Rect<Au>,
d: &Point2D<Au>, d: &Point2D<Au>,
e: &Cell<DisplayList>); e: &Cell<DisplayList>);
} }
impl FlowDisplayListBuilderMethods for FlowContext { impl FlowDisplayListBuilderMethods for FlowContext {
fn build_display_list(@mut self, fn build_display_list(&self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
dirty: &Rect<Au>, dirty: &Rect<Au>,
list: &Cell<DisplayList>) { list: &Cell<DisplayList>) {
@ -47,21 +47,21 @@ impl FlowDisplayListBuilderMethods for FlowContext {
self.build_display_list_recurse(builder, dirty, &zero, list); self.build_display_list_recurse(builder, dirty, &zero, list);
} }
fn build_display_list_for_child(@mut self, fn build_display_list_for_child(&self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
child_flow: @mut FlowContext, child_flow: FlowContext,
dirty: &Rect<Au>, offset: &Point2D<Au>, dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) { list: &Cell<DisplayList>) {
// adjust the dirty rect to child flow context coordinates // adjust the dirty rect to child flow context coordinates
let d = child_flow.d(); // FIXME: borrow checker workaround do child_flow.with_common_info |child_flow_info| {
let abs_flow_bounds = d.position.translate(offset); let abs_flow_bounds = child_flow_info.position.translate(offset);
let adj_offset = offset.add(&d.position.origin); let adj_offset = offset.add(&child_flow_info.position.origin);
debug!("build_display_list_for_child: rel=%?, abs=%?", debug!("build_display_list_for_child: rel=%?, abs=%?",
d.position, abs_flow_bounds); child_flow_info.position,
debug!("build_display_list_for_child: dirty=%?, offset=%?", abs_flow_bounds);
dirty, offset); debug!("build_display_list_for_child: dirty=%?, offset=%?", dirty, offset);
if dirty.intersects(&abs_flow_bounds) { if dirty.intersects(&abs_flow_bounds) {
debug!("build_display_list_for_child: intersected. recursing into child flow..."); debug!("build_display_list_for_child: intersected. recursing into child flow...");
@ -71,4 +71,5 @@ impl FlowDisplayListBuilderMethods for FlowContext {
} }
} }
} }
}

View file

@ -28,7 +28,7 @@ use dom::node::AbstractNode;
use layout::block::{BlockFlowData, BlockLayout}; use layout::block::{BlockFlowData, BlockLayout};
use layout::box::RenderBox; use layout::box::RenderBox;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::BoxedMutDebugMethods; use layout::debug::DebugMethods;
use layout::display_list_builder::DisplayListBuilder; use layout::display_list_builder::DisplayListBuilder;
use layout::inline::{InlineFlowData, InlineLayout}; use layout::inline::{InlineFlowData, InlineLayout};
use layout::root::{RootFlowData, RootLayout}; use layout::root::{RootFlowData, RootLayout};
@ -43,13 +43,13 @@ use gfx::geometry::Au;
/// The type of the formatting context and data specific to each context, such as line box /// The type of the formatting context and data specific to each context, such as line box
/// structures or float lists. /// structures or float lists.
pub enum FlowContext { pub enum FlowContext {
AbsoluteFlow(FlowData), AbsoluteFlow(@mut FlowData),
BlockFlow(FlowData, BlockFlowData), BlockFlow(@mut BlockFlowData),
FloatFlow(FlowData), FloatFlow(@mut FlowData),
InlineBlockFlow(FlowData), InlineBlockFlow(@mut FlowData),
InlineFlow(FlowData, InlineFlowData), InlineFlow(@mut InlineFlowData),
RootFlow(FlowData, RootFlowData), RootFlow(@mut RootFlowData),
TableFlow(FlowData) TableFlow(@mut FlowData),
} }
pub enum FlowContextType { pub enum FlowContextType {
@ -62,16 +62,18 @@ pub enum FlowContextType {
Flow_Table Flow_Table
} }
/* A particular kind of layout context. It manages the positioning of /// Data common to all flows.
render boxes within the context. */ ///
/// FIXME: We need a naming convention for pseudo-inheritance like this. How about
/// `CommonFlowInfo`?
pub struct FlowData { pub struct FlowData {
node: AbstractNode, node: AbstractNode,
parent: Option<@mut FlowContext>, parent: Option<FlowContext>,
first_child: Option<@mut FlowContext>, first_child: Option<FlowContext>,
last_child: Option<@mut FlowContext>, last_child: Option<FlowContext>,
prev_sibling: Option<@mut FlowContext>, prev_sibling: Option<FlowContext>,
next_sibling: Option<@mut FlowContext>, next_sibling: Option<FlowContext>,
/* TODO (Issue #87): debug only */ /* TODO (Issue #87): debug only */
id: int, id: int,
@ -105,31 +107,53 @@ impl FlowData {
} }
impl<'self> FlowContext { impl<'self> FlowContext {
pub fn d(&'self mut self) -> &'self mut FlowData { #[inline(always)]
unsafe { pub fn with_common_info<R>(&self, block: &fn(&mut FlowData) -> R) -> R {
match *self { match *self {
AbsoluteFlow(ref d) => cast::transmute(d), AbsoluteFlow(info) => block(info),
BlockFlow(ref d, _) => cast::transmute(d), BlockFlow(info) => {
FloatFlow(ref d) => cast::transmute(d), let info = &mut *info; // FIXME: Borrow check workaround.
InlineBlockFlow(ref d) => cast::transmute(d), block(&mut info.common)
InlineFlow(ref d, _) => cast::transmute(d),
RootFlow(ref d, _) => cast::transmute(d),
TableFlow(ref d) => cast::transmute(d)
} }
FloatFlow(info) => block(info),
InlineBlockFlow(info) => block(info),
InlineFlow(info) => {
let info = &mut *info; // FIXME: Borrow check workaround.
block(&mut info.common)
}
RootFlow(info) => {
let info = &mut *info; // FIXME: Borrow check workaround.
block(&mut info.common)
}
TableFlow(info) => block(info),
}
}
pub fn position(&self) -> Rect<Au> {
do self.with_common_info |common_info| {
common_info.position
}
}
/// Returns the ID of this flow.
#[inline(always)]
pub fn id(&self) -> int {
do self.with_common_info |info| {
info.id
} }
} }
/// Iterates over the immediate children of this flow. /// Iterates over the immediate children of this flow.
/// ///
/// TODO: Fold me into `util::tree`. /// TODO: Fold me into `util::tree`.
pub fn each_child(@mut self, f: &fn(@mut FlowContext) -> bool) { pub fn each_child(&self, f: &fn(FlowContext) -> bool) {
let mut current_opt = self.d().first_child; let mut current_opt = self.with_common_info(|info| info.first_child);
while !current_opt.is_none() { while !current_opt.is_none() {
let current = current_opt.get(); let current = current_opt.get();
if !f(current) { if !f(current) {
break; break;
} }
current_opt = current.d().next_sibling; current_opt = current.with_common_info(|info| info.next_sibling);
} }
} }
@ -137,173 +161,187 @@ impl<'self> FlowContext {
/// detached from the tree before calling this method. /// detached from the tree before calling this method.
/// ///
/// TODO: Fold me into `util::tree`. /// TODO: Fold me into `util::tree`.
pub fn add_child(@mut self, child: @mut FlowContext) { pub fn add_child(&self, child: FlowContext) {
let self_data = self.d(), child_data = child.d(); do self.with_common_info |self_info| {
do child.with_common_info |child_info| {
assert!(child_info.parent.is_none());
assert!(child_info.prev_sibling.is_none());
assert!(child_info.next_sibling.is_none());
assert!(child_data.parent.is_none()); match self_info.last_child {
assert!(child_data.prev_sibling.is_none());
assert!(child_data.next_sibling.is_none());
match self_data.last_child {
None => { None => {
self_data.first_child = Some(child); self_info.first_child = Some(child);
} }
Some(last_child) => { Some(last_child) => {
assert!(last_child.d().next_sibling.is_none()); do last_child.with_common_info |last_child_info| {
last_child.d().next_sibling = Some(child); assert!(last_child_info.next_sibling.is_none());
child_data.prev_sibling = Some(last_child); last_child_info.next_sibling = Some(child);
child_info.prev_sibling = Some(last_child);
}
} }
} }
self_data.last_child = Some(child); self_info.last_child = Some(child);
child_data.parent = Some(self); child_info.parent = Some(*self);
}
}
} }
/// Removes the given flow from the tree. /// Removes the given flow from the tree.
/// ///
/// TODO: Fold me into `util::tree`. /// TODO: Fold me into `util::tree`.
pub fn remove_child(@mut self, child: @mut FlowContext) { pub fn remove_child(&self, child: FlowContext) {
let self_data = self.d(), child_data = child.d(); do self.with_common_info |self_info| {
do child.with_common_info |child_info| {
assert!(child_info.parent.is_some());
assert!(child_data.parent.is_some()); match child_info.prev_sibling {
assert!(ptr::ref_eq(&*child_data.parent.get(), self)); None => self_info.first_child = child_info.next_sibling,
match child_data.prev_sibling {
None => self_data.first_child = child_data.next_sibling,
Some(prev_sibling) => { Some(prev_sibling) => {
prev_sibling.d().next_sibling = child_data.next_sibling; do prev_sibling.with_common_info |prev_sibling_info| {
child_data.prev_sibling = None; prev_sibling_info.next_sibling = child_info.next_sibling;
child_info.prev_sibling = None;
}
} }
} }
match child_data.next_sibling { match child_info.next_sibling {
None => self_data.last_child = child.d().prev_sibling, None => {
do child.with_common_info |child_info| {
self_info.last_child = child_info.prev_sibling;
}
}
Some(next_sibling) => { Some(next_sibling) => {
next_sibling.d().prev_sibling = Some(next_sibling); do next_sibling.with_common_info |next_sibling_info| {
child_data.next_sibling = None; next_sibling_info.prev_sibling = Some(next_sibling);
child_info.next_sibling = None;
}
} }
} }
child_data.parent = None; child_info.parent = None;
} }
pub fn inline(&'self mut self) -> &'self mut InlineFlowData {
match self {
&InlineFlow(_, ref i) => unsafe { cast::transmute(i) },
_ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.d().id))
} }
} }
pub fn block(&'self mut self) -> &'self mut BlockFlowData { pub fn inline(&self) -> @mut InlineFlowData {
match self { match *self {
&BlockFlow(_, ref mut b) => unsafe { cast::transmute(b) }, InlineFlow(info) => info,
_ => fail!(fmt!("Tried to access block data of non-block: f%d", self.d().id)) _ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id()))
} }
} }
pub fn root(&'self mut self) -> &'self mut RootFlowData { pub fn block(&self) -> @mut BlockFlowData {
match self { match *self {
&RootFlow(_, ref r) => unsafe { cast::transmute(r) }, BlockFlow(info) => info,
_ => fail!(fmt!("Tried to access root data of non-root: f%d", self.d().id)) _ => fail!(fmt!("Tried to access block data of non-block: f%d", self.id()))
} }
} }
pub fn bubble_widths(@mut self, ctx: &mut LayoutContext) { pub fn root(&self) -> @mut RootFlowData {
match self { match *self {
@BlockFlow(*) => self.bubble_widths_block(ctx), RootFlow(info) => info,
@InlineFlow(*) => self.bubble_widths_inline(ctx), _ => fail!(fmt!("Tried to access root data of non-root: f%d", self.id()))
@RootFlow(*) => self.bubble_widths_root(ctx),
_ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.d().id))
} }
} }
pub fn assign_widths(@mut self, ctx: &mut LayoutContext) { pub fn bubble_widths(&self, ctx: &mut LayoutContext) {
match self { match *self {
@BlockFlow(*) => self.assign_widths_block(ctx), BlockFlow(*) => self.bubble_widths_block(ctx),
@InlineFlow(*) => self.assign_widths_inline(ctx), InlineFlow(info) => info.bubble_widths_inline(ctx),
@RootFlow(*) => self.assign_widths_root(ctx), RootFlow(info) => info.bubble_widths_root(ctx),
_ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.d().id)) _ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id()))
} }
} }
pub fn assign_height(@mut self, ctx: &mut LayoutContext) { pub fn assign_widths(&self, ctx: &mut LayoutContext) {
match self { match *self {
@BlockFlow(*) => self.assign_height_block(ctx), BlockFlow(*) => self.assign_widths_block(ctx),
@InlineFlow(*) => self.assign_height_inline(ctx), InlineFlow(info) => info.assign_widths_inline(ctx),
@RootFlow(*) => self.assign_height_root(ctx), RootFlow(info) => info.assign_widths_root(ctx),
_ => fail!(fmt!("Tried to assign_height of flow: f%d", self.d().id)) _ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id()))
} }
} }
pub fn build_display_list_recurse(@mut self, pub fn assign_height(&self, ctx: &mut LayoutContext) {
match *self {
BlockFlow(*) => self.assign_height_block(ctx),
InlineFlow(info) => info.assign_height_inline(ctx),
RootFlow(info) => info.assign_height_root(ctx),
_ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id()))
}
}
pub fn build_display_list_recurse(&self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
dirty: &Rect<Au>, dirty: &Rect<Au>,
offset: &Point2D<Au>, offset: &Point2D<Au>,
list: &Cell<DisplayList>) { list: &Cell<DisplayList>) {
let d = self.d(); // FIXME: borrow checker workaround do self.with_common_info |info| {
debug!("FlowContext::build_display_list at %?: %s", d.position, self.debug_str()); debug!("FlowContext::build_display_list at %?: %s", info.position, self.debug_str());
}
match self { match *self {
@RootFlow(*) => self.build_display_list_root(builder, dirty, offset, list), RootFlow(info) => info.build_display_list_root(builder, dirty, offset, list),
@BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list), BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list),
@InlineFlow(*) => self.build_display_list_inline(builder, dirty, offset, list), InlineFlow(info) => info.build_display_list_inline(builder, dirty, offset, list),
_ => fail!(fmt!("Tried to build_display_list_recurse of flow: %?", self)) _ => fail!(fmt!("Tried to build_display_list_recurse of flow: %?", self))
} }
} }
// Actual methods that do not require much flow-specific logic // Actual methods that do not require much flow-specific logic
pub fn foldl_all_boxes<B:Copy>(&mut self, pub fn foldl_all_boxes<B:Copy>(&self, seed: B, cb: &fn(a: B, b: @mut RenderBox) -> B) -> B {
seed: B, match *self {
cb: &fn(a: B, b: @mut RenderBox) -> B) RootFlow(root) => {
-> B { let root = &mut *root;
match self {
&RootFlow(*) => {
let root = self.root(); // FIXME: borrow checker workaround
root.box.map_default(seed, |box| { cb(seed, *box) }) root.box.map_default(seed, |box| { cb(seed, *box) })
} }
&BlockFlow(*) => { BlockFlow(block) => {
let block = self.block(); // FIXME: borrow checker workaround let block = &mut *block;
block.box.map_default(seed, |box| { cb(seed, *box) }) block.box.map_default(seed, |box| { cb(seed, *box) })
} }
&InlineFlow(*) => { InlineFlow(inline) => {
let inline = self.inline(); // FIXME: borrow checker workaround let inline = &mut *inline;
inline.boxes.foldl(seed, |acc, box| { cb(*acc, *box) }) inline.boxes.foldl(seed, |acc, box| { cb(*acc, *box) })
} }
_ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self)) _ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self))
} }
} }
pub fn foldl_boxes_for_node<B:Copy>(&mut self, pub fn foldl_boxes_for_node<B:Copy>(&self,
node: AbstractNode, node: AbstractNode,
seed: B, seed: B,
cb: &fn(a: B, @mut RenderBox) -> B) callback: &fn(a: B, @mut RenderBox) -> B)
-> B { -> B {
do self.foldl_all_boxes(seed) |acc, box| { do self.foldl_all_boxes(seed) |acc, box| {
if box.d().node == node { cb(acc, box) } if box.d().node == node {
else { acc } callback(acc, box)
} else {
acc
}
} }
} }
pub fn iter_all_boxes(&mut self, cb: &fn(@mut RenderBox) -> bool) { pub fn iter_all_boxes(&self, cb: &fn(@mut RenderBox) -> bool) {
match self { match *self {
&RootFlow(*) => { RootFlow(root) => {
let root = self.root(); // FIXME: borrow checker workaround let root = &mut *root;
for root.box.each |box| { for root.box.each |box| {
if !cb(*box) { if !cb(*box) {
break; break;
} }
} }
} }
&BlockFlow(*) => { BlockFlow(block) => {
let block = self.block(); // FIXME: borrow checker workaround let block = &mut *block;
for block.box.each |box| { for block.box.each |box| {
if !cb(*box) { if !cb(*box) {
break; break;
} }
} }
} }
&InlineFlow(*) => { InlineFlow(inline) => {
let inline = self.inline(); // FIXME: borrow checker workaround let inline = &mut *inline;
for inline.boxes.each |box| { for inline.boxes.each |box| {
if !cb(*box) { if !cb(*box) {
break; break;
@ -314,10 +352,10 @@ impl<'self> FlowContext {
} }
} }
pub fn iter_boxes_for_node(&mut self, node: AbstractNode, cb: &fn(@mut RenderBox) -> bool) { pub fn iter_boxes_for_node(&self, node: AbstractNode, callback: &fn(@mut RenderBox) -> bool) {
for self.iter_all_boxes |box| { for self.iter_all_boxes |box| {
if box.d().node == node { if box.d().node == node {
if !cb(box) { if !callback(box) {
break; break;
} }
} }
@ -325,13 +363,13 @@ impl<'self> FlowContext {
} }
} }
impl BoxedMutDebugMethods for FlowContext { impl DebugMethods for FlowContext {
fn dump(@mut self) { fn dump(&self) {
self.dump_indent(0); self.dump_indent(0);
} }
/// Dumps the flow tree, for debugging, with indentation. /// Dumps the flow tree, for debugging, with indentation.
fn dump_indent(@mut self, indent: uint) { fn dump_indent(&self, indent: uint) {
let mut s = ~"|"; let mut s = ~"|";
for uint::range(0, indent) |_i| { for uint::range(0, indent) |_i| {
s += ~"---- "; s += ~"---- ";
@ -346,24 +384,26 @@ impl BoxedMutDebugMethods for FlowContext {
} }
} }
fn debug_str(@mut self) -> ~str { fn debug_str(&self) -> ~str {
let repr = match *self { let repr = match *self {
InlineFlow(*) => { InlineFlow(inline) => {
let inline = self.inline(); // FIXME: borrow checker workaround let inline = &mut *inline;
let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| { let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| {
fmt!("%s b%d", *s, box.d().id) fmt!("%s b%d", *s, box.d().id)
}); });
s += ~")"; s += ~")";
s s
}, },
BlockFlow(*) => { BlockFlow(block) => {
match self.block().box { let block = &mut *block;
match block.box {
Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id), Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id),
None => ~"BlockFlow", None => ~"BlockFlow",
} }
}, },
RootFlow(*) => { RootFlow(root) => {
match self.root().box { let root = &mut *root;
match root.box {
Some(box) => fmt!("RootFlo(box=b%d)", box.d().id), Some(box) => fmt!("RootFlo(box=b%d)", box.d().id),
None => ~"RootFlow", None => ~"RootFlow",
} }
@ -371,8 +411,9 @@ impl BoxedMutDebugMethods for FlowContext {
_ => ~"(Unknown flow)" _ => ~"(Unknown flow)"
}; };
let d = self.d(); // FIXME: borrow checker workaround do self.with_common_info |info| {
fmt!("f%? %?", d.id, repr) fmt!("f%? %?", info.id, repr)
}
} }
} }

View file

@ -2,26 +2,27 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use core;
use core::cell::Cell; use core::cell::Cell;
use core;
use dom::node::AbstractNode; use dom::node::AbstractNode;
use layout::box::*; use layout::box::*;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods}; use layout::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods};
use layout::display_list_builder::DisplayListBuilder; use layout::display_list_builder::DisplayListBuilder;
use layout::flow::{FlowContext, InlineFlow}; use layout::flow::{FlowContext, FlowData, InlineFlow};
use layout::text::{UnscannedMethods, adapt_textbox_with_range}; use layout::text::{UnscannedMethods, adapt_textbox_with_range};
use core::util;
use geom::{Point2D, Rect, Size2D}; use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use gfx::geometry::Au; use gfx::geometry::Au;
use gfx::image::holder; use gfx::image::holder;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use gfx::text::util::*; use gfx::text::util::*;
use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft};
use newcss::values::{CSSTextAlignRight};
use servo_util::range::Range; use servo_util::range::Range;
use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft, CSSTextAlignRight};
use std::deque::Deque; use std::deque::Deque;
use core::util;
/* /*
Lineboxes are represented as offsets into the child list, rather than Lineboxes are represented as offsets into the child list, rather than
@ -81,7 +82,9 @@ pub impl ElementMapping {
do self.entries.eachi |i, nr| { cb(i, nr) } do self.entries.eachi |i, nr| { cb(i, nr) }
} }
fn repair_for_box_changes(&mut self, old_boxes: &[@mut RenderBox], new_boxes: &[@mut RenderBox]) { fn repair_for_box_changes(&mut self,
old_boxes: &[@mut RenderBox],
new_boxes: &[@mut RenderBox]) {
let entries = &mut self.entries; let entries = &mut self.entries;
debug!("--- Old boxes: ---"); debug!("--- Old boxes: ---");
@ -166,34 +169,30 @@ priv impl TextRunScanner {
} }
priv impl TextRunScanner { priv impl TextRunScanner {
fn scan_for_runs(&mut self, ctx: &mut LayoutContext, flow: @mut FlowContext) { fn scan_for_runs(&mut self, ctx: &mut LayoutContext, flow: FlowContext) {
let inline = flow.inline(); let inline = &mut *flow.inline();
assert!(inline.boxes.len() > 0); assert!(inline.boxes.len() > 0);
debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len());
let in_boxes = &mut flow.inline().boxes;
//do boxes.swap |in_boxes| {
debug!("TextRunScanner: scanning %u boxes for text runs...", in_boxes.len());
let mut out_boxes = ~[]; let mut out_boxes = ~[];
for uint::range(0, inline.boxes.len()) |box_i| {
for uint::range(0, in_boxes.len()) |box_i| { debug!("TextRunScanner: considering box: %?", inline.boxes[box_i].debug_str());
debug!("TextRunScanner: considering box: %?", in_boxes[box_i].debug_str()); if box_i > 0 && !can_coalesce_text_nodes(inline.boxes, box_i-1, box_i) {
if box_i > 0 && !can_coalesce_text_nodes(*in_boxes, box_i-1, box_i) { self.flush_clump_to_list(ctx, flow, inline.boxes, &mut out_boxes);
self.flush_clump_to_list(ctx, flow, *in_boxes, &mut out_boxes);
} }
self.clump.extend_by(1); self.clump.extend_by(1);
} }
// handle remaining clumps // handle remaining clumps
if self.clump.length() > 0 { if self.clump.length() > 0 {
self.flush_clump_to_list(ctx, flow, *in_boxes, &mut out_boxes); self.flush_clump_to_list(ctx, flow, inline.boxes, &mut out_boxes);
} }
debug!("TextRunScanner: swapping out boxes."); debug!("TextRunScanner: swapping out boxes.");
// swap out old and new box list of flow, by supplying
// temp boxes as return value to boxes.swap |...|
util::swap(in_boxes, &mut out_boxes);
//}
// helper functions // Swap out the old and new box list of the flow.
inline.boxes = out_boxes;
// A helper function.
fn can_coalesce_text_nodes(boxes: &[@mut RenderBox], left_i: uint, right_i: uint) -> bool { fn can_coalesce_text_nodes(boxes: &[@mut RenderBox], left_i: uint, right_i: uint) -> bool {
assert!(left_i < boxes.len()); assert!(left_i < boxes.len());
assert!(right_i > 0 && right_i < boxes.len()); assert!(right_i > 0 && right_i < boxes.len());
@ -221,7 +220,7 @@ priv impl TextRunScanner {
// boxes are appended, the caller swaps the flow's box list. // boxes are appended, the caller swaps the flow's box list.
fn flush_clump_to_list(&mut self, fn flush_clump_to_list(&mut self,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
flow: @mut FlowContext, flow: FlowContext,
in_boxes: &[@mut RenderBox], in_boxes: &[@mut RenderBox],
out_boxes: &mut ~[@mut RenderBox]) { out_boxes: &mut ~[@mut RenderBox]) {
assert!(self.clump.length() > 0); assert!(self.clump.length() > 0);
@ -337,14 +336,14 @@ struct PendingLine {
} }
struct LineboxScanner { struct LineboxScanner {
flow: @mut FlowContext, flow: FlowContext,
new_boxes: ~[@mut RenderBox], new_boxes: ~[@mut RenderBox],
work_list: @mut Deque<@mut RenderBox>, work_list: @mut Deque<@mut RenderBox>,
pending_line: PendingLine, pending_line: PendingLine,
line_spans: ~[Range], line_spans: ~[Range],
} }
fn LineboxScanner(inline: @mut FlowContext) -> LineboxScanner { fn LineboxScanner(inline: FlowContext) -> LineboxScanner {
assert!(inline.starts_inline_flow()); assert!(inline.starts_inline_flow());
LineboxScanner { LineboxScanner {
@ -358,7 +357,7 @@ fn LineboxScanner(inline: @mut FlowContext) -> LineboxScanner {
impl LineboxScanner { impl LineboxScanner {
priv fn reset_scanner(&mut self) { priv fn reset_scanner(&mut self) {
debug!("Resetting line box scanner's state for flow f%d.", self.flow.d().id); debug!("Resetting line box scanner's state for flow f%d.", self.flow.id());
self.line_spans = ~[]; self.line_spans = ~[];
self.new_boxes = ~[]; self.new_boxes = ~[];
self.reset_linebox(); self.reset_linebox();
@ -412,20 +411,13 @@ impl LineboxScanner {
priv fn swap_out_results(&mut self) { priv fn swap_out_results(&mut self) {
debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d", debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d",
self.line_spans.len(), self.flow.d().id); self.line_spans.len(),
self.flow.id());
//do self.new_boxes.swap |boxes| {
let inline_boxes = &mut self.flow.inline().boxes; let inline_boxes = &mut self.flow.inline().boxes;
util::swap(inline_boxes, &mut self.new_boxes); util::swap(inline_boxes, &mut self.new_boxes);
//inline_boxes = boxes;
// ~[]
//};
//do self.line_spans.swap |boxes| {
let lines = &mut self.flow.inline().lines; let lines = &mut self.flow.inline().lines;
util::swap(lines, &mut self.line_spans); util::swap(lines, &mut self.line_spans);
// lines = boxes;
// ~[]
//};
} }
priv fn flush_current_line(&mut self) { priv fn flush_current_line(&mut self) {
@ -450,7 +442,7 @@ impl LineboxScanner {
linebox_align = CSSTextAlignLeft; linebox_align = CSSTextAlignLeft;
} }
let slack_width = self.flow.d().position.size.width - self.pending_line.width; let slack_width = self.flow.position().size.width - self.pending_line.width;
match linebox_align { match linebox_align {
// So sorry, but justified text is more complicated than shuffling linebox coordinates. // So sorry, but justified text is more complicated than shuffling linebox coordinates.
// TODO(Issue #213): implement `text-align: justify` // TODO(Issue #213): implement `text-align: justify`
@ -478,6 +470,7 @@ impl LineboxScanner {
} }
}, },
} }
// clear line and add line mapping // clear line and add line mapping
debug!("LineboxScanner: Saving information for flushed line %u.", self.line_spans.len()); debug!("LineboxScanner: Saving information for flushed line %u.", self.line_spans.len());
self.line_spans.push(line_range); self.line_spans.push(line_range);
@ -486,7 +479,7 @@ impl LineboxScanner {
// return value: whether any box was appended. // return value: whether any box was appended.
priv fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: @mut RenderBox) -> bool { priv fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: @mut RenderBox) -> bool {
let remaining_width = self.flow.d().position.size.width - self.pending_line.width; let remaining_width = self.flow.position().size.width - self.pending_line.width;
let in_box_width = in_box.d().position.size.width; let in_box_width = in_box.d().position.size.width;
let line_is_empty: bool = self.pending_line.range.length() == 0; let line_is_empty: bool = self.pending_line.range.length() == 0;
@ -579,6 +572,9 @@ impl LineboxScanner {
} }
pub struct InlineFlowData { pub struct InlineFlowData {
/// Data common to all flows.
common: FlowData,
// A vec of all inline render boxes. Several boxes may // A vec of all inline render boxes. Several boxes may
// correspond to one Node/Element. // correspond to one Node/Element.
boxes: ~[@mut RenderBox], boxes: ~[@mut RenderBox],
@ -591,65 +587,69 @@ pub struct InlineFlowData {
elems: ElementMapping elems: ElementMapping
} }
pub fn InlineFlowData() -> InlineFlowData { impl InlineFlowData {
pub fn new(common: FlowData) -> InlineFlowData {
InlineFlowData { InlineFlowData {
common: common,
boxes: ~[], boxes: ~[],
lines: ~[], lines: ~[],
elems: ElementMapping::new(), elems: ElementMapping::new(),
} }
} }
}
pub trait InlineLayout { pub trait InlineLayout {
fn starts_inline_flow(&self) -> bool; fn starts_inline_flow(&self) -> bool;
fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext);
fn assign_widths_inline(@mut self, ctx: &mut LayoutContext);
fn assign_height_inline(@mut self, ctx: &mut LayoutContext);
fn build_display_list_inline(@mut self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Point2D<Au>,
d: &Cell<DisplayList>);
} }
impl InlineLayout for FlowContext { impl InlineLayout for FlowContext {
fn starts_inline_flow(&self) -> bool { match *self { InlineFlow(*) => true, _ => false } } fn starts_inline_flow(&self) -> bool {
match *self {
fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) { InlineFlow(*) => true,
assert!(self.starts_inline_flow()); _ => false
}
}
}
impl InlineFlowData {
pub fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) {
let mut scanner = TextRunScanner::new(); let mut scanner = TextRunScanner::new();
scanner.scan_for_runs(ctx, self); scanner.scan_for_runs(ctx, InlineFlow(self));
{
let this = &mut *self;
let mut min_width = Au(0); let mut min_width = Au(0);
let mut pref_width = Au(0); let mut pref_width = Au(0);
let boxes = &mut self.inline().boxes; for this.boxes.each |box| {
for boxes.each |box| { debug!("FlowContext[%d]: measuring %s", self.common.id, box.debug_str());
debug!("FlowContext[%d]: measuring %s", self.d().id, box.debug_str());
min_width = Au::max(min_width, box.get_min_width(ctx)); min_width = Au::max(min_width, box.get_min_width(ctx));
pref_width = Au::max(pref_width, box.get_pref_width(ctx)); pref_width = Au::max(pref_width, box.get_pref_width(ctx));
} }
self.d().min_width = min_width; this.common.min_width = min_width;
self.d().pref_width = pref_width; this.common.pref_width = pref_width;
}
} }
/* Recursively (top-down) determines the actual width of child /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
contexts and boxes. When called on this context, the context has /// on this context, the context has had its width set by the parent context.
had its width set by the parent context. */ pub fn assign_widths_inline(@mut self, ctx: &mut LayoutContext) {
fn assign_widths_inline(@mut self, ctx: &mut LayoutContext) { {
assert!(self.starts_inline_flow());
// initialize (content) box widths, if they haven't been // initialize (content) box widths, if they haven't been
// already. This could be combined with LineboxScanner's walk // already. This could be combined with LineboxScanner's walk
// over the box list, and/or put into RenderBox. // over the box list, and/or put into RenderBox.
let boxes = &mut self.inline().boxes; let this = &mut *self;
for boxes.each |&box| { for this.boxes.each |&box| {
let box2 = &mut *box; let box2 = &mut *box;
box.d().position.size.width = match *box2 { box.d().position.size.width = match *box2 {
ImageBox(_, ref img) => { ImageBox(_, ref img) => {
let img2: &mut holder::ImageHolder = unsafe { cast::transmute(img) }; let img2: &mut holder::ImageHolder = unsafe { cast::transmute(img) };
Au::from_px(img2.get_size().get_or_default(Size2D(0,0)).width) Au::from_px(img2.get_size().get_or_default(Size2D(0,0)).width)
} }
TextBox(*) => { /* text boxes are initialized with dimensions */ TextBox(*) => {
// Text boxes are initialized with dimensions.
box.d().position.size.width box.d().position.size.width
}, },
// TODO(Issue #225): different cases for 'inline-block', other replaced content // TODO(Issue #225): different cases for 'inline-block', other replaced content
@ -657,8 +657,9 @@ impl InlineLayout for FlowContext {
_ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box)) _ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box))
}; };
} // for boxes.each |box| } // for boxes.each |box|
}
let mut scanner = LineboxScanner(self); let mut scanner = LineboxScanner(InlineFlow(self));
scanner.scan_for_lines(ctx); scanner.scan_for_lines(ctx);
/* There are no child contexts, so stop here. */ /* There are no child contexts, so stop here. */
@ -670,31 +671,34 @@ impl InlineLayout for FlowContext {
// 'inline-block' box that created this flow before recursing. // 'inline-block' box that created this flow before recursing.
} }
fn assign_height_inline(@mut self, _ctx: &mut LayoutContext) { pub fn assign_height_inline(&mut self, _ctx: &mut LayoutContext) {
// TODO(Issue #226): get CSS 'line-height' property from // TODO(#226): Get the CSS `line-height` property from the containing block's style to
// containing block's style to determine minimum linebox height. // determine minimum linebox height.
// TODO(Issue #226): get CSS 'line-height' property from each non-replaced //
// inline element to determine its height for computing linebox height. // TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to
// determine its height for computing linebox height.
let line_height = Au::from_px(20); let line_height = Au::from_px(20);
let mut cur_y = Au(0); let mut cur_y = Au(0);
let lines = &mut self.inline().lines; for self.lines.eachi |i, line_span| {
for lines.eachi |i, line_span| {
debug!("assign_height_inline: processing line %u with box span: %?", i, line_span); debug!("assign_height_inline: processing line %u with box span: %?", i, line_span);
// coords relative to left baseline // coords relative to left baseline
let mut linebox_bounding_box = Au::zero_rect(); let mut linebox_bounding_box = Au::zero_rect();
let boxes = &mut self.inline().boxes; let boxes = &mut self.boxes;
for line_span.eachi |box_i| { for line_span.eachi |box_i| {
let cur_box: &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround let cur_box: &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround
// compute box height. // Compute the height of each box.
let d = cur_box.d(); // FIXME: borrow checker workaround let d = cur_box.d(); // FIXME: borrow checker workaround
let cur_box: &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround let cur_box: &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround
d.position.size.height = match *cur_box { d.position.size.height = match *cur_box {
ImageBox(_, ref img) => { ImageBox(_, ref img) => {
Au::from_px(img.size().height) Au::from_px(img.size().height)
} }
TextBox(*) => { /* text boxes are initialized with dimensions */ TextBox(*) => {
// Text boxes are initialized with dimensions.
d.position.size.height d.position.size.height
}, },
// TODO(Issue #225): different cases for 'inline-block', other replaced content // TODO(Issue #225): different cases for 'inline-block', other replaced content
@ -706,31 +710,38 @@ impl InlineLayout for FlowContext {
} }
}; };
// compute bounding rect, with left baseline as origin. // Compute the bounding rect with the left baseline as origin. Linebox height is a
// so, linebox height is a matter of lining up ideal baselines, // matter of lining up ideal baselines and then using the union of all these rects.
// and then using the union of all these rects.
let bounding_box = match *cur_box { let bounding_box = match *cur_box {
// adjust to baseline coords // Adjust to baseline coordinates.
// TODO(Issue #227): use left/right margins, border, padding for nonreplaced content, //
// and also use top/bottom margins, border, padding for replaced or inline-block content. // TODO(#227): Use left/right margins, border, padding for nonreplaced content,
// TODO(Issue #225): use height, width for 'inline-block', other replaced content // and also use top/bottom margins, border, padding for replaced or
// inline-block content.
//
// TODO(#225): Use height, width for 'inline-block' and other replaced content.
ImageBox(*) | GenericBox(*) => { ImageBox(*) | GenericBox(*) => {
let box_bounds = d.position; let box_bounds = d.position;
box_bounds.translate(&Point2D(Au(0), -d.position.size.height)) box_bounds.translate(&Point2D(Au(0), -d.position.size.height))
}, },
// adjust bounding box metric to box's horizontal offset
// TODO: we can use font metrics directly instead of re-measuring for the bounding box. // Adjust the bounding box metric to the box's horizontal offset.
// TODO: We can use font metrics directly instead of re-measuring for the
// bounding box.
TextBox(_, data) => { TextBox(_, data) => {
let text_bounds = data.run.metrics_for_range(&data.range).bounding_box; let text_bounds = data.run.metrics_for_range(&data.range).bounding_box;
text_bounds.translate(&Point2D(d.position.origin.x, Au(0))) text_bounds.translate(&Point2D(d.position.origin.x, Au(0)))
}, },
_ => { _ => {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s", fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s",
cur_box.debug_str())) cur_box.debug_str()))
} }
}; };
debug!("assign_height_inline: bounding box for box b%d = %?", cur_box.d().id, bounding_box); debug!("assign_height_inline: bounding box for box b%d = %?",
cur_box.d().id,
bounding_box);
linebox_bounding_box = linebox_bounding_box.union(&bounding_box); linebox_bounding_box = linebox_bounding_box.union(&bounding_box);
debug!("assign_height_inline: linebox bounding box = %?", linebox_bounding_box); debug!("assign_height_inline: linebox bounding box = %?", linebox_bounding_box);
} }
@ -743,30 +754,33 @@ impl InlineLayout for FlowContext {
// 'line-height' when calculating linebox height. Then, go back over // 'line-height' when calculating linebox height. Then, go back over
// and set y offsets according to 'vertical-align' property of containing block. // and set y offsets according to 'vertical-align' property of containing block.
let halfleading = match cur_box { let halfleading = match cur_box {
@TextBox(_, data) => { (data.run.font.metrics.em_size - line_height).scale_by(0.5f) }, @TextBox(_, data) => {
_ => { Au(0) } (data.run.font.metrics.em_size - line_height).scale_by(0.5f)
},
_ => Au(0),
}; };
cur_box.d().position.origin.y = cur_y + halfleading + (baseline_offset - cur_box.d().position.size.height); cur_box.d().position.origin.y =
cur_y + halfleading + (baseline_offset - cur_box.d().position.size.height);
} }
cur_y += Au::max(line_height, linebox_height); cur_y += Au::max(line_height, linebox_height);
} // /lines.each |line_span| } // /lines.each |line_span|
self.d().position.size.height = cur_y; self.common.position.size.height = cur_y;
} }
fn build_display_list_inline(@mut self, builder: &DisplayListBuilder, dirty: &Rect<Au>, pub fn build_display_list_inline(&mut self,
offset: &Point2D<Au>, list: &Cell<DisplayList>) { builder: &DisplayListBuilder,
dirty: &Rect<Au>,
assert!(self.starts_inline_flow()); offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
// TODO(Issue #228): once we form line boxes and have their cached bounds, we can be // TODO(Issue #228): once we form line boxes and have their cached bounds, we can be
// smarter and not recurse on a line if nothing in it can intersect dirty // smarter and not recurse on a line if nothing in it can intersect dirty
let inline = self.inline(); // FIXME: borrow checker workaround
debug!("FlowContext[%d]: building display list for %u inline boxes", debug!("FlowContext[%d]: building display list for %u inline boxes",
self.d().id, inline.boxes.len()); self.common.id,
let boxes = &mut self.inline().boxes; self.boxes.len());
for boxes.each |box| {
for self.boxes.each |box| {
box.build_display_list(builder, dirty, offset, list) box.build_display_list(builder, dirty, offset, list)
} }

View file

@ -205,10 +205,9 @@ impl Layout {
} }
} }
let layout_root: @mut FlowContext = do time("layout: tree construction") { let layout_root: FlowContext = do time("layout: tree construction") {
let mut builder = LayoutTreeBuilder::new(); let mut builder = LayoutTreeBuilder::new();
let layout_root: @mut FlowContext = match builder.construct_trees(&layout_ctx, let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
*node) {
Ok(root) => root, Ok(root) => root,
Err(*) => fail!(~"Root flow should always exist") Err(*) => fail!(~"Root flow should always exist")
}; };
@ -235,9 +234,11 @@ impl Layout {
// 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
do layout_root.with_common_info |layout_root_info| {
layout_root.build_display_list(&builder, layout_root.build_display_list(&builder,
&copy layout_root.d().position, &copy layout_root_info.position,
display_list); display_list);
}
let render_layer = RenderLayer { let render_layer = RenderLayer {
display_list: display_list.take(), display_list: display_list.take(),

View file

@ -10,27 +10,28 @@ use gfx::geometry::Au;
use layout::block::BlockLayout; use layout::block::BlockLayout;
use layout::box::RenderBox; use layout::box::RenderBox;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::flow::{FlowContext, RootFlow}; use layout::flow::{FlowContext, FlowData, RootFlow};
use layout::display_list_builder::DisplayListBuilder; use layout::display_list_builder::DisplayListBuilder;
pub struct RootFlowData { pub struct RootFlowData {
/// Data common to all flows.
common: FlowData,
/// The render box at the root of the tree.
box: Option<@mut RenderBox> box: Option<@mut RenderBox>
} }
pub fn RootFlowData() -> RootFlowData { impl RootFlowData {
pub fn new(common: FlowData) -> RootFlowData {
RootFlowData { RootFlowData {
box: None common: common,
box: None,
}
} }
} }
pub trait RootLayout { pub trait RootLayout {
fn starts_root_flow(&self) -> bool; fn starts_root_flow(&self) -> bool;
fn bubble_widths_root(@mut self, ctx: &LayoutContext);
fn assign_widths_root(@mut self, ctx: &LayoutContext);
fn assign_height_root(@mut self, ctx: &LayoutContext);
fn build_display_list_root(@mut self, a: &DisplayListBuilder, b: &Rect<Au>,
c: &Point2D<Au>, d: &Cell<DisplayList>);
} }
impl RootLayout for FlowContext { impl RootLayout for FlowContext {
@ -40,47 +41,47 @@ impl RootLayout for FlowContext {
_ => false _ => false
} }
} }
/* defer to the block algorithm */
fn bubble_widths_root(@mut self, ctx: &LayoutContext) {
assert!(self.starts_root_flow());
self.bubble_widths_block(ctx)
} }
fn assign_widths_root(@mut self, ctx: &LayoutContext) { impl RootFlowData {
assert!(self.starts_root_flow()); /// Defer to the block algorithm.
pub fn bubble_widths_root(@mut self, ctx: &LayoutContext) {
self.d().position.origin = Au::zero_point(); RootFlow(self).bubble_widths_block(ctx)
self.d().position.size.width = ctx.screen_size.size.width;
self.assign_widths_block(ctx)
} }
fn assign_height_root(@mut self, ctx: &LayoutContext) { pub fn assign_widths_root(@mut self, ctx: &LayoutContext) {
assert!(self.starts_root_flow()); self.common.position.origin = Au::zero_point();
self.common.position.size.width = ctx.screen_size.size.width;
RootFlow(self).assign_widths_block(ctx)
}
pub fn assign_height_root(@mut self, ctx: &LayoutContext) {
// this is essentially the same as assign_height_block(), except // this is essentially the same as assign_height_block(), except
// the root adjusts self height to at least cover the viewport. // the root adjusts self height to at least cover the viewport.
let mut cur_y = Au(0); let mut cur_y = Au(0);
for self.each_child |child_ctx| { for RootFlow(self).each_child |child_flow| {
child_ctx.d().position.origin.y = cur_y; do child_flow.with_common_info |child_common_info| {
cur_y += child_ctx.d().position.size.height; child_common_info.position.origin.y = cur_y;
cur_y += child_common_info.position.size.height;
}
} }
self.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y); self.common.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
do self.with_block_box |box| { do RootFlow(self).with_block_box |box| {
box.d().position.origin.y = Au(0); box.d().position.origin.y = Au(0);
box.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y); box.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
let (_used_top, _used_bot) = box.get_used_height(); let (_used_top, _used_bot) = box.get_used_height();
} }
} }
fn build_display_list_root(@mut self, builder: &DisplayListBuilder, dirty: &Rect<Au>, pub fn build_display_list_root(@mut self,
offset: &Point2D<Au>, list: &Cell<DisplayList>) { builder: &DisplayListBuilder,
assert!(self.starts_root_flow()); dirty: &Rect<Au>,
offset: &Point2D<Au>,
self.build_display_list_block(builder, dirty, offset, list); list: &Cell<DisplayList>) {
RootFlow(self).build_display_list_block(builder, dirty, offset, list);
} }
} }

View file

@ -6,22 +6,22 @@ use layout::flow::FlowContext;
/// A trait for running tree-based traversals over flows contexts. /// A trait for running tree-based traversals over flows contexts.
pub trait FlowContextTraversals { pub trait FlowContextTraversals {
fn traverse_preorder(@mut self, preorder_cb: &fn(@mut FlowContext)); fn traverse_preorder(&self, preorder_cb: &fn(FlowContext));
fn traverse_postorder(@mut self, postorder_cb: &fn(@mut FlowContext)); fn traverse_postorder(&self, postorder_cb: &fn(FlowContext));
} }
impl FlowContextTraversals for FlowContext { impl FlowContextTraversals for FlowContext {
fn traverse_preorder(@mut self, preorder_cb: &fn(@mut FlowContext)) { fn traverse_preorder(&self, preorder_cb: &fn(FlowContext)) {
preorder_cb(self); preorder_cb(*self);
for self.each_child |child| { for self.each_child |child| {
child.traverse_preorder(preorder_cb); child.traverse_preorder(preorder_cb);
} }
} }
fn traverse_postorder(@mut self, postorder_cb: &fn(@mut FlowContext)) { fn traverse_postorder(&self, postorder_cb: &fn(FlowContext)) {
for self.each_child |child| { for self.each_child |child| {
child.traverse_postorder(postorder_cb); child.traverse_postorder(postorder_cb);
} }
postorder_cb(self); postorder_cb(*self);
} }
} }