Build inline flow box list using BoxConsumer. Fix failure cases in

FlowContext variant helper methods to only print flow/box id, so they
don't run out of stack when reflectively printing cycles.
This commit is contained in:
Brian J. Burg 2012-10-10 15:53:45 -07:00
parent c8a69b9d76
commit e7dd2610c9
5 changed files with 109 additions and 21 deletions

View file

@ -22,7 +22,7 @@ fn BlockFlowData() -> BlockFlowData {
trait BlockLayout { trait BlockLayout {
pure fn starts_block_flow() -> bool; pure fn starts_block_flow() -> bool;
pure fn with_block_box(fn(box: &@RenderBox) -> ()) -> (); pure fn with_block_box(@self, fn(box: &@RenderBox) -> ()) -> ();
fn bubble_widths_block(@self, ctx: &LayoutContext); fn bubble_widths_block(@self, ctx: &LayoutContext);
fn assign_widths_block(@self, ctx: &LayoutContext); fn assign_widths_block(@self, ctx: &LayoutContext);
@ -42,8 +42,8 @@ impl FlowContext : BlockLayout {
/* 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. */
pure fn with_block_box(cb: fn(box: &@RenderBox) -> ()) -> () { pure fn with_block_box(@self, cb: fn(box: &@RenderBox) -> ()) -> () {
match self { match *self {
BlockFlow(*) => { BlockFlow(*) => {
let mut box = self.block().box; let mut box = self.block().box;
box.iter(cb); box.iter(cb);

View file

@ -91,12 +91,18 @@ pub enum RenderBox {
UnscannedTextBox(RenderBoxData, ~str) UnscannedTextBox(RenderBoxData, ~str)
} }
enum InlineSpacerSide {
LogicalBefore,
LogicalAfter,
}
trait RenderBoxMethods { trait RenderBoxMethods {
pure fn d(&self) -> &self/RenderBoxData; pure fn d(&self) -> &self/RenderBoxData;
pure fn is_replaced() -> bool; pure fn is_replaced() -> bool;
pure fn can_split() -> bool; pure fn can_split() -> bool;
pure fn can_merge_with_box(@self, other: @RenderBox) -> bool; pure fn can_merge_with_box(@self, other: @RenderBox) -> bool;
pure fn requires_inline_spacers() -> bool;
pure fn content_box() -> Rect<au>; pure fn content_box() -> Rect<au>;
pure fn border_box() -> Rect<au>; pure fn border_box() -> Rect<au>;
@ -104,6 +110,7 @@ trait RenderBoxMethods {
fn get_pref_width(&LayoutContext) -> au; fn get_pref_width(&LayoutContext) -> au;
fn get_used_width() -> (au, au); fn get_used_width() -> (au, au);
fn get_used_height() -> (au, au); fn get_used_height() -> (au, au);
fn create_inline_spacer_for_side(&LayoutContext, InlineSpacerSide) -> Option<@RenderBox>;
fn build_display_list(&dl::DisplayListBuilder, dirty: &Rect<au>, fn build_display_list(&dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, &dl::DisplayList); offset: &Point2D<au>, &dl::DisplayList);
} }
@ -226,6 +233,11 @@ impl RenderBox : RenderBoxMethods {
(au(0), au(0)) (au(0), au(0))
} }
/* Whether "spacer" boxes are needed to stand in for this DOM node */
pure fn requires_inline_spacers() -> bool {
return false;
}
/* The box formed by the content edge, as defined in CSS 2.1 Section 8.1. /* The box formed by the content edge, as defined in CSS 2.1 Section 8.1.
Coordinates are relative to the owning flow. */ Coordinates are relative to the owning flow. */
pure fn content_box() -> Rect<au> { pure fn content_box() -> Rect<au> {
@ -267,6 +279,12 @@ impl RenderBox : RenderBoxMethods {
self.content_box() self.content_box()
} }
// TODO: implement this, generating spacer
fn create_inline_spacer_for_side(_ctx: &LayoutContext, _side: InlineSpacerSide) -> Option<@RenderBox> {
None
}
// TODO: to implement stacking contexts correctly, we need to // TODO: to implement stacking contexts correctly, we need to
// create a set of display lists, one per each layer of a stacking // create a set of display lists, one per each layer of a stacking
// context. (CSS 2.1, Section 9.9.1). Each box is passed the list // context. (CSS 2.1, Section 9.9.1). Each box is passed the list

View file

@ -88,15 +88,19 @@ impl LayoutTreeBuilder {
} }
}; };
let mut builder_ctx : BuilderContext = copy *parent_ctx; let builder_ctx : BuilderContext;
// if this is a new flow, attach to parent flow and make a new BuilderContext. // if this is a new flow, attach to parent flow and make a new BuilderContext.
if !core::box::ptr_eq(next_flow, parent_ctx.flow) { if !core::box::ptr_eq(next_flow, parent_ctx.flow) {
debug!("using parent builder context");
debug!("Adding child flow f%? of f%?", debug!("Adding child flow f%? of f%?",
parent_ctx.flow.d().id, next_flow.d().id); parent_ctx.flow.d().id, next_flow.d().id);
FlowTree.add_child(parent_ctx.flow, next_flow); FlowTree.add_child(parent_ctx.flow, next_flow);
builder_ctx = { flow: next_flow, consumer: BoxConsumer(next_flow) }; builder_ctx = { flow: next_flow, consumer: BoxConsumer(next_flow) };
} else {
debug!("creating fresh builder context");
builder_ctx = copy *parent_ctx;
} }
// store reference to the flow context which contains any // store reference to the flow context which contains any
@ -106,16 +110,17 @@ impl LayoutTreeBuilder {
assert cur_node.has_aux(); assert cur_node.has_aux();
do cur_node.aux |data| { data.flow = Some(builder_ctx.flow) } do cur_node.aux |data| { data.flow = Some(builder_ctx.flow) }
let new_box = self.make_box(layout_ctx, box_type, cur_node, builder_ctx.flow); let new_box = self.make_box(layout_ctx, box_type, cur_node, builder_ctx.flow);
debug!("Assign ^box to flow: %?", builder_ctx.flow.debug_str()); debug!("Assign ^box to flow: %?", builder_ctx.flow.debug_str());
builder_ctx.consumer.accept_box(layout_ctx, new_box); builder_ctx.consumer.push_box(layout_ctx, new_box);
// recurse on child nodes. // recurse on child nodes.
do NodeTree.each_child(&cur_node) |child_node| { do NodeTree.each_child(&cur_node) |child_node| {
self.construct_recursively(layout_ctx, *child_node, &builder_ctx); true self.construct_recursively(layout_ctx, *child_node, &builder_ctx); true
} }
builder_ctx.consumer.pop_box(layout_ctx, new_box);
// 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)
if (builder_ctx.flow.starts_inline_flow()) { if (builder_ctx.flow.starts_inline_flow()) {
let mut found_child_inline = false; let mut found_child_inline = false;

View file

@ -1,12 +1,13 @@
use au = gfx::geometry; use au = gfx::geometry;
use au::au; use au::au;
use core::dvec::DVec;
use dl = gfx::display_list; use dl = gfx::display_list;
use dom::node::Node; use dom::node::Node;
use geom::rect::Rect; use geom::rect::Rect;
use geom::point::Point2D; use geom::point::Point2D;
// TODO: pub-use these // TODO: pub-use these
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::box::RenderBox; use layout::box::{LogicalBefore, LogicalAfter, RenderBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::BoxedDebugMethods; use layout::debug::BoxedDebugMethods;
use layout::inline::InlineFlowData; use layout::inline::InlineFlowData;
@ -105,31 +106,94 @@ fn FlowData(id: int) -> FlowData {
} }
} }
struct PendingEntry {
start_box: @RenderBox,
start_idx: uint
}
// 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 BoxConsumer { struct BoxConsumer {
flow: @FlowContext, flow: @FlowContext,
stack: DVec<PendingEntry>,
} }
fn BoxConsumer(flow: @FlowContext) -> BoxConsumer { fn BoxConsumer(flow: @FlowContext) -> BoxConsumer {
debug!("Creating box consumer for flow: f%s", flow.debug_str());
BoxConsumer { BoxConsumer {
flow: flow, flow: flow,
stack: DVec()
} }
} }
impl BoxConsumer { impl BoxConsumer {
pub fn accept_box(_ctx: &LayoutContext, box: @RenderBox) { pub fn push_box(ctx: &LayoutContext, box: @RenderBox) {
debug!("BoxConsumer: pushing box b%d to flow f%d", box.d().id, self.flow.d().id);
let length = match self.flow {
@InlineFlow(*) => self.flow.inline().boxes.len(),
_ => 0
};
let entry = PendingEntry { start_box: box, start_idx: length };
self.stack.push(entry);
match self.flow { match self.flow {
@InlineFlow(*) => self.flow.inline().boxes.push(box), @InlineFlow(*) => {
if box.requires_inline_spacers() {
do box.create_inline_spacer_for_side(ctx, LogicalBefore).iter |b: &@RenderBox| {
self.flow.inline().boxes.push(*b);
}
}
},
@BlockFlow(*) | @RootFlow(*) => {
assert self.stack.len() == 1;
},
_ => { warn!("push_box() not implemented for flow f%d", self.flow.d().id) }
}
}
pub fn pop_box(ctx: &LayoutContext, box: @RenderBox) {
assert self.stack.len() > 0;
let entry = self.stack.pop();
assert core::box::ptr_eq(box, entry.start_box);
debug!("BoxConsumer: popping box b%d to flow f%d", box.d().id, self.flow.d().id);
match self.flow {
@InlineFlow(*) => {
let pre_length = self.flow.inline().boxes.len() - entry.start_idx;
match (pre_length, box.requires_inline_spacers()) {
// leaf box
(0, _) => { self.flow.inline().boxes.push(box); },
// if this non-leaf box generates extra horizontal
// spacing, add a SpacerBox for it.
(_, true) => {
do box.create_inline_spacer_for_side(ctx, LogicalAfter).iter |b: &@RenderBox| {
self.flow.inline().boxes.push(*b);
}
},
// non-leaf with no spacer; do nothing
(_, false) => { }
}
let post_length = self.flow.inline().boxes.len() - entry.start_idx;
let mapping = { node: copy box.d().node,
span: {
start: entry.start_idx as u8,
len: post_length as u8
}
};
debug!("BoxConsumer: adding element range %?", mapping.span);
self.flow.inline().elems.push(mapping);
},
@BlockFlow(*) => { @BlockFlow(*) => {
assert self.flow.block().box.is_none(); assert self.flow.block().box.is_none();
self.flow.block().box = Some(box); self.flow.block().box = Some(entry.start_box);
}, },
@RootFlow(*) => { @RootFlow(*) => {
assert self.flow.block().box.is_none(); assert self.flow.root().box.is_none();
self.flow.block().box = Some(box); self.flow.root().box = Some(entry.start_box);
}, },
_ => { warn!("accept_box not implemented for flow %?", self.flow.d().id) } _ => { warn!("pop_box not implemented for flow %?", self.flow.d().id) }
} }
} }
} }
@ -150,21 +214,21 @@ impl FlowContext : FlowContextMethods {
pure fn inline(&self) -> &self/InlineFlowData { pure fn inline(&self) -> &self/InlineFlowData {
match *self { match *self {
InlineFlow(_, ref i) => i, InlineFlow(_, ref i) => i,
_ => fail fmt!("Tried to access inline data of non-inline: %?", self) _ => fail fmt!("Tried to access inline data of non-inline: f%d", self.d().id)
} }
} }
pure fn block(&self) -> &self/BlockFlowData { pure fn block(&self) -> &self/BlockFlowData {
match *self { match *self {
BlockFlow(_, ref b) => b, BlockFlow(_, ref b) => b,
_ => fail fmt!("Tried to access block data of non-block: %?", self) _ => fail fmt!("Tried to access block data of non-block: f%d", self.d().id)
} }
} }
pure fn root(&self) -> &self/RootFlowData { pure fn root(&self) -> &self/RootFlowData {
match *self { match *self {
RootFlow(_, ref r) => r, RootFlow(_, ref r) => r,
_ => fail fmt!("Tried to access root data of non-root: %?", self) _ => fail fmt!("Tried to access root data of non-root: f%d", self.d().id)
} }
} }
@ -173,7 +237,7 @@ impl FlowContext : FlowContextMethods {
@BlockFlow(*) => self.bubble_widths_block(ctx), @BlockFlow(*) => self.bubble_widths_block(ctx),
@InlineFlow(*) => self.bubble_widths_inline(ctx), @InlineFlow(*) => self.bubble_widths_inline(ctx),
@RootFlow(*) => self.bubble_widths_root(ctx), @RootFlow(*) => self.bubble_widths_root(ctx),
_ => fail fmt!("Tried to bubble_widths of flow: %?", self) _ => fail fmt!("Tried to bubble_widths of flow: f%d", self.d().id)
} }
} }
@ -182,7 +246,7 @@ impl FlowContext : FlowContextMethods {
@BlockFlow(*) => self.assign_widths_block(ctx), @BlockFlow(*) => self.assign_widths_block(ctx),
@InlineFlow(*) => self.assign_widths_inline(ctx), @InlineFlow(*) => self.assign_widths_inline(ctx),
@RootFlow(*) => self.assign_widths_root(ctx), @RootFlow(*) => self.assign_widths_root(ctx),
_ => fail fmt!("Tried to assign_widths of flow: %?", self) _ => fail fmt!("Tried to assign_widths of flow: f%d", self.d().id)
} }
} }
@ -191,7 +255,7 @@ impl FlowContext : FlowContextMethods {
@BlockFlow(*) => self.assign_height_block(ctx), @BlockFlow(*) => self.assign_height_block(ctx),
@InlineFlow(*) => self.assign_height_inline(ctx), @InlineFlow(*) => self.assign_height_inline(ctx),
@RootFlow(*) => self.assign_height_root(ctx), @RootFlow(*) => self.assign_height_root(ctx),
_ => fail fmt!("Tried to assign_height of flow: %?", self) _ => fail fmt!("Tried to assign_height of flow: f%d", self.d().id)
} }
} }
@ -290,7 +354,6 @@ impl FlowContext : BoxedDebugMethods {
} }
} }
/* TODO: we need a string builder. This is horribly inefficient */
fn debug_str(@self) -> ~str { fn debug_str(@self) -> ~str {
let repr = match *self { let repr = match *self {
InlineFlow(*) => { InlineFlow(*) => {

View file

@ -3,6 +3,7 @@ use core::dlist::DList;
use core::dvec::DVec; use core::dvec::DVec;
use css::values::{BoxAuto, BoxLength, Px}; use css::values::{BoxAuto, BoxLength, Px};
use dl = gfx::display_list; use dl = gfx::display_list;
use dom::node::Node;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
@ -39,6 +40,7 @@ hard to try out that alternative.
*/ */
type BoxRange = {start: u8, len: u8}; type BoxRange = {start: u8, len: u8};
type NodeRange = {node: Node, span: BoxRange};
// stack-allocated object for scanning an inline flow into // stack-allocated object for scanning an inline flow into
// TextRun-containing TextBoxes. // TextRun-containing TextBoxes.
@ -173,7 +175,7 @@ struct InlineFlowData {
// vec of ranges into boxes that represent elements. These // vec of ranges into boxes that represent elements. These
// ranges must be disjoint or well-nested, and are only related to // ranges must be disjoint or well-nested, and are only related to
// the content of boxes (not lines) // the content of boxes (not lines)
elems: DVec<BoxRange> elems: DVec<NodeRange>
} }
fn InlineFlowData() -> InlineFlowData { fn InlineFlowData() -> InlineFlowData {