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 {
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 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.
This works on both BlockFlow and RootFlow, since they are mostly the same. */
pure fn with_block_box(cb: fn(box: &@RenderBox) -> ()) -> () {
match self {
pure fn with_block_box(@self, cb: fn(box: &@RenderBox) -> ()) -> () {
match *self {
BlockFlow(*) => {
let mut box = self.block().box;
box.iter(cb);

View file

@ -91,12 +91,18 @@ pub enum RenderBox {
UnscannedTextBox(RenderBoxData, ~str)
}
enum InlineSpacerSide {
LogicalBefore,
LogicalAfter,
}
trait RenderBoxMethods {
pure fn d(&self) -> &self/RenderBoxData;
pure fn is_replaced() -> bool;
pure fn can_split() -> 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 border_box() -> Rect<au>;
@ -104,6 +110,7 @@ trait RenderBoxMethods {
fn get_pref_width(&LayoutContext) -> au;
fn get_used_width() -> (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>,
offset: &Point2D<au>, &dl::DisplayList);
}
@ -226,6 +233,11 @@ impl RenderBox : RenderBoxMethods {
(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.
Coordinates are relative to the owning flow. */
pure fn content_box() -> Rect<au> {
@ -267,6 +279,12 @@ impl RenderBox : RenderBoxMethods {
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
// 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

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 !core::box::ptr_eq(next_flow, parent_ctx.flow) {
debug!("using parent builder context");
debug!("Adding child flow f%? of f%?",
parent_ctx.flow.d().id, next_flow.d().id);
FlowTree.add_child(parent_ctx.flow, 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
@ -106,16 +110,17 @@ impl LayoutTreeBuilder {
assert cur_node.has_aux();
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);
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.
do NodeTree.each_child(&cur_node) |child_node| {
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)
if (builder_ctx.flow.starts_inline_flow()) {
let mut found_child_inline = false;

View file

@ -1,12 +1,13 @@
use au = gfx::geometry;
use au::au;
use core::dvec::DVec;
use dl = gfx::display_list;
use dom::node::Node;
use geom::rect::Rect;
use geom::point::Point2D;
// TODO: pub-use these
use layout::block::BlockFlowData;
use layout::box::RenderBox;
use layout::box::{LogicalBefore, LogicalAfter, RenderBox};
use layout::context::LayoutContext;
use layout::debug::BoxedDebugMethods;
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
// mapping between DOM nodes and boxes.
struct BoxConsumer {
flow: @FlowContext,
stack: DVec<PendingEntry>,
}
fn BoxConsumer(flow: @FlowContext) -> BoxConsumer {
debug!("Creating box consumer for flow: f%s", flow.debug_str());
BoxConsumer {
flow: flow,
stack: DVec()
}
}
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 {
@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(*) => {
assert self.flow.block().box.is_none();
self.flow.block().box = Some(box);
self.flow.block().box = Some(entry.start_box);
},
@RootFlow(*) => {
assert self.flow.block().box.is_none();
self.flow.block().box = Some(box);
assert self.flow.root().box.is_none();
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 {
match *self {
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 {
match *self {
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 {
match *self {
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),
@InlineFlow(*) => self.bubble_widths_inline(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),
@InlineFlow(*) => self.assign_widths_inline(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),
@InlineFlow(*) => self.assign_height_inline(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 {
let repr = match *self {
InlineFlow(*) => {

View file

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