aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-05-07 20:52:38 -0700
committerPatrick Walton <pcwalton@mimiga.net>2013-05-08 15:20:13 -0700
commitb17e68ee048756bf2ca5a43f26204059a5f48f32 (patch)
treebfcfa5ed95b5493ea3c3120b3a6b5f68d31e1a5c /src
parent49edf85f5d98ea44198c24befa02bebada8fdad4 (diff)
downloadservo-b17e68ee048756bf2ca5a43f26204059a5f48f32.tar.gz
servo-b17e68ee048756bf2ca5a43f26204059a5f48f32.zip
layout: Refactor the RenderBox type to save memory and improve the coding style of box.rs
Diffstat (limited to 'src')
-rw-r--r--src/servo/css/node_style.rs4
-rw-r--r--src/servo/layout/block.rs27
-rw-r--r--src/servo/layout/box.rs949
-rw-r--r--src/servo/layout/box_builder.rs68
-rw-r--r--src/servo/layout/display_list_builder.rs17
-rw-r--r--src/servo/layout/flow.rs32
-rw-r--r--src/servo/layout/inline.rs224
-rw-r--r--src/servo/layout/root.rs10
-rw-r--r--src/servo/layout/text.rs48
9 files changed, 801 insertions, 578 deletions
diff --git a/src/servo/css/node_style.rs b/src/servo/css/node_style.rs
index 5dce11b8f34..c6b72a7cb70 100644
--- a/src/servo/css/node_style.rs
+++ b/src/servo/css/node_style.rs
@@ -10,11 +10,11 @@ use newcss::complete::CompleteStyle;
/// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode {
- fn style<'a>(&'a self) -> CompleteStyle<'a>;
+ fn style(&self) -> CompleteStyle;
}
impl StyledNode for AbstractNode {
- fn style<'a>(&'a self) -> CompleteStyle<'a> {
+ fn style(&self) -> CompleteStyle {
assert!(self.is_element()); // Only elements can have styles
let results = self.get_css_select_results();
results.computed_style()
diff --git a/src/servo/layout/block.rs b/src/servo/layout/block.rs
index 8bb3a93429a..1c284c0c891 100644
--- a/src/servo/layout/block.rs
+++ b/src/servo/layout/block.rs
@@ -23,7 +23,7 @@ pub struct BlockFlowData {
common: FlowData,
/// The associated render box.
- box: Option<@mut RenderBox>
+ box: Option<RenderBox>
}
impl BlockFlowData {
@@ -40,7 +40,7 @@ impl BlockFlowData {
/// merged into this.
pub trait BlockLayout {
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 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.
/// 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 {
BlockFlow(*) => {
let box = self.block().box;
- for box.each |b| {
+ for box.each |&b| {
callback(b);
}
},
RootFlow(*) => {
let mut box = self.root().box;
- for box.each |b| {
+ for box.each |&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.
do self.with_block_box |box| {
- box.d().position.size.width = remaining_width;
- let (left_used, right_used) = box.get_used_width();
- remaining_width -= left_used.add(&right_used);
+ do box.with_mut_base |base| {
+ base.position.size.width = remaining_width;
+
+ let (left_used, right_used) = box.get_used_width();
+ remaining_width -= left_used.add(&right_used);
+ }
}
for self.each_child |kid| {
@@ -167,9 +170,11 @@ impl BlockLayout for FlowContext {
let _used_bot = Au(0);
do self.with_block_box |box| {
- box.d().position.origin.y = Au(0);
- box.d().position.size.height = cur_y;
- let (_used_top, _used_bot) = box.get_used_height();
+ do box.with_mut_base |base| {
+ base.position.origin.y = Au(0);
+ base.position.size.height = cur_y;
+ let (_used_top, _used_bot) = box.get_used_height();
+ }
}
}
diff --git a/src/servo/layout/box.rs b/src/servo/layout/box.rs
index b6b46f7f5d3..a0e8b9f0cad 100644
--- a/src/servo/layout/box.rs
+++ b/src/servo/layout/box.rs
@@ -2,84 +2,114 @@
* 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/. */
-/* Fundamental layout structures and algorithms. */
+//! The `RenderBox` type, which represents the leaves of the layout tree.
use css::node_style::StyledNode;
use dom::node::AbstractNode;
use layout::context::LayoutContext;
-use layout::debug::BoxedMutDebugMethods;
-use layout::display_list_builder::DisplayListBuilder;
+use layout::debug::DebugMethods;
+use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
use layout::flow::FlowContext;
use layout::text::TextBoxData;
-use layout;
-use newcss::color::{Color, rgb};
-use newcss::complete::CompleteStyle;
-use newcss::units::{Cursive, Em, Fantasy, Length, Monospace, Pt, Px, SansSerif, Serif};
-use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium};
-use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
-use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
-use newcss::values::{CSSFontStyleOblique, CSSTextAlign};
+use layout::text;
-use core::managed;
use core::cell::Cell;
+use core::managed;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::{DisplayItem, DisplayList};
use gfx::font::{FontStyle, FontWeight300};
use gfx::geometry::Au;
use gfx::image::holder::ImageHolder;
-use servo_util::range::*;
+use gfx::resource::local_image_cache::LocalImageCache;
use gfx;
+use newcss::color::{Color, rgb};
+use newcss::complete::CompleteStyle;
+use newcss::units::{Cursive, Em, Fantasy, Length, Monospace, Pt, Px, SansSerif, Serif};
+use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium};
+use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
+use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
+use newcss::values::{CSSFontStyleOblique, CSSTextAlign};
+use servo_util::range::*;
use std::arc;
+use std::cmp::FuzzyEq;
+use std::net::url::Url;
+
+/// Render boxes (`struct RenderBox`) are the leaves of the layout tree. They cannot position
+/// themselves. In general, render boxes do not have a simple correspondence with CSS boxes as in
+/// the specification:
+///
+/// * Several render boxes may correspond to the same CSS box or DOM node. For example, a CSS text
+/// box broken across two lines is represented by two render boxes.
+///
+/// * Some CSS boxes are not created at all, such as some anonymous block boxes induced by inline
+/// boxes with block-level sibling boxes. In that case, Servo uses an `InlineFlow` with
+/// `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is
+/// positioned as if it were a block box, but its children are positioned according to inline
+/// flow.
+///
+/// A `GenericBox` is an empty box that contributes only borders, margins, padding, and
+/// backgrounds. It is analogous to a CSS nonreplaced content box.
+///
+/// A box's type influences how its styles are interpreted during layout. For example, replaced
+/// content such as images are resized differently from tables, text, or other content. Different
+/// types of boxes may also contain custom data; for example, text boxes contain text.
+pub enum RenderBox {
+ GenericRenderBoxClass(@mut RenderBoxBase),
+ ImageRenderBoxClass(@mut ImageRenderBox),
+ TextRenderBoxClass(@mut TextRenderBox),
+ UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox),
+}
-/**
-Render boxes (`struct RenderBox`) are the leafs of the layout
-tree. They cannot position themselves. In general, render boxes do not
-have a simple correspondence with CSS boxes as in the specification:
-
- * Several render boxes may correspond to the same CSS box or DOM
- node. For example, a CSS text box broken across two lines is
- represented by two render boxes.
-
- * Some CSS boxes are not created at all, such as some anonymous block
- boxes induced by inline boxes with block-level sibling boxes. In
- that case, Servo uses an InlineFlow with BlockFlow siblings; the
- InlineFlow is block-level, but not a block container. It is
- positioned as if it were a block box, but its children are
- positioned according to inline flow.
-
-Fundamental box types include:
+/// A box that represents a (replaced content) image and its accompanying borders, shadows, etc.
+pub struct ImageRenderBox {
+ base: RenderBoxBase,
+ image: ImageHolder,
+}
- * GenericBox: an empty box that contributes only borders, margins,
-padding, backgrounds. It is analogous to a CSS nonreplaced content box.
+impl ImageRenderBox {
+ pub fn new(base: RenderBoxBase, image_url: Url, local_image_cache: @mut LocalImageCache)
+ -> ImageRenderBox {
+ assert!(base.node.is_image_element());
- * ImageBox: a box that represents a (replaced content) image and its
- accompanying borders, shadows, etc.
+ ImageRenderBox {
+ base: base,
+ image: ImageHolder::new(image_url, local_image_cache),
+ }
+ }
+}
- * TextBox: a box representing a single run of text with a distinct
- style. A TextBox may be split into two or more render boxes across
- line breaks. Several TextBoxes may correspond to a single DOM text
- node. Split text boxes are implemented by referring to subsets of a
- master TextRun object.
+/// A box representing a single run of text with a distinct style. A `TextRenderBox` may be split
+/// into two or more render boxes across line breaks. Several `TextBox`es may correspond to a
+/// single DOM text node. Split text boxes are implemented by referring to subsets of a master
+/// `TextRun` object.
+pub struct TextRenderBox {
+ base: RenderBoxBase,
-*/
+ // TODO: Flatten `TextBoxData` into this type.
+ text_data: TextBoxData,
+}
-/* A box's kind influences how its styles are interpreted during
- layout. For example, replaced content such as images are resized
- differently than tables, text, or other content.
+/// The data for an unscanned text box.
+pub struct UnscannedTextRenderBox {
+ base: RenderBoxBase,
+ text: ~str,
+}
- It also holds data specific to different box types, such as text.
-*/
-pub struct RenderBoxData {
- /* originating DOM node */
- node: AbstractNode,
- /* reference to containing flow context, which this box
- participates in */
- ctx: FlowContext,
- /* position of this box relative to owning flow */
- position: Rect<Au>,
- font_size: Length,
- /* TODO (Issue #87): debug only */
- id: int
+impl UnscannedTextRenderBox {
+ /// Creates a new instance of `UnscannedTextRenderBox`.
+ pub fn new(base: RenderBoxBase) -> UnscannedTextRenderBox {
+ assert!(base.node.is_text());
+
+ do base.node.with_imm_text |text_node| {
+ // FIXME: Don't copy text; atomically reference count it instead.
+ // FIXME(pcwalton): If we're just looking at node data, do we have to ensure this is
+ // a text node?
+ UnscannedTextRenderBox {
+ base: base,
+ text: text_node.parent.data.to_str(),
+ }
+ }
+ }
}
pub enum RenderBoxType {
@@ -88,102 +118,186 @@ pub enum RenderBoxType {
RenderBox_Text,
}
-pub enum RenderBox {
- GenericBox(RenderBoxData),
- ImageBox(RenderBoxData, ImageHolder),
- TextBox(RenderBoxData, TextBoxData),
- UnscannedTextBox(RenderBoxData, ~str)
-}
-
+/// Represents the outcome of attempting to split a render box.
pub enum SplitBoxResult {
- CannotSplit(@mut RenderBox),
+ CannotSplit(RenderBox),
// in general, when splitting the left or right side can
// be zero length, due to leading/trailing trimmable whitespace
- SplitDidFit(Option<@mut RenderBox>, Option<@mut RenderBox>),
- SplitDidNotFit(Option<@mut RenderBox>, Option<@mut RenderBox>)
+ SplitDidFit(Option<RenderBox>, Option<RenderBox>),
+ SplitDidNotFit(Option<RenderBox>, Option<RenderBox>)
+}
+
+/// Data common to all render boxes.
+pub struct RenderBoxBase {
+ /// The DOM node that this `RenderBox` originates from.
+ node: AbstractNode,
+
+ /// The reference to the containing flow context which this box participates in.
+ ctx: FlowContext,
+
+ /// The position of this box relative to its owning flow.
+ position: Rect<Au>,
+
+ /// The font size.
+ ///
+ /// FIXME(pcwalton): Why is this present on non-text-boxes?
+ font_size: Length,
+
+ /// A debug ID.
+ ///
+ /// TODO(#87) Make this only present in debug builds.
+ id: int
}
-pub fn RenderBoxData(node: AbstractNode, ctx: FlowContext, id: int) -> RenderBoxData {
- RenderBoxData {
- node : node,
- ctx : ctx,
- position : Au::zero_rect(),
- font_size: Px(0.0),
- id : id
+impl RenderBoxBase {
+ /// Constructs a new `RenderBoxBase` instance.
+ pub fn new(node: AbstractNode, flow_context: FlowContext, id: int) -> RenderBoxBase {
+ RenderBoxBase {
+ node: node,
+ ctx: flow_context,
+ position: Au::zero_rect(),
+ font_size: Px(0.0),
+ id: id,
+ }
}
}
-impl<'self> RenderBox {
- fn d(&'self mut self) -> &'self mut RenderBoxData {
- unsafe {
- //Rust #5074 - we can't take mutable references to the
- // data that needs to be returned right now.
- match self {
- &GenericBox(ref d) => cast::transmute(d),
- &ImageBox(ref d, _) => cast::transmute(d),
- &TextBox(ref d, _) => cast::transmute(d),
- &UnscannedTextBox(ref d, _) => cast::transmute(d),
+pub impl RenderBox {
+ /// Borrows this render box immutably in order to work with its common data.
+ #[inline(always)]
+ fn with_imm_base<R>(&self, callback: &fn(&RenderBoxBase) -> R) -> R {
+ match *self {
+ GenericRenderBoxClass(generic_box) => callback(generic_box),
+ ImageRenderBoxClass(image_box) => {
+ let image_box = &*image_box; // FIXME: Borrow check workaround.
+ callback(&image_box.base)
+ }
+ TextRenderBoxClass(text_box) => {
+ let text_box = &*text_box; // FIXME: Borrow check workaround.
+ callback(&text_box.base)
+ }
+ UnscannedTextRenderBoxClass(unscanned_text_box) => {
+ let unscanned_text_box = &*unscanned_text_box; // FIXME: Borrow check workaround.
+ callback(&unscanned_text_box.base)
+ }
+ }
+ }
+
+ /// Borrows this render box mutably in order to work with its common data.
+ #[inline(always)]
+ fn with_mut_base<R>(&self, callback: &fn(&mut RenderBoxBase) -> R) -> R {
+ match *self {
+ GenericRenderBoxClass(generic_box) => callback(generic_box),
+ ImageRenderBoxClass(image_box) => {
+ let image_box = &mut *image_box; // FIXME: Borrow check workaround.
+ callback(&mut image_box.base)
+ }
+ TextRenderBoxClass(text_box) => {
+ let text_box = &mut *text_box; // FIXME: Borrow check workaround.
+ callback(&mut text_box.base)
+ }
+ UnscannedTextRenderBoxClass(unscanned_text_box) => {
+ // FIXME: Borrow check workaround.
+ let unscanned_text_box = &mut *unscanned_text_box;
+ callback(&mut unscanned_text_box.base)
+ }
+ }
+ }
+
+ /// A convenience function to return the position of this box.
+ fn position(&self) -> Rect<Au> {
+ do self.with_imm_base |base| {
+ base.position
+ }
+ }
+
+ /// A convenience function to return the debugging ID of this box.
+ fn id(&self) -> int {
+ do self.with_mut_base |base| {
+ base.id
}
- }
}
- fn is_replaced(self) -> bool {
- match self {
- ImageBox(*) => true, // TODO: form elements, etc
+ /// Returns true if this element is replaced content. This is true for images, form elements,
+ /// and so on.
+ fn is_replaced(&self) -> bool {
+ match *self {
+ ImageRenderBoxClass(*) => true,
_ => false
}
}
+ /// Returns true if this element can be split. This is true for text boxes.
fn can_split(&self) -> bool {
match *self {
- TextBox(*) => true,
+ TextRenderBoxClass(*) => true,
_ => false
}
}
+ /// Returns true if this element is an unscanned text box that consists entirely of whitespace.
fn is_whitespace_only(&self) -> bool {
match *self {
- UnscannedTextBox(_, ref raw_text) => raw_text.is_whitespace(),
+ UnscannedTextRenderBoxClass(unscanned_text_box) => {
+ let mut unscanned_text_box = &mut *unscanned_text_box; // FIXME: Borrow check.
+ unscanned_text_box.text.is_whitespace()
+ }
_ => false
}
}
- fn can_merge_with_box(@mut self, other: @mut RenderBox) -> bool {
- assert!(!managed::mut_ptr_eq(self, other));
-
- match (self, other) {
- (@UnscannedTextBox(*), @UnscannedTextBox(*)) => {
+ /// Determines whether this box can merge with another render box.
+ fn can_merge_with_box(&self, other: RenderBox) -> bool {
+ match (self, &other) {
+ (&UnscannedTextRenderBoxClass(*), &UnscannedTextRenderBoxClass(*)) => {
self.font_style() == other.font_style()
},
- (@TextBox(_, d1), @TextBox(_, d2)) => managed::ptr_eq(d1.run, d2.run),
- (_, _) => false
+ (&TextRenderBoxClass(text_box_a), &TextRenderBoxClass(text_box_b)) => {
+ managed::ptr_eq(text_box_a.text_data.run, text_box_b.text_data.run)
+ }
+ (_, _) => false,
}
}
- fn split_to_width(@mut self, _ctx: &LayoutContext, max_width: Au, starts_line: bool) -> SplitBoxResult {
- match self {
- @GenericBox(*) => CannotSplit(self),
- @ImageBox(*) => CannotSplit(self),
- @UnscannedTextBox(*) => fail!(~"WAT: shouldn't be an unscanned text box here."),
- @TextBox(_,data) => {
-
- let mut pieces_processed_count : uint = 0;
- let mut remaining_width : Au = max_width;
- let mut left_range = Range::new(data.range.begin(), 0);
- let mut right_range : Option<Range> = None;
+ /// Attempts to split this box so that its width is no more than `max_width`. Fails if this box
+ /// is an unscanned text box.
+ fn split_to_width(&self, _: &LayoutContext, max_width: Au, starts_line: bool)
+ -> SplitBoxResult {
+ match *self {
+ GenericRenderBoxClass(*) | ImageRenderBoxClass(*) => CannotSplit(*self),
+ UnscannedTextRenderBoxClass(*) => {
+ fail!(~"WAT: shouldn't be an unscanned text box here.")
+ }
+
+ TextRenderBoxClass(text_box) => {
+ let text_box = &mut *text_box; // FIXME: Borrow check.
+
+ let mut pieces_processed_count: uint = 0;
+ let mut remaining_width: Au = max_width;
+ let mut left_range = Range::new(text_box.text_data.range.begin(), 0);
+ let mut right_range: Option<Range> = None;
+
debug!("split_to_width: splitting text box (strlen=%u, range=%?, avail_width=%?)",
- data.run.text.len(), data.range, max_width);
- do data.run.iter_indivisible_pieces_for_range(&data.range) |piece_range| {
+ text_box.text_data.run.text.len(),
+ text_box.text_data.range,
+ max_width);
+
+ for text_box.text_data.run.iter_indivisible_pieces_for_range(
+ &text_box.text_data.range) |piece_range| {
debug!("split_to_width: considering piece (range=%?, remain_width=%?)",
- piece_range, remaining_width);
- let metrics = data.run.metrics_for_range(piece_range);
+ piece_range,
+ remaining_width);
+
+ let metrics = text_box.text_data.run.metrics_for_range(piece_range);
let advance = metrics.advance_width;
- let should_continue : bool;
+ let should_continue: bool;
if advance <= remaining_width {
should_continue = true;
- if starts_line && pieces_processed_count == 0
- && data.run.range_is_trimmable_whitespace(piece_range) {
+
+ if starts_line &&
+ pieces_processed_count == 0 &&
+ text_box.text_data.run.range_is_trimmable_whitespace(piece_range) {
debug!("split_to_width: case=skipping leading trimmable whitespace");
left_range.shift_by(piece_range.length() as int);
} else {
@@ -191,295 +305,369 @@ impl<'self> RenderBox {
remaining_width -= advance;
left_range.extend_by(piece_range.length() as int);
}
- } else { /* advance > remaining_width */
+ } else { // The advance is more than the remaining width.
should_continue = false;
- if data.run.range_is_trimmable_whitespace(piece_range) {
- // if there are still things after the trimmable whitespace, create right chunk
- if piece_range.end() < data.range.end() {
- debug!("split_to_width: case=skipping trimmable trailing whitespace, then split remainder");
- right_range = Some(Range::new(piece_range.end(),
- data.range.end() - piece_range.end()));
+ if text_box.text_data.run.range_is_trimmable_whitespace(piece_range) {
+ // If there are still things after the trimmable whitespace, create the
+ // right chunk.
+ if piece_range.end() < text_box.text_data.range.end() {
+ debug!("split_to_width: case=skipping trimmable trailing \
+ whitespace, then split remainder");
+ let right_range_end =
+ text_box.text_data.range.end() - piece_range.end();
+ right_range = Some(Range::new(piece_range.end(), right_range_end));
} else {
- debug!("split_to_width: case=skipping trimmable trailing whitespace");
+ debug!("split_to_width: case=skipping trimmable trailing \
+ whitespace");
}
- } else if piece_range.begin() < data.range.end() {
- // still things left, create right chunk
- right_range = Some(Range::new(piece_range.begin(),
- data.range.end() - piece_range.begin()));
+ } else if piece_range.begin() < text_box.text_data.range.end() {
+ // There are still some things left over at the end of the line. Create
+ // the right chunk.
+ let right_range_end =
+ text_box.text_data.range.end() - piece_range.begin();
+ right_range = Some(Range::new(piece_range.begin(), right_range_end));
debug!("split_to_width: case=splitting remainder with right range=%?",
right_range);
}
}
+
pieces_processed_count += 1;
- should_continue
+
+ if !should_continue {
+ break
+ }
}
let left_box = if left_range.length() > 0 {
- Some(layout::text::adapt_textbox_with_range(self.d(), data.run, &left_range))
- } else { None };
-
- let right_box = right_range.map_default(None, |range: &Range| {
- Some(layout::text::adapt_textbox_with_range(self.d(), data.run, range))
- });
+ let new_text_box = @mut text::adapt_textbox_with_range(text_box.base,
+ text_box.text_data.run,
+ left_range);
+ Some(TextRenderBoxClass(new_text_box))
+ } else {
+ None
+ };
+
+ let right_box = do right_range.map_default(None) |range: &Range| {
+ let new_text_box = @mut text::adapt_textbox_with_range(text_box.base,
+ text_box.text_data.run,
+ *range);
+ Some(TextRenderBoxClass(new_text_box))
+ };
- return if pieces_processed_count == 1 || left_box.is_none() {
+ if pieces_processed_count == 1 || left_box.is_none() {
SplitDidNotFit(left_box, right_box)
} else {
SplitDidFit(left_box, right_box)
}
- },
+ }
}
}
- /** In general, these functions are transitively impure because they
- * may cause glyphs to be allocated. For now, it's impure because of
- * holder.get_image()
- */
- fn get_min_width(&mut self, _ctx: &LayoutContext) -> Au {
+ /// Returns the *minimum width* of this render box as defined by the CSS specification.
+ fn get_min_width(&self, _: &LayoutContext) -> Au {
match *self {
- // TODO: this should account for min/pref widths of the
- // box element in isolation. That includes
- // border/margin/padding but not child widths. The block
- // FlowContext will combine the width of this element and
- // that of its children to arrive at the context width.
- GenericBox(*) => Au(0),
- // TODO: consult CSS 'width', margin, border.
- // TODO: If image isn't available, consult 'width'.
- ImageBox(_, ref mut i) => Au::from_px(i.get_size().get_or_default(Size2D(0,0)).width),
- TextBox(_,d) => d.run.min_width_for_range(&d.range),
- UnscannedTextBox(*) => fail!(~"Shouldn't see unscanned boxes here.")
+ // TODO: This should account for the minimum width of the box element in isolation.
+ // That includes borders, margins, and padding, but not child widths. The block
+ // `FlowContext` will combine the width of this element and that of its children to
+ // arrive at the context width.
+ GenericRenderBoxClass(*) => Au(0),
+
+ ImageRenderBoxClass(image_box) => {
+ // TODO: Consult the CSS `width` property as well as margins and borders.
+ // TODO: If the image isn't available, consult `width`.
+ Au::from_px(image_box.image.get_size().get_or_default(Size2D(0, 0)).width)
+ }
+
+ TextRenderBoxClass(text_box) => {
+ let mut text_box = &mut *text_box; // FIXME: Borrow check.
+ text_box.text_data.run.min_width_for_range(&text_box.text_data.range)
+ }
+
+ UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here.")
}
}
- fn get_pref_width(&mut self, _ctx: &LayoutContext) -> Au {
- match self {
- // TODO: this should account for min/pref widths of the
- // box element in isolation. That includes
- // border/margin/padding but not child widths. The block
- // FlowContext will combine the width of this element and
- // that of its children to arrive at the context width.
- &GenericBox(*) => Au(0),
- &ImageBox(_, ref mut i) => Au::from_px(i.get_size().get_or_default(Size2D(0,0)).width),
-
- // a text box cannot span lines, so assume that this is an unsplit text box.
-
- // TODO: If text boxes have been split to wrap lines, then
- // they could report a smaller pref width during incremental reflow.
- // maybe text boxes should report nothing, and the parent flow could
- // factor in min/pref widths of any text runs that it owns.
- &TextBox(_,d) => {
- let mut max_line_width: Au = Au(0);
- for d.run.iter_natural_lines_for_range(&d.range) |line_range| {
+ /// Returns the *preferred width* of this render box as defined by the CSS specification.
+ fn get_pref_width(&self, _: &LayoutContext) -> Au {
+ match *self {
+ // TODO: This should account for the preferred width of the box element in isolation.
+ // That includes borders, margins, and padding, but not child widths. The block
+ // `FlowContext` will combine the width of this element and that of its children to
+ // arrive at the context width.
+ GenericRenderBoxClass(*) => Au(0),
+
+ ImageRenderBoxClass(image_box) => {
+ Au::from_px(image_box.image.get_size().get_or_default(Size2D(0, 0)).width)
+ }
+
+ TextRenderBoxClass(text_box) => {
+ let mut text_box = &mut *text_box; // FIXME: Borrow check bug.
+
+ // A text box cannot span lines, so assume that this is an unsplit text box.
+ //
+ // TODO: If text boxes have been split to wrap lines, then they could report a
+ // smaller preferred width during incremental reflow. Maybe text boxes should
+ // report nothing and the parent flow can factor in minimum/preferred widths of any
+ // text runs that it owns.
+ let mut max_line_width = Au(0);
+ for text_box.text_data.run.iter_natural_lines_for_range(&text_box.text_data.range)
+ |line_range| {
let mut line_width: Au = Au(0);
- for d.run.glyphs.iter_glyphs_for_char_range(line_range) |_char_i, glyph| {
+ for text_box.text_data.run.glyphs.iter_glyphs_for_char_range(line_range)
+ |_, glyph| {
line_width += glyph.advance()
}
+
max_line_width = Au::max(max_line_width, line_width);
}
max_line_width
- },
- &UnscannedTextBox(*) => fail!(~"Shouldn't see unscanned boxes here.")
+ }
+
+ UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here."),
}
}
- /* Returns the amount of left, right "fringe" used by this
- box. This should be based on margin, border, padding, width. */
+ /// Returns the amount of left and right "fringe" used by this box. This is based on margins,
+ /// borders, padding, and width.
fn get_used_width(&self) -> (Au, Au) {
- // TODO: this should actually do some computation!
- // See CSS 2.1, Section 10.3, 10.4.
-
+ // TODO: This should actually do some computation! See CSS 2.1, Sections 10.3 and 10.4.
(Au(0), Au(0))
}
- /* Returns the amount of left, right "fringe" used by this
- box. This should be based on margin, border, padding, width. */
+ /// Returns the amount of left and right "fringe" used by this box. This should be based on
+ /// margins, borders, padding, and width.
fn get_used_height(&self) -> (Au, Au) {
- // TODO: this should actually do some computation!
- // See CSS 2.1, Section 10.5, 10.6.
-
+ // TODO: This should actually do some computation! See CSS 2.1, Sections 10.5 and 10.6.
(Au(0), Au(0))
}
- /* The box formed by the content edge, as defined in CSS 2.1 Section 8.1.
- Coordinates are relative to the owning flow. */
- fn content_box(&mut self) -> Rect<Au> {
- let origin = {copy self.d().position.origin};
- match self {
- &ImageBox(_, ref mut i) => {
- let size = i.size();
+ /// The box formed by the content edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
+ /// the owning flow.
+ fn content_box(&self) -> Rect<Au> {
+ let origin = self.position().origin;
+ match *self {
+ ImageRenderBoxClass(image_box) => {
Rect {
origin: origin,
- size: Size2D(Au::from_px(size.width),
- Au::from_px(size.height))
+ size: image_box.base.position.size,
}
},
- &GenericBox(*) => {
- copy self.d().position
- /* FIXME: The following hits an ICE for whatever reason
+ GenericRenderBoxClass(*) => {
+ self.position()
+
+ // FIXME: The following hits an ICE for whatever reason.
+ /*
let origin = self.d().position.origin;
- let size = self.d().position.size;
+ let size = self.d().position.size;
let (offset_left, offset_right) = self.get_used_width();
let (offset_top, offset_bottom) = self.get_used_height();
Rect {
origin: Point2D(origin.x + offset_left, origin.y + offset_top),
- size: Size2D(size.width - (offset_left + offset_right),
- size.height - (offset_top + offset_bottom))
- }*/
- },
- &TextBox(*) => {
- copy self.d().position
+ size: Size2D(size.width - (offset_left + offset_right),
+ size.height - (offset_top + offset_bottom))
+ }
+ */
},
- &UnscannedTextBox(*) => fail!(~"Shouldn't see unscanned boxes here.")
+ TextRenderBoxClass(*) => self.position(),
+ UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here.")
}
}
- /* The box formed by the border edge, as defined in CSS 2.1 Section 8.1.
- Coordinates are relative to the owning flow. */
- fn border_box(&mut self) -> Rect<Au> {
- // TODO: actually compute content_box + padding + border
+ /// The box formed by the border edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
+ /// the owning flow.
+ fn border_box(&self) -> Rect<Au> {
+ // TODO: Actually compute the content box, padding, and border.
self.content_box()
}
- /* The box fromed by the margin edge, as defined in CSS 2.1 Section 8.1.
- Coordinates are relative to the owning flow. */
- fn margin_box(&mut self) -> Rect<Au> {
- // TODO: actually compute content_box + padding + border + margin
+ /// The box formed by the margin edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
+ /// the owning flow.
+ fn margin_box(&self) -> Rect<Au> {
+ // TODO: Actually compute the content_box, padding, border, and margin.
self.content_box()
}
- fn style(&'self mut self) -> CompleteStyle<'self> {
- let d: &'self mut RenderBoxData = self.d();
- d.node.style()
- }
-
- fn with_style_of_nearest_element<R>(@mut self, f: &fn(CompleteStyle) -> R) -> R {
- let mut node = self.d().node;
- while !node.is_element() {
- node = node.parent_node().get();
+ /// A convenience function to determine whether this render box represents a DOM element.
+ fn is_element(&self) -> bool {
+ do self.with_imm_base |base| {
+ base.node.is_element()
}
- f(node.style())
}
- // TODO: to implement stacking contexts correctly, we need to
- // create a set of display lists, one per each layer of a stacking
- // context. (CSS 2.1, Section 9.9.1). Each box is passed the list
- // set representing the box's stacking context. When asked to
- // construct its constituent display items, each box puts its
- // DisplayItems into the correct stack layer (according to CSS 2.1
- // Appendix E). and then builder flattens the list at the end.
-
- /* Methods for building a display list. This is a good candidate
- for a function pointer as the number of boxes explodes.
-
- # Arguments
+ /// A convenience function to access the computed style of the DOM node that this render box
+ /// represents.
+ fn style(&self) -> CompleteStyle {
+ self.with_imm_base(|base| base.node.style())
+ }
- * `builder` - the display list builder which manages the coordinate system and options.
- * `dirty` - Dirty rectangle, in the coordinate system of the owning flow (self.ctx)
- * `origin` - Total offset from display list root flow to this box's owning flow
- * `list` - List to which items should be appended
- */
- fn build_display_list(@mut self, _builder: &DisplayListBuilder, dirty: &Rect<Au>,
- offset: &Point2D<Au>, list: &Cell<DisplayList>) {
+ /// A convenience function to access the DOM node that this render box represents.
+ fn node(&self) -> AbstractNode {
+ self.with_imm_base(|base| base.node)
+ }
- let box_bounds = self.d().position;
+ /// Returns the nearest ancestor-or-self `Element` to the DOM node that this render box
+ /// represents.
+ ///
+ /// If there is no ancestor-or-self `Element` node, fails.
+ fn nearest_ancestor_element(&self) -> AbstractNode {
+ do self.with_imm_base |base| {
+ let mut node = base.node;
+ while !node.is_element() {
+ match node.parent_node() {
+ None => fail!(~"no nearest element?!"),
+ Some(parent) => node = parent,
+ }
+ }
+ node
+ }
+ }
- let abs_box_bounds = box_bounds.translate(offset);
+ //
+ // Painting
+ //
+
+ /// Adds the display items for this render box to the given display list.
+ ///
+ /// Arguments:
+ /// * `builder`: The display list builder, which manages the coordinate system and options.
+ /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
+ /// * `origin`: The total offset from the display list root flow to the owning flow of this
+ /// box.
+ /// * `list`: The display list to which items should be appended.
+ ///
+ /// TODO: To implement stacking contexts correctly, we need to create a set of display lists,
+ /// one per layer of the stacking context (CSS 2.1 § 9.9.1). Each box is passed the list set
+ /// representing the box's stacking context. When asked to construct its constituent display
+ /// items, each box puts its display items into the correct stack layer according to CSS 2.1
+ /// Appendix E. Finally, the builder flattens the list.
+ fn build_display_list(&self,
+ _: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ offset: &Point2D<Au>,
+ list: &Cell<DisplayList>) {
+ let box_bounds = self.position();
+ let absolute_box_bounds = box_bounds.translate(offset);
debug!("RenderBox::build_display_list at rel=%?, abs=%?: %s",
- box_bounds, abs_box_bounds, self.debug_str());
+ box_bounds, absolute_box_bounds, self.debug_str());
debug!("RenderBox::build_display_list: dirty=%?, offset=%?", dirty, offset);
- if abs_box_bounds.intersects(dirty) {
+
+ if absolute_box_bounds.intersects(dirty) {
debug!("RenderBox::build_display_list: intersected. Adding display item...");
} else {
debug!("RenderBox::build_display_list: Did not intersect...");
return;
}
- self.add_bgcolor_to_list(list, &abs_box_bounds);
+ // Add the background to the list, if applicable.
+ self.paint_background_if_applicable(list, &absolute_box_bounds);
+
+ match *self {
+ UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here."),
+ TextRenderBoxClass(text_box) => {
+ let text_box = &mut *text_box; // FIXME: Borrow check bug.
- let m = &mut *self;
- match m {
- &UnscannedTextBox(*) => fail!(~"Shouldn't see unscanned boxes here."),
- &TextBox(_,data) => {
let nearest_ancestor_element = self.nearest_ancestor_element();
let color = nearest_ancestor_element.style().color().to_gfx_color();
- let mut l = list.take(); // FIXME: this should use with_mut_ref when that appears
- l.append_item(~DisplayItem::new_Text(&abs_box_bounds,
- ~data.run.serialize(),
- data.range,
- color));
- list.put_back(l);
-
- // debug frames for text box bounds
+
+ // FIXME: This should use `with_mut_ref` when that appears.
+ let mut this_list = list.take();
+ this_list.append_item(~DisplayItem::new_Text(&absolute_box_bounds,
+ ~text_box.text_data.run.serialize(),
+ text_box.text_data.range,
+ color));
+ list.put_back(this_list);
+
+ // Draw debug frames for text bounds.
+ //
+ // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
+ // should have a real `SERVO_DEBUG` system.
debug!("%?", {
- // text box bounds
- let mut l = list.take(); // FIXME: use with_mut_ref when that appears
- l.append_item(~DisplayItem::new_Border(&abs_box_bounds,
- Au::from_px(1),
- rgb(0, 0, 200).to_gfx_color()));
- // baseline "rect"
- // TODO(Issue #221): create and use a Line display item for baseline.
- let ascent = data.run.metrics_for_range(&data.range).ascent;
- let baseline = Rect(abs_box_bounds.origin + Point2D(Au(0),ascent),
- Size2D(abs_box_bounds.size.width, Au(0)));
-
- l.append_item(~DisplayItem::new_Border(&baseline,
- Au::from_px(1),
- rgb(0, 200, 0).to_gfx_color()));
- list.put_back(l);
- ; ()});
+ // Compute the text box bounds.
+ //
+ // FIXME: This should use `with_mut_ref` when that appears.
+ let mut this_list = list.take();
+ this_list.append_item(~DisplayItem::new_Border(&absolute_box_bounds,
+ Au::from_px(1),
+ rgb(0, 0, 200).to_gfx_color()));
+
+ // Draw a rectangle representing the baselines.
+ //
+ // TODO(Issue #221): Create and use a Line display item for the baseline.
+ let ascent = text_box.text_data.run.metrics_for_range(
+ &text_box.text_data.range).ascent;
+ let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
+ Size2D(absolute_box_bounds.size.width, Au(0)));
+
+ this_list.append_item(~DisplayItem::new_Border(&baseline,
+ Au::from_px(1),
+ rgb(0, 200, 0).to_gfx_color()));
+ list.put_back(this_list);
+ ()
+ });
},
- // TODO: items for background, border, outline
- &GenericBox(_) => {}
- &ImageBox(_, ref mut i) => {
- //let i: &mut ImageHolder = unsafe { cast::transmute(i) }; // Rust #5074
- match i.get_image() {
+
+ GenericRenderBoxClass(_) => {}
+
+ ImageRenderBoxClass(image_box) => {
+ match image_box.image.get_image() {
Some(image) => {
debug!("(building display list) building image box");
- let mut l = list.take(); // FIXME: use with_mut_ref when available
- l.append_item(~DisplayItem::new_Image(&abs_box_bounds,
- arc::clone(&image)));
- list.put_back(l);
+
+ // FIXME: This should use `with_mut_ref` when that appears.
+ let mut this_list = list.take();
+ this_list.append_item(~DisplayItem::new_Image(&absolute_box_bounds,
+ arc::clone(&image)));
+ list.put_back(this_list);
}
None => {
- /* No image data at all? Okay, add some fallback content instead. */
+ // No image data at all? Do nothing.
+ //
+ // TODO: Add some kind of placeholder image.
debug!("(building display list) no image :(");
}
}
}
}
- self.add_border_to_list(list, &abs_box_bounds);
+ // Add a border, if applicable.
+ //
+ // TODO: Outlines.
+ self.paint_borders_if_applicable(list, &absolute_box_bounds);
}
- fn add_bgcolor_to_list(&mut self, list: &Cell<DisplayList>, abs_bounds: &Rect<Au>) {
- use std::cmp::FuzzyEq;
-
+ /// Adds the display items necessary to paint the background of this render box to the display
+ /// list if necessary.
+ fn paint_background_if_applicable(&self,
+ list: &Cell<DisplayList>,
+ absolute_bounds: &Rect<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems
// inefficient. What we really want is something like "nearest ancestor element that
- // doesn't have a RenderBox".
+ // doesn't have a render box".
let nearest_ancestor_element = self.nearest_ancestor_element();
let bgcolor = nearest_ancestor_element.style().background_color();
if !bgcolor.alpha.fuzzy_eq(&0.0) {
let mut l = list.take(); // FIXME: use with_mut_ref when available
- l.append_item(~DisplayItem::new_SolidColor(abs_bounds, bgcolor.to_gfx_color()));
+ l.append_item(~DisplayItem::new_SolidColor(absolute_bounds, bgcolor.to_gfx_color()));
list.put_back(l);
}
}
- fn add_border_to_list(&mut self, list: &Cell<DisplayList>, abs_bounds: &Rect<Au>) {
- if !self.d().node.is_element() { return }
-
- let top_width = self.style().border_top_width();
- let right_width = self.style().border_right_width();
- let bottom_width = self.style().border_bottom_width();
- let left_width = self.style().border_left_width();
+ /// Adds the display items necessary to paint the borders of this render box to the display
+ /// list if necessary.
+ fn paint_borders_if_applicable(&self, list: &Cell<DisplayList>, abs_bounds: &Rect<Au>) {
+ if !self.is_element() {
+ return
+ }
+ let style = self.style();
+ let (top_width, right_width) = (style.border_top_width(), style.border_right_width());
+ let (bottom_width, left_width) = (style.border_bottom_width(), style.border_left_width());
match (top_width, right_width, bottom_width, left_width) {
(CSSBorderWidthLength(Px(top)),
CSSBorderWidthLength(Px(right)),
@@ -490,9 +678,8 @@ impl<'self> RenderBox {
let bottom_au = Au::from_frac_px(bottom);
let left_au = Au::from_frac_px(left);
- let all_widths_equal = [top_au, right_au, bottom_au].all(|a| *a == left_au);
-
- if all_widths_equal {
+ // Are all the widths equal?
+ if [ top_au, right_au, bottom_au ].all(|a| *a == left_au) {
let border_width = top_au;
let bounds = Rect {
origin: Point2D {
@@ -507,9 +694,11 @@ impl<'self> RenderBox {
let top_color = self.style().border_top_color();
let color = top_color.to_gfx_color(); // FIXME
- let mut l = list.take(); // FIXME: use with_mut_ref when available
- l.append_item(~DisplayItem::new_Border(&bounds, border_width, color));
- list.put_back(l);
+
+ // FIXME: Use `with_mut_ref` when that works.
+ let mut this_list = list.take();
+ this_list.append_item(~DisplayItem::new_Border(&bounds, border_width, color));
+ list.put_back(this_list);
} else {
warn!("ignoring unimplemented border widths");
}
@@ -518,121 +707,95 @@ impl<'self> RenderBox {
CSSBorderWidthMedium,
CSSBorderWidthMedium,
CSSBorderWidthMedium) => {
- // FIXME: This seems to be the default for non-root nodes. For now we'll ignore it
+ // FIXME: This seems to be the default for non-root nodes. For now we'll ignore it.
warn!("ignoring medium border widths");
}
_ => warn!("ignoring unimplemented border widths")
}
}
- // Converts this node's ComputedStyle to a font style used in the graphics code.
- fn font_style(@mut self) -> FontStyle {
- do self.with_style_of_nearest_element |my_style| {
- let font_families = do my_style.font_family().map |family| {
- match *family {
- CSSFontFamilyFamilyName(ref family_str) => copy *family_str,
- CSSFontFamilyGenericFamily(Serif) => ~"serif",
- CSSFontFamilyGenericFamily(SansSerif) => ~"sans-serif",
- CSSFontFamilyGenericFamily(Cursive) => ~"cursive",
- CSSFontFamilyGenericFamily(Fantasy) => ~"fantasy",
- CSSFontFamilyGenericFamily(Monospace) => ~"monospace",
- }
- };
- let font_families = str::connect(font_families, ~", ");
- debug!("(font style) font families: `%s`", font_families);
-
- let font_size = match my_style.font_size() {
- CSSFontSizeLength(Px(l)) |
- CSSFontSizeLength(Pt(l)) => l,
- CSSFontSizeLength(Em(l)) => l,
- _ => 16f
- };
- debug!("(font style) font size: `%f`", font_size);
-
- let italic, oblique;
- match my_style.font_style() {
- CSSFontStyleNormal => { italic = false; oblique = false; }
- CSSFontStyleItalic => { italic = true; oblique = false; }
- CSSFontStyleOblique => { italic = false; oblique = true; }
+ /// Converts this node's computed style to a font style used for rendering.
+ fn font_style(&self) -> FontStyle {
+ let my_style = self.nearest_ancestor_element().style();
+
+ // FIXME: Too much allocation here.
+ let font_families = do my_style.font_family().map |family| {
+ match *family {
+ CSSFontFamilyFamilyName(ref family_str) => copy *family_str,
+ CSSFontFamilyGenericFamily(Serif) => ~"serif",
+ CSSFontFamilyGenericFamily(SansSerif) => ~"sans-serif",
+ CSSFontFamilyGenericFamily(Cursive) => ~"cursive",
+ CSSFontFamilyGenericFamily(Fantasy) => ~"fantasy",
+ CSSFontFamilyGenericFamily(Monospace) => ~"monospace",
}
+ };
+ let font_families = str::connect(font_families, ~", ");
+ debug!("(font style) font families: `%s`", font_families);
+
+ let font_size = match my_style.font_size() {
+ CSSFontSizeLength(Px(length)) |
+ CSSFontSizeLength(Pt(length)) |
+ CSSFontSizeLength(Em(length)) => length,
+ _ => 16.0
+ };
+ debug!("(font style) font size: `%f`", font_size);
- FontStyle {
- pt_size: font_size,
- weight: FontWeight300,
- italic: italic,
- oblique: oblique,
- families: font_families,
- }
+ let (italic, oblique) = match my_style.font_style() {
+ CSSFontStyleNormal => (false, false),
+ CSSFontStyleItalic => (true, false),
+ CSSFontStyleOblique => (false, true),
+ };
+
+ FontStyle {
+ pt_size: font_size,
+ weight: FontWeight300,
+ italic: italic,
+ oblique: oblique,
+ families: font_families,
}
}
- // Converts this node's ComputedStyle to a text alignment used in the inline layout code.
- fn text_align(@mut self) -> CSSTextAlign {
- do self.with_style_of_nearest_element |my_style| {
- my_style.text_align()
- }
+ /// Returns the text alignment of the computed style of the nearest ancestor-or-self `Element`
+ /// node.
+ fn text_align(&self) -> CSSTextAlign {
+ self.nearest_ancestor_element().style().text_align()
}
}
-impl BoxedMutDebugMethods for RenderBox {
- fn dump(@mut self) {
- self.dump_indent(0u);
+impl DebugMethods for RenderBox {
+ fn dump(&self) {
+ self.dump_indent(0);
}
- /* Dumps the node tree, for debugging, with indentation. */
- fn dump_indent(@mut self, indent: uint) {
- let mut s = ~"";
+ /// Dumps a render box for debugging, with indentation.
+ fn dump_indent(&self, indent: uint) {
+ let mut string = ~"";
for uint::range(0u, indent) |_i| {
- s += ~" ";
+ string += ~" ";
}
- s += self.debug_str();
- debug!("%s", s);
+ string += self.debug_str();
+ debug!("%s", string);
}
- fn debug_str(@mut self) -> ~str {
- let borrowed_self : &mut RenderBox = self; // FIXME: borrow checker workaround
- let repr = match borrowed_self {
- &GenericBox(*) => ~"GenericBox",
- &ImageBox(*) => ~"ImageBox",
- &TextBox(_,d) => fmt!("TextBox(text=%s)", str::substr(d.run.text, d.range.begin(), d.range.length())),
- &UnscannedTextBox(_, ref s) => {
- let s = s; // FIXME: borrow checker workaround
- fmt!("UnscannedTextBox(%s)", *s)
+ /// Returns a debugging string describing this box.
+ fn debug_str(&self) -> ~str {
+ let representation = match *self {
+ GenericRenderBoxClass(*) => ~"GenericRenderBox",
+ ImageRenderBoxClass(*) => ~"ImageRenderBox",
+ TextRenderBoxClass(text_box) => {
+ let mut text_box = &mut *text_box; // FIXME: Borrow check bug.
+ fmt!("TextRenderBox(text=%s)", str::substr(text_box.text_data.run.text,
+ text_box.text_data.range.begin(),
+ text_box.text_data.range.length()))
+ }
+ UnscannedTextRenderBoxClass(text_box) => {
+ let mut text_box = &mut *text_box; // FIXME: Borrow check bug.
+ fmt!("UnscannedTextRenderBox(%s)", text_box.text)
}
};
- let borrowed_self : &mut RenderBox = self; // FIXME: borrow checker workaround
- let id = borrowed_self.d().id;
- fmt!("box b%?: %?", id, repr)
+ fmt!("box b%?: %s", self.id(), representation)
}
}
-// Other methods
-impl RenderBox {
- /// Returns the nearest ancestor-or-self element node. Infallible.
- fn nearest_ancestor_element(&mut self) -> AbstractNode {
- let mut node = self.d().node;
- while !node.is_element() {
- match node.parent_node() {
- None => fail!(~"no nearest element?!"),
- Some(parent) => node = parent,
- }
- }
- node
- }
-}
-
-// FIXME: This belongs somewhere else
-trait ToGfxColor {
- fn to_gfx_color(&self) -> gfx::color::Color;
-}
-
-impl ToGfxColor for Color {
- fn to_gfx_color(&self) -> gfx::color::Color {
- gfx::color::rgba(self.red,
- self.green,
- self.blue,
- self.alpha)
- }
-}
diff --git a/src/servo/layout/box_builder.rs b/src/servo/layout/box_builder.rs
index 7c2bc3cecb6..b5e50a9f45f 100644
--- a/src/servo/layout/box_builder.rs
+++ b/src/servo/layout/box_builder.rs
@@ -8,10 +8,15 @@ use dom::element::*;
use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
use dom::node::{ElementNodeTypeId, TextNodeTypeId};
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::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::root::RootFlowData;
@@ -102,7 +107,7 @@ impl BoxGenerator {
_: &LayoutContext,
_: AbstractNode,
_: InlineSpacerSide)
- -> Option<@mut RenderBox> {
+ -> Option<RenderBox> {
None
}
@@ -133,7 +138,7 @@ impl BoxGenerator {
} else if self.inline_spacers_needed_for_node(node) {
// else, maybe make a spacer for "left" margin, border, padding
for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each
- |spacer: &@mut RenderBox| {
+ |spacer: &RenderBox| {
inline.boxes.push(*spacer);
}
}
@@ -145,7 +150,7 @@ impl BoxGenerator {
debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)",
block.common.id,
- new_box.d().id,
+ new_box.id(),
node.debug_str());
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]: attaching box[b%d] to root flow (node: %s)",
root.common.id,
- new_box.d().id,
+ new_box.id(),
node.debug_str());
assert!(root.box.is_none());
@@ -450,55 +455,40 @@ pub impl LayoutTreeBuilder {
layout_ctx: &LayoutContext,
ty: RenderBoxType,
node: AbstractNode,
- ctx: FlowContext)
- -> @mut RenderBox {
+ flow_context: FlowContext)
+ -> RenderBox {
+ let base = RenderBoxBase::new(node, flow_context, self.next_box_id());
let result = match ty {
- RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx),
- RenderBox_Text => self.make_text_box(layout_ctx, node, ctx),
- RenderBox_Image => self.make_image_box(layout_ctx, node, ctx),
+ RenderBox_Generic => GenericRenderBoxClass(@mut base),
+ RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)),
+ RenderBox_Image => self.make_image_box(layout_ctx, node, flow_context, base),
};
debug!("LayoutTreeBuilder: created box: %s", result.debug_str());
result
}
- fn make_generic_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext)
- -> @mut RenderBox {
- @mut GenericBox(RenderBoxData(copy node, ctx, self.next_box_id()))
- }
-
- fn make_image_box(&mut self, layout_ctx: &LayoutContext, node: AbstractNode, ctx: FlowContext)
- -> @mut RenderBox {
- if !node.is_image_element() {
- fail!(~"WAT error: why couldn't we make an image box?");
- }
+ fn make_image_box(&mut self,
+ layout_ctx: &LayoutContext,
+ node: AbstractNode,
+ flow_context: FlowContext,
+ base: RenderBoxBase)
+ -> RenderBox {
+ assert!(node.is_image_element());
do node.with_imm_image_element |image_element| {
if image_element.image.is_some() {
- let holder = ImageHolder::new(copy *image_element.image.get_ref(),
- layout_ctx.image_cache);
- @mut ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder)
+ // FIXME(pcwalton): Don't copy URLs.
+ let url = copy *image_element.image.get_ref();
+ ImageRenderBoxClass(@mut ImageRenderBox::new(base, url, layout_ctx.image_cache))
} else {
info!("Tried to make image box, but couldn't find image. Made generic box \
instead.");
- self.make_generic_box(layout_ctx, node, ctx)
+ GenericRenderBoxClass(@mut base)
}
}
}
- fn make_text_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext)
- -> @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 {
+ fn decide_box_type(&self, node: AbstractNode, _: CSSDisplay) -> RenderBoxType {
if node.is_text() {
RenderBox_Text
} else if node.is_image_element() {
diff --git a/src/servo/layout/display_list_builder.rs b/src/servo/layout/display_list_builder.rs
index 353f25561e7..7716c6f8473 100644
--- a/src/servo/layout/display_list_builder.rs
+++ b/src/servo/layout/display_list_builder.rs
@@ -16,6 +16,7 @@ use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use gfx;
+use newcss;
use servo_util::tree::TreeNodeRef;
/// 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)
+ }
+}
+
diff --git a/src/servo/layout/flow.rs b/src/servo/layout/flow.rs
index a6d0c23626a..231a5a09fd9 100644
--- a/src/servo/layout/flow.rs
+++ b/src/servo/layout/flow.rs
@@ -285,31 +285,37 @@ impl<'self> FlowContext {
}
// 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 {
RootFlow(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) => {
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) => {
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,
node: AbstractNode,
seed: B,
- callback: &fn(a: B, @mut RenderBox) -> B)
+ callback: &fn(a: B, RenderBox) -> B)
-> B {
do self.foldl_all_boxes(seed) |acc, box| {
- if box.d().node == node {
+ if box.node() == node {
callback(acc, box)
} else {
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 {
RootFlow(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| {
- if box.d().node == node {
+ if box.node() == node {
if !callback(box) {
break;
}
@@ -384,7 +390,7 @@ impl DebugMethods for FlowContext {
InlineFlow(inline) => {
let inline = &mut *inline;
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
@@ -392,14 +398,14 @@ impl DebugMethods for FlowContext {
BlockFlow(block) => {
let block = &mut *block;
match block.box {
- Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id),
+ Some(box) => fmt!("BlockFlow(box=b%d)", box.id()),
None => ~"BlockFlow",
}
},
RootFlow(root) => {
let root = &mut *root;
match root.box {
- Some(box) => fmt!("RootFlo(box=b%d)", box.d().id),
+ Some(box) => fmt!("RootFlow(box=b%d)", box.id()),
None => ~"RootFlow",
}
},
diff --git a/src/servo/layout/inline.rs b/src/servo/layout/inline.rs
index 706f2b1f8cc..1e0f4ba2313 100644
--- a/src/servo/layout/inline.rs
+++ b/src/servo/layout/inline.rs
@@ -5,7 +5,8 @@
use core::cell::Cell;
use core;
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::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods};
use layout::display_list_builder::DisplayListBuilder;
@@ -82,9 +83,7 @@ impl ElementMapping {
do self.entries.eachi |i, nr| { cb(i, nr) }
}
- pub fn repair_for_box_changes(&mut self,
- old_boxes: &[@mut RenderBox],
- new_boxes: &[@mut RenderBox]) {
+ pub fn repair_for_box_changes(&mut self, old_boxes: &[RenderBox], new_boxes: &[RenderBox]) {
let entries = &mut self.entries;
debug!("--- Old boxes: ---");
@@ -129,9 +128,15 @@ impl ElementMapping {
// 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() {
- let o = old_boxes[old_i];
- let n = new_boxes[new_j];
- if o.d().node != n.d().node { break }
+ let should_leave = do old_boxes[old_i].with_imm_base |old_box_base| {
+ do new_boxes[new_j].with_imm_base |new_box_base| {
+ old_box_base.node != new_box_base.node
+ }
+ };
+ if should_leave {
+ break
+ }
+
debug!("repair_for_box_changes: Slide through new box %u", new_j);
new_j += 1;
}
@@ -192,14 +197,16 @@ impl TextRunScanner {
inline.boxes = out_boxes;
// 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!(right_i > 0 && right_i < boxes.len());
assert!(left_i != right_i);
let (left, right) = (boxes[left_i], boxes[right_i]);
match (left, right) {
- (@UnscannedTextBox(*), @UnscannedTextBox(*)) => left.can_merge_with_box(right),
+ (UnscannedTextRenderBoxClass(*), UnscannedTextRenderBoxClass(*)) => {
+ left.can_merge_with_box(right)
+ }
(_, _) => false
}
}
@@ -218,14 +225,14 @@ impl TextRunScanner {
fn flush_clump_to_list(&mut self,
ctx: &mut LayoutContext,
flow: FlowContext,
- in_boxes: &[@mut RenderBox],
- out_boxes: &mut ~[@mut RenderBox]) {
+ in_boxes: &[RenderBox],
+ out_boxes: &mut ~[RenderBox]) {
assert!(self.clump.length() > 0);
debug!("TextRunScanner: flushing boxes in range=%?", self.clump);
let is_singleton = self.clump.length() == 1;
- let is_text_clump = match *in_boxes[self.clump.begin()] {
- UnscannedTextBox(*) => true,
+ let is_text_clump = match in_boxes[self.clump.begin()] {
+ UnscannedTextRenderBoxClass(*) => true,
_ => false
};
@@ -254,10 +261,12 @@ impl TextRunScanner {
let run = @fontgroup.create_textrun(transformed_text);
debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
- let new_box = adapt_textbox_with_range(old_box.d(),
- run,
- &Range::new(0, run.char_len()));
- out_boxes.push(new_box);
+ let new_box = do old_box.with_imm_base |old_box_base| {
+ let range = Range::new(0, run.char_len());
+ @mut adapt_textbox_with_range(*old_box_base, run, range)
+ };
+
+ out_boxes.push(TextRenderBoxClass(new_box));
},
(false, true) => {
// 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);
let clump = self.clump;
for clump.eachi |i| {
- let range = &new_ranges[i - self.clump.begin()];
+ let range = new_ranges[i - self.clump.begin()];
if range.length() == 0 {
error!("Elided an `UnscannedTextbox` because it was zero-length after \
compression; %s",
@@ -305,8 +314,10 @@ impl TextRunScanner {
loop
}
- let new_box = adapt_textbox_with_range(in_boxes[i].d(), run, range);
- out_boxes.push(new_box);
+ do in_boxes[i].with_imm_base |base| {
+ let new_box = @mut adapt_textbox_with_range(*base, run, range);
+ out_boxes.push(TextRenderBoxClass(new_box));
+ }
}
}
} // End of match.
@@ -342,14 +353,14 @@ struct PendingLine {
struct LineboxScanner {
flow: FlowContext,
- new_boxes: ~[@mut RenderBox],
- work_list: @mut Deque<@mut RenderBox>,
+ new_boxes: ~[RenderBox],
+ work_list: @mut Deque<RenderBox>,
pending_line: PendingLine,
line_spans: ~[Range],
}
impl LineboxScanner {
- fn new(inline: FlowContext) -> LineboxScanner {
+ pub fn new(inline: FlowContext) -> LineboxScanner {
assert!(inline.starts_inline_flow());
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());
self.line_spans = ~[];
self.new_boxes = ~[];
self.reset_linebox();
}
- priv fn reset_linebox(&mut self) {
+ fn reset_linebox(&mut self) {
self.pending_line.range.reset(0,0);
self.pending_line.width = Au(0);
}
@@ -382,13 +393,15 @@ impl LineboxScanner {
loop {
// acquire the next box to lay out from work list or box list
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;
- 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
} else {
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
};
@@ -414,7 +427,7 @@ impl LineboxScanner {
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",
self.line_spans.len(),
self.flow.id());
@@ -425,7 +438,7 @@ impl LineboxScanner {
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: %?",
self.line_spans.len(), self.pending_line);
// set box horizontal offsets
@@ -453,25 +466,28 @@ impl LineboxScanner {
// TODO(Issue #213): implement `text-align: justify`
CSSTextAlignLeft | CSSTextAlignJustify => {
for line_range.eachi |i| {
- let box_data = &self.new_boxes[i].d();
- box_data.position.origin.x = offset_x;
- offset_x += box_data.position.size.width;
+ do self.new_boxes[i].with_mut_base |base| {
+ base.position.origin.x = offset_x;
+ offset_x += base.position.size.width;
+ }
}
},
CSSTextAlignCenter => {
offset_x = slack_width.scale_by(0.5f);
for line_range.eachi |i| {
- let box_data = &self.new_boxes[i].d();
- box_data.position.origin.x = offset_x;
- offset_x += box_data.position.size.width;
+ do self.new_boxes[i].with_mut_base |base| {
+ base.position.origin.x = offset_x;
+ offset_x += base.position.size.width;
+ }
}
},
CSSTextAlignRight => {
offset_x = slack_width;
for line_range.eachi |i| {
- let box_data = &self.new_boxes[i].d();
- box_data.position.origin.x = offset_x;
- offset_x += box_data.position.size.width;
+ do self.new_boxes[i].with_mut_base |base| {
+ base.position.origin.x = offset_x;
+ offset_x += base.position.size.width;
+ }
}
},
}
@@ -483,13 +499,17 @@ impl LineboxScanner {
}
// 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 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;
- debug!("LineboxScanner: Trying to append box to line %u (box width: %?, remaining width: %?): %s",
- self.line_spans.len(), in_box_width, remaining_width, in_box.debug_str());
+ debug!("LineboxScanner: Trying to append box to line %u (box width: %?, remaining width: \
+ %?): %s",
+ self.line_spans.len(),
+ in_box_width,
+ remaining_width,
+ in_box.debug_str());
if in_box_width <= remaining_width {
debug!("LineboxScanner: case=box fits without splitting");
@@ -501,8 +521,9 @@ impl LineboxScanner {
// force it onto the line anyway, if its otherwise empty
// TODO(Issue #224): signal that horizontal overflow happened?
if line_is_empty {
- debug!("LineboxScanner: case=box can't split and line %u is empty, so overflowing.",
- self.line_spans.len());
+ debug!("LineboxScanner: case=box can't split and line %u is empty, so \
+ overflowing.",
+ self.line_spans.len());
self.push_box_to_line(in_box);
return true;
} else {
@@ -514,7 +535,8 @@ impl LineboxScanner {
// not enough width; try splitting?
match in_box.split_to_width(ctx, remaining_width, line_is_empty) {
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;
},
SplitDidFit(left, right) => {
@@ -524,11 +546,9 @@ impl LineboxScanner {
self.push_box_to_line(left_box);
self.work_list.add_front(right_box);
},
- (Some(left_box), None) => { self.push_box_to_line(left_box); }
- (None, Some(right_box)) => { self.push_box_to_line(right_box); }
- (None, None) => {
- error!("LineboxScanner: This split case makes no sense!");
- }
+ (Some(left_box), None) => self.push_box_to_line(left_box),
+ (None, Some(right_box)) => self.push_box_to_line(right_box),
+ (None, None) => error!("LineboxScanner: This split case makes no sense!"),
}
return true;
},
@@ -563,15 +583,15 @@ impl LineboxScanner {
}
// unconditional push
- priv fn push_box_to_line(&mut self, box: @mut RenderBox) {
- debug!("LineboxScanner: Pushing box b%d to line %u", box.d().id, self.line_spans.len());
+ fn push_box_to_line(&mut self, box: RenderBox) {
+ debug!("LineboxScanner: Pushing box b%d to line %u", box.id(), self.line_spans.len());
if self.pending_line.range.length() == 0 {
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.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);
}
}
@@ -582,7 +602,7 @@ pub struct InlineFlowData {
// A vec of all inline render boxes. Several boxes may
// correspond to one Node/Element.
- boxes: ~[@mut RenderBox],
+ boxes: ~[RenderBox],
// vec of ranges into boxes that represents line positions.
// these ranges are disjoint, and are the result of inline layout.
lines: ~[Range],
@@ -648,21 +668,24 @@ impl InlineFlowData {
{
let this = &mut *self;
for this.boxes.each |&box| {
- let box2 = &mut *box;
- box.d().position.size.width = match *box2 {
- ImageBox(_, ref img) => {
- let img2: &mut holder::ImageHolder = unsafe { cast::transmute(img) };
- Au::from_px(img2.get_size().get_or_default(Size2D(0,0)).width)
+ match box {
+ ImageRenderBoxClass(image_box) => {
+ let size = image_box.image.get_size();
+ let width = Au::from_px(size.get_or_default(Size2D(0, 0)).width);
+ image_box.base.position.size.width = width;
}
- TextBox(*) => {
- // Text boxes are initialized with dimensions.
- box.d().position.size.width
- },
- // TODO(#225): There will be different cases here for `inline-block` and other
- // replaced content.
- GenericBox(*) => Au::from_px(45),
- _ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box))
- };
+ TextRenderBoxClass(text_box) => {
+ // Text boxes are preinitialized.
+ }
+ GenericRenderBoxClass(generic_box) => {
+ // TODO(#225): There will be different cases here for `inline-block` and
+ // other replaced content.
+ // FIXME(pcwalton): This seems clownshoes; can we remove?
+ 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.
}
@@ -698,59 +721,64 @@ impl InlineFlowData {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
// Compute the height of each box.
- let d = cur_box.d(); // FIXME: borrow checker workaround
- let cur_box = &mut *cur_box; // FIXME: borrow checker workaround
- d.position.size.height = match *cur_box {
- ImageBox(_, ref img) => {
- Au::from_px(img.size().height)
+ match cur_box {
+ ImageRenderBoxClass(image_box) => {
+ let size = image_box.image.get_size();
+ let height = Au::from_px(size.get_or_default(Size2D(0, 0)).height);
+ image_box.base.position.size.height = height;
}
- TextBox(*) => {
- // Text boxes are initialized with dimensions.
- d.position.size.height
- },
- // TODO(Issue #225): different cases for 'inline-block', other replaced content
- GenericBox(*) => Au::from_px(30),
+ TextRenderBoxClass(*) => {
+ // Text boxes are preinitialized.
+ }
+ GenericRenderBoxClass(generic_box) => {
+ // TODO(Issue #225): There will be different cases here for `inline-block`
+ // 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",
cur_box.debug_str()))
}
- };
+ }
// 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
// these rects.
- let bounding_box = match *cur_box {
+ let bounding_box = match cur_box {
// Adjust to baseline coordinates.
//
// TODO(#227): Use left/right margins, border, padding for nonreplaced content,
// and also use top/bottom margins, border, padding for replaced or
// inline-block content.
//
- // TODO(#225): Use height, width for 'inline-block' and other replaced content.
- ImageBox(*) | GenericBox(*) => {
- let box_bounds = d.position;
- box_bounds.translate(&Point2D(Au(0), -d.position.size.height))
+ // TODO(#225): Use height, width for `inline-block` and other replaced content.
+ ImageRenderBoxClass(*) | GenericRenderBoxClass(*) => {
+ let height = cur_box.position().size.height;
+ cur_box.position().translate(&Point2D(Au(0), -height))
},
// Adjust the bounding box metric to the box's horizontal offset.
//
// TODO: We can use font metrics directly instead of re-measuring for the
// bounding box.
- TextBox(_, data) => {
- let text_bounds = data.run.metrics_for_range(&data.range).bounding_box;
- text_bounds.translate(&Point2D(d.position.origin.x, Au(0)))
+ TextRenderBoxClass(text_box) => {
+ let text_box = &mut *text_box; // FIXME: borrow check workaround
+ 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",
cur_box.debug_str()))
}
};
debug!("assign_height_inline: bounding box for box b%d = %?",
- cur_box.d().id,
+ cur_box.id(),
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`
// 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.
- let halfleading = match *cur_box {
- TextBox(_, data) => {
- (data.run.font.metrics.em_size - line_height).scale_by(0.5f)
+ let halfleading = match cur_box {
+ TextRenderBoxClass(text_box) => {
+ (text_box.text_data.run.font.metrics.em_size - line_height).scale_by(0.5)
},
_ => Au(0),
};
- cur_box.d().position.origin.y =
- cur_y + halfleading + (baseline_offset - cur_box.d().position.size.height);
+ do cur_box.with_mut_base |base| {
+ let height = base.position.size.height;
+ base.position.origin.y = cur_y + halfleading + baseline_offset - height;
+ }
}
cur_y += Au::max(line_height, linebox_height);
diff --git a/src/servo/layout/root.rs b/src/servo/layout/root.rs
index a67eb0b6b1c..1e33894621d 100644
--- a/src/servo/layout/root.rs
+++ b/src/servo/layout/root.rs
@@ -20,7 +20,7 @@ pub struct RootFlowData {
common: FlowData,
/// The render box at the root of the tree.
- box: Option<@mut RenderBox>
+ box: Option<RenderBox>
}
impl RootFlowData {
@@ -73,9 +73,11 @@ impl RootFlowData {
self.common.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
do RootFlow(self).with_block_box |box| {
- box.d().position.origin.y = Au(0);
- box.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
- let (_used_top, _used_bot) = box.get_used_height();
+ do box.with_mut_base |base| {
+ base.position.origin.y = Au(0);
+ base.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
+ let (_used_top, _used_bot) = box.get_used_height();
+ }
}
}
diff --git a/src/servo/layout/text.rs b/src/servo/layout/text.rs
index 0f3e5683cfb..2bf2d49f0ac 100644
--- a/src/servo/layout/text.rs
+++ b/src/servo/layout/text.rs
@@ -2,9 +2,9 @@
* 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/. */
-/** 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 servo_util::range::Range;
@@ -14,37 +14,47 @@ pub struct TextBoxData {
range: Range,
}
-pub fn TextBoxData(run: @TextRun, range: &Range) -> TextBoxData {
- TextBoxData {
- run: run,
- range: copy *range,
+impl TextBoxData {
+ pub fn new(run: @TextRun, range: Range) -> TextBoxData {
+ TextBoxData {
+ run: run,
+ range: range,
+ }
}
}
-pub fn adapt_textbox_with_range(box_data: &mut RenderBoxData, run: @TextRun,
- range: &Range) -> @mut RenderBox {
+pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range)
+ -> TextRenderBox {
assert!(range.begin() < run.char_len());
assert!(range.end() <= run.char_len());
assert!(range.length() > 0);
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
- run.char_len(), range.begin(), range.length(), run.text);
- let mut new_box_data = copy *box_data;
- let new_text_data = TextBoxData(run, range);
- let metrics = run.metrics_for_range(range);
- new_box_data.position.size = metrics.bounding_box.size;
- @mut TextBox(new_box_data, new_text_data)
+ run.char_len(),
+ range.begin(),
+ range.length(),
+ run.text);
+ let new_text_data = TextBoxData::new(run, range);
+ 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 {
- 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 {
- fn raw_text(&mut self) -> ~str {
- match self {
- &UnscannedTextBox(_, ref s) => copy *s,
- _ => fail!(~"unsupported operation: box.raw_text() on non-unscanned text box.")
+ fn raw_text(&self) -> ~str {
+ match *self {
+ UnscannedTextRenderBoxClass(text_box) => copy text_box.text,
+ _ => fail!(~"unsupported operation: box.raw_text() on non-unscanned text box."),
}
}
}