diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2013-05-07 20:52:38 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2013-05-08 15:20:13 -0700 |
commit | b17e68ee048756bf2ca5a43f26204059a5f48f32 (patch) | |
tree | bfcfa5ed95b5493ea3c3120b3a6b5f68d31e1a5c /src | |
parent | 49edf85f5d98ea44198c24befa02bebada8fdad4 (diff) | |
download | servo-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.rs | 4 | ||||
-rw-r--r-- | src/servo/layout/block.rs | 27 | ||||
-rw-r--r-- | src/servo/layout/box.rs | 949 | ||||
-rw-r--r-- | src/servo/layout/box_builder.rs | 68 | ||||
-rw-r--r-- | src/servo/layout/display_list_builder.rs | 17 | ||||
-rw-r--r-- | src/servo/layout/flow.rs | 32 | ||||
-rw-r--r-- | src/servo/layout/inline.rs | 224 | ||||
-rw-r--r-- | src/servo/layout/root.rs | 10 | ||||
-rw-r--r-- | src/servo/layout/text.rs | 48 |
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."), } } } |