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 {
|
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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(*) => {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue