auto merge of #721 : eric93/servo/parallel-layout-ownedtree, r=metajack

Don't store flows and boxes in DOM nodes and display lists.
This commit is contained in:
bors-servo 2013-08-20 12:03:52 -07:00
commit abccd82eba
13 changed files with 655 additions and 618 deletions

View file

@ -19,19 +19,22 @@ use render_context::RenderContext;
use std::cell::Cell; use std::cell::Cell;
use std::comm::{Chan, Port, SharedChan}; use std::comm::{Chan, Port, SharedChan};
use extra::arc::Arc;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use extra::arc; use extra::arc;
pub struct RenderLayer {
display_list: DisplayList<()>,
pub struct RenderLayer<T> {
display_list: Arc<DisplayList<T>>,
size: Size2D<uint> size: Size2D<uint>
} }
pub enum Msg { pub enum Msg<T> {
RenderMsg(RenderLayer), RenderMsg(RenderLayer<T>),
ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch), ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch),
PaintPermissionGranted, PaintPermissionGranted,
PaintPermissionRevoked, PaintPermissionRevoked,
@ -56,24 +59,24 @@ pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferReq
} }
#[deriving(Clone)] #[deriving(Clone)]
pub struct RenderChan { pub struct RenderChan<T> {
chan: SharedChan<Msg>, chan: SharedChan<Msg<T>>,
} }
impl RenderChan { impl<T> RenderChan<T> {
pub fn new(chan: Chan<Msg>) -> RenderChan { pub fn new(chan: Chan<Msg<T>>) -> RenderChan<T> {
RenderChan { RenderChan {
chan: SharedChan::new(chan), chan: SharedChan::new(chan),
} }
} }
pub fn send(&self, msg: Msg) { pub fn send(&self, msg: Msg<T>) {
self.chan.send(msg); self.chan.send(msg);
} }
} }
struct RenderTask<C> { struct RenderTask<C,T> {
id: PipelineId, id: PipelineId,
port: Port<Msg>, port: Port<Msg<T>>,
compositor: C, compositor: C,
font_ctx: @mut FontContext, font_ctx: @mut FontContext,
opts: Opts, opts: Opts,
@ -84,7 +87,7 @@ struct RenderTask<C> {
share_gl_context: AzGLContext, share_gl_context: AzGLContext,
/// The layer to be rendered /// The layer to be rendered
render_layer: Option<RenderLayer>, render_layer: Option<RenderLayer<T>>,
/// Permission to send paint messages to the compositor /// Permission to send paint messages to the compositor
paint_permission: bool, paint_permission: bool,
/// Cached copy of last layers rendered /// Cached copy of last layers rendered
@ -93,9 +96,9 @@ struct RenderTask<C> {
epoch: Epoch, epoch: Epoch,
} }
impl<C: RenderListener + Send> RenderTask<C> { impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
pub fn create(id: PipelineId, pub fn create(id: PipelineId,
port: Port<Msg>, port: Port<Msg<T>>,
compositor: C, compositor: C,
opts: Opts, opts: Opts,
profiler_chan: ProfilerChan) { profiler_chan: ProfilerChan) {
@ -226,7 +229,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
// Draw the display list. // Draw the display list.
do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) { do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
render_layer.display_list.draw_into_context(&ctx); render_layer.display_list.get().draw_into_context(&ctx);
ctx.canvas.draw_target.flush(); ctx.canvas.draw_target.flush();
} }
} }

View file

@ -4,13 +4,20 @@
//! Code for managing the layout data in the DOM. //! Code for managing the layout data in the DOM.
use layout::flow::FlowContext;
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use gfx::display_list::DisplayList;
use servo_util::range::Range;
use extra::arc::Arc;
use newcss::complete::CompleteSelectResults; use newcss::complete::CompleteSelectResults;
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef; use servo_util::tree::TreeNodeRef;
pub struct DisplayBoxes {
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
range: Option<Range>,
}
/// Data that layout associates with a node. /// Data that layout associates with a node.
pub struct LayoutData { pub struct LayoutData {
/// The results of CSS styling for this node. /// The results of CSS styling for this node.
@ -19,8 +26,9 @@ pub struct LayoutData {
/// Description of how to account for recent style changes. /// Description of how to account for recent style changes.
restyle_damage: Option<RestyleDamage>, restyle_damage: Option<RestyleDamage>,
/// The CSS flow that this node is associated with. /// The boxes assosiated with this flow.
flow: Option<FlowContext>, /// Used for getBoundingClientRect and friends.
boxes: DisplayBoxes,
} }
impl LayoutData { impl LayoutData {
@ -29,7 +37,7 @@ impl LayoutData {
LayoutData { LayoutData {
style: None, style: None,
restyle_damage: None, restyle_damage: None,
flow: None, boxes: DisplayBoxes { display_list: None, range: None },
} }
} }
} }
@ -65,14 +73,8 @@ impl LayoutAuxMethods for AbstractNode<LayoutView> {
/// box in the COW model) and populates it with an empty style object. /// box in the COW model) and populates it with an empty style object.
fn initialize_layout_data(self) -> Option<@mut LayoutData> { fn initialize_layout_data(self) -> Option<@mut LayoutData> {
if self.has_layout_data() { if self.has_layout_data() {
{ self.layout_data().boxes.display_list = None;
let layout_data = &mut self.layout_data().flow; self.layout_data().boxes.range = None;
match *layout_data {
Some(ref flow) => flow.teardown(),
None => ()
}
}
self.layout_data().flow = None;
None None
} else { } else {
let data = @mut LayoutData::new(); let data = @mut LayoutData::new();

View file

@ -19,7 +19,6 @@ use geom::rect::Rect;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use gfx::geometry::{Au, to_frac_px}; use gfx::geometry::{Au, to_frac_px};
use gfx::geometry; use gfx::geometry;
use servo_util::tree::TreeNodeRef;
pub struct BlockFlowData { pub struct BlockFlowData {
/// Data common to all flows. /// Data common to all flows.
@ -50,7 +49,6 @@ impl BlockFlowData {
} }
pub fn teardown(&mut self) { pub fn teardown(&mut self) {
self.common.teardown();
for box in self.box.iter() { for box in self.box.iter() {
box.teardown(); box.teardown();
} }
@ -66,7 +64,7 @@ pub trait BlockLayout {
impl BlockLayout for FlowContext { impl BlockLayout for FlowContext {
fn starts_root_flow(&self) -> bool { fn starts_root_flow(&self) -> bool {
match *self { match *self {
BlockFlow(info) => info.is_root, BlockFlow(ref info) => info.is_root,
_ => false _ => false
} }
} }
@ -89,13 +87,13 @@ impl BlockFlowData {
/* TODO: floats */ /* TODO: floats */
/* TODO: absolute contexts */ /* TODO: absolute contexts */
/* TODO: inline-blocks */ /* TODO: inline-blocks */
pub fn bubble_widths_block(@mut self, ctx: &LayoutContext) { pub fn bubble_widths_block(&mut self, ctx: &LayoutContext) {
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 mut num_floats = 0; let mut num_floats = 0;
/* find max width from child block contexts */ /* find max width from child block contexts */
for child_ctx in BlockFlow(self).children() { for child_ctx in self.common.child_iter() {
assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
do child_ctx.with_mut_base |child_node| { do child_ctx.with_mut_base |child_node| {
@ -179,7 +177,7 @@ impl BlockFlowData {
/// ///
/// Dual boxes consume some width first, and the remainder is assigned to all child (block) /// Dual boxes consume some width first, and the remainder is assigned to all child (block)
/// contexts. /// contexts.
pub fn assign_widths_block(@mut self, ctx: &LayoutContext) { pub fn assign_widths_block(&mut self, ctx: &LayoutContext) {
debug!("assign_widths_block: assigning width for flow %?", self.common.id); debug!("assign_widths_block: assigning width for flow %?", self.common.id);
if self.is_root { if self.is_root {
debug!("Setting root position"); debug!("Setting root position");
@ -240,7 +238,7 @@ impl BlockFlowData {
} }
let has_inorder_children = self.common.is_inorder || self.common.num_floats > 0; let has_inorder_children = self.common.is_inorder || self.common.num_floats > 0;
for kid in BlockFlow(self).children() { for kid in self.common.child_iter() {
assert!(kid.starts_block_flow() || kid.starts_inline_flow()); assert!(kid.starts_block_flow() || kid.starts_inline_flow());
do kid.with_mut_base |child_node| { do kid.with_mut_base |child_node| {
@ -255,12 +253,12 @@ impl BlockFlowData {
} }
} }
pub fn assign_height_inorder_block(@mut self, ctx: &mut LayoutContext) { pub fn assign_height_inorder_block(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_inorder_block: assigning height for block %?", self.common.id); debug!("assign_height_inorder_block: assigning height for block %?", self.common.id);
self.assign_height_block_base(ctx, true); self.assign_height_block_base(ctx, true);
} }
pub fn assign_height_block(@mut self, ctx: &mut LayoutContext) { pub fn assign_height_block(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_block: assigning height for block %?", self.common.id); debug!("assign_height_block: assigning height for block %?", self.common.id);
// This is the only case in which a block flow can start an inorder // This is the only case in which a block flow can start an inorder
// subtraversal. // subtraversal.
@ -271,7 +269,7 @@ impl BlockFlowData {
self.assign_height_block_base(ctx, false); self.assign_height_block_base(ctx, false);
} }
fn assign_height_block_base(@mut self, ctx: &mut LayoutContext, inorder: bool) { fn assign_height_block_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
let mut cur_y = Au(0); let mut cur_y = Au(0);
let mut clearance = Au(0); let mut clearance = Au(0);
let mut top_offset = Au(0); let mut top_offset = Au(0);
@ -304,7 +302,7 @@ impl BlockFlowData {
// repeat until all children are visited. // repeat until all children are visited.
// last_child.floats_out -> self.floats_out (done at the end of this method) // last_child.floats_out -> self.floats_out (done at the end of this method)
float_ctx = self.common.floats_in.translate(Point2D(-left_offset, -top_offset)); float_ctx = self.common.floats_in.translate(Point2D(-left_offset, -top_offset));
for kid in BlockFlow(self).children() { for kid in self.common.child_iter() {
do kid.with_mut_base |child_node| { do kid.with_mut_base |child_node| {
child_node.floats_in = float_ctx.clone(); child_node.floats_in = float_ctx.clone();
} }
@ -314,7 +312,7 @@ impl BlockFlowData {
} }
} }
} }
for kid in BlockFlow(self).children() { for kid in self.common.child_iter() {
do kid.with_mut_base |child_node| { do kid.with_mut_base |child_node| {
child_node.position.origin.y = cur_y; child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height; cur_y = cur_y + child_node.position.size.height;
@ -359,7 +357,7 @@ impl BlockFlowData {
} }
} }
pub fn build_display_list_block<E:ExtraDisplayListData>(@mut self, pub fn build_display_list_block<E:ExtraDisplayListData>(&mut self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
dirty: &Rect<Au>, dirty: &Rect<Au>,
list: &Cell<DisplayList<E>>) list: &Cell<DisplayList<E>>)
@ -388,9 +386,11 @@ impl BlockFlowData {
let abs_rect = Rect(self.common.abs_position, self.common.position.size); let abs_rect = Rect(self.common.abs_position, self.common.position.size);
if !abs_rect.intersects(dirty) { if !abs_rect.intersects(dirty) {
return false; return true;
} }
debug!("build_display_list_block: adding display element");
// add box that starts block context // add box that starts block context
self.box.map(|&box| { self.box.map(|&box| {
box.build_display_list(builder, dirty, &self.common.abs_position, list) box.build_display_list(builder, dirty, &self.common.abs_position, list)
@ -398,16 +398,14 @@ impl BlockFlowData {
// TODO: handle any out-of-flow elements // TODO: handle any out-of-flow elements
let this_position = self.common.abs_position;
// go deeper into the flow tree for child in self.common.child_iter() {
let flow = BlockFlow(self);
for child in flow.children() {
do child.with_mut_base |base| { do child.with_mut_base |base| {
base.abs_position = self.common.abs_position + base.position.origin; base.abs_position = this_position + base.position.origin;
} }
} }
true false
} }
} }

View file

@ -8,7 +8,6 @@ use css::node_style::StyledNode;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
use layout::float_context::{ClearType, ClearLeft, ClearRight, ClearBoth}; use layout::float_context::{ClearType, ClearLeft, ClearRight, ClearBoth};
use layout::flow::FlowContext;
use layout::model::{BoxModel, MaybeAuto}; use layout::model::{BoxModel, MaybeAuto};
use layout::text; use layout::text;
@ -153,9 +152,6 @@ pub struct RenderBoxBase {
/// The DOM node that this `RenderBox` originates from. /// The DOM node that this `RenderBox` originates from.
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
/// The reference to the containing flow context which this box participates in.
ctx: FlowContext,
/// The position of this box relative to its owning flow. /// The position of this box relative to its owning flow.
position: Rect<Au>, position: Rect<Au>,
@ -170,11 +166,10 @@ pub struct RenderBoxBase {
impl RenderBoxBase { impl RenderBoxBase {
/// Constructs a new `RenderBoxBase` instance. /// Constructs a new `RenderBoxBase` instance.
pub fn new(node: AbstractNode<LayoutView>, flow_context: FlowContext, id: int) pub fn new(node: AbstractNode<LayoutView>, id: int)
-> RenderBoxBase { -> RenderBoxBase {
RenderBoxBase { RenderBoxBase {
node: node, node: node,
ctx: flow_context,
position: Au::zero_rect(), position: Au::zero_rect(),
model: Zero::zero(), model: Zero::zero(),
id: id, id: id,

View file

@ -4,7 +4,6 @@
//! Creates CSS boxes from a DOM tree. //! Creates CSS boxes from a DOM tree.
use layout::aux::LayoutAuxMethods;
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::float::FloatFlowData; use layout::float::FloatFlowData;
use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox}; use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox};
@ -31,9 +30,9 @@ use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId}; use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId};
use servo_util::range::Range; use servo_util::range::Range;
use servo_util::tree::{TreeNodeRef, TreeNode}; use servo_util::tree::{TreeNodeRef, TreeNode};
use std::cell::Cell;
pub struct LayoutTreeBuilder { pub struct LayoutTreeBuilder {
root_flow: Option<FlowContext>,
next_cid: int, next_cid: int,
next_bid: int, next_bid: int,
} }
@ -41,7 +40,6 @@ pub struct LayoutTreeBuilder {
impl LayoutTreeBuilder { impl LayoutTreeBuilder {
pub fn new() -> LayoutTreeBuilder { pub fn new() -> LayoutTreeBuilder {
LayoutTreeBuilder { LayoutTreeBuilder {
root_flow: None,
next_cid: -1, next_cid: -1,
next_bid: -1, next_bid: -1,
} }
@ -50,9 +48,9 @@ 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<'self> {
flow: FlowContext, flow: &'self mut FlowContext,
range_stack: ~[uint], range_stack: @mut ~[uint],
} }
enum InlineSpacerSide { enum InlineSpacerSide {
@ -94,25 +92,32 @@ fn simulate_UA_display_rules(node: AbstractNode<LayoutView>) -> CSSDisplay {
} }
} }
impl BoxGenerator { impl<'self> BoxGenerator<'self> {
/* Debug ids only */ /* Debug ids only */
fn new(flow: FlowContext) -> BoxGenerator { fn new(flow: &'self mut FlowContext) -> BoxGenerator<'self> {
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,
range_stack: ~[] range_stack: @mut ~[]
} }
} }
fn with_clone<R> (&mut self, cb: &fn(BoxGenerator<'self>) -> R) -> R {
let gen = BoxGenerator {
flow: &mut *self.flow,
range_stack: self.range_stack
};
cb(gen)
}
/* Whether "spacer" boxes are needed to stand in for this DOM node */ /* Whether "spacer" boxes are needed to stand in for this DOM node */
fn inline_spacers_needed_for_node(&self, _: AbstractNode<LayoutView>) -> bool { fn inline_spacers_needed_for_node(_: AbstractNode<LayoutView>) -> bool {
return false; return false;
} }
// TODO: implement this, generating spacer // TODO: implement this, generating spacer
fn make_inline_spacer_for_node_side(&self, fn make_inline_spacer_for_node_side(_: &LayoutContext,
_: &LayoutContext,
_: AbstractNode<LayoutView>, _: AbstractNode<LayoutView>,
_: InlineSpacerSide) _: InlineSpacerSide)
-> Option<RenderBox> { -> Option<RenderBox> {
@ -132,28 +137,29 @@ impl BoxGenerator {
debug!("BoxGenerator[f%d]: point a", self.flow.id()); debug!("BoxGenerator[f%d]: point a", self.flow.id());
let range_stack = &mut self.range_stack;
// 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(inline) => { InlineFlow(ref mut inline) => {
let node_range_start = inline.boxes.len(); let node_range_start = inline.boxes.len();
self.range_stack.push(node_range_start); 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 = self.make_box(ctx, box_type, node, self.flow, builder); let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
inline.boxes.push(new_box); inline.boxes.push(new_box);
} else if self.inline_spacers_needed_for_node(node) { } else if BoxGenerator::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
let inline_spacer = self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore); let inline_spacer = BoxGenerator::make_inline_spacer_for_node_side(ctx, node, LogicalBefore);
for spacer in inline_spacer.iter() { for spacer in inline_spacer.iter() {
inline.boxes.push(*spacer); inline.boxes.push(*spacer);
} }
} }
// TODO: cases for inline-block, etc. // TODO: cases for inline-block, etc.
}, },
BlockFlow(block) => { BlockFlow(ref mut block) => {
debug!("BoxGenerator[f%d]: point b", block.common.id); debug!("BoxGenerator[f%d]: point b", block.common.id);
let new_box = self.make_box(ctx, box_type, node, self.flow, builder); let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
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)",
block.common.id, block.common.id,
@ -162,46 +168,20 @@ impl BoxGenerator {
assert!(block.box.is_none()); assert!(block.box.is_none());
block.box = Some(new_box); block.box = Some(new_box);
}, }
FloatFlow(float) => { FloatFlow(ref mut float) => {
debug!("BoxGenerator[f%d]: point b", float.common.id); debug!("BoxGenerator[f%d]: point b", float.common.id);
let mut parent_flow = None; let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
do self.flow.with_base |base| { debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)",
parent_flow = base.parent_node(); float.common.id,
} new_box.id(),
node.debug_str());
match parent_flow { assert!(float.box.is_none() && float.index.is_none());
None => fail!("Float flow as root node"), float.box = Some(new_box);
Some(BlockFlow(*)) | }
Some(FloatFlow(*)) => {
let new_box = self.make_box(ctx, box_type, node, self.flow, builder);
debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)",
float.common.id,
new_box.id(),
node.debug_str());
assert!(float.box.is_none() && float.index.is_none());
float.box = Some(new_box);
}
Some(InlineFlow(inline)) => {
let new_box = self.make_box(ctx, box_type, node, self.flow, builder);
debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)",
float.common.id,
new_box.id(),
node.debug_str());
assert!(float.box.is_none() && float.index.is_none());
inline.boxes.push(new_box);
float.index = Some(inline.boxes.len() - 1);
}
_ => warn!("push_node() not implemented for flow f%d", self.flow.id())
}
},
_ => warn!("push_node() not implemented for flow f%d", self.flow.id()), _ => warn!("push_node() not implemented for flow f%d", self.flow.id()),
} }
} }
@ -211,16 +191,16 @@ impl BoxGenerator {
node: AbstractNode<LayoutView>) { node: AbstractNode<LayoutView>) {
debug!("BoxGenerator[f%d]: popping node: %s", self.flow.id(), node.debug_str()); debug!("BoxGenerator[f%d]: popping node: %s", self.flow.id(), node.debug_str());
match self.flow { match *self.flow {
InlineFlow(inline) => { InlineFlow(ref mut inline) => {
let inline = &mut *inline; let inline = &mut *inline;
if self.inline_spacers_needed_for_node(node) { if BoxGenerator::inline_spacers_needed_for_node(node) {
// If this non-leaf box generates extra horizontal spacing, add a SpacerBox for // If this non-leaf box generates extra horizontal spacing, add a SpacerBox for
// it. // it.
let result = self.make_inline_spacer_for_node_side(ctx, node, LogicalAfter); let result = BoxGenerator::make_inline_spacer_for_node_side(ctx, node, LogicalAfter);
for spacer in result.iter() { for spacer in result.iter() {
let boxes = &mut self.flow.inline().boxes; let boxes = &mut inline.boxes;
boxes.push(*spacer); boxes.push(*spacer);
} }
} }
@ -235,31 +215,29 @@ impl BoxGenerator {
inline.elems.add_mapping(node, &node_range); inline.elems.add_mapping(node, &node_range);
}, },
BlockFlow(*) => assert!(self.range_stack.len() == 0), BlockFlow(*) => assert!(self.range_stack.len() == 0),
FloatFlow(*) => assert!(self.range_stack.len() == 0),
_ => warn!("pop_node() not implemented for flow %?", self.flow.id()), _ => warn!("pop_node() not implemented for flow %?", self.flow.id()),
} }
} }
/// Disambiguate between different methods here instead of inlining, since each case has very /// Disambiguate between different methods here instead of inlining, since each case has very
/// different complexity. /// different complexity.
fn make_box(&self, fn make_box(layout_ctx: &LayoutContext,
layout_ctx: &LayoutContext,
ty: RenderBoxType, ty: RenderBoxType,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
flow_context: FlowContext,
builder: &mut LayoutTreeBuilder) builder: &mut LayoutTreeBuilder)
-> RenderBox { -> RenderBox {
let base = RenderBoxBase::new(node, flow_context, builder.next_box_id()); let base = RenderBoxBase::new(node, builder.next_box_id());
let result = match ty { let result = match ty {
RenderBox_Generic => GenericRenderBoxClass(@mut base), RenderBox_Generic => GenericRenderBoxClass(@mut base),
RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)), RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)),
RenderBox_Image => self.make_image_box(layout_ctx, node, base), RenderBox_Image => BoxGenerator::make_image_box(layout_ctx, node, base),
}; };
debug!("BoxGenerator: created box: %s", result.debug_str()); debug!("BoxGenerator: created box: %s", result.debug_str());
result result
} }
fn make_image_box(&self, fn make_image_box(layout_ctx: &LayoutContext,
layout_ctx: &LayoutContext,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
base: RenderBoxBase) base: RenderBoxBase)
-> RenderBox { -> RenderBox {
@ -297,6 +275,13 @@ impl BoxGenerator {
} }
enum BoxGenResult<'self> {
NoGenerator,
ParentGenerator,
SiblingGenerator,
NewGenerator(BoxGenerator<'self>),
Mixed(BoxGenerator<'self>, ~BoxGenResult<'self>),
}
impl LayoutTreeBuilder { impl LayoutTreeBuilder {
/* Debug-only ids */ /* Debug-only ids */
@ -305,59 +290,74 @@ impl LayoutTreeBuilder {
/// Creates necessary box(es) and flow context(s) for the current DOM node, /// Creates necessary box(es) and flow context(s) for the current DOM node,
/// and recurses on its children. /// and recurses on its children.
pub fn construct_recursively(&mut self, pub fn construct_recursively<'a>(&mut self,
layout_ctx: &LayoutContext, layout_ctx: &LayoutContext,
cur_node: AbstractNode<LayoutView>, cur_node: AbstractNode<LayoutView>,
parent_generator: @mut BoxGenerator, mut parent_generator: BoxGenerator<'a>,
prev_sibling_generator: Option<@mut BoxGenerator>) mut prev_sibling_generator: Option<BoxGenerator<'a>>)
-> Option<@mut BoxGenerator> { -> Option<BoxGenerator<'a>> {
debug!("Considering node: %s", cur_node.debug_str()); debug!("Considering node: %s", cur_node.debug_str());
let box_gen_result = {
let sibling_gen_ref = match prev_sibling_generator {
Some(ref mut generator) => Some(generator),
None => None,
};
self.box_generator_for_node(cur_node, &mut parent_generator, sibling_gen_ref)
};
debug!("result from generator_for_node: %?", &box_gen_result);
// Skip over nodes that don't belong in the flow tree // Skip over nodes that don't belong in the flow tree
let (this_generator, next_generator) = let (this_generator, next_generator) =
match self.box_generator_for_node(cur_node, parent_generator, prev_sibling_generator) { match box_gen_result {
NoGenerator => return prev_sibling_generator,
Some((gen, n_gen)) => (gen, n_gen), ParentGenerator => (parent_generator, None),
None => { return prev_sibling_generator; } SiblingGenerator => (prev_sibling_generator.take_unwrap(), None),
NewGenerator(gen) => (gen, None),
Mixed(gen, next_gen) => (gen, Some(match *next_gen {
ParentGenerator => parent_generator,
SiblingGenerator => prev_sibling_generator.take_unwrap(),
_ => fail!("Unexpect BoxGenResult")
}))
}; };
let mut this_generator = this_generator;
debug!("point a: %s", cur_node.debug_str()); debug!("point a: %s", cur_node.debug_str());
this_generator.push_node(layout_ctx, cur_node, self); this_generator.push_node(layout_ctx, cur_node, self);
debug!("point b: %s", cur_node.debug_str()); debug!("point b: %s", cur_node.debug_str());
// recurse on child nodes. // recurse on child nodes.
let mut prev_generator: Option<@mut BoxGenerator> = None; let prev_gen_cell = Cell::new(None);
for child_node in cur_node.children() { for child_node in cur_node.children() {
prev_generator = self.construct_recursively(layout_ctx, child_node, this_generator, prev_generator); do this_generator.with_clone |clone| {
let mut prev_generator = prev_gen_cell.take();
prev_generator = self.construct_recursively(layout_ctx, child_node, clone, prev_generator);
prev_gen_cell.put_back(prev_generator);
}
} }
this_generator.pop_node(layout_ctx, cur_node); this_generator.pop_node(layout_ctx, cur_node);
self.simplify_children_of_flow(layout_ctx, &mut this_generator.flow); self.simplify_children_of_flow(layout_ctx, this_generator.flow);
// store reference to the flow context which contains any match next_generator {
// boxes that correspond to child_flow.node. These boxes may Some(n_gen) => Some(n_gen),
// eventually be elided or split, but the mapping between None => Some(this_generator),
// nodes and FlowContexts should not change during layout.
let flow: &FlowContext = &this_generator.flow;
for child_flow in flow.children() {
do child_flow.with_base |child_node| {
let dom_node = child_node.node;
assert!(dom_node.has_layout_data());
dom_node.layout_data().flow = Some(child_flow);
}
} }
Some(next_generator)
} }
pub fn box_generator_for_node(&mut self,
pub fn box_generator_for_node<'a>(&mut self,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
parent_generator: @mut BoxGenerator, parent_generator: &mut BoxGenerator<'a>,
sibling_generator: Option<@mut BoxGenerator>) mut sibling_generator: Option<&mut BoxGenerator<'a>>)
-> Option<(@mut BoxGenerator, @mut BoxGenerator)> { -> BoxGenResult<'a> {
let display = if node.is_element() { let display = if node.is_element() {
match node.style().display(node.is_root()) { match node.style().display(node.is_root()) {
CSSDisplayNone => return None, // tree ends here if 'display: none' CSSDisplayNone => return NoGenerator, // tree ends here if 'display: none'
// TODO(eatkinson) these are hacks so that the code doesn't crash // TODO(eatkinson) these are hacks so that the code doesn't crash
// when unsupported display values are used. They should be deleted // when unsupported display values are used. They should be deleted
// as they are implemented. // as they are implemented.
@ -368,8 +368,8 @@ impl LayoutTreeBuilder {
CSSDisplayTableHeaderGroup => CSSDisplayBlock, CSSDisplayTableHeaderGroup => CSSDisplayBlock,
CSSDisplayTableFooterGroup => CSSDisplayBlock, CSSDisplayTableFooterGroup => CSSDisplayBlock,
CSSDisplayTableRow => CSSDisplayBlock, CSSDisplayTableRow => CSSDisplayBlock,
CSSDisplayTableColumnGroup => return None, CSSDisplayTableColumnGroup => return NoGenerator,
CSSDisplayTableColumn => return None, CSSDisplayTableColumn => return NoGenerator,
CSSDisplayTableCell => CSSDisplayBlock, CSSDisplayTableCell => CSSDisplayBlock,
CSSDisplayTableCaption => CSSDisplayBlock, CSSDisplayTableCaption => CSSDisplayBlock,
v => v v => v
@ -379,11 +379,11 @@ impl LayoutTreeBuilder {
ElementNodeTypeId(_) => CSSDisplayInline, ElementNodeTypeId(_) => CSSDisplayInline,
TextNodeTypeId => CSSDisplayInline, TextNodeTypeId => CSSDisplayInline,
DoctypeNodeTypeId | CommentNodeTypeId => return None, DoctypeNodeTypeId | CommentNodeTypeId => return NoGenerator,
} }
}; };
let sibling_flow: Option<FlowContext> = sibling_generator.map(|gen| gen.flow); let sibling_flow: Option<&mut FlowContext> = sibling_generator.map_mut(|gen| &mut *gen.flow);
// TODO(eatkinson): use the value of the float property to // TODO(eatkinson): use the value of the float property to
// determine whether to float left or right. // determine whether to float left or right.
@ -398,105 +398,91 @@ impl LayoutTreeBuilder {
}; };
let new_generator = match (display, parent_generator.flow, sibling_flow) { let new_generator = match (display, &mut parent_generator.flow, sibling_flow) {
// Floats // Floats
(CSSDisplayBlock, BlockFlow(_), _) | (CSSDisplayBlock, & &BlockFlow(_), _) |
(CSSDisplayBlock, FloatFlow(_), _) if !is_float.is_none() => { (CSSDisplayBlock, & &FloatFlow(_), _) if !is_float.is_none() => {
self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap())) self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap()))
} }
// If we're placing a float after an inline, append the float to the inline flow, // If we're placing a float after an inline, append the float to the inline flow,
// then continue building from the inline flow in case there are more inlines // then continue building from the inline flow in case there are more inlines
// afterward. // afterward.
(CSSDisplayBlock, _, Some(InlineFlow(_))) if !is_float.is_none() => { (CSSDisplayBlock, _, Some(&InlineFlow(_))) if !is_float.is_none() => {
let float_generator = self.create_child_generator(node, let float_generator = self.create_child_generator(node,
sibling_generator.unwrap(), sibling_generator.unwrap(),
Flow_Float(is_float.unwrap())); Flow_Float(is_float.unwrap()));
return Some((float_generator, sibling_generator.unwrap())); return Mixed(float_generator, ~SiblingGenerator);
} }
// This is a catch-all case for when: // This is a catch-all case for when:
// a) sibling_flow is None // a) sibling_flow is None
// b) sibling_flow is a BlockFlow // b) sibling_flow is a BlockFlow
(CSSDisplayBlock, InlineFlow(_), _) if !is_float.is_none() => { (CSSDisplayBlock, & &InlineFlow(_), _) if !is_float.is_none() => {
self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap())) self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap()))
} }
(CSSDisplayBlock, BlockFlow(info), _) => match (info.is_root, node.parent_node()) { (CSSDisplayBlock, & &BlockFlow(ref info), _) => match (info.is_root, node.parent_node().is_some()) {
// 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.
(true, Some(_)) => { self.create_child_generator(node, parent_generator, Flow_Block) } (true, true) => self.create_child_generator(node, parent_generator, Flow_Block),
(true, None) => { parent_generator } (true, false) => { return ParentGenerator }
(false, _) => { (false, _) => {
self.create_child_generator(node, parent_generator, Flow_Block) self.create_child_generator(node, parent_generator, Flow_Block)
} }
}, },
(CSSDisplayBlock, FloatFlow(*), _) => { (CSSDisplayBlock, & &FloatFlow(*), _) => {
self.create_child_generator(node, parent_generator, Flow_Block) self.create_child_generator(node, parent_generator, Flow_Block)
} }
// Inlines that are children of inlines are part of the same flow // Inlines that are children of inlines are part of the same flow
(CSSDisplayInline, InlineFlow(*), _) => parent_generator, (CSSDisplayInline, & &InlineFlow(*), _) => return ParentGenerator,
(CSSDisplayInlineBlock, InlineFlow(*), _) => parent_generator, (CSSDisplayInlineBlock, & &InlineFlow(*), _) => return ParentGenerator,
// Inlines that are children of blocks create new flows if their // Inlines that are children of blocks create new flows if their
// previous sibling was a block. // previous sibling was a block.
(CSSDisplayInline, BlockFlow(*), Some(BlockFlow(*))) | (CSSDisplayInline, & &BlockFlow(*), Some(&BlockFlow(*))) |
(CSSDisplayInlineBlock, BlockFlow(*), Some(BlockFlow(*))) => { (CSSDisplayInlineBlock, & &BlockFlow(*), Some(&BlockFlow(*))) => {
self.create_child_generator(node, parent_generator, Flow_Inline) self.create_child_generator(node, parent_generator, Flow_Inline)
} }
// The first two cases should only be hit when a FloatFlow // The first two cases should only be hit when a FloatFlow
// is the first child of a BlockFlow. Other times, we will // is the first child of a BlockFlow. Other times, we will
(CSSDisplayInline, _, Some(FloatFlow(*))) | (CSSDisplayInline, _, Some(&FloatFlow(*))) |
(CSSDisplayInlineBlock, _, Some(FloatFlow(*))) | (CSSDisplayInlineBlock, _, Some(&FloatFlow(*))) |
(CSSDisplayInline, FloatFlow(*), _) | (CSSDisplayInline, & &FloatFlow(*), _) |
(CSSDisplayInlineBlock, FloatFlow(*), _) => { (CSSDisplayInlineBlock, & &FloatFlow(*), _) => {
self.create_child_generator(node, parent_generator, Flow_Inline) self.create_child_generator(node, parent_generator, Flow_Inline)
} }
// Inlines whose previous sibling was not a block try to use their // Inlines whose previous sibling was not a block try to use their
// sibling's flow context. // sibling's flow context.
(CSSDisplayInline, BlockFlow(*), _) | (CSSDisplayInline, & &BlockFlow(*), _) |
(CSSDisplayInlineBlock, BlockFlow(*), _) => { (CSSDisplayInlineBlock, & &BlockFlow(*), _) => {
self.create_child_generator_if_needed(node, return match sibling_generator {
parent_generator, None => NewGenerator(self.create_child_generator(node,
sibling_generator, parent_generator,
Flow_Inline) Flow_Inline)),
Some(*) => SiblingGenerator
}
} }
// TODO(eatkinson): blocks that are children of inlines need // TODO(eatkinson): blocks that are children of inlines need
// to split their parent flows. // to split their parent flows.
_ => parent_generator _ => return ParentGenerator
}; };
// Usually, the node we add boxes to will be prev_sibling on the NewGenerator(new_generator)
// next call to this function.
Some((new_generator, new_generator))
} }
pub fn create_child_generator(&mut self, pub fn create_child_generator<'a>(&mut self,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
parent_generator: @mut BoxGenerator, parent_generator: &mut BoxGenerator<'a>,
ty: FlowContextType) ty: FlowContextType)
-> @mut BoxGenerator { -> BoxGenerator<'a> {
let new_flow = self.make_flow(ty, node); let new_flow = self.make_flow(ty, node);
parent_generator.flow.add_child(new_flow); parent_generator.flow.add_new_child(new_flow);
BoxGenerator::new(parent_generator.flow.last_child().unwrap())
@mut BoxGenerator::new(new_flow)
}
pub fn create_child_generator_if_needed(&mut self,
node: AbstractNode<LayoutView>,
parent_generator: @mut BoxGenerator,
maybe_generator: Option<@mut BoxGenerator>,
ty: FlowContextType)
-> @mut BoxGenerator {
match maybe_generator {
None => self.create_child_generator(node, parent_generator, ty),
Some(gen) => gen
}
} }
/// Fix up any irregularities such as: /// Fix up any irregularities such as:
@ -513,74 +499,71 @@ impl LayoutTreeBuilder {
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 = *parent_flow; for child_ctx in parent_flow.child_iter() {
for child_ctx in flow.children() {
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,
_ => {} _ => {}
} }
} }
if found_child_block && found_child_inline { if found_child_block && found_child_inline {
self.fixup_split_inline(*parent_flow) self.fixup_split_inline(parent_flow)
} }
}, },
BlockFlow(*) | FloatFlow(*) => { BlockFlow(*) | FloatFlow(*) => {
// FIXME: this will create refcounted cycles between the removed flow and any
// of its RenderBox or FlowContext children, and possibly keep alive other junk
// check first/last child for whitespace-ness // check first/last child for whitespace-ness
let first_child = do parent_flow.with_base |parent_node| { let mut do_remove = false;
parent_node.first_child let p_id = parent_flow.id();
}; do parent_flow.with_first_child |mut first_child| {
for &first_flow in first_child.iter() { for first_flow in first_child.mut_iter() {
if first_flow.starts_inline_flow() { if first_flow.starts_inline_flow() {
// FIXME: workaround for rust#6393 // FIXME: workaround for rust#6393
let mut do_remove = false; {
{ let boxes = &first_flow.imm_inline().boxes;
let boxes = &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 \
debug!("LayoutTreeBuilder: pruning whitespace-only first child \ flow f%d from parent f%d",
flow f%d from parent f%d", first_flow.id(),
first_flow.id(), p_id);
parent_flow.id()); do_remove = true;
do_remove = true; }
} }
} }
if (do_remove) {
(*parent_flow).remove_child(first_flow);
}
} }
} }
if (do_remove) {
parent_flow.remove_first();
}
let last_child = do parent_flow.with_base |parent_node| {
parent_node.last_child do_remove = false;
}; let p_id = parent_flow.id();
for &last_flow in last_child.iter() { do parent_flow.with_last_child |mut last_child| {
if last_flow.starts_inline_flow() { for last_flow in last_child.mut_iter() {
// FIXME: workaround for rust#6393 if last_flow.starts_inline_flow() {
let mut do_remove = false; // FIXME: workaround for rust#6393
{ {
let boxes = &last_flow.inline().boxes; let boxes = &last_flow.imm_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 \ debug!("LayoutTreeBuilder: pruning whitespace-only last child \
flow f%d from parent f%d", flow f%d from parent f%d",
last_flow.id(), last_flow.id(),
parent_flow.id()); p_id);
do_remove = true; do_remove = true;
}
} }
} }
if (do_remove) {
(*parent_flow).remove_child(last_flow);
}
} }
} }
if (do_remove) {
parent_flow.remove_last();
}
// Issue 543: We only need to do this if there are inline child // Issue 543: We only need to do this if there are inline child
// flows, but there's not a quick way to check at the moment. // flows, but there's not a quick way to check at the moment.
for child_flow in (*parent_flow).children() { for child_flow in (*parent_flow).child_iter() {
match child_flow { match *child_flow {
InlineFlow(*) | InlineBlockFlow(*) => { InlineFlow(*) | InlineBlockFlow(*) => {
let mut scanner = TextRunScanner::new(); let mut scanner = TextRunScanner::new();
scanner.scan_for_runs(ctx, child_flow); scanner.scan_for_runs(ctx, child_flow);
@ -593,7 +576,7 @@ impl LayoutTreeBuilder {
} }
} }
pub fn fixup_split_inline(&self, _: FlowContext) { pub fn fixup_split_inline(&self, _: &mut 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")
} }
@ -601,11 +584,14 @@ impl LayoutTreeBuilder {
/// Entry point for box creation. Should only be called on the root DOM element. /// Entry point for box creation. Should only be called on the root DOM element.
pub fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode<LayoutView>) pub fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode<LayoutView>)
-> Result<FlowContext, ()> { -> Result<FlowContext, ()> {
let new_flow = self.make_flow(Flow_Root, root); debug!("Constructing flow tree for DOM: ");
let new_generator = @mut BoxGenerator::new(new_flow); root.dump();
self.root_flow = Some(new_flow); let mut new_flow = self.make_flow(Flow_Root, root);
self.construct_recursively(layout_ctx, root, new_generator, None); {
let new_generator = BoxGenerator::new(&mut new_flow);
self.construct_recursively(layout_ctx, root, new_generator, None);
}
return Ok(new_flow) return Ok(new_flow)
} }
@ -613,13 +599,13 @@ impl LayoutTreeBuilder {
pub fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode<LayoutView>) -> FlowContext { pub fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode<LayoutView>) -> FlowContext {
let info = FlowData::new(self.next_flow_id(), node); let info = FlowData::new(self.next_flow_id(), node);
let result = match ty { let result = match ty {
Flow_Absolute => AbsoluteFlow(@mut info), Flow_Absolute => AbsoluteFlow(~info),
Flow_Block => BlockFlow(@mut BlockFlowData::new(info)), Flow_Block => BlockFlow(~BlockFlowData::new(info)),
Flow_Float(f_type) => FloatFlow(@mut FloatFlowData::new(info, f_type)), Flow_Float(f_type) => FloatFlow(~FloatFlowData::new(info, f_type)),
Flow_InlineBlock => InlineBlockFlow(@mut info), Flow_InlineBlock => InlineBlockFlow(~info),
Flow_Inline => InlineFlow(@mut InlineFlowData::new(info)), Flow_Inline => InlineFlow(~InlineFlowData::new(info)),
Flow_Root => BlockFlow(@mut BlockFlowData::new_root(info)), Flow_Root => BlockFlow(~BlockFlowData::new_root(info)),
Flow_Table => TableFlow(@mut info), Flow_Table => TableFlow(~info),
}; };
debug!("LayoutTreeBuilder: created flow: %s", result.debug_str()); debug!("LayoutTreeBuilder: created flow: %s", result.debug_str());
result result

View file

@ -6,20 +6,29 @@
use layout::box::RenderBox; use layout::box::RenderBox;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use std::cast::transmute;
use script::dom::node::AbstractNode;
use gfx; use gfx;
use newcss; use newcss;
/// Extra display list data is either nothing (if the display list is to be rendered) or the /// Display list data is usually an AbstractNode with view () to indicate
/// originating render box (if the display list is generated for hit testing). /// that nodes in this view shoud not really be touched. The idea is to
/// store the nodes in the display list and have layout transmute them.
pub trait ExtraDisplayListData { pub trait ExtraDisplayListData {
fn new(box: RenderBox) -> Self; fn new(box: RenderBox) -> Self;
} }
/// The type representing the lack of extra display list data. This is used when sending display
/// list data off to be rendered.
pub type Nothing = (); pub type Nothing = ();
impl ExtraDisplayListData for AbstractNode<()> {
fn new (box: RenderBox) -> AbstractNode<()> {
unsafe {
transmute(box.node())
}
}
}
impl ExtraDisplayListData for Nothing { impl ExtraDisplayListData for Nothing {
fn new(_: RenderBox) -> Nothing { fn new(_: RenderBox) -> Nothing {
() ()

View file

@ -5,7 +5,7 @@
use layout::box::{RenderBox}; use layout::box::{RenderBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{FloatFlow, FlowData}; use layout::flow::{FlowData};
use layout::model::{MaybeAuto}; use layout::model::{MaybeAuto};
use layout::float_context::{FloatContext, PlacementInfo, FloatType}; use layout::float_context::{FloatContext, PlacementInfo, FloatType};
@ -15,7 +15,6 @@ use geom::rect::Rect;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use gfx::geometry::Au; use gfx::geometry::Au;
use gfx::geometry; use gfx::geometry;
use servo_util::tree::TreeNodeRef;
pub struct FloatFlowData { pub struct FloatFlowData {
/// Data common to all flows. /// Data common to all flows.
@ -54,7 +53,6 @@ impl FloatFlowData {
} }
pub fn teardown(&mut self) { pub fn teardown(&mut self) {
self.common.teardown();
for box in self.box.iter() { for box in self.box.iter() {
box.teardown(); box.teardown();
} }
@ -64,12 +62,12 @@ impl FloatFlowData {
} }
impl FloatFlowData { impl FloatFlowData {
pub fn bubble_widths_float(@mut self, ctx: &LayoutContext) { pub fn bubble_widths_float(&mut self, ctx: &LayoutContext) {
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 mut num_floats = 0; let mut num_floats = 0;
for child_ctx in FloatFlow(self).children() { for child_ctx in self.common.child_iter() {
//assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); //assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
do child_ctx.with_mut_base |child_node| { do child_ctx.with_mut_base |child_node| {
@ -97,7 +95,7 @@ impl FloatFlowData {
self.common.pref_width = pref_width; self.common.pref_width = pref_width;
} }
pub fn assign_widths_float(@mut self) { pub fn assign_widths_float(&mut self) {
debug!("assign_widths_float: assigning width for flow %?", self.common.id); debug!("assign_widths_float: assigning width for flow %?", self.common.id);
// position.size.width is set by parent even though we don't know // position.size.width is set by parent even though we don't know
// position.origin yet. // position.origin yet.
@ -162,7 +160,7 @@ impl FloatFlowData {
self.common.position.size.width = remaining_width; self.common.position.size.width = remaining_width;
let has_inorder_children = self.common.num_floats > 0; let has_inorder_children = self.common.num_floats > 0;
for kid in FloatFlow(self).children() { for kid in self.common.child_iter() {
//assert!(kid.starts_block_flow() || kid.starts_inline_flow()); //assert!(kid.starts_block_flow() || kid.starts_inline_flow());
do kid.with_mut_base |child_node| { do kid.with_mut_base |child_node| {
@ -177,7 +175,7 @@ impl FloatFlowData {
} }
} }
pub fn assign_height_inorder_float(@mut self) { pub fn assign_height_inorder_float(&mut self) {
debug!("assign_height_inorder_float: assigning height for float %?", self.common.id); debug!("assign_height_inorder_float: assigning height for float %?", self.common.id);
// assign_height_float was already called by the traversal function // assign_height_float was already called by the traversal function
// so this is well-defined // so this is well-defined
@ -222,12 +220,12 @@ impl FloatFlowData {
self.rel_pos = self.common.floats_out.last_float_pos(); self.rel_pos = self.common.floats_out.last_float_pos();
} }
pub fn assign_height_float(@mut self, ctx: &mut LayoutContext) { pub fn assign_height_float(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_float: assigning height for float %?", self.common.id); debug!("assign_height_float: assigning height for float %?", self.common.id);
let has_inorder_children = self.common.num_floats > 0; let has_inorder_children = self.common.num_floats > 0;
if has_inorder_children { if has_inorder_children {
let mut float_ctx = FloatContext::new(self.floated_children); let mut float_ctx = FloatContext::new(self.floated_children);
for kid in FloatFlow(self).children() { for kid in self.common.child_iter() {
do kid.with_mut_base |child_node| { do kid.with_mut_base |child_node| {
child_node.floats_in = float_ctx.clone(); child_node.floats_in = float_ctx.clone();
} }
@ -248,7 +246,7 @@ impl FloatFlowData {
} }
} }
for kid in FloatFlow(self).children() { for kid in self.common.child_iter() {
do kid.with_mut_base |child_node| { do kid.with_mut_base |child_node| {
child_node.position.origin.y = cur_y; child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height; cur_y = cur_y + child_node.position.size.height;
@ -289,7 +287,7 @@ impl FloatFlowData {
} }
} }
pub fn build_display_list_float<E:ExtraDisplayListData>(@mut self, pub fn build_display_list_float<E:ExtraDisplayListData>(&mut self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
dirty: &Rect<Au>, dirty: &Rect<Au>,
list: &Cell<DisplayList<E>>) list: &Cell<DisplayList<E>>)
@ -301,7 +299,7 @@ impl FloatFlowData {
} }
let abs_rect = Rect(self.common.abs_position, self.common.position.size); let abs_rect = Rect(self.common.abs_position, self.common.position.size);
if !abs_rect.intersects(dirty) { if !abs_rect.intersects(dirty) {
return false; return true;
} }
@ -315,14 +313,13 @@ impl FloatFlowData {
// TODO: handle any out-of-flow elements // TODO: handle any out-of-flow elements
// go deeper into the flow tree // go deeper into the flow tree
let flow = FloatFlow(self); for child in self.common.child_iter() {
for child in flow.children() {
do child.with_mut_base |base| { do child.with_mut_base |base| {
base.abs_position = offset + base.position.origin; base.abs_position = offset + base.position.origin;
} }
} }
true false
} }
} }

View file

@ -48,12 +48,12 @@ pub struct PlacementInfo{
/// destroy the context on modification. /// destroy the context on modification.
pub enum FloatContext { pub enum FloatContext {
Invalid, Invalid,
Valid(FloatContextBase) Valid(~FloatContextBase)
} }
impl FloatContext { impl FloatContext {
pub fn new(num_floats: uint) -> FloatContext { pub fn new(num_floats: uint) -> FloatContext {
Valid(FloatContextBase::new(num_floats)) Valid(~FloatContextBase::new(num_floats))
} }
#[inline(always)] #[inline(always)]
@ -68,7 +68,7 @@ impl FloatContext {
fn with_mut_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R { fn with_mut_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R {
match *self { match *self {
Invalid => fail!("Float context no longer available"), Invalid => fail!("Float context no longer available"),
Valid(ref mut base) => callback(base) Valid(ref mut base) => callback(&mut **base)
} }
} }
@ -76,7 +76,7 @@ impl FloatContext {
pub fn with_base<R>(&self, callback: &fn(&FloatContextBase) -> R) -> R { pub fn with_base<R>(&self, callback: &fn(&FloatContextBase) -> R) -> R {
match *self { match *self {
Invalid => fail!("Float context no longer available"), Invalid => fail!("Float context no longer available"),
Valid(ref base) => callback(base) Valid(ref base) => callback(& **base)
} }
} }

View file

@ -34,6 +34,8 @@ use layout::inline::{InlineFlowData};
use layout::float_context::{FloatContext, Invalid, FloatType}; use layout::float_context::{FloatContext, Invalid, FloatType};
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use css::node_style::StyledNode; use css::node_style::StyledNode;
use extra::dlist::{DList,MutDListIterator};
use extra::container::Deque;
use std::cell::Cell; use std::cell::Cell;
use std::io::stderr; use std::io::stderr;
@ -42,18 +44,16 @@ use geom::rect::Rect;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use gfx::geometry::Au; use gfx::geometry::Au;
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::{TreeNode, TreeNodeRef};
/// 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.
#[deriving(Clone)]
pub enum FlowContext { pub enum FlowContext {
AbsoluteFlow(@mut FlowData), AbsoluteFlow(~FlowData),
BlockFlow(@mut BlockFlowData), BlockFlow(~BlockFlowData),
FloatFlow(@mut FloatFlowData), FloatFlow(~FloatFlowData),
InlineBlockFlow(@mut FlowData), InlineBlockFlow(~FlowData),
InlineFlow(@mut InlineFlowData), InlineFlow(~InlineFlowData),
TableFlow(@mut FlowData), TableFlow(~FlowData),
} }
pub enum FlowContextType { pub enum FlowContextType {
@ -67,142 +67,161 @@ pub enum FlowContextType {
} }
impl FlowContext { impl FlowContext {
pub fn teardown(&self) { pub fn each_bu_sub_inorder (&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
match *self { for kid in self.child_iter() {
AbsoluteFlow(data) |
InlineBlockFlow(data) |
TableFlow(data) => data.teardown(),
BlockFlow(data) => data.teardown(),
FloatFlow(data) => data.teardown(),
InlineFlow(data) => data.teardown()
}
}
/// Like traverse_preorder, but don't end the whole traversal if the callback
/// returns false.
//
// FIXME: Unify this with traverse_preorder_prune, which takes a separate
// 'prune' function.
pub fn partially_traverse_preorder(&self, callback: &fn(FlowContext) -> bool) {
if !callback((*self).clone()) {
return;
}
for kid in self.children() {
// FIXME: Work around rust#2202. We should be able to pass the callback directly. // FIXME: Work around rust#2202. We should be able to pass the callback directly.
kid.partially_traverse_preorder(|a| callback(a)); if !kid.each_bu_sub_inorder(|a| callback(a)) {
} return false;
} }
pub fn traverse_bu_sub_inorder (&self, callback: &fn(FlowContext)) {
for kid in self.children() {
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
kid.traverse_bu_sub_inorder(|a| callback(a));
} }
if !self.is_inorder() { if !self.is_inorder() {
callback((*self).clone()) callback(self)
} else {
true
} }
} }
pub fn each_preorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool,
callback: &fn(&mut FlowContext) -> bool)
-> bool {
if prune(self) {
return true;
}
if !callback(self) {
return false;
}
for kid in self.child_iter() {
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
if !kid.each_preorder_prune(|a| prune(a), |a| callback(a)) {
return false;
}
}
true
}
pub fn each_postorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool,
callback: &fn(&mut FlowContext) -> bool)
-> bool {
if prune(self) {
return true;
}
for kid in self.child_iter() {
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
if !kid.each_postorder_prune(|a| prune(a), |a| callback(a)) {
return false;
}
}
callback(self)
}
pub fn each_preorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
self.each_preorder_prune(|_| false, callback)
}
pub fn each_postorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
self.each_postorder_prune(|_| false, callback)
}
} }
impl FlowData { impl<'self> FlowContext {
pub fn teardown(&mut self) { pub fn leaf(&self) -> bool {
// Under the assumption that all flows exist in a tree, do self.with_base |base| {
// we must restrict ourselves to finalizing flows that base.children.len() == 0
// are descendents and subsequent siblings to ourselves,
// or we risk dynamic borrow failures.
self.parent = None;
for flow in self.first_child.iter() {
flow.teardown();
} }
self.first_child = None;
self.last_child = None;
for flow in self.next_sibling.iter() {
flow.teardown();
}
self.next_sibling = None;
self.prev_sibling = None;
} }
pub fn add_new_child(&mut self, new_child: FlowContext) {
let cell = Cell::new(new_child);
do self.with_mut_base |base| {
base.children.push_back(cell.take());
}
}
pub fn with_first_child<R>(&mut self, cb: &fn(Option<&mut FlowContext>) -> R) -> R {
do self.with_mut_base |base| {
cb(base.children.front_mut())
}
}
pub fn with_last_child<R>(&mut self, cb: &fn(Option<&mut FlowContext>) -> R) -> R {
do self.with_mut_base |base| {
cb(base.children.back_mut())
}
}
pub fn last_child(&'self mut self) -> Option<&'self mut FlowContext> {
self.mut_base().children.back_mut()
}
pub fn remove_first(&mut self) {
do self.with_mut_base |base| {
base.children.pop_front();
}
}
pub fn remove_last(&mut self) {
do self.with_mut_base |base| {
base.children.pop_back();
}
}
pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a, FlowContext> {
self.mut_base().children.mut_iter()
}
} }
impl TreeNodeRef<FlowData> for FlowContext { impl<'self> FlowContext {
fn with_base<R>(&self, callback: &fn(&FlowData) -> R) -> R { pub fn with_base<R>(&self, callback: &fn(&FlowData) -> R) -> R {
match *self { match *self {
AbsoluteFlow(info) => callback(info), AbsoluteFlow(ref info) => callback(&**info),
BlockFlow(info) => { BlockFlow(ref info) => {
callback(&info.common) callback(&info.common)
} }
FloatFlow(info) => callback(&info.common), FloatFlow(ref info) => callback(&info.common),
InlineBlockFlow(info) => callback(info), InlineBlockFlow(ref info) => callback(&**info),
InlineFlow(info) => { InlineFlow(ref info) => {
callback(&info.common) callback(&info.common)
} }
TableFlow(info) => callback(info) TableFlow(ref info) => callback(&**info)
} }
} }
fn with_mut_base<R>(&self, callback: &fn(&mut FlowData) -> R) -> R { pub fn with_mut_base<R>(&mut self, callback: &fn(&mut FlowData) -> R) -> R {
match *self { match *self {
AbsoluteFlow(info) => callback(info), AbsoluteFlow(ref mut info) => callback(&mut **info),
BlockFlow(info) => { BlockFlow(ref mut info) => {
callback(&mut info.common) callback(&mut info.common)
} }
FloatFlow(info) => callback(&mut info.common), FloatFlow(ref mut info) => callback(&mut info.common),
InlineBlockFlow(info) => callback(info), InlineBlockFlow(ref mut info) => callback(&mut **info),
InlineFlow(info) => { InlineFlow(ref mut info) => {
callback(&mut info.common) callback(&mut info.common)
} }
TableFlow(info) => callback(info), TableFlow(ref mut info) => callback(&mut **info),
} }
} }
pub fn mut_base(&'self mut self) -> &'self mut FlowData {
fn parent_node(node: &FlowData) -> Option<FlowContext> { match *self {
node.parent AbsoluteFlow(ref mut info) => &mut(**info),
} BlockFlow(ref mut info) => {
&mut info.common
fn first_child(node: &FlowData) -> Option<FlowContext> { }
node.first_child FloatFlow(ref mut info) => &mut info.common,
} InlineBlockFlow(ref mut info) => &mut(**info),
InlineFlow(ref mut info) => {
fn last_child(node: &FlowData) -> Option<FlowContext> { &mut info.common
node.last_child }
} TableFlow(ref mut info) => &mut(**info),
}
fn prev_sibling(node: &FlowData) -> Option<FlowContext> {
node.prev_sibling
}
fn next_sibling(node: &FlowData) -> Option<FlowContext> {
node.next_sibling
}
fn set_parent_node(node: &mut FlowData, new_parent_node: Option<FlowContext>) {
node.parent = new_parent_node
}
fn set_first_child(node: &mut FlowData, new_first_child: Option<FlowContext>) {
node.first_child = new_first_child
}
fn set_last_child(node: &mut FlowData, new_last_child: Option<FlowContext>) {
node.last_child = new_last_child
}
fn set_prev_sibling(node: &mut FlowData, new_prev_sibling: Option<FlowContext>) {
node.prev_sibling = new_prev_sibling
}
fn set_next_sibling(node: &mut FlowData, new_next_sibling: Option<FlowContext>) {
node.next_sibling = new_next_sibling
} }
} }
impl TreeNode<FlowContext> for FlowData { }
/// Data common to all flows. /// Data common to all flows.
/// ///
/// FIXME: We need a naming convention for pseudo-inheritance like this. How about /// FIXME: We need a naming convention for pseudo-inheritance like this. How about
@ -211,11 +230,7 @@ pub struct FlowData {
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
restyle_damage: RestyleDamage, restyle_damage: RestyleDamage,
parent: Option<FlowContext>, children: DList<FlowContext>,
first_child: Option<FlowContext>,
last_child: Option<FlowContext>,
prev_sibling: Option<FlowContext>,
next_sibling: Option<FlowContext>,
/* TODO (Issue #87): debug only */ /* TODO (Issue #87): debug only */
id: int, id: int,
@ -237,7 +252,6 @@ pub struct BoxIterator {
priv boxes: ~[RenderBox], priv boxes: ~[RenderBox],
priv index: uint, priv index: uint,
} }
impl Iterator<RenderBox> for BoxIterator { impl Iterator<RenderBox> for BoxIterator {
fn next(&mut self) -> Option<RenderBox> { fn next(&mut self) -> Option<RenderBox> {
if self.index >= self.boxes.len() { if self.index >= self.boxes.len() {
@ -249,18 +263,13 @@ impl Iterator<RenderBox> for BoxIterator {
} }
} }
} }
impl FlowData { impl FlowData {
pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData { pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData {
FlowData { FlowData {
node: node, node: node,
restyle_damage: node.restyle_damage(), restyle_damage: node.restyle_damage(),
parent: None, children: DList::new(),
first_child: None,
last_child: None,
prev_sibling: None,
next_sibling: None,
id: id, id: id,
@ -274,6 +283,11 @@ impl FlowData {
is_inorder: false is_inorder: false
} }
} }
pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a, FlowContext> {
self.children.mut_iter()
}
} }
impl<'self> FlowContext { impl<'self> FlowContext {
@ -302,75 +316,88 @@ impl<'self> FlowContext {
} }
} }
pub fn inline(&'self mut self) -> &'self mut InlineFlowData {
pub fn inline(&self) -> @mut InlineFlowData {
match *self { match *self {
InlineFlow(info) => info, InlineFlow(ref mut info) => &mut (**info),
_ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id())) _ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id()))
} }
} }
pub fn block(&self) -> @mut BlockFlowData { pub fn imm_inline(&'self self) -> &'self InlineFlowData {
match *self { match *self {
BlockFlow(info) => info, InlineFlow(ref info) => &**info,
_ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id()))
}
}
pub fn block(&'self mut self) -> &'self mut BlockFlowData {
match *self {
BlockFlow(ref mut info) => &mut (**info),
_ => fail!(fmt!("Tried to access block data of non-block: f%d", self.id())) _ => fail!(fmt!("Tried to access block data of non-block: f%d", self.id()))
} }
} }
pub fn root(&self) -> @mut BlockFlowData { pub fn root(&'self mut self) -> &'self mut BlockFlowData {
match *self { match *self {
BlockFlow(info) if info.is_root => info, BlockFlow(ref mut info) if info.is_root => &mut (**info),
_ => fail!(fmt!("Tried to access root block data of non-root: f%d", self.id())) _ => fail!(fmt!("Tried to access root block data of non-root: f%d", self.id()))
} }
} }
pub fn bubble_widths(&self, ctx: &mut LayoutContext) { pub fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
debug!("FlowContext: bubbling widths for f%?", self.id());
match *self { match *self {
BlockFlow(info) => info.bubble_widths_block(ctx), BlockFlow(ref mut info) => info.bubble_widths_block(ctx),
InlineFlow(info) => info.bubble_widths_inline(ctx), InlineFlow(ref mut info) => info.bubble_widths_inline(ctx),
FloatFlow(info) => info.bubble_widths_float(ctx), FloatFlow(ref mut info) => info.bubble_widths_float(ctx),
_ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id())) _ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id()))
} }
} }
pub fn assign_widths(&self, ctx: &mut LayoutContext) { pub fn assign_widths(&mut self, ctx: &mut LayoutContext) {
debug!("FlowContext: assigning widths for f%?", self.id());
match *self { match *self {
BlockFlow(info) => info.assign_widths_block(ctx), BlockFlow(ref mut info) => info.assign_widths_block(ctx),
InlineFlow(info) => info.assign_widths_inline(ctx), InlineFlow(ref mut info) => info.assign_widths_inline(ctx),
FloatFlow(info) => info.assign_widths_float(), FloatFlow(ref mut info) => info.assign_widths_float(),
_ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id())) _ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id()))
} }
} }
pub fn assign_height(&self, ctx: &mut LayoutContext) { pub fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("FlowContext: assigning height for f%?", self.id());
match *self { match *self {
BlockFlow(info) => info.assign_height_block(ctx), BlockFlow(ref mut info) => info.assign_height_block(ctx),
InlineFlow(info) => info.assign_height_inline(ctx), InlineFlow(ref mut info) => info.assign_height_inline(ctx),
FloatFlow(info) => info.assign_height_float(ctx), FloatFlow(ref mut info) => info.assign_height_float(ctx),
_ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id())) _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id()))
} }
} }
pub fn assign_height_inorder(&self, ctx: &mut LayoutContext) { pub fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
match *self { match *self {
BlockFlow(info) => info.assign_height_inorder_block(ctx), BlockFlow(ref mut info) => info.assign_height_inorder_block(ctx),
InlineFlow(info) => info.assign_height_inorder_inline(ctx), InlineFlow(ref mut info) => info.assign_height_inorder_inline(ctx),
FloatFlow(info) => info.assign_height_inorder_float(), FloatFlow(ref mut info) => info.assign_height_inorder_float(),
_ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id())) _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id()))
} }
} }
pub fn build_display_list<E:ExtraDisplayListData>(&self, pub fn build_display_list<E:ExtraDisplayListData>(&mut self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
dirty: &Rect<Au>, dirty: &Rect<Au>,
list: &Cell<DisplayList<E>>) list: &Cell<DisplayList<E>>)
-> bool { -> bool {
debug!("FlowContext: building display list for f%?", self.id());
match *self { match *self {
BlockFlow(info) => info.build_display_list_block(builder, dirty, list), BlockFlow(ref mut info) => info.build_display_list_block(builder, dirty, list),
InlineFlow(info) => info.build_display_list_inline(builder, dirty, list), InlineFlow(ref mut info) => info.build_display_list_inline(builder, dirty, list),
FloatFlow(info) => info.build_display_list_float(builder, dirty, list), FloatFlow(ref mut info) => info.build_display_list_float(builder, dirty, list),
_ => { _ => {
fail!("Tried to build_display_list_recurse of flow: %?", self) fail!("Tried to build_display_list_recurse of flow: %?", self)
} }
@ -388,16 +415,14 @@ impl<'self> FlowContext {
// 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:Clone>(&self, seed: B, cb: &fn(a: B, b: RenderBox) -> B) -> B { pub fn foldl_all_boxes<B:Clone>(&mut self, seed: B, cb: &fn(a: B, b: RenderBox) -> B) -> B {
match *self { match *self {
BlockFlow(block) => { BlockFlow(ref mut block) => {
let block = &mut *block;
do block.box.map_default(seed.clone()) |box| { do block.box.map_default(seed.clone()) |box| {
cb(seed.clone(), *box) cb(seed.clone(), *box)
} }
} }
InlineFlow(inline) => { InlineFlow(ref mut inline) => {
let inline = &mut *inline;
do inline.boxes.iter().fold(seed) |acc, box| { do inline.boxes.iter().fold(seed) |acc, box| {
cb(acc.clone(), *box) cb(acc.clone(), *box)
} }
@ -406,7 +431,7 @@ impl<'self> FlowContext {
} }
} }
pub fn foldl_boxes_for_node<B:Clone>(&self, pub fn foldl_boxes_for_node<B:Clone>(&mut self,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
seed: B, seed: B,
callback: &fn(a: B, RenderBox) -> B) callback: &fn(a: B, RenderBox) -> B)
@ -420,11 +445,11 @@ impl<'self> FlowContext {
} }
} }
pub fn iter_all_boxes(&self) -> BoxIterator { pub fn iter_all_boxes(&mut self) -> BoxIterator {
BoxIterator { BoxIterator {
boxes: match *self { boxes: match *self {
BlockFlow (block) => block.box.map_default(~[], |&x| ~[x]), BlockFlow (ref mut block) => block.box.map_default(~[], |&x| ~[x]),
InlineFlow(inline) => inline.boxes.clone(), InlineFlow(ref mut inline) => inline.boxes.clone(),
_ => 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))
}, },
index: 0, index: 0,
@ -432,12 +457,12 @@ impl<'self> FlowContext {
} }
/// Dumps the flow tree for debugging. /// Dumps the flow tree for debugging.
pub fn dump(&self) { pub fn dump(&mut 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.
pub fn dump_indent(&self, indent: uint) { pub fn dump_indent(&mut self, indent: uint) {
let mut s = ~"|"; let mut s = ~"|";
for _ in range(0, indent) { for _ in range(0, indent) {
s.push_str("---- "); s.push_str("---- ");
@ -447,27 +472,27 @@ impl<'self> FlowContext {
stderr().write_line(s); stderr().write_line(s);
// FIXME: this should have a pure/const version? // FIXME: this should have a pure/const version?
for child in self.children() { for child in self.child_iter() {
child.dump_indent(indent + 1) child.dump_indent(indent + 1)
} }
} }
pub fn debug_str(&self) -> ~str { pub fn debug_str(&self) -> ~str {
let repr = match *self { let repr = match *self {
InlineFlow(inline) => { InlineFlow(ref inline) => {
let mut s = inline.boxes.iter().fold(~"InlineFlow(children=", |s, box| { let mut s = inline.boxes.iter().fold(~"InlineFlow(children=", |s, box| {
fmt!("%s b%d", s, box.id()) fmt!("%s b%d", s, box.id())
}); });
s.push_str(")"); s.push_str(")");
s s
}, },
BlockFlow(block) => { BlockFlow(ref block) => {
match block.box { match block.box {
Some(box) => fmt!("BlockFlow(box=b%d)", box.id()), Some(box) => fmt!("BlockFlow(box=b%d)", box.id()),
None => ~"BlockFlow", None => ~"BlockFlow",
} }
}, },
FloatFlow(float) => { FloatFlow(ref float) => {
match float.box { match float.box {
Some(box) => fmt!("FloatFlow(box=b%d)", box.id()), Some(box) => fmt!("FloatFlow(box=b%d)", box.id()),
None => ~"FloatFlow", None => ~"FloatFlow",

View file

@ -60,7 +60,6 @@ struct LineBox {
} }
struct LineboxScanner { struct LineboxScanner {
flow: FlowContext,
floats: FloatContext, floats: FloatContext,
new_boxes: ~[RenderBox], new_boxes: ~[RenderBox],
work_list: @mut RingBuf<RenderBox>, work_list: @mut RingBuf<RenderBox>,
@ -70,11 +69,8 @@ struct LineboxScanner {
} }
impl LineboxScanner { impl LineboxScanner {
pub fn new(inline: FlowContext, float_ctx: FloatContext) -> LineboxScanner { pub fn new(float_ctx: FloatContext) -> LineboxScanner {
assert!(inline.starts_inline_flow());
LineboxScanner { LineboxScanner {
flow: inline,
floats: float_ctx, floats: float_ctx,
new_boxes: ~[], new_boxes: ~[],
work_list: @mut RingBuf::new(), work_list: @mut RingBuf::new(),
@ -92,8 +88,8 @@ impl LineboxScanner {
self.floats.clone() self.floats.clone()
} }
fn reset_scanner(&mut self) { fn reset_scanner(&mut self, flow: &mut InlineFlowData) {
debug!("Resetting line box scanner's state for flow f%d.", self.flow.id()); debug!("Resetting line box scanner's state for flow f%d.", flow.common.id);
self.lines = ~[]; self.lines = ~[];
self.new_boxes = ~[]; self.new_boxes = ~[];
self.cur_y = Au(0); self.cur_y = Au(0);
@ -106,60 +102,55 @@ impl LineboxScanner {
self.pending_line.green_zone = Size2D(Au(0), Au(0)) self.pending_line.green_zone = Size2D(Au(0), Au(0))
} }
pub fn scan_for_lines(&mut self) { pub fn scan_for_lines(&mut self, flow: &mut InlineFlowData) {
self.reset_scanner(); self.reset_scanner(flow);
{ // FIXME: manually control borrow length let mut i = 0u;
let inline: &InlineFlowData = self.flow.inline();
let mut i = 0u;
loop { loop {
// acquire the next box to lay out from work list or box list // acquire the next box to lay out from work list or box list
let cur_box = if self.work_list.is_empty() { let cur_box = if self.work_list.is_empty() {
if i == inline.boxes.len() { if i == flow.boxes.len() {
break break
}
let box = inline.boxes[i]; i += 1;
debug!("LineboxScanner: Working with box from box list: b%d", box.id());
box
} else {
let box = self.work_list.pop_front().unwrap();
debug!("LineboxScanner: Working with box from work list: b%d", box.id());
box
};
let box_was_appended = self.try_append_to_line(cur_box);
if !box_was_appended {
debug!("LineboxScanner: Box wasn't appended, because line %u was full.",
self.lines.len());
self.flush_current_line();
} else {
debug!("LineboxScanner: appended a box to line %u", self.lines.len());
} }
} let box = flow.boxes[i]; i += 1;
debug!("LineboxScanner: Working with box from box list: b%d", box.id());
box
} else {
let box = self.work_list.pop_front().unwrap();
debug!("LineboxScanner: Working with box from work list: b%d", box.id());
box
};
if self.pending_line.range.length() > 0 { let box_was_appended = self.try_append_to_line(cur_box, flow);
debug!("LineboxScanner: Partially full linebox %u left at end of scanning.", if !box_was_appended {
self.lines.len()); debug!("LineboxScanner: Box wasn't appended, because line %u was full.",
self.lines.len());
self.flush_current_line(); self.flush_current_line();
} else {
debug!("LineboxScanner: appended a box to line %u", self.lines.len());
} }
} }
{ // FIXME: scope the borrow if self.pending_line.range.length() > 0 {
let inline: &mut InlineFlowData = self.flow.inline(); debug!("LineboxScanner: Partially full linebox %u left at end of scanning.",
inline.elems.repair_for_box_changes(inline.boxes, self.new_boxes); self.lines.len());
self.flush_current_line();
} }
self.swap_out_results();
flow.elems.repair_for_box_changes(flow.boxes, self.new_boxes);
self.swap_out_results(flow);
} }
fn swap_out_results(&mut self) { fn swap_out_results(&mut self, flow: &mut InlineFlowData) {
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.lines.len(), self.lines.len(),
self.flow.id()); flow.common.id);
let inline: &mut InlineFlowData = self.flow.inline(); util::swap(&mut flow.boxes, &mut self.new_boxes);
util::swap(&mut inline.boxes, &mut self.new_boxes); util::swap(&mut flow.lines, &mut self.lines);
util::swap(&mut inline.lines, &mut self.lines);
} }
fn flush_current_line(&mut self) { fn flush_current_line(&mut self) {
@ -222,7 +213,7 @@ impl LineboxScanner {
/// Computes the position of a line that has only the provided RenderBox. /// Computes the position of a line that has only the provided RenderBox.
/// Returns: the bounding rect of the line's green zone (whose origin coincides /// Returns: the bounding rect of the line's green zone (whose origin coincides
/// with the line's origin) and the actual width of the first box after splitting. /// with the line's origin) and the actual width of the first box after splitting.
fn initial_line_placement (&self, first_box: RenderBox, ceiling: Au) -> (Rect<Au>, Au) { fn initial_line_placement (&self, first_box: RenderBox, ceiling: Au, flow: &mut InlineFlowData) -> (Rect<Au>, Au) {
debug!("LineboxScanner: Trying to place first box of line %?", self.lines.len()); debug!("LineboxScanner: Trying to place first box of line %?", self.lines.len());
debug!("LineboxScanner: box size: %?", first_box.position().size); debug!("LineboxScanner: box size: %?", first_box.position().size);
let splitable = first_box.can_split(); let splitable = first_box.can_split();
@ -241,7 +232,7 @@ impl LineboxScanner {
width: placement_width, width: placement_width,
height: first_box.position().size.height, height: first_box.position().size.height,
ceiling: ceiling, ceiling: ceiling,
max_width: self.flow.position().size.width, max_width: flow.common.position.size.width,
f_type: FloatLeft f_type: FloatLeft
}; };
@ -307,11 +298,11 @@ impl LineboxScanner {
} }
/// Returns false only if we should break the line. /// Returns false only if we should break the line.
fn try_append_to_line(&mut self, in_box: RenderBox) -> bool { fn try_append_to_line(&mut self, in_box: RenderBox, flow: &mut InlineFlowData) -> bool {
let line_is_empty: bool = self.pending_line.range.length() == 0; let line_is_empty: bool = self.pending_line.range.length() == 0;
if line_is_empty { if line_is_empty {
let (line_bounds, _) = self.initial_line_placement(in_box, self.cur_y); let (line_bounds, _) = self.initial_line_placement(in_box, self.cur_y, flow);
self.pending_line.bounds.origin = line_bounds.origin; self.pending_line.bounds.origin = line_bounds.origin;
self.pending_line.green_zone = line_bounds.size; self.pending_line.green_zone = line_bounds.size;
} }
@ -348,7 +339,7 @@ impl LineboxScanner {
// First predict where the next line is going to be // First predict where the next line is going to be
let this_line_y = self.pending_line.bounds.origin.y; let this_line_y = self.pending_line.bounds.origin.y;
let (next_line, first_box_width) = self.initial_line_placement(in_box, this_line_y); let (next_line, first_box_width) = self.initial_line_placement(in_box, this_line_y, flow);
let next_green_zone = next_line.size; let next_green_zone = next_line.size;
let new_width = self.pending_line.bounds.size.width + first_box_width; let new_width = self.pending_line.bounds.size.width + first_box_width;
@ -490,7 +481,6 @@ impl InlineFlowData {
} }
pub fn teardown(&mut self) { pub fn teardown(&mut self) {
self.common.teardown();
for box in self.boxes.iter() { for box in self.boxes.iter() {
box.teardown(); box.teardown();
} }
@ -512,10 +502,10 @@ impl InlineLayout for FlowContext {
} }
impl InlineFlowData { impl InlineFlowData {
pub fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) { pub fn bubble_widths_inline(&mut self, ctx: &mut LayoutContext) {
let mut num_floats = 0; let mut num_floats = 0;
for kid in InlineFlow(self).children() { for kid in self.common.child_iter() {
do kid.with_mut_base |base| { do kid.with_mut_base |base| {
num_floats += base.num_floats; num_floats += base.num_floats;
base.floats_in = FloatContext::new(base.num_floats); base.floats_in = FloatContext::new(base.num_floats);
@ -542,11 +532,13 @@ impl InlineFlowData {
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
/// on this context, the context has had its width set by the parent context. /// on this context, the context has had its width set by the parent context.
pub fn assign_widths_inline(@mut self, _: &LayoutContext) { pub fn assign_widths_inline(&mut self, _: &LayoutContext) {
// Initialize content box widths if they haven't been initialized already. // Initialize content box widths if they haven't been initialized already.
// //
// TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into // TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into
// `RenderBox`. // `RenderBox`.
debug!("assign_widths_inline: floats_in: %?", self.common.floats_in);
{ {
let this = &mut *self; let this = &mut *self;
for &box in this.boxes.iter() { for &box in this.boxes.iter() {
@ -571,7 +563,7 @@ impl InlineFlowData {
} // End of for loop. } // End of for loop.
} }
for kid in InlineFlow(self).children() { for kid in self.common.child_iter() {
do kid.with_mut_base |base| { do kid.with_mut_base |base| {
base.position.size.width = self.common.position.size.width; base.position.size.width = self.common.position.size.width;
base.is_inorder = self.common.is_inorder; base.is_inorder = self.common.is_inorder;
@ -586,14 +578,14 @@ impl InlineFlowData {
// 'inline-block' box that created this flow before recursing. // 'inline-block' box that created this flow before recursing.
} }
pub fn assign_height_inorder_inline(@mut self, ctx: &mut LayoutContext) { pub fn assign_height_inorder_inline(&mut self, ctx: &mut LayoutContext) {
for kid in InlineFlow(self).children() { for kid in self.common.child_iter() {
kid.assign_height_inorder(ctx); kid.assign_height_inorder(ctx);
} }
self.assign_height_inline(ctx); self.assign_height_inline(ctx);
} }
pub fn assign_height_inline(@mut self, _: &LayoutContext) { pub fn assign_height_inline(&mut self, _: &LayoutContext) {
debug!("assign_height_inline: assigning height for flow %?", self.common.id); debug!("assign_height_inline: assigning height for flow %?", self.common.id);
@ -603,8 +595,10 @@ impl InlineFlowData {
// //
// TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to // TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to
// determine its height for computing linebox height. // determine its height for computing linebox height.
let mut scanner = LineboxScanner::new(InlineFlow(self), self.common.floats_in.clone()); debug!("assign_height_inline: floats_in: %?", self.common.floats_in);
scanner.scan_for_lines(); let scanner_floats = self.common.floats_in.clone();
let mut scanner = LineboxScanner::new(scanner_floats);
scanner.scan_for_lines(self);
// Now, go through each line and lay out the boxes inside // Now, go through each line and lay out the boxes inside
for line in self.lines.iter() { for line in self.lines.iter() {
@ -760,7 +754,7 @@ impl InlineFlowData {
let abs_rect = Rect(self.common.abs_position, self.common.position.size); let abs_rect = Rect(self.common.abs_position, self.common.position.size);
if !abs_rect.intersects(dirty) { if !abs_rect.intersects(dirty) {
return false; return true;
} }
// TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and // TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
@ -777,7 +771,7 @@ impl InlineFlowData {
// should the flow be nested inside the box somehow? // should the flow be nested inside the box somehow?
// For now, don't traverse the subtree rooted here // For now, don't traverse the subtree rooted here
false true
} }
} }

View file

@ -8,7 +8,6 @@
use css::matching::MatchMethods; use css::matching::MatchMethods;
use css::select::new_css_select_ctx; use css::select::new_css_select_ctx;
use layout::aux::{LayoutData, LayoutAuxMethods}; use layout::aux::{LayoutData, LayoutAuxMethods};
use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder; use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder}; use layout::display_list_builder::{DisplayListBuilder};
@ -18,6 +17,7 @@ use layout::incremental::{RestyleDamage, BubbleWidths};
use std::cast::transmute; use std::cast::transmute;
use std::cell::Cell; use std::cell::Cell;
use std::comm::{Port}; use std::comm::{Port};
use extra::arc::Arc;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
@ -44,6 +44,7 @@ use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::TreeNodeRef; use servo_util::tree::TreeNodeRef;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use servo_util::range::Range;
use extra::url::Url; use extra::url::Url;
struct LayoutTask { struct LayoutTask {
@ -51,7 +52,7 @@ struct LayoutTask {
port: Port<Msg>, port: Port<Msg>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan, render_chan: RenderChan<AbstractNode<()>>,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
local_image_cache: @mut LocalImageCache, local_image_cache: @mut LocalImageCache,
font_ctx: @mut FontContext, font_ctx: @mut FontContext,
@ -61,6 +62,8 @@ struct LayoutTask {
/// This is used to root reader data. /// This is used to root reader data.
layout_refs: ~[@mut LayoutData], layout_refs: ~[@mut LayoutData],
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
css_select_ctx: @mut SelectCtx, css_select_ctx: @mut SelectCtx,
profiler_chan: ProfilerChan, profiler_chan: ProfilerChan,
} }
@ -70,7 +73,7 @@ impl LayoutTask {
port: Port<Msg>, port: Port<Msg>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan, render_chan: RenderChan<AbstractNode<()>>,
img_cache_task: ImageCacheTask, img_cache_task: ImageCacheTask,
opts: Opts, opts: Opts,
profiler_chan: ProfilerChan) { profiler_chan: ProfilerChan) {
@ -99,7 +102,7 @@ impl LayoutTask {
port: Port<Msg>, port: Port<Msg>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan, render_chan: RenderChan<AbstractNode<()>>,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
opts: &Opts, opts: &Opts,
profiler_chan: ProfilerChan) profiler_chan: ProfilerChan)
@ -118,6 +121,8 @@ impl LayoutTask {
doc_url: None, doc_url: None,
screen_size: None, screen_size: None,
display_list: None,
layout_refs: ~[], layout_refs: ~[],
css_select_ctx: @mut new_css_select_ctx(), css_select_ctx: @mut new_css_select_ctx(),
profiler_chan: profiler_chan, profiler_chan: profiler_chan,
@ -219,7 +224,7 @@ impl LayoutTask {
} }
// Construct the flow tree. // Construct the flow tree.
let layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory, let mut layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory,
self.profiler_chan.clone()) { self.profiler_chan.clone()) {
let mut builder = LayoutTreeBuilder::new(); let mut builder = LayoutTreeBuilder::new();
let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) { let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
@ -232,7 +237,7 @@ impl LayoutTask {
// Propagate restyle damage up and down the tree, as appropriate. // Propagate restyle damage up and down the tree, as appropriate.
// FIXME: Merge this with flow tree building and/or the other traversals. // FIXME: Merge this with flow tree building and/or the other traversals.
for flow in layout_root.traverse_preorder() { do layout_root.each_preorder |flow| {
// Also set any damage implied by resize. // Also set any damage implied by resize.
if resized { if resized {
do flow.with_mut_base |base| { do flow.with_mut_base |base| {
@ -242,23 +247,29 @@ impl LayoutTask {
let prop = flow.with_base(|base| base.restyle_damage.propagate_down()); let prop = flow.with_base(|base| base.restyle_damage.propagate_down());
if prop.is_nonempty() { if prop.is_nonempty() {
for kid_ctx in flow.children() { for kid_ctx in flow.child_iter() {
do kid_ctx.with_mut_base |kid| { do kid_ctx.with_mut_base |kid| {
kid.restyle_damage.union_in_place(prop); kid.restyle_damage.union_in_place(prop);
} }
} }
} }
} true
};
for flow in layout_root.traverse_postorder() { do layout_root.each_postorder |flow| {
for child in flow.children() { let mut damage = do flow.with_base |base| {
base.restyle_damage
};
for child in flow.child_iter() {
do child.with_base |child_base| { do child.with_base |child_base| {
do flow.with_mut_base |base| { damage.union_in_place(child_base.restyle_damage);
base.restyle_damage.union_in_place(child_base.restyle_damage);
}
} }
} }
} do flow.with_mut_base |base| {
base.restyle_damage = damage;
}
true
};
debug!("layout: constructed Flow tree"); debug!("layout: constructed Flow tree");
debug!("%?", layout_root.dump()); debug!("%?", layout_root.dump());
@ -266,22 +277,27 @@ impl LayoutTask {
// Perform the primary layout passes over the flow tree to compute the locations of all // Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes. // the boxes.
do profile(time::LayoutMainCategory, self.profiler_chan.clone()) { do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
for flow in layout_root.traverse_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) { do layout_root.each_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) |flow| {
flow.bubble_widths(&mut layout_ctx); flow.bubble_widths(&mut layout_ctx);
true
}; };
// FIXME: We want to do // FIXME: We want to do
// for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow)) { // for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow))
// but FloatContext values can't be reused, so we need to recompute them every time. // but FloatContext values can't be reused, so we need to recompute them every time.
for flow in layout_root.traverse_preorder() { debug!("assigning widths");
do layout_root.each_preorder |flow| {
flow.assign_widths(&mut layout_ctx); flow.assign_widths(&mut layout_ctx);
true
}; };
// For now, this is an inorder traversal // For now, this is an inorder traversal
// FIXME: prune this traversal as well // FIXME: prune this traversal as well
do layout_root.traverse_bu_sub_inorder |flow| { debug!("assigning height");
do layout_root.each_bu_sub_inorder |flow| {
flow.assign_height(&mut layout_ctx); flow.assign_height(&mut layout_ctx);
} true
};
} }
// Build the display list if necessary, and send it to the renderer. // Build the display list if necessary, and send it to the renderer.
@ -291,23 +307,52 @@ impl LayoutTask {
ctx: &layout_ctx, ctx: &layout_ctx,
}; };
let display_list = @Cell::new(DisplayList::new()); let display_list = ~Cell::new(DisplayList::new::<AbstractNode<()>>());
// 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.partially_traverse_preorder |flow| { let root_pos = &layout_root.position().clone();
flow.build_display_list(&builder, &layout_root.position(), display_list) layout_root.each_preorder_prune(|flow| {
} flow.build_display_list(&builder, root_pos, display_list)
}, |_| { true } );
let root_size = do layout_root.with_base |base| { let root_size = do layout_root.with_base |base| {
base.position.size base.position.size
}; };
let display_list = Arc::new(display_list.take());
for i in range(0,display_list.get().list.len()) {
let node: AbstractNode<LayoutView> = unsafe {
transmute(display_list.get().list[i].base().extra)
};
assert!(node.has_layout_data(), "Node has display item but no layout data");
let layout_data = node.layout_data();
layout_data.boxes.display_list = Some(display_list.clone());
if layout_data.boxes.range.is_none() {
debug!("Creating initial range for node");
layout_data.boxes.range = Some(Range::new(i,1));
} else {
debug!("Appending item to range");
unsafe {
let old_node: AbstractNode<()> = transmute(node);
assert!(old_node == display_list.get().list[i-1].base().extra,
"Non-contiguous arrangement of display items");
}
layout_data.boxes.range.unwrap().extend_by(1);
}
}
let render_layer = RenderLayer { let render_layer = RenderLayer {
display_list: display_list.take(), display_list: display_list.clone(),
size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint) size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint)
}; };
self.display_list = Some(display_list.clone());
self.render_chan.send(RenderMsg(render_layer)); self.render_chan.send(RenderMsg(render_layer));
} // time(layout: display list building) } // time(layout: display list building)
} }
@ -330,19 +375,15 @@ impl LayoutTask {
transmute(node) transmute(node)
}; };
let response = match node.layout_data().flow { let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) {
None => { (Some(display_list), Some(range)) => {
error!("no flow present"); let mut rect: Option<Rect<Au>> = None;
Err(()) for i in range.eachi() {
} rect = match rect {
Some(flow) => { Some(acc) => Some(acc.union(&display_list.get().list[i].bounds())),
let start_val: Option<Rect<Au>> = None; None => Some(display_list.get().list[i].bounds())
let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
match acc {
Some(acc) => Some(acc.union(&box.content_box())),
None => Some(box.content_box())
} }
}; }
match rect { match rect {
None => { None => {
@ -352,6 +393,10 @@ impl LayoutTask {
Some(rect) => Ok(ContentBoxResponse(rect)) Some(rect) => Ok(ContentBoxResponse(rect))
} }
} }
_ => {
error!("no display list present");
Err(())
}
}; };
reply_chan.send(response) reply_chan.send(response)
@ -362,71 +407,49 @@ impl LayoutTask {
transmute(node) transmute(node)
}; };
let response = match node.layout_data().flow { let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) {
None => Err(()), (Some(display_list), Some(range)) => {
Some(flow) => {
let mut boxes = ~[]; let mut boxes = ~[];
for box in flow.iter_all_boxes() { for i in range.eachi() {
if box.node() == node { boxes.push(display_list.get().list[i].bounds());
boxes.push(box.content_box());
}
} }
Ok(ContentBoxesResponse(boxes)) Ok(ContentBoxesResponse(boxes))
} }
_ => Err(()),
}; };
reply_chan.send(response) reply_chan.send(response)
} }
HitTestQuery(node, point, reply_chan) => { HitTestQuery(_, point, reply_chan) => {
// FIXME: Isolate this transmutation into a single "bridge" module. let response = {
let node: AbstractNode<LayoutView> = unsafe { match self.display_list {
transmute(node) Some(ref list) => {
}; let display_list = list.get();
let mut flow_node: AbstractNode<LayoutView> = node; let (x, y) = (Au::from_frac_px(point.x as float),
for node in node.traverse_preorder() { Au::from_frac_px(point.y as float));
if node.layout_data().flow.is_some() { let mut resp = Err(());
flow_node = node; // iterate in reverse to ensure we have the most recently painted render box
break; for display_item in display_list.list.rev_iter() {
} let bounds = display_item.bounds();
}; // TODO this check should really be performed by a method of DisplayItem
if x <= bounds.origin.x + bounds.size.width &&
let response = match flow_node.layout_data().flow { bounds.origin.x <= x &&
None => { y < bounds.origin.y + bounds.size.height &&
debug!("HitTestQuery: flow is None"); bounds.origin.y < y {
Err(()) let node: AbstractNode<LayoutView> = unsafe {
} transmute(display_item.base().extra)
Some(flow) => { };
let layout_ctx = self.build_layout_context(); resp = Ok(HitTestResponse(node));
let builder = DisplayListBuilder { break;
ctx: &layout_ctx, }
};
let display_list: @Cell<DisplayList<RenderBox>> =
@Cell::new(DisplayList::new());
do flow.partially_traverse_preorder |this_flow| {
this_flow.build_display_list(&builder,
&flow.position(),
display_list)
}
let (x, y) = (Au::from_frac_px(point.x as float),
Au::from_frac_px(point.y as float));
let mut resp = Err(());
let display_list = &display_list.take().list;
// iterate in reverse to ensure we have the most recently painted render box
for display_item in display_list.rev_iter() {
let bounds = display_item.bounds();
// TODO this check should really be performed by a method of DisplayItem
if x <= bounds.origin.x + bounds.size.width &&
bounds.origin.x <= x &&
y < bounds.origin.y + bounds.size.height &&
bounds.origin.y < y {
resp = Ok(HitTestResponse(display_item.base().extra.node()));
break;
} }
resp
} }
resp None => {
error!("Can't hit test: no display list");
Err(())
},
} }
}; };

View file

@ -66,16 +66,20 @@ impl TextRunScanner {
} }
} }
pub fn scan_for_runs(&mut self, ctx: &LayoutContext, flow: FlowContext) { pub fn scan_for_runs(&mut self, ctx: &LayoutContext, flow: &mut FlowContext) {
let inline = flow.inline(); {
assert!(inline.boxes.len() > 0); let inline = flow.imm_inline();
debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len()); // FIXME: this assertion fails on wikipedia, but doesn't seem
// to cause problems.
// assert!(inline.boxes.len() > 0);
debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len());
}
let mut last_whitespace = true; let mut last_whitespace = true;
let mut out_boxes = ~[]; let mut out_boxes = ~[];
for box_i in range(0, flow.inline().boxes.len()) { for box_i in range(0, flow.imm_inline().boxes.len()) {
debug!("TextRunScanner: considering box: %?", flow.inline().boxes[box_i].debug_str()); debug!("TextRunScanner: considering box: %?", flow.imm_inline().boxes[box_i].debug_str());
if box_i > 0 && !can_coalesce_text_nodes(flow.inline().boxes, box_i-1, box_i) { if box_i > 0 && !can_coalesce_text_nodes(flow.imm_inline().boxes, box_i-1, box_i) {
last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes); last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
} }
self.clump.extend_by(1); self.clump.extend_by(1);
@ -118,10 +122,10 @@ impl TextRunScanner {
/// necessary. /// necessary.
pub fn flush_clump_to_list(&mut self, pub fn flush_clump_to_list(&mut self,
ctx: &LayoutContext, ctx: &LayoutContext,
flow: FlowContext, flow: &mut FlowContext,
last_whitespace: bool, last_whitespace: bool,
out_boxes: &mut ~[RenderBox]) -> bool { out_boxes: &mut ~[RenderBox]) -> bool {
let inline = &mut *flow.inline(); let inline = flow.inline();
let in_boxes = &inline.boxes; let in_boxes = &inline.boxes;
fn has_underline(decoration: CSSTextDecoration) -> bool{ fn has_underline(decoration: CSSTextDecoration) -> bool{

View file

@ -12,6 +12,7 @@ use layout::layout_task::LayoutTask;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use script::script_task::{ExecuteMsg, LoadMsg}; use script::script_task::{ExecuteMsg, LoadMsg};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId};
use script::dom::node::AbstractNode;
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan}; use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
use script::script_task; use script::script_task;
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;
@ -28,7 +29,7 @@ pub struct Pipeline {
subpage_id: Option<SubpageId>, subpage_id: Option<SubpageId>,
script_chan: ScriptChan, script_chan: ScriptChan,
layout_chan: LayoutChan, layout_chan: LayoutChan,
render_chan: RenderChan, render_chan: RenderChan<AbstractNode<()>>,
/// The most recently loaded url /// The most recently loaded url
url: Option<Url>, url: Option<Url>,
} }
@ -130,7 +131,7 @@ impl Pipeline {
subpage_id: Option<SubpageId>, subpage_id: Option<SubpageId>,
script_chan: ScriptChan, script_chan: ScriptChan,
layout_chan: LayoutChan, layout_chan: LayoutChan,
render_chan: RenderChan) render_chan: RenderChan<AbstractNode<()>>)
-> Pipeline { -> Pipeline {
Pipeline { Pipeline {
id: id, id: id,