mirror of
https://github.com/servo/servo.git
synced 2025-06-23 08:34:42 +01:00
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:
parent
c8a69b9d76
commit
e7dd2610c9
5 changed files with 109 additions and 21 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(*) => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue