layout: Refactor the RenderBox type to save memory and improve the coding style of box.rs

This commit is contained in:
Patrick Walton 2013-05-07 20:52:38 -07:00
parent 49edf85f5d
commit b17e68ee04
9 changed files with 792 additions and 569 deletions

View file

@ -10,11 +10,11 @@ use newcss::complete::CompleteStyle;
/// Node mixin providing `style` method that returns a `NodeStyle` /// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode { pub trait StyledNode {
fn style<'a>(&'a self) -> CompleteStyle<'a>; fn style(&self) -> CompleteStyle;
} }
impl StyledNode for AbstractNode { impl StyledNode for AbstractNode {
fn style<'a>(&'a self) -> CompleteStyle<'a> { fn style(&self) -> CompleteStyle {
assert!(self.is_element()); // Only elements can have styles assert!(self.is_element()); // Only elements can have styles
let results = self.get_css_select_results(); let results = self.get_css_select_results();
results.computed_style() results.computed_style()

View file

@ -23,7 +23,7 @@ pub struct BlockFlowData {
common: FlowData, common: FlowData,
/// The associated render box. /// The associated render box.
box: Option<@mut RenderBox> box: Option<RenderBox>
} }
impl BlockFlowData { impl BlockFlowData {
@ -40,7 +40,7 @@ impl BlockFlowData {
/// merged into this. /// merged into this.
pub trait BlockLayout { pub trait BlockLayout {
fn starts_block_flow(&self) -> bool; fn starts_block_flow(&self) -> bool;
fn with_block_box(&self, &fn(box: &@mut RenderBox) -> ()) -> (); fn with_block_box(&self, &fn(box: RenderBox) -> ()) -> ();
fn bubble_widths_block(&self, ctx: &LayoutContext); fn bubble_widths_block(&self, ctx: &LayoutContext);
fn assign_widths_block(&self, ctx: &LayoutContext); fn assign_widths_block(&self, ctx: &LayoutContext);
@ -62,17 +62,17 @@ impl BlockLayout for FlowContext {
/// Get the current flow's corresponding block box, if it exists, and do something with it. /// Get the current flow's corresponding block box, if it exists, and do something with it.
/// This works on both BlockFlow and RootFlow, since they are mostly the same. /// This works on both BlockFlow and RootFlow, since they are mostly the same.
fn with_block_box(&self, callback: &fn(box: &@mut RenderBox) -> ()) -> () { fn with_block_box(&self, callback: &fn(box: RenderBox) -> ()) -> () {
match *self { match *self {
BlockFlow(*) => { BlockFlow(*) => {
let box = self.block().box; let box = self.block().box;
for box.each |b| { for box.each |&b| {
callback(b); callback(b);
} }
}, },
RootFlow(*) => { RootFlow(*) => {
let mut box = self.root().box; let mut box = self.root().box;
for box.each |b| { for box.each |&b| {
callback(b); callback(b);
} }
}, },
@ -132,9 +132,12 @@ impl BlockLayout for FlowContext {
// Let the box consume some width. It will return the amount remaining for its children. // Let the box consume some width. It will return the amount remaining for its children.
do self.with_block_box |box| { do self.with_block_box |box| {
box.d().position.size.width = remaining_width; do box.with_mut_base |base| {
let (left_used, right_used) = box.get_used_width(); base.position.size.width = remaining_width;
remaining_width -= left_used.add(&right_used);
let (left_used, right_used) = box.get_used_width();
remaining_width -= left_used.add(&right_used);
}
} }
for self.each_child |kid| { for self.each_child |kid| {
@ -167,9 +170,11 @@ impl BlockLayout for FlowContext {
let _used_bot = Au(0); let _used_bot = Au(0);
do self.with_block_box |box| { do self.with_block_box |box| {
box.d().position.origin.y = Au(0); do box.with_mut_base |base| {
box.d().position.size.height = cur_y; base.position.origin.y = Au(0);
let (_used_top, _used_bot) = box.get_used_height(); base.position.size.height = cur_y;
let (_used_top, _used_bot) = box.get_used_height();
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -8,10 +8,15 @@ use dom::element::*;
use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId}; use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
use dom::node::{ElementNodeTypeId, TextNodeTypeId}; use dom::node::{ElementNodeTypeId, TextNodeTypeId};
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::box::*; use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox};
use layout::box::{RenderBoxBase, RenderBoxType, RenderBox_Generic, RenderBox_Image};
use layout::box::{RenderBox_Text, TextRenderBox, UnscannedTextRenderBox};
use layout::box::{UnscannedTextRenderBoxClass};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::{BoxedMutDebugMethods, DebugMethods}; use layout::debug::{BoxedMutDebugMethods, DebugMethods};
use layout::flow::*; use layout::flow::{AbsoluteFlow, BlockFlow, FloatFlow, Flow_Absolute, Flow_Block, Flow_Float};
use layout::flow::{Flow_Inline, Flow_InlineBlock, Flow_Root, Flow_Table, FlowContext};
use layout::flow::{FlowContextType, FlowData, InlineBlockFlow, InlineFlow, RootFlow, TableFlow};
use layout::inline::{InlineFlowData, InlineLayout}; use layout::inline::{InlineFlowData, InlineLayout};
use layout::root::RootFlowData; use layout::root::RootFlowData;
@ -102,7 +107,7 @@ impl BoxGenerator {
_: &LayoutContext, _: &LayoutContext,
_: AbstractNode, _: AbstractNode,
_: InlineSpacerSide) _: InlineSpacerSide)
-> Option<@mut RenderBox> { -> Option<RenderBox> {
None None
} }
@ -133,7 +138,7 @@ impl BoxGenerator {
} else if self.inline_spacers_needed_for_node(node) { } else if self.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
for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each
|spacer: &@mut RenderBox| { |spacer: &RenderBox| {
inline.boxes.push(*spacer); inline.boxes.push(*spacer);
} }
} }
@ -145,7 +150,7 @@ impl BoxGenerator {
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,
new_box.d().id, new_box.id(),
node.debug_str()); node.debug_str());
assert!(block.box.is_none()); assert!(block.box.is_none());
@ -157,7 +162,7 @@ impl BoxGenerator {
debug!("BoxGenerator[f%d]: (node is: %s)", root.common.id, node.debug_str()); debug!("BoxGenerator[f%d]: (node is: %s)", root.common.id, node.debug_str());
debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)", debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)",
root.common.id, root.common.id,
new_box.d().id, new_box.id(),
node.debug_str()); node.debug_str());
assert!(root.box.is_none()); assert!(root.box.is_none());
@ -450,55 +455,40 @@ pub impl LayoutTreeBuilder {
layout_ctx: &LayoutContext, layout_ctx: &LayoutContext,
ty: RenderBoxType, ty: RenderBoxType,
node: AbstractNode, node: AbstractNode,
ctx: FlowContext) flow_context: FlowContext)
-> @mut RenderBox { -> RenderBox {
let base = RenderBoxBase::new(node, flow_context, self.next_box_id());
let result = match ty { let result = match ty {
RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx), RenderBox_Generic => GenericRenderBoxClass(@mut base),
RenderBox_Text => self.make_text_box(layout_ctx, node, ctx), RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)),
RenderBox_Image => self.make_image_box(layout_ctx, node, ctx), RenderBox_Image => self.make_image_box(layout_ctx, node, flow_context, base),
}; };
debug!("LayoutTreeBuilder: created box: %s", result.debug_str()); debug!("LayoutTreeBuilder: created box: %s", result.debug_str());
result result
} }
fn make_generic_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext) fn make_image_box(&mut self,
-> @mut RenderBox { layout_ctx: &LayoutContext,
@mut GenericBox(RenderBoxData(copy node, ctx, self.next_box_id())) node: AbstractNode,
} flow_context: FlowContext,
base: RenderBoxBase)
fn make_image_box(&mut self, layout_ctx: &LayoutContext, node: AbstractNode, ctx: FlowContext) -> RenderBox {
-> @mut RenderBox { assert!(node.is_image_element());
if !node.is_image_element() {
fail!(~"WAT error: why couldn't we make an image box?");
}
do node.with_imm_image_element |image_element| { do node.with_imm_image_element |image_element| {
if image_element.image.is_some() { if image_element.image.is_some() {
let holder = ImageHolder::new(copy *image_element.image.get_ref(), // FIXME(pcwalton): Don't copy URLs.
layout_ctx.image_cache); let url = copy *image_element.image.get_ref();
@mut ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder) ImageRenderBoxClass(@mut ImageRenderBox::new(base, url, layout_ctx.image_cache))
} else { } else {
info!("Tried to make image box, but couldn't find image. Made generic box \ info!("Tried to make image box, but couldn't find image. Made generic box \
instead."); instead.");
self.make_generic_box(layout_ctx, node, ctx) GenericRenderBoxClass(@mut base)
} }
} }
} }
fn make_text_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext) fn decide_box_type(&self, node: AbstractNode, _: CSSDisplay) -> RenderBoxType {
-> @mut RenderBox {
if !node.is_text() {
fail!(~"WAT error: why couldn't we make a text box?");
}
// FIXME: Don't copy text. I guess it should be atomically reference counted?
do node.with_imm_text |text_node| {
let string = text_node.parent.data.to_str();
@mut UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), string)
}
}
fn decide_box_type(&self, node: AbstractNode, _display: CSSDisplay) -> RenderBoxType {
if node.is_text() { if node.is_text() {
RenderBox_Text RenderBox_Text
} else if node.is_image_element() { } else if node.is_image_element() {

View file

@ -16,6 +16,7 @@ use geom::rect::Rect;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use gfx::geometry::Au; use gfx::geometry::Au;
use gfx; use gfx;
use newcss;
use servo_util::tree::TreeNodeRef; use servo_util::tree::TreeNodeRef;
/// A builder object that manages display list builder should mainly hold information about the /// A builder object that manages display list builder should mainly hold information about the
@ -73,3 +74,19 @@ impl FlowDisplayListBuilderMethods for FlowContext {
} }
} }
//
// Miscellaneous useful routines
//
/// Allows a CSS color to be converted into a graphics color.
pub trait ToGfxColor {
/// Converts a CSS color to a graphics color.
fn to_gfx_color(&self) -> gfx::color::Color;
}
impl ToGfxColor for newcss::color::Color {
fn to_gfx_color(&self) -> gfx::color::Color {
gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
}
}

View file

@ -285,31 +285,37 @@ 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:Copy>(&self, seed: B, cb: &fn(a: B, b: @mut RenderBox) -> B) -> B { pub fn foldl_all_boxes<B:Copy>(&self, seed: B, cb: &fn(a: B, b: RenderBox) -> B) -> B {
match *self { match *self {
RootFlow(root) => { RootFlow(root) => {
let root = &mut *root; let root = &mut *root;
root.box.map_default(seed, |box| { cb(seed, *box) }) do root.box.map_default(seed) |box| {
cb(seed, *box)
}
} }
BlockFlow(block) => { BlockFlow(block) => {
let block = &mut *block; let block = &mut *block;
block.box.map_default(seed, |box| { cb(seed, *box) }) do block.box.map_default(seed) |box| {
cb(seed, *box)
}
} }
InlineFlow(inline) => { InlineFlow(inline) => {
let inline = &mut *inline; let inline = &mut *inline;
inline.boxes.foldl(seed, |acc, box| { cb(*acc, *box) }) do inline.boxes.foldl(seed) |acc, box| {
cb(*acc, *box)
}
} }
_ => 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)),
} }
} }
pub fn foldl_boxes_for_node<B:Copy>(&self, pub fn foldl_boxes_for_node<B:Copy>(&self,
node: AbstractNode, node: AbstractNode,
seed: B, seed: B,
callback: &fn(a: B, @mut RenderBox) -> B) callback: &fn(a: B, RenderBox) -> B)
-> B { -> B {
do self.foldl_all_boxes(seed) |acc, box| { do self.foldl_all_boxes(seed) |acc, box| {
if box.d().node == node { if box.node() == node {
callback(acc, box) callback(acc, box)
} else { } else {
acc acc
@ -317,7 +323,7 @@ impl<'self> FlowContext {
} }
} }
pub fn iter_all_boxes(&self, cb: &fn(@mut RenderBox) -> bool) { pub fn iter_all_boxes(&self, cb: &fn(RenderBox) -> bool) {
match *self { match *self {
RootFlow(root) => { RootFlow(root) => {
let root = &mut *root; let root = &mut *root;
@ -347,9 +353,9 @@ impl<'self> FlowContext {
} }
} }
pub fn iter_boxes_for_node(&self, node: AbstractNode, callback: &fn(@mut RenderBox) -> bool) { pub fn iter_boxes_for_node(&self, node: AbstractNode, callback: &fn(RenderBox) -> bool) {
for self.iter_all_boxes |box| { for self.iter_all_boxes |box| {
if box.d().node == node { if box.node() == node {
if !callback(box) { if !callback(box) {
break; break;
} }
@ -384,7 +390,7 @@ impl DebugMethods for FlowContext {
InlineFlow(inline) => { InlineFlow(inline) => {
let inline = &mut *inline; let inline = &mut *inline;
let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| { let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| {
fmt!("%s b%d", *s, box.d().id) fmt!("%s b%d", *s, box.id())
}); });
s += ~")"; s += ~")";
s s
@ -392,14 +398,14 @@ impl DebugMethods for FlowContext {
BlockFlow(block) => { BlockFlow(block) => {
let block = &mut *block; let block = &mut *block;
match block.box { match block.box {
Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id), Some(box) => fmt!("BlockFlow(box=b%d)", box.id()),
None => ~"BlockFlow", None => ~"BlockFlow",
} }
}, },
RootFlow(root) => { RootFlow(root) => {
let root = &mut *root; let root = &mut *root;
match root.box { match root.box {
Some(box) => fmt!("RootFlo(box=b%d)", box.d().id), Some(box) => fmt!("RootFlow(box=b%d)", box.id()),
None => ~"RootFlow", None => ~"RootFlow",
} }
}, },

View file

@ -5,7 +5,8 @@
use core::cell::Cell; use core::cell::Cell;
use core; use core;
use dom::node::AbstractNode; use dom::node::AbstractNode;
use layout::box::*; use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox};
use layout::box::{SplitDidFit, SplitDidNotFit, TextRenderBoxClass, UnscannedTextRenderBoxClass};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods}; use layout::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods};
use layout::display_list_builder::DisplayListBuilder; use layout::display_list_builder::DisplayListBuilder;
@ -82,9 +83,7 @@ impl ElementMapping {
do self.entries.eachi |i, nr| { cb(i, nr) } do self.entries.eachi |i, nr| { cb(i, nr) }
} }
pub fn repair_for_box_changes(&mut self, pub fn repair_for_box_changes(&mut self, old_boxes: &[RenderBox], new_boxes: &[RenderBox]) {
old_boxes: &[@mut RenderBox],
new_boxes: &[@mut RenderBox]) {
let entries = &mut self.entries; let entries = &mut self.entries;
debug!("--- Old boxes: ---"); debug!("--- Old boxes: ---");
@ -129,9 +128,15 @@ impl ElementMapping {
// XXX: the following loop form causes segfaults; assigning to locals doesn't. // XXX: the following loop form causes segfaults; assigning to locals doesn't.
// while new_j < new_boxes.len() && old_boxes[old_i].d().node != new_boxes[new_j].d().node { // while new_j < new_boxes.len() && old_boxes[old_i].d().node != new_boxes[new_j].d().node {
while new_j < new_boxes.len() { while new_j < new_boxes.len() {
let o = old_boxes[old_i]; let should_leave = do old_boxes[old_i].with_imm_base |old_box_base| {
let n = new_boxes[new_j]; do new_boxes[new_j].with_imm_base |new_box_base| {
if o.d().node != n.d().node { break } old_box_base.node != new_box_base.node
}
};
if should_leave {
break
}
debug!("repair_for_box_changes: Slide through new box %u", new_j); debug!("repair_for_box_changes: Slide through new box %u", new_j);
new_j += 1; new_j += 1;
} }
@ -192,14 +197,16 @@ impl TextRunScanner {
inline.boxes = out_boxes; inline.boxes = out_boxes;
// A helper function. // A helper function.
fn can_coalesce_text_nodes(boxes: &[@mut RenderBox], left_i: uint, right_i: uint) -> bool { fn can_coalesce_text_nodes(boxes: &[RenderBox], left_i: uint, right_i: uint) -> bool {
assert!(left_i < boxes.len()); assert!(left_i < boxes.len());
assert!(right_i > 0 && right_i < boxes.len()); assert!(right_i > 0 && right_i < boxes.len());
assert!(left_i != right_i); assert!(left_i != right_i);
let (left, right) = (boxes[left_i], boxes[right_i]); let (left, right) = (boxes[left_i], boxes[right_i]);
match (left, right) { match (left, right) {
(@UnscannedTextBox(*), @UnscannedTextBox(*)) => left.can_merge_with_box(right), (UnscannedTextRenderBoxClass(*), UnscannedTextRenderBoxClass(*)) => {
left.can_merge_with_box(right)
}
(_, _) => false (_, _) => false
} }
} }
@ -218,14 +225,14 @@ impl TextRunScanner {
fn flush_clump_to_list(&mut self, fn flush_clump_to_list(&mut self,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
flow: FlowContext, flow: FlowContext,
in_boxes: &[@mut RenderBox], in_boxes: &[RenderBox],
out_boxes: &mut ~[@mut RenderBox]) { out_boxes: &mut ~[RenderBox]) {
assert!(self.clump.length() > 0); assert!(self.clump.length() > 0);
debug!("TextRunScanner: flushing boxes in range=%?", self.clump); debug!("TextRunScanner: flushing boxes in range=%?", self.clump);
let is_singleton = self.clump.length() == 1; let is_singleton = self.clump.length() == 1;
let is_text_clump = match *in_boxes[self.clump.begin()] { let is_text_clump = match in_boxes[self.clump.begin()] {
UnscannedTextBox(*) => true, UnscannedTextRenderBoxClass(*) => true,
_ => false _ => false
}; };
@ -254,10 +261,12 @@ impl TextRunScanner {
let run = @fontgroup.create_textrun(transformed_text); let run = @fontgroup.create_textrun(transformed_text);
debug!("TextRunScanner: pushing single text box in range: %?", self.clump); debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
let new_box = adapt_textbox_with_range(old_box.d(), let new_box = do old_box.with_imm_base |old_box_base| {
run, let range = Range::new(0, run.char_len());
&Range::new(0, run.char_len())); @mut adapt_textbox_with_range(*old_box_base, run, range)
out_boxes.push(new_box); };
out_boxes.push(TextRenderBoxClass(new_box));
}, },
(false, true) => { (false, true) => {
// TODO(#115): Use the actual CSS `white-space` property of the relevant style. // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
@ -297,7 +306,7 @@ impl TextRunScanner {
debug!("TextRunScanner: pushing box(es) in range: %?", self.clump); debug!("TextRunScanner: pushing box(es) in range: %?", self.clump);
let clump = self.clump; let clump = self.clump;
for clump.eachi |i| { for clump.eachi |i| {
let range = &new_ranges[i - self.clump.begin()]; let range = new_ranges[i - self.clump.begin()];
if range.length() == 0 { if range.length() == 0 {
error!("Elided an `UnscannedTextbox` because it was zero-length after \ error!("Elided an `UnscannedTextbox` because it was zero-length after \
compression; %s", compression; %s",
@ -305,8 +314,10 @@ impl TextRunScanner {
loop loop
} }
let new_box = adapt_textbox_with_range(in_boxes[i].d(), run, range); do in_boxes[i].with_imm_base |base| {
out_boxes.push(new_box); let new_box = @mut adapt_textbox_with_range(*base, run, range);
out_boxes.push(TextRenderBoxClass(new_box));
}
} }
} }
} // End of match. } // End of match.
@ -342,14 +353,14 @@ struct PendingLine {
struct LineboxScanner { struct LineboxScanner {
flow: FlowContext, flow: FlowContext,
new_boxes: ~[@mut RenderBox], new_boxes: ~[RenderBox],
work_list: @mut Deque<@mut RenderBox>, work_list: @mut Deque<RenderBox>,
pending_line: PendingLine, pending_line: PendingLine,
line_spans: ~[Range], line_spans: ~[Range],
} }
impl LineboxScanner { impl LineboxScanner {
fn new(inline: FlowContext) -> LineboxScanner { pub fn new(inline: FlowContext) -> LineboxScanner {
assert!(inline.starts_inline_flow()); assert!(inline.starts_inline_flow());
LineboxScanner { LineboxScanner {
@ -361,14 +372,14 @@ impl LineboxScanner {
} }
} }
priv fn reset_scanner(&mut self) { fn reset_scanner(&mut self) {
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.", self.flow.id());
self.line_spans = ~[]; self.line_spans = ~[];
self.new_boxes = ~[]; self.new_boxes = ~[];
self.reset_linebox(); self.reset_linebox();
} }
priv fn reset_linebox(&mut self) { fn reset_linebox(&mut self) {
self.pending_line.range.reset(0,0); self.pending_line.range.reset(0,0);
self.pending_line.width = Au(0); self.pending_line.width = Au(0);
} }
@ -382,13 +393,15 @@ impl LineboxScanner {
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 == boxes.len() { break; } if i == boxes.len() {
break
}
let box = boxes[i]; i += 1; let box = boxes[i]; i += 1;
debug!("LineboxScanner: Working with box from box list: b%d", box.d().id); debug!("LineboxScanner: Working with box from box list: b%d", box.id());
box box
} else { } else {
let box = self.work_list.pop_front(); let box = self.work_list.pop_front();
debug!("LineboxScanner: Working with box from work list: b%d", box.d().id); debug!("LineboxScanner: Working with box from work list: b%d", box.id());
box box
}; };
@ -414,7 +427,7 @@ impl LineboxScanner {
self.swap_out_results(); self.swap_out_results();
} }
priv fn swap_out_results(&mut self) { fn swap_out_results(&mut self) {
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.line_spans.len(), self.line_spans.len(),
self.flow.id()); self.flow.id());
@ -425,7 +438,7 @@ impl LineboxScanner {
util::swap(lines, &mut self.line_spans); util::swap(lines, &mut self.line_spans);
} }
priv fn flush_current_line(&mut self) { fn flush_current_line(&mut self) {
debug!("LineboxScanner: Flushing line %u: %?", debug!("LineboxScanner: Flushing line %u: %?",
self.line_spans.len(), self.pending_line); self.line_spans.len(), self.pending_line);
// set box horizontal offsets // set box horizontal offsets
@ -453,25 +466,28 @@ impl LineboxScanner {
// TODO(Issue #213): implement `text-align: justify` // TODO(Issue #213): implement `text-align: justify`
CSSTextAlignLeft | CSSTextAlignJustify => { CSSTextAlignLeft | CSSTextAlignJustify => {
for line_range.eachi |i| { for line_range.eachi |i| {
let box_data = &self.new_boxes[i].d(); do self.new_boxes[i].with_mut_base |base| {
box_data.position.origin.x = offset_x; base.position.origin.x = offset_x;
offset_x += box_data.position.size.width; offset_x += base.position.size.width;
}
} }
}, },
CSSTextAlignCenter => { CSSTextAlignCenter => {
offset_x = slack_width.scale_by(0.5f); offset_x = slack_width.scale_by(0.5f);
for line_range.eachi |i| { for line_range.eachi |i| {
let box_data = &self.new_boxes[i].d(); do self.new_boxes[i].with_mut_base |base| {
box_data.position.origin.x = offset_x; base.position.origin.x = offset_x;
offset_x += box_data.position.size.width; offset_x += base.position.size.width;
}
} }
}, },
CSSTextAlignRight => { CSSTextAlignRight => {
offset_x = slack_width; offset_x = slack_width;
for line_range.eachi |i| { for line_range.eachi |i| {
let box_data = &self.new_boxes[i].d(); do self.new_boxes[i].with_mut_base |base| {
box_data.position.origin.x = offset_x; base.position.origin.x = offset_x;
offset_x += box_data.position.size.width; offset_x += base.position.size.width;
}
} }
}, },
} }
@ -483,13 +499,17 @@ impl LineboxScanner {
} }
// return value: whether any box was appended. // return value: whether any box was appended.
priv fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: @mut RenderBox) -> bool { fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: RenderBox) -> bool {
let remaining_width = self.flow.position().size.width - self.pending_line.width; let remaining_width = self.flow.position().size.width - self.pending_line.width;
let in_box_width = in_box.d().position.size.width; let in_box_width = in_box.position().size.width;
let line_is_empty: bool = self.pending_line.range.length() == 0; let line_is_empty: bool = self.pending_line.range.length() == 0;
debug!("LineboxScanner: Trying to append box to line %u (box width: %?, remaining width: %?): %s", debug!("LineboxScanner: Trying to append box to line %u (box width: %?, remaining width: \
self.line_spans.len(), in_box_width, remaining_width, in_box.debug_str()); %?): %s",
self.line_spans.len(),
in_box_width,
remaining_width,
in_box.debug_str());
if in_box_width <= remaining_width { if in_box_width <= remaining_width {
debug!("LineboxScanner: case=box fits without splitting"); debug!("LineboxScanner: case=box fits without splitting");
@ -501,8 +521,9 @@ impl LineboxScanner {
// force it onto the line anyway, if its otherwise empty // force it onto the line anyway, if its otherwise empty
// TODO(Issue #224): signal that horizontal overflow happened? // TODO(Issue #224): signal that horizontal overflow happened?
if line_is_empty { if line_is_empty {
debug!("LineboxScanner: case=box can't split and line %u is empty, so overflowing.", debug!("LineboxScanner: case=box can't split and line %u is empty, so \
self.line_spans.len()); overflowing.",
self.line_spans.len());
self.push_box_to_line(in_box); self.push_box_to_line(in_box);
return true; return true;
} else { } else {
@ -514,7 +535,8 @@ impl LineboxScanner {
// not enough width; try splitting? // not enough width; try splitting?
match in_box.split_to_width(ctx, remaining_width, line_is_empty) { match in_box.split_to_width(ctx, remaining_width, line_is_empty) {
CannotSplit(_) => { CannotSplit(_) => {
error!("LineboxScanner: Tried to split unsplittable render box! %s", in_box.debug_str()); error!("LineboxScanner: Tried to split unsplittable render box! %s",
in_box.debug_str());
return false; return false;
}, },
SplitDidFit(left, right) => { SplitDidFit(left, right) => {
@ -524,11 +546,9 @@ impl LineboxScanner {
self.push_box_to_line(left_box); self.push_box_to_line(left_box);
self.work_list.add_front(right_box); self.work_list.add_front(right_box);
}, },
(Some(left_box), None) => { self.push_box_to_line(left_box); } (Some(left_box), None) => self.push_box_to_line(left_box),
(None, Some(right_box)) => { self.push_box_to_line(right_box); } (None, Some(right_box)) => self.push_box_to_line(right_box),
(None, None) => { (None, None) => error!("LineboxScanner: This split case makes no sense!"),
error!("LineboxScanner: This split case makes no sense!");
}
} }
return true; return true;
}, },
@ -563,15 +583,15 @@ impl LineboxScanner {
} }
// unconditional push // unconditional push
priv fn push_box_to_line(&mut self, box: @mut RenderBox) { fn push_box_to_line(&mut self, box: RenderBox) {
debug!("LineboxScanner: Pushing box b%d to line %u", box.d().id, self.line_spans.len()); debug!("LineboxScanner: Pushing box b%d to line %u", box.id(), self.line_spans.len());
if self.pending_line.range.length() == 0 { if self.pending_line.range.length() == 0 {
assert!(self.new_boxes.len() <= (core::u16::max_value as uint)); assert!(self.new_boxes.len() <= (core::u16::max_value as uint));
self.pending_line.range.reset(self.new_boxes.len(), 0); self.pending_line.range.reset(self.new_boxes.len(), 0);
} }
self.pending_line.range.extend_by(1); self.pending_line.range.extend_by(1);
self.pending_line.width += box.d().position.size.width; self.pending_line.width += box.position().size.width;
self.new_boxes.push(box); self.new_boxes.push(box);
} }
} }
@ -582,7 +602,7 @@ pub struct InlineFlowData {
// A vec of all inline render boxes. Several boxes may // A vec of all inline render boxes. Several boxes may
// correspond to one Node/Element. // correspond to one Node/Element.
boxes: ~[@mut RenderBox], boxes: ~[RenderBox],
// vec of ranges into boxes that represents line positions. // vec of ranges into boxes that represents line positions.
// these ranges are disjoint, and are the result of inline layout. // these ranges are disjoint, and are the result of inline layout.
lines: ~[Range], lines: ~[Range],
@ -648,21 +668,24 @@ impl InlineFlowData {
{ {
let this = &mut *self; let this = &mut *self;
for this.boxes.each |&box| { for this.boxes.each |&box| {
let box2 = &mut *box; match box {
box.d().position.size.width = match *box2 { ImageRenderBoxClass(image_box) => {
ImageBox(_, ref img) => { let size = image_box.image.get_size();
let img2: &mut holder::ImageHolder = unsafe { cast::transmute(img) }; let width = Au::from_px(size.get_or_default(Size2D(0, 0)).width);
Au::from_px(img2.get_size().get_or_default(Size2D(0,0)).width) image_box.base.position.size.width = width;
} }
TextBox(*) => { TextRenderBoxClass(text_box) => {
// Text boxes are initialized with dimensions. // Text boxes are preinitialized.
box.d().position.size.width }
}, GenericRenderBoxClass(generic_box) => {
// TODO(#225): There will be different cases here for `inline-block` and other // TODO(#225): There will be different cases here for `inline-block` and
// replaced content. // other replaced content.
GenericBox(*) => Au::from_px(45), // FIXME(pcwalton): This seems clownshoes; can we remove?
_ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box)) generic_box.position.size.width = Au::from_px(45);
}; }
// FIXME(pcwalton): This isn't very type safe!
_ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box)),
}
} // End of for loop. } // End of for loop.
} }
@ -698,59 +721,64 @@ impl InlineFlowData {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
// Compute the height of each box. // Compute the height of each box.
let d = cur_box.d(); // FIXME: borrow checker workaround match cur_box {
let cur_box = &mut *cur_box; // FIXME: borrow checker workaround ImageRenderBoxClass(image_box) => {
d.position.size.height = match *cur_box { let size = image_box.image.get_size();
ImageBox(_, ref img) => { let height = Au::from_px(size.get_or_default(Size2D(0, 0)).height);
Au::from_px(img.size().height) image_box.base.position.size.height = height;
} }
TextBox(*) => { TextRenderBoxClass(*) => {
// Text boxes are initialized with dimensions. // Text boxes are preinitialized.
d.position.size.height }
}, GenericRenderBoxClass(generic_box) => {
// TODO(Issue #225): different cases for 'inline-block', other replaced content // TODO(Issue #225): There will be different cases here for `inline-block`
GenericBox(*) => Au::from_px(30), // and other replaced content.
// FIXME(pcwalton): This seems clownshoes; can we remove?
generic_box.position.size.height = Au::from_px(30);
}
// FIXME(pcwalton): This isn't very type safe!
_ => { _ => {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
fail!(fmt!("Tried to assign height to unknown Box variant: %s", fail!(fmt!("Tried to assign height to unknown Box variant: %s",
cur_box.debug_str())) cur_box.debug_str()))
} }
}; }
// Compute the bounding rect with the left baseline as origin. Determining line box // Compute the bounding rect with the left baseline as origin. Determining line box
// height is a matter of lining up ideal baselines and then taking the union of all // height is a matter of lining up ideal baselines and then taking the union of all
// these rects. // these rects.
let bounding_box = match *cur_box { let bounding_box = match cur_box {
// Adjust to baseline coordinates. // Adjust to baseline coordinates.
// //
// TODO(#227): Use left/right margins, border, padding for nonreplaced content, // TODO(#227): Use left/right margins, border, padding for nonreplaced content,
// and also use top/bottom margins, border, padding for replaced or // and also use top/bottom margins, border, padding for replaced or
// inline-block content. // inline-block content.
// //
// TODO(#225): Use height, width for 'inline-block' and other replaced content. // TODO(#225): Use height, width for `inline-block` and other replaced content.
ImageBox(*) | GenericBox(*) => { ImageRenderBoxClass(*) | GenericRenderBoxClass(*) => {
let box_bounds = d.position; let height = cur_box.position().size.height;
box_bounds.translate(&Point2D(Au(0), -d.position.size.height)) cur_box.position().translate(&Point2D(Au(0), -height))
}, },
// Adjust the bounding box metric to the box's horizontal offset. // Adjust the bounding box metric to the box's horizontal offset.
// //
// TODO: We can use font metrics directly instead of re-measuring for the // TODO: We can use font metrics directly instead of re-measuring for the
// bounding box. // bounding box.
TextBox(_, data) => { TextRenderBoxClass(text_box) => {
let text_bounds = data.run.metrics_for_range(&data.range).bounding_box; let text_box = &mut *text_box; // FIXME: borrow check workaround
text_bounds.translate(&Point2D(d.position.origin.x, Au(0))) let range = &text_box.text_data.range;
let run = &text_box.text_data.run;
let text_bounds = run.metrics_for_range(range).bounding_box;
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
}, },
_ => { _ => {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s", fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s",
cur_box.debug_str())) cur_box.debug_str()))
} }
}; };
debug!("assign_height_inline: bounding box for box b%d = %?", debug!("assign_height_inline: bounding box for box b%d = %?",
cur_box.d().id, cur_box.id(),
bounding_box); bounding_box);
linebox_bounding_box = linebox_bounding_box.union(&bounding_box); linebox_bounding_box = linebox_bounding_box.union(&bounding_box);
@ -768,15 +796,17 @@ impl InlineFlowData {
// TODO(#226): This is completely wrong. We need to use the element's `line-height` // TODO(#226): This is completely wrong. We need to use the element's `line-height`
// when calculating line box height. Then we should go back over and set Y offsets // when calculating line box height. Then we should go back over and set Y offsets
// according to the `vertical-align` property of the containing block. // according to the `vertical-align` property of the containing block.
let halfleading = match *cur_box { let halfleading = match cur_box {
TextBox(_, data) => { TextRenderBoxClass(text_box) => {
(data.run.font.metrics.em_size - line_height).scale_by(0.5f) (text_box.text_data.run.font.metrics.em_size - line_height).scale_by(0.5)
}, },
_ => Au(0), _ => Au(0),
}; };
cur_box.d().position.origin.y = do cur_box.with_mut_base |base| {
cur_y + halfleading + (baseline_offset - cur_box.d().position.size.height); let height = base.position.size.height;
base.position.origin.y = cur_y + halfleading + baseline_offset - height;
}
} }
cur_y += Au::max(line_height, linebox_height); cur_y += Au::max(line_height, linebox_height);

View file

@ -20,7 +20,7 @@ pub struct RootFlowData {
common: FlowData, common: FlowData,
/// The render box at the root of the tree. /// The render box at the root of the tree.
box: Option<@mut RenderBox> box: Option<RenderBox>
} }
impl RootFlowData { impl RootFlowData {
@ -73,9 +73,11 @@ impl RootFlowData {
self.common.position.size.height = Au::max(ctx.screen_size.size.height, cur_y); self.common.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
do RootFlow(self).with_block_box |box| { do RootFlow(self).with_block_box |box| {
box.d().position.origin.y = Au(0); do box.with_mut_base |base| {
box.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y); base.position.origin.y = Au(0);
let (_used_top, _used_bot) = box.get_used_height(); base.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
let (_used_top, _used_bot) = box.get_used_height();
}
} }
} }

View file

@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** Text layout. */ //! Text layout.
use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox}; use layout::box::{RenderBox, RenderBoxBase, TextRenderBox, UnscannedTextRenderBoxClass};
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use servo_util::range::Range; use servo_util::range::Range;
@ -14,37 +14,47 @@ pub struct TextBoxData {
range: Range, range: Range,
} }
pub fn TextBoxData(run: @TextRun, range: &Range) -> TextBoxData { impl TextBoxData {
TextBoxData { pub fn new(run: @TextRun, range: Range) -> TextBoxData {
run: run, TextBoxData {
range: copy *range, run: run,
range: range,
}
} }
} }
pub fn adapt_textbox_with_range(box_data: &mut RenderBoxData, run: @TextRun, pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range)
range: &Range) -> @mut RenderBox { -> TextRenderBox {
assert!(range.begin() < run.char_len()); assert!(range.begin() < run.char_len());
assert!(range.end() <= run.char_len()); assert!(range.end() <= run.char_len());
assert!(range.length() > 0); assert!(range.length() > 0);
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s", debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
run.char_len(), range.begin(), range.length(), run.text); run.char_len(),
let mut new_box_data = copy *box_data; range.begin(),
let new_text_data = TextBoxData(run, range); range.length(),
let metrics = run.metrics_for_range(range); run.text);
new_box_data.position.size = metrics.bounding_box.size; let new_text_data = TextBoxData::new(run, range);
@mut TextBox(new_box_data, new_text_data) let metrics = run.metrics_for_range(&range);
base.position.size = metrics.bounding_box.size;
TextRenderBox {
base: base,
text_data: new_text_data,
}
} }
pub trait UnscannedMethods { pub trait UnscannedMethods {
fn raw_text(&mut self) -> ~str; /// Copies out the text from an unscanned text box. Fails if this is not an unscanned text box.
fn raw_text(&self) -> ~str;
} }
impl UnscannedMethods for RenderBox { impl UnscannedMethods for RenderBox {
fn raw_text(&mut self) -> ~str { fn raw_text(&self) -> ~str {
match self { match *self {
&UnscannedTextBox(_, ref s) => copy *s, UnscannedTextRenderBoxClass(text_box) => copy text_box.text,
_ => fail!(~"unsupported operation: box.raw_text() on non-unscanned text box.") _ => fail!(~"unsupported operation: box.raw_text() on non-unscanned text box."),
} }
} }
} }