layout: Change RenderBox to an enum and shorten its name in

preparation for removing its `@`-ness.

Also removes a few text copies that were taking place.

This sure does remove a lot of code!
This commit is contained in:
Patrick Walton 2013-12-02 21:45:25 -08:00
parent 2c4714e803
commit 4fda26f76e
8 changed files with 679 additions and 957 deletions

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/. */
//! CSS block layout. //! CSS block formatting contexts.
use layout::box::{RenderBox, RenderBoxUtils}; use layout::box::Box;
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::{BlockFlowClass, FlowClass, Flow, FlowData, ImmutableFlowUtils}; use layout::flow::{BlockFlowClass, FlowClass, Flow, FlowData, ImmutableFlowUtils};
@ -13,13 +13,12 @@ use layout::model::{MaybeAuto, Specified, Auto, specified_or_none, specified};
use layout::float_context::{FloatContext, PlacementInfo, Invalid, FloatType}; use layout::float_context::{FloatContext, PlacementInfo, Invalid, FloatType};
use std::cell::Cell; use std::cell::Cell;
use geom::point::Point2D; use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use geom::size::Size2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use servo_util::geometry::{Au, to_frac_px}; use servo_util::geometry::{Au, to_frac_px};
use servo_util::geometry; use servo_util::geometry;
/// Information specific to floated blocks.
pub struct FloatedBlockInfo { pub struct FloatedBlockInfo {
containing_width: Au, containing_width: Au,
@ -48,12 +47,13 @@ impl FloatedBlockInfo {
} }
} }
/// A block formatting context.
pub struct BlockFlow { pub struct BlockFlow {
/// Data common to all flows. /// Data common to all flows.
base: FlowData, base: FlowData,
/// The associated render box. /// The associated box.
box: Option<@RenderBox>, box: Option<@Box>,
/// Whether this block flow is the root flow. /// Whether this block flow is the root flow.
is_root: bool, is_root: bool,
@ -72,7 +72,7 @@ impl BlockFlow {
} }
} }
pub fn from_box(base: FlowData, box: @RenderBox) -> BlockFlow { pub fn from_box(base: FlowData, box: @Box) -> BlockFlow {
BlockFlow { BlockFlow {
base: base, base: base,
box: Some(box), box: Some(box),
@ -81,7 +81,7 @@ impl BlockFlow {
} }
} }
pub fn float_from_box(base: FlowData, float_type: FloatType, box: @RenderBox) -> BlockFlow { pub fn float_from_box(base: FlowData, float_type: FloatType, box: @Box) -> BlockFlow {
BlockFlow { BlockFlow {
base: base, base: base,
box: Some(box), box: Some(box),
@ -172,9 +172,9 @@ impl BlockFlow {
(width_Au, left_margin_Au, right_margin_Au) (width_Au, left_margin_Au, right_margin_Au)
} }
fn compute_block_margins(&self, box: @RenderBox, remaining_width: Au, available_width: Au) -> (Au, Au, Au) { fn compute_block_margins(&self, box: @Box, remaining_width: Au, available_width: Au)
let base = box.base(); -> (Au, Au, Au) {
let style = base.style(); let style = box.style();
let (width, maybe_margin_left, maybe_margin_right) = let (width, maybe_margin_left, maybe_margin_right) =
(MaybeAuto::from_style(style.Box.width, remaining_width), (MaybeAuto::from_style(style.Box.width, remaining_width),
@ -215,8 +215,8 @@ impl BlockFlow {
return (width, margin_left, margin_right); return (width, margin_left, margin_right);
} }
fn compute_float_margins(&self, box: @RenderBox, remaining_width: Au) -> (Au, Au, Au) { fn compute_float_margins(&self, box: @Box, remaining_width: Au) -> (Au, Au, Au) {
let style = box.base().style(); let style = box.style();
let margin_left = MaybeAuto::from_style(style.Margin.margin_left, let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
remaining_width).specified_or_zero(); remaining_width).specified_or_zero();
let margin_right = MaybeAuto::from_style(style.Margin.margin_right, let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
@ -241,18 +241,19 @@ impl BlockFlow {
let mut float_ctx = Invalid; let mut float_ctx = Invalid;
for &box in self.box.iter() { for &box in self.box.iter() {
let base = box.base(); clearance = match box.clear() {
clearance = match base.clear() {
None => Au::new(0), None => Au::new(0),
Some(clear) => { Some(clear) => {
self.base.floats_in.clearance(clear) self.base.floats_in.clearance(clear)
} }
}; };
top_offset = clearance + base.margin.top + base.border.top + base.padding.top; top_offset = clearance + box.margin.get().top + box.border.get().top +
box.padding.get().top;
cur_y = cur_y + top_offset; cur_y = cur_y + top_offset;
bottom_offset = base.margin.bottom + base.border.bottom + base.padding.bottom; bottom_offset = box.margin.get().bottom + box.border.get().bottom +
left_offset = base.offset(); box.padding.get().bottom;
left_offset = box.offset();
} }
if inorder { if inorder {
@ -279,16 +280,16 @@ impl BlockFlow {
let mut bottom_margin_collapsible = false; let mut bottom_margin_collapsible = false;
let mut first_in_flow = true; let mut first_in_flow = true;
for &box in self.box.iter() { for &box in self.box.iter() {
let base = box.base(); if !self.is_root && box.border.get().top == Au(0) && box.padding.get().top == Au(0) {
if !self.is_root && base.border.top == Au::new(0) && base.padding.top == Au::new(0) { collapsible = box.margin.get().top;
collapsible = base.margin.top;
top_margin_collapsible = true; top_margin_collapsible = true;
} }
if !self.is_root && base.border.bottom == Au::new(0) && base.padding.bottom == Au::new(0) { if !self.is_root && box.border.get().bottom == Au(0) &&
box.padding.get().bottom == Au(0) {
bottom_margin_collapsible = true; bottom_margin_collapsible = true;
} }
margin_top = base.margin.top; margin_top = box.margin.get().top;
margin_bottom = base.margin.bottom; margin_bottom = box.margin.get().bottom;
} }
for kid in self.base.child_iter() { for kid in self.base.child_iter() {
@ -328,8 +329,7 @@ impl BlockFlow {
}; };
for &box in self.box.iter() { for &box in self.box.iter() {
let base = box.base(); let style = box.style();
let style = base.style();
height = match MaybeAuto::from_style(style.Box.height, Au::new(0)) { height = match MaybeAuto::from_style(style.Box.height, Au::new(0)) {
Auto => height, Auto => height,
Specified(value) => value Specified(value) => value
@ -338,22 +338,23 @@ impl BlockFlow {
let mut noncontent_height = Au::new(0); let mut noncontent_height = Au::new(0);
for box in self.box.iter() { for box in self.box.iter() {
let base = box.mut_base(); let mut position = box.position.get();
let mut position_ref = base.position.mutate(); let mut margin = box.margin.get();
let position = &mut position_ref.ptr;
// The associated box is the border box of this flow. // The associated box is the border box of this flow.
base.margin.top = margin_top; margin.top = margin_top;
base.margin.bottom = margin_bottom; margin.bottom = margin_bottom;
position.origin.y = clearance + base.margin.top; position.origin.y = clearance + margin.top;
noncontent_height = base.padding.top + base.padding.bottom + base.border.top + noncontent_height = box.padding.get().top + box.padding.get().bottom +
base.border.bottom; box.border.get().top + box.border.get().bottom;
position.size.height = height + noncontent_height; position.size.height = height + noncontent_height;
noncontent_height = noncontent_height + clearance + base.margin.top + noncontent_height = noncontent_height + clearance + margin.top + margin.bottom;
base.margin.bottom;
box.position.set(position);
box.margin.set(margin);
} }
self.base.position.size.height = height + noncontent_height; self.base.position.size.height = height + noncontent_height;
@ -376,18 +377,18 @@ impl BlockFlow {
let mut margin_height = Au(0); let mut margin_height = Au(0);
for box in self.box.iter() { for box in self.box.iter() {
let base = box.base(); height = box.position.get().size.height;
height = base.position.borrow().ptr.size.height; clearance = match box.clear() {
clearance = match base.clear() {
None => Au(0), None => Au(0),
Some(clear) => self.base.floats_in.clearance(clear), Some(clear) => self.base.floats_in.clearance(clear),
}; };
let noncontent_width = base.padding.left + base.padding.right + base.border.left + let noncontent_width = box.padding.get().left + box.padding.get().right +
base.border.right; box.border.get().left + box.border.get().right;
full_noncontent_width = noncontent_width + base.margin.left + base.margin.right; full_noncontent_width = noncontent_width + box.margin.get().left +
margin_height = base.margin.top + base.margin.bottom; box.margin.get().right;
margin_height = box.margin.get().top + box.margin.get().bottom;
} }
let info = PlacementInfo { let info = PlacementInfo {
@ -419,8 +420,7 @@ impl BlockFlow {
let mut top_offset = Au(0); let mut top_offset = Au(0);
for &box in self.box.iter() { for &box in self.box.iter() {
let base = box.base(); top_offset = box.margin.get().top + box.border.get().top + box.padding.get().top;
top_offset = base.margin.top + base.border.top + base.padding.top;
cur_y = cur_y + top_offset; cur_y = cur_y + top_offset;
} }
@ -434,24 +434,23 @@ impl BlockFlow {
let mut noncontent_height; let mut noncontent_height;
let box = self.box.as_ref().unwrap(); let box = self.box.as_ref().unwrap();
let base = box.base(); let mut position = box.position.get();
let mut position_ref = base.position.mutate();
let position = &mut position_ref.ptr;
// The associated box is the border box of this flow. // The associated box is the border box of this flow.
position.origin.y = base.margin.top; position.origin.y = box.margin.get().top;
noncontent_height = base.padding.top + base.padding.bottom + base.border.top + noncontent_height = box.padding.get().top + box.padding.get().bottom +
base.border.bottom; box.border.get().top + box.border.get().bottom;
//TODO(eatkinson): compute heights properly using the 'height' property. //TODO(eatkinson): compute heights properly using the 'height' property.
let height_prop = MaybeAuto::from_style(base.style().Box.height, let height_prop = MaybeAuto::from_style(box.style().Box.height,
Au::new(0)).specified_or_zero(); Au::new(0)).specified_or_zero();
height = geometry::max(height, height_prop) + noncontent_height; height = geometry::max(height, height_prop) + noncontent_height;
debug!("assign_height_float -- height: {}", height); debug!("assign_height_float -- height: {}", height);
position.size.height = height; position.size.height = height;
box.position.set(position);
} }
pub fn build_display_list_block<E:ExtraDisplayListData>( pub fn build_display_list_block<E:ExtraDisplayListData>(
@ -466,18 +465,16 @@ impl BlockFlow {
if self.base.node.is_iframe_element() { if self.base.node.is_iframe_element() {
let x = self.base.abs_position.x + do self.box.map_default(Au::new(0)) |box| { let x = self.base.abs_position.x + do self.box.map_default(Au::new(0)) |box| {
let base = box.base(); box.margin.get().left + box.border.get().left + box.padding.get().left
base.margin.left + base.border.left + base.padding.left
}; };
let y = self.base.abs_position.y + do self.box.map_default(Au::new(0)) |box| { let y = self.base.abs_position.y + do self.box.map_default(Au::new(0)) |box| {
let base = box.base(); box.margin.get().top + box.border.get().top + box.padding.get().top
base.margin.top + base.border.top + base.padding.top
}; };
let w = self.base.position.size.width - do self.box.map_default(Au::new(0)) |box| { let w = self.base.position.size.width - do self.box.map_default(Au::new(0)) |box| {
box.base().noncontent_width() box.noncontent_width()
}; };
let h = self.base.position.size.height - do self.box.map_default(Au::new(0)) |box| { let h = self.base.position.size.height - do self.box.map_default(Au::new(0)) |box| {
box.base().noncontent_height() box.noncontent_height()
}; };
do self.base.node.with_mut_iframe_element |iframe_element| { do self.base.node.with_mut_iframe_element |iframe_element| {
iframe_element.size.get_mut_ref().set_rect(Rect(Point2D(to_frac_px(x) as f32, iframe_element.size.get_mut_ref().set_rect(Rect(Point2D(to_frac_px(x) as f32,
@ -587,7 +584,7 @@ impl Flow for BlockFlow {
for box in self.box.iter() { for box in self.box.iter() {
{ {
// Can compute border width here since it doesn't depend on anything. // Can compute border width here since it doesn't depend on anything.
box.mut_base().compute_borders(box.base().style()); box.compute_borders(box.style())
} }
let (this_minimum_width, this_preferred_width) = box.minimum_and_preferred_widths(); let (this_minimum_width, this_preferred_width) = box.minimum_and_preferred_widths();
@ -631,16 +628,13 @@ impl Flow for BlockFlow {
} }
for &box in self.box.iter() { for &box in self.box.iter() {
let base = box.base(); let style = box.style();
let mut_base = box.mut_base();
let style = base.style();
let mut_position = &mut base.position.mutate().ptr;
// Can compute padding here since we know containing block width. // Can compute padding here since we know containing block width.
mut_base.compute_padding(style, remaining_width); box.compute_padding(style, remaining_width);
// Margins are 0 right now so base.noncontent_width() is just borders + padding. // Margins are 0 right now so base.noncontent_width() is just borders + padding.
let available_width = remaining_width - base.noncontent_width(); let available_width = remaining_width - box.noncontent_width();
// Top and bottom margins for blocks are 0 if auto. // Top and bottom margins for blocks are 0 if auto.
let margin_top = MaybeAuto::from_style(style.Margin.margin_top, let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
@ -654,20 +648,20 @@ impl Flow for BlockFlow {
self.compute_block_margins(box, remaining_width, available_width) self.compute_block_margins(box, remaining_width, available_width)
}; };
mut_base.margin.top = margin_top; box.margin.set(SideOffsets2D::new(margin_top,
mut_base.margin.right = margin_right; margin_right,
mut_base.margin.bottom = margin_bottom; margin_bottom,
mut_base.margin.left = margin_left; margin_left));
x_offset = base.offset(); x_offset = box.offset();
remaining_width = width; remaining_width = width;
//The associated box is the border box of this flow // The associated box is the border box of this flow.
mut_position.origin.x = base.margin.left; let position_ref = box.position.mutate();
position_ref.ptr.origin.x = box.margin.get().left;
let padding_and_borders = base.padding.left + base.padding.right + let padding_and_borders = box.padding.get().left + box.padding.get().right +
base.border.left + base.border.right; box.border.get().left + box.border.get().right;
mut_position.size.width = remaining_width + padding_and_borders; position_ref.ptr.size.width = remaining_width + padding_and_borders;
} }
if self.is_float() { if self.is_float() {
@ -734,25 +728,23 @@ impl Flow for BlockFlow {
} }
for &box in self.box.iter() { for &box in self.box.iter() {
let base = box.base();
// The top margin collapses with its first in-flow block-level child's // The top margin collapses with its first in-flow block-level child's
// top margin if the parent has no top border, no top padding. // top margin if the parent has no top border, no top padding.
if *first_in_flow && top_margin_collapsible { if *first_in_flow && top_margin_collapsible {
// If top-margin of parent is less than top-margin of its first child, // If top-margin of parent is less than top-margin of its first child,
// the parent box goes down until its top is aligned with the child. // the parent box goes down until its top is aligned with the child.
if *margin_top < base.margin.top { if *margin_top < box.margin.get().top {
// TODO: The position of child floats should be updated and this // TODO: The position of child floats should be updated and this
// would influence clearance as well. See #725 // would influence clearance as well. See #725
let extra_margin = base.margin.top - *margin_top; let extra_margin = box.margin.get().top - *margin_top;
*top_offset = *top_offset + extra_margin; *top_offset = *top_offset + extra_margin;
*margin_top = base.margin.top; *margin_top = box.margin.get().top;
} }
} }
// The bottom margin of an in-flow block-level element collapses // The bottom margin of an in-flow block-level element collapses
// with the top margin of its next in-flow block-level sibling. // with the top margin of its next in-flow block-level sibling.
*collapsing = geometry::min(base.margin.top, *collapsible); *collapsing = geometry::min(box.margin.get().top, *collapsible);
*collapsible = base.margin.bottom; *collapsible = box.margin.get().bottom;
} }
*first_in_flow = false; *first_in_flow = false;

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,8 @@
//! Each step of the traversal considers the node and existing flow, if there is one. If a node is //! Each step of the traversal considers the node and existing flow, if there is one. If a node is
//! not dirty and an existing flow exists, then the traversal reuses that flow. Otherwise, it //! not dirty and an existing flow exists, then the traversal reuses that flow. Otherwise, it
//! proceeds to construct either a flow or a `ConstructionItem`. A construction item is a piece of //! proceeds to construct either a flow or a `ConstructionItem`. A construction item is a piece of
//! intermediate data that goes with a DOM node and hasn't found its "home" yet—maybe it's a render //! intermediate data that goes with a DOM node and hasn't found its "home" yet—maybe it's a box,
//! box, maybe it's an absolute or fixed position thing that hasn't found its containing block yet. //! maybe it's an absolute or fixed position thing that hasn't found its containing block yet.
//! Construction items bubble up the tree from children to parents until they find their homes. //! Construction items bubble up the tree from children to parents until they find their homes.
//! //!
//! TODO(pcwalton): There is no incremental reflow yet. This scheme requires that nodes either have //! TODO(pcwalton): There is no incremental reflow yet. This scheme requires that nodes either have
@ -22,8 +22,7 @@
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::block::BlockFlow; use layout::block::BlockFlow;
use layout::box::{GenericRenderBox, ImageRenderBox, RenderBox, RenderBoxBase}; use layout::box::{Box, GenericBox, ImageBox, ImageBoxInfo, UnscannedTextBox, UnscannedTextBoxInfo};
use layout::box::{UnscannedTextRenderBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::float_context::FloatType; use layout::float_context::FloatType;
use layout::flow::{Flow, FlowData, MutableFlowUtils}; use layout::flow::{Flow, FlowData, MutableFlowUtils};
@ -70,8 +69,8 @@ struct InlineBoxesConstructionResult {
/// TODO(pcwalton): Small vector optimization. /// TODO(pcwalton): Small vector optimization.
splits: Option<~[InlineBlockSplit]>, splits: Option<~[InlineBlockSplit]>,
/// Any render boxes that succeed the {ib} splits. /// Any boxes that succeed the {ib} splits.
boxes: ~[@RenderBox], boxes: ~[@Box],
} }
/// Represents an {ib} split that has not yet found the containing block that it belongs to. This /// Represents an {ib} split that has not yet found the containing block that it belongs to. This
@ -97,10 +96,10 @@ struct InlineBoxesConstructionResult {
/// C /// C
/// ]) /// ])
struct InlineBlockSplit { struct InlineBlockSplit {
/// The inline render boxes that precede the flow. /// The inline boxes that precede the flow.
/// ///
/// TODO(pcwalton): Small vector optimization. /// TODO(pcwalton): Small vector optimization.
predecessor_boxes: ~[@RenderBox], predecessor_boxes: ~[@Box],
/// The flow that caused this {ib} split. /// The flow that caused this {ib} split.
flow: ~Flow:, flow: ~Flow:,
@ -180,11 +179,6 @@ pub struct FlowConstructor<'self> {
/// ///
/// FIXME(pcwalton): This is going to have to be atomic; can't we do something better? /// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
next_flow_id: Slot<int>, next_flow_id: Slot<int>,
/// The next box ID to assign.
///
/// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
next_box_id: Slot<int>,
} }
impl<'self> FlowConstructor<'self> { impl<'self> FlowConstructor<'self> {
@ -193,7 +187,6 @@ impl<'self> FlowConstructor<'self> {
FlowConstructor { FlowConstructor {
layout_context: layout_context, layout_context: layout_context,
next_flow_id: Slot::init(0), next_flow_id: Slot::init(0),
next_box_id: Slot::init(0),
} }
} }
@ -204,39 +197,36 @@ impl<'self> FlowConstructor<'self> {
id id
} }
/// Returns the next render box ID and bumps the internal counter. /// Builds the `ImageBoxInfo` for the given image. This is out of line to guide inlining.
fn next_box_id(&self) -> int { fn build_box_info_for_image(&self, node: AbstractNode<LayoutView>) -> Option<ImageBoxInfo> {
let id = self.next_box_id.get();
self.next_box_id.set(id + 1);
id
}
/// Builds a `RenderBox` for the given image. This is out of line to guide inlining.
fn build_box_for_image(&self, base: RenderBoxBase, node: AbstractNode<LayoutView>)
-> @RenderBox {
// FIXME(pcwalton): Don't copy URLs. // FIXME(pcwalton): Don't copy URLs.
let url = node.with_imm_image_element(|image_element| { let url = node.with_imm_image_element(|image_element| {
image_element.image.as_ref().map(|url| (*url).clone()) image_element.image.as_ref().map(|url| (*url).clone())
}); });
match url { match url {
None => @GenericRenderBox::new(base) as @RenderBox, None => None,
Some(url) => { Some(url) => {
// FIXME(pcwalton): The fact that image render boxes store the cache in the // FIXME(pcwalton): The fact that image boxes store the cache within them makes
// box makes little sense to me. // little sense to me.
@ImageRenderBox::new(base, url, self.layout_context.image_cache) as @RenderBox Some(ImageBoxInfo::new(url, self.layout_context.image_cache))
} }
} }
} }
/// Builds a `RenderBox` for the given node. /// Builds a `Box` for the given node.
fn build_box_for_node(&self, node: AbstractNode<LayoutView>) -> @RenderBox { fn build_box_for_node(&self, node: AbstractNode<LayoutView>) -> @Box {
let base = RenderBoxBase::new(node, self.next_box_id()); let specific = match node.type_id() {
match node.type_id() { ElementNodeTypeId(HTMLImageElementTypeId) => {
ElementNodeTypeId(HTMLImageElementTypeId) => self.build_box_for_image(base, node), match self.build_box_info_for_image(node) {
TextNodeTypeId => @UnscannedTextRenderBox::new(base) as @RenderBox, None => GenericBox,
_ => @GenericRenderBox::new(base) as @RenderBox, Some(image_box_info) => ImageBox(image_box_info),
} }
}
TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(&node)),
_ => GenericBox,
};
@Box::new(node, specific)
} }
/// Creates an inline flow from a set of inline boxes and adds it as a child of the given flow. /// Creates an inline flow from a set of inline boxes and adds it as a child of the given flow.
@ -245,7 +235,7 @@ impl<'self> FlowConstructor<'self> {
/// otherwise. /// otherwise.
#[inline(always)] #[inline(always)]
fn flush_inline_boxes_to_flow(&self, fn flush_inline_boxes_to_flow(&self,
boxes: ~[@RenderBox], boxes: ~[@Box],
flow: &mut ~Flow:, flow: &mut ~Flow:,
node: AbstractNode<LayoutView>) { node: AbstractNode<LayoutView>) {
if boxes.len() > 0 { if boxes.len() > 0 {
@ -259,7 +249,7 @@ impl<'self> FlowConstructor<'self> {
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of /// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
/// the given flow. /// the given flow.
fn flush_inline_boxes_to_flow_if_necessary(&self, fn flush_inline_boxes_to_flow_if_necessary(&self,
opt_boxes: &mut Option<~[@RenderBox]>, opt_boxes: &mut Option<~[@Box]>,
flow: &mut ~Flow:, flow: &mut ~Flow:,
node: AbstractNode<LayoutView>) { node: AbstractNode<LayoutView>) {
let opt_boxes = util::replace(opt_boxes, None); let opt_boxes = util::replace(opt_boxes, None);
@ -378,13 +368,13 @@ impl<'self> FlowConstructor<'self> {
let mut opt_inline_block_splits = None; let mut opt_inline_block_splits = None;
let mut opt_box_accumulator = None; let mut opt_box_accumulator = None;
// Concatenate all the render boxes of our kids, creating {ib} splits as necessary. // Concatenate all the boxes of our kids, creating {ib} splits as necessary.
for kid in node.children() { for kid in node.children() {
match kid.swap_out_construction_result() { match kid.swap_out_construction_result() {
NoConstructionResult => {} NoConstructionResult => {}
FlowConstructionResult(flow) => { FlowConstructionResult(flow) => {
// {ib} split. Flush the accumulator to our new split and make a new // {ib} split. Flush the accumulator to our new split and make a new
// accumulator to hold any subsequent `RenderBox`es we come across. // accumulator to hold any subsequent boxes we come across.
let split = InlineBlockSplit { let split = InlineBlockSplit {
predecessor_boxes: util::replace(&mut opt_box_accumulator, None).to_vec(), predecessor_boxes: util::replace(&mut opt_box_accumulator, None).to_vec(),
flow: flow, flow: flow,
@ -438,7 +428,7 @@ impl<'self> FlowConstructor<'self> {
} }
/// Creates an `InlineBoxesConstructionResult` for replaced content. Replaced content doesn't /// Creates an `InlineBoxesConstructionResult` for replaced content. Replaced content doesn't
/// render its children, so this just nukes a child's boxes and creates a `RenderBox`. /// render its children, so this just nukes a child's boxes and creates a `Box`.
fn build_boxes_for_replaced_inline_content(&self, node: AbstractNode<LayoutView>) fn build_boxes_for_replaced_inline_content(&self, node: AbstractNode<LayoutView>)
-> ConstructionResult { -> ConstructionResult {
for kid in node.children() { for kid in node.children() {
@ -454,7 +444,7 @@ impl<'self> FlowConstructor<'self> {
ConstructionItemConstructionResult(construction_item) ConstructionItemConstructionResult(construction_item)
} }
/// Builds one or more render boxes for a node with `display: inline`. This yields an /// Builds one or more boxes for a node with `display: inline`. This yields an
/// `InlineBoxesConstructionResult`. /// `InlineBoxesConstructionResult`.
fn build_boxes_for_inline(&self, node: AbstractNode<LayoutView>) -> ConstructionResult { fn build_boxes_for_inline(&self, node: AbstractNode<LayoutView>) -> ConstructionResult {
// Is this node replaced content? // Is this node replaced content?
@ -462,8 +452,7 @@ impl<'self> FlowConstructor<'self> {
// Go to a path that concatenates our kids' boxes. // Go to a path that concatenates our kids' boxes.
self.build_boxes_for_nonreplaced_inline_content(node) self.build_boxes_for_nonreplaced_inline_content(node)
} else { } else {
// Otherwise, just nuke our kids' boxes, create our `RenderBox` if any, and be done // Otherwise, just nuke our kids' boxes, create our box if any, and be done with it.
// with it.
self.build_boxes_for_replaced_inline_content(node) self.build_boxes_for_replaced_inline_content(node)
} }
} }
@ -494,7 +483,7 @@ impl<'self> PostorderNodeTraversal for FlowConstructor<'self> {
} }
} }
// Inline items contribute inline render box construction results. // Inline items contribute inline box construction results.
(display::inline, float::none) => { (display::inline, float::none) => {
let construction_result = self.build_boxes_for_inline(node); let construction_result = self.build_boxes_for_inline(node);
node.set_flow_construction_result(construction_result) node.set_flow_construction_result(construction_result)
@ -575,7 +564,7 @@ impl NodeUtils for AbstractNode<LayoutView> {
} }
/// Strips ignorable whitespace from the start of a list of boxes. /// Strips ignorable whitespace from the start of a list of boxes.
fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[@RenderBox]>) { fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[@Box]>) {
match util::replace(opt_boxes, None) { match util::replace(opt_boxes, None) {
None => return, None => return,
Some(boxes) => { Some(boxes) => {
@ -597,7 +586,7 @@ fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[@RenderBox]>)
} }
/// Strips ignorable whitespace from the end of a list of boxes. /// Strips ignorable whitespace from the end of a list of boxes.
fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[@RenderBox]>) { fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[@Box]>) {
match *opt_boxes { match *opt_boxes {
None => {} None => {}
Some(ref mut boxes) => { Some(ref mut boxes) => {

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/. */
//! Constructs display lists from render boxes. //! Constructs display lists from boxes.
use layout::box::{RenderBox, RenderBoxUtils}; use layout::box::Box;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use std::cast::transmute; use std::cast::transmute;
use script::dom::node::AbstractNode; use script::dom::node::AbstractNode;
@ -16,27 +16,27 @@ use style;
/// that nodes in this view shoud not really be touched. The idea is to /// 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. /// 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: &@Box) -> Self;
} }
pub type Nothing = (); pub type Nothing = ();
impl ExtraDisplayListData for AbstractNode<()> { impl ExtraDisplayListData for AbstractNode<()> {
fn new(box: &@RenderBox) -> AbstractNode<()> { fn new(box: &@Box) -> AbstractNode<()> {
unsafe { unsafe {
transmute(box.base().node) transmute(box.node)
} }
} }
} }
impl ExtraDisplayListData for Nothing { impl ExtraDisplayListData for Nothing {
fn new(_: &@RenderBox) -> Nothing { fn new(_: &@Box) -> Nothing {
() ()
} }
} }
impl ExtraDisplayListData for @RenderBox { impl ExtraDisplayListData for @Box {
fn new(box: &@RenderBox) -> @RenderBox { fn new(box: &@Box) -> @Box {
*box *box
} }
} }

View file

@ -2,23 +2,23 @@
* 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/. */
//! Servo's experimental layout system builds a tree of `Flow` and `RenderBox` objects and //! Servo's experimental layout system builds a tree of `Flow` and `Box` objects and solves
/// solves layout constraints to obtain positions and display attributes of tree nodes. Positions //! layout constraints to obtain positions and display attributes of tree nodes. Positions are
/// are computed in several tree traversals driven by the fundamental data dependencies required by //! computed in several tree traversals driven by the fundamental data dependencies required by
/// inline and block layout. /// inline and block layout.
/// ///
/// Flows are interior nodes in the layout tree and correspond closely to *flow contexts* in the /// Flows are interior nodes in the layout tree and correspond closely to *flow contexts* in the
/// CSS specification. Flows are responsible for positioning their child flow contexts and render /// CSS specification. Flows are responsible for positioning their child flow contexts and boxes.
/// boxes. Flows have purpose-specific fields, such as auxiliary line box structs, out-of-flow /// Flows have purpose-specific fields, such as auxiliary line box structs, out-of-flow child
/// child lists, and so on. /// lists, and so on.
/// ///
/// Currently, the important types of flows are: /// Currently, the important types of flows are:
/// ///
/// * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of /// * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of
/// which are positioned according to block formatting context rules (CSS block boxes). Block /// which are positioned according to block formatting context rules (CSS block boxes). Block
/// flows also contain a single `GenericBox` to represent their rendered borders, padding, etc. /// flows also contain a single `GenericBox` to represent their rendered borders, padding, etc.
/// (In the future, this render box may be folded into `BlockFlow` to save space.) The BlockFlow /// (In the future, this box may be folded into `BlockFlow` to save space.) The BlockFlow at the
/// at the root of the tree has special behavior: it stretches to the boundaries of the viewport. /// root of the tree has special behavior: it stretches to the boundaries of the viewport.
/// ///
/// * `InlineFlow`: A flow that establishes an inline context. It has a flat list of child /// * `InlineFlow`: A flow that establishes an inline context. It has a flat list of child
/// boxes/flows that are subject to inline layout and line breaking and structs to represent /// boxes/flows that are subject to inline layout and line breaking and structs to represent
@ -27,7 +27,7 @@
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::block::BlockFlow; use layout::block::BlockFlow;
use layout::box::RenderBox; use layout::box::Box;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::float_context::{FloatContext, Invalid}; use layout::float_context::{FloatContext, Invalid};
@ -340,12 +340,12 @@ pub struct FlowData {
} }
pub struct BoxIterator { pub struct BoxIterator {
priv boxes: ~[@RenderBox], priv boxes: ~[@Box],
priv index: uint, priv index: uint,
} }
impl Iterator<@RenderBox> for BoxIterator { impl Iterator<@Box> for BoxIterator {
fn next(&mut self) -> Option<@RenderBox> { fn next(&mut self) -> Option<@Box> {
if self.index >= self.boxes.len() { if self.index >= self.boxes.len() {
None None
} else { } else {

View file

@ -3,8 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox}; use layout::box::{Box, CannotSplit, GenericBox, ImageBox, ScannedTextBox, SplitDidFit};
use layout::box::{RenderBoxUtils, SplitDidFit, SplitDidNotFit, TextRenderBoxClass}; use layout::box::{SplitDidNotFit, UnscannedTextBox};
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::{FlowClass, Flow, FlowData, InlineFlowClass}; use layout::flow::{FlowClass, Flow, FlowData, InlineFlowClass};
@ -25,34 +25,31 @@ use std::cell::Cell;
use std::u16; use std::u16;
use std::util; use std::util;
/* /// Lineboxes are represented as offsets into the child list, rather than
Lineboxes are represented as offsets into the child list, rather than /// as an object that "owns" boxes. Choosing a different set of line
as an object that "owns" boxes. Choosing a different set of line /// breaks requires a new list of offsets, and possibly some splitting and
breaks requires a new list of offsets, and possibly some splitting and /// merging of TextBoxes.
merging of TextBoxes. ///
/// A similar list will keep track of the mapping between CSS boxes and
A similar list will keep track of the mapping between CSS boxes and /// the corresponding boxes in the inline flow.
the corresponding render boxes in the inline flow. ///
/// After line breaks are determined, render boxes in the inline flow may
After line breaks are determined, render boxes in the inline flow may /// overlap visually. For example, in the case of nested inline CSS boxes,
overlap visually. For example, in the case of nested inline CSS boxes, /// outer inlines must be at least as large as the inner inlines, for
outer inlines must be at least as large as the inner inlines, for /// purposes of drawing noninherited things like backgrounds, borders,
purposes of drawing noninherited things like backgrounds, borders, /// outlines.
outlines. ///
/// N.B. roc has an alternative design where the list instead consists of
N.B. roc has an alternative design where the list instead consists of /// things like "start outer box, text, start inner box, text, end inner
things like "start outer box, text, start inner box, text, end inner /// box, text, end outer box, text". This seems a little complicated to
box, text, end outer box, text". This seems a little complicated to /// serve as the starting point, but the current design doesn't make it
serve as the starting point, but the current design doesn't make it /// hard to try out that alternative.
hard to try out that alternative. ///
/// Line boxes also contain some metadata used during line breaking. The
Line boxes also contain some metadata used during line breaking. The /// green zone is the area that the line can expand to before it collides
green zone is the area that the line can expand to before it collides /// with a float or a horizontal wall of the containing block. The top
with a float or a horizontal wall of the containing block. The top /// left corner of the green zone is the same as that of the line, but
left corner of the green zone is the same as that of the line, but /// the green zone can be taller and wider than the line itself.
the green zone can be taller and wider than the line itself.
*/
struct LineBox { struct LineBox {
range: Range, range: Range,
bounds: Rect<Au>, bounds: Rect<Au>,
@ -61,8 +58,9 @@ struct LineBox {
struct LineboxScanner { struct LineboxScanner {
floats: FloatContext, floats: FloatContext,
new_boxes: ~[@RenderBox], new_boxes: ~[@Box],
work_list: @mut RingBuf<@RenderBox>, /// FIXME(pcwalton): `@mut`? :(
work_list: @mut RingBuf<@Box>,
pending_line: LineBox, pending_line: LineBox,
lines: ~[LineBox], lines: ~[LineBox],
cur_y: Au, cur_y: Au,
@ -128,11 +126,11 @@ impl LineboxScanner {
break break
} }
let box = flow.boxes[i]; i += 1; let box = flow.boxes[i]; i += 1;
debug!("LineboxScanner: Working with box from box list: b{:d}", box.base().id()); debug!("LineboxScanner: Working with box from box list: b{}", box.debug_id());
box box
} else { } else {
let box = self.work_list.pop_front().unwrap(); let box = self.work_list.pop_front().unwrap();
debug!("LineboxScanner: Working with box from work list: b{:d}", box.base().id()); debug!("LineboxScanner: Working with box from work list: b{}", box.debug_id());
box box
}; };
@ -179,7 +177,7 @@ impl LineboxScanner {
// FIXME(eatkinson): this assumes that the tallest box in the line determines the line height // FIXME(eatkinson): this assumes that the tallest box in the line determines the line height
// This might not be the case with some weird text fonts. // This might not be the case with some weird text fonts.
fn new_height_for_line(&self, new_box: &RenderBox) -> Au { fn new_height_for_line(&self, new_box: &Box) -> Au {
let box_height = new_box.box_height(); let box_height = new_box.box_height();
if box_height > self.pending_line.bounds.size.height { if box_height > self.pending_line.bounds.size.height {
box_height box_height
@ -188,14 +186,14 @@ 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 box. Returns the bounding rect
/// Returns: the bounding rect of the line's green zone (whose origin coincides /// of the line's green zone (whose origin coincides with the line's origin) and the actual
/// with the line's origin) and the actual width of the first box after splitting. /// width of the first box after splitting.
fn initial_line_placement(&self, first_box: @RenderBox, ceiling: Au, flow: &mut InlineFlow) fn initial_line_placement(&self, first_box: @Box, ceiling: Au, flow: &mut InlineFlow)
-> (Rect<Au>, Au) { -> (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());
let first_box_size = first_box.base().position.get().size; let first_box_size = first_box.position.get().size;
let splittable = first_box.can_split(); let splittable = first_box.can_split();
debug!("LineboxScanner: box size: {}, splittable: {}", first_box_size, splittable); debug!("LineboxScanner: box size: {}, splittable: {}", first_box_size, splittable);
let line_is_empty: bool = self.pending_line.range.length() == 0; let line_is_empty: bool = self.pending_line.range.length() == 0;
@ -250,9 +248,9 @@ impl LineboxScanner {
debug!("LineboxScanner: case=box split and fit"); debug!("LineboxScanner: case=box split and fit");
let actual_box_width = match (left, right) { let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.base().position.get().size.width, (Some(l_box), Some(_)) => l_box.position.get().size.width,
(Some(l_box), None) => l_box.base().position.get().size.width, (Some(l_box), None) => l_box.position.get().size.width,
(None, Some(r_box)) => r_box.base().position.get().size.width, (None, Some(r_box)) => r_box.position.get().size.width,
(None, None) => fail!("This case makes no sense.") (None, None) => fail!("This case makes no sense.")
}; };
return (line_bounds, actual_box_width); return (line_bounds, actual_box_width);
@ -264,9 +262,9 @@ impl LineboxScanner {
debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down"); debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down");
let actual_box_width = match (left, right) { let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.base().position.get().size.width, (Some(l_box), Some(_)) => l_box.position.get().size.width,
(Some(l_box), None) => l_box.base().position.get().size.width, (Some(l_box), None) => l_box.position.get().size.width,
(None, Some(r_box)) => r_box.base().position.get().size.width, (None, Some(r_box)) => r_box.position.get().size.width,
(None, None) => fail!("This case makes no sense.") (None, None) => fail!("This case makes no sense.")
}; };
@ -281,7 +279,7 @@ 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, flow: &mut InlineFlow) -> bool { fn try_append_to_line(&mut self, in_box: @Box, flow: &mut InlineFlow) -> 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 {
@ -293,7 +291,7 @@ impl LineboxScanner {
debug!("LineboxScanner: Trying to append box to line {:u} (box size: {}, green zone: \ debug!("LineboxScanner: Trying to append box to line {:u} (box size: {}, green zone: \
{}): {:s}", {}): {:s}",
self.lines.len(), self.lines.len(),
in_box.base().position.get().size, in_box.position.get().size,
self.pending_line.green_zone, self.pending_line.green_zone,
in_box.debug_str()); in_box.debug_str());
@ -349,8 +347,7 @@ impl LineboxScanner {
// horizontally. We'll try to place the whole box on this line and break somewhere // horizontally. We'll try to place the whole box on this line and break somewhere
// if it doesn't fit. // if it doesn't fit.
let new_width = self.pending_line.bounds.size.width + let new_width = self.pending_line.bounds.size.width + in_box.position.get().size.width;
in_box.base().position.get().size.width;
if new_width <= green_zone.width { if new_width <= green_zone.width {
debug!("LineboxScanner: case=box fits without splitting"); debug!("LineboxScanner: case=box fits without splitting");
@ -424,8 +421,8 @@ impl LineboxScanner {
} }
// unconditional push // unconditional push
fn push_box_to_line(&mut self, box: @RenderBox) { fn push_box_to_line(&mut self, box: @Box) {
debug!("LineboxScanner: Pushing box b{:d} to line {:u}", box.base().id(), self.lines.len()); debug!("LineboxScanner: Pushing box {} to line {:u}", box.debug_id(), self.lines.len());
if self.pending_line.range.length() == 0 { if self.pending_line.range.length() == 0 {
assert!(self.new_boxes.len() <= (u16::max_value as uint)); assert!(self.new_boxes.len() <= (u16::max_value as uint));
@ -433,9 +430,9 @@ impl LineboxScanner {
} }
self.pending_line.range.extend_by(1); self.pending_line.range.extend_by(1);
self.pending_line.bounds.size.width = self.pending_line.bounds.size.width + self.pending_line.bounds.size.width = self.pending_line.bounds.size.width +
box.base().position.get().size.width; box.position.get().size.width;
self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height, self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height,
box.base().position.get().size.height); box.position.get().size.height);
self.new_boxes.push(box); self.new_boxes.push(box);
} }
} }
@ -444,9 +441,9 @@ pub struct InlineFlow {
/// Data common to all flows. /// Data common to all flows.
base: FlowData, base: FlowData,
// A vec of all inline render boxes. Several boxes may /// A vector of all inline render boxes. Several boxes may correspond to one node/element.
// correspond to one Node/Element. boxes: ~[@Box],
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.
// also some metadata used for positioning lines // also some metadata used for positioning lines
@ -467,7 +464,7 @@ impl InlineFlow {
} }
} }
pub fn from_boxes(base: FlowData, boxes: ~[@RenderBox]) -> InlineFlow { pub fn from_boxes(base: FlowData, boxes: ~[@Box]) -> InlineFlow {
InlineFlow { InlineFlow {
base: base, base: base,
boxes: boxes, boxes: boxes,
@ -560,8 +557,7 @@ impl Flow for InlineFlow {
fn assign_widths(&mut self, _: &mut LayoutContext) { fn assign_widths(&mut self, _: &mut 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 `Box`.
// `RenderBox`.
debug!("assign_widths_inline: floats_in: {:?}", self.base.floats_in); debug!("assign_widths_inline: floats_in: {:?}", self.base.floats_in);
{ {
@ -620,14 +616,13 @@ impl Flow for InlineFlow {
if slack_width < Au::new(0) { if slack_width < Au::new(0) {
slack_width = Au::new(0); slack_width = Au::new(0);
} }
//assert!(slack_width >= Au::new(0), "Too many boxes on line");
// Get the text alignment. // Get the text alignment.
// TODO(Issue #222): use 'text-align' property from InlineFlow's // TODO(Issue #222): use 'text-align' property from InlineFlow's
// block container, not from the style of the first box child. // block container, not from the style of the first box child.
let linebox_align = if line.range.begin() < self.boxes.len() { let linebox_align = if line.range.begin() < self.boxes.len() {
let first_box = self.boxes[line.range.begin()]; let first_box = self.boxes[line.range.begin()];
first_box.base().nearest_ancestor_element().style().Text.text_align first_box.nearest_ancestor_element().style().Text.text_align
} else { } else {
// Nothing to lay out, so assume left alignment. // Nothing to lay out, so assume left alignment.
text_align::left text_align::left
@ -640,7 +635,7 @@ impl Flow for InlineFlow {
// TODO(Issue #213): implement `text-align: justify` // TODO(Issue #213): implement `text-align: justify`
text_align::left | text_align::justify => { text_align::left | text_align::justify => {
for i in line.range.eachi() { for i in line.range.eachi() {
let box = self.boxes[i].base(); let box = &self.boxes[i];
box.position.mutate().ptr.origin.x = offset_x; box.position.mutate().ptr.origin.x = offset_x;
offset_x = offset_x + box.position.get().size.width; offset_x = offset_x + box.position.get().size.width;
} }
@ -648,7 +643,7 @@ impl Flow for InlineFlow {
text_align::center => { text_align::center => {
offset_x = offset_x + slack_width.scale_by(0.5); offset_x = offset_x + slack_width.scale_by(0.5);
for i in line.range.eachi() { for i in line.range.eachi() {
let box = self.boxes[i].base(); let box = &self.boxes[i];
box.position.mutate().ptr.origin.x = offset_x; box.position.mutate().ptr.origin.x = offset_x;
offset_x = offset_x + box.position.get().size.width; offset_x = offset_x + box.position.get().size.width;
} }
@ -656,7 +651,7 @@ impl Flow for InlineFlow {
text_align::right => { text_align::right => {
offset_x = offset_x + slack_width; offset_x = offset_x + slack_width;
for i in line.range.eachi() { for i in line.range.eachi() {
let box = self.boxes[i].base(); let box = &self.boxes[i];
box.position.mutate().ptr.origin.x = offset_x; box.position.mutate().ptr.origin.x = offset_x;
offset_x = offset_x + box.position.get().size.width; offset_x = offset_x + box.position.get().size.width;
} }
@ -680,41 +675,40 @@ impl Flow for InlineFlow {
for box_i in line.range.eachi() { for box_i in line.range.eachi() {
let cur_box = self.boxes[box_i]; let cur_box = self.boxes[box_i];
let (top_from_base, bottom_from_base, ascent) = match cur_box.class() { // FIXME(pcwalton): Move into `box.rs` like the rest of box-specific layout code?
ImageRenderBoxClass => { let (top_from_base, bottom_from_base, ascent) = match cur_box.specific {
let image_box = cur_box.as_image_render_box(); ImageBox(ref image_box) => {
let mut height = image_box.image_height(); let mut height = image_box.image_height(cur_box);
// TODO: margin, border, padding's top and bottom should be calculated in advance, // TODO: margin, border, padding's top and bottom should be calculated in advance,
// since baseline of image is bottom margin edge. // since baseline of image is bottom margin edge.
let mut top; let mut top;
let mut bottom; let mut bottom;
{ {
let base = &image_box.base; top = cur_box.border.get().top + cur_box.padding.get().top +
top = base.border.top + base.padding.top + base.margin.top; cur_box.margin.get().top;
bottom = base.border.bottom + base.padding.bottom + bottom = cur_box.border.get().bottom + cur_box.padding.get().bottom +
base.margin.bottom; cur_box.margin.get().bottom;
} }
let noncontent_height = top + bottom; let noncontent_height = top + bottom;
height = height + noncontent_height; height = height + noncontent_height;
let position_ref = image_box.base.position.mutate(); let position_ref = cur_box.position.mutate();
position_ref.ptr.size.height = height; position_ref.ptr.size.height = height;
position_ref.ptr.translate(&Point2D(Au::new(0), -height)); position_ref.ptr.translate(&Point2D(Au::new(0), -height));
let ascent = height + bottom; let ascent = height + bottom;
(height, Au::new(0), ascent) (height, Au::new(0), ascent)
}, },
TextRenderBoxClass => { ScannedTextBox(ref text_box) => {
let text_box = cur_box.as_text_render_box();
let range = &text_box.range; let range = &text_box.range;
let run = &text_box.run; let run = &text_box.run;
// Compute the height based on the line-height and font size // Compute the height based on the line-height and font size
let text_bounds = run.metrics_for_range(range).bounding_box; let text_bounds = run.metrics_for_range(range).bounding_box;
let em_size = text_bounds.size.height; let em_size = text_bounds.size.height;
let line_height = text_box.base.calculate_line_height(em_size); let line_height = cur_box.calculate_line_height(em_size);
// Find the top and bottom of the content area. // Find the top and bottom of the content area.
// Those are used in text-top and text-bottom value of 'vertical-align' // Those are used in text-top and text-bottom value of 'vertical-align'
@ -722,20 +716,16 @@ impl Flow for InlineFlow {
// Offset from the top of the box is 1/2 of the leading + ascent // Offset from the top of the box is 1/2 of the leading + ascent
let text_offset = text_ascent + (line_height - em_size).scale_by(0.5); let text_offset = text_ascent + (line_height - em_size).scale_by(0.5);
text_bounds.translate(&Point2D(text_box.base.position.get().origin.x, text_bounds.translate(&Point2D(cur_box.position.get().origin.x, Au(0)));
Au::new(0)));
(text_offset, line_height - text_offset, text_ascent) (text_offset, line_height - text_offset, text_ascent)
}, },
GenericRenderBoxClass => { GenericBox => {
let base = cur_box.base(); let height = cur_box.position.get().size.height;
let height = base.position.get().size.height;
(height, Au::new(0), height) (height, Au::new(0), height)
}, },
// FIXME(pcwalton): This isn't very type safe! UnscannedTextBox(_) => {
_ => { fail!("Unscanned text boxes should have been scanned by now.")
fail!("Tried to assign height to unknown Box variant: {:s}",
cur_box.debug_str())
} }
}; };
let mut top_from_base = top_from_base; let mut top_from_base = top_from_base;
@ -753,9 +743,8 @@ impl Flow for InlineFlow {
// It should calculate the distance from baseline to the bottom of parent's content area. // It should calculate the distance from baseline to the bottom of parent's content area.
// But, it is assumed now as 0. // But, it is assumed now as 0.
let parent_text_bottom = Au::new(0); let parent_text_bottom = Au::new(0);
let cur_box_base = cur_box.base();
// Get parent node // Get parent node
let parent = cur_box_base.node.parent_node(); let parent = cur_box.node.parent_node();
let font_size = parent.unwrap().style().Font.font_size; let font_size = parent.unwrap().style().Font.font_size;
parent_text_top = font_size; parent_text_top = font_size;
@ -764,7 +753,7 @@ impl Flow for InlineFlow {
// That is, if the box has top or bottom value, no_update_flag becomes true. // That is, if the box has top or bottom value, no_update_flag becomes true.
let mut no_update_flag = false; let mut no_update_flag = false;
// Calculate a relative offset from baseline. // Calculate a relative offset from baseline.
let offset = match cur_box_base.vertical_align() { let offset = match cur_box.vertical_align() {
vertical_align::baseline => { vertical_align::baseline => {
-ascent -ascent
}, },
@ -819,9 +808,8 @@ impl Flow for InlineFlow {
-(length + ascent) -(length + ascent)
}, },
vertical_align::Percentage(p) => { vertical_align::Percentage(p) => {
let pt_size = cur_box.base().font_style().pt_size; let pt_size = cur_box.font_style().pt_size;
let line_height = cur_box.base() let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size));
.calculate_line_height(Au::from_pt(pt_size));
let percent_offset = line_height.scale_by(p); let percent_offset = line_height.scale_by(p);
-(percent_offset + ascent) -(percent_offset + ascent)
} }
@ -836,7 +824,7 @@ impl Flow for InlineFlow {
bottommost = bottom_from_base; bottommost = bottom_from_base;
} }
cur_box.base().position.mutate().ptr.origin.y = line.bounds.origin.y + offset; cur_box.position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
} }
// Calculate the distance from baseline to the top of the biggest box with 'bottom' value. // Calculate the distance from baseline to the top of the biggest box with 'bottom' value.
@ -859,15 +847,14 @@ impl Flow for InlineFlow {
// All boxes' y position is updated following the new baseline offset. // All boxes' y position is updated following the new baseline offset.
for box_i in line.range.eachi() { for box_i in line.range.eachi() {
let cur_box = self.boxes[box_i]; let cur_box = self.boxes[box_i];
let cur_box_base = cur_box.base(); let adjust_offset = match cur_box.vertical_align() {
let adjust_offset = match cur_box_base.vertical_align() {
vertical_align::top => Au::new(0), vertical_align::top => Au::new(0),
vertical_align::bottom => baseline_offset + bottommost, vertical_align::bottom => baseline_offset + bottommost,
_ => baseline_offset, _ => baseline_offset,
}; };
cur_box_base.position.mutate().ptr.origin.y = cur_box.position.mutate().ptr.origin.y = cur_box.position.get().origin.y +
cur_box_base.position.get().origin.y + adjust_offset; adjust_offset;
} }
// This is used to set the top y position of the next linebox in the next loop. // This is used to set the top y position of the next linebox in the next loop.

View file

@ -4,13 +4,13 @@
//! Text layout. //! Text layout.
use std::vec; use layout::box::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
use layout::context::LayoutContext;
use layout::flow::Flow;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text}; use gfx::text::util::{CompressWhitespaceNewline, transform_text};
use layout::box::{RenderBox, RenderBoxUtils, TextRenderBox, UnscannedTextRenderBoxClass}; use std::vec;
use layout::context::LayoutContext;
use layout::flow::Flow;
use servo_util::range::Range; use servo_util::range::Range;
/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es. /// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
@ -56,27 +56,20 @@ impl TextRunScanner {
flow.as_inline().boxes = out_boxes; flow.as_inline().boxes = out_boxes;
// A helper function. // A helper function.
fn can_coalesce_text_nodes(boxes: &[@RenderBox], left_i: uint, right_i: uint) -> bool { fn can_coalesce_text_nodes(boxes: &[@Box], 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);
boxes[left_i].can_merge_with_box(boxes[right_i])
let (left, right) = (boxes[left_i], boxes[right_i]);
match (left.class(), right.class()) {
(UnscannedTextRenderBoxClass, UnscannedTextRenderBoxClass) => {
left.can_merge_with_box(right)
}
(_, _) => false
}
} }
} }
/// A "clump" is a range of inline flow leaves that can be merged together into a single /// A "clump" is a range of inline flow leaves that can be merged together into a single box.
/// `RenderBox`. Adjacent text with the same style can be merged, and nothing else can. /// Adjacent text with the same style can be merged, and nothing else can.
/// ///
/// The flow keeps track of the `RenderBox`es contained by all non-leaf DOM nodes. This is /// The flow keeps track of the boxes contained by all non-leaf DOM nodes. This is necessary
/// necessary for correct painting order. Since we compress several leaf `RenderBox`es here, /// for correct painting order. Since we compress several leaf boxes here, the mapping must be
/// the mapping must be adjusted. /// adjusted.
/// ///
/// N.B. `in_boxes` is passed by reference, since the old code used a `DVec`. The caller is /// N.B. `in_boxes` is passed by reference, since the old code used a `DVec`. The caller is
/// responsible for swapping out the list. It is not clear to me (pcwalton) that this is still /// responsible for swapping out the list. It is not clear to me (pcwalton) that this is still
@ -85,7 +78,7 @@ impl TextRunScanner {
ctx: &LayoutContext, ctx: &LayoutContext,
flow: &mut Flow, flow: &mut Flow,
last_whitespace: bool, last_whitespace: bool,
out_boxes: &mut ~[@RenderBox]) out_boxes: &mut ~[@Box])
-> bool { -> bool {
let inline = flow.as_inline(); let inline = flow.as_inline();
let in_boxes = &inline.boxes; let in_boxes = &inline.boxes;
@ -95,10 +88,12 @@ impl TextRunScanner {
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 possible_text_clump = in_boxes[self.clump.begin()]; // FIXME(pcwalton): Rust bug let possible_text_clump = in_boxes[self.clump.begin()]; // FIXME(pcwalton): Rust bug
let is_text_clump = possible_text_clump.class() == UnscannedTextRenderBoxClass; let is_text_clump = match possible_text_clump.specific {
UnscannedTextBox(_) => true,
_ => false,
};
let mut new_whitespace = last_whitespace; let mut new_whitespace = last_whitespace;
match (is_singleton, is_text_clump) { match (is_singleton, is_text_clump) {
(false, false) => { (false, false) => {
fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!") fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!")
@ -109,14 +104,20 @@ impl TextRunScanner {
}, },
(true, true) => { (true, true) => {
let old_box = in_boxes[self.clump.begin()]; let old_box = in_boxes[self.clump.begin()];
let text = old_box.as_unscanned_text_render_box().raw_text(); let text = match old_box.specific {
let font_style = old_box.base().font_style(); UnscannedTextBox(ref text_box_info) => &text_box_info.text,
let decoration = old_box.base().text_decoration(); _ => fail!("Expected an unscanned text box!"),
};
let font_style = old_box.font_style();
let decoration = old_box.text_decoration();
// 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.
let compression = CompressWhitespaceNewline; let compression = CompressWhitespaceNewline;
let (transformed_text, whitespace) = transform_text(text, compression, last_whitespace); let (transformed_text, whitespace) = transform_text(*text,
compression,
last_whitespace);
new_whitespace = whitespace; new_whitespace = whitespace;
if transformed_text.len() > 0 { if transformed_text.len() > 0 {
@ -126,11 +127,15 @@ impl TextRunScanner {
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style); let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
let run = @fontgroup.create_textrun(transformed_text, decoration); let run = @fontgroup.create_textrun(transformed_text, decoration);
debug!("TextRunScanner: pushing single text box in range: {} ({})", self.clump, text); debug!("TextRunScanner: pushing single text box in range: {} ({})",
self.clump,
*text);
let range = Range::new(0, run.char_len()); let range = Range::new(0, run.char_len());
let new_box = @TextRenderBox::new((*old_box.base()).clone(), run, range); let new_text_box_info = ScannedTextBoxInfo::new(run, range);
let new_metrics = run.metrics_for_range(&range);
out_boxes.push(new_box as @RenderBox); let new_box = @old_box.transform(new_metrics.bounding_box.size,
ScannedTextBox(new_text_box_info));
out_boxes.push(new_box)
} }
}, },
(false, true) => { (false, true) => {
@ -144,8 +149,12 @@ impl TextRunScanner {
// `transform_text`, so that boxes starting and/or ending with whitespace can // `transform_text`, so that boxes starting and/or ending with whitespace can
// be compressed correctly with respect to the text run. // be compressed correctly with respect to the text run.
let idx = i + self.clump.begin(); let idx = i + self.clump.begin();
let in_box = in_boxes[idx].as_unscanned_text_render_box().raw_text(); let in_box = match in_boxes[idx].specific {
let (new_str, new_whitespace) = transform_text(in_box, UnscannedTextBox(ref text_box_info) => &text_box_info.text,
_ => fail!("Expected an unscanned text box!"),
};
let (new_str, new_whitespace) = transform_text(*in_box,
compression, compression,
last_whitespace_in_clump); last_whitespace_in_clump);
last_whitespace_in_clump = new_whitespace; last_whitespace_in_clump = new_whitespace;
@ -171,9 +180,9 @@ impl TextRunScanner {
// font group fonts. This is probably achieved by creating the font group above // font group fonts. This is probably achieved by creating the font group above
// and then letting `FontGroup` decide which `Font` to stick into the text run. // and then letting `FontGroup` decide which `Font` to stick into the text run.
let in_box = in_boxes[self.clump.begin()]; let in_box = in_boxes[self.clump.begin()];
let font_style = in_box.base().font_style(); let font_style = in_box.font_style();
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style); let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
let decoration = in_box.base().text_decoration(); let decoration = in_box.text_decoration();
// TextRuns contain a cycle which is usually resolved by the teardown // TextRuns contain a cycle which is usually resolved by the teardown
// sequence. If no clump takes ownership, however, it will leak. // sequence. If no clump takes ownership, however, it will leak.
@ -195,10 +204,11 @@ impl TextRunScanner {
continue continue
} }
let new_box = @TextRenderBox::new((*in_boxes[i].base()).clone(), let new_text_box_info = ScannedTextBoxInfo::new(run.unwrap(), range);
run.unwrap(), let new_metrics = new_text_box_info.run.metrics_for_range(&range);
range); let new_box = @in_boxes[i].transform(new_metrics.bounding_box.size,
out_boxes.push(new_box as @RenderBox); ScannedTextBox(new_text_box_info));
out_boxes.push(new_box)
} }
} }
} // End of match. } // End of match.

View file

@ -2,7 +2,7 @@
* 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/. */
use layout::box::{RenderBox, RenderBoxUtils}; use layout::box::Box;
use layout::construct::{ConstructionResult, NoConstructionResult}; use layout::construct::{ConstructionResult, NoConstructionResult};
use extra::arc::Arc; use extra::arc::Arc;
@ -76,7 +76,7 @@ impl ElementMapping {
self.entries.iter().enumerate() self.entries.iter().enumerate()
} }
pub fn repair_for_box_changes(&mut self, old_boxes: &[@RenderBox], new_boxes: &[@RenderBox]) { pub fn repair_for_box_changes(&mut self, old_boxes: &[@Box], new_boxes: &[@Box]) {
let entries = &mut self.entries; let entries = &mut self.entries;
debug!("--- Old boxes: ---"); debug!("--- Old boxes: ---");
@ -118,8 +118,7 @@ impl ElementMapping {
repair_stack.push(item); repair_stack.push(item);
entries_k += 1; entries_k += 1;
} }
while new_j < new_boxes.len() && while new_j < new_boxes.len() && old_boxes[old_i].node != new_boxes[new_j].node {
old_boxes[old_i].base().node != new_boxes[new_j].base().node {
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;
} }