aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-10-24 16:57:42 -0700
committerPatrick Walton <pcwalton@mimiga.net>2013-10-28 13:22:26 -0700
commit42092921c17a779eb8c0db3b056204dfaaa9dab7 (patch)
treec12417ad6538e0ba55328c4f3d254295055e133a
parent81f5ba7d059805e1c879bff1b7380c1d90ba9fd8 (diff)
downloadservo-42092921c17a779eb8c0db3b056204dfaaa9dab7.tar.gz
servo-42092921c17a779eb8c0db3b056204dfaaa9dab7.zip
Optimize reflow by changing enums to traits and inlining more
-rw-r--r--Makefile.in2
-rw-r--r--src/components/gfx/font.rs44
-rw-r--r--src/components/gfx/platform/linux/font.rs4
-rw-r--r--src/components/gfx/text/glyph.rs56
-rw-r--r--src/components/gfx/text/text_run.rs25
-rw-r--r--src/components/main/compositing/compositor_layer.rs5
-rw-r--r--src/components/main/css/node_style.rs5
-rw-r--r--src/components/main/layout/block.rs615
-rw-r--r--src/components/main/layout/box.rs1427
-rw-r--r--src/components/main/layout/box_builder.rs376
-rw-r--r--src/components/main/layout/display_list_builder.rs16
-rw-r--r--src/components/main/layout/float.rs329
-rw-r--r--src/components/main/layout/float_context.rs165
-rw-r--r--src/components/main/layout/flow.rs632
-rw-r--r--src/components/main/layout/inline.rs509
-rw-r--r--src/components/main/layout/layout_task.rs240
-rw-r--r--src/components/main/layout/model.rs25
-rw-r--r--src/components/main/layout/text.rs101
-rw-r--r--src/components/main/layout/util.rs18
-rw-r--r--src/components/main/pipeline.rs2
-rw-r--r--src/components/main/platform/common/glfw_windowing.rs36
-rw-r--r--src/components/net/local_image_cache.rs10
-rw-r--r--src/components/script/dom/node.rs14
-rw-r--r--src/components/script/html/hubbub_html_parser.rs2
-rw-r--r--src/components/style/properties.rs.mako5
-rw-r--r--src/components/style/selector_matching.rs14
-rw-r--r--src/components/util/geometry.rs55
-rw-r--r--src/components/util/range.rs22
-rw-r--r--src/components/util/slot.rs127
-rw-r--r--src/components/util/time.rs17
-rw-r--r--src/components/util/util.rc1
m---------src/support/spidermonkey/mozjs0
-rw-r--r--src/test/html/longcatbot.pngbin17588 -> 18610 bytes
-rw-r--r--src/test/html/longcatmid.pngbin329 -> 463 bytes
-rw-r--r--src/test/html/longcattop.pngbin17457 -> 18222 bytes
-rw-r--r--src/test/html/rust-0.pngbin4399 -> 4399 bytes
-rw-r--r--src/test/html/rust-135.pngbin10767 -> 10547 bytes
-rw-r--r--src/test/html/rust-180.pngbin4395 -> 4395 bytes
-rw-r--r--src/test/html/rust-225.pngbin10889 -> 10808 bytes
-rw-r--r--src/test/html/rust-270.pngbin4497 -> 4497 bytes
-rw-r--r--src/test/html/rust-315.pngbin10753 -> 10574 bytes
-rw-r--r--src/test/html/rust-45.pngbin10873 -> 10807 bytes
-rw-r--r--src/test/html/rust-90.pngbin4466 -> 4466 bytes
-rw-r--r--src/test/html/test_sandboxed_iframe.html8
44 files changed, 2635 insertions, 2272 deletions
diff --git a/Makefile.in b/Makefile.in
index 3d372fd0500..9689d5fd121 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -32,7 +32,7 @@ MKFILE_DEPS := config.stamp $(call rwildcard,$(S)mk/,*)
# Enable debug!() etc even without configure --enable-debug
# The evaluation of these prints & their arguments is controlled
# at runtime by the environment variable RUST_LOG.
-CFG_RUSTC_FLAGS += --cfg debug -Z debug-info
+CFG_RUSTC_FLAGS += -Z debug-info -Z no-debug-borrows
ifdef CFG_DISABLE_OPTIMIZE
$(info cfg: disabling rustc optimization (CFG_DISABLE_OPTIMIZE))
diff --git a/src/components/gfx/font.rs b/src/components/gfx/font.rs
index 7ec40f29071..faecef29904 100644
--- a/src/components/gfx/font.rs
+++ b/src/components/gfx/font.rs
@@ -2,33 +2,29 @@
* 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/. */
+use azure::{AzFloat, AzScaledFontRef};
+use azure::azure_hl::{BackendType, ColorPattern};
+use azure::scaled_font::ScaledFont;
+use extra::arc::Arc;
+use geom::{Point2D, Rect, Size2D};
+use std::cast;
+use std::ptr;
+use std::str;
+use std::vec;
+use servo_util::cache::{Cache, HashCache};
+use servo_util::range::Range;
+use servo_util::time::ProfilerChan;
+use style::computed_values::text_decoration;
+
use color::Color;
use font_context::FontContext;
use servo_util::geometry::Au;
use platform::font_context::FontContextHandle;
use platform::font::{FontHandle, FontTable};
use render_context::RenderContext;
-use servo_util::range::Range;
-use std::cast;
-use std::ptr;
-use std::str;
-use std::vec;
-use servo_util::cache::{Cache, HashCache};
use text::glyph::{GlyphStore, GlyphIndex};
use text::shaping::ShaperMethods;
use text::{Shaper, TextRun};
-use extra::arc::Arc;
-
-use azure::{AzFloat, AzScaledFontRef};
-use azure::scaled_font::ScaledFont;
-use azure::azure_hl::{BackendType, ColorPattern};
-use geom::{Point2D, Rect, Size2D};
-
-use servo_util::time;
-use servo_util::time::profile;
-use servo_util::time::ProfilerChan;
-
-use style::computed_values::text_decoration;
// FontHandle encapsulates access to the platform's font API,
// e.g. quartz, FreeType. It provides access to metrics and tables
@@ -459,13 +455,11 @@ impl Font {
}
pub fn shape_text(@mut self, text: ~str, is_whitespace: bool) -> Arc<GlyphStore> {
- do profile(time::LayoutShapingCategory, self.profiler_chan.clone()) {
- let shaper = self.get_shaper();
- do self.shape_cache.find_or_create(&text) |txt| {
- let mut glyphs = GlyphStore::new(text.char_len(), is_whitespace);
- shaper.shape_text(*txt, &mut glyphs);
- Arc::new(glyphs)
- }
+ let shaper = self.get_shaper();
+ do self.shape_cache.find_or_create(&text) |txt| {
+ let mut glyphs = GlyphStore::new(text.char_len(), is_whitespace);
+ shaper.shape_text(*txt, &mut glyphs);
+ Arc::new(glyphs)
}
}
diff --git a/src/components/gfx/platform/linux/font.rs b/src/components/gfx/platform/linux/font.rs
index f17ca694813..0f33720d420 100644
--- a/src/components/gfx/platform/linux/font.rs
+++ b/src/components/gfx/platform/linux/font.rs
@@ -276,7 +276,7 @@ impl<'self> FontHandle {
}
#[fixed_stack_segment]
- pub fn new_from_file(fctx: &FontContextHandle, file: ~str,
+ pub fn new_from_file(fctx: &FontContextHandle, file: &str,
style: &SpecifiedFontStyle) -> Result<FontHandle, ()> {
unsafe {
let ft_ctx: FT_Library = fctx.ctx.ctx;
@@ -293,7 +293,7 @@ impl<'self> FontHandle {
}
if FontHandle::set_char_size(face, style.pt_size).is_ok() {
Ok(FontHandle {
- source: FontSourceFile(file),
+ source: FontSourceFile(file.to_str()),
face: face,
handle: *fctx
})
diff --git a/src/components/gfx/text/glyph.rs b/src/components/gfx/text/glyph.rs
index 0baa11dff51..077a393d9c3 100644
--- a/src/components/gfx/text/glyph.rs
+++ b/src/components/gfx/text/glyph.rs
@@ -303,7 +303,11 @@ impl Ord for DetailedGlyphRecord {
// usage pattern of setting/appending all the detailed glyphs, and
// then querying without setting.
struct DetailedGlyphStore {
+ // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
+ // optimization.
detail_buffer: ~[DetailedGlyph],
+ // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
+ // optimization.
detail_lookup: ~[DetailedGlyphRecord],
lookup_is_sorted: bool,
}
@@ -506,8 +510,12 @@ impl<'self> GlyphInfo<'self> {
// Public data structure and API for storing and retrieving glyph data
pub struct GlyphStore {
+ // TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
+ // optimization.
entry_buffer: ~[GlyphEntry],
+
detail_store: DetailedGlyphStore,
+
is_whitespace: bool,
}
@@ -602,6 +610,7 @@ impl<'self> GlyphStore {
self.iter_glyphs_for_char_range(&Range::new(i, 1))
}
+ #[inline]
pub fn iter_glyphs_for_char_range(&'self self, rang: &Range) -> GlyphIterator<'self> {
if rang.begin() >= self.entry_buffer.len() {
fail!("iter_glyphs_for_range: range.begin beyond length!");
@@ -682,23 +691,44 @@ pub struct GlyphIterator<'self> {
priv glyph_range: Option<iter::Range<uint>>,
}
+impl<'self> GlyphIterator<'self> {
+ // Slow path when there is a glyph range.
+ #[inline(never)]
+ fn next_glyph_range(&mut self) -> Option<(uint, GlyphInfo<'self>)> {
+ match self.glyph_range.get_mut_ref().next() {
+ Some(j) => Some((self.char_index,
+ DetailGlyphInfo(self.store, self.char_index, j as u16))),
+ None => {
+ // No more glyphs for current character. Try to get another.
+ self.glyph_range = None;
+ self.next()
+ }
+ }
+ }
+
+ // Slow path when there is a complex glyph.
+ #[inline(never)]
+ fn next_complex_glyph(&mut self, entry: &GlyphEntry, i: uint)
+ -> Option<(uint, GlyphInfo<'self>)> {
+ let glyphs = self.store.detail_store.get_detailed_glyphs_for_entry(i, entry.glyph_count());
+ self.glyph_range = Some(range(0, glyphs.len()));
+ self.next()
+ }
+}
+
impl<'self> Iterator<(uint, GlyphInfo<'self>)> for GlyphIterator<'self> {
// I tried to start with something simpler and apply FlatMap, but the
// inability to store free variables in the FlatMap struct was problematic.
-
+ //
+ // This function consists of the fast path and is designed to be inlined into its caller. The
+ // slow paths, which should not be inlined, are `next_glyph_range()` and
+ // `next_complex_glyph()`.
+ #[inline(always)]
fn next(&mut self) -> Option<(uint, GlyphInfo<'self>)> {
// Would use 'match' here but it borrows contents in a way that
// interferes with mutation.
if self.glyph_range.is_some() {
- match self.glyph_range.get_mut_ref().next() {
- Some(j) => Some((self.char_index,
- DetailGlyphInfo(self.store, self.char_index, j as u16))),
- None => {
- // No more glyphs for current character. Try to get another.
- self.glyph_range = None;
- self.next()
- }
- }
+ self.next_glyph_range()
} else {
// No glyph range. Look at next character.
match self.char_range.next() {
@@ -709,10 +739,8 @@ impl<'self> Iterator<(uint, GlyphInfo<'self>)> for GlyphIterator<'self> {
if entry.is_simple() {
Some((self.char_index, SimpleGlyphInfo(self.store, i)))
} else {
- let glyphs = self.store.detail_store
- .get_detailed_glyphs_for_entry(i, entry.glyph_count());
- self.glyph_range = Some(range(0, glyphs.len()));
- self.next()
+ // Fall back to the slow path.
+ self.next_complex_glyph(entry, i)
}
},
None => None
diff --git a/src/components/gfx/text/text_run.rs b/src/components/gfx/text/text_run.rs
index bcc8d3c876e..81df6f0ee3a 100644
--- a/src/components/gfx/text/text_run.rs
+++ b/src/components/gfx/text/text_run.rs
@@ -14,18 +14,19 @@ use style::computed_values::text_decoration;
/// A text run.
pub struct TextRun {
- text: ~str,
+ text: Arc<~str>,
font: @mut Font,
decoration: text_decoration::T,
- glyphs: ~[Arc<GlyphStore>],
+ glyphs: Arc<~[Arc<GlyphStore>]>,
}
-/// This is a hack until TextRuns are normally sendable, or we instead use Arc<TextRun> everywhere.
+/// The same as a text run, but with a font descriptor instead of a font. This makes them thread
+/// safe.
pub struct SendableTextRun {
- text: ~str,
+ text: Arc<~str>,
font: FontDescriptor,
decoration: text_decoration::T,
- priv glyphs: ~[Arc<GlyphStore>],
+ priv glyphs: Arc<~[Arc<GlyphStore>]>,
}
impl SendableTextRun {
@@ -51,6 +52,8 @@ pub struct SliceIterator<'self> {
}
impl<'self> Iterator<(&'self GlyphStore, uint, Range)> for SliceIterator<'self> {
+ // inline(always) due to the inefficient rt failures messing up inline heuristics, I think.
+ #[inline(always)]
fn next(&mut self) -> Option<(&'self GlyphStore, uint, Range)> {
loop {
let slice_glyphs = self.glyph_iter.next();
@@ -121,10 +124,10 @@ impl<'self> TextRun {
let glyphs = TextRun::break_and_shape(font, text);
let run = TextRun {
- text: text,
+ text: Arc::new(text),
font: font,
decoration: decoration,
- glyphs: glyphs,
+ glyphs: Arc::new(glyphs),
};
return run;
}
@@ -198,12 +201,14 @@ impl<'self> TextRun {
}
pub fn char_len(&self) -> uint {
- do self.glyphs.iter().fold(0u) |len, slice_glyphs| {
+ do self.glyphs.get().iter().fold(0u) |len, slice_glyphs| {
len + slice_glyphs.get().char_len()
}
}
- pub fn glyphs(&'self self) -> &'self ~[Arc<GlyphStore>] { &self.glyphs }
+ pub fn glyphs(&'self self) -> &'self ~[Arc<GlyphStore>] {
+ self.glyphs.get()
+ }
pub fn range_is_trimmable_whitespace(&self, range: &Range) -> bool {
for (slice_glyphs, _, _) in self.iter_slices_for_range(range) {
@@ -233,7 +238,7 @@ impl<'self> TextRun {
pub fn iter_slices_for_range(&'self self, range: &Range) -> SliceIterator<'self> {
SliceIterator {
- glyph_iter: self.glyphs.iter(),
+ glyph_iter: self.glyphs.get().iter(),
range: *range,
offset: 0,
}
diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs
index 13d22f6602a..29b79ef06d4 100644
--- a/src/components/main/compositing/compositor_layer.rs
+++ b/src/components/main/compositing/compositor_layer.rs
@@ -19,7 +19,8 @@ use script::script_task::SendEventMsg;
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch, Tile};
use servo_msg::constellation_msg::PipelineId;
use std::cell::Cell;
-use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
+use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
+use windowing::{MouseWindowMouseUpEvent};
#[cfg(not(target_os="macos"))]
use layers::texturegl::TextureTarget2D;
@@ -372,7 +373,7 @@ impl CompositorLayer {
}
}
- // Returns whether the layer should be vertically flipped. and
+ // Returns whether the layer should be vertically flipped.
#[cfg(target_os="macos")]
fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) {
let flip = if cpu_painting {
diff --git a/src/components/main/css/node_style.rs b/src/components/main/css/node_style.rs
index 49ab10710e8..e4c40e1e3b4 100644
--- a/src/components/main/css/node_style.rs
+++ b/src/components/main/css/node_style.rs
@@ -9,8 +9,6 @@ use layout::incremental::RestyleDamage;
use style::ComputedValues;
use script::dom::node::{AbstractNode, LayoutView};
-use servo_util::tree::TreeNodeRef;
-
/// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode {
@@ -20,7 +18,8 @@ pub trait StyledNode {
impl StyledNode for AbstractNode<LayoutView> {
fn style(&self) -> &ComputedValues {
- assert!(self.is_element()); // Only elements can have styles
+ // FIXME(pcwalton): Is this assertion needed for memory safety? It's slow.
+ //assert!(self.is_element()); // Only elements can have styles
let results = self.get_css_select_results();
results
}
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs
index 45044caa341..ba3b4746a63 100644
--- a/src/components/main/layout/block.rs
+++ b/src/components/main/layout/block.rs
@@ -4,11 +4,11 @@
//! CSS block layout.
-use layout::box::{RenderBox};
+use layout::box::{RenderBox, RenderBoxUtils};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
-use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow, FloatFlow, InlineFlow};
-use layout::inline::InlineLayout;
+use layout::flow::{BlockFlowClass, FlowClass, FlowContext, FlowData, ImmutableFlowUtils};
+use layout::flow;
use layout::model::{MaybeAuto, Specified, Auto};
use layout::float_context::{FloatContext, Invalid};
@@ -20,29 +20,29 @@ use gfx::display_list::DisplayList;
use servo_util::geometry::{Au, to_frac_px};
use servo_util::geometry;
-pub struct BlockFlowData {
+pub struct BlockFlow {
/// Data common to all flows.
- common: FlowData,
+ base: FlowData,
/// The associated render box.
- box: Option<RenderBox>,
+ box: Option<@RenderBox>,
/// Whether this block flow is the root flow.
is_root: bool
}
-impl BlockFlowData {
- pub fn new(common: FlowData) -> BlockFlowData {
- BlockFlowData {
- common: common,
+impl BlockFlow {
+ pub fn new(base: FlowData) -> BlockFlow {
+ BlockFlow {
+ base: base,
box: None,
is_root: false
}
}
- pub fn new_root(common: FlowData) -> BlockFlowData {
- BlockFlowData {
- common: common,
+ pub fn new_root(base: FlowData) -> BlockFlow {
+ BlockFlow {
+ base: base,
box: None,
is_root: true
}
@@ -54,78 +54,18 @@ impl BlockFlowData {
}
self.box = None;
}
-}
-
-pub trait BlockLayout {
- fn starts_root_flow(&self) -> bool;
- fn starts_block_flow(&self) -> bool;
-}
-
-impl BlockLayout for FlowContext {
- fn starts_root_flow(&self) -> bool {
- match *self {
- BlockFlow(ref info) => info.is_root,
- _ => false
- }
- }
-
- fn starts_block_flow(&self) -> bool {
- match *self {
- BlockFlow(*) | InlineBlockFlow(*) | FloatFlow(*) => true,
- _ => false
- }
- }
-}
-
-impl BlockFlowData {
- /* Recursively (bottom-up) determine the context's preferred and
- minimum widths. When called on this context, all child contexts
- have had their min/pref widths set. This function must decide
- min/pref widths based on child context widths and dimensions of
- any boxes it is responsible for flowing. */
-
- /* TODO: floats */
- /* TODO: absolute contexts */
- /* TODO: inline-blocks */
- pub fn bubble_widths_block(&mut self, ctx: &LayoutContext) {
- let mut min_width = Au(0);
- let mut pref_width = Au(0);
- let mut num_floats = 0;
-
- /* find max width from child block contexts */
- for child_ctx in self.common.child_iter() {
- assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
-
- do child_ctx.with_mut_base |child_node| {
- min_width = geometry::max(min_width, child_node.min_width);
- pref_width = geometry::max(pref_width, child_node.pref_width);
-
- num_floats = num_floats + child_node.num_floats;
- }
- }
-
- /* if not an anonymous block context, add in block box's widths.
- these widths will not include child elements, just padding etc. */
- for box in self.box.iter() {
- min_width = min_width.add(&box.get_min_width(ctx));
- pref_width = pref_width.add(&box.get_pref_width(ctx));
- }
- self.common.min_width = min_width;
- self.common.pref_width = pref_width;
- self.common.num_floats = num_floats;
- }
-
- /// Computes left and right margins and width based on CSS 2.1 secion 10.3.3.
- /// Requires borders and padding to already be computed
- fn compute_horiz( &self,
- width: MaybeAuto,
- left_margin: MaybeAuto,
- right_margin: MaybeAuto,
- available_width: Au) -> (Au, Au, Au) {
-
- //If width is not 'auto', and width + margins > available_width, all 'auto' margins are treated as '0'
- let (left_margin, right_margin) = match width{
+ /// Computes left and right margins and width based on CSS 2.1 section 10.3.3.
+ /// Requires borders and padding to already be computed.
+ fn compute_horiz(&self,
+ width: MaybeAuto,
+ left_margin: MaybeAuto,
+ right_margin: MaybeAuto,
+ available_width: Au)
+ -> (Au, Au, Au) {
+ // If width is not 'auto', and width + margins > available_width, all 'auto' margins are
+ // treated as 0.
+ let (left_margin, right_margin) = match width {
Auto => (left_margin, right_margin),
Specified(width) => {
let left = left_margin.specified_or_zero();
@@ -152,9 +92,9 @@ impl BlockFlowData {
(Specified(margin_l), Specified(width), Auto) => (margin_l, width, available_width - (margin_l + width)),
//If width is set to 'auto', any other 'auto' value becomes '0', and width is solved for
- (Auto, Auto, Specified(margin_r)) => (Au(0), available_width - margin_r, margin_r),
- (Specified(margin_l), Auto, Auto) => (margin_l, available_width - margin_l, Au(0)),
- (Auto, Auto, Auto) => (Au(0), available_width, Au(0)),
+ (Auto, Auto, Specified(margin_r)) => (Au::new(0), available_width - margin_r, margin_r),
+ (Specified(margin_l), Auto, Auto) => (margin_l, available_width - margin_l, Au::new(0)),
+ (Auto, Auto, Auto) => (Au::new(0), available_width, Au::new(0)),
//If left and right margins are auto, they become equal
(Auto, Specified(width), Auto) => {
@@ -167,126 +107,32 @@ impl BlockFlowData {
(width_Au, left_margin_Au, right_margin_Au)
}
- /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
- /// on this context, the context has had its width set by the parent context.
- ///
- /// Dual boxes consume some width first, and the remainder is assigned to all child (block)
- /// contexts.
- pub fn assign_widths_block(&mut self, ctx: &LayoutContext) {
- debug!("assign_widths_block: assigning width for flow %?", self.common.id);
- if self.is_root {
- debug!("Setting root position");
- self.common.position.origin = Au::zero_point();
- self.common.position.size.width = ctx.screen_size.size.width;
- self.common.floats_in = FloatContext::new(self.common.num_floats);
- self.common.is_inorder = false;
- }
-
- //position was set to the containing block by the flow's parent
- let mut remaining_width = self.common.position.size.width;
- let mut x_offset = Au(0);
-
- for &box in self.box.iter() {
- let style = box.style();
- do box.with_model |model| {
- //Can compute border width here since it doesn't depend on anything
- model.compute_borders(style);
-
- // Can compute padding here since we know containing block width.
- model.compute_padding(style, remaining_width);
-
- // Margins are 0 right now so model.noncontent_width() is just borders + padding.
- let available_width = remaining_width - model.noncontent_width();
-
- // Top and bottom margins for blocks are 0 if auto.
- let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
- remaining_width).specified_or_zero();
- let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
- remaining_width).specified_or_zero();
-
- let (width, margin_left, margin_right) =
- (MaybeAuto::from_style(style.Box.width, remaining_width),
- MaybeAuto::from_style(style.Margin.margin_left, remaining_width),
- MaybeAuto::from_style(style.Margin.margin_right, remaining_width));
-
- let (width, margin_left, margin_right) = self.compute_horiz(width,
- margin_left,
- margin_right,
- available_width);
-
- model.margin.top = margin_top;
- model.margin.right = margin_right;
- model.margin.bottom = margin_bottom;
- model.margin.left = margin_left;
-
- x_offset = model.offset();
- remaining_width = width;
- }
-
- do box.with_mut_base |base| {
- //The associated box is the border box of this flow
- base.position.origin.x = base.model.margin.left;
-
- let pb = base.model.padding.left + base.model.padding.right +
- base.model.border.left + base.model.border.right;
- base.position.size.width = remaining_width + pb;
- }
- }
-
- let has_inorder_children = self.common.is_inorder || self.common.num_floats > 0;
- for kid in self.common.child_iter() {
- assert!(kid.starts_block_flow() || kid.starts_inline_flow());
-
- do kid.with_mut_base |child_node| {
- child_node.position.origin.x = x_offset;
- child_node.position.size.width = remaining_width;
- child_node.is_inorder = has_inorder_children;
-
- if !child_node.is_inorder {
- child_node.floats_in = FloatContext::new(0);
- }
- }
- }
- }
-
- pub fn assign_height_inorder_block(&mut self, ctx: &mut LayoutContext) {
- debug!("assign_height_inorder_block: assigning height for block %?", self.common.id);
- self.assign_height_block_base(ctx, true);
- }
-
- pub fn assign_height_block(&mut self, ctx: &mut LayoutContext) {
- debug!("assign_height_block: assigning height for block %?", self.common.id);
- // This is the only case in which a block flow can start an inorder
- // subtraversal.
- if self.is_root && self.common.num_floats > 0 {
- self.assign_height_inorder_block(ctx);
- return;
- }
- self.assign_height_block_base(ctx, false);
- }
-
+ // inline(always) because this is only ever called by in-order or non-in-order top-level
+ // methods
+ #[inline(always)]
fn assign_height_block_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
- let mut cur_y = Au(0);
- let mut clearance = Au(0);
- let mut top_offset = Au(0);
- let mut bottom_offset = Au(0);
- let mut left_offset = Au(0);
+ let mut cur_y = Au::new(0);
+ let mut clearance = Au::new(0);
+ let mut top_offset = Au::new(0);
+ let mut bottom_offset = Au::new(0);
+ let mut left_offset = Au::new(0);
let mut float_ctx = Invalid;
for &box in self.box.iter() {
- clearance = match box.clear() {
- None => Au(0),
+ let base = box.base();
+ clearance = match base.clear() {
+ None => Au::new(0),
Some(clear) => {
- self.common.floats_in.clearance(clear)
+ self.base.floats_in.clearance(clear)
}
};
- do box.with_model |model| {
- top_offset = clearance + model.margin.top + model.border.top + model.padding.top;
- cur_y = cur_y + top_offset;
- bottom_offset = model.margin.bottom + model.border.bottom + model.padding.bottom;
- left_offset = model.offset();
- };
+ let mut model_ref = base.model.mutate();
+ let model = &mut model_ref.ptr;
+ top_offset = clearance + model.margin.top + model.border.top + model.padding.top;
+ cur_y = cur_y + top_offset;
+ bottom_offset = model.margin.bottom + model.border.bottom + model.padding.bottom;
+ left_offset = model.offset();
}
if inorder {
@@ -297,84 +143,48 @@ impl BlockFlowData {
// visit child[i]
// repeat until all children are visited.
// last_child.floats_out -> self.floats_out (done at the end of this method)
- float_ctx = self.common.floats_in.translate(Point2D(-left_offset, -top_offset));
- for kid in self.common.child_iter() {
- do kid.with_mut_base |child_node| {
- child_node.floats_in = float_ctx.clone();
- }
+ float_ctx = self.base.floats_in.translate(Point2D(-left_offset, -top_offset));
+ for kid in self.base.child_iter() {
+ flow::mut_base(*kid).floats_in = float_ctx.clone();
kid.assign_height_inorder(ctx);
- do kid.with_mut_base |child_node| {
- float_ctx = child_node.floats_out.clone();
- }
+ float_ctx = flow::mut_base(*kid).floats_out.clone();
}
}
- let mut collapsible = Au(0);
- let mut collapsing = Au(0);
- let mut margin_top = Au(0);
- let mut margin_bottom = Au(0);
+ let mut collapsible = Au::new(0);
+ let mut collapsing = Au::new(0);
+ let mut margin_top = Au::new(0);
+ let mut margin_bottom = Au::new(0);
let mut top_margin_collapsible = false;
let mut bottom_margin_collapsible = false;
- let mut first_inflow = true;
+ let mut first_in_flow = true;
for &box in self.box.iter() {
- do box.with_model |model| {
- if !self.is_root && model.border.top == Au(0) && model.padding.top == Au(0) {
- collapsible = model.margin.top;
- top_margin_collapsible = true;
- }
- if !self.is_root && model.border.bottom == Au(0) && model.padding.bottom == Au(0) {
- bottom_margin_collapsible = true;
- }
- margin_top = model.margin.top;
- margin_bottom = model.margin.bottom;
+ let base = box.base();
+ let mut model_ref = base.model.mutate();
+ let model = &mut model_ref.ptr;
+ if !self.is_root && model.border.top == Au::new(0) && model.padding.top == Au::new(0) {
+ collapsible = model.margin.top;
+ top_margin_collapsible = true;
}
- }
-
- for kid in self.common.child_iter() {
- match *kid {
- BlockFlow(ref info) => {
- for &box in info.box.iter() {
- do box.with_model |model| {
- // The top margin collapses with its first in-flow block-level child's
- // top margin if the parent has no top boder, no top padding.
- if first_inflow && top_margin_collapsible {
- // If top-margin of parent is less than top-margin of its first child,
- // the parent box goes down until its top is aligned with the child.
- if margin_top < model.margin.top {
- // TODO: The position of child floats should be updated and this
- // would influence clearance as well. See #725
- let extra_margin = model.margin.top - margin_top;
- top_offset = top_offset + extra_margin;
- margin_top = model.margin.top;
- }
- }
- // The bottom margin of an in-flow block-level element collapses
- // with the top margin of its next in-flow block-level sibling.
- collapsing = geometry::min(model.margin.top, collapsible);
- collapsible = model.margin.bottom;
- }
- }
- first_inflow = false;
- }
- InlineFlow(ref info) => {
- collapsing = Au(0);
- // Non-empty inline flows prevent collapsing between the previous margion and the next.
- if info.common.position.size.height > Au(0) {
- collapsible = Au(0);
- }
- }
- // Margins between a floated box and any other box do not collapse.
- _ => {
- collapsing = Au(0);
- }
- // TODO: Handling for AbsoluteFlow, InlineBlockFlow and TableFlow?
+ if !self.is_root && model.border.bottom == Au::new(0) && model.padding.bottom == Au::new(0) {
+ bottom_margin_collapsible = true;
}
+ margin_top = model.margin.top;
+ margin_bottom = model.margin.bottom;
+ }
- do kid.with_mut_base |child_node| {
- cur_y = cur_y - collapsing;
- child_node.position.origin.y = cur_y;
- cur_y = cur_y + child_node.position.size.height;
- };
+ for kid in self.base.child_iter() {
+ kid.collapse_margins(top_margin_collapsible,
+ &mut first_in_flow,
+ &mut margin_top,
+ &mut top_offset,
+ &mut collapsing,
+ &mut collapsible);
+
+ let child_node = flow::mut_base(*kid);
+ cur_y = cur_y - collapsing;
+ child_node.position.origin.y = cur_y;
+ cur_y = cur_y + child_node.position.size.height;
}
// The bottom margin collapses with its last in-flow block-level child's bottom margin
@@ -385,7 +195,7 @@ impl BlockFlowData {
}
collapsible
} else {
- Au(0)
+ Au::new(0)
};
// TODO: A box's own margins collapse if the 'min-height' property is zero, and it has neither
@@ -400,61 +210,67 @@ impl BlockFlowData {
};
for &box in self.box.iter() {
- let style = box.style();
- let maybe_height = MaybeAuto::from_style(style.Box.height, Au(0));
+ let base = box.base();
+ let style = base.style();
+ let maybe_height = MaybeAuto::from_style(style.Box.height, Au::new(0));
let maybe_height = maybe_height.specified_or_zero();
height = geometry::max(height, maybe_height);
}
- let mut noncontent_height = Au(0);
- for box in self.box.mut_iter() {
- do box.with_mut_base |base| {
- //The associated box is the border box of this flow
- base.model.margin.top = margin_top;
- base.model.margin.bottom = margin_bottom;
+ let mut noncontent_height = Au::new(0);
+ for box in self.box.iter() {
+ let base = box.base();
+ let mut model_ref = base.model.mutate();
+ let mut position_ref = base.position.mutate();
+ let (model, position) = (&mut model_ref.ptr, &mut position_ref.ptr);
+
+ // The associated box is the border box of this flow.
+ model.margin.top = margin_top;
+ model.margin.bottom = margin_bottom;
- base.position.origin.y = clearance + base.model.margin.top;
+ position.origin.y = clearance + model.margin.top;
- noncontent_height = base.model.padding.top + base.model.padding.bottom +
- base.model.border.top + base.model.border.bottom;
- base.position.size.height = height + noncontent_height;
+ noncontent_height = model.padding.top + model.padding.bottom + model.border.top +
+ model.border.bottom;
+ position.size.height = height + noncontent_height;
- noncontent_height = noncontent_height + clearance +
- base.model.margin.top + base.model.margin.bottom;
- }
+ noncontent_height = noncontent_height + clearance + model.margin.top +
+ model.margin.bottom;
}
//TODO(eatkinson): compute heights using the 'height' property.
- self.common.position.size.height = height + noncontent_height;
+ self.base.position.size.height = height + noncontent_height;
if inorder {
let extra_height = height - (cur_y - top_offset) + bottom_offset;
- self.common.floats_out = float_ctx.translate(Point2D(left_offset, -extra_height));
+ self.base.floats_out = float_ctx.translate(Point2D(left_offset, -extra_height));
} else {
- self.common.floats_out = self.common.floats_in.clone();
+ self.base.floats_out = self.base.floats_in.clone();
}
}
- pub fn build_display_list_block<E:ExtraDisplayListData>(&mut self,
- builder: &DisplayListBuilder,
- dirty: &Rect<Au>,
- list: &Cell<DisplayList<E>>)
- -> bool {
-
- if self.common.node.is_iframe_element() {
- let x = self.common.abs_position.x + do self.box.as_ref().map_default(Au(0)) |box| {
- box.with_model(|model| model.margin.left + model.border.left + model.padding.left)
+ pub fn build_display_list_block<E:ExtraDisplayListData>(
+ &mut self,
+ builder: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ list: &Cell<DisplayList<E>>)
+ -> bool {
+ if self.base.node.is_iframe_element() {
+ let x = self.base.abs_position.x + do self.box.map_default(Au::new(0)) |box| {
+ let model = box.base().model.get();
+ model.margin.left + model.border.left + model.padding.left
};
- let y = self.common.abs_position.y + do self.box.as_ref().map_default(Au(0)) |box| {
- box.with_model(|model| model.margin.top + model.border.top + model.padding.top)
+ let y = self.base.abs_position.y + do self.box.map_default(Au::new(0)) |box| {
+ let model = box.base().model.get();
+ model.margin.top + model.border.top + model.padding.top
};
- let w = self.common.position.size.width - do self.box.as_ref().map_default(Au(0)) |box| {
- box.with_model(|model| model.noncontent_width())
+ let w = self.base.position.size.width - do self.box.map_default(Au::new(0)) |box| {
+ box.base().model.get().noncontent_width()
};
- let h = self.common.position.size.height - do self.box.as_ref().map_default(Au(0)) |box| {
- box.with_model(|model| model.noncontent_height())
+ let h = self.base.position.size.height - do self.box.map_default(Au::new(0)) |box| {
+ box.base().model.get().noncontent_height()
};
- do self.common.node.with_mut_iframe_element |iframe_element| {
+ do self.base.node.with_mut_iframe_element |iframe_element| {
iframe_element.size.get_mut_ref().set_rect(Rect(Point2D(to_frac_px(x) as f32,
to_frac_px(y) as f32),
Size2D(to_frac_px(w) as f32,
@@ -462,7 +278,7 @@ impl BlockFlowData {
}
}
- let abs_rect = Rect(self.common.abs_position, self.common.position.size);
+ let abs_rect = Rect(self.base.abs_position, self.base.position.size);
if !abs_rect.intersects(dirty) {
return true;
}
@@ -471,18 +287,199 @@ impl BlockFlowData {
// add box that starts block context
for box in self.box.iter() {
- box.build_display_list(builder, dirty, &self.common.abs_position, list)
+ box.build_display_list(builder, dirty, &self.base.abs_position, list)
}
// TODO: handle any out-of-flow elements
- let this_position = self.common.abs_position;
- for child in self.common.child_iter() {
- do child.with_mut_base |base| {
- base.abs_position = this_position + base.position.origin;
- }
+ let this_position = self.base.abs_position;
+ for child in self.base.child_iter() {
+ let child_base = flow::mut_base(*child);
+ child_base.abs_position = this_position + child_base.position.origin;
}
false
}
}
+impl FlowContext for BlockFlow {
+ fn class(&self) -> FlowClass {
+ BlockFlowClass
+ }
+
+ fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
+ self
+ }
+
+ /* Recursively (bottom-up) determine the context's preferred and
+ minimum widths. When called on this context, all child contexts
+ have had their min/pref widths set. This function must decide
+ min/pref widths based on child context widths and dimensions of
+ any boxes it is responsible for flowing. */
+
+ /* TODO: floats */
+ /* TODO: absolute contexts */
+ /* TODO: inline-blocks */
+ fn bubble_widths(&mut self, _: &mut LayoutContext) {
+ let mut min_width = Au::new(0);
+ let mut pref_width = Au::new(0);
+ let mut num_floats = 0;
+
+ /* find max width from child block contexts */
+ for child_ctx in self.base.child_iter() {
+ assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
+
+ let child_base = flow::mut_base(*child_ctx);
+ min_width = geometry::max(min_width, child_base.min_width);
+ pref_width = geometry::max(pref_width, child_base.pref_width);
+
+ num_floats = num_floats + child_base.num_floats;
+ }
+
+ /* if not an anonymous block context, add in block box's widths.
+ these widths will not include child elements, just padding etc. */
+ for box in self.box.iter() {
+ {
+ // Can compute border width here since it doesn't depend on anything.
+ let base = box.base();
+ base.model.mutate().ptr.compute_borders(base.style())
+ }
+
+ let (this_minimum_width, this_preferred_width) = box.minimum_and_preferred_widths();
+ min_width = min_width + this_minimum_width;
+ pref_width = pref_width + this_preferred_width;
+ }
+
+ self.base.min_width = min_width;
+ self.base.pref_width = pref_width;
+ self.base.num_floats = num_floats;
+ }
+
+ /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
+ /// on this context, the context has had its width set by the parent context.
+ ///
+ /// Dual boxes consume some width first, and the remainder is assigned to all child (block)
+ /// contexts.
+ fn assign_widths(&mut self, ctx: &mut LayoutContext) {
+ debug!("assign_widths_block: assigning width for flow %?", self.base.id);
+ if self.is_root {
+ debug!("Setting root position");
+ self.base.position.origin = Au::zero_point();
+ self.base.position.size.width = ctx.screen_size.size.width;
+ self.base.floats_in = FloatContext::new(self.base.num_floats);
+ self.base.is_inorder = false;
+ }
+
+ //position was set to the containing block by the flow's parent
+ let mut remaining_width = self.base.position.size.width;
+ let mut x_offset = Au::new(0);
+
+ for &box in self.box.iter() {
+ let base = box.base();
+ let style = base.style();
+ let mut model_ref = base.model.mutate();
+ let model = &mut model_ref.ptr;
+
+ // Can compute padding here since we know containing block width.
+ model.compute_padding(style, remaining_width);
+
+ // Margins are 0 right now so model.noncontent_width() is just borders + padding.
+ let available_width = remaining_width - model.noncontent_width();
+
+ // Top and bottom margins for blocks are 0 if auto.
+ let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
+ remaining_width).specified_or_zero();
+ let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
+ remaining_width).specified_or_zero();
+
+ let (width, margin_left, margin_right) =
+ (MaybeAuto::from_style(style.Box.width, remaining_width),
+ MaybeAuto::from_style(style.Margin.margin_left, remaining_width),
+ MaybeAuto::from_style(style.Margin.margin_right, remaining_width));
+
+ let (width, margin_left, margin_right) = self.compute_horiz(width,
+ margin_left,
+ margin_right,
+ available_width);
+
+ model.margin.top = margin_top;
+ model.margin.right = margin_right;
+ model.margin.bottom = margin_bottom;
+ model.margin.left = margin_left;
+
+ x_offset = model.offset();
+ remaining_width = width;
+
+ //The associated box is the border box of this flow
+ let position_ref = base.position.mutate();
+ position_ref.ptr.origin.x = model.margin.left;
+ let padding_and_borders = model.padding.left + model.padding.right +
+ model.border.left + model.border.right;
+ position_ref.ptr.size.width = remaining_width + padding_and_borders;
+ }
+
+ let has_inorder_children = self.base.is_inorder || self.base.num_floats > 0;
+ for kid in self.base.child_iter() {
+ assert!(kid.starts_block_flow() || kid.starts_inline_flow());
+
+ let child_base = flow::mut_base(*kid);
+ child_base.position.origin.x = x_offset;
+ child_base.position.size.width = remaining_width;
+ child_base.is_inorder = has_inorder_children;
+
+ if !child_base.is_inorder {
+ child_base.floats_in = FloatContext::new(0);
+ }
+ }
+ }
+
+ fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
+ debug!("assign_height_inorder: assigning height for block %?", self.base.id);
+ self.assign_height_block_base(ctx, true);
+ }
+
+ fn assign_height(&mut self, ctx: &mut LayoutContext) {
+ debug!("assign_height: assigning height for block %?", self.base.id);
+ // This is the only case in which a block flow can start an inorder
+ // subtraversal.
+ if self.is_root && self.base.num_floats > 0 {
+ self.assign_height_inorder(ctx);
+ return;
+ }
+ self.assign_height_block_base(ctx, false);
+ }
+
+ fn collapse_margins(&mut self,
+ top_margin_collapsible: bool,
+ first_in_flow: &mut bool,
+ margin_top: &mut Au,
+ top_offset: &mut Au,
+ collapsing: &mut Au,
+ collapsible: &mut Au) {
+ for &box in self.box.iter() {
+ let base = box.base();
+ let mut model_ref = base.model.mutate();
+ let model = &mut model_ref.ptr;
+
+ // The top margin collapses with its first in-flow block-level child's
+ // top margin if the parent has no top border, no top padding.
+ if *first_in_flow && top_margin_collapsible {
+ // If top-margin of parent is less than top-margin of its first child,
+ // the parent box goes down until its top is aligned with the child.
+ if *margin_top < model.margin.top {
+ // TODO: The position of child floats should be updated and this
+ // would influence clearance as well. See #725
+ let extra_margin = model.margin.top - *margin_top;
+ *top_offset = *top_offset + extra_margin;
+ *margin_top = model.margin.top;
+ }
+ }
+ // The bottom margin of an in-flow block-level element collapses
+ // with the top margin of its next in-flow block-level sibling.
+ *collapsing = geometry::min(model.margin.top, *collapsible);
+ *collapsible = model.margin.bottom;
+ }
+
+ *first_in_flow = false;
+ }
+}
+
diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs
index 012fd5b681f..364b6c03f70 100644
--- a/src/components/main/layout/box.rs
+++ b/src/components/main/layout/box.rs
@@ -4,26 +4,27 @@
//! The `RenderBox` type, which represents the leaves of the layout tree.
-use css::node_style::StyledNode;
-use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
-use layout::float_context::{ClearType, ClearLeft, ClearRight, ClearBoth};
-use layout::model::{BoxModel, MaybeAuto};
-use layout::text;
-
-use std::cell::Cell;
-use std::cmp::ApproxEq;
-use std::managed;
-use std::num::Zero;
+use extra::url::Url;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass};
use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass};
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem};
use gfx::display_list::{TextDisplayItemClass};
use gfx::font::{FontStyle, FontWeight300};
-use servo_util::geometry::Au;
use gfx::text::text_run::TextRun;
use gfx::color::rgb;
+use script::dom::node::{AbstractNode, LayoutView};
+use servo_net::image::holder::ImageHolder;
+use servo_net::local_image_cache::LocalImageCache;
+use servo_util::geometry::Au;
+use servo_util::range::*;
+use servo_util::slot::Slot;
+use servo_util::tree::{TreeNodeRef, ElementLike};
+use std::cast;
+use std::cell::Cell;
+use std::cmp::ApproxEq;
+use std::num::Zero;
+use std::unstable::raw::Box;
use style::ComputedValues;
use style::computed_values::border_style;
use style::computed_values::clear;
@@ -35,19 +36,17 @@ use style::computed_values::position;
use style::computed_values::text_align;
use style::computed_values::text_decoration;
use style::computed_values::vertical_align;
-use script::dom::node::{AbstractNode, LayoutView};
-use servo_net::image::holder::ImageHolder;
-use servo_net::local_image_cache::LocalImageCache;
-use servo_util::range::*;
-use servo_util::tree::{TreeNodeRef, ElementLike};
-use extra::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:
+use css::node_style::StyledNode;
+use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
+use layout::float_context::{ClearType, ClearLeft, ClearRight, ClearBoth};
+use layout::model::{BoxModel, MaybeAuto};
+
+/// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In
+/// general, boxes do not have a simple correspondence with CSS boxes 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.
+/// * Several 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 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
@@ -61,27 +60,168 @@ use extra::url::Url;
/// 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.
-#[deriving(Clone)]
-pub enum RenderBox {
- GenericRenderBoxClass(@mut RenderBoxBase),
- ImageRenderBoxClass(@mut ImageRenderBox),
- TextRenderBoxClass(@mut TextRenderBox),
- UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox),
+pub trait RenderBox {
+ /// Returns the class of render box that this is.
+ fn class(&self) -> RenderBoxClass;
+
+ /// If this is an image render box, returns the underlying object. Fails otherwise.
+ ///
+ /// FIXME(pcwalton): Ugly. Replace with a real downcast operation.
+ fn as_image_render_box(@self) -> @ImageRenderBox {
+ fail!("as_text_render_box() called on a non-text-render-box")
+ }
+
+ /// If this is a text render box, returns the underlying object. Fails otherwise.
+ ///
+ /// FIXME(pcwalton): Ugly. Replace with a real downcast operation.
+ fn as_text_render_box(@self) -> @TextRenderBox {
+ fail!("as_text_render_box() called on a non-text-render-box")
+ }
+
+ /// If this is an unscanned text render box, returns the underlying object. Fails otherwise.
+ ///
+ /// FIXME(pcwalton): Ugly. Replace with a real downcast operation.
+ fn as_unscanned_text_render_box(@self) -> @UnscannedTextRenderBox {
+ fail!("as_unscanned_text_render_box() called on a non-unscanned-text-render-box")
+ }
+
+ /// Cleans up all memory associated with this render box.
+ fn teardown(&self) {}
+
+ /// Returns true if this element is an unscanned text box that consists entirely of whitespace.
+ fn is_whitespace_only(&self) -> bool {
+ false
+ }
+
+ /// 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, _: Au, _: bool) -> SplitBoxResult;
+
+ /// Determines whether this box can merge with another box.
+ fn can_merge_with_box(&self, _: &RenderBox) -> bool {
+ false
+ }
+
+ /// Returns the *minimum width* and *preferred width* of this render box as defined by CSS 2.1.
+ fn minimum_and_preferred_widths(&self) -> (Au, Au);
+
+ fn box_height(&self) -> Au;
+
+ /// Assigns the appropriate width.
+ fn assign_width(&self);
+
+ fn debug_str(&self) -> ~str {
+ ~"???"
+ }
+}
+
+impl Clone for @RenderBox {
+ fn clone(&self) -> @RenderBox {
+ *self
+ }
}
-impl RenderBox {
- pub fn teardown(&self) {
- match *self {
- TextRenderBoxClass(box) => box.teardown(),
- _ => ()
+// FIXME(pcwalton): These are botches and can be removed once Rust gets trait fields.
+
+pub trait RenderBoxUtils {
+ fn base<'a>(&'a self) -> &'a RenderBoxBase;
+
+ /// Returns true if this element is replaced content. This is true for images, form elements,
+ /// and so on.
+ fn is_replaced(&self) -> bool;
+
+ /// Returns true if this element can be split. This is true for text boxes.
+ fn can_split(&self) -> bool;
+
+ /// 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);
+
+ /// 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);
+
+ /// Adds the display items necessary to paint the background of this render box to the display
+ /// list if necessary.
+ fn paint_background_if_applicable<E:ExtraDisplayListData>(
+ &self,
+ list: &Cell<DisplayList<E>>,
+ absolute_bounds: &Rect<Au>);
+
+ /// Adds the display items necessary to paint the borders of this render box to a display list
+ /// if necessary.
+ fn paint_borders_if_applicable<E:ExtraDisplayListData>(
+ &self,
+ list: &Cell<DisplayList<E>>,
+ abs_bounds: &Rect<Au>);
+
+ /// 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<E:ExtraDisplayListData>(
+ &self,
+ _: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ offset: &Point2D<Au>,
+ list: &Cell<DisplayList<E>>);
+}
+
+pub trait RenderBoxRefUtils<'self> {
+ fn base(self) -> &'self RenderBoxBase;
+}
+
+/// A box that represents a generic render box.
+pub struct GenericRenderBox {
+ base: RenderBoxBase,
+}
+
+impl GenericRenderBox {
+ pub fn new(base: RenderBoxBase) -> GenericRenderBox {
+ GenericRenderBox {
+ base: base,
}
}
}
+impl RenderBox for GenericRenderBox {
+ fn class(&self) -> RenderBoxClass {
+ GenericRenderBoxClass
+ }
+
+ fn minimum_and_preferred_widths(&self) -> (Au, Au) {
+ let guessed_width = self.base.guess_width();
+ (guessed_width, guessed_width)
+ }
+
+ fn split_to_width(@self, _: Au, _: bool) -> SplitBoxResult {
+ CannotSplit(self as @RenderBox)
+ }
+
+ fn box_height(&self) -> Au {
+ Au::new(0)
+ }
+
+ fn assign_width(&self) {
+ // FIXME(pcwalton): This seems clownshoes; can we remove?
+ self.base.position.mutate().ptr.size.width = Au::from_px(45)
+ }
+}
+
/// A box that represents a (replaced content) image and its accompanying borders, shadows, etc.
pub struct ImageRenderBox {
base: RenderBoxBase,
- image: ImageHolder,
+ image: Slot<ImageHolder>,
}
impl ImageRenderBox {
@@ -91,13 +231,104 @@ impl ImageRenderBox {
ImageRenderBox {
base: base,
- image: ImageHolder::new(image_url, local_image_cache),
+ image: Slot::init(ImageHolder::new(image_url, local_image_cache)),
}
}
+
+ // Calculate the width of an image, accounting for the width attribute
+ // TODO: This could probably go somewhere else
+ pub fn image_width(&self) -> Au {
+ let attr_width: Option<int> = do self.base.node.with_imm_element |elt| {
+ match elt.get_attr("width") {
+ Some(width) => {
+ FromStr::from_str(width)
+ }
+ None => {
+ None
+ }
+ }
+ };
+
+ // TODO: Consult margins and borders?
+ let px_width = if attr_width.is_some() {
+ attr_width.unwrap()
+ } else {
+ self.image.mutate().ptr.get_size().unwrap_or(Size2D(0, 0)).width
+ };
+
+ Au::from_px(px_width)
+ }
+
+ // Calculate the height of an image, accounting for the height attribute
+ // TODO: This could probably go somewhere else
+ pub fn image_height(&self) -> Au {
+ let attr_height: Option<int> = do self.base.node.with_imm_element |elt| {
+ match elt.get_attr("height") {
+ Some(height) => {
+ FromStr::from_str(height)
+ }
+ None => {
+ None
+ }
+ }
+ };
+
+ // TODO: Consult margins and borders?
+ let px_height = if attr_height.is_some() {
+ attr_height.unwrap()
+ } else {
+ self.image.mutate().ptr.get_size().unwrap_or(Size2D(0, 0)).height
+ };
+
+ Au::from_px(px_height)
+ }
+
+ /// If this is an image render box, returns the underlying object. Fails otherwise.
+ ///
+ /// FIXME(pcwalton): Ugly. Replace with a real downcast operation.
+ fn as_image_render_box(@self) -> @ImageRenderBox {
+ self
+ }
+}
+
+impl RenderBox for ImageRenderBox {
+ fn class(&self) -> RenderBoxClass {
+ ImageRenderBoxClass
+ }
+
+ fn split_to_width(@self, _: Au, _: bool) -> SplitBoxResult {
+ CannotSplit(self as @RenderBox)
+ }
+
+ fn minimum_and_preferred_widths(&self) -> (Au, Au) {
+ let guessed_width = self.base.guess_width();
+ let image_width = self.image_width();
+ (guessed_width + image_width, guessed_width + image_width)
+ }
+
+ fn box_height(&self) -> Au {
+ let size = self.image.mutate().ptr.get_size();
+ let height = Au::from_px(size.unwrap_or(Size2D(0, 0)).height);
+ self.base.position.mutate().ptr.size.height = height;
+ debug!("box_height: found image height: %?", height);
+ height
+ }
+
+ fn assign_width(&self) {
+ let width = self.image_width();
+ self.base.position.mutate().ptr.size.width = width;
+ }
+
+ /// If this is an image render box, returns the underlying object. Fails otherwise.
+ ///
+ /// FIXME(pcwalton): Ugly. Replace with a real downcast operation.
+ fn as_image_render_box(@self) -> @ImageRenderBox {
+ self
+ }
}
/// 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
+/// into two or more 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 {
@@ -107,9 +338,168 @@ pub struct TextRenderBox {
}
impl TextRenderBox {
+ /// Creates a TextRenderBox from a base render box, a range, and a text run. The size of the
+ /// the base render box is ignored and becomes the size of the text run.
+ ///
+ /// FIXME(pcwalton): This API is confusing.
+ pub fn new(base: RenderBoxBase, run: @TextRun, range: Range) -> TextRenderBox {
+ debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun (%s) (len=%u)",
+ run.char_len(),
+ range.begin(),
+ range.length(),
+ *run.text.get(),
+ run.char_len());
+
+ assert!(range.begin() < run.char_len());
+ assert!(range.end() <= run.char_len());
+ assert!(range.length() > 0);
+
+ let metrics = run.metrics_for_range(&range);
+
+ // FIXME(pcwalton): This block is necessary due to Rust #6248. If we don't have it, then
+ // the "currently borrowed" flag will be moved before the destructor runs, causing a
+ // (harmless) undefined memory write and a (very harmful) sticking of `position` in the
+ // "mutably borrowed" state, which will cause failures later.
+ {
+ base.position.mutate().ptr.size = metrics.bounding_box.size;
+ }
+
+ TextRenderBox {
+ base: base,
+ run: run,
+ range: range,
+ }
+ }
+}
+
+impl RenderBox for TextRenderBox {
+ fn class(&self) -> RenderBoxClass {
+ TextRenderBoxClass
+ }
+
+ fn as_text_render_box(@self) -> @TextRenderBox {
+ self
+ }
+
fn teardown(&self) {
self.run.teardown();
}
+
+ fn minimum_and_preferred_widths(&self) -> (Au, Au) {
+ let guessed_width = self.base.guess_width();
+ let min_width = self.run.min_width_for_range(&self.range);
+
+ let mut max_line_width = Au::new(0);
+ for line_range in self.run.iter_natural_lines_for_range(&self.range) {
+ let line_metrics = self.run.metrics_for_range(&line_range);
+ max_line_width = Au::max(max_line_width, line_metrics.advance_width);
+ }
+
+ (guessed_width + min_width, guessed_width + max_line_width)
+ }
+
+ fn box_height(&self) -> Au {
+ let range = &self.range;
+ let run = &self.run;
+
+ // Compute the height based on the line-height and font size
+ let text_bounds = run.metrics_for_range(range).bounding_box;
+ let em_size = text_bounds.size.height;
+ let line_height = self.base.calculate_line_height(em_size);
+
+ line_height
+ }
+
+ fn assign_width(&self) {
+ // Text boxes are preinitialized.
+ }
+
+ /// 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, max_width: Au, starts_line: bool) -> SplitBoxResult {
+ let mut pieces_processed_count: uint = 0;
+ let mut remaining_width: Au = max_width;
+ let mut left_range = Range::new(self.range.begin(), 0);
+ let mut right_range: Option<Range> = None;
+
+ debug!("split_to_width: splitting text box (strlen=%u, range=%?, avail_width=%?)",
+ self.run.text.get().len(),
+ self.range,
+ max_width);
+
+ for (glyphs, offset, slice_range) in self.run.iter_slices_for_range(&self.range) {
+ debug!("split_to_width: considering slice (offset=%?, range=%?, remain_width=%?)",
+ offset,
+ slice_range,
+ remaining_width);
+
+ let metrics = self.run.metrics_for_slice(glyphs, &slice_range);
+ let advance = metrics.advance_width;
+ let should_continue: bool;
+
+ if advance <= remaining_width {
+ should_continue = true;
+
+ if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() {
+ debug!("split_to_width: case=skipping leading trimmable whitespace");
+ left_range.shift_by(slice_range.length() as int);
+ } else {
+ debug!("split_to_width: case=enlarging span");
+ remaining_width = remaining_width - advance;
+ left_range.extend_by(slice_range.length() as int);
+ }
+ } else { // The advance is more than the remaining width.
+ should_continue = false;
+ let slice_begin = offset + slice_range.begin();
+ let slice_end = offset + slice_range.end();
+
+ if glyphs.is_whitespace() {
+ // If there are still things after the trimmable whitespace, create the
+ // right chunk.
+ if slice_end < self.range.end() {
+ debug!("split_to_width: case=skipping trimmable trailing \
+ whitespace, then split remainder");
+ let right_range_end = self.range.end() - slice_end;
+ right_range = Some(Range::new(slice_end, right_range_end));
+ } else {
+ debug!("split_to_width: case=skipping trimmable trailing \
+ whitespace");
+ }
+ } else if slice_begin < self.range.end() {
+ // There are still some things left over at the end of the line. Create
+ // the right chunk.
+ let right_range_end = self.range.end() - slice_begin;
+ right_range = Some(Range::new(slice_begin, right_range_end));
+ debug!("split_to_width: case=splitting remainder with right range=%?",
+ right_range);
+ }
+ }
+
+ pieces_processed_count += 1;
+
+ if !should_continue {
+ break
+ }
+ }
+
+ let left_box = if left_range.length() > 0 {
+ let new_text_box = @TextRenderBox::new(self.base.clone(), self.run, left_range);
+ Some(new_text_box as @RenderBox)
+ } else {
+ None
+ };
+
+ let right_box = do right_range.map_default(None) |range: Range| {
+ let new_text_box = @TextRenderBox::new(self.base.clone(), self.run, range);
+ Some(new_text_box as @RenderBox)
+ };
+
+ if pieces_processed_count == 1 || left_box.is_none() {
+ SplitDidNotFit(left_box, right_box)
+ } else {
+ SplitDidFit(left_box, right_box)
+ }
+ }
}
/// The data for an unscanned text box.
@@ -133,40 +523,94 @@ impl UnscannedTextRenderBox {
// FIXME(pcwalton): If we're just looking at node data, do we have to ensure this is
// a text node?
UnscannedTextRenderBox {
- base: base,
+ base: base.clone(),
text: text_node.element.data.to_str(),
font_style: None,
text_decoration: None,
}
}
}
+
+ /// Copies out the text from an unscanned text box.
+ pub fn raw_text(&self) -> ~str {
+ self.text.clone()
+ }
}
-pub enum RenderBoxType {
- RenderBox_Generic,
- RenderBox_Image,
- RenderBox_Text,
+impl RenderBox for UnscannedTextRenderBox {
+ fn class(&self) -> RenderBoxClass {
+ UnscannedTextRenderBoxClass
+ }
+
+ fn is_whitespace_only(&self) -> bool {
+ self.text.is_whitespace()
+ }
+
+ fn can_merge_with_box(&self, other: &RenderBox) -> bool {
+ if other.class() == UnscannedTextRenderBoxClass {
+ let this_base = &self.base;
+ let other_base = other.base();
+ return this_base.font_style() == other_base.font_style() &&
+ this_base.text_decoration() == other_base.text_decoration()
+ }
+ false
+ }
+
+ fn box_height(&self) -> Au {
+ fail!("can't get height of unscanned text box")
+ }
+
+ /// 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, _: Au, _: bool) -> SplitBoxResult {
+ fail!("WAT: shouldn't be an unscanned text box here.")
+ }
+
+ /// Returns the *minimum width* and *preferred width* of this render box as defined by CSS 2.1.
+ fn minimum_and_preferred_widths(&self) -> (Au, Au) {
+ fail!("WAT: shouldn't be an unscanned text box here.")
+ }
+
+ fn assign_width(&self) {
+ fail!("WAT: shouldn't be an unscanned text box here.")
+ }
+
+ /// If this is an unscanned text render box, returns the underlying object. Fails otherwise.
+ ///
+ /// FIXME(pcwalton): Ugly. Replace with a real downcast operation.
+ fn as_unscanned_text_render_box(@self) -> @UnscannedTextRenderBox {
+ self
+ }
}
-/// Represents the outcome of attempting to split a render box.
+#[deriving(Eq)]
+pub enum RenderBoxClass {
+ GenericRenderBoxClass,
+ ImageRenderBoxClass,
+ TextRenderBoxClass,
+ UnscannedTextRenderBoxClass,
+}
+
+/// Represents the outcome of attempting to split a box.
pub enum SplitBoxResult {
- CannotSplit(RenderBox),
+ CannotSplit(@RenderBox),
// in general, when splitting the left or right side can
// be zero length, due to leading/trailing trimmable whitespace
- SplitDidFit(Option<RenderBox>, Option<RenderBox>),
- SplitDidNotFit(Option<RenderBox>, Option<RenderBox>)
+ SplitDidFit(Option<@RenderBox>, Option<@RenderBox>),
+ SplitDidNotFit(Option<@RenderBox>, Option<@RenderBox>)
}
-/// Data common to all render boxes.
+/// Data common to all boxes.
+#[deriving(Clone)]
pub struct RenderBoxBase {
/// The DOM node that this `RenderBox` originates from.
node: AbstractNode<LayoutView>,
/// The position of this box relative to its owning flow.
- position: Rect<Au>,
+ position: Slot<Rect<Au>>,
/// The core parameters (border, padding, margin) used by the box model.
- model: BoxModel,
+ model: Slot<BoxModel>,
/// A debug ID.
///
@@ -180,442 +624,307 @@ impl RenderBoxBase {
-> RenderBoxBase {
RenderBoxBase {
node: node,
- position: Au::zero_rect(),
- model: Zero::zero(),
+ position: Slot::init(Au::zero_rect()),
+ model: Slot::init(Zero::zero()),
id: id,
}
}
-}
-impl RenderBox {
- /// Borrows this render box immutably in order to work with its common data.
- #[inline(always)]
- pub fn with_base<R>(&self, callback: &fn(&RenderBoxBase) -> R) -> R {
- match *self {
- GenericRenderBoxClass(generic_box) => callback(generic_box),
- ImageRenderBoxClass(image_box) => {
- callback(&image_box.base)
- }
- TextRenderBoxClass(text_box) => {
- callback(&text_box.base)
- }
- UnscannedTextRenderBoxClass(unscanned_text_box) => {
- callback(&unscanned_text_box.base)
- }
- }
+ pub fn id(&self) -> int {
+ 0
}
- /// Borrows this render box mutably in order to work with its common data.
- #[inline(always)]
- pub fn with_mut_base<R>(&self, callback: &fn(&mut RenderBoxBase) -> R) -> R {
- match *self {
- GenericRenderBoxClass(generic_box) => callback(generic_box),
- ImageRenderBoxClass(image_box) => {
- callback(&mut image_box.base)
- }
- TextRenderBoxClass(text_box) => {
- callback(&mut text_box.base)
- }
- UnscannedTextRenderBoxClass(unscanned_text_box) => {
- callback(&mut unscanned_text_box.base)
- }
+ fn guess_width(&self) -> Au {
+ if !self.node.is_element() {
+ return Au(0)
}
+
+ let style = self.style();
+ let width = MaybeAuto::from_style(style.Box.width, Au::new(0)).specified_or_zero();
+ let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
+ Au::new(0)).specified_or_zero();
+ let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
+ Au::new(0)).specified_or_zero();
+
+ let mut model_ref = self.model.mutate();
+ let model = &mut model_ref.ptr;
+ let padding_left = model.compute_padding_length(style.Padding.padding_left, Au::new(0));
+ let padding_right = model.compute_padding_length(style.Padding.padding_right, Au::new(0));
+
+ width + margin_left + margin_right + padding_left + padding_right + model.border.left +
+ model.border.right
}
- /// A convenience function to return the position of this box.
- pub fn position(&self) -> Rect<Au> {
- do self.with_base |base| {
- base.position
+ pub fn calculate_line_height(&self, font_size: Au) -> Au {
+ match self.line_height() {
+ line_height::Normal => font_size.scale_by(1.14),
+ line_height::Number(l) => font_size.scale_by(l),
+ line_height::Length(l) => l
}
}
- /// A convenience function to return the debugging ID of this box.
- pub fn id(&self) -> int {
- do self.with_mut_base |base| {
- base.id
- }
+ pub fn compute_padding(&self, containing_block_width: Au) {
+ self.model.mutate().ptr.compute_padding(self.node.style(), containing_block_width);
}
- /// Returns true if this element is replaced content. This is true for images, form elements,
- /// and so on.
- pub fn is_replaced(&self) -> bool {
- match *self {
- ImageRenderBoxClass(*) => true,
- _ => false
- }
+ pub fn get_noncontent_width(&self) -> Au {
+ let model_ref = self.model.mutate();
+ model_ref.ptr.border.left + model_ref.ptr.padding.left + model_ref.ptr.border.right +
+ model_ref.ptr.padding.right
}
- /// Returns true if this element can be split. This is true for text boxes.
- pub fn can_split(&self) -> bool {
- match *self {
- TextRenderBoxClass(*) => true,
- _ => false
- }
+ /// The box formed by the content edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
+ /// the owning flow.
+ pub fn content_box(&self) -> Rect<Au> {
+ let (position, model) = (self.position.get(), self.model.get());
+ let origin = Point2D(position.origin.x + model.border.left + model.padding.left,
+ position.origin.y);
+ let size = Size2D(position.size.width - self.get_noncontent_width(), position.size.height);
+ Rect(origin, size)
}
- /// Returns true if this element is an unscanned text box that consists entirely of whitespace.
- pub fn is_whitespace_only(&self) -> bool {
- match *self {
- UnscannedTextRenderBoxClass(unscanned_text_box) => {
- unscanned_text_box.text.is_whitespace()
- }
- _ => false
- }
+ /// The box formed by the border edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
+ /// the owning flow.
+ pub fn border_box(&self) -> Rect<Au> {
+ // TODO: Actually compute the content box, padding, and border.
+ self.content_box()
}
- /// Determines whether this box can merge with another render box.
- pub fn can_merge_with_box(&self, other: RenderBox) -> bool {
- match (self, &other) {
- (&UnscannedTextRenderBoxClass(*), &UnscannedTextRenderBoxClass(*)) => {
- self.font_style() == other.font_style() && self.text_decoration() == other.text_decoration()
- },
- (&TextRenderBoxClass(text_box_a), &TextRenderBoxClass(text_box_b)) => {
- managed::ptr_eq(text_box_a.run, text_box_b.run)
+ /// The box formed by the margin edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
+ /// the owning flow.
+ pub fn margin_box(&self) -> Rect<Au> {
+ // TODO: Actually compute the content_box, padding, border, and margin.
+ self.content_box()
+ }
+
+ /// 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.
+ pub fn nearest_ancestor_element(&self) -> AbstractNode<LayoutView> {
+ let mut node = self.node;
+ while !node.is_element() {
+ match node.parent_node() {
+ None => fail!("no nearest element?!"),
+ Some(parent) => node = parent,
}
- (_, _) => false,
}
+ node
}
- /// Attempts to split this box so that its width is no more than `max_width`. Fails if this box
- /// is an unscanned text box.
- pub fn split_to_width(&self, max_width: Au, starts_line: bool)
- -> SplitBoxResult {
- match *self {
- GenericRenderBoxClass(*) | ImageRenderBoxClass(*) => CannotSplit(*self),
- UnscannedTextRenderBoxClass(*) => {
- fail!(~"WAT: shouldn't be an unscanned text box here.")
- }
+ // Always inline for SCCP.
+ #[inline(always)]
+ pub fn clear(&self) -> Option<ClearType> {
+ let style = self.node.style();
+ match style.Box.clear {
+ clear::none => None,
+ clear::left => Some(ClearLeft),
+ clear::right => Some(ClearRight),
+ clear::both => Some(ClearBoth),
+ }
+ }
- TextRenderBoxClass(text_box) => {
- let mut pieces_processed_count: uint = 0;
- let mut remaining_width: Au = max_width;
- let mut left_range = Range::new(text_box.range.begin(), 0);
- let mut right_range: Option<Range> = None;
-
- debug!("split_to_width: splitting text box (strlen=%u, range=%?, avail_width=%?)",
- text_box.run.text.len(),
- text_box.range,
- max_width);
-
- for (glyphs, offset, slice_range) in text_box.run.iter_slices_for_range(&text_box.range) {
- debug!("split_to_width: considering slice (offset=%?, range=%?, remain_width=%?)",
- offset,
- slice_range,
- remaining_width);
-
- let metrics = text_box.run.metrics_for_slice(glyphs, &slice_range);
- let advance = metrics.advance_width;
- let should_continue: bool;
-
- if advance <= remaining_width {
- should_continue = true;
-
- if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() {
- debug!("split_to_width: case=skipping leading trimmable whitespace");
- left_range.shift_by(slice_range.length() as int);
- } else {
- debug!("split_to_width: case=enlarging span");
- remaining_width = remaining_width - advance;
- left_range.extend_by(slice_range.length() as int);
- }
- } else { // The advance is more than the remaining width.
- should_continue = false;
- let slice_begin = offset + slice_range.begin();
- let slice_end = offset + slice_range.end();
-
- if glyphs.is_whitespace() {
- // If there are still things after the trimmable whitespace, create the
- // right chunk.
- if slice_end < text_box.range.end() {
- debug!("split_to_width: case=skipping trimmable trailing \
- whitespace, then split remainder");
- let right_range_end =
- text_box.range.end() - slice_end;
- right_range = Some(Range::new(slice_end, right_range_end));
- } else {
- debug!("split_to_width: case=skipping trimmable trailing \
- whitespace");
- }
- } else if slice_begin < text_box.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.range.end() - slice_begin;
- right_range = Some(Range::new(slice_begin, right_range_end));
- debug!("split_to_width: case=splitting remainder with right range=%?",
- right_range);
- }
- }
+ /// Converts this node's computed style to a font style used for rendering.
+ pub fn font_style(&self) -> FontStyle {
+ let my_style = self.nearest_ancestor_element().style();
- pieces_processed_count += 1;
+ debug!("(font style) start: %?", self.nearest_ancestor_element().type_id());
- if !should_continue {
- break
- }
- }
+ // FIXME: Too much allocation here.
+ let font_families = do my_style.Font.font_family.map |family| {
+ match *family {
+ FamilyName(ref name) => (*name).clone(),
+ }
+ };
+ let font_families = font_families.connect(", ");
+ debug!("(font style) font families: `%s`", font_families);
- let left_box = if left_range.length() > 0 {
- let new_text_box = @mut text::adapt_textbox_with_range(text_box.base,
- text_box.run,
- left_range);
- Some(TextRenderBoxClass(new_text_box))
- } else {
- None
- };
+ let font_size = my_style.Font.font_size.to_f64().unwrap() / 60.0;
+ debug!("(font style) font size: `%fpx`", font_size);
- 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.run,
- range);
- Some(TextRenderBoxClass(new_text_box))
- };
+ let (italic, oblique) = match my_style.Font.font_style {
+ font_style::normal => (false, false),
+ font_style::italic => (true, false),
+ font_style::oblique => (false, true),
+ };
- if pieces_processed_count == 1 || left_box.is_none() {
- SplitDidNotFit(left_box, right_box)
- } else {
- SplitDidFit(left_box, right_box)
- }
- }
+ FontStyle {
+ pt_size: font_size,
+ weight: FontWeight300,
+ italic: italic,
+ oblique: oblique,
+ families: font_families,
}
}
- /// Guess the intrinsic width of this box for
- /// computation of min and preferred widths.
- //
- // TODO(eatkinson): this is unspecified in
- // CSS 2.1, but we need to do something reasonable
- // here. What this function does currently is
- // NOT reasonable.
- //
- // TODO(eatkinson): integrate with
- // get_min_width and get_pref_width?
- fn guess_width (&self) -> Au {
- do self.with_base |base| {
- if(!base.node.is_element()) {
- Au(0)
- } else {
- let style = self.style();
- let width = MaybeAuto::from_style(style.Box.width,
- Au(0)).specified_or_zero();
- let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
- Au(0)).specified_or_zero();
- let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
- Au(0)).specified_or_zero();
- let padding_left = base.model.compute_padding_length(style.Padding.padding_left,
- Au(0));
- let padding_right = base.model.compute_padding_length(style.Padding.padding_right,
- Au(0));
- let border_left = style.Border.border_left_width;
- let border_right = style.Border.border_right_width;
-
- width + margin_left + margin_right + padding_left + padding_right +
- border_left + border_right
- }
- }
+ // FIXME(pcwalton): Why &'static??? Isn't that wildly unsafe?
+ #[inline(always)]
+ pub fn style(&self) -> &'static ComputedValues {
+ self.node.style()
}
- /// Returns the *minimum width* of this render box as defined by the CSS specification.
- pub fn get_min_width(&self, _: &LayoutContext) -> Au {
- // FIXME(pcwalton): I think we only need to calculate this if the damage says that CSS
- // needs to be restyled.
+ /// Returns the text alignment of the computed style of the nearest ancestor-or-self `Element`
+ /// node.
+ pub fn text_align(&self) -> text_align::T {
+ self.nearest_ancestor_element().style().Text.text_align
+ }
- self.guess_width() + match *self {
- // 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),
+ pub fn line_height(&self) -> line_height::T {
+ self.nearest_ancestor_element().style().Box.line_height
+ }
- ImageRenderBoxClass(image_box) => {
- self.image_width(image_box)
- }
+ pub fn vertical_align(&self) -> vertical_align::T {
+ self.nearest_ancestor_element().style().Box.vertical_align
+ }
- TextRenderBoxClass(text_box) => {
- text_box.run.min_width_for_range(&text_box.range)
+ /// Returns the text decoration of the computed style of the nearest `Element` node
+ pub fn text_decoration(&self) -> text_decoration::T {
+ /// Computes the propagated value of text-decoration, as specified in CSS 2.1 § 16.3.1
+ /// TODO: make sure this works with anonymous box generation.
+ fn get_propagated_text_decoration(element: AbstractNode<LayoutView>)
+ -> text_decoration::T {
+ //Skip over non-element nodes in the DOM
+ if !element.is_element() {
+ return match element.parent_node() {
+ None => text_decoration::none,
+ Some(parent) => get_propagated_text_decoration(parent),
+ };
}
- UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here.")
- }
- }
+ // FIXME: Implement correctly.
+ let display_in_flow = true;
- /// Returns the *preferred width* of this render box as defined by the CSS specification.
- pub fn get_pref_width(&self, _: &LayoutContext) -> Au {
- self.guess_width() + 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),
+ let position = element.style().Box.position;
+ let float = element.style().Box.float;
- ImageRenderBoxClass(image_box) => {
- self.image_width(image_box)
- }
+ let in_flow = (position == position::static_) && (float == float::none) &&
+ display_in_flow;
- TextRenderBoxClass(text_box) => {
- // 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 line_range in text_box.run.iter_natural_lines_for_range(&text_box.range) {
- let line_metrics = text_box.run.metrics_for_range(&line_range);
- max_line_width = Au::max(max_line_width, line_metrics.advance_width);
- }
+ let text_decoration = element.style().Text.text_decoration;
- max_line_width
+ if text_decoration == text_decoration::none && in_flow {
+ match element.parent_node() {
+ None => text_decoration::none,
+ Some(parent) => get_propagated_text_decoration(parent),
+ }
+ }
+ else {
+ text_decoration
}
-
- UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here."),
}
+ get_propagated_text_decoration(self.nearest_ancestor_element())
}
- // Calculate the width of an image, accounting for the width attribute
- // TODO: This could probably go somewhere else
- pub fn image_width(&self, image_box: @mut ImageRenderBox) -> Au {
- let attr_width: Option<int> = do self.with_base |base| {
- do base.node.with_imm_element |elt| {
- match elt.get_attr("width") {
- Some(width) => {
- FromStr::from_str(width)
- }
- None => {
- None
- }
- }
- }
- };
-
- // TODO: Consult margins and borders?
- let px_width = if attr_width.is_some() {
- attr_width.unwrap()
- } else {
- image_box.image.get_size().unwrap_or(Size2D(0, 0)).width
- };
+}
- Au::from_px(px_width)
+impl RenderBoxUtils for @RenderBox {
+ #[inline(always)]
+ fn base<'a>(&'a self) -> &'a RenderBoxBase {
+ unsafe {
+ let (_, box_ptr): (uint, *Box<RenderBoxBase>) = cast::transmute(*self);
+ cast::transmute(&(*box_ptr).data)
+ }
}
- // Calculate the height of an image, accounting for the height attribute
- // TODO: This could probably go somewhere else
- pub fn image_height(&self, image_box: @mut ImageRenderBox) -> Au {
- let attr_height: Option<int> = do self.with_base |base| {
- do base.node.with_imm_element |elt| {
- match elt.get_attr("height") {
- Some(height) => {
- FromStr::from_str(height)
- }
- None => {
- None
- }
- }
- }
- };
-
- // TODO: Consult margins and borders?
- let px_height = if attr_height.is_some() {
- attr_height.unwrap()
- } else {
- image_box.image.get_size().unwrap_or(Size2D(0, 0)).height
- };
+ fn is_replaced(&self) -> bool {
+ self.class() == ImageRenderBoxClass
+ }
- Au::from_px(px_height)
+ fn can_split(&self) -> bool {
+ self.class() == TextRenderBoxClass
}
/// Returns the amount of left and right "fringe" used by this box. This is based on margins,
/// borders, padding, and width.
- pub fn get_used_width(&self) -> (Au, Au) {
+ fn get_used_width(&self) -> (Au, Au) {
// TODO: This should actually do some computation! See CSS 2.1, Sections 10.3 and 10.4.
- (Au(0), Au(0))
+ (Au::new(0), Au::new(0))
}
/// Returns the amount of left and right "fringe" used by this box. This should be based on
/// margins, borders, padding, and width.
- pub fn get_used_height(&self) -> (Au, Au) {
+ fn get_used_height(&self) -> (Au, Au) {
// TODO: This should actually do some computation! See CSS 2.1, Sections 10.5 and 10.6.
- (Au(0), Au(0))
+ (Au::new(0), Au::new(0))
}
- pub fn compute_padding(&self, cb_width: Au) {
- do self.with_mut_base |base| {
- base.model.compute_padding(base.node.style(), cb_width);
- }
- }
+ /// Adds the display items necessary to paint the background of this render box to the display
+ /// list if necessary.
+ fn paint_background_if_applicable<E:ExtraDisplayListData>(
+ &self,
+ list: &Cell<DisplayList<E>>,
+ 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 render box".
+ let nearest_ancestor_element = self.base().nearest_ancestor_element();
- pub fn get_noncontent_width(&self) -> Au {
- do self.with_base |base| {
- base.model.border.left + base.model.padding.left +
- base.model.border.right + base.model.padding.right
- }
- }
+ let style = nearest_ancestor_element.style();
+ let background_color = style.resolve_color(style.Background.background_color);
+ if !background_color.alpha.approx_eq(&0.0) {
+ do list.with_mut_ref |list| {
+ let solid_color_display_item = ~SolidColorDisplayItem {
+ base: BaseDisplayItem {
+ bounds: *absolute_bounds,
+ extra: ExtraDisplayListData::new(self),
+ },
+ color: background_color.to_gfx_color(),
+ };
- pub fn with_model<R>(&self, callback: &fn(&mut BoxModel) -> R) -> R {
- do self.with_mut_base |base| {
- callback(&mut base.model)
+ list.append_item(SolidColorDisplayItemClass(solid_color_display_item))
+ }
}
}
- /// The box formed by the content edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
- /// the owning flow.
- pub fn content_box(&self) -> Rect<Au> {
- do self.with_base |base| {
- let origin = Point2D(base.position.origin.x +
- base.model.border.left +
- base.model.padding.left,
- base.position.origin.y);
- let size = Size2D(base.position.size.width - self.get_noncontent_width(),
- base.position.size.height);
- Rect(origin, size)
+ /// Adds the display items necessary to paint the borders of this render box to a display list
+ /// if necessary.
+ fn paint_borders_if_applicable<E:ExtraDisplayListData>(
+ &self,
+ list: &Cell<DisplayList<E>>,
+ abs_bounds: &Rect<Au>) {
+ // Fast path.
+ let base = self.base();
+ let border = base.model.get().border;
+ if border.is_zero() {
+ return
}
- }
- /// The box formed by the border edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
- /// the owning flow.
- pub fn border_box(&self) -> Rect<Au> {
- // TODO: Actually compute the content box, padding, and border.
- self.content_box()
- }
-
- /// The box formed by the margin edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
- /// the owning flow.
- pub fn margin_box(&self) -> Rect<Au> {
- // TODO: Actually compute the content_box, padding, border, and margin.
- self.content_box()
- }
-
- /// A convenience function to access the computed style of the DOM node that this render box
- /// represents.
- pub fn style(&self) -> &ComputedValues {
- self.with_base(|base| base.node.style())
- }
+ let style = base.style();
+ let top_color = style.resolve_color(style.Border.border_top_color);
+ let right_color = style.resolve_color(style.Border.border_right_color);
+ let bottom_color = style.resolve_color(style.Border.border_bottom_color);
+ let left_color = style.resolve_color(style.Border.border_left_color);
+ let top_style = style.Border.border_top_style;
+ let right_style = style.Border.border_right_style;
+ let bottom_style = style.Border.border_bottom_style;
+ let left_style = style.Border.border_left_style;
- /// A convenience function to access the DOM node that this render box represents.
- pub fn node(&self) -> AbstractNode<LayoutView> {
- self.with_base(|base| base.node)
- }
+ // Append the border to the display list.
+ do list.with_mut_ref |list| {
+ let border_display_item = ~BorderDisplayItem {
+ base: BaseDisplayItem {
+ bounds: *abs_bounds,
+ extra: ExtraDisplayListData::new(self),
+ },
+ border: SideOffsets2D::new(border.top,
+ border.right,
+ border.bottom,
+ border.left),
+ color: SideOffsets2D::new(top_color.to_gfx_color(),
+ right_color.to_gfx_color(),
+ bottom_color.to_gfx_color(),
+ left_color.to_gfx_color()),
+ style: SideOffsets2D::new(top_style,
+ right_style,
+ bottom_style,
+ left_style)
+ };
- /// 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.
- pub fn nearest_ancestor_element(&self) -> AbstractNode<LayoutView> {
- do self.with_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
+ list.append_item(BorderDisplayItemClass(border_display_item))
}
}
- //
- // Painting
- //
-
/// Adds the display items for this render box to the given display list.
///
/// Arguments:
@@ -630,12 +939,14 @@ impl RenderBox {
/// 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.
- pub fn build_display_list<E:ExtraDisplayListData>(&self,
- _: &DisplayListBuilder,
- dirty: &Rect<Au>,
- offset: &Point2D<Au>,
- list: &Cell<DisplayList<E>>) {
- let box_bounds = self.position();
+ fn build_display_list<E:ExtraDisplayListData>(
+ &self,
+ _: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ offset: &Point2D<Au>,
+ list: &Cell<DisplayList<E>>) {
+ let base = self.base();
+ let box_bounds = base.position.get();
let absolute_box_bounds = box_bounds.translate(offset);
debug!("RenderBox::build_display_list at rel=%?, abs=%?: %s",
box_bounds, absolute_box_bounds, self.debug_str());
@@ -648,14 +959,15 @@ impl RenderBox {
return;
}
- match *self {
- UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here."),
- TextRenderBoxClass(text_box) => {
+ match self.class() {
+ UnscannedTextRenderBoxClass => fail!("Shouldn't see unscanned boxes here."),
+ TextRenderBoxClass => {
+ let text_box = self.as_text_render_box();
// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);
- let nearest_ancestor_element = self.nearest_ancestor_element();
+ let nearest_ancestor_element = base.nearest_ancestor_element();
let color = nearest_ancestor_element.style().Color.color.to_gfx_color();
// Create the text box.
@@ -663,7 +975,7 @@ impl RenderBox {
let text_display_item = ~TextDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(*self),
+ extra: ExtraDisplayListData::new(self),
},
// FIXME(pcwalton): Allocation? Why?!
text_run: ~text_box.run.serialize(),
@@ -686,7 +998,7 @@ impl RenderBox {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(*self),
+ extra: ExtraDisplayListData::new(self),
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
@@ -708,7 +1020,7 @@ impl RenderBox {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: baseline,
- extra: ExtraDisplayListData::new(*self),
+ extra: ExtraDisplayListData::new(self),
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 200, 0)),
@@ -721,8 +1033,7 @@ impl RenderBox {
()
});
},
- GenericRenderBoxClass(_) => {
-
+ GenericRenderBoxClass => {
// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);
@@ -735,7 +1046,7 @@ impl RenderBox {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(*self),
+ extra: ExtraDisplayListData::new(self),
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
@@ -747,14 +1058,14 @@ impl RenderBox {
()
});
-
},
- ImageRenderBoxClass(image_box) => {
+ ImageRenderBoxClass => {
+ let image_box = self.as_image_render_box();
// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);
- match image_box.image.get_image() {
+ match image_box.image.mutate().ptr.get_image() {
Some(image) => {
debug!("(building display list) building image box");
@@ -763,7 +1074,7 @@ impl RenderBox {
let image_display_item = ~ImageDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(*self),
+ extra: ExtraDisplayListData::new(self),
},
image: image.clone(),
};
@@ -785,257 +1096,15 @@ impl RenderBox {
// TODO: Outlines.
self.paint_borders_if_applicable(list, &absolute_box_bounds);
}
+}
- /// Adds the display items necessary to paint the background of this render box to the display
- /// list if necessary.
- pub fn paint_background_if_applicable<E:ExtraDisplayListData>(&self,
- list: &Cell<DisplayList<E>>,
- 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 render box".
- let nearest_ancestor_element = self.nearest_ancestor_element();
-
- let style = nearest_ancestor_element.style();
- let background_color = style.resolve_color(style.Background.background_color);
- if !background_color.alpha.approx_eq(&0.0) {
- do list.with_mut_ref |list| {
- let solid_color_display_item = ~SolidColorDisplayItem {
- base: BaseDisplayItem {
- bounds: *absolute_bounds,
- extra: ExtraDisplayListData::new(*self),
- },
- color: background_color.to_gfx_color(),
- };
-
- list.append_item(SolidColorDisplayItemClass(solid_color_display_item))
- }
- }
- }
-
- pub fn clear(&self) -> Option<ClearType> {
- let style = self.style();
- match style.Box.clear {
- clear::none => None,
- clear::left => Some(ClearLeft),
- clear::right => Some(ClearRight),
- clear::both => Some(ClearBoth)
- }
- }
-
- /// Converts this node's computed style to a font style used for rendering.
- pub fn font_style(&self) -> FontStyle {
- fn get_font_style(element: AbstractNode<LayoutView>) -> FontStyle {
- let my_style = element.style();
-
- debug!("(font style) start: %?", element.type_id());
-
- // FIXME: Too much allocation here.
- let font_families = do my_style.Font.font_family.map |family| {
- match *family {
- FamilyName(ref name) => (*name).clone()
- }
- };
- let font_families = font_families.connect(", ");
- debug!("(font style) font families: `%s`", font_families);
-
- let font_size = my_style.Font.font_size.to_f64().unwrap() / 60.0;
- debug!("(font style) font size: `%fpx`", font_size);
-
- let (italic, oblique) = match my_style.Font.font_style {
- font_style::normal => (false, false),
- font_style::italic => (true, false),
- font_style::oblique => (false, true),
- };
-
- FontStyle {
- pt_size: font_size,
- weight: FontWeight300,
- italic: italic,
- oblique: oblique,
- families: font_families,
- }
- }
-
- let font_style_cached = match *self {
- UnscannedTextRenderBoxClass(ref box) => {
- match box.font_style {
- Some(ref style) => Some(style.clone()),
- None => None
- }
- }
- _ => None
- };
-
- if font_style_cached.is_some() {
- return font_style_cached.unwrap();
- } else {
- let font_style = get_font_style(self.nearest_ancestor_element());
- match *self {
- UnscannedTextRenderBoxClass(ref box) => {
- box.font_style = Some(font_style.clone());
- }
- _ => ()
- }
- return font_style;
- }
- }
-
- /// Returns the text alignment of the computed style of the nearest ancestor-or-self `Element`
- /// node.
- pub fn text_align(&self) -> text_align::T {
- self.nearest_ancestor_element().style().Text.text_align
- }
-
- pub fn line_height(&self) -> line_height::T {
- self.nearest_ancestor_element().style().Box.line_height
- }
-
- pub fn vertical_align(&self) -> vertical_align::T {
- self.nearest_ancestor_element().style().Box.vertical_align
- }
-
- /// Returns the text decoration of the computed style of the nearest `Element` node
- pub fn text_decoration(&self) -> text_decoration::T {
- /// Computes the propagated value of text-decoration, as specified in CSS 2.1 § 16.3.1
- /// TODO: make sure this works with anonymous box generation.
- fn get_propagated_text_decoration(element: AbstractNode<LayoutView>) -> text_decoration::T {
- //Skip over non-element nodes in the DOM
- if(!element.is_element()){
- return match element.parent_node() {
- None => text_decoration::none,
- Some(parent) => get_propagated_text_decoration(parent),
- };
- }
-
- //FIXME: Implement correctly
- let display_in_flow = true;
-
- let position = element.style().Box.position;
- let float = element.style().Box.float;
-
- let in_flow = (position == position::static_) && (float == float::none) &&
- display_in_flow;
-
- let text_decoration = element.style().Text.text_decoration;
-
- if text_decoration == text_decoration::none && in_flow {
- match element.parent_node() {
- None => text_decoration::none,
- Some(parent) => get_propagated_text_decoration(parent),
- }
- }
- else {
- text_decoration
- }
- }
-
- let text_decoration_cached = match *self {
- UnscannedTextRenderBoxClass(ref box) => {
- match box.text_decoration {
- Some(ref decoration) => Some(decoration.clone()),
- None => None
- }
- }
- _ => None
- };
-
- if text_decoration_cached.is_some() {
- return text_decoration_cached.unwrap();
- } else {
- let text_decoration = get_propagated_text_decoration(self.nearest_ancestor_element());
- match *self {
- UnscannedTextRenderBoxClass(ref box) => {
- box.text_decoration = Some(text_decoration.clone());
- }
- _ => ()
- }
- return text_decoration;
- }
- }
-
- /// Dumps this node, for debugging.
- pub fn dump(&self) {
- self.dump_indent(0);
- }
-
- /// Dumps a render box for debugging, with indentation.
- pub fn dump_indent(&self, indent: uint) {
- let mut string = ~"";
- for _ in range(0u, indent) {
- string.push_str(" ");
- }
-
- string.push_str(self.debug_str());
- debug!("%s", string);
- }
-
- /// Returns a debugging string describing this box.
- pub fn debug_str(&self) -> ~str {
- let representation = match *self {
- GenericRenderBoxClass(*) => ~"GenericRenderBox",
- ImageRenderBoxClass(*) => ~"ImageRenderBox",
- TextRenderBoxClass(text_box) => {
- fmt!("TextRenderBox(text=%s)", text_box.run.text.slice_chars(text_box.range.begin(),
- text_box.range.end()))
- }
- UnscannedTextRenderBoxClass(text_box) => {
- fmt!("UnscannedTextRenderBox(%s)", text_box.text)
- }
- };
-
- fmt!("box b%?: %s", self.id(), representation)
- }
-
- //
- // Painting
- //
-
- /// Adds the display items necessary to paint the borders of this render box to a display list
- /// if necessary.
- pub fn paint_borders_if_applicable<E:ExtraDisplayListData>(&self,
- list: &Cell<DisplayList<E>>,
- abs_bounds: &Rect<Au>) {
- // Fast path.
- let border = do self.with_base |base| {
- base.model.border
- };
- if border.is_zero() {
- return
- }
-
- let style = self.style();
- let top_color = style.resolve_color(style.Border.border_top_color);
- let right_color = style.resolve_color(style.Border.border_right_color);
- let bottom_color = style.resolve_color(style.Border.border_bottom_color);
- let left_color = style.resolve_color(style.Border.border_left_color);
- let top_style = style.Border.border_top_style;
- let right_style = style.Border.border_right_style;
- let bottom_style = style.Border.border_bottom_style;
- let left_style = style.Border.border_left_style;
- // Append the border to the display list.
- do list.with_mut_ref |list| {
- let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: *abs_bounds,
- extra: ExtraDisplayListData::new(*self),
- },
- border: SideOffsets2D::new(border.top,
- border.right,
- border.bottom,
- border.left),
- color: SideOffsets2D::new(top_color.to_gfx_color(),
- right_color.to_gfx_color(),
- bottom_color.to_gfx_color(),
- left_color.to_gfx_color()),
- style: SideOffsets2D::new(top_style,
- right_style,
- bottom_style,
- left_style)
- };
-
- list.append_item(BorderDisplayItemClass(border_display_item))
+impl<'self> RenderBoxRefUtils<'self> for &'self RenderBox {
+ #[inline(always)]
+ fn base(self) -> &'self RenderBoxBase {
+ unsafe {
+ let (_, box_ptr): (uint, *RenderBoxBase) = cast::transmute(self);
+ cast::transmute(box_ptr)
}
}
}
+
diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs
index 9686ddcb6cb..7afff453df0 100644
--- a/src/components/main/layout/box_builder.rs
+++ b/src/components/main/layout/box_builder.rs
@@ -4,16 +4,18 @@
//! Creates CSS boxes from a DOM tree.
-use layout::block::BlockFlowData;
-use layout::float::FloatFlowData;
-use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox};
-use layout::box::{RenderBoxBase, RenderBoxType, RenderBox_Generic, RenderBox_Image};
-use layout::box::{RenderBox_Text, UnscannedTextRenderBox, UnscannedTextRenderBoxClass};
+use layout::block::BlockFlow;
+use layout::float::FloatFlow;
+use layout::box::{GenericRenderBox, GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass};
+use layout::box::{RenderBox, RenderBoxBase, RenderBoxClass, RenderBoxUtils, TextRenderBoxClass};
+use layout::box::{UnscannedTextRenderBox, UnscannedTextRenderBoxClass};
use layout::context::LayoutContext;
-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, TableFlow};
-use layout::inline::{InlineFlowData, InlineLayout};
+use layout::float_context::FloatType;
+use layout::flow::{AbsoluteFlow, BlockFlowClass, FloatFlowClass, FlowContext, FlowData};
+use layout::flow::{ImmutableFlowUtils, InlineBlockFlow, InlineBlockFlowClass, InlineFlowClass};
+use layout::flow::{MutableFlowUtils, TableFlow};
+use layout::flow;
+use layout::inline::{InlineFlow};
use layout::text::TextRunScanner;
use css::node_style::StyledNode;
@@ -25,8 +27,19 @@ use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId};
use script::dom::node::DocumentFragmentNodeTypeId;
use servo_util::range::Range;
use servo_util::tree::{TreeNodeRef, TreeNode};
+use std::cast;
use std::cell::Cell;
+enum FlowType {
+ AbsoluteFlowType,
+ BlockFlowType,
+ FloatFlowType(FloatType),
+ InlineBlockFlowType,
+ InlineFlowType,
+ RootFlowType,
+ TableFlowType,
+}
+
pub struct LayoutTreeBuilder {
next_cid: int,
next_bid: int,
@@ -64,12 +77,15 @@ impl<'self> BoxGenerator<'self> {
}
}
- fn with_clone<R> (&mut self, cb: &fn(BoxGenerator<'self>) -> R) -> R {
- let gen = BoxGenerator {
- flow: &mut *self.flow,
- range_stack: self.range_stack
- };
- cb(gen)
+ fn with_clone<R>(&mut self, cb: &fn(BoxGenerator<'self>) -> R) -> R {
+ // FIXME(pcwalton): This is a hack; it can be done safely with linearity.
+ unsafe {
+ let gen = BoxGenerator {
+ flow: cast::transmute_copy(&self.flow),
+ range_stack: self.range_stack
+ };
+ cb(gen)
+ }
}
/* Whether "spacer" boxes are needed to stand in for this DOM node */
@@ -81,7 +97,7 @@ impl<'self> BoxGenerator<'self> {
fn make_inline_spacer_for_node_side(_: &LayoutContext,
_: AbstractNode<LayoutView>,
_: InlineSpacerSide)
- -> Option<RenderBox> {
+ -> Option<@RenderBox> {
None
}
@@ -89,17 +105,18 @@ impl<'self> BoxGenerator<'self> {
ctx: &LayoutContext,
node: AbstractNode<LayoutView>,
builder: &mut LayoutTreeBuilder) {
- debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.id(), node.debug_str());
+ debug!("BoxGenerator[f%d]: pushing node: %s", flow::base(self.flow).id, node.debug_str());
// TODO: remove this once UA styles work
let box_type = self.decide_box_type(node);
- debug!("BoxGenerator[f%d]: point a", self.flow.id());
+ debug!("BoxGenerator[f%d]: point a", flow::base(self.flow).id);
let range_stack = &mut self.range_stack;
// depending on flow, make a box for this node.
- match *self.flow {
- InlineFlow(ref mut inline) => {
+ match self.flow.class() {
+ InlineFlowClass => {
+ let inline = self.flow.as_inline();
let node_range_start = inline.boxes.len();
range_stack.push(node_range_start);
@@ -116,42 +133,43 @@ impl<'self> BoxGenerator<'self> {
}
// TODO: cases for inline-block, etc.
},
- BlockFlow(ref mut block) => {
- debug!("BoxGenerator[f%d]: point b", block.common.id);
+ BlockFlowClass => {
+ let block = self.flow.as_block();
+ debug!("BoxGenerator[f%d]: point b", block.base.id);
let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)",
- block.common.id,
- new_box.id(),
+ block.base.id,
+ new_box.base().id(),
node.debug_str());
assert!(block.box.is_none());
block.box = Some(new_box);
}
- FloatFlow(ref mut float) => {
- debug!("BoxGenerator[f%d]: point b", float.common.id);
+ FloatFlowClass => {
+ let float = self.flow.as_float();
+ debug!("BoxGenerator[f%d]: point b", float.base.id);
let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)",
- float.common.id,
- new_box.id(),
+ float.base.id,
+ new_box.base().id(),
node.debug_str());
assert!(float.box.is_none() && float.index.is_none());
float.box = Some(new_box);
}
- _ => warn!("push_node() not implemented for flow f%d", self.flow.id()),
+ _ => warn!("push_node() not implemented for flow f%d", flow::base(self.flow).id),
}
}
- pub fn pop_node(&mut self,
- ctx: &LayoutContext,
- node: AbstractNode<LayoutView>) {
- debug!("BoxGenerator[f%d]: popping node: %s", self.flow.id(), node.debug_str());
+ pub fn pop_node(&mut self, ctx: &LayoutContext, node: AbstractNode<LayoutView>) {
+ debug!("BoxGenerator[f%d]: popping node: %s", flow::base(self.flow).id, node.debug_str());
- match *self.flow {
- InlineFlow(ref mut inline) => {
+ match self.flow.class() {
+ InlineFlowClass => {
+ let inline = self.flow.as_inline();
let inline = &mut *inline;
if BoxGenerator::inline_spacers_needed_for_node(node) {
@@ -173,24 +191,26 @@ impl<'self> BoxGenerator<'self> {
debug!("BoxGenerator: adding element range=%?", node_range);
inline.elems.add_mapping(node, &node_range);
},
- BlockFlow(*) => assert!(self.range_stack.len() == 0),
- FloatFlow(*) => assert!(self.range_stack.len() == 0),
- _ => warn!("pop_node() not implemented for flow %?", self.flow.id()),
+ BlockFlowClass => assert!(self.range_stack.len() == 0),
+ FloatFlowClass => assert!(self.range_stack.len() == 0),
+ _ => warn!("pop_node() not implemented for flow %?", flow::base(self.flow).id),
}
}
/// Disambiguate between different methods here instead of inlining, since each case has very
/// different complexity.
fn make_box(layout_ctx: &LayoutContext,
- ty: RenderBoxType,
+ ty: RenderBoxClass,
node: AbstractNode<LayoutView>,
builder: &mut LayoutTreeBuilder)
- -> RenderBox {
+ -> @RenderBox {
let base = RenderBoxBase::new(node, builder.next_box_id());
let result = match ty {
- RenderBox_Generic => GenericRenderBoxClass(@mut base),
- RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)),
- RenderBox_Image => BoxGenerator::make_image_box(layout_ctx, node, base),
+ GenericRenderBoxClass => @GenericRenderBox::new(base) as @RenderBox,
+ TextRenderBoxClass | UnscannedTextRenderBoxClass => {
+ @UnscannedTextRenderBox::new(base) as @RenderBox
+ }
+ ImageRenderBoxClass => BoxGenerator::make_image_box(layout_ctx, node, base),
};
debug!("BoxGenerator: created box: %s", result.debug_str());
result
@@ -199,36 +219,36 @@ impl<'self> BoxGenerator<'self> {
fn make_image_box(layout_ctx: &LayoutContext,
node: AbstractNode<LayoutView>,
base: RenderBoxBase)
- -> RenderBox {
+ -> @RenderBox {
assert!(node.is_image_element());
do node.with_imm_image_element |image_element| {
if image_element.image.is_some() {
// FIXME(pcwalton): Don't copy URLs.
let url = (*image_element.image.get_ref()).clone();
- ImageRenderBoxClass(@mut ImageRenderBox::new(base, url, layout_ctx.image_cache))
+ @ImageRenderBox::new(base.clone(), url, layout_ctx.image_cache) as @RenderBox
} else {
info!("Tried to make image box, but couldn't find image. Made generic box \
instead.");
- GenericRenderBoxClass(@mut base)
+ @GenericRenderBox::new(base.clone()) as @RenderBox
}
}
}
- fn decide_box_type(&self, node: AbstractNode<LayoutView>) -> RenderBoxType {
+ fn decide_box_type(&self, node: AbstractNode<LayoutView>) -> RenderBoxClass {
if node.is_text() {
- RenderBox_Text
+ TextRenderBoxClass
} else if node.is_image_element() {
do node.with_imm_image_element |image_element| {
match image_element.image {
- Some(_) => RenderBox_Image,
- None => RenderBox_Generic,
+ Some(_) => ImageRenderBoxClass,
+ None => GenericRenderBoxClass,
}
}
} else if node.is_element() {
- RenderBox_Generic
+ GenericRenderBoxClass
} else {
- fail!(~"Hey, doctypes and comments shouldn't get here! They are display:none!")
+ fail!("Hey, doctypes and comments shouldn't get here! They are display:none!")
}
}
@@ -261,13 +281,14 @@ impl LayoutTreeBuilder {
/// Creates necessary box(es) and flow context(s) for the current DOM node,
/// and recurses on its children.
- pub fn construct_recursively<'a>(&mut self,
- layout_ctx: &LayoutContext,
- cur_node: AbstractNode<LayoutView>,
- mut grandparent_generator: Option<BoxGenerator<'a>>,
- mut parent_generator: BoxGenerator<'a>,
- mut prev_sibling_generator: Option<BoxGenerator<'a>>)
- -> BoxConstructResult<'a> {
+ pub fn construct_recursively<'a>(
+ &mut self,
+ layout_ctx: &LayoutContext,
+ cur_node: AbstractNode<LayoutView>,
+ mut grandparent_generator: Option<BoxGenerator<'a>>,
+ mut parent_generator: BoxGenerator<'a>,
+ mut prev_sibling_generator: Option<BoxGenerator<'a>>)
+ -> BoxConstructResult<'a> {
debug!("Considering node: %s", cur_node.debug_str());
let box_gen_result = {
let grandparent_gen_ref = match grandparent_generator {
@@ -285,32 +306,29 @@ impl LayoutTreeBuilder {
debug!("result from generator_for_node: %?", &box_gen_result);
// Skip over nodes that don't belong in the flow tree
- let (this_generator, next_generator) =
- match box_gen_result {
- NoGenerator => return Normal(prev_sibling_generator),
+ let (this_generator, next_generator) = match box_gen_result {
+ NoGenerator => return Normal(prev_sibling_generator),
+ ParentGenerator => {
+ do parent_generator.with_clone |clone| {
+ (clone, None)
+ }
+ }
+ SiblingGenerator => (prev_sibling_generator.take_unwrap(), None),
+ NewGenerator(gen) => (gen, None),
+ ReparentingGenerator(gen) => {
+ reparent = true;
+ (gen, None)
+ }
+ Mixed(gen, next_gen) => (gen, Some(match *next_gen {
ParentGenerator => {
do parent_generator.with_clone |clone| {
- (clone, None)
+ clone
}
}
- SiblingGenerator => (prev_sibling_generator.take_unwrap(), None),
- NewGenerator(gen) => (gen, None),
- ReparentingGenerator(gen) => {
- reparent = true;
- (gen, None)
- }
- Mixed(gen, next_gen) => (gen, Some(match *next_gen {
- ParentGenerator => {
- do parent_generator.with_clone |clone| {
- clone
- }
- }
- SiblingGenerator => prev_sibling_generator.take_unwrap(),
- _ => fail!("Unexpect BoxGenResult")
- }))
- };
-
-
+ SiblingGenerator => prev_sibling_generator.take_unwrap(),
+ _ => fail!("Unexpect BoxGenResult")
+ }))
+ };
let mut this_generator = this_generator;
@@ -395,7 +413,12 @@ impl LayoutTreeBuilder {
}
};
- let sibling_flow: Option<&mut FlowContext> = sibling_generator.as_mut().map(|gen| &mut *gen.flow);
+ // FIXME(pcwalton): Unsafe.
+ let sibling_flow: Option<&mut FlowContext> = sibling_generator.as_mut().map(|gen| {
+ unsafe {
+ cast::transmute_copy(&gen.flow)
+ }
+ });
let is_float = if (node.is_element()) {
match node.style().Box.float {
@@ -406,85 +429,100 @@ impl LayoutTreeBuilder {
} else {
None
};
-
- let new_generator = match (display, &mut parent_generator.flow, sibling_flow) {
+ let sibling_flow_class = match sibling_flow {
+ None => None,
+ Some(flow) => Some(flow.class()),
+ };
+
+ let new_generator = match (display, parent_generator.flow.class(), sibling_flow_class) {
// Floats
- (display::block, & &BlockFlow(_), _) |
- (display::block, & &FloatFlow(_), _) if is_float.is_some() => {
- self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap()))
+ (display::block, BlockFlowClass, _) |
+ (display::block, FloatFlowClass, _) if is_float.is_some() => {
+ self.create_child_generator(node,
+ parent_generator,
+ FloatFlowType(is_float.unwrap()))
}
// If we're placing a float after an inline, append the float to the inline flow,
// then continue building from the inline flow in case there are more inlines
// afterward.
- (display::block, _, Some(&InlineFlow(_))) if is_float.is_some() => {
+ (display::block, _, Some(InlineFlowClass)) if is_float.is_some() => {
+ let float_type = FloatFlowType(is_float.unwrap());
let float_generator = self.create_child_generator(node,
sibling_generator.unwrap(),
- Flow_Float(is_float.unwrap()));
+ float_type);
return Mixed(float_generator, ~SiblingGenerator);
}
// This is a catch-all case for when:
// a) sibling_flow is None
// b) sibling_flow is a BlockFlow
- (display::block, & &InlineFlow(_), _) if is_float.is_some() => {
- self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap()))
+ (display::block, InlineFlowClass, _) if is_float.is_some() => {
+ self.create_child_generator(node,
+ parent_generator,
+ FloatFlowType(is_float.unwrap()))
}
- (display::block, & &BlockFlow(ref info), _) => match (info.is_root, node.parent_node().is_some()) {
- // If this is the root node, then use the root flow's
- // context. Otherwise, make a child block context.
- (true, true) => self.create_child_generator(node, parent_generator, Flow_Block),
- (true, false) => { return ParentGenerator }
- (false, _) => {
- self.create_child_generator(node, parent_generator, Flow_Block)
+ (display::block, BlockFlowClass, _) => {
+ match (parent_generator.flow.as_block().is_root, node.parent_node()) {
+ // If this is the root node, then use the root flow's
+ // context. Otherwise, make a child block context.
+ (true, Some(_)) => {
+ self.create_child_generator(node, parent_generator, BlockFlowType)
+ }
+ (true, None) => return ParentGenerator,
+ (false, _) => {
+ self.create_child_generator(node, parent_generator, BlockFlowType)
+ }
}
- },
+ }
- (display::block, & &FloatFlow(*), _) => {
- self.create_child_generator(node, parent_generator, Flow_Block)
+ (display::block, FloatFlowClass, _) => {
+ self.create_child_generator(node, parent_generator, BlockFlowType)
}
// Inlines that are children of inlines are part of the same flow
- (display::inline, & &InlineFlow(*), _) => return ParentGenerator,
- (display::inline_block, & &InlineFlow(*), _) => return ParentGenerator,
+ (display::inline, InlineFlowClass, _) => return ParentGenerator,
+ (display::inline_block, InlineFlowClass, _) => return ParentGenerator,
// Inlines that are children of blocks create new flows if their
// previous sibling was a block.
- (display::inline, & &BlockFlow(*), Some(&BlockFlow(*))) |
- (display::inline_block, & &BlockFlow(*), Some(&BlockFlow(*))) => {
- self.create_child_generator(node, parent_generator, Flow_Inline)
+ (display::inline, BlockFlowClass, Some(BlockFlowClass)) |
+ (display::inline_block, BlockFlowClass, Some(BlockFlowClass)) => {
+ self.create_child_generator(node, parent_generator, InlineFlowType)
}
// The first two cases should only be hit when a FloatFlow
// is the first child of a BlockFlow. Other times, we will
- (display::inline, _, Some(&FloatFlow(*))) |
- (display::inline_block, _, Some(&FloatFlow(*))) |
- (display::inline, & &FloatFlow(*), _) |
- (display::inline_block, & &FloatFlow(*), _) => {
- self.create_child_generator(node, parent_generator, Flow_Inline)
+ (display::inline, _, Some(FloatFlowClass)) |
+ (display::inline_block, _, Some(FloatFlowClass)) |
+ (display::inline, FloatFlowClass, _) |
+ (display::inline_block, FloatFlowClass, _) => {
+ self.create_child_generator(node, parent_generator, InlineFlowType)
}
// Inlines whose previous sibling was not a block try to use their
// sibling's flow context.
- (display::inline, & &BlockFlow(*), _) |
- (display::inline_block, & &BlockFlow(*), _) => {
+ (display::inline, BlockFlowClass, _) |
+ (display::inline_block, BlockFlowClass, _) => {
return match sibling_generator {
None => NewGenerator(self.create_child_generator(node,
parent_generator,
- Flow_Inline)),
+ InlineFlowType)),
Some(*) => SiblingGenerator
}
}
// blocks that are children of inlines need to split their parent
// flows.
- (display::block, & &InlineFlow(*), _) => {
+ (display::block, InlineFlowClass, _) => {
match grandparent_generator {
None => fail!("expected to have a grandparent block flow"),
Some(grandparent_gen) => {
assert!(grandparent_gen.flow.is_block_like());
- let block_gen = self.create_child_generator(node, grandparent_gen, Flow_Block);
+ let block_gen = self.create_child_generator(node,
+ grandparent_gen,
+ BlockFlowType);
return ReparentingGenerator(block_gen);
}
}
@@ -496,15 +534,16 @@ impl LayoutTreeBuilder {
NewGenerator(new_generator)
}
- pub fn create_child_generator<'a>(&mut self,
- node: AbstractNode<LayoutView>,
- parent_generator: &mut BoxGenerator<'a>,
- ty: FlowContextType)
- -> BoxGenerator<'a> {
-
+ pub fn create_child_generator<'a>(
+ &mut self,
+ node: AbstractNode<LayoutView>,
+ parent_generator: &mut BoxGenerator<'a>,
+ ty: FlowType)
+ -> BoxGenerator<'a> {
let new_flow = self.make_flow(ty, node);
parent_generator.flow.add_new_child(new_flow);
- BoxGenerator::new(parent_generator.flow.last_child().unwrap())
+ let flow_ref = flow::last_child(parent_generator.flow).unwrap();
+ BoxGenerator::new(*flow_ref)
}
/// Fix up any irregularities such as:
@@ -516,15 +555,15 @@ impl LayoutTreeBuilder {
/// The latter can only be done immediately adjacent to, or at the beginning or end of a block
/// flow. Otherwise, the whitespace might affect whitespace collapsing with adjacent text.
pub fn simplify_children_of_flow(&self, ctx: &LayoutContext, parent_flow: &mut FlowContext) {
- match *parent_flow {
- InlineFlow(*) => {
+ match parent_flow.class() {
+ InlineFlowClass => {
let mut found_child_inline = false;
let mut found_child_block = false;
- for child_ctx in parent_flow.child_iter() {
- match child_ctx {
- &InlineFlow(*) | &InlineBlockFlow(*) => found_child_inline = true,
- &BlockFlow(*) => found_child_block = true,
+ for child_ctx in flow::child_iter(parent_flow) {
+ match child_ctx.class() {
+ InlineFlowClass | InlineBlockFlowClass => found_child_inline = true,
+ BlockFlowClass => found_child_block = true,
_ => {}
}
}
@@ -532,23 +571,27 @@ impl LayoutTreeBuilder {
if found_child_block && found_child_inline {
self.fixup_split_inline(parent_flow)
}
- },
- BlockFlow(*) | FloatFlow(*) => {
+ }
+ BlockFlowClass | FloatFlowClass => {
// check first/last child for whitespace-ness
let mut do_remove = false;
- let p_id = parent_flow.id();
+ let p_id = flow::base(parent_flow).id;
do parent_flow.with_first_child |mut first_child| {
for first_flow in first_child.mut_iter() {
if first_flow.starts_inline_flow() {
// FIXME: workaround for rust#6393
{
- let boxes = &first_flow.imm_inline().boxes;
- if boxes.len() == 1 && boxes[0].is_whitespace_only() {
- debug!("LayoutTreeBuilder: pruning whitespace-only first child \
- flow f%d from parent f%d",
- first_flow.id(),
- p_id);
- do_remove = true;
+ let first_inline_flow = first_flow.as_inline();
+ let boxes = &first_inline_flow.boxes;
+ if boxes.len() == 1 {
+ let first_box = boxes[0]; // FIXME(pcwalton): Rust bug
+ if first_box.is_whitespace_only() {
+ debug!("LayoutTreeBuilder: pruning whitespace-only first \
+ child flow f%d from parent f%d",
+ first_inline_flow.base.id,
+ p_id);
+ do_remove = true;
+ }
}
}
}
@@ -560,19 +603,23 @@ impl LayoutTreeBuilder {
do_remove = false;
- let p_id = parent_flow.id();
+ let p_id = flow::base(parent_flow).id;
do parent_flow.with_last_child |mut last_child| {
for last_flow in last_child.mut_iter() {
if last_flow.starts_inline_flow() {
// FIXME: workaround for rust#6393
{
- let boxes = &last_flow.imm_inline().boxes;
+ let last_inline_flow = last_flow.as_inline();
+ let boxes = &last_inline_flow.boxes;
if boxes.len() == 1 && boxes.last().is_whitespace_only() {
- debug!("LayoutTreeBuilder: pruning whitespace-only last child \
- flow f%d from parent f%d",
- last_flow.id(),
- p_id);
- do_remove = true;
+ let last_box = boxes.last(); // FIXME(pcwalton): Rust bug
+ if last_box.is_whitespace_only() {
+ debug!("LayoutTreeBuilder: pruning whitespace-only last \
+ child flow f%d from parent f%d",
+ last_inline_flow.base.id,
+ p_id);
+ do_remove = true;
+ }
}
}
}
@@ -584,16 +631,16 @@ impl LayoutTreeBuilder {
// Issue 543: We only need to do this if there are inline child
// flows, but there's not a quick way to check at the moment.
- for child_flow in (*parent_flow).child_iter() {
- match *child_flow {
- InlineFlow(*) | InlineBlockFlow(*) => {
+ for child_flow in flow::child_iter(parent_flow) {
+ match child_flow.class() {
+ InlineFlowClass | InlineBlockFlowClass => {
let mut scanner = TextRunScanner::new();
- scanner.scan_for_runs(ctx, child_flow);
+ scanner.scan_for_runs(ctx, *child_flow);
}
_ => {}
}
}
- },
+ }
_ => {}
}
}
@@ -605,29 +652,30 @@ impl LayoutTreeBuilder {
/// Entry point for box creation. Should only be called on the root DOM element.
pub fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode<LayoutView>)
- -> Result<FlowContext, ()> {
+ -> Result<~FlowContext:, ()> {
debug!("Constructing flow tree for DOM: ");
- root.dump();
+ debug!("%?", root.dump());
- let mut new_flow = self.make_flow(Flow_Root, root);
+ let mut new_flow = self.make_flow(RootFlowType, root);
{
- let new_generator = BoxGenerator::new(&mut new_flow);
+ let new_generator = BoxGenerator::new(new_flow);
self.construct_recursively(layout_ctx, root, None, new_generator, None);
}
return Ok(new_flow)
}
/// Creates a flow of the given type for the supplied node.
- pub fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode<LayoutView>) -> FlowContext {
+ pub fn make_flow(&mut self, flow_type: FlowType, node: AbstractNode<LayoutView>)
+ -> ~FlowContext: {
let info = FlowData::new(self.next_flow_id(), node);
- let result = match ty {
- Flow_Absolute => AbsoluteFlow(~info),
- Flow_Block => BlockFlow(~BlockFlowData::new(info)),
- Flow_Float(f_type) => FloatFlow(~FloatFlowData::new(info, f_type)),
- Flow_InlineBlock => InlineBlockFlow(~info),
- Flow_Inline => InlineFlow(~InlineFlowData::new(info)),
- Flow_Root => BlockFlow(~BlockFlowData::new_root(info)),
- Flow_Table => TableFlow(~info),
+ let result = match flow_type {
+ AbsoluteFlowType => ~AbsoluteFlow::new(info) as ~FlowContext:,
+ BlockFlowType => ~BlockFlow::new(info) as ~FlowContext:,
+ FloatFlowType(f_type) => ~FloatFlow::new(info, f_type) as ~FlowContext:,
+ InlineBlockFlowType => ~InlineBlockFlow::new(info) as ~FlowContext:,
+ InlineFlowType => ~InlineFlow::new(info) as ~FlowContext:,
+ RootFlowType => ~BlockFlow::new_root(info) as ~FlowContext:,
+ TableFlowType => ~TableFlow::new(info) as ~FlowContext:,
};
debug!("LayoutTreeBuilder: created flow: %s", result.debug_str());
result
diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs
index 5dc6a7ebec2..ac0cfaf716a 100644
--- a/src/components/main/layout/display_list_builder.rs
+++ b/src/components/main/layout/display_list_builder.rs
@@ -4,7 +4,7 @@
//! Constructs display lists from render boxes.
-use layout::box::RenderBox;
+use layout::box::{RenderBox, RenderBoxUtils};
use layout::context::LayoutContext;
use std::cast::transmute;
use script::dom::node::AbstractNode;
@@ -16,28 +16,28 @@ use style;
/// that nodes in this view shoud not really be touched. The idea is to
/// store the nodes in the display list and have layout transmute them.
pub trait ExtraDisplayListData {
- fn new(box: RenderBox) -> Self;
+ fn new(box: &@RenderBox) -> Self;
}
pub type Nothing = ();
impl ExtraDisplayListData for AbstractNode<()> {
- fn new (box: RenderBox) -> AbstractNode<()> {
+ fn new(box: &@RenderBox) -> AbstractNode<()> {
unsafe {
- transmute(box.node())
+ transmute(box.base().node)
}
}
}
impl ExtraDisplayListData for Nothing {
- fn new(_: RenderBox) -> Nothing {
+ fn new(_: &@RenderBox) -> Nothing {
()
}
}
-impl ExtraDisplayListData for RenderBox {
- fn new(box: RenderBox) -> RenderBox {
- box
+impl ExtraDisplayListData for @RenderBox {
+ fn new(box: &@RenderBox) -> @RenderBox {
+ *box
}
}
diff --git a/src/components/main/layout/float.rs b/src/components/main/layout/float.rs
index 857f03dc4c5..df97ebf9112 100644
--- a/src/components/main/layout/float.rs
+++ b/src/components/main/layout/float.rs
@@ -2,10 +2,11 @@
* 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/. */
-use layout::box::{RenderBox};
+use layout::box::{RenderBox, RenderBoxUtils};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
-use layout::flow::{FlowData};
+use layout::flow::{FloatFlowClass, FlowClass, FlowContext, FlowData};
+use layout::flow;
use layout::model::{MaybeAuto};
use layout::float_context::{FloatContext, PlacementInfo, FloatType};
@@ -16,12 +17,12 @@ use gfx::display_list::DisplayList;
use servo_util::geometry::Au;
use servo_util::geometry;
-pub struct FloatFlowData {
+pub struct FloatFlow {
/// Data common to all flows.
- common: FlowData,
+ base: FlowData,
/// The associated render box.
- box: Option<RenderBox>,
+ box: Option<@RenderBox>,
containing_width: Au,
@@ -39,10 +40,10 @@ pub struct FloatFlowData {
}
-impl FloatFlowData {
- pub fn new(common: FlowData, float_type: FloatType) -> FloatFlowData {
- FloatFlowData {
- common: common,
+impl FloatFlow {
+ pub fn new(base: FlowData, float_type: FloatType) -> FloatFlow {
+ FloatFlow {
+ base: base,
containing_width: Au(0),
box: None,
index: None,
@@ -59,119 +60,157 @@ impl FloatFlowData {
self.box = None;
self.index = None;
}
+
+ pub fn build_display_list_float<E:ExtraDisplayListData>(&mut self,
+ builder: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ list: &Cell<DisplayList<E>>)
+ -> bool {
+ //TODO: implement iframe size messaging
+ if self.base.node.is_iframe_element() {
+ error!("float iframe size messaging not implemented yet");
+ }
+ let abs_rect = Rect(self.base.abs_position, self.base.position.size);
+ if !abs_rect.intersects(dirty) {
+ return true;
+ }
+
+
+ let offset = self.base.abs_position + self.rel_pos;
+ // add box that starts block context
+ for box in self.box.iter() {
+ box.build_display_list(builder, dirty, &offset, list)
+ }
+
+
+ // TODO: handle any out-of-flow elements
+
+ // go deeper into the flow tree
+ for child in self.base.child_iter() {
+ let child_base = flow::mut_base(*child);
+ child_base.abs_position = offset + child_base.position.origin;
+ }
+
+ false
+ }
}
-impl FloatFlowData {
- pub fn bubble_widths_float(&mut self, ctx: &LayoutContext) {
+impl FlowContext for FloatFlow {
+ fn class(&self) -> FlowClass {
+ FloatFlowClass
+ }
+
+ fn as_float<'a>(&'a mut self) -> &'a mut FloatFlow {
+ self
+ }
+
+ fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
let mut num_floats = 0;
- for child_ctx in self.common.child_iter() {
+ for child_ctx in self.base.child_iter() {
//assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
- do child_ctx.with_mut_base |child_node| {
- min_width = geometry::max(min_width, child_node.min_width);
- pref_width = geometry::max(pref_width, child_node.pref_width);
- num_floats = num_floats + child_node.num_floats;
- }
+ let child_base = flow::mut_base(*child_ctx);
+ min_width = geometry::max(min_width, child_base.min_width);
+ pref_width = geometry::max(pref_width, child_base.pref_width);
+ num_floats = num_floats + child_base.num_floats;
}
- self.common.num_floats = 1;
+ self.base.num_floats = 1;
self.floated_children = num_floats;
-
for box in self.box.iter() {
- let style = box.style();
- do box.with_model |model| {
- model.compute_borders(style)
+ {
+ let base = box.base();
+ base.model.mutate().ptr.compute_borders(base.style());
}
- min_width = min_width.add(&box.get_min_width(ctx));
- pref_width = pref_width.add(&box.get_pref_width(ctx));
+ let (this_minimum_width, this_preferred_width) = box.minimum_and_preferred_widths();
+ min_width = min_width + this_minimum_width;
+ pref_width = pref_width + this_preferred_width;
}
- self.common.min_width = min_width;
- self.common.pref_width = pref_width;
+ self.base.min_width = min_width;
+ self.base.pref_width = pref_width;
}
- pub fn assign_widths_float(&mut self) {
- debug!("assign_widths_float: assigning width for flow %?", self.common.id);
+ fn assign_widths(&mut self, _: &mut LayoutContext) {
+ debug!("assign_widths_float: assigning width for flow %?", self.base.id);
// position.size.width is set by parent even though we don't know
// position.origin yet.
- let mut remaining_width = self.common.position.size.width;
+ let mut remaining_width = self.base.position.size.width;
self.containing_width = remaining_width;
let mut x_offset = Au(0);
// Parent usually sets this, but floats are never inorder
- self.common.is_inorder = false;
+ self.base.is_inorder = false;
for &box in self.box.iter() {
- let style = box.style();
- do box.with_model |model| {
- // Can compute padding here since we know containing block width.
- model.compute_padding(style, remaining_width);
+ let base = box.base();
+ let style = base.style();
+ let mut position_ref = base.position.mutate();
+ let mut model_ref = base.model.mutate();
+ let (position, model) = (&mut position_ref.ptr, &mut model_ref.ptr);
- // Margins for floats are 0 if auto.
- let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
- remaining_width).specified_or_zero();
- let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
- remaining_width).specified_or_zero();
- let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
- remaining_width).specified_or_zero();
- let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
- remaining_width).specified_or_zero();
+ // Can compute padding here since we know containing block width.
+ model.compute_padding(style, remaining_width);
+ // Margins for floats are 0 if auto.
+ let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
+ remaining_width).specified_or_zero();
+ let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
+ remaining_width).specified_or_zero();
+ let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
+ remaining_width).specified_or_zero();
+ let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
+ remaining_width).specified_or_zero();
- let shrink_to_fit = geometry::min(self.common.pref_width,
- geometry::max(self.common.min_width,
- remaining_width));
+ let shrink_to_fit = geometry::min(self.base.pref_width,
+ geometry::max(self.base.min_width, remaining_width));
- let width = MaybeAuto::from_style(style.Box.width,
- remaining_width).specified_or_default(shrink_to_fit);
- debug!("assign_widths_float -- width: %?", width);
+ let width = MaybeAuto::from_style(style.Box.width,
+ remaining_width).specified_or_default(shrink_to_fit);
+ debug!("assign_widths_float -- width: %?", width);
- model.margin.top = margin_top;
- model.margin.right = margin_right;
- model.margin.bottom = margin_bottom;
- model.margin.left = margin_left;
+ model.margin.top = margin_top;
+ model.margin.right = margin_right;
+ model.margin.bottom = margin_bottom;
+ model.margin.left = margin_left;
- x_offset = model.offset();
- remaining_width = width;
- }
+ x_offset = model.offset();
+ remaining_width = width;
- do box.with_mut_base |base| {
- //The associated box is the border box of this flow
- base.position.origin.x = base.model.margin.left;
+ // The associated box is the border box of this flow.
+ position.origin.x = model.margin.left;
- let pb = base.model.padding.left + base.model.padding.right +
- base.model.border.left + base.model.border.right;
- base.position.size.width = remaining_width + pb;
- }
+ let padding_and_borders = model.padding.left + model.padding.right +
+ model.border.left + model.border.right;
+ position.size.width = remaining_width + padding_and_borders;
}
- self.common.position.size.width = remaining_width;
+ self.base.position.size.width = remaining_width;
- let has_inorder_children = self.common.num_floats > 0;
- for kid in self.common.child_iter() {
+ let has_inorder_children = self.base.num_floats > 0;
+ for kid in self.base.child_iter() {
//assert!(kid.starts_block_flow() || kid.starts_inline_flow());
- do kid.with_mut_base |child_node| {
- child_node.position.origin.x = x_offset;
- child_node.position.size.width = remaining_width;
- child_node.is_inorder = has_inorder_children;
+ let child_base = flow::mut_base(*kid);
+ child_base.position.origin.x = x_offset;
+ child_base.position.size.width = remaining_width;
+ child_base.is_inorder = has_inorder_children;
- if !child_node.is_inorder {
- child_node.floats_in = FloatContext::new(0);
- }
+ if !child_base.is_inorder {
+ child_base.floats_in = FloatContext::new(0);
}
}
}
- pub fn assign_height_inorder_float(&mut self) {
- debug!("assign_height_inorder_float: assigning height for float %?", self.common.id);
+ fn assign_height_inorder(&mut self, _: &mut LayoutContext) {
+ debug!("assign_height_inorder_float: assigning height for float %?", self.base.id);
// assign_height_float was already called by the traversal function
// so this is well-defined
@@ -181,28 +220,23 @@ impl FloatFlowData {
let mut margin_height = Au(0);
for box in self.box.iter() {
- height = do box.with_base |base| {
- base.position.size.height
- };
- clearance = match box.clear() {
+ let base = box.base();
+ height = base.position.borrow().ptr.size.height;
+ clearance = match base.clear() {
None => Au(0),
- Some(clear) => {
- self.common.floats_in.clearance(clear)
- }
+ Some(clear) => self.base.floats_in.clearance(clear),
};
- do box.with_base |base| {
- let noncontent_width = base.model.padding.left + base.model.padding.right +
- base.model.border.left + base.model.border.right;
-
- full_noncontent_width = noncontent_width + base.model.margin.left + base.model.margin.right;
- margin_height = base.model.margin.top + base.model.margin.bottom;
- }
+ let model = base.model.get();
+ let noncontent_width = model.padding.left + model.padding.right + model.border.left +
+ model.border.right;
+ full_noncontent_width = noncontent_width + model.margin.left + model.margin.right;
+ margin_height = model.margin.top + model.margin.bottom;
}
let info = PlacementInfo {
- width: self.common.position.size.width + full_noncontent_width,
+ width: self.base.position.size.width + full_noncontent_width,
height: height + margin_height,
ceiling: clearance,
max_width: self.containing_width,
@@ -211,23 +245,19 @@ impl FloatFlowData {
// Place the float and return the FloatContext back to the parent flow.
// After, grab the position and use that to set our position.
- self.common.floats_out = self.common.floats_in.add_float(&info);
- self.rel_pos = self.common.floats_out.last_float_pos();
+ self.base.floats_out = self.base.floats_in.add_float(&info);
+ self.rel_pos = self.base.floats_out.last_float_pos();
}
- pub fn assign_height_float(&mut self, ctx: &mut LayoutContext) {
- debug!("assign_height_float: assigning height for float %?", self.common.id);
- let has_inorder_children = self.common.num_floats > 0;
+ fn assign_height(&mut self, ctx: &mut LayoutContext) {
+ debug!("assign_height_float: assigning height for float %?", self.base.id);
+ let has_inorder_children = self.base.num_floats > 0;
if has_inorder_children {
let mut float_ctx = FloatContext::new(self.floated_children);
- for kid in self.common.child_iter() {
- do kid.with_mut_base |child_node| {
- child_node.floats_in = float_ctx.clone();
- }
+ for kid in self.base.child_iter() {
+ flow::mut_base(*kid).floats_in = float_ctx.clone();
kid.assign_height_inorder(ctx);
- do kid.with_mut_base |child_node| {
- float_ctx = child_node.floats_out.clone();
- }
+ float_ctx = flow::mut_base(*kid).floats_out.clone();
}
}
@@ -235,83 +265,54 @@ impl FloatFlowData {
let mut top_offset = Au(0);
for &box in self.box.iter() {
- do box.with_model |model| {
- top_offset = model.margin.top + model.border.top + model.padding.top;
- cur_y = cur_y + top_offset;
- }
+ let base = box.base();
+ let model_ref = base.model.borrow();
+ top_offset = model_ref.ptr.margin.top + model_ref.ptr.border.top +
+ model_ref.ptr.padding.top;
+ cur_y = cur_y + top_offset;
}
- for kid in self.common.child_iter() {
- do kid.with_mut_base |child_node| {
- child_node.position.origin.y = cur_y;
- cur_y = cur_y + child_node.position.size.height;
- };
+ for kid in self.base.child_iter() {
+ let child_base = flow::mut_base(*kid);
+ child_base.position.origin.y = cur_y;
+ cur_y = cur_y + child_base.position.size.height;
}
let mut height = cur_y - top_offset;
- let mut noncontent_width = Au(0);
- let mut noncontent_height = Au(0);
- for box in self.box.mut_iter() {
- do box.with_mut_base |base| {
- //The associated box is the border box of this flow
- base.position.origin.y = base.model.margin.top;
+ let mut noncontent_height;
+ for box in self.box.iter() {
+ let base = box.base();
+ let mut model_ref = base.model.mutate();
+ let mut position_ref = base.position.mutate();
+ let (model, position) = (&mut model_ref.ptr, &mut position_ref.ptr);
- noncontent_width = base.model.padding.left + base.model.padding.right +
- base.model.border.left + base.model.border.right;
- noncontent_height = base.model.padding.top + base.model.padding.bottom +
- base.model.border.top + base.model.border.bottom;
- base.position.size.height = height + noncontent_height;
+ // The associated box is the border box of this flow.
+ position.origin.y = model.margin.top;
- }
- }
+ noncontent_height = model.padding.top + model.padding.bottom + model.border.top +
+ model.border.bottom;
- //TODO(eatkinson): compute heights properly using the 'height' property.
- for &box in self.box.iter() {
- let height_prop =
- MaybeAuto::from_style(box.style().Box.height,
- Au(0)).specified_or_zero();
+ //TODO(eatkinson): compute heights properly using the 'height' property.
+ let height_prop = MaybeAuto::from_style(base.style().Box.height,
+ Au::new(0)).specified_or_zero();
height = geometry::max(height, height_prop) + noncontent_height;
debug!("assign_height_float -- height: %?", height);
- do box.with_mut_base |base| {
- base.position.size.height = height;
- }
- }
- }
-
- pub fn build_display_list_float<E:ExtraDisplayListData>(&mut self,
- builder: &DisplayListBuilder,
- dirty: &Rect<Au>,
- list: &Cell<DisplayList<E>>)
- -> bool {
-
- //TODO: implement iframe size messaging
- if self.common.node.is_iframe_element() {
- error!("float iframe size messaging not implemented yet");
- }
- let abs_rect = Rect(self.common.abs_position, self.common.position.size);
- if !abs_rect.intersects(dirty) {
- return true;
- }
-
-
- let offset = self.common.abs_position + self.rel_pos;
- // add box that starts block context
- for box in self.box.iter() {
- box.build_display_list(builder, dirty, &offset, list)
- }
-
- // TODO: handle any out-of-flow elements
- // go deeper into the flow tree
- for child in self.common.child_iter() {
- do child.with_mut_base |base| {
- base.abs_position = offset + base.position.origin;
- }
+ position.size.height = height;
}
+ }
- false
+ fn collapse_margins(&mut self,
+ _: bool,
+ _: &mut bool,
+ _: &mut Au,
+ _: &mut Au,
+ collapsing: &mut Au,
+ _: &mut Au) {
+ // Margins between a floated box and any other box do not collapse.
+ *collapsing = Au::new(0);
}
}
diff --git a/src/components/main/layout/float_context.rs b/src/components/main/layout/float_context.rs
index e9f39586081..5786cf369ae 100644
--- a/src/components/main/layout/float_context.rs
+++ b/src/components/main/layout/float_context.rs
@@ -11,7 +11,7 @@ use std::vec;
use std::i32::max_value;
#[deriving(Clone)]
-pub enum FloatType{
+pub enum FloatType {
FloatLeft,
FloatRight
}
@@ -22,11 +22,12 @@ pub enum ClearType {
ClearBoth
}
-struct FloatContextBase{
- float_data: ~[Option<FloatData>],
+struct FloatContextBase {
+ /// This is an option of a vector to avoid allocation in the fast path (no floats).
+ float_data: Option<~[Option<FloatData>]>,
floats_used: uint,
- max_y : Au,
- offset: Point2D<Au>
+ max_y: Au,
+ offset: Point2D<Au>,
}
#[deriving(Clone)]
@@ -48,12 +49,12 @@ pub struct PlacementInfo{
/// destroy the context on modification.
pub enum FloatContext {
Invalid,
- Valid(~FloatContextBase)
+ Valid(FloatContextBase)
}
impl FloatContext {
pub fn new(num_floats: uint) -> FloatContext {
- Valid(~FloatContextBase::new(num_floats))
+ Valid(FloatContextBase::new(num_floats))
}
#[inline(always)]
@@ -68,7 +69,7 @@ impl FloatContext {
fn with_mut_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R {
match *self {
Invalid => fail!("Float context no longer available"),
- Valid(ref mut base) => callback(&mut **base)
+ Valid(ref mut base) => callback(&mut *base)
}
}
@@ -76,7 +77,7 @@ impl FloatContext {
pub fn with_base<R>(&self, callback: &fn(&FloatContextBase) -> R) -> R {
match *self {
Invalid => fail!("Float context no longer available"),
- Valid(ref base) => callback(& **base)
+ Valid(ref base) => callback(&*base)
}
}
@@ -128,9 +129,12 @@ impl FloatContext {
impl FloatContextBase{
fn new(num_floats: uint) -> FloatContextBase {
debug!("Creating float context of size %?", num_floats);
- let new_data = vec::from_elem(num_floats, None);
FloatContextBase {
- float_data: new_data,
+ float_data: if num_floats == 0 {
+ None
+ } else {
+ Some(vec::from_elem(num_floats, None))
+ },
floats_used: 0,
max_y: Au(0),
offset: Point2D(Au(0), Au(0))
@@ -144,7 +148,7 @@ impl FloatContextBase{
fn last_float_pos(&self) -> Point2D<Au> {
assert!(self.floats_used > 0, "Error: tried to access FloatContext with no floats in it");
- match self.float_data[self.floats_used - 1] {
+ match self.float_data.get_ref()[self.floats_used - 1] {
None => fail!("FloatContext error: floats should never be None here"),
Some(float) => {
debug!("Returning float position: %?", float.bounds.origin + self.offset);
@@ -176,36 +180,38 @@ impl FloatContextBase{
let mut r_bottom = None;
// Find the float collisions for the given vertical range.
- for float in self.float_data.iter() {
- debug!("available_rect: Checking for collision against float");
- match *float{
- None => (),
- Some(data) => {
- let float_pos = data.bounds.origin;
- let float_size = data.bounds.size;
- debug!("float_pos: %?, float_size: %?", float_pos, float_size);
- match data.f_type {
- FloatLeft => {
- if(float_pos.x + float_size.width > max_left &&
- float_pos.y + float_size.height > top && float_pos.y < top + height) {
- max_left = float_pos.x + float_size.width;
-
- l_top = Some(float_pos.y);
- l_bottom = Some(float_pos.y + float_size.height);
-
- debug!("available_rect: collision with left float: new max_left is %?",
- max_left);
+ for floats in self.float_data.iter() {
+ for float in floats.iter() {
+ debug!("available_rect: Checking for collision against float");
+ match *float {
+ None => (),
+ Some(data) => {
+ let float_pos = data.bounds.origin;
+ let float_size = data.bounds.size;
+ debug!("float_pos: %?, float_size: %?", float_pos, float_size);
+ match data.f_type {
+ FloatLeft => {
+ if(float_pos.x + float_size.width > max_left &&
+ float_pos.y + float_size.height > top && float_pos.y < top + height) {
+ max_left = float_pos.x + float_size.width;
+
+ l_top = Some(float_pos.y);
+ l_bottom = Some(float_pos.y + float_size.height);
+
+ debug!("available_rect: collision with left float: new max_left is %?",
+ max_left);
+ }
}
- }
- FloatRight => {
- if(float_pos.x < min_right &&
- float_pos.y + float_size.height > top && float_pos.y < top + height) {
- min_right = float_pos.x;
-
- r_top = Some(float_pos.y);
- r_bottom = Some(float_pos.y + float_size.height);
- debug!("available_rect: collision with right float: new min_right is %?",
- min_right);
+ FloatRight => {
+ if(float_pos.x < min_right &&
+ float_pos.y + float_size.height > top && float_pos.y < top + height) {
+ min_right = float_pos.x;
+
+ r_top = Some(float_pos.y);
+ r_bottom = Some(float_pos.y + float_size.height);
+ debug!("available_rect: collision with right float: new min_right is %?",
+ min_right);
+ }
}
}
}
@@ -242,9 +248,12 @@ impl FloatContextBase{
}
fn add_float(&mut self, info: &PlacementInfo) {
- debug!("Floats_used: %?, Floats available: %?", self.floats_used, self.float_data.len());
- assert!(self.floats_used < self.float_data.len() &&
- self.float_data[self.floats_used].is_none());
+ assert!(self.float_data.is_some());
+ debug!("Floats_used: %?, Floats available: %?",
+ self.floats_used,
+ self.float_data.get_ref().len());
+ assert!(self.floats_used < self.float_data.get_ref().len() &&
+ self.float_data.get_ref()[self.floats_used].is_none());
let new_info = PlacementInfo {
width: info.width,
@@ -263,22 +272,24 @@ impl FloatContextBase{
},
f_type: info.f_type
};
- self.float_data[self.floats_used] = Some(new_float);
+ self.float_data.get_mut_ref()[self.floats_used] = Some(new_float);
self.max_y = max(self.max_y, new_float.bounds.origin.y);
self.floats_used += 1;
}
/// Returns true if the given rect overlaps with any floats.
fn collides_with_float(&self, bounds: &Rect<Au>) -> bool {
- for float in self.float_data.iter() {
- match *float{
- None => (),
- Some(data) => {
- if data.bounds.translate(&self.offset).intersects(bounds) {
- return true;
+ for floats in self.float_data.iter() {
+ for float in floats.iter() {
+ match *float {
+ None => (),
+ Some(data) => {
+ if data.bounds.translate(&self.offset).intersects(bounds) {
+ return true;
+ }
}
- }
- };
+ };
+ }
}
return false;
@@ -292,16 +303,18 @@ impl FloatContextBase{
let left = left - self.offset.x;
let mut max_height = None;
- for float in self.float_data.iter() {
- match *float {
- None => (),
- Some(f_data) => {
- if f_data.bounds.origin.y + f_data.bounds.size.height > top &&
- f_data.bounds.origin.x + f_data.bounds.size.width > left &&
- f_data.bounds.origin.x < left + width {
- let new_y = f_data.bounds.origin.y;
- max_height = Some(min(max_height.unwrap_or(new_y), new_y));
- }
+ for floats in self.float_data.iter() {
+ for float in floats.iter() {
+ match *float {
+ None => (),
+ Some(f_data) => {
+ if f_data.bounds.origin.y + f_data.bounds.size.height > top &&
+ f_data.bounds.origin.x + f_data.bounds.size.width > left &&
+ f_data.bounds.origin.x < left + width {
+ let new_y = f_data.bounds.origin.y;
+ max_height = Some(min(max_height.unwrap_or(new_y), new_y));
+ }
+ }
}
}
}
@@ -361,19 +374,21 @@ impl FloatContextBase{
fn clearance(&self, clear: ClearType) -> Au {
let mut clearance = Au(0);
- for float in self.float_data.iter() {
- match *float {
- None => (),
- Some(f_data) => {
- match (clear, f_data.f_type) {
- (ClearLeft, FloatLeft) |
- (ClearRight, FloatRight) |
- (ClearBoth, _) => {
- clearance = max(
- clearance,
- self.offset.y + f_data.bounds.origin.y + f_data.bounds.size.height);
+ for floats in self.float_data.iter() {
+ for float in floats.iter() {
+ match *float {
+ None => (),
+ Some(f_data) => {
+ match (clear, f_data.f_type) {
+ (ClearLeft, FloatLeft) |
+ (ClearRight, FloatRight) |
+ (ClearBoth, _) => {
+ clearance = max(
+ clearance,
+ self.offset.y + f_data.bounds.origin.y + f_data.bounds.size.height);
+ }
+ _ => ()
}
- _ => ()
}
}
}
diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs
index 273272fbe04..472950b4a96 100644
--- a/src/components/main/layout/flow.rs
+++ b/src/components/main/layout/flow.rs
@@ -25,207 +25,275 @@
/// line breaks and mapping to CSS boxes, for the purpose of handling `getClientRects()` and
/// similar methods.
-use layout::block::BlockFlowData;
-use layout::float::FloatFlowData;
+use css::node_style::StyledNode;
+use layout::block::BlockFlow;
use layout::box::RenderBox;
use layout::context::LayoutContext;
+use layout::float::FloatFlow;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
-use layout::inline::{InlineFlowData};
-use layout::float_context::{FloatContext, Invalid, FloatType};
+use layout::float_context::{FloatContext, Invalid};
use layout::incremental::RestyleDamage;
-use css::node_style::StyledNode;
+use layout::inline::InlineFlow;
+
use extra::dlist::{DList,MutDListIterator};
use extra::container::Deque;
-
-use std::cell::Cell;
-use std::io::stderr;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use servo_util::geometry::Au;
use script::dom::node::{AbstractNode, LayoutView};
+use std::cast;
+use std::cell::Cell;
-/// The type of the formatting context and data specific to each context, such as line box
-/// structures or float lists.
-pub enum FlowContext {
- AbsoluteFlow(~FlowData),
- BlockFlow(~BlockFlowData),
- FloatFlow(~FloatFlowData),
- InlineBlockFlow(~FlowData),
- InlineFlow(~InlineFlowData),
- TableFlow(~FlowData),
-}
+/// Virtual methods that make up a float context.
+///
+/// Note that virtual methods have a cost; we should not overuse them in Servo. Consider adding
+/// methods to `ImmutableFlowUtils` or `MutableFlowUtils` before adding more methods here.
+pub trait FlowContext {
+ // RTTI
+ //
+ // TODO(pcwalton): Use Rust's RTTI, once that works.
-pub enum FlowContextType {
- Flow_Absolute,
- Flow_Block,
- Flow_Float(FloatType),
- Flow_InlineBlock,
- Flow_Inline,
- Flow_Root,
- Flow_Table
-}
+ /// Returns the class of flow that this is.
+ fn class(&self) -> FlowClass;
-impl FlowContext {
- pub fn each_bu_sub_inorder (&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
- for kid in self.child_iter() {
- // FIXME: Work around rust#2202. We should be able to pass the callback directly.
- if !kid.each_bu_sub_inorder(|a| callback(a)) {
- return false;
- }
- }
+ /// If this is a block flow, returns the underlying object. Fails otherwise.
+ fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
+ fail!("called as_block() on a non-block flow")
+ }
- if !self.is_inorder() {
- callback(self)
- } else {
- true
- }
+ /// If this is an inline flow, returns the underlying object, borrowed immutably. Fails
+ /// otherwise.
+ fn as_immutable_inline<'a>(&'a self) -> &'a InlineFlow {
+ fail!("called as_immutable_inline() on a non-inline flow")
}
- pub fn each_preorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool,
- callback: &fn(&mut FlowContext) -> bool)
- -> bool {
- if prune(self) {
- return true;
- }
+ /// If this is an inline flow, returns the underlying object. Fails otherwise.
+ fn as_inline<'a>(&'a mut self) -> &'a mut InlineFlow {
+ fail!("called as_inline() on a non-inline flow")
+ }
- if !callback(self) {
- return false;
- }
+ /// If this is a float flow, returns the underlying object. Fails otherwise.
+ fn as_float<'a>(&'a mut self) -> &'a mut FloatFlow {
+ fail!("called as_float() on a non-float flow")
+ }
- for kid in self.child_iter() {
- // FIXME: Work around rust#2202. We should be able to pass the callback directly.
- if !kid.each_preorder_prune(|a| prune(a), |a| callback(a)) {
- return false;
- }
- }
+ // Main methods
- true
+ /// Pass 1 of reflow: computes minimum and preferred widths.
+ fn bubble_widths(&mut self, _ctx: &mut LayoutContext) {
+ fail!("bubble_widths not yet implemented")
}
- pub fn each_postorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool,
- callback: &fn(&mut FlowContext) -> bool)
- -> bool {
- if prune(self) {
- return true;
- }
+ /// Pass 2 of reflow: computes width.
+ fn assign_widths(&mut self, _ctx: &mut LayoutContext) {
+ fail!("assign_widths not yet implemented")
+ }
- for kid in self.child_iter() {
- // FIXME: Work around rust#2202. We should be able to pass the callback directly.
- if !kid.each_postorder_prune(|a| prune(a), |a| callback(a)) {
- return false;
- }
- }
+ /// Pass 3 of reflow: computes height.
+ fn assign_height(&mut self, _ctx: &mut LayoutContext) {
+ fail!("assign_height not yet implemented")
+ }
- callback(self)
+ /// In-order version of pass 3 of reflow: computes heights with floats present.
+ fn assign_height_inorder(&mut self, _ctx: &mut LayoutContext) {
+ fail!("assign_height_inorder not yet implemented")
}
- pub fn each_preorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
- self.each_preorder_prune(|_| false, callback)
+ /// Collapses margins with the parent flow. This runs as part of assign-heights.
+ fn collapse_margins(&mut self,
+ _top_margin_collapsible: bool,
+ _first_in_flow: &mut bool,
+ _margin_top: &mut Au,
+ _top_offset: &mut Au,
+ _collapsing: &mut Au,
+ _collapsible: &mut Au) {
+ fail!("collapse_margins not yet implemented")
}
- pub fn each_postorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
- self.each_postorder_prune(|_| false, callback)
+ /// Returns a debugging string describing this flow.
+ fn debug_str(&self) -> ~str {
+ ~"???"
}
}
-impl<'self> FlowContext {
- pub fn is_block_like(&self) -> bool {
- match *self {
- BlockFlow(*) | FloatFlow(*) => true,
- _ => false,
- }
+// Base access
+
+#[inline(always)]
+pub fn base<'a>(this: &'a FlowContext) -> &'a FlowData {
+ unsafe {
+ let (_, ptr): (uint, &FlowData) = cast::transmute(this);
+ ptr
}
+}
- pub fn is_leaf(&self) -> bool {
- do self.with_base |base| {
- base.children.len() == 0
- }
+#[inline(always)]
+pub fn mut_base<'a>(this: &'a mut FlowContext) -> &'a mut FlowData {
+ unsafe {
+ let (_, ptr): (uint, &mut FlowData) = cast::transmute(this);
+ ptr
}
+}
- pub fn add_new_child(&mut self, new_child: FlowContext) {
- let cell = Cell::new(new_child);
- do self.with_mut_base |base| {
- base.children.push_back(cell.take());
+/// Returns the last child of this flow.
+pub fn last_child<'a>(flow: &'a mut FlowContext) -> Option<&'a mut ~FlowContext:> {
+ mut_base(flow).children.back_mut()
+}
+
+/// Iterates over the children of this flow.
+pub fn child_iter<'a>(flow: &'a mut FlowContext) -> MutDListIterator<'a,~FlowContext:> {
+ mut_base(flow).children.mut_iter()
+}
+
+pub trait ImmutableFlowUtils {
+ // Convenience functions
+
+ /// Returns true if this flow is a block or a float flow.
+ fn is_block_like(self) -> bool;
+
+ /// Returns true if this flow has no children.
+ fn is_leaf(self) -> bool;
+
+ /// Returns true if this flow is a block flow, an inline flow, or a float flow.
+ fn starts_block_flow(self) -> bool;
+
+ /// Returns true if this flow is an inline flow.
+ fn starts_inline_flow(self) -> bool;
+
+ /// Dumps the flow tree for debugging.
+ fn dump(self);
+}
+
+pub trait MutableFlowUtils {
+ // Traversals
+
+ /// Traverses the tree in preorder.
+ fn traverse_preorder<T:PreorderFlowTraversal>(self, traversal: &mut T) -> bool;
+
+ /// Traverses the tree in postorder.
+ fn traverse_postorder<T:PostorderFlowTraversal>(self, traversal: &mut T) -> bool;
+
+ // Mutators
+
+ /// Adds a new flow as a child of this flow.
+ fn add_new_child(self, new_child: ~FlowContext:);
+
+ /// Invokes a closure with the first child of this flow.
+ fn with_first_child<R>(self, f: &fn(Option<&mut ~FlowContext:>) -> R) -> R;
+
+ /// Invokes a closure with the last child of this flow.
+ fn with_last_child<R>(self, f: &fn(Option<&mut ~FlowContext:>) -> R) -> R;
+
+ /// Removes the first child of this flow and destroys it.
+ fn remove_first(self);
+
+ /// Removes the last child of this flow and destroys it.
+ fn remove_last(self);
+
+ /// Builds a display list for this flow and its children.
+ fn build_display_list<E:ExtraDisplayListData>(
+ self,
+ builder: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ list: &Cell<DisplayList<E>>)
+ -> bool;
+}
+
+pub enum FlowClass {
+ AbsoluteFlowClass,
+ BlockFlowClass,
+ FloatFlowClass,
+ InlineBlockFlowClass,
+ InlineFlowClass,
+ TableFlowClass,
+}
+
+// Miscellaneous flows that are not yet implemented.
+
+pub struct AbsoluteFlow {
+ base: FlowData,
+}
+
+impl AbsoluteFlow {
+ pub fn new(base: FlowData) -> AbsoluteFlow {
+ AbsoluteFlow {
+ base: base,
}
}
+}
- pub fn with_first_child<R>(&mut self, cb: &fn(Option<&mut FlowContext>) -> R) -> R {
- do self.with_mut_base |base| {
- cb(base.children.front_mut())
- }
+impl FlowContext for AbsoluteFlow {
+ fn class(&self) -> FlowClass {
+ AbsoluteFlowClass
}
+}
+
+pub struct InlineBlockFlow {
+ base: FlowData,
+}
- pub fn with_last_child<R>(&mut self, cb: &fn(Option<&mut FlowContext>) -> R) -> R {
- do self.with_mut_base |base| {
- cb(base.children.back_mut())
+impl InlineBlockFlow {
+ pub fn new(base: FlowData) -> InlineBlockFlow {
+ InlineBlockFlow {
+ base: base,
}
}
+}
- pub fn last_child(&'self mut self) -> Option<&'self mut FlowContext> {
- self.mut_base().children.back_mut()
+impl FlowContext for InlineBlockFlow {
+ fn class(&self) -> FlowClass {
+ InlineBlockFlowClass
}
+}
- pub fn remove_first(&mut self) {
- do self.with_mut_base |base| {
- base.children.pop_front();
- }
- }
+pub struct TableFlow {
+ base: FlowData,
+}
- pub fn remove_last(&mut self) {
- do self.with_mut_base |base| {
- base.children.pop_back();
+impl TableFlow {
+ pub fn new(base: FlowData) -> TableFlow {
+ TableFlow {
+ base: base,
}
}
+}
- pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a, FlowContext> {
- self.mut_base().children.mut_iter()
+impl FlowContext for TableFlow {
+ fn class(&self) -> FlowClass {
+ TableFlowClass
}
-
}
-impl<'self> FlowContext {
- pub fn with_base<R>(&self, callback: &fn(&FlowData) -> R) -> R {
- match *self {
- AbsoluteFlow(ref info) => callback(&**info),
- BlockFlow(ref info) => {
- callback(&info.common)
- }
- FloatFlow(ref info) => callback(&info.common),
- InlineBlockFlow(ref info) => callback(&**info),
- InlineFlow(ref info) => {
- callback(&info.common)
- }
- TableFlow(ref info) => callback(&**info)
- }
+/// A top-down traversal.
+pub trait PreorderFlowTraversal {
+ /// The operation to perform. Return true to continue or false to stop.
+ fn process(&mut self, flow: &mut FlowContext) -> bool;
+
+ /// Returns true if this node should be pruned. If this returns true, we skip the operation
+ /// entirely and do not process any descendant nodes. This is called *before* child nodes are
+ /// visited. The default implementation never prunes any nodes.
+ fn should_prune(&mut self, _flow: &mut FlowContext) -> bool {
+ false
}
- pub fn with_mut_base<R>(&mut self, callback: &fn(&mut FlowData) -> R) -> R {
- match *self {
- AbsoluteFlow(ref mut info) => callback(&mut **info),
- BlockFlow(ref mut info) => {
- callback(&mut info.common)
- }
- FloatFlow(ref mut info) => callback(&mut info.common),
- InlineBlockFlow(ref mut info) => callback(&mut **info),
- InlineFlow(ref mut info) => {
- callback(&mut info.common)
- }
- TableFlow(ref mut info) => callback(&mut **info),
- }
+}
+
+/// A bottom-up traversal, with a optional in-order pass.
+pub trait PostorderFlowTraversal {
+ /// The operation to perform. Return true to continue or false to stop.
+ fn process(&mut self, flow: &mut FlowContext) -> bool;
+
+ /// Returns false if this node must be processed in-order. If this returns false, we skip the
+ /// operation for this node, but continue processing the descendants. This is called *after*
+ /// child nodes are visited.
+ fn should_process(&mut self, _flow: &mut FlowContext) -> bool {
+ true
}
- pub fn mut_base(&'self mut self) -> &'self mut FlowData {
- match *self {
- AbsoluteFlow(ref mut info) => &mut(**info),
- BlockFlow(ref mut info) => {
- &mut info.common
- }
- FloatFlow(ref mut info) => &mut info.common,
- InlineBlockFlow(ref mut info) => &mut(**info),
- InlineFlow(ref mut info) => {
- &mut info.common
- }
- TableFlow(ref mut info) => &mut(**info),
- }
+
+ /// Returns true if this node should be pruned. If this returns true, we skip the operation
+ /// entirely and do not process any descendant nodes. This is called *before* child nodes are
+ /// visited. The default implementation never prunes any nodes.
+ fn should_prune(&mut self, _flow: &mut FlowContext) -> bool {
+ false
}
}
@@ -237,7 +305,7 @@ pub struct FlowData {
node: AbstractNode<LayoutView>,
restyle_damage: RestyleDamage,
- children: DList<FlowContext>,
+ children: DList<~FlowContext:>,
/* TODO (Issue #87): debug only */
id: int,
@@ -256,11 +324,12 @@ pub struct FlowData {
}
pub struct BoxIterator {
- priv boxes: ~[RenderBox],
+ priv boxes: ~[@RenderBox],
priv index: uint,
}
-impl Iterator<RenderBox> for BoxIterator {
- fn next(&mut self) -> Option<RenderBox> {
+
+impl Iterator<@RenderBox> for BoxIterator {
+ fn next(&mut self) -> Option<@RenderBox> {
if self.index >= self.boxes.len() {
None
} else {
@@ -270,6 +339,7 @@ impl Iterator<RenderBox> for BoxIterator {
}
}
}
+
impl FlowData {
pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData {
FlowData {
@@ -280,237 +350,135 @@ impl FlowData {
id: id,
- min_width: Au(0),
- pref_width: Au(0),
+ min_width: Au::new(0),
+ pref_width: Au::new(0),
position: Au::zero_rect(),
floats_in: Invalid,
floats_out: Invalid,
num_floats: 0,
- abs_position: Point2D(Au(0), Au(0)),
+ abs_position: Point2D(Au::new(0), Au::new(0)),
is_inorder: false
}
}
- pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a, FlowContext> {
+ pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a,~FlowContext:> {
self.children.mut_iter()
}
-
}
-impl<'self> FlowContext {
- /// A convenience method to return the position of this flow. Fails if the flow is currently
- /// being borrowed mutably.
- #[inline(always)]
- pub fn position(&self) -> Rect<Au> {
- do self.with_base |common_info| {
- common_info.position
- }
- }
-
- #[inline(always)]
- pub fn is_inorder(&self) -> bool {
- do self.with_base |common_info| {
- common_info.is_inorder
+impl<'self> ImmutableFlowUtils for &'self FlowContext {
+ /// Returns true if this flow is a block or a float flow.
+ fn is_block_like(self) -> bool {
+ match self.class() {
+ BlockFlowClass | FloatFlowClass => true,
+ AbsoluteFlowClass | InlineBlockFlowClass | InlineFlowClass | TableFlowClass => false,
}
}
- /// A convenience method to return the ID of this flow. Fails if the flow is currently being
- /// borrowed mutably.
- #[inline(always)]
- pub fn id(&self) -> int {
- do self.with_base |info| {
- info.id
- }
+ /// Returns true if this flow has no children.
+ fn is_leaf(self) -> bool {
+ base(self).children.len() == 0
}
- pub fn inline(&'self mut self) -> &'self mut InlineFlowData {
- match *self {
- InlineFlow(ref mut info) => &mut (**info),
- _ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id()))
+ /// Returns true if this flow is a block flow, an inline-block flow, or a float flow.
+ fn starts_block_flow(self) -> bool {
+ match self.class() {
+ BlockFlowClass | InlineBlockFlowClass | FloatFlowClass => true,
+ AbsoluteFlowClass | InlineFlowClass | TableFlowClass => false,
}
}
- pub fn imm_inline(&'self self) -> &'self InlineFlowData {
- match *self {
- InlineFlow(ref info) => &**info,
- _ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id()))
+ /// Returns true if this flow is a block flow, an inline flow, or a float flow.
+ fn starts_inline_flow(self) -> bool {
+ match self.class() {
+ InlineFlowClass => true,
+ AbsoluteFlowClass | BlockFlowClass | FloatFlowClass | InlineBlockFlowClass |
+ TableFlowClass => false,
}
}
- pub fn block(&'self mut self) -> &'self mut BlockFlowData {
- match *self {
- BlockFlow(ref mut info) => &mut (**info),
- _ => fail!(fmt!("Tried to access block data of non-block: f%d", self.id()))
- }
+ /// Dumps the flow tree for debugging.
+ fn dump(self) {
+ // TODO(pcwalton): Fill this in.
}
+}
- pub fn root(&'self mut self) -> &'self mut BlockFlowData {
- match *self {
- BlockFlow(ref mut info) if info.is_root => &mut (**info),
- _ => fail!(fmt!("Tried to access root block data of non-root: f%d", self.id()))
+impl<'self> MutableFlowUtils for &'self mut FlowContext {
+ /// Traverses the tree in preorder.
+ fn traverse_preorder<T:PreorderFlowTraversal>(self, traversal: &mut T) -> bool {
+ if traversal.should_prune(self) {
+ return true
}
- }
- pub fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
-
- debug!("FlowContext: bubbling widths for f%?", self.id());
- match *self {
- BlockFlow(ref mut info) => info.bubble_widths_block(ctx),
- InlineFlow(ref mut info) => info.bubble_widths_inline(ctx),
- FloatFlow(ref mut info) => info.bubble_widths_float(ctx),
- _ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id()))
+ if !traversal.process(self) {
+ return false
}
- }
- pub fn assign_widths(&mut self, ctx: &mut LayoutContext) {
-
- debug!("FlowContext: assigning widths for f%?", self.id());
- match *self {
- BlockFlow(ref mut info) => info.assign_widths_block(ctx),
- InlineFlow(ref mut info) => info.assign_widths_inline(ctx),
- FloatFlow(ref mut info) => info.assign_widths_float(),
- _ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id()))
+ for kid in child_iter(self) {
+ if !kid.traverse_preorder(traversal) {
+ return false
+ }
}
- }
-
- pub fn assign_height(&mut self, ctx: &mut LayoutContext) {
- debug!("FlowContext: assigning height for f%?", self.id());
- match *self {
- BlockFlow(ref mut info) => info.assign_height_block(ctx),
- InlineFlow(ref mut info) => info.assign_height_inline(ctx),
- FloatFlow(ref mut info) => info.assign_height_float(ctx),
- _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id()))
- }
+ true
}
- pub fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
- match *self {
- BlockFlow(ref mut info) => info.assign_height_inorder_block(ctx),
- InlineFlow(ref mut info) => info.assign_height_inorder_inline(ctx),
- FloatFlow(ref mut info) => info.assign_height_inorder_float(),
- _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id()))
+ /// Traverses the tree in postorder.
+ fn traverse_postorder<T:PostorderFlowTraversal>(self, traversal: &mut T) -> bool {
+ if traversal.should_prune(self) {
+ return true
}
- }
- pub fn build_display_list<E:ExtraDisplayListData>(&mut self,
- builder: &DisplayListBuilder,
- dirty: &Rect<Au>,
- list: &Cell<DisplayList<E>>)
- -> bool {
-
-
- debug!("FlowContext: building display list for f%?", self.id());
- match *self {
- BlockFlow(ref mut info) => info.build_display_list_block(builder, dirty, list),
- InlineFlow(ref mut info) => info.build_display_list_inline(builder, dirty, list),
- FloatFlow(ref mut info) => info.build_display_list_float(builder, dirty, list),
- _ => {
- fail!("Tried to build_display_list_recurse of flow: %?", self)
+ for kid in child_iter(self) {
+ if !kid.traverse_postorder(traversal) {
+ return false
}
}
- }
- /// A convenience method to return the restyle damage of this flow. Fails if the flow is
- /// currently being borrowed mutably.
- #[inline(always)]
- pub fn restyle_damage(&self) -> RestyleDamage {
- do self.with_base |info| {
- info.restyle_damage
+ if !traversal.should_process(self) {
+ return true
}
- }
-
- // Actual methods that do not require much flow-specific logic
- pub fn foldl_all_boxes<B:Clone>(&mut self, seed: B, cb: &fn(a: B, b: RenderBox) -> B) -> B {
- match *self {
- BlockFlow(ref mut block) => {
- do block.box.as_ref().map_default(seed.clone()) |box| {
- cb(seed.clone(), *box)
- }
- }
- InlineFlow(ref mut inline) => {
- do inline.boxes.iter().fold(seed) |acc, box| {
- cb(acc.clone(), *box)
- }
- }
- _ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self)),
- }
+ traversal.process(self)
}
- pub fn foldl_boxes_for_node<B:Clone>(&mut self,
- node: AbstractNode<LayoutView>,
- seed: B,
- callback: &fn(a: B, RenderBox) -> B)
- -> B {
- do self.foldl_all_boxes(seed) |acc, box| {
- if box.node() == node {
- callback(acc, box)
- } else {
- acc
- }
- }
+ /// Adds a new flow as a child of this flow.
+ fn add_new_child(self, new_child: ~FlowContext:) {
+ mut_base(self).children.push_back(new_child)
}
- pub fn iter_all_boxes(&mut self) -> BoxIterator {
- BoxIterator {
- boxes: match *self {
- BlockFlow(ref mut block) => block.box.as_ref().map_default(~[], |&x| ~[x]),
- InlineFlow(ref mut inline) => inline.boxes.clone(),
- _ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self))
- },
- index: 0,
- }
+ /// Invokes a closure with the first child of this flow.
+ fn with_first_child<R>(self, f: &fn(Option<&mut ~FlowContext:>) -> R) -> R {
+ f(mut_base(self).children.front_mut())
}
- /// Dumps the flow tree for debugging.
- pub fn dump(&mut self) {
- self.dump_indent(0);
+ /// Invokes a closure with the last child of this flow.
+ fn with_last_child<R>(self, f: &fn(Option<&mut ~FlowContext:>) -> R) -> R {
+ f(mut_base(self).children.back_mut())
}
- /// Dumps the flow tree, for debugging, with indentation.
- pub fn dump_indent(&mut self, indent: uint) {
- let mut s = ~"|";
- for _ in range(0, indent) {
- s.push_str("---- ");
- }
-
- s.push_str(self.debug_str());
- stderr().write_line(s);
+ /// Removes the first child of this flow and destroys it.
+ fn remove_first(self) {
+ let _ = mut_base(self).children.pop_front();
+ }
- // FIXME: this should have a pure/const version?
- for child in self.child_iter() {
- child.dump_indent(indent + 1)
- }
+ /// Removes the last child of this flow and destroys it.
+ fn remove_last(self) {
+ let _ = mut_base(self).children.pop_back();
}
-
- pub fn debug_str(&self) -> ~str {
- let repr = match *self {
- InlineFlow(ref inline) => {
- let mut s = inline.boxes.iter().fold(~"InlineFlow(children=", |s, box| {
- fmt!("%s b%d", s, box.id())
- });
- s.push_str(")");
- s
- },
- BlockFlow(ref block) => {
- match block.box {
- Some(box) => fmt!("BlockFlow(box=b%d)", box.id()),
- None => ~"BlockFlow",
- }
- },
- FloatFlow(ref float) => {
- match float.box {
- Some(box) => fmt!("FloatFlow(box=b%d)", box.id()),
- None => ~"FloatFlow",
- }
- },
- _ => ~"(Unknown flow)"
- };
-
- do self.with_base |base| {
- fmt!("f%? %? floats %? size %? damage %?", base.id, repr, base.num_floats,
- base.position, base.restyle_damage)
+
+ fn build_display_list<E:ExtraDisplayListData>(
+ self,
+ builder: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ list: &Cell<DisplayList<E>>)
+ -> bool {
+ debug!("FlowContext: building display list for f%?", base(self).id);
+ match self.class() {
+ BlockFlowClass => self.as_block().build_display_list_block(builder, dirty, list),
+ InlineFlowClass => self.as_inline().build_display_list_inline(builder, dirty, list),
+ FloatFlowClass => self.as_float().build_display_list_float(builder, dirty, list),
+ _ => fail!("Tried to build_display_list_recurse of flow: %?", self),
}
}
}
diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs
index cad2ec09a2f..037b782edf0 100644
--- a/src/components/main/layout/inline.rs
+++ b/src/components/main/layout/inline.rs
@@ -3,28 +3,27 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode;
-use std::cell::Cell;
use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox};
-use layout::box::{SplitDidFit, SplitDidNotFit, TextRenderBoxClass};
+use layout::box::{RenderBoxUtils, SplitDidFit, SplitDidNotFit, TextRenderBoxClass};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
-use layout::flow::{FlowContext, FlowData, InlineFlow};
+use layout::flow::{FlowClass, FlowContext, FlowData, InlineFlowClass};
+use layout::flow;
use layout::float_context::FloatContext;
use layout::util::{ElementMapping};
use layout::float_context::{PlacementInfo, FloatLeft};
-use std::u16;
-use std::util;
+use extra::container::Deque;
+use extra::ringbuf::RingBuf;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayList;
-use servo_util::geometry::Au;
-use style::computed_values::line_height;
use style::computed_values::text_align;
use style::computed_values::vertical_align;
+use servo_util::geometry::Au;
use servo_util::range::Range;
-use servo_util::tree::TreeNodeRef;
-use extra::container::Deque;
-use extra::ringbuf::RingBuf;
+use std::cell::Cell;
+use std::u16;
+use std::util;
/*
Lineboxes are represented as offsets into the child list, rather than
@@ -62,13 +61,15 @@ struct LineBox {
struct LineboxScanner {
floats: FloatContext,
- new_boxes: ~[RenderBox],
- work_list: @mut RingBuf<RenderBox>,
+ new_boxes: ~[@RenderBox],
+ work_list: @mut RingBuf<@RenderBox>,
pending_line: LineBox,
lines: ~[LineBox],
cur_y: Au,
}
+local_data_key!(local_linebox_scanner: LineboxScanner)
+
impl LineboxScanner {
pub fn new(float_ctx: FloatContext) -> LineboxScanner {
LineboxScanner {
@@ -77,33 +78,45 @@ impl LineboxScanner {
work_list: @mut RingBuf::new(),
pending_line: LineBox {
range: Range::empty(),
- bounds: Rect(Point2D(Au(0), Au(0)), Size2D(Au(0), Au(0))),
- green_zone: Size2D(Au(0), Au(0))
+ bounds: Rect(Point2D(Au::new(0), Au::new(0)), Size2D(Au::new(0), Au::new(0))),
+ green_zone: Size2D(Au::new(0), Au::new(0))
},
lines: ~[],
- cur_y: Au(0)
+ cur_y: Au::new(0)
}
}
+
+ fn reinitialize(&mut self, float_ctx: FloatContext) {
+ self.floats = float_ctx;
+ self.new_boxes.truncate(0);
+ self.work_list.clear();
+ self.pending_line.range = Range::empty();
+ self.pending_line.bounds = Rect(Point2D(Au::new(0), Au::new(0)),
+ Size2D(Au::new(0), Au::new(0)));
+ self.pending_line.green_zone = Size2D(Au::new(0), Au::new(0));
+ self.lines.truncate(0);
+ self.cur_y = Au::new(0);
+ }
pub fn floats_out(&mut self) -> FloatContext {
self.floats.clone()
}
- fn reset_scanner(&mut self, flow: &mut InlineFlowData) {
- debug!("Resetting line box scanner's state for flow f%d.", flow.common.id);
+ fn reset_scanner(&mut self, flow: &mut InlineFlow) {
+ debug!("Resetting line box scanner's state for flow f%d.", flow.base.id);
self.lines = ~[];
self.new_boxes = ~[];
- self.cur_y = Au(0);
+ self.cur_y = Au::new(0);
self.reset_linebox();
}
fn reset_linebox(&mut self) {
self.pending_line.range.reset(0,0);
- self.pending_line.bounds = Rect(Point2D(Au(0), self.cur_y), Size2D(Au(0), Au(0)));
- self.pending_line.green_zone = Size2D(Au(0), Au(0))
+ self.pending_line.bounds = Rect(Point2D(Au::new(0), self.cur_y), Size2D(Au::new(0), Au::new(0)));
+ self.pending_line.green_zone = Size2D(Au::new(0), Au::new(0))
}
- pub fn scan_for_lines(&mut self, flow: &mut InlineFlowData) {
+ pub fn scan_for_lines(&mut self, flow: &mut InlineFlow) {
self.reset_scanner(flow);
let mut i = 0u;
@@ -115,11 +128,11 @@ impl LineboxScanner {
break
}
let box = flow.boxes[i]; i += 1;
- debug!("LineboxScanner: Working with box from box list: b%d", box.id());
+ debug!("LineboxScanner: Working with box from box list: b%d", box.base().id());
box
} else {
- let box = self.work_list.pop_front().unwrap();
- debug!("LineboxScanner: Working with box from work list: b%d", box.id());
+ let box = self.work_list.pop_front().unwrap();
+ debug!("LineboxScanner: Working with box from work list: b%d", box.base().id());
box
};
@@ -139,16 +152,15 @@ impl LineboxScanner {
self.flush_current_line();
}
-
flow.elems.repair_for_box_changes(flow.boxes, self.new_boxes);
self.swap_out_results(flow);
}
- fn swap_out_results(&mut self, flow: &mut InlineFlowData) {
+ fn swap_out_results(&mut self, flow: &mut InlineFlow) {
debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d",
self.lines.len(),
- flow.common.id);
+ flow.base.id);
util::swap(&mut flow.boxes, &mut self.new_boxes);
util::swap(&mut flow.lines, &mut self.lines);
@@ -165,47 +177,10 @@ impl LineboxScanner {
self.reset_linebox();
}
- fn calculate_line_height(&self, box: RenderBox, font_size: Au) -> Au {
- match box.line_height() {
- line_height::Normal => font_size.scale_by(1.14),
- line_height::Number(l) => font_size.scale_by(l),
- line_height::Length(l) => l
- }
- }
-
- fn box_height(&self, box: RenderBox) -> Au {
- match box {
- ImageRenderBoxClass(image_box) => {
- let size = image_box.image.get_size();
- let height = Au::from_px(size.unwrap_or(Size2D(0, 0)).height);
- image_box.base.position.size.height = height;
- debug!("box_height: found image height: %?", height);
- height
- }
- TextRenderBoxClass(text_box) => {
- let range = &text_box.range;
- let run = &text_box.run;
-
- // Compute the height based on the line-height and font size
- let text_bounds = run.metrics_for_range(range).bounding_box;
- let em_size = text_bounds.size.height;
- let line_height = self.calculate_line_height(box, em_size);
-
- line_height
- }
- GenericRenderBoxClass(_) => {
- Au(0)
- }
- _ => {
- fail!(fmt!("Tried to get height of unknown Box variant: %s", box.debug_str()))
- }
- }
- }
-
// FIXME(eatkinson): this assumes that the tallest box in the line determines the line height
// This might not be the case with some weird text fonts.
- fn new_height_for_line(&self, new_box: RenderBox) -> Au {
- let box_height = self.box_height(new_box);
+ fn new_height_for_line(&self, new_box: &RenderBox) -> Au {
+ let box_height = new_box.box_height();
if box_height > self.pending_line.bounds.size.height {
box_height
} else {
@@ -216,9 +191,12 @@ impl LineboxScanner {
/// Computes the position of a line that has only the provided RenderBox.
/// Returns: the bounding rect of the line's green zone (whose origin coincides
/// with the line's origin) and the actual width of the first box after splitting.
- fn initial_line_placement (&self, first_box: RenderBox, ceiling: Au, flow: &mut InlineFlowData) -> (Rect<Au>, Au) {
+ fn initial_line_placement(&self, first_box: @RenderBox, ceiling: Au, flow: &mut InlineFlow)
+ -> (Rect<Au>, Au) {
debug!("LineboxScanner: Trying to place first box of line %?", self.lines.len());
- debug!("LineboxScanner: box size: %?", first_box.position().size);
+
+ let first_box_size = first_box.base().position.get().size;
+ debug!("LineboxScanner: box size: %?", first_box_size);
let splitable = first_box.can_split();
let line_is_empty: bool = self.pending_line.range.length() == 0;
@@ -226,34 +204,36 @@ impl LineboxScanner {
// We will move it later if it has nonzero width
// and that causes problems.
let placement_width = if splitable {
- Au(0)
+ Au::new(0)
} else {
- first_box.position().size.width
+ first_box_size.width
};
let mut info = PlacementInfo {
width: placement_width,
- height: first_box.position().size.height,
+ height: first_box_size.height,
ceiling: ceiling,
- max_width: flow.common.position.size.width,
+ max_width: flow.base.position.size.width,
f_type: FloatLeft
};
let line_bounds = self.floats.place_between_floats(&info);
- debug!("LineboxScanner: found position for line: %? using placement_info: %?", line_bounds, info);
+ debug!("LineboxScanner: found position for line: %? using placement_info: %?",
+ line_bounds,
+ info);
// Simple case: if the box fits, then we can stop here
- if line_bounds.size.width > first_box.position().size.width {
+ if line_bounds.size.width > first_box_size.width {
debug!("LineboxScanner: case=box fits");
- return (line_bounds, first_box.position().size.width);
+ return (line_bounds, first_box_size.width);
}
// If not, but we can't split the box, then we'll place
// the line here and it will overflow.
if !splitable {
debug!("LineboxScanner: case=line doesn't fit, but is unsplittable");
- return (line_bounds, first_box.position().size.width);
+ return (line_bounds, first_box_size.width);
}
// Otherwise, try and split the box
@@ -264,15 +244,15 @@ impl LineboxScanner {
CannotSplit(_) => {
error!("LineboxScanner: Tried to split unsplittable render box! %s",
first_box.debug_str());
- return (line_bounds, first_box.position().size.width);
+ return (line_bounds, first_box_size.width);
}
SplitDidFit(left, right) => {
debug!("LineboxScanner: case=box split and fit");
let actual_box_width = match (left, right) {
- (Some(l_box), Some(_)) => l_box.position().size.width,
- (Some(l_box), None) => l_box.position().size.width,
- (None, Some(r_box)) => r_box.position().size.width,
+ (Some(l_box), Some(_)) => l_box.base().position.get().size.width,
+ (Some(l_box), None) => l_box.base().position.get().size.width,
+ (None, Some(r_box)) => r_box.base().position.get().size.width,
(None, None) => fail!("This case makes no sense.")
};
return (line_bounds, actual_box_width);
@@ -284,9 +264,9 @@ impl LineboxScanner {
debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down");
let actual_box_width = match (left, right) {
- (Some(l_box), Some(_)) => l_box.position().size.width,
- (Some(l_box), None) => l_box.position().size.width,
- (None, Some(r_box)) => r_box.position().size.width,
+ (Some(l_box), Some(_)) => l_box.base().position.get().size.width,
+ (Some(l_box), None) => l_box.base().position.get().size.width,
+ (None, Some(r_box)) => r_box.base().position.get().size.width,
(None, None) => fail!("This case makes no sense.")
};
@@ -301,7 +281,7 @@ impl LineboxScanner {
}
/// Returns false only if we should break the line.
- fn try_append_to_line(&mut self, in_box: RenderBox, flow: &mut InlineFlowData) -> bool {
+ fn try_append_to_line(&mut self, in_box: @RenderBox, flow: &mut InlineFlow) -> bool {
let line_is_empty: bool = self.pending_line.range.length() == 0;
if line_is_empty {
@@ -313,7 +293,7 @@ impl LineboxScanner {
debug!("LineboxScanner: Trying to append box to line %u (box size: %?, green zone: \
%?): %s",
self.lines.len(),
- in_box.position().size,
+ in_box.base().position.get().size,
self.pending_line.green_zone,
in_box.debug_str());
@@ -326,6 +306,8 @@ impl LineboxScanner {
let new_height = self.new_height_for_line(in_box);
if new_height > green_zone.height {
+ debug!("LineboxScanner: entering float collision avoider!");
+
// Uh-oh. Adding this box is going to increase the height,
// and because of that we will collide with some floats.
@@ -367,9 +349,10 @@ impl LineboxScanner {
// horizontally. We'll try to place the whole box on this line and break somewhere
// if it doesn't fit.
- let new_width = self.pending_line.bounds.size.width + in_box.position().size.width;
+ let new_width = self.pending_line.bounds.size.width +
+ in_box.base().position.get().size.width;
- if(new_width <= green_zone.width){
+ if new_width <= green_zone.width {
debug!("LineboxScanner: case=box fits without splitting");
self.push_box_to_line(in_box);
return true;
@@ -441,28 +424,29 @@ impl LineboxScanner {
}
// unconditional push
- fn push_box_to_line(&mut self, box: RenderBox) {
- debug!("LineboxScanner: Pushing box b%d to line %u", box.id(), self.lines.len());
+ fn push_box_to_line(&mut self, box: @RenderBox) {
+ debug!("LineboxScanner: Pushing box b%d to line %u", box.base().id(), self.lines.len());
if self.pending_line.range.length() == 0 {
assert!(self.new_boxes.len() <= (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.bounds.size.width = self.pending_line.bounds.size.width + box.position().size.width;
+ self.pending_line.bounds.size.width = self.pending_line.bounds.size.width +
+ box.base().position.get().size.width;
self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height,
- box.position().size.height);
+ box.base().position.get().size.height);
self.new_boxes.push(box);
}
}
-pub struct InlineFlowData {
+pub struct InlineFlow {
/// Data common to all flows.
- common: FlowData,
+ base: FlowData,
// A vec of all inline render boxes. Several boxes may
// correspond to one Node/Element.
- boxes: ~[RenderBox],
+ boxes: ~[@RenderBox],
// vec of ranges into boxes that represents line positions.
// these ranges are disjoint, and are the result of inline layout.
// also some metadata used for positioning lines
@@ -473,10 +457,10 @@ pub struct InlineFlowData {
elems: ElementMapping
}
-impl InlineFlowData {
- pub fn new(common: FlowData) -> InlineFlowData {
- InlineFlowData {
- common: common,
+impl InlineFlow {
+ pub fn new(base: FlowData) -> InlineFlow {
+ InlineFlow {
+ base: base,
boxes: ~[],
lines: ~[],
elems: ElementMapping::new(),
@@ -489,87 +473,103 @@ impl InlineFlowData {
}
self.boxes = ~[];
}
-}
-pub trait InlineLayout {
- fn starts_inline_flow(&self) -> bool;
-}
+ pub fn build_display_list_inline<E:ExtraDisplayListData>(&self,
+ builder: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ list: &Cell<DisplayList<E>>)
+ -> bool {
-impl InlineLayout for FlowContext {
- fn starts_inline_flow(&self) -> bool {
- match *self {
- InlineFlow(*) => true,
- _ => false
+ //TODO: implement inline iframe size messaging
+ if self.base.node.is_iframe_element() {
+ error!("inline iframe size messaging not implemented yet");
}
+
+ let abs_rect = Rect(self.base.abs_position, self.base.position.size);
+ if !abs_rect.intersects(dirty) {
+ return true;
+ }
+
+ // TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
+ // not recurse on a line if nothing in it can intersect the dirty region.
+ debug!("FlowContext[%d]: building display list for %u inline boxes",
+ self.base.id,
+ self.boxes.len());
+
+ for box in self.boxes.iter() {
+ box.build_display_list(builder, dirty, &self.base.abs_position, list)
+ }
+
+ // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
+ // should the flow be nested inside the box somehow?
+
+ // For now, don't traverse the subtree rooted here
+ true
}
}
-impl InlineFlowData {
- pub fn bubble_widths_inline(&mut self, ctx: &mut LayoutContext) {
+impl FlowContext for InlineFlow {
+ fn class(&self) -> FlowClass {
+ InlineFlowClass
+ }
+
+ fn as_immutable_inline<'a>(&'a self) -> &'a InlineFlow {
+ self
+ }
+
+ fn as_inline<'a>(&'a mut self) -> &'a mut InlineFlow {
+ self
+ }
+
+ fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut num_floats = 0;
- for kid in self.common.child_iter() {
- do kid.with_mut_base |base| {
- num_floats += base.num_floats;
- base.floats_in = FloatContext::new(base.num_floats);
- }
+ for kid in self.base.child_iter() {
+ let child_base = flow::mut_base(*kid);
+ num_floats += child_base.num_floats;
+ child_base.floats_in = FloatContext::new(child_base.num_floats);
}
{
let this = &mut *self;
- let mut min_width = Au(0);
- let mut pref_width = Au(0);
+ let mut min_width = Au::new(0);
+ let mut pref_width = Au::new(0);
for box in this.boxes.iter() {
- debug!("FlowContext[%d]: measuring %s", self.common.id, box.debug_str());
- min_width = Au::max(min_width, box.get_min_width(ctx));
- pref_width = Au::max(pref_width, box.get_pref_width(ctx));
+ debug!("FlowContext[%d]: measuring %s", self.base.id, box.debug_str());
+ let (this_minimum_width, this_preferred_width) =
+ box.minimum_and_preferred_widths();
+ min_width = Au::max(min_width, this_minimum_width);
+ pref_width = Au::max(pref_width, this_preferred_width);
}
- this.common.min_width = min_width;
- this.common.pref_width = pref_width;
- this.common.num_floats = num_floats;
+ this.base.min_width = min_width;
+ this.base.pref_width = pref_width;
+ this.base.num_floats = num_floats;
}
}
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
/// on this context, the context has had its width set by the parent context.
- pub fn assign_widths_inline(&mut self, _: &LayoutContext) {
+ fn assign_widths(&mut self, _: &mut LayoutContext) {
// Initialize content box widths if they haven't been initialized already.
//
// TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into
// `RenderBox`.
- debug!("assign_widths_inline: floats_in: %?", self.common.floats_in);
+ debug!("assign_widths_inline: floats_in: %?", self.base.floats_in);
{
let this = &mut *self;
for &box in this.boxes.iter() {
- match box {
- ImageRenderBoxClass(image_box) => {
- let width = box.image_width(image_box);
- image_box.base.position.size.width = width;
- }
- TextRenderBoxClass(_) => {
- // 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.
+ box.assign_width();
+ }
}
- for kid in self.common.child_iter() {
- do kid.with_mut_base |base| {
- base.position.size.width = self.common.position.size.width;
- base.is_inorder = self.common.is_inorder;
- }
+ for kid in self.base.child_iter() {
+ let child_base = flow::mut_base(*kid);
+ child_base.position.size.width = self.base.position.size.width;
+ child_base.is_inorder = self.base.is_inorder;
}
// There are no child contexts, so stop here.
@@ -580,16 +580,15 @@ impl InlineFlowData {
// 'inline-block' box that created this flow before recursing.
}
- pub fn assign_height_inorder_inline(&mut self, ctx: &mut LayoutContext) {
- for kid in self.common.child_iter() {
+ fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
+ for kid in self.base.child_iter() {
kid.assign_height_inorder(ctx);
}
- self.assign_height_inline(ctx);
+ self.assign_height(ctx);
}
- pub fn assign_height_inline(&mut self, _: &LayoutContext) {
-
- debug!("assign_height_inline: assigning height for flow %?", self.common.id);
+ fn assign_height(&mut self, _: &mut LayoutContext) {
+ debug!("assign_height_inline: assigning height for flow %?", self.base.id);
// Divide the boxes into lines
// TODO(#226): Get the CSS `line-height` property from the containing block's style to
@@ -597,33 +596,37 @@ impl InlineFlowData {
//
// TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to
// determine its height for computing linebox height.
- debug!("assign_height_inline: floats_in: %?", self.common.floats_in);
- let scanner_floats = self.common.floats_in.clone();
+ //
+ // TODO(pcwalton): Cache the linebox scanner?
+ debug!("assign_height_inline: floats_in: %?", self.base.floats_in);
+
+ let scanner_floats = self.base.floats_in.clone();
let mut scanner = LineboxScanner::new(scanner_floats);
+
+ // Access the linebox scanner.
scanner.scan_for_lines(self);
- let mut line_height_offset = Au(0);
+ let mut line_height_offset = Au::new(0);
// Now, go through each line and lay out the boxes inside
for line in self.lines.mut_iter() {
// We need to distribute extra width based on text-align.
let mut slack_width = line.green_zone.width - line.bounds.size.width;
- if slack_width < Au(0) {
- slack_width = Au(0);
+ if slack_width < Au::new(0) {
+ slack_width = Au::new(0);
}
- //assert!(slack_width >= Au(0), "Too many boxes on line");
+ //assert!(slack_width >= Au::new(0), "Too many boxes on line");
// Get the text alignment.
// TODO(Issue #222): use 'text-align' property from InlineFlow's
// block container, not from the style of the first box child.
- let linebox_align;
- if line.range.begin() < self.boxes.len() {
+ let linebox_align = if line.range.begin() < self.boxes.len() {
let first_box = self.boxes[line.range.begin()];
- linebox_align = first_box.text_align();
+ first_box.base().nearest_ancestor_element().style().Text.text_align
} else {
// Nothing to lay out, so assume left alignment.
- linebox_align = text_align::left;
- }
+ text_align::left
+ };
// Set the box x positions
let mut offset_x = line.bounds.origin.x;
@@ -632,28 +635,25 @@ impl InlineFlowData {
// TODO(Issue #213): implement `text-align: justify`
text_align::left | text_align::justify => {
for i in line.range.eachi() {
- do self.boxes[i].with_mut_base |base| {
- base.position.origin.x = offset_x;
- offset_x = offset_x + base.position.size.width;
- }
+ let box = self.boxes[i].base();
+ box.position.mutate().ptr.origin.x = offset_x;
+ offset_x = offset_x + box.position.get().size.width;
}
}
text_align::center => {
offset_x = offset_x + slack_width.scale_by(0.5);
for i in line.range.eachi() {
- do self.boxes[i].with_mut_base |base| {
- base.position.origin.x = offset_x;
- offset_x = offset_x + base.position.size.width;
- }
+ let box = self.boxes[i].base();
+ box.position.mutate().ptr.origin.x = offset_x;
+ offset_x = offset_x + box.position.get().size.width;
}
}
text_align::right => {
offset_x = offset_x + slack_width;
for i in line.range.eachi() {
- do self.boxes[i].with_mut_base |base| {
- base.position.origin.x = offset_x;
- offset_x = offset_x + base.position.size.width;
- }
+ let box = self.boxes[i].base();
+ box.position.mutate().ptr.origin.x = offset_x;
+ offset_x = offset_x + box.position.get().size.width;
}
}
};
@@ -663,46 +663,53 @@ impl InlineFlowData {
line.bounds.origin.y = line.bounds.origin.y + line_height_offset;
// Calculate the distance from baseline to the top of the linebox.
- let mut topmost = Au(0);
+ let mut topmost = Au::new(0);
// Calculate the distance from baseline to the bottom of the linebox.
- let mut bottommost = Au(0);
+ let mut bottommost = Au::new(0);
// Calculate the biggest height among boxes with 'top' value.
- let mut biggest_top = Au(0);
+ let mut biggest_top = Au::new(0);
// Calculate the biggest height among boxes with 'bottom' value.
- let mut biggest_bottom = Au(0);
+ let mut biggest_bottom = Au::new(0);
for box_i in line.range.eachi() {
let cur_box = self.boxes[box_i];
- let (top_from_base, bottom_from_base, ascent) = match cur_box {
- ImageRenderBoxClass(image_box) => {
- let mut height = cur_box.image_height(image_box);
+ let (top_from_base, bottom_from_base, ascent) = match cur_box.class() {
+ ImageRenderBoxClass => {
+ let image_box = cur_box.as_image_render_box();
+ let mut height = image_box.image_height();
// TODO: margin, border, padding's top and bottom should be calculated in advance,
// since baseline of image is bottom margin edge.
- let mut top = Au(0);
- let mut bottom = Au(0);
- do cur_box.with_model |model| {
+ let mut top;
+ let mut bottom;
+ {
+ let model = image_box.base.model.get();
top = model.border.top + model.padding.top + model.margin.top;
- bottom = model.border.bottom + model.padding.bottom + model.margin.bottom;
+ bottom = model.border.bottom + model.padding.bottom +
+ model.margin.bottom;
}
+
let noncontent_height = top + bottom;
height = height + noncontent_height;
- image_box.base.position.size.height = height;
- image_box.base.position.translate(&Point2D(Au(0), -height));
+
+ let position_ref = image_box.base.position.mutate();
+ position_ref.ptr.size.height = height;
+ position_ref.ptr.translate(&Point2D(Au::new(0), -height));
let ascent = height + bottom;
- (height, Au(0), ascent)
+ (height, Au::new(0), ascent)
},
- TextRenderBoxClass(text_box) => {
+ TextRenderBoxClass => {
+ let text_box = cur_box.as_text_render_box();
let range = &text_box.range;
let run = &text_box.run;
// Compute the height based on the line-height and font size
let text_bounds = run.metrics_for_range(range).bounding_box;
let em_size = text_bounds.size.height;
- let line_height = scanner.calculate_line_height(cur_box, em_size);
+ let line_height = text_box.base.calculate_line_height(em_size);
// Find the top and bottom of the content area.
// Those are used in text-top and text-bottom value of 'vertical-align'
@@ -710,12 +717,15 @@ impl InlineFlowData {
// Offset from the top of the box is 1/2 of the leading + ascent
let text_offset = text_ascent + (line_height - em_size).scale_by(0.5);
- text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)));
+ text_bounds.translate(&Point2D(text_box.base.position.get().origin.x,
+ Au::new(0)));
(text_offset, line_height - text_offset, text_ascent)
},
- GenericRenderBoxClass(generic_box) => {
- (generic_box.position.size.height, Au(0), generic_box.position.size.height)
+ GenericRenderBoxClass => {
+ let base = cur_box.base();
+ let height = base.position.get().size.height;
+ (height, Au::new(0), height)
},
// FIXME(pcwalton): This isn't very type safe!
_ => {
@@ -734,44 +744,40 @@ impl InlineFlowData {
// It should calculate the distance from baseline to the top of parent's content area.
// But, it is assumed now as font size of parent.
- let mut parent_text_top = Au(0);
+ let mut parent_text_top;
// It should calculate the distance from baseline to the bottom of parent's content area.
// But, it is assumed now as 0.
- let parent_text_bottom = Au(0);
- do cur_box.with_mut_base |base| {
- // Get parent node
- let parent = match base.node.parent_node() {
- None => base.node,
- Some(parent) => parent,
- };
-
- let font_size = parent.style().Font.font_size;
- parent_text_top = font_size;
- }
+ let parent_text_bottom = Au::new(0);
+ let cur_box_base = cur_box.base();
+ // Get parent node
+ let parent = cur_box_base.node.parent_node();
+
+ let font_size = parent.unwrap().style().Font.font_size;
+ parent_text_top = font_size;
// This flag decides whether topmost and bottommost are updated or not.
// That is, if the box has top or bottom value, no_update_flag becomes true.
let mut no_update_flag = false;
// Calculate a relative offset from baseline.
- let offset = match cur_box.vertical_align() {
+ let offset = match cur_box_base.vertical_align() {
vertical_align::baseline => {
-ascent
},
vertical_align::middle => {
// TODO: x-height value should be used from font info.
- let xheight = Au(0);
- -(xheight + scanner.box_height(cur_box)).scale_by(0.5)
+ let xheight = Au::new(0);
+ -(xheight + cur_box.box_height()).scale_by(0.5)
},
vertical_align::sub => {
// TODO: The proper position for subscripts should be used.
// Lower the baseline to the proper position for subscripts
- let sub_offset = Au(0);
+ let sub_offset = Au::new(0);
(sub_offset - ascent)
},
vertical_align::super_ => {
// TODO: The proper position for superscripts should be used.
// Raise the baseline to the proper position for superscripts
- let super_offset = Au(0);
+ let super_offset = Au::new(0);
(-super_offset - ascent)
},
vertical_align::text_top => {
@@ -808,8 +814,9 @@ impl InlineFlowData {
-(length + ascent)
},
vertical_align::Percentage(p) => {
- let pt_size = cur_box.font_style().pt_size;
- let line_height = scanner.calculate_line_height(cur_box, Au::from_pt(pt_size));
+ let pt_size = cur_box.base().font_style().pt_size;
+ let line_height = cur_box.base()
+ .calculate_line_height(Au::from_pt(pt_size));
let percent_offset = line_height.scale_by(p);
-(percent_offset + ascent)
}
@@ -824,9 +831,7 @@ impl InlineFlowData {
bottommost = bottom_from_base;
}
- do cur_box.with_mut_base |base| {
- base.position.origin.y = line.bounds.origin.y + offset;
- }
+ cur_box.base().position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
}
// Calculate the distance from baseline to the top of the biggest box with 'bottom' value.
@@ -849,21 +854,15 @@ impl InlineFlowData {
// All boxes' y position is updated following the new baseline offset.
for box_i in line.range.eachi() {
let cur_box = self.boxes[box_i];
- let adjust_offset = match cur_box.vertical_align() {
- vertical_align::top => {
- Au(0)
- },
- vertical_align::bottom => {
- baseline_offset + bottommost
- },
- _ => {
- baseline_offset
- }
+ let cur_box_base = cur_box.base();
+ let adjust_offset = match cur_box_base.vertical_align() {
+ vertical_align::top => Au::new(0),
+ vertical_align::bottom => baseline_offset + bottommost,
+ _ => baseline_offset,
};
- do cur_box.with_mut_base |base| {
- base.position.origin.y = base.position.origin.y + adjust_offset;
- }
+ cur_box_base.position.mutate().ptr.origin.y =
+ cur_box_base.position.get().origin.y + adjust_offset;
}
// This is used to set the top y position of the next linebox in the next loop.
@@ -871,48 +870,30 @@ impl InlineFlowData {
line.bounds.size.height = topmost + bottommost;
} // End of `lines.each` loop.
- self.common.position.size.height =
+ self.base.position.size.height =
if self.lines.len() > 0 {
self.lines.last().bounds.origin.y + self.lines.last().bounds.size.height
} else {
- Au(0)
+ Au::new(0)
};
- self.common.floats_out = scanner.floats_out().translate(Point2D(Au(0),
- -self.common.position.size.height));
+ self.base.floats_out = scanner.floats_out()
+ .translate(Point2D(Au::new(0),
+ -self.base.position.size.height));
}
- pub fn build_display_list_inline<E:ExtraDisplayListData>(&self,
- builder: &DisplayListBuilder,
- dirty: &Rect<Au>,
- list: &Cell<DisplayList<E>>)
- -> bool {
-
- //TODO: implement inline iframe size messaging
- if self.common.node.is_iframe_element() {
- error!("inline iframe size messaging not implemented yet");
- }
-
- let abs_rect = Rect(self.common.abs_position, self.common.position.size);
- if !abs_rect.intersects(dirty) {
- return true;
+ fn collapse_margins(&mut self,
+ _: bool,
+ _: &mut bool,
+ _: &mut Au,
+ _: &mut Au,
+ collapsing: &mut Au,
+ collapsible: &mut Au) {
+ *collapsing = Au::new(0);
+ // Non-empty inline flows prevent collapsing between the previous margion and the next.
+ if self.base.position.size.height > Au::new(0) {
+ *collapsible = Au::new(0);
}
-
- // TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
- // not recurse on a line if nothing in it can intersect the dirty region.
- debug!("FlowContext[%d]: building display list for %u inline boxes",
- self.common.id,
- self.boxes.len());
-
- for box in self.boxes.iter() {
- box.build_display_list(builder, dirty, &self.common.abs_position, list)
- }
-
- // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
- // should the flow be nested inside the box somehow?
-
- // For now, don't traverse the subtree rooted here
- true
}
}
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index 07a638a2604..cb029942710 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -11,7 +11,9 @@ use layout::aux::LayoutAuxMethods;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder};
-use layout::flow::FlowContext;
+use layout::flow::{FlowContext, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
+use layout::flow::{PostorderFlowTraversal};
+use layout::flow;
use layout::incremental::{RestyleDamage, BubbleWidths};
use std::cast::transmute;
@@ -42,7 +44,7 @@ use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
-use servo_net::local_image_cache::LocalImageCache;
+use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::tree::TreeNodeRef;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
@@ -67,6 +69,129 @@ struct LayoutTask {
profiler_chan: ProfilerChan,
}
+/// The damage computation traversal.
+#[deriving(Clone)]
+struct ComputeDamageTraversal;
+
+impl PostorderFlowTraversal for ComputeDamageTraversal {
+ #[inline]
+ fn process(&mut self, flow: &mut FlowContext) -> bool {
+ let mut damage = flow::base(flow).restyle_damage;
+ for child in flow::child_iter(flow) {
+ damage.union_in_place(flow::base(*child).restyle_damage)
+ }
+ flow::mut_base(flow).restyle_damage = damage;
+ true
+ }
+}
+
+/// Propagates restyle damage up and down the tree as appropriate.
+///
+/// FIXME(pcwalton): Merge this with flow tree building and/or other traversals.
+struct PropagateDamageTraversal {
+ resized: bool,
+}
+
+impl PreorderFlowTraversal for PropagateDamageTraversal {
+ #[inline]
+ fn process(&mut self, flow: &mut FlowContext) -> bool {
+ // Also set any damage implied by resize.
+ if self.resized {
+ flow::mut_base(flow).restyle_damage.union_in_place(RestyleDamage::for_resize())
+ }
+
+ let prop = flow::base(flow).restyle_damage.propagate_down();
+ if prop.is_nonempty() {
+ for kid_ctx in flow::child_iter(flow) {
+ flow::mut_base(*kid_ctx).restyle_damage.union_in_place(prop)
+ }
+ }
+ true
+ }
+}
+
+/// The bubble-widths traversal, the first part of layout computation. This computes preferred
+/// and intrinsic widths and bubbles them up the tree.
+struct BubbleWidthsTraversal<'self>(&'self mut LayoutContext);
+
+impl<'self> PostorderFlowTraversal for BubbleWidthsTraversal<'self> {
+ #[inline]
+ fn process(&mut self, flow: &mut FlowContext) -> bool {
+ flow.bubble_widths(**self);
+ true
+ }
+
+ #[inline]
+ fn should_prune(&mut self, flow: &mut FlowContext) -> bool {
+ flow::mut_base(flow).restyle_damage.lacks(BubbleWidths)
+ }
+}
+
+/// The assign-widths traversal. In Gecko this corresponds to `Reflow`.
+struct AssignWidthsTraversal<'self>(&'self mut LayoutContext);
+
+impl<'self> PreorderFlowTraversal for AssignWidthsTraversal<'self> {
+ #[inline]
+ fn process(&mut self, flow: &mut FlowContext) -> bool {
+ flow.assign_widths(**self);
+ true
+ }
+}
+
+/// The assign-heights traversal, the last (and most expensive) part of layout computation.
+/// Determines the final heights for all layout objects. In Gecko this corresponds to
+/// `FinishAndStoreOverflow`.
+struct AssignHeightsTraversal<'self>(&'self mut LayoutContext);
+
+impl<'self> PostorderFlowTraversal for AssignHeightsTraversal<'self> {
+ #[inline]
+ fn process(&mut self, flow: &mut FlowContext) -> bool {
+ flow.assign_height(**self);
+ true
+ }
+
+ #[inline]
+ fn should_process(&mut self, flow: &mut FlowContext) -> bool {
+ !flow::base(flow).is_inorder
+ }
+}
+
+/// The display list building traversal. In WebKit this corresponds to `paint`. In Gecko this
+/// corresponds to `BuildDisplayListForChild`.
+struct DisplayListBuildingTraversal<'self> {
+ builder: DisplayListBuilder<'self>,
+ root_pos: Rect<Au>,
+ display_list: ~Cell<DisplayList<AbstractNode<()>>>,
+}
+
+impl<'self> PreorderFlowTraversal for DisplayListBuildingTraversal<'self> {
+ #[inline]
+ fn process(&mut self, _: &mut FlowContext) -> bool {
+ true
+ }
+
+ #[inline]
+ fn should_prune(&mut self, flow: &mut FlowContext) -> bool {
+ flow.build_display_list(&self.builder, &self.root_pos, self.display_list)
+ }
+}
+
+struct LayoutImageResponder {
+ id: PipelineId,
+ script_chan: ScriptChan,
+}
+
+impl ImageResponder for LayoutImageResponder {
+ fn respond(&self) -> ~fn(ImageResponseMsg) {
+ let id = self.id.clone();
+ let script_chan = self.script_chan.clone();
+ let f: ~fn(ImageResponseMsg) = |_| {
+ script_chan.send(SendEventMsg(id.clone(), ReflowEvent))
+ };
+ f
+ }
+}
+
impl LayoutTask {
pub fn create(id: PipelineId,
port: Port<Msg>,
@@ -180,14 +305,13 @@ impl LayoutTask {
// FIXME: Bad copy!
let doc_url = data.url.clone();
- let script_chan = data.script_chan.clone();
debug!("layout: received layout request for: %s", doc_url.to_str());
debug!("layout: damage is %?", data.damage);
debug!("layout: parsed Node tree");
debug!("%?", node.dump());
// Reset the image cache.
- self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan));
+ self.local_image_cache.next_round(self.make_on_image_available_cb());
self.doc_url = Some(doc_url);
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
@@ -218,10 +342,10 @@ impl LayoutTask {
}
// Construct the flow tree.
- let mut layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory,
- self.profiler_chan.clone()) {
+ let mut layout_root: ~FlowContext: = do profile(time::LayoutTreeBuilderCategory,
+ self.profiler_chan.clone()) {
let mut builder = LayoutTreeBuilder::new();
- let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
+ let layout_root: ~FlowContext: = match builder.construct_trees(&layout_ctx, *node) {
Ok(root) => root,
Err(*) => fail!(~"Root flow should always exist")
};
@@ -229,41 +353,11 @@ impl LayoutTask {
layout_root
};
- // Propagate restyle damage up and down the tree, as appropriate.
- // FIXME: Merge this with flow tree building and/or the other traversals.
- do layout_root.each_preorder |flow| {
- // Also set any damage implied by resize.
- if resized {
- do flow.with_mut_base |base| {
- base.restyle_damage.union_in_place(RestyleDamage::for_resize());
- }
- }
-
- let prop = flow.with_base(|base| base.restyle_damage.propagate_down());
- if prop.is_nonempty() {
- for kid_ctx in flow.child_iter() {
- do kid_ctx.with_mut_base |kid| {
- kid.restyle_damage.union_in_place(prop);
- }
- }
- }
- true
- };
-
- do layout_root.each_postorder |flow| {
- let mut damage = do flow.with_base |base| {
- base.restyle_damage
- };
- for child in flow.child_iter() {
- do child.with_base |child_base| {
- damage.union_in_place(child_base.restyle_damage);
- }
- }
- do flow.with_mut_base |base| {
- base.restyle_damage = damage;
- }
- true
- };
+ // Propagate damage.
+ layout_root.traverse_preorder(&mut PropagateDamageTraversal {
+ resized: resized,
+ });
+ layout_root.traverse_postorder(&mut ComputeDamageTraversal.clone());
debug!("layout: constructed Flow tree");
debug!("%?", layout_root.dump());
@@ -271,51 +365,37 @@ impl LayoutTask {
// Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes.
do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
- do layout_root.each_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) |flow| {
- flow.bubble_widths(&mut layout_ctx);
- true
- };
+ let _ = layout_root.traverse_postorder(&mut BubbleWidthsTraversal(&mut layout_ctx));
- // FIXME: We want to do
+ // FIXME(kmc): We want to do
// for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow))
// but FloatContext values can't be reused, so we need to recompute them every time.
// NOTE: this currently computes borders, so any pruning should separate that operation out.
- debug!("assigning widths");
- do layout_root.each_preorder |flow| {
- flow.assign_widths(&mut layout_ctx);
- true
- };
+ let _ = layout_root.traverse_preorder(&mut AssignWidthsTraversal(&mut layout_ctx));
// For now, this is an inorder traversal
// FIXME: prune this traversal as well
- debug!("assigning height");
- do layout_root.each_bu_sub_inorder |flow| {
- flow.assign_height(&mut layout_ctx);
- true
- };
+ let _ = layout_root.traverse_postorder(&mut AssignHeightsTraversal(&mut layout_ctx));
}
// Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay {
do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) {
- let builder = DisplayListBuilder {
- ctx: &layout_ctx,
- };
-
- let display_list = ~Cell::new(DisplayList::<AbstractNode<()>>::new());
-
// TODO: Set options on the builder before building.
// TODO: Be smarter about what needs painting.
- let root_pos = &layout_root.position().clone();
- layout_root.each_preorder_prune(|flow| {
- flow.build_display_list(&builder, root_pos, display_list)
- }, |_| { true } );
-
- let root_size = do layout_root.with_base |base| {
- base.position.size
+ let mut traversal = DisplayListBuildingTraversal {
+ builder: DisplayListBuilder {
+ ctx: &layout_ctx,
+ },
+ root_pos: flow::base(layout_root).position.clone(),
+ display_list: ~Cell::new(DisplayList::<AbstractNode<()>>::new()),
};
- let display_list = Arc::new(display_list.take());
+ let _ = layout_root.traverse_preorder(&mut traversal);
+
+ let root_size = flow::base(layout_root).position.size;
+
+ let display_list = Arc::new(traversal.display_list.take());
for i in range(0,display_list.get().list.len()) {
let node: AbstractNode<LayoutView> = unsafe {
@@ -445,7 +525,7 @@ impl LayoutTask {
Some(ref list) => {
let display_list = list.get();
let (x, y) = (Au::from_frac_px(point.x as f64),
- Au::from_frac_px(point.y as f64));
+ Au::from_frac_px(point.y as f64));
let mut resp = Err(());
// iterate in reverse to ensure we have the most recently painted render box
for display_item in display_list.list.rev_iter() {
@@ -481,21 +561,15 @@ impl LayoutTask {
// to the script task, and ultimately cause the image to be
// re-requested. We probably don't need to go all the way back to
// the script task for this.
- fn make_on_image_available_cb(&self, script_chan: ScriptChan)
- -> ~fn() -> ~fn(ImageResponseMsg) {
+ fn make_on_image_available_cb(&self) -> @ImageResponder {
// This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event
// channel is not a copyable type, so this is actually a
// little factory to produce callbacks
- let id = self.id.clone();
- let f: ~fn() -> ~fn(ImageResponseMsg) = || {
- let script_chan = script_chan.clone();
- let f: ~fn(ImageResponseMsg) = |_| {
- script_chan.send(SendEventMsg(id.clone(), ReflowEvent))
- };
- f
- };
- return f;
+ @LayoutImageResponder {
+ id: self.id.clone(),
+ script_chan: self.script_chan.clone(),
+ } as @ImageResponder
}
}
diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs
index 9225c8408fd..cc66ec1f70d 100644
--- a/src/components/main/layout/model.rs
+++ b/src/components/main/layout/model.rs
@@ -11,6 +11,7 @@ use style::ComputedValues;
use style::properties::common_types::computed;
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
+#[deriving(Clone)]
pub struct BoxModel {
border: SideOffsets2D<Au>,
padding: SideOffsets2D<Au>,
@@ -26,7 +27,9 @@ pub enum MaybeAuto {
}
impl MaybeAuto {
- pub fn from_style(length: computed::LengthOrPercentageOrAuto, containing_length: Au) -> MaybeAuto {
+ #[inline]
+ pub fn from_style(length: computed::LengthOrPercentageOrAuto, containing_length: Au)
+ -> MaybeAuto {
match length {
computed::LPA_Auto => Auto,
computed::LPA_Percentage(percent) => Specified(containing_length.scale_by(percent)),
@@ -34,6 +37,7 @@ impl MaybeAuto {
}
}
+ #[inline]
pub fn specified_or_default(&self, default: Au) -> Au {
match *self {
Auto => default,
@@ -41,8 +45,9 @@ impl MaybeAuto {
}
}
+ #[inline]
pub fn specified_or_zero(&self) -> Au {
- self.specified_or_default(Au(0))
+ self.specified_or_default(Au::new(0))
}
}
@@ -62,20 +67,24 @@ impl Zero for BoxModel {
}
impl BoxModel {
- /// Populates the box model parameters from the given computed style.
+ /// Populates the box model border parameters from the given computed style.
pub fn compute_borders(&mut self, style: &ComputedValues) {
- // Compute the borders.
self.border.top = style.Border.border_top_width;
self.border.right = style.Border.border_right_width;
self.border.bottom = style.Border.border_bottom_width;
self.border.left = style.Border.border_left_width;
}
+ /// Populates the box model padding parameters from the given computed style.
pub fn compute_padding(&mut self, style: &ComputedValues, containing_width: Au) {
- self.padding.top = self.compute_padding_length(style.Padding.padding_top, containing_width);
- self.padding.right = self.compute_padding_length(style.Padding.padding_right, containing_width);
- self.padding.bottom = self.compute_padding_length(style.Padding.padding_bottom, containing_width);
- self.padding.left = self.compute_padding_length(style.Padding.padding_left, containing_width);
+ self.padding.top = self.compute_padding_length(style.Padding.padding_top,
+ containing_width);
+ self.padding.right = self.compute_padding_length(style.Padding.padding_right,
+ containing_width);
+ self.padding.bottom = self.compute_padding_length(style.Padding.padding_bottom,
+ containing_width);
+ self.padding.left = self.compute_padding_length(style.Padding.padding_left,
+ containing_width);
}
pub fn noncontent_width(&self) -> Au {
diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs
index a14dd2bf74a..77a8831d137 100644
--- a/src/components/main/layout/text.rs
+++ b/src/components/main/layout/text.rs
@@ -8,51 +8,11 @@ use std::vec;
use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text};
-use layout::box::{RenderBox, RenderBoxBase, TextRenderBox};
-use layout::box::{TextRenderBoxClass, UnscannedTextRenderBoxClass};
+use layout::box::{RenderBox, RenderBoxUtils, TextRenderBox, UnscannedTextRenderBoxClass};
use layout::context::LayoutContext;
use layout::flow::FlowContext;
use servo_util::range::Range;
-
-/// Creates a TextRenderBox from a range and a text run.
-pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range)
- -> TextRenderBox {
- debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun (%s) (len=%u)",
- run.char_len(),
- range.begin(),
- range.length(),
- run.text,
- run.char_len());
-
- assert!(range.begin() < run.char_len());
- assert!(range.end() <= run.char_len());
- assert!(range.length() > 0);
-
- let metrics = run.metrics_for_range(&range);
- base.position.size = metrics.bounding_box.size;
-
- TextRenderBox {
- base: base,
- run: run,
- range: range,
- }
-}
-
-pub trait UnscannedMethods {
- /// 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(&self) -> ~str {
- match *self {
- UnscannedTextRenderBoxClass(text_box) => text_box.text.clone(),
- _ => fail!(~"unsupported operation: box.raw_text() on non-unscanned text box."),
- }
- }
-}
-
/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
struct TextRunScanner {
clump: Range,
@@ -67,7 +27,7 @@ impl TextRunScanner {
pub fn scan_for_runs(&mut self, ctx: &LayoutContext, flow: &mut FlowContext) {
{
- let inline = flow.imm_inline();
+ let inline = flow.as_immutable_inline();
// FIXME: this assertion fails on wikipedia, but doesn't seem
// to cause problems.
// assert!(inline.boxes.len() > 0);
@@ -76,9 +36,11 @@ impl TextRunScanner {
let mut last_whitespace = true;
let mut out_boxes = ~[];
- for box_i in range(0, flow.imm_inline().boxes.len()) {
- debug!("TextRunScanner: considering box: %?", flow.imm_inline().boxes[box_i].debug_str());
- if box_i > 0 && !can_coalesce_text_nodes(flow.imm_inline().boxes, box_i-1, box_i) {
+ for box_i in range(0, flow.as_immutable_inline().boxes.len()) {
+ debug!("TextRunScanner: considering box: %u", box_i);
+ if box_i > 0 && !can_coalesce_text_nodes(flow.as_immutable_inline().boxes,
+ box_i - 1,
+ box_i) {
last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
}
self.clump.extend_by(1);
@@ -91,17 +53,17 @@ impl TextRunScanner {
debug!("TextRunScanner: swapping out boxes.");
// Swap out the old and new box list of the flow.
- flow.inline().boxes = out_boxes;
+ flow.as_inline().boxes = out_boxes;
// A helper function.
- fn can_coalesce_text_nodes(boxes: &[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) {
- (UnscannedTextRenderBoxClass(*), UnscannedTextRenderBoxClass(*)) => {
+ match (left.class(), right.class()) {
+ (UnscannedTextRenderBoxClass, UnscannedTextRenderBoxClass) => {
left.can_merge_with_box(right)
}
(_, _) => false
@@ -123,18 +85,17 @@ impl TextRunScanner {
ctx: &LayoutContext,
flow: &mut FlowContext,
last_whitespace: bool,
- out_boxes: &mut ~[RenderBox]) -> bool {
- let inline = flow.inline();
+ out_boxes: &mut ~[@RenderBox])
+ -> bool {
+ let inline = flow.as_inline();
let in_boxes = &inline.boxes;
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()] {
- UnscannedTextRenderBoxClass(*) => true,
- _ => false
- };
+ let possible_text_clump = in_boxes[self.clump.begin()]; // FIXME(pcwalton): Rust bug
+ let is_text_clump = possible_text_clump.class() == UnscannedTextRenderBoxClass;
let mut new_whitespace = last_whitespace;
@@ -148,9 +109,9 @@ impl TextRunScanner {
},
(true, true) => {
let old_box = in_boxes[self.clump.begin()];
- let text = old_box.raw_text();
- let font_style = old_box.font_style();
- let decoration = old_box.text_decoration();
+ let text = old_box.as_unscanned_text_render_box().raw_text();
+ let font_style = old_box.base().font_style();
+ let decoration = old_box.base().text_decoration();
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
let compression = CompressWhitespaceNewline;
@@ -166,12 +127,10 @@ impl TextRunScanner {
let run = @fontgroup.create_textrun(transformed_text, decoration);
debug!("TextRunScanner: pushing single text box in range: %? (%?)", self.clump, text);
- let new_box = do old_box.with_base |old_box_base| {
- let range = Range::new(0, run.char_len());
- @mut adapt_textbox_with_range(*old_box_base, run, range)
- };
+ let range = Range::new(0, run.char_len());
+ let new_box = @TextRenderBox::new((*old_box.base()).clone(), run, range);
- out_boxes.push(TextRenderBoxClass(new_box));
+ out_boxes.push(new_box as @RenderBox);
}
},
(false, true) => {
@@ -185,7 +144,8 @@ impl TextRunScanner {
// `transform_text`, so that boxes starting and/or ending with whitespace can
// be compressed correctly with respect to the text run.
let idx = i + self.clump.begin();
- let (new_str, new_whitespace) = transform_text(in_boxes[idx].raw_text(),
+ let in_box = in_boxes[idx].as_unscanned_text_render_box().raw_text();
+ let (new_str, new_whitespace) = transform_text(in_box,
compression,
last_whitespace_in_clump);
last_whitespace_in_clump = new_whitespace;
@@ -210,9 +170,10 @@ impl TextRunScanner {
// TODO(#177): Text run creation must account for the renderability of text by
// font group fonts. This is probably achieved by creating the font group above
// and then letting `FontGroup` decide which `Font` to stick into the text run.
- let font_style = in_boxes[self.clump.begin()].font_style();
+ let in_box = in_boxes[self.clump.begin()];
+ let font_style = in_box.base().font_style();
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
- let decoration = in_boxes[self.clump.begin()].text_decoration();
+ let decoration = in_box.base().text_decoration();
// TextRuns contain a cycle which is usually resolved by the teardown
// sequence. If no clump takes ownership, however, it will leak.
@@ -234,10 +195,10 @@ impl TextRunScanner {
continue
}
- do in_boxes[i].with_base |base| {
- let new_box = @mut adapt_textbox_with_range(*base, run.unwrap(), range);
- out_boxes.push(TextRenderBoxClass(new_box));
- }
+ let new_box = @TextRenderBox::new((*in_boxes[i].base()).clone(),
+ run.unwrap(),
+ range);
+ out_boxes.push(new_box as @RenderBox);
}
}
} // End of match.
diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs
index c45ca1f3631..7701aa2bd66 100644
--- a/src/components/main/layout/util.rs
+++ b/src/components/main/layout/util.rs
@@ -2,7 +2,7 @@
* 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/. */
-use layout::box::{RenderBox};
+use layout::box::{RenderBox, RenderBoxUtils};
use script::dom::node::{AbstractNode, LayoutView};
use servo_util::range::Range;
@@ -46,7 +46,7 @@ impl ElementMapping {
self.entries.iter().enumerate()
}
- pub fn repair_for_box_changes(&mut self, old_boxes: &[RenderBox], new_boxes: &[RenderBox]) {
+ pub fn repair_for_box_changes(&mut self, old_boxes: &[@RenderBox], new_boxes: &[@RenderBox]) {
let entries = &mut self.entries;
debug!("--- Old boxes: ---");
@@ -88,18 +88,8 @@ impl ElementMapping {
repair_stack.push(item);
entries_k += 1;
}
- // 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 should_leave = do old_boxes[old_i].with_base |old_box_base| {
- do new_boxes[new_j].with_base |new_box_base| {
- old_box_base.node != new_box_base.node
- }
- };
- if should_leave {
- break
- }
-
+ while new_j < new_boxes.len() &&
+ old_boxes[old_i].base().node != new_boxes[new_j].base().node {
debug!("repair_for_box_changes: Slide through new box %u", new_j);
new_j += 1;
}
diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs
index fc34ecd9f29..9c89a07fb4e 100644
--- a/src/components/main/pipeline.rs
+++ b/src/components/main/pipeline.rs
@@ -44,7 +44,6 @@ impl Pipeline {
opts: Opts,
script_pipeline: &Pipeline,
size_future: Future<Size2D<uint>>) -> Pipeline {
-
let (layout_port, layout_chan) = special_stream!(LayoutChan);
let (render_port, render_chan) = special_stream!(RenderChan);
@@ -78,7 +77,6 @@ impl Pipeline {
script_pipeline.script_chan.clone(),
layout_chan,
render_chan)
-
}
pub fn create(id: PipelineId,
diff --git a/src/components/main/platform/common/glfw_windowing.rs b/src/components/main/platform/common/glfw_windowing.rs
index 6dee5c7f97b..88032a221b4 100644
--- a/src/components/main/platform/common/glfw_windowing.rs
+++ b/src/components/main/platform/common/glfw_windowing.rs
@@ -11,6 +11,8 @@ use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEven
use windowing::{Forward, Back};
use alert::{Alert, AlertMethods};
+use extra::time::Timespec;
+use extra::time;
use std::libc::c_int;
use std::local_data;
use geom::point::Point2D;
@@ -20,8 +22,6 @@ use servo_msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayou
use glfw;
-static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];
-
/// A structure responsible for setting up and tearing down the entire windowing system.
pub struct Application;
@@ -57,7 +57,8 @@ pub struct Window {
ready_state: ReadyState,
render_state: RenderState,
- throbber_frame: u8,
+
+ last_title_set_time: Timespec,
}
impl WindowMethods<Application> for Window {
@@ -81,7 +82,8 @@ impl WindowMethods<Application> for Window {
ready_state: Blank,
render_state: IdleRenderState,
- throbber_frame: 0,
+
+ last_title_set_time: Timespec::new(0, 0),
};
install_local_window(window);
@@ -141,8 +143,7 @@ impl WindowMethods<Application> for Window {
return self.event_queue.shift()
}
glfw::poll_events();
- self.throbber_frame = (self.throbber_frame + 1) % (THROBBER.len() as u8);
- self.update_window_title(false);
+
if self.glfw_window.should_close() {
QuitWindowEvent
} else if !self.event_queue.is_empty() {
@@ -155,7 +156,7 @@ impl WindowMethods<Application> for Window {
/// Sets the ready state.
fn set_ready_state(@mut self, ready_state: ReadyState) {
self.ready_state = ready_state;
- self.update_window_title(true)
+ self.update_window_title()
}
/// Sets the render state.
@@ -168,7 +169,7 @@ impl WindowMethods<Application> for Window {
}
self.render_state = render_state;
- self.update_window_title(true)
+ self.update_window_title()
}
fn hidpi_factor(@mut self) -> f32 {
@@ -180,25 +181,30 @@ impl WindowMethods<Application> for Window {
impl Window {
/// Helper function to set the window title in accordance with the ready state.
- fn update_window_title(&self, state_change: bool) {
- let throbber = THROBBER[self.throbber_frame];
+ fn update_window_title(&mut self) {
+ let now = time::get_time();
+ if now.sec == self.last_title_set_time.sec {
+ return
+ }
+ self.last_title_set_time = now;
+
match self.ready_state {
Blank => {
- if state_change { self.glfw_window.set_title(fmt!("blank — Servo")) }
+ self.glfw_window.set_title(fmt!("blank — Servo"))
}
Loading => {
- self.glfw_window.set_title(fmt!("%c Loading — Servo", throbber))
+ self.glfw_window.set_title(fmt!("Loading — Servo"))
}
PerformingLayout => {
- self.glfw_window.set_title(fmt!("%c Performing Layout — Servo", throbber))
+ self.glfw_window.set_title(fmt!("Performing Layout — Servo"))
}
FinishedLoading => {
match self.render_state {
RenderingRenderState => {
- self.glfw_window.set_title(fmt!("%c Rendering — Servo", throbber))
+ self.glfw_window.set_title(fmt!("Rendering — Servo"))
}
IdleRenderState => {
- if state_change { self.glfw_window.set_title("Servo") }
+ self.glfw_window.set_title("Servo")
}
}
}
diff --git a/src/components/net/local_image_cache.rs b/src/components/net/local_image_cache.rs
index c7c922e6910..2970c16bb5f 100644
--- a/src/components/net/local_image_cache.rs
+++ b/src/components/net/local_image_cache.rs
@@ -17,6 +17,10 @@ use std::task;
use servo_util::url::{UrlMap, url_map};
use extra::url::Url;
+pub trait ImageResponder {
+ fn respond(&self) -> ~fn(ImageResponseMsg);
+}
+
pub fn LocalImageCache(image_cache_task: ImageCacheTask) -> LocalImageCache {
LocalImageCache {
image_cache_task: image_cache_task,
@@ -29,7 +33,7 @@ pub fn LocalImageCache(image_cache_task: ImageCacheTask) -> LocalImageCache {
pub struct LocalImageCache {
priv image_cache_task: ImageCacheTask,
priv round_number: uint,
- priv on_image_available: Option<~fn() -> ~fn(ImageResponseMsg)>,
+ priv on_image_available: Option<@ImageResponder>,
priv state_map: UrlMap<@mut ImageState>
}
@@ -43,7 +47,7 @@ struct ImageState {
impl LocalImageCache {
/// The local cache will only do a single remote request for a given
/// URL in each 'round'. Layout should call this each time it begins
- pub fn next_round(&mut self, on_image_available: ~fn() -> ~fn(ImageResponseMsg)) {
+ pub fn next_round(&mut self, on_image_available: @ImageResponder) {
self.round_number += 1;
self.on_image_available = Some(on_image_available);
}
@@ -109,7 +113,7 @@ impl LocalImageCache {
// on the image to load and triggering layout
let image_cache_task = self.image_cache_task.clone();
assert!(self.on_image_available.is_some());
- let on_image_available = (*self.on_image_available.get_ref())();
+ let on_image_available = self.on_image_available.unwrap().respond();
let url = (*url).clone();
do task::spawn {
let (response_port, response_chan) = comm::stream();
diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs
index 9b8f7ea1b07..8b5478fefe2 100644
--- a/src/components/script/dom/node.rs
+++ b/src/components/script/dom/node.rs
@@ -882,7 +882,7 @@ pub struct DisplayBoxes {
/// Data that layout associates with a node.
pub struct LayoutData {
/// The results of CSS matching for this node.
- applicable_declarations: ~[@[PropertyDeclaration]],
+ applicable_declarations: ~[Arc<~[PropertyDeclaration]>],
/// The results of CSS styling for this node.
style: Option<ComputedValues>,
@@ -902,11 +902,21 @@ impl LayoutData {
applicable_declarations: ~[],
style: None,
restyle_damage: None,
- boxes: DisplayBoxes { display_list: None, range: None },
+ boxes: DisplayBoxes {
+ display_list: None,
+ range: None,
+ },
}
}
}
+// This serves as a static assertion that layout data remains sendable. If this is not done, then
+// we can have memory unsafety, which usually manifests as shutdown crashes.
+fn assert_is_sendable<T:Send>(_: T) {}
+fn assert_layout_data_is_sendable() {
+ assert_is_sendable(LayoutData::new())
+}
+
impl AbstractNode<LayoutView> {
// These accessors take a continuation rather than returning a reference, because
// an AbstractNode doesn't have a lifetime parameter relating to the underlying
diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs
index c6dca22ee21..460053d9686 100644
--- a/src/components/script/html/hubbub_html_parser.rs
+++ b/src/components/script/html/hubbub_html_parser.rs
@@ -28,8 +28,8 @@ use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::{Load, Payload, Done, ResourceTask, load_whole_resource};
use servo_util::tree::{TreeNodeRef, ElementLike};
use servo_util::url::make_url;
-use extra::url::Url;
use extra::future::Future;
+use extra::url::Url;
use geom::size::Size2D;
macro_rules! handle_element(
diff --git a/src/components/style/properties.rs.mako b/src/components/style/properties.rs.mako
index 5585e0ba764..e2f968d432f 100644
--- a/src/components/style/properties.rs.mako
+++ b/src/components/style/properties.rs.mako
@@ -5,6 +5,7 @@
// This file is a Mako template: http://www.makotemplates.org/
use std::ascii::StrAsciiExt;
+pub use extra::arc::Arc;
pub use std::iter;
pub use cssparser::*;
pub use errors::{ErrorLoggerIterator, log_css_error};
@@ -979,7 +980,7 @@ fn get_initial_values() -> ComputedValues {
// Most specific/important declarations last
-pub fn cascade(applicable_declarations: &[@[PropertyDeclaration]],
+pub fn cascade(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
parent_style: Option< &ComputedValues>)
-> ComputedValues {
let initial_keep_alive;
@@ -1002,7 +1003,7 @@ pub fn cascade(applicable_declarations: &[@[PropertyDeclaration]],
% endfor
};
for sub_list in applicable_declarations.iter() {
- for declaration in sub_list.iter() {
+ for declaration in sub_list.get().iter() {
match declaration {
% for property in LONGHANDS:
&${property.ident}_declaration(ref value) => {
diff --git a/src/components/style/selector_matching.rs b/src/components/style/selector_matching.rs
index a203df620e1..d6c7d75fdd0 100644
--- a/src/components/style/selector_matching.rs
+++ b/src/components/style/selector_matching.rs
@@ -2,8 +2,8 @@
* 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/. */
-use std::at_vec;
use std::ascii::StrAsciiExt;
+use extra::arc::Arc;
use extra::sort::tim_sort;
use selectors::*;
@@ -54,7 +54,7 @@ impl Stylist {
// TODO: avoid copying?
rules.$priority.push(Rule {
selector: @(*selector).clone(),
- declarations:at_vec::to_managed(style_rule.declarations.$priority),
+ declarations: Arc::new(style_rule.declarations.$priority.clone()),
})
}
}
@@ -79,7 +79,7 @@ impl Stylist {
pub fn get_applicable_declarations<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
&self, element: &T, style_attribute: Option<&PropertyDeclarationBlock>,
- pseudo_element: Option<PseudoElement>) -> ~[@[PropertyDeclaration]] {
+ pseudo_element: Option<PseudoElement>) -> ~[Arc<~[PropertyDeclaration]>] {
assert!(element.is_element())
assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements")
@@ -89,7 +89,7 @@ impl Stylist {
($rules: expr) => {
for rule in $rules.iter() {
if matches_selector::<N, T, E>(rule.selector, element, pseudo_element) {
- applicable_declarations.push(rule.declarations)
+ applicable_declarations.push(rule.declarations.clone())
}
}
};
@@ -102,11 +102,11 @@ impl Stylist {
// Style attributes have author origin but higher specificity than style rules.
append!(self.author_rules.normal);
// TODO: avoid copying?
- style_attribute.map(|sa| applicable_declarations.push(at_vec::to_managed(sa.normal)));
+ style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.normal.clone())));
append!(self.author_rules.important);
// TODO: avoid copying?
- style_attribute.map(|sa| applicable_declarations.push(at_vec::to_managed(sa.important)));
+ style_attribute.map(|sa| applicable_declarations.push(Arc::new(sa.important.clone())));
append!(self.user_rules.important);
append!(self.ua_rules.important);
@@ -131,7 +131,7 @@ impl PerOriginRules {
#[deriving(Clone)]
struct Rule {
selector: @Selector,
- declarations: @[PropertyDeclaration],
+ declarations: Arc<~[PropertyDeclaration]>,
}
diff --git a/src/components/util/geometry.rs b/src/components/util/geometry.rs
index 4acc8d8dd72..d67fae0c003 100644
--- a/src/components/util/geometry.rs
+++ b/src/components/util/geometry.rs
@@ -8,52 +8,78 @@ use geom::size::Size2D;
use std::num::{NumCast, One, Zero};
-#[deriving(Clone,Eq)]
+#[deriving(Clone)]
pub struct Au(i32);
+impl Eq for Au {
+ #[inline]
+ fn eq(&self, other: &Au) -> bool {
+ **self == **other
+ }
+ #[inline]
+ fn ne(&self, other: &Au) -> bool {
+ **self != **other
+ }
+}
+
impl Add<Au,Au> for Au {
+ #[inline]
fn add(&self, other: &Au) -> Au { Au(**self + **other) }
}
impl Sub<Au,Au> for Au {
+ #[inline]
fn sub(&self, other: &Au) -> Au { Au(**self - **other) }
}
impl Mul<Au,Au> for Au {
+ #[inline]
fn mul(&self, other: &Au) -> Au { Au(**self * **other) }
}
impl Div<Au,Au> for Au {
+ #[inline]
fn div(&self, other: &Au) -> Au { Au(**self / **other) }
}
impl Rem<Au,Au> for Au {
+ #[inline]
fn rem(&self, other: &Au) -> Au { Au(**self % **other) }
}
impl Neg<Au> for Au {
+ #[inline]
fn neg(&self) -> Au { Au(-**self) }
}
impl Ord for Au {
+ #[inline]
fn lt(&self, other: &Au) -> bool { **self < **other }
+ #[inline]
fn le(&self, other: &Au) -> bool { **self <= **other }
+ #[inline]
fn ge(&self, other: &Au) -> bool { **self >= **other }
+ #[inline]
fn gt(&self, other: &Au) -> bool { **self > **other }
}
impl One for Au {
+ #[inline]
fn one() -> Au { Au(1) }
}
impl Zero for Au {
+ #[inline]
fn zero() -> Au { Au(0) }
+ #[inline]
fn is_zero(&self) -> bool { **self == 0 }
}
impl Num for Au {}
+#[inline]
pub fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } }
+#[inline]
pub fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } }
impl NumCast for Au {
@@ -70,6 +96,14 @@ impl ToPrimitive for Au {
fn to_u64(&self) -> Option<u64> {
Some(**self as u64)
}
+
+ fn to_f32(&self) -> Option<f32> {
+ (**self).to_f32()
+ }
+
+ fn to_f64(&self) -> Option<f64> {
+ (**self).to_f64()
+ }
}
pub fn box<T:Clone + Ord + Add<T,T> + Sub<T,T>>(x: T, y: T, w: T, h: T) -> Rect<T> {
@@ -77,42 +111,58 @@ pub fn box<T:Clone + Ord + Add<T,T> + Sub<T,T>>(x: T, y: T, w: T, h: T) -> Rect<
}
impl Au {
+ /// FIXME(pcwalton): Workaround for lack of cross crate inlining of newtype structs!
+ #[inline]
+ pub fn new(value: i32) -> Au {
+ Au(value)
+ }
+
+ #[inline]
pub fn scale_by(self, factor: f64) -> Au {
- Au(((*self as f64) * factor).round() as i32)
+ Au(((*self as f64) * factor) as i32)
}
+ #[inline]
pub fn from_px(px: int) -> Au {
NumCast::from(px * 60).unwrap()
}
+ #[inline]
pub fn to_nearest_px(&self) -> int {
((**self as f64) / 60f64).round() as int
}
+ #[inline]
pub fn to_snapped(&self) -> Au {
let res = **self % 60i32;
return if res >= 30i32 { return Au(**self - res + 60i32) }
else { return Au(**self - res) };
}
+ #[inline]
pub fn zero_point() -> Point2D<Au> {
Point2D(Au(0), Au(0))
}
+ #[inline]
pub fn zero_rect() -> Rect<Au> {
let z = Au(0);
Rect(Point2D(z, z), Size2D(z, z))
}
+ #[inline]
pub fn from_pt(pt: f64) -> Au {
from_px(pt_to_px(pt) as int)
}
+ #[inline]
pub fn from_frac_px(px: f64) -> Au {
Au((px * 60f64) as i32)
}
+ #[inline]
pub fn min(x: Au, y: Au) -> Au { if *x < *y { x } else { y } }
+ #[inline]
pub fn max(x: Au, y: Au) -> Au { if *x > *y { x } else { y } }
}
@@ -159,3 +209,4 @@ pub fn to_frac_px(au: Au) -> f64 {
pub fn from_pt(pt: f64) -> Au {
from_px((pt / 72f64 * 96f64) as int)
}
+
diff --git a/src/components/util/range.rs b/src/components/util/range.rs
index a0943728a77..e52dbae02ab 100644
--- a/src/components/util/range.rs
+++ b/src/components/util/range.rs
@@ -22,58 +22,76 @@ pub struct Range {
}
impl Range {
+ #[inline]
pub fn new(off: uint, len: uint) -> Range {
- Range { off: off, len: len }
+ Range {
+ off: off,
+ len: len,
+ }
}
+ #[inline]
pub fn empty() -> Range {
Range::new(0, 0)
}
}
impl Range {
+ #[inline]
pub fn begin(&self) -> uint { self.off }
+ #[inline]
pub fn length(&self) -> uint { self.len }
+ #[inline]
pub fn end(&self) -> uint { self.off + self.len }
+ #[inline]
pub fn eachi(&self) -> iter::Range<uint> {
range(self.off, self.off + self.len)
}
+ #[inline]
pub fn contains(&self, i: uint) -> bool {
i >= self.begin() && i < self.end()
}
+ #[inline]
pub fn is_valid_for_string(&self, s: &str) -> bool {
self.begin() < s.len() && self.end() <= s.len() && self.length() <= s.len()
}
+ #[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
+ #[inline]
pub fn shift_by(&mut self, i: int) {
self.off = ((self.off as int) + i) as uint;
}
+ #[inline]
pub fn extend_by(&mut self, i: int) {
self.len = ((self.len as int) + i) as uint;
}
+ #[inline]
pub fn extend_to(&mut self, i: uint) {
self.len = i - self.off;
}
+ #[inline]
pub fn adjust_by(&mut self, off_i: int, len_i: int) {
self.off = ((self.off as int) + off_i) as uint;
self.len = ((self.len as int) + len_i) as uint;
}
+ #[inline]
pub fn reset(&mut self, off_i: uint, len_i: uint) {
self.off = off_i;
self.len = len_i;
}
+ #[inline]
pub fn intersect(&self, other: &Range) -> Range {
let begin = max(self.begin(), other.begin());
let end = min(self.end(), other.end());
@@ -88,6 +106,7 @@ impl Range {
/// Computes the relationship between two ranges (`self` and `other`),
/// from the point of view of `self`. So, 'EntirelyBefore' means
/// that the `self` range is entirely before `other` range.
+ #[inline]
pub fn relation_to_range(&self, other: &Range) -> RangeRelation {
if other.begin() > self.end() {
return EntirelyBefore;
@@ -116,6 +135,7 @@ impl Range {
self, other));
}
+ #[inline]
pub fn repair_after_coalesced_range(&mut self, other: &Range) {
let relation = self.relation_to_range(other);
debug!("repair_after_coalesced_range: possibly repairing range %?", self);
diff --git a/src/components/util/slot.rs b/src/components/util/slot.rs
new file mode 100644
index 00000000000..6c8eca9068f
--- /dev/null
+++ b/src/components/util/slot.rs
@@ -0,0 +1,127 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! An in-place, dynamically borrowable slot. Useful for "mutable fields". Assuming this works out
+//! well, this type should be upstreamed to the Rust standard library.
+
+use std::cast;
+
+#[unsafe_no_drop_flag]
+#[no_freeze]
+pub struct Slot<T> {
+ // NB: Must be priv, or else someone could borrow it.
+ priv value: T,
+ priv immutable_borrow_count: u8,
+ priv mutably_borrowed: bool,
+}
+
+impl<T:Clone> Clone for Slot<T> {
+ #[inline]
+ fn clone(&self) -> Slot<T> {
+ Slot {
+ value: self.value.clone(),
+ immutable_borrow_count: 0,
+ mutably_borrowed: false,
+ }
+ }
+}
+
+#[unsafe_destructor]
+impl<T> Drop for Slot<T> {
+ fn drop(&mut self) {
+ // Noncopyable.
+ }
+}
+
+pub struct SlotRef<'self,T> {
+ ptr: &'self T,
+ priv immutable_borrow_count: *mut u8,
+}
+
+#[unsafe_destructor]
+impl<'self,T> Drop for SlotRef<'self,T> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ *self.immutable_borrow_count -= 1
+ }
+ }
+}
+
+pub struct MutSlotRef<'self,T> {
+ ptr: &'self mut T,
+ priv mutably_borrowed: *mut bool,
+}
+
+#[unsafe_destructor]
+impl<'self,T> Drop for MutSlotRef<'self,T> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ *self.mutably_borrowed = false
+ }
+ }
+}
+
+impl<T> Slot<T> {
+ #[inline]
+ pub fn init(value: T) -> Slot<T> {
+ Slot {
+ value: value,
+ immutable_borrow_count: 0,
+ mutably_borrowed: false,
+ }
+ }
+
+ #[inline]
+ pub fn borrow<'a>(&'a self) -> SlotRef<'a,T> {
+ unsafe {
+ if self.immutable_borrow_count == 255 || self.mutably_borrowed {
+ self.fail()
+ }
+ let immutable_borrow_count = cast::transmute_mut(&self.immutable_borrow_count);
+ *immutable_borrow_count += 1;
+ SlotRef {
+ ptr: &self.value,
+ immutable_borrow_count: immutable_borrow_count,
+ }
+ }
+ }
+
+ #[inline]
+ pub fn mutate<'a>(&'a self) -> MutSlotRef<'a,T> {
+ unsafe {
+ if self.immutable_borrow_count > 0 || self.mutably_borrowed {
+ self.fail()
+ }
+ let mutably_borrowed = cast::transmute_mut(&self.mutably_borrowed);
+ *mutably_borrowed = true;
+ MutSlotRef {
+ ptr: cast::transmute_mut(&self.value),
+ mutably_borrowed: mutably_borrowed,
+ }
+ }
+ }
+
+ #[inline]
+ pub fn set(&self, value: T) {
+ *self.mutate().ptr = value
+ }
+
+ #[inline(never)]
+ pub fn fail(&self) {
+ fail!("slot is borrowed")
+ }
+}
+
+impl<T:Clone> Slot<T> {
+ #[inline]
+ pub fn get(&self) -> T {
+ self.value.clone()
+ }
+}
+
+
+
+
diff --git a/src/components/util/time.rs b/src/components/util/time.rs
index 35186ef3fa5..9937dbd40e0 100644
--- a/src/components/util/time.rs
+++ b/src/components/util/time.rs
@@ -2,23 +2,28 @@
* 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/. */
-// Timing functions.
-use std::comm::{Port, SharedChan};
-use std::iter::AdditiveIterator;
-use std::rt::io::timer::Timer;
-use std::task::spawn_with;
+//! Timing functions.
use extra::sort::tim_sort;
use extra::time::precise_time_ns;
use extra::treemap::TreeMap;
+use std::comm::{Port, SendDeferred, SharedChan};
+use std::iter::AdditiveIterator;
+use std::rt::io::timer::Timer;
+use std::task::spawn_with;
// front-end representation of the profiler used to communicate with the profiler
#[deriving(Clone)]
pub struct ProfilerChan(SharedChan<ProfilerMsg>);
+
impl ProfilerChan {
pub fn new(chan: Chan<ProfilerMsg>) -> ProfilerChan {
ProfilerChan(SharedChan::new(chan))
}
+
+ pub fn send_deferred(&self, msg: ProfilerMsg) {
+ (**self).send_deferred(msg);
+ }
}
pub enum ProfilerMsg {
@@ -185,7 +190,7 @@ pub fn profile<T>(category: ProfilerCategory,
let val = callback();
let end_time = precise_time_ns();
let ms = ((end_time - start_time) as f64 / 1000000f64);
- profiler_chan.send(TimeMsg(category, ms));
+ profiler_chan.send_deferred(TimeMsg(category, ms));
return val;
}
diff --git a/src/components/util/util.rc b/src/components/util/util.rc
index bbcf7203740..c84a87b13e5 100644
--- a/src/components/util/util.rc
+++ b/src/components/util/util.rc
@@ -16,6 +16,7 @@ extern mod geom;
pub mod cache;
pub mod geometry;
pub mod range;
+pub mod slot;
pub mod time;
pub mod tree;
pub mod url;
diff --git a/src/support/spidermonkey/mozjs b/src/support/spidermonkey/mozjs
-Subproject 81aeed0b6159acdedf7dd530fb6c3bde5ddbb70
+Subproject d57edb998865f9616d9164d561f26d54ade4964
diff --git a/src/test/html/longcatbot.png b/src/test/html/longcatbot.png
index 10b897e88a8..e1a4bc00fcf 100644
--- a/src/test/html/longcatbot.png
+++ b/src/test/html/longcatbot.png
Binary files differ
diff --git a/src/test/html/longcatmid.png b/src/test/html/longcatmid.png
index bd9a921ac64..e1b6f83eea5 100644
--- a/src/test/html/longcatmid.png
+++ b/src/test/html/longcatmid.png
Binary files differ
diff --git a/src/test/html/longcattop.png b/src/test/html/longcattop.png
index beb85663d3a..1d786dc1950 100644
--- a/src/test/html/longcattop.png
+++ b/src/test/html/longcattop.png
Binary files differ
diff --git a/src/test/html/rust-0.png b/src/test/html/rust-0.png
index dd0f5c102fc..20d93badf5e 100644
--- a/src/test/html/rust-0.png
+++ b/src/test/html/rust-0.png
Binary files differ
diff --git a/src/test/html/rust-135.png b/src/test/html/rust-135.png
index 1f4f8500432..d5a26f62139 100644
--- a/src/test/html/rust-135.png
+++ b/src/test/html/rust-135.png
Binary files differ
diff --git a/src/test/html/rust-180.png b/src/test/html/rust-180.png
index d3230266ce1..af2a0631b13 100644
--- a/src/test/html/rust-180.png
+++ b/src/test/html/rust-180.png
Binary files differ
diff --git a/src/test/html/rust-225.png b/src/test/html/rust-225.png
index 03252755fc6..b0a671ea3b1 100644
--- a/src/test/html/rust-225.png
+++ b/src/test/html/rust-225.png
Binary files differ
diff --git a/src/test/html/rust-270.png b/src/test/html/rust-270.png
index 2fc953bb363..0c7d1d7f002 100644
--- a/src/test/html/rust-270.png
+++ b/src/test/html/rust-270.png
Binary files differ
diff --git a/src/test/html/rust-315.png b/src/test/html/rust-315.png
index 4c41fad4c0c..5d15acd0cfb 100644
--- a/src/test/html/rust-315.png
+++ b/src/test/html/rust-315.png
Binary files differ
diff --git a/src/test/html/rust-45.png b/src/test/html/rust-45.png
index d1bc4bbc05d..b7dd1fd9c5a 100644
--- a/src/test/html/rust-45.png
+++ b/src/test/html/rust-45.png
Binary files differ
diff --git a/src/test/html/rust-90.png b/src/test/html/rust-90.png
index 0c956fce847..25fd8a66251 100644
--- a/src/test/html/rust-90.png
+++ b/src/test/html/rust-90.png
Binary files differ
diff --git a/src/test/html/test_sandboxed_iframe.html b/src/test/html/test_sandboxed_iframe.html
index d4dbb045ea7..061ea8c09a5 100644
--- a/src/test/html/test_sandboxed_iframe.html
+++ b/src/test/html/test_sandboxed_iframe.html
@@ -37,11 +37,11 @@ function run() {
var now = new Date();
var div = document.getElementsByTagName('div')[0];
var elems = [];
- for (var i = 0; i < 900; i++) {
+ for (var i = 0; i < 600; i++) {
elems.push(i);
}
var outer = [];
- for (var i = 0; i < 900; i++) {
+ for (var i = 0; i < 600; i++) {
outer.push(elems);
}
var a = new Matrix(outer);
@@ -56,10 +56,10 @@ function run() {
setTimeout(function forever() {
run();
- setTimeout(forever, 3000);
+ setTimeout(forever, 1000);
}, 0);
</script>
-<p>Time required to multiply two 900x900 matrices:</p>
+<p>Time required to multiply two 600x600 matrices:</p>
<div></div>
</body>
</html>