Optimize reflow by changing enums to traits and inlining more

This commit is contained in:
Patrick Walton 2013-10-24 16:57:42 -07:00
parent 81f5ba7d05
commit 42092921c1
44 changed files with 2725 additions and 2362 deletions

View file

@ -32,7 +32,7 @@ MKFILE_DEPS := config.stamp $(call rwildcard,$(S)mk/,*)
# Enable debug!() etc even without configure --enable-debug # Enable debug!() etc even without configure --enable-debug
# The evaluation of these prints & their arguments is controlled # The evaluation of these prints & their arguments is controlled
# at runtime by the environment variable RUST_LOG. # 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 ifdef CFG_DISABLE_OPTIMIZE
$(info cfg: disabling rustc optimization (CFG_DISABLE_OPTIMIZE)) $(info cfg: disabling rustc optimization (CFG_DISABLE_OPTIMIZE))

View file

@ -2,33 +2,29 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use 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 color::Color;
use font_context::FontContext; use font_context::FontContext;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use platform::font_context::FontContextHandle; use platform::font_context::FontContextHandle;
use platform::font::{FontHandle, FontTable}; use platform::font::{FontHandle, FontTable};
use render_context::RenderContext; 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::glyph::{GlyphStore, GlyphIndex};
use text::shaping::ShaperMethods; use text::shaping::ShaperMethods;
use text::{Shaper, TextRun}; 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, // FontHandle encapsulates access to the platform's font API,
// e.g. quartz, FreeType. It provides access to metrics and tables // 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> { 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();
let shaper = self.get_shaper(); do self.shape_cache.find_or_create(&text) |txt| {
do self.shape_cache.find_or_create(&text) |txt| { let mut glyphs = GlyphStore::new(text.char_len(), is_whitespace);
let mut glyphs = GlyphStore::new(text.char_len(), is_whitespace); shaper.shape_text(*txt, &mut glyphs);
shaper.shape_text(*txt, &mut glyphs); Arc::new(glyphs)
Arc::new(glyphs)
}
} }
} }

View file

@ -276,7 +276,7 @@ impl<'self> FontHandle {
} }
#[fixed_stack_segment] #[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, ()> { style: &SpecifiedFontStyle) -> Result<FontHandle, ()> {
unsafe { unsafe {
let ft_ctx: FT_Library = fctx.ctx.ctx; 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() { if FontHandle::set_char_size(face, style.pt_size).is_ok() {
Ok(FontHandle { Ok(FontHandle {
source: FontSourceFile(file), source: FontSourceFile(file.to_str()),
face: face, face: face,
handle: *fctx handle: *fctx
}) })

View file

@ -303,7 +303,11 @@ impl Ord for DetailedGlyphRecord {
// usage pattern of setting/appending all the detailed glyphs, and // usage pattern of setting/appending all the detailed glyphs, and
// then querying without setting. // then querying without setting.
struct DetailedGlyphStore { struct DetailedGlyphStore {
// TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
// optimization.
detail_buffer: ~[DetailedGlyph], detail_buffer: ~[DetailedGlyph],
// TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
// optimization.
detail_lookup: ~[DetailedGlyphRecord], detail_lookup: ~[DetailedGlyphRecord],
lookup_is_sorted: bool, lookup_is_sorted: bool,
} }
@ -506,8 +510,12 @@ impl<'self> GlyphInfo<'self> {
// Public data structure and API for storing and retrieving glyph data // Public data structure and API for storing and retrieving glyph data
pub struct GlyphStore { pub struct GlyphStore {
// TODO(pcwalton): Allocation of this buffer is expensive. Consider a small-vector
// optimization.
entry_buffer: ~[GlyphEntry], entry_buffer: ~[GlyphEntry],
detail_store: DetailedGlyphStore, detail_store: DetailedGlyphStore,
is_whitespace: bool, is_whitespace: bool,
} }
@ -602,6 +610,7 @@ impl<'self> GlyphStore {
self.iter_glyphs_for_char_range(&Range::new(i, 1)) self.iter_glyphs_for_char_range(&Range::new(i, 1))
} }
#[inline]
pub fn iter_glyphs_for_char_range(&'self self, rang: &Range) -> GlyphIterator<'self> { pub fn iter_glyphs_for_char_range(&'self self, rang: &Range) -> GlyphIterator<'self> {
if rang.begin() >= self.entry_buffer.len() { if rang.begin() >= self.entry_buffer.len() {
fail!("iter_glyphs_for_range: range.begin beyond length!"); fail!("iter_glyphs_for_range: range.begin beyond length!");
@ -682,23 +691,44 @@ pub struct GlyphIterator<'self> {
priv glyph_range: Option<iter::Range<uint>>, 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> { impl<'self> Iterator<(uint, GlyphInfo<'self>)> for GlyphIterator<'self> {
// I tried to start with something simpler and apply FlatMap, but the // I tried to start with something simpler and apply FlatMap, but the
// inability to store free variables in the FlatMap struct was problematic. // 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>)> { fn next(&mut self) -> Option<(uint, GlyphInfo<'self>)> {
// Would use 'match' here but it borrows contents in a way that // Would use 'match' here but it borrows contents in a way that
// interferes with mutation. // interferes with mutation.
if self.glyph_range.is_some() { if self.glyph_range.is_some() {
match self.glyph_range.get_mut_ref().next() { self.next_glyph_range()
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()
}
}
} else { } else {
// No glyph range. Look at next character. // No glyph range. Look at next character.
match self.char_range.next() { match self.char_range.next() {
@ -709,10 +739,8 @@ impl<'self> Iterator<(uint, GlyphInfo<'self>)> for GlyphIterator<'self> {
if entry.is_simple() { if entry.is_simple() {
Some((self.char_index, SimpleGlyphInfo(self.store, i))) Some((self.char_index, SimpleGlyphInfo(self.store, i)))
} else { } else {
let glyphs = self.store.detail_store // Fall back to the slow path.
.get_detailed_glyphs_for_entry(i, entry.glyph_count()); self.next_complex_glyph(entry, i)
self.glyph_range = Some(range(0, glyphs.len()));
self.next()
} }
}, },
None => None None => None

View file

@ -14,18 +14,19 @@ use style::computed_values::text_decoration;
/// A text run. /// A text run.
pub struct TextRun { pub struct TextRun {
text: ~str, text: Arc<~str>,
font: @mut Font, font: @mut Font,
decoration: text_decoration::T, 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 { pub struct SendableTextRun {
text: ~str, text: Arc<~str>,
font: FontDescriptor, font: FontDescriptor,
decoration: text_decoration::T, decoration: text_decoration::T,
priv glyphs: ~[Arc<GlyphStore>], priv glyphs: Arc<~[Arc<GlyphStore>]>,
} }
impl SendableTextRun { impl SendableTextRun {
@ -51,6 +52,8 @@ pub struct SliceIterator<'self> {
} }
impl<'self> Iterator<(&'self GlyphStore, uint, Range)> for 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)> { fn next(&mut self) -> Option<(&'self GlyphStore, uint, Range)> {
loop { loop {
let slice_glyphs = self.glyph_iter.next(); let slice_glyphs = self.glyph_iter.next();
@ -121,10 +124,10 @@ impl<'self> TextRun {
let glyphs = TextRun::break_and_shape(font, text); let glyphs = TextRun::break_and_shape(font, text);
let run = TextRun { let run = TextRun {
text: text, text: Arc::new(text),
font: font, font: font,
decoration: decoration, decoration: decoration,
glyphs: glyphs, glyphs: Arc::new(glyphs),
}; };
return run; return run;
} }
@ -198,12 +201,14 @@ impl<'self> TextRun {
} }
pub fn char_len(&self) -> uint { 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() 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 { pub fn range_is_trimmable_whitespace(&self, range: &Range) -> bool {
for (slice_glyphs, _, _) in self.iter_slices_for_range(range) { 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> { pub fn iter_slices_for_range(&'self self, range: &Range) -> SliceIterator<'self> {
SliceIterator { SliceIterator {
glyph_iter: self.glyphs.iter(), glyph_iter: self.glyphs.get().iter(),
range: *range, range: *range,
offset: 0, offset: 0,
} }

View file

@ -19,7 +19,8 @@ use script::script_task::SendEventMsg;
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch, Tile}; use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch, Tile};
use servo_msg::constellation_msg::PipelineId; use servo_msg::constellation_msg::PipelineId;
use std::cell::Cell; use std::cell::Cell;
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
use windowing::{MouseWindowMouseUpEvent};
#[cfg(not(target_os="macos"))] #[cfg(not(target_os="macos"))]
use layers::texturegl::TextureTarget2D; 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")] #[cfg(target_os="macos")]
fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) { fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) {
let flip = if cpu_painting { let flip = if cpu_painting {

View file

@ -9,8 +9,6 @@ use layout::incremental::RestyleDamage;
use style::ComputedValues; use style::ComputedValues;
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef;
/// Node mixin providing `style` method that returns a `NodeStyle` /// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode { pub trait StyledNode {
@ -20,7 +18,8 @@ pub trait StyledNode {
impl StyledNode for AbstractNode<LayoutView> { impl StyledNode for AbstractNode<LayoutView> {
fn style(&self) -> &ComputedValues { 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(); let results = self.get_css_select_results();
results results
} }

View file

@ -4,11 +4,11 @@
//! CSS block layout. //! CSS block layout.
use layout::box::{RenderBox}; use layout::box::{RenderBox, RenderBoxUtils};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow, FloatFlow, InlineFlow}; use layout::flow::{BlockFlowClass, FlowClass, FlowContext, FlowData, ImmutableFlowUtils};
use layout::inline::InlineLayout; use layout::flow;
use layout::model::{MaybeAuto, Specified, Auto}; use layout::model::{MaybeAuto, Specified, Auto};
use layout::float_context::{FloatContext, Invalid}; 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::{Au, to_frac_px};
use servo_util::geometry; use servo_util::geometry;
pub struct BlockFlowData { pub struct BlockFlow {
/// Data common to all flows. /// Data common to all flows.
common: FlowData, base: FlowData,
/// The associated render box. /// The associated render box.
box: Option<RenderBox>, box: Option<@RenderBox>,
/// Whether this block flow is the root flow. /// Whether this block flow is the root flow.
is_root: bool is_root: bool
} }
impl BlockFlowData { impl BlockFlow {
pub fn new(common: FlowData) -> BlockFlowData { pub fn new(base: FlowData) -> BlockFlow {
BlockFlowData { BlockFlow {
common: common, base: base,
box: None, box: None,
is_root: false is_root: false
} }
} }
pub fn new_root(common: FlowData) -> BlockFlowData { pub fn new_root(base: FlowData) -> BlockFlow {
BlockFlowData { BlockFlow {
common: common, base: base,
box: None, box: None,
is_root: true is_root: true
} }
@ -54,78 +54,18 @@ impl BlockFlowData {
} }
self.box = None; self.box = None;
} }
}
pub trait BlockLayout { /// Computes left and right margins and width based on CSS 2.1 section 10.3.3.
fn starts_root_flow(&self) -> bool; /// Requires borders and padding to already be computed.
fn starts_block_flow(&self) -> bool; fn compute_horiz(&self,
} width: MaybeAuto,
left_margin: MaybeAuto,
impl BlockLayout for FlowContext { right_margin: MaybeAuto,
fn starts_root_flow(&self) -> bool { available_width: Au)
match *self { -> (Au, Au, Au) {
BlockFlow(ref info) => info.is_root, // If width is not 'auto', and width + margins > available_width, all 'auto' margins are
_ => false // treated as 0.
} let (left_margin, right_margin) = match width {
}
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{
Auto => (left_margin, right_margin), Auto => (left_margin, right_margin),
Specified(width) => { Specified(width) => {
let left = left_margin.specified_or_zero(); 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)), (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 //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), (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(0)), (Specified(margin_l), Auto, Auto) => (margin_l, available_width - margin_l, Au::new(0)),
(Auto, Auto, Auto) => (Au(0), available_width, Au(0)), (Auto, Auto, Auto) => (Au::new(0), available_width, Au::new(0)),
//If left and right margins are auto, they become equal //If left and right margins are auto, they become equal
(Auto, Specified(width), Auto) => { (Auto, Specified(width), Auto) => {
@ -167,126 +107,32 @@ impl BlockFlowData {
(width_Au, left_margin_Au, right_margin_Au) (width_Au, left_margin_Au, right_margin_Au)
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called // inline(always) because this is only ever called by in-order or non-in-order top-level
/// on this context, the context has had its width set by the parent context. // methods
/// #[inline(always)]
/// 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);
}
fn assign_height_block_base(&mut self, ctx: &mut LayoutContext, inorder: bool) { fn assign_height_block_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
let mut cur_y = Au(0); let mut cur_y = Au::new(0);
let mut clearance = Au(0); let mut clearance = Au::new(0);
let mut top_offset = Au(0); let mut top_offset = Au::new(0);
let mut bottom_offset = Au(0); let mut bottom_offset = Au::new(0);
let mut left_offset = Au(0); let mut left_offset = Au::new(0);
let mut float_ctx = Invalid; let mut float_ctx = Invalid;
for &box in self.box.iter() { for &box in self.box.iter() {
clearance = match box.clear() { let base = box.base();
None => Au(0), clearance = match base.clear() {
None => Au::new(0),
Some(clear) => { Some(clear) => {
self.common.floats_in.clearance(clear) self.base.floats_in.clearance(clear)
} }
}; };
do box.with_model |model| { let mut model_ref = base.model.mutate();
top_offset = clearance + model.margin.top + model.border.top + model.padding.top; let model = &mut model_ref.ptr;
cur_y = cur_y + top_offset; top_offset = clearance + model.margin.top + model.border.top + model.padding.top;
bottom_offset = model.margin.bottom + model.border.bottom + model.padding.bottom; cur_y = cur_y + top_offset;
left_offset = model.offset(); bottom_offset = model.margin.bottom + model.border.bottom + model.padding.bottom;
}; left_offset = model.offset();
} }
if inorder { if inorder {
@ -297,84 +143,48 @@ impl BlockFlowData {
// visit child[i] // visit child[i]
// repeat until all children are visited. // repeat until all children are visited.
// last_child.floats_out -> self.floats_out (done at the end of this method) // 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)); float_ctx = self.base.floats_in.translate(Point2D(-left_offset, -top_offset));
for kid in self.common.child_iter() { for kid in self.base.child_iter() {
do kid.with_mut_base |child_node| { flow::mut_base(*kid).floats_in = float_ctx.clone();
child_node.floats_in = float_ctx.clone();
}
kid.assign_height_inorder(ctx); kid.assign_height_inorder(ctx);
do kid.with_mut_base |child_node| { float_ctx = flow::mut_base(*kid).floats_out.clone();
float_ctx = child_node.floats_out.clone();
}
} }
} }
let mut collapsible = Au(0); let mut collapsible = Au::new(0);
let mut collapsing = Au(0); let mut collapsing = Au::new(0);
let mut margin_top = Au(0); let mut margin_top = Au::new(0);
let mut margin_bottom = Au(0); let mut margin_bottom = Au::new(0);
let mut top_margin_collapsible = false; let mut top_margin_collapsible = false;
let mut bottom_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() { for &box in self.box.iter() {
do box.with_model |model| { let base = box.base();
if !self.is_root && model.border.top == Au(0) && model.padding.top == Au(0) { let mut model_ref = base.model.mutate();
collapsible = model.margin.top; let model = &mut model_ref.ptr;
top_margin_collapsible = true; if !self.is_root && model.border.top == Au::new(0) && model.padding.top == Au::new(0) {
} collapsible = model.margin.top;
if !self.is_root && model.border.bottom == Au(0) && model.padding.bottom == Au(0) { top_margin_collapsible = true;
bottom_margin_collapsible = true;
}
margin_top = model.margin.top;
margin_bottom = model.margin.bottom;
} }
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;
} }
for kid in self.common.child_iter() { for kid in self.base.child_iter() {
match *kid { kid.collapse_margins(top_margin_collapsible,
BlockFlow(ref info) => { &mut first_in_flow,
for &box in info.box.iter() { &mut margin_top,
do box.with_model |model| { &mut top_offset,
// The top margin collapses with its first in-flow block-level child's &mut collapsing,
// top margin if the parent has no top boder, no top padding. &mut collapsible);
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?
}
do kid.with_mut_base |child_node| { let child_node = flow::mut_base(*kid);
cur_y = cur_y - collapsing; cur_y = cur_y - collapsing;
child_node.position.origin.y = cur_y; child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height; cur_y = cur_y + child_node.position.size.height;
};
} }
// The bottom margin collapses with its last in-flow block-level child's bottom margin // The bottom margin collapses with its last in-flow block-level child's bottom margin
@ -385,7 +195,7 @@ impl BlockFlowData {
} }
collapsible collapsible
} else { } else {
Au(0) Au::new(0)
}; };
// TODO: A box's own margins collapse if the 'min-height' property is zero, and it has neither // 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() { for &box in self.box.iter() {
let style = box.style(); let base = box.base();
let maybe_height = MaybeAuto::from_style(style.Box.height, Au(0)); let style = base.style();
let maybe_height = MaybeAuto::from_style(style.Box.height, Au::new(0));
let maybe_height = maybe_height.specified_or_zero(); let maybe_height = maybe_height.specified_or_zero();
height = geometry::max(height, maybe_height); height = geometry::max(height, maybe_height);
} }
let mut noncontent_height = Au(0); let mut noncontent_height = Au::new(0);
for box in self.box.mut_iter() { for box in self.box.iter() {
do box.with_mut_base |base| { let base = box.base();
//The associated box is the border box of this flow let mut model_ref = base.model.mutate();
base.model.margin.top = margin_top; let mut position_ref = base.position.mutate();
base.model.margin.bottom = margin_bottom; let (model, position) = (&mut model_ref.ptr, &mut position_ref.ptr);
base.position.origin.y = clearance + base.model.margin.top; // The associated box is the border box of this flow.
model.margin.top = margin_top;
model.margin.bottom = margin_bottom;
noncontent_height = base.model.padding.top + base.model.padding.bottom + position.origin.y = clearance + model.margin.top;
base.model.border.top + base.model.border.bottom;
base.position.size.height = height + noncontent_height;
noncontent_height = noncontent_height + clearance + noncontent_height = model.padding.top + model.padding.bottom + model.border.top +
base.model.margin.top + base.model.margin.bottom; model.border.bottom;
} position.size.height = height + noncontent_height;
noncontent_height = noncontent_height + clearance + model.margin.top +
model.margin.bottom;
} }
//TODO(eatkinson): compute heights using the 'height' property. //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 { if inorder {
let extra_height = height - (cur_y - top_offset) + bottom_offset; 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 { } 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, pub fn build_display_list_block<E:ExtraDisplayListData>(
builder: &DisplayListBuilder, &mut self,
dirty: &Rect<Au>, builder: &DisplayListBuilder,
list: &Cell<DisplayList<E>>) dirty: &Rect<Au>,
-> bool { list: &Cell<DisplayList<E>>)
-> bool {
if self.common.node.is_iframe_element() { if self.base.node.is_iframe_element() {
let x = self.common.abs_position.x + do self.box.as_ref().map_default(Au(0)) |box| { let x = self.base.abs_position.x + do self.box.map_default(Au::new(0)) |box| {
box.with_model(|model| model.margin.left + model.border.left + model.padding.left) 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| { let y = self.base.abs_position.y + do self.box.map_default(Au::new(0)) |box| {
box.with_model(|model| model.margin.top + model.border.top + model.padding.top) 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| { let w = self.base.position.size.width - do self.box.map_default(Au::new(0)) |box| {
box.with_model(|model| model.noncontent_width()) box.base().model.get().noncontent_width()
}; };
let h = self.common.position.size.height - do self.box.as_ref().map_default(Au(0)) |box| { let h = self.base.position.size.height - do self.box.map_default(Au::new(0)) |box| {
box.with_model(|model| model.noncontent_height()) 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, iframe_element.size.get_mut_ref().set_rect(Rect(Point2D(to_frac_px(x) as f32,
to_frac_px(y) as f32), to_frac_px(y) as f32),
Size2D(to_frac_px(w) 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) { if !abs_rect.intersects(dirty) {
return true; return true;
} }
@ -471,18 +287,199 @@ impl BlockFlowData {
// add box that starts block context // add box that starts block context
for box in self.box.iter() { 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 // TODO: handle any out-of-flow elements
let this_position = self.common.abs_position; let this_position = self.base.abs_position;
for child in self.common.child_iter() { for child in self.base.child_iter() {
do child.with_mut_base |base| { let child_base = flow::mut_base(*child);
base.abs_position = this_position + base.position.origin; child_base.abs_position = this_position + child_base.position.origin;
}
} }
false 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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -4,16 +4,18 @@
//! Creates CSS boxes from a DOM tree. //! Creates CSS boxes from a DOM tree.
use layout::block::BlockFlowData; use layout::block::BlockFlow;
use layout::float::FloatFlowData; use layout::float::FloatFlow;
use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox}; use layout::box::{GenericRenderBox, GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass};
use layout::box::{RenderBoxBase, RenderBoxType, RenderBox_Generic, RenderBox_Image}; use layout::box::{RenderBox, RenderBoxBase, RenderBoxClass, RenderBoxUtils, TextRenderBoxClass};
use layout::box::{RenderBox_Text, UnscannedTextRenderBox, UnscannedTextRenderBoxClass}; use layout::box::{UnscannedTextRenderBox, UnscannedTextRenderBoxClass};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::flow::{AbsoluteFlow, BlockFlow, FloatFlow, Flow_Absolute, Flow_Block, Flow_Float}; use layout::float_context::FloatType;
use layout::flow::{Flow_Inline, Flow_InlineBlock, Flow_Root, Flow_Table, FlowContext}; use layout::flow::{AbsoluteFlow, BlockFlowClass, FloatFlowClass, FlowContext, FlowData};
use layout::flow::{FlowContextType, FlowData, InlineBlockFlow, InlineFlow, TableFlow}; use layout::flow::{ImmutableFlowUtils, InlineBlockFlow, InlineBlockFlowClass, InlineFlowClass};
use layout::inline::{InlineFlowData, InlineLayout}; use layout::flow::{MutableFlowUtils, TableFlow};
use layout::flow;
use layout::inline::{InlineFlow};
use layout::text::TextRunScanner; use layout::text::TextRunScanner;
use css::node_style::StyledNode; use css::node_style::StyledNode;
@ -25,8 +27,19 @@ use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId};
use script::dom::node::DocumentFragmentNodeTypeId; use script::dom::node::DocumentFragmentNodeTypeId;
use servo_util::range::Range; use servo_util::range::Range;
use servo_util::tree::{TreeNodeRef, TreeNode}; use servo_util::tree::{TreeNodeRef, TreeNode};
use std::cast;
use std::cell::Cell; use std::cell::Cell;
enum FlowType {
AbsoluteFlowType,
BlockFlowType,
FloatFlowType(FloatType),
InlineBlockFlowType,
InlineFlowType,
RootFlowType,
TableFlowType,
}
pub struct LayoutTreeBuilder { pub struct LayoutTreeBuilder {
next_cid: int, next_cid: int,
next_bid: int, next_bid: int,
@ -64,12 +77,15 @@ impl<'self> BoxGenerator<'self> {
} }
} }
fn with_clone<R> (&mut self, cb: &fn(BoxGenerator<'self>) -> R) -> R { fn with_clone<R>(&mut self, cb: &fn(BoxGenerator<'self>) -> R) -> R {
let gen = BoxGenerator { // FIXME(pcwalton): This is a hack; it can be done safely with linearity.
flow: &mut *self.flow, unsafe {
range_stack: self.range_stack let gen = BoxGenerator {
}; flow: cast::transmute_copy(&self.flow),
cb(gen) range_stack: self.range_stack
};
cb(gen)
}
} }
/* Whether "spacer" boxes are needed to stand in for this DOM node */ /* 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, fn make_inline_spacer_for_node_side(_: &LayoutContext,
_: AbstractNode<LayoutView>, _: AbstractNode<LayoutView>,
_: InlineSpacerSide) _: InlineSpacerSide)
-> Option<RenderBox> { -> Option<@RenderBox> {
None None
} }
@ -89,17 +105,18 @@ impl<'self> BoxGenerator<'self> {
ctx: &LayoutContext, ctx: &LayoutContext,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
builder: &mut LayoutTreeBuilder) { 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 // TODO: remove this once UA styles work
let box_type = self.decide_box_type(node); 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; let range_stack = &mut self.range_stack;
// depending on flow, make a box for this node. // depending on flow, make a box for this node.
match *self.flow { match self.flow.class() {
InlineFlow(ref mut inline) => { InlineFlowClass => {
let inline = self.flow.as_inline();
let node_range_start = inline.boxes.len(); let node_range_start = inline.boxes.len();
range_stack.push(node_range_start); range_stack.push(node_range_start);
@ -116,42 +133,43 @@ impl<'self> BoxGenerator<'self> {
} }
// TODO: cases for inline-block, etc. // TODO: cases for inline-block, etc.
}, },
BlockFlow(ref mut block) => { BlockFlowClass => {
debug!("BoxGenerator[f%d]: point b", block.common.id); 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); let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)", debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)",
block.common.id, block.base.id,
new_box.id(), new_box.base().id(),
node.debug_str()); node.debug_str());
assert!(block.box.is_none()); assert!(block.box.is_none());
block.box = Some(new_box); block.box = Some(new_box);
} }
FloatFlow(ref mut float) => { FloatFlowClass => {
debug!("BoxGenerator[f%d]: point b", float.common.id); 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); let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)", debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)",
float.common.id, float.base.id,
new_box.id(), new_box.base().id(),
node.debug_str()); node.debug_str());
assert!(float.box.is_none() && float.index.is_none()); assert!(float.box.is_none() && float.index.is_none());
float.box = Some(new_box); 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, pub fn pop_node(&mut self, ctx: &LayoutContext, node: AbstractNode<LayoutView>) {
ctx: &LayoutContext, debug!("BoxGenerator[f%d]: popping node: %s", flow::base(self.flow).id, node.debug_str());
node: AbstractNode<LayoutView>) {
debug!("BoxGenerator[f%d]: popping node: %s", self.flow.id(), node.debug_str());
match *self.flow { match self.flow.class() {
InlineFlow(ref mut inline) => { InlineFlowClass => {
let inline = self.flow.as_inline();
let inline = &mut *inline; let inline = &mut *inline;
if BoxGenerator::inline_spacers_needed_for_node(node) { if BoxGenerator::inline_spacers_needed_for_node(node) {
@ -173,24 +191,26 @@ impl<'self> BoxGenerator<'self> {
debug!("BoxGenerator: adding element range=%?", node_range); debug!("BoxGenerator: adding element range=%?", node_range);
inline.elems.add_mapping(node, &node_range); inline.elems.add_mapping(node, &node_range);
}, },
BlockFlow(*) => assert!(self.range_stack.len() == 0), BlockFlowClass => assert!(self.range_stack.len() == 0),
FloatFlow(*) => assert!(self.range_stack.len() == 0), FloatFlowClass => assert!(self.range_stack.len() == 0),
_ => warn!("pop_node() not implemented for flow %?", self.flow.id()), _ => 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 /// Disambiguate between different methods here instead of inlining, since each case has very
/// different complexity. /// different complexity.
fn make_box(layout_ctx: &LayoutContext, fn make_box(layout_ctx: &LayoutContext,
ty: RenderBoxType, ty: RenderBoxClass,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
builder: &mut LayoutTreeBuilder) builder: &mut LayoutTreeBuilder)
-> RenderBox { -> @RenderBox {
let base = RenderBoxBase::new(node, builder.next_box_id()); let base = RenderBoxBase::new(node, builder.next_box_id());
let result = match ty { let result = match ty {
RenderBox_Generic => GenericRenderBoxClass(@mut base), GenericRenderBoxClass => @GenericRenderBox::new(base) as @RenderBox,
RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)), TextRenderBoxClass | UnscannedTextRenderBoxClass => {
RenderBox_Image => BoxGenerator::make_image_box(layout_ctx, node, base), @UnscannedTextRenderBox::new(base) as @RenderBox
}
ImageRenderBoxClass => BoxGenerator::make_image_box(layout_ctx, node, base),
}; };
debug!("BoxGenerator: created box: %s", result.debug_str()); debug!("BoxGenerator: created box: %s", result.debug_str());
result result
@ -199,36 +219,36 @@ impl<'self> BoxGenerator<'self> {
fn make_image_box(layout_ctx: &LayoutContext, fn make_image_box(layout_ctx: &LayoutContext,
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
base: RenderBoxBase) base: RenderBoxBase)
-> RenderBox { -> @RenderBox {
assert!(node.is_image_element()); assert!(node.is_image_element());
do node.with_imm_image_element |image_element| { do node.with_imm_image_element |image_element| {
if image_element.image.is_some() { if image_element.image.is_some() {
// FIXME(pcwalton): Don't copy URLs. // FIXME(pcwalton): Don't copy URLs.
let url = (*image_element.image.get_ref()).clone(); 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 { } else {
info!("Tried to make image box, but couldn't find image. Made generic box \ info!("Tried to make image box, but couldn't find image. Made generic box \
instead."); instead.");
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() { if node.is_text() {
RenderBox_Text TextRenderBoxClass
} else if node.is_image_element() { } else if node.is_image_element() {
do node.with_imm_image_element |image_element| { do node.with_imm_image_element |image_element| {
match image_element.image { match image_element.image {
Some(_) => RenderBox_Image, Some(_) => ImageRenderBoxClass,
None => RenderBox_Generic, None => GenericRenderBoxClass,
} }
} }
} else if node.is_element() { } else if node.is_element() {
RenderBox_Generic GenericRenderBoxClass
} else { } 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, /// Creates necessary box(es) and flow context(s) for the current DOM node,
/// and recurses on its children. /// and recurses on its children.
pub fn construct_recursively<'a>(&mut self, pub fn construct_recursively<'a>(
layout_ctx: &LayoutContext, &mut self,
cur_node: AbstractNode<LayoutView>, layout_ctx: &LayoutContext,
mut grandparent_generator: Option<BoxGenerator<'a>>, cur_node: AbstractNode<LayoutView>,
mut parent_generator: BoxGenerator<'a>, mut grandparent_generator: Option<BoxGenerator<'a>>,
mut prev_sibling_generator: Option<BoxGenerator<'a>>) mut parent_generator: BoxGenerator<'a>,
-> BoxConstructResult<'a> { mut prev_sibling_generator: Option<BoxGenerator<'a>>)
-> BoxConstructResult<'a> {
debug!("Considering node: %s", cur_node.debug_str()); debug!("Considering node: %s", cur_node.debug_str());
let box_gen_result = { let box_gen_result = {
let grandparent_gen_ref = match grandparent_generator { let grandparent_gen_ref = match grandparent_generator {
@ -285,32 +306,29 @@ impl LayoutTreeBuilder {
debug!("result from generator_for_node: %?", &box_gen_result); debug!("result from generator_for_node: %?", &box_gen_result);
// Skip over nodes that don't belong in the flow tree // Skip over nodes that don't belong in the flow tree
let (this_generator, next_generator) = let (this_generator, next_generator) = match box_gen_result {
match box_gen_result { NoGenerator => return Normal(prev_sibling_generator),
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 => { ParentGenerator => {
do parent_generator.with_clone |clone| { do parent_generator.with_clone |clone| {
(clone, None) clone
} }
} }
SiblingGenerator => (prev_sibling_generator.take_unwrap(), None), SiblingGenerator => prev_sibling_generator.take_unwrap(),
NewGenerator(gen) => (gen, None), _ => fail!("Unexpect BoxGenResult")
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")
}))
};
let mut this_generator = this_generator; 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()) { let is_float = if (node.is_element()) {
match node.style().Box.float { match node.style().Box.float {
@ -407,84 +430,99 @@ impl LayoutTreeBuilder {
None None
}; };
let sibling_flow_class = match sibling_flow {
None => None,
Some(flow) => Some(flow.class()),
};
let new_generator = match (display, &mut parent_generator.flow, sibling_flow) { let new_generator = match (display, parent_generator.flow.class(), sibling_flow_class) {
// Floats // Floats
(display::block, & &BlockFlow(_), _) | (display::block, BlockFlowClass, _) |
(display::block, & &FloatFlow(_), _) if is_float.is_some() => { (display::block, FloatFlowClass, _) if is_float.is_some() => {
self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap())) 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, // 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 // then continue building from the inline flow in case there are more inlines
// afterward. // 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, let float_generator = self.create_child_generator(node,
sibling_generator.unwrap(), sibling_generator.unwrap(),
Flow_Float(is_float.unwrap())); float_type);
return Mixed(float_generator, ~SiblingGenerator); return Mixed(float_generator, ~SiblingGenerator);
} }
// This is a catch-all case for when: // This is a catch-all case for when:
// a) sibling_flow is None // a) sibling_flow is None
// b) sibling_flow is a BlockFlow // b) sibling_flow is a BlockFlow
(display::block, & &InlineFlow(_), _) if is_float.is_some() => { (display::block, InlineFlowClass, _) if is_float.is_some() => {
self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap())) 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()) { (display::block, BlockFlowClass, _) => {
// If this is the root node, then use the root flow's match (parent_generator.flow.as_block().is_root, node.parent_node()) {
// context. Otherwise, make a child block context. // If this is the root node, then use the root flow's
(true, true) => self.create_child_generator(node, parent_generator, Flow_Block), // context. Otherwise, make a child block context.
(true, false) => { return ParentGenerator } (true, Some(_)) => {
(false, _) => { self.create_child_generator(node, parent_generator, BlockFlowType)
self.create_child_generator(node, parent_generator, Flow_Block) }
(true, None) => return ParentGenerator,
(false, _) => {
self.create_child_generator(node, parent_generator, BlockFlowType)
}
} }
}, }
(display::block, & &FloatFlow(*), _) => { (display::block, FloatFlowClass, _) => {
self.create_child_generator(node, parent_generator, Flow_Block) self.create_child_generator(node, parent_generator, BlockFlowType)
} }
// Inlines that are children of inlines are part of the same flow // Inlines that are children of inlines are part of the same flow
(display::inline, & &InlineFlow(*), _) => return ParentGenerator, (display::inline, InlineFlowClass, _) => return ParentGenerator,
(display::inline_block, & &InlineFlow(*), _) => return ParentGenerator, (display::inline_block, InlineFlowClass, _) => return ParentGenerator,
// Inlines that are children of blocks create new flows if their // Inlines that are children of blocks create new flows if their
// previous sibling was a block. // previous sibling was a block.
(display::inline, & &BlockFlow(*), Some(&BlockFlow(*))) | (display::inline, BlockFlowClass, Some(BlockFlowClass)) |
(display::inline_block, & &BlockFlow(*), Some(&BlockFlow(*))) => { (display::inline_block, BlockFlowClass, Some(BlockFlowClass)) => {
self.create_child_generator(node, parent_generator, Flow_Inline) self.create_child_generator(node, parent_generator, InlineFlowType)
} }
// The first two cases should only be hit when a FloatFlow // The first two cases should only be hit when a FloatFlow
// is the first child of a BlockFlow. Other times, we will // is the first child of a BlockFlow. Other times, we will
(display::inline, _, Some(&FloatFlow(*))) | (display::inline, _, Some(FloatFlowClass)) |
(display::inline_block, _, Some(&FloatFlow(*))) | (display::inline_block, _, Some(FloatFlowClass)) |
(display::inline, & &FloatFlow(*), _) | (display::inline, FloatFlowClass, _) |
(display::inline_block, & &FloatFlow(*), _) => { (display::inline_block, FloatFlowClass, _) => {
self.create_child_generator(node, parent_generator, Flow_Inline) self.create_child_generator(node, parent_generator, InlineFlowType)
} }
// Inlines whose previous sibling was not a block try to use their // Inlines whose previous sibling was not a block try to use their
// sibling's flow context. // sibling's flow context.
(display::inline, & &BlockFlow(*), _) | (display::inline, BlockFlowClass, _) |
(display::inline_block, & &BlockFlow(*), _) => { (display::inline_block, BlockFlowClass, _) => {
return match sibling_generator { return match sibling_generator {
None => NewGenerator(self.create_child_generator(node, None => NewGenerator(self.create_child_generator(node,
parent_generator, parent_generator,
Flow_Inline)), InlineFlowType)),
Some(*) => SiblingGenerator Some(*) => SiblingGenerator
} }
} }
// blocks that are children of inlines need to split their parent // blocks that are children of inlines need to split their parent
// flows. // flows.
(display::block, & &InlineFlow(*), _) => { (display::block, InlineFlowClass, _) => {
match grandparent_generator { match grandparent_generator {
None => fail!("expected to have a grandparent block flow"), None => fail!("expected to have a grandparent block flow"),
Some(grandparent_gen) => { Some(grandparent_gen) => {
assert!(grandparent_gen.flow.is_block_like()); 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); return ReparentingGenerator(block_gen);
} }
} }
@ -496,15 +534,16 @@ impl LayoutTreeBuilder {
NewGenerator(new_generator) NewGenerator(new_generator)
} }
pub fn create_child_generator<'a>(&mut self, pub fn create_child_generator<'a>(
node: AbstractNode<LayoutView>, &mut self,
parent_generator: &mut BoxGenerator<'a>, node: AbstractNode<LayoutView>,
ty: FlowContextType) parent_generator: &mut BoxGenerator<'a>,
-> BoxGenerator<'a> { ty: FlowType)
-> BoxGenerator<'a> {
let new_flow = self.make_flow(ty, node); let new_flow = self.make_flow(ty, node);
parent_generator.flow.add_new_child(new_flow); 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: /// 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 /// 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. /// flow. Otherwise, the whitespace might affect whitespace collapsing with adjacent text.
pub fn simplify_children_of_flow(&self, ctx: &LayoutContext, parent_flow: &mut FlowContext) { pub fn simplify_children_of_flow(&self, ctx: &LayoutContext, parent_flow: &mut FlowContext) {
match *parent_flow { match parent_flow.class() {
InlineFlow(*) => { InlineFlowClass => {
let mut found_child_inline = false; let mut found_child_inline = false;
let mut found_child_block = false; let mut found_child_block = false;
for child_ctx in parent_flow.child_iter() { for child_ctx in flow::child_iter(parent_flow) {
match child_ctx { match child_ctx.class() {
&InlineFlow(*) | &InlineBlockFlow(*) => found_child_inline = true, InlineFlowClass | InlineBlockFlowClass => found_child_inline = true,
&BlockFlow(*) => found_child_block = true, BlockFlowClass => found_child_block = true,
_ => {} _ => {}
} }
} }
@ -532,23 +571,27 @@ impl LayoutTreeBuilder {
if found_child_block && found_child_inline { if found_child_block && found_child_inline {
self.fixup_split_inline(parent_flow) self.fixup_split_inline(parent_flow)
} }
}, }
BlockFlow(*) | FloatFlow(*) => { BlockFlowClass | FloatFlowClass => {
// check first/last child for whitespace-ness // check first/last child for whitespace-ness
let mut do_remove = false; 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| { do parent_flow.with_first_child |mut first_child| {
for first_flow in first_child.mut_iter() { for first_flow in first_child.mut_iter() {
if first_flow.starts_inline_flow() { if first_flow.starts_inline_flow() {
// FIXME: workaround for rust#6393 // FIXME: workaround for rust#6393
{ {
let boxes = &first_flow.imm_inline().boxes; let first_inline_flow = first_flow.as_inline();
if boxes.len() == 1 && boxes[0].is_whitespace_only() { let boxes = &first_inline_flow.boxes;
debug!("LayoutTreeBuilder: pruning whitespace-only first child \ if boxes.len() == 1 {
flow f%d from parent f%d", let first_box = boxes[0]; // FIXME(pcwalton): Rust bug
first_flow.id(), if first_box.is_whitespace_only() {
p_id); debug!("LayoutTreeBuilder: pruning whitespace-only first \
do_remove = true; 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; 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| { do parent_flow.with_last_child |mut last_child| {
for last_flow in last_child.mut_iter() { for last_flow in last_child.mut_iter() {
if last_flow.starts_inline_flow() { if last_flow.starts_inline_flow() {
// FIXME: workaround for rust#6393 // 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() { if boxes.len() == 1 && boxes.last().is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only last child \ let last_box = boxes.last(); // FIXME(pcwalton): Rust bug
flow f%d from parent f%d", if last_box.is_whitespace_only() {
last_flow.id(), debug!("LayoutTreeBuilder: pruning whitespace-only last \
p_id); child flow f%d from parent f%d",
do_remove = true; 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 // 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. // flows, but there's not a quick way to check at the moment.
for child_flow in (*parent_flow).child_iter() { for child_flow in flow::child_iter(parent_flow) {
match *child_flow { match child_flow.class() {
InlineFlow(*) | InlineBlockFlow(*) => { InlineFlowClass | InlineBlockFlowClass => {
let mut scanner = TextRunScanner::new(); 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. /// 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>) pub fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode<LayoutView>)
-> Result<FlowContext, ()> { -> Result<~FlowContext:, ()> {
debug!("Constructing flow tree for DOM: "); 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); self.construct_recursively(layout_ctx, root, None, new_generator, None);
} }
return Ok(new_flow) return Ok(new_flow)
} }
/// Creates a flow of the given type for the supplied node. /// 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 info = FlowData::new(self.next_flow_id(), node);
let result = match ty { let result = match flow_type {
Flow_Absolute => AbsoluteFlow(~info), AbsoluteFlowType => ~AbsoluteFlow::new(info) as ~FlowContext:,
Flow_Block => BlockFlow(~BlockFlowData::new(info)), BlockFlowType => ~BlockFlow::new(info) as ~FlowContext:,
Flow_Float(f_type) => FloatFlow(~FloatFlowData::new(info, f_type)), FloatFlowType(f_type) => ~FloatFlow::new(info, f_type) as ~FlowContext:,
Flow_InlineBlock => InlineBlockFlow(~info), InlineBlockFlowType => ~InlineBlockFlow::new(info) as ~FlowContext:,
Flow_Inline => InlineFlow(~InlineFlowData::new(info)), InlineFlowType => ~InlineFlow::new(info) as ~FlowContext:,
Flow_Root => BlockFlow(~BlockFlowData::new_root(info)), RootFlowType => ~BlockFlow::new_root(info) as ~FlowContext:,
Flow_Table => TableFlow(~info), TableFlowType => ~TableFlow::new(info) as ~FlowContext:,
}; };
debug!("LayoutTreeBuilder: created flow: %s", result.debug_str()); debug!("LayoutTreeBuilder: created flow: %s", result.debug_str());
result result

View file

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

View file

@ -2,10 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::box::{RenderBox}; use layout::box::{RenderBox, RenderBoxUtils};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{FlowData}; use layout::flow::{FloatFlowClass, FlowClass, FlowContext, FlowData};
use layout::flow;
use layout::model::{MaybeAuto}; use layout::model::{MaybeAuto};
use layout::float_context::{FloatContext, PlacementInfo, FloatType}; 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::Au;
use servo_util::geometry; use servo_util::geometry;
pub struct FloatFlowData { pub struct FloatFlow {
/// Data common to all flows. /// Data common to all flows.
common: FlowData, base: FlowData,
/// The associated render box. /// The associated render box.
box: Option<RenderBox>, box: Option<@RenderBox>,
containing_width: Au, containing_width: Au,
@ -39,10 +40,10 @@ pub struct FloatFlowData {
} }
impl FloatFlowData { impl FloatFlow {
pub fn new(common: FlowData, float_type: FloatType) -> FloatFlowData { pub fn new(base: FlowData, float_type: FloatType) -> FloatFlow {
FloatFlowData { FloatFlow {
common: common, base: base,
containing_width: Au(0), containing_width: Au(0),
box: None, box: None,
index: None, index: None,
@ -59,119 +60,157 @@ impl FloatFlowData {
self.box = None; self.box = None;
self.index = 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 { impl FlowContext for FloatFlow {
pub fn bubble_widths_float(&mut self, ctx: &LayoutContext) { 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 min_width = Au(0);
let mut pref_width = Au(0); let mut pref_width = Au(0);
let mut num_floats = 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()); //assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
do child_ctx.with_mut_base |child_node| { let child_base = flow::mut_base(*child_ctx);
min_width = geometry::max(min_width, child_node.min_width); min_width = geometry::max(min_width, child_base.min_width);
pref_width = geometry::max(pref_width, child_node.pref_width); pref_width = geometry::max(pref_width, child_base.pref_width);
num_floats = num_floats + child_node.num_floats; num_floats = num_floats + child_base.num_floats;
}
} }
self.common.num_floats = 1; self.base.num_floats = 1;
self.floated_children = num_floats; self.floated_children = num_floats;
for box in self.box.iter() { for box in self.box.iter() {
let style = box.style(); {
do box.with_model |model| { let base = box.base();
model.compute_borders(style) base.model.mutate().ptr.compute_borders(base.style());
} }
min_width = min_width.add(&box.get_min_width(ctx)); let (this_minimum_width, this_preferred_width) = box.minimum_and_preferred_widths();
pref_width = pref_width.add(&box.get_pref_width(ctx)); min_width = min_width + this_minimum_width;
pref_width = pref_width + this_preferred_width;
} }
self.common.min_width = min_width; self.base.min_width = min_width;
self.common.pref_width = pref_width; self.base.pref_width = pref_width;
} }
pub fn assign_widths_float(&mut self) { fn assign_widths(&mut self, _: &mut LayoutContext) {
debug!("assign_widths_float: assigning width for flow %?", self.common.id); 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.size.width is set by parent even though we don't know
// position.origin yet. // 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; self.containing_width = remaining_width;
let mut x_offset = Au(0); let mut x_offset = Au(0);
// Parent usually sets this, but floats are never inorder // 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() { for &box in self.box.iter() {
let style = box.style(); let base = box.base();
do box.with_model |model| { let style = base.style();
// Can compute padding here since we know containing block width. let mut position_ref = base.position.mutate();
model.compute_padding(style, remaining_width); 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. // Can compute padding here since we know containing block width.
let margin_top = MaybeAuto::from_style(style.Margin.margin_top, model.compute_padding(style, remaining_width);
remaining_width).specified_or_zero();
let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom, // Margins for floats are 0 if auto.
remaining_width).specified_or_zero(); let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
let margin_left = MaybeAuto::from_style(style.Margin.margin_left, remaining_width).specified_or_zero();
remaining_width).specified_or_zero(); let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
let margin_right = MaybeAuto::from_style(style.Margin.margin_right, remaining_width).specified_or_zero();
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.base.pref_width,
let shrink_to_fit = geometry::min(self.common.pref_width, geometry::max(self.base.min_width, remaining_width));
geometry::max(self.common.min_width,
remaining_width));
let width = MaybeAuto::from_style(style.Box.width, let width = MaybeAuto::from_style(style.Box.width,
remaining_width).specified_or_default(shrink_to_fit); remaining_width).specified_or_default(shrink_to_fit);
debug!("assign_widths_float -- width: %?", width); debug!("assign_widths_float -- width: %?", width);
model.margin.top = margin_top; model.margin.top = margin_top;
model.margin.right = margin_right; model.margin.right = margin_right;
model.margin.bottom = margin_bottom; model.margin.bottom = margin_bottom;
model.margin.left = margin_left; model.margin.left = margin_left;
x_offset = model.offset(); x_offset = model.offset();
remaining_width = width; remaining_width = width;
}
do box.with_mut_base |base| { // The associated box is the border box of this flow.
//The associated box is the border box of this flow position.origin.x = model.margin.left;
base.position.origin.x = base.model.margin.left;
let pb = base.model.padding.left + base.model.padding.right + let padding_and_borders = model.padding.left + model.padding.right +
base.model.border.left + base.model.border.right; model.border.left + model.border.right;
base.position.size.width = remaining_width + pb; 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; let has_inorder_children = self.base.num_floats > 0;
for kid in self.common.child_iter() { for kid in self.base.child_iter() {
//assert!(kid.starts_block_flow() || kid.starts_inline_flow()); //assert!(kid.starts_block_flow() || kid.starts_inline_flow());
do kid.with_mut_base |child_node| { let child_base = flow::mut_base(*kid);
child_node.position.origin.x = x_offset; child_base.position.origin.x = x_offset;
child_node.position.size.width = remaining_width; child_base.position.size.width = remaining_width;
child_node.is_inorder = has_inorder_children; child_base.is_inorder = has_inorder_children;
if !child_node.is_inorder { if !child_base.is_inorder {
child_node.floats_in = FloatContext::new(0); child_base.floats_in = FloatContext::new(0);
}
} }
} }
} }
pub fn assign_height_inorder_float(&mut self) { fn assign_height_inorder(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inorder_float: assigning height for float %?", self.common.id); debug!("assign_height_inorder_float: assigning height for float %?", self.base.id);
// assign_height_float was already called by the traversal function // assign_height_float was already called by the traversal function
// so this is well-defined // so this is well-defined
@ -181,28 +220,23 @@ impl FloatFlowData {
let mut margin_height = Au(0); let mut margin_height = Au(0);
for box in self.box.iter() { for box in self.box.iter() {
height = do box.with_base |base| { let base = box.base();
base.position.size.height height = base.position.borrow().ptr.size.height;
}; clearance = match base.clear() {
clearance = match box.clear() {
None => Au(0), None => Au(0),
Some(clear) => { Some(clear) => self.base.floats_in.clearance(clear),
self.common.floats_in.clearance(clear)
}
}; };
do box.with_base |base| { let model = base.model.get();
let noncontent_width = base.model.padding.left + base.model.padding.right + let noncontent_width = model.padding.left + model.padding.right + model.border.left +
base.model.border.left + base.model.border.right; 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;
}
full_noncontent_width = noncontent_width + model.margin.left + model.margin.right;
margin_height = model.margin.top + model.margin.bottom;
} }
let info = PlacementInfo { 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, height: height + margin_height,
ceiling: clearance, ceiling: clearance,
max_width: self.containing_width, max_width: self.containing_width,
@ -211,23 +245,19 @@ impl FloatFlowData {
// Place the float and return the FloatContext back to the parent flow. // Place the float and return the FloatContext back to the parent flow.
// After, grab the position and use that to set our position. // After, grab the position and use that to set our position.
self.common.floats_out = self.common.floats_in.add_float(&info); self.base.floats_out = self.base.floats_in.add_float(&info);
self.rel_pos = self.common.floats_out.last_float_pos(); self.rel_pos = self.base.floats_out.last_float_pos();
} }
pub fn assign_height_float(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_float: assigning height for float %?", self.common.id); debug!("assign_height_float: assigning height for float %?", self.base.id);
let has_inorder_children = self.common.num_floats > 0; let has_inorder_children = self.base.num_floats > 0;
if has_inorder_children { if has_inorder_children {
let mut float_ctx = FloatContext::new(self.floated_children); let mut float_ctx = FloatContext::new(self.floated_children);
for kid in self.common.child_iter() { for kid in self.base.child_iter() {
do kid.with_mut_base |child_node| { flow::mut_base(*kid).floats_in = float_ctx.clone();
child_node.floats_in = float_ctx.clone();
}
kid.assign_height_inorder(ctx); kid.assign_height_inorder(ctx);
do kid.with_mut_base |child_node| { float_ctx = flow::mut_base(*kid).floats_out.clone();
float_ctx = child_node.floats_out.clone();
}
} }
} }
@ -235,83 +265,54 @@ impl FloatFlowData {
let mut top_offset = Au(0); let mut top_offset = Au(0);
for &box in self.box.iter() { for &box in self.box.iter() {
do box.with_model |model| { let base = box.base();
top_offset = model.margin.top + model.border.top + model.padding.top; let model_ref = base.model.borrow();
cur_y = cur_y + top_offset; 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() { for kid in self.base.child_iter() {
do kid.with_mut_base |child_node| { let child_base = flow::mut_base(*kid);
child_node.position.origin.y = cur_y; child_base.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height; cur_y = cur_y + child_base.position.size.height;
};
} }
let mut height = cur_y - top_offset; let mut height = cur_y - top_offset;
let mut noncontent_width = Au(0); let mut noncontent_height;
let mut noncontent_height = Au(0); for box in self.box.iter() {
for box in self.box.mut_iter() { let base = box.base();
do box.with_mut_base |base| { let mut model_ref = base.model.mutate();
//The associated box is the border box of this flow let mut position_ref = base.position.mutate();
base.position.origin.y = base.model.margin.top; let (model, position) = (&mut model_ref.ptr, &mut position_ref.ptr);
noncontent_width = base.model.padding.left + base.model.padding.right + // The associated box is the border box of this flow.
base.model.border.left + base.model.border.right; position.origin.y = 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;
//TODO(eatkinson): compute heights properly using the 'height' property. //TODO(eatkinson): compute heights properly using the 'height' property.
for &box in self.box.iter() { let height_prop = MaybeAuto::from_style(base.style().Box.height,
let height_prop = Au::new(0)).specified_or_zero();
MaybeAuto::from_style(box.style().Box.height,
Au(0)).specified_or_zero();
height = geometry::max(height, height_prop) + noncontent_height; height = geometry::max(height, height_prop) + noncontent_height;
debug!("assign_height_float -- height: %?", height); debug!("assign_height_float -- height: %?", height);
do box.with_mut_base |base| {
base.position.size.height = height; position.size.height = height;
}
} }
} }
pub fn build_display_list_float<E:ExtraDisplayListData>(&mut self, fn collapse_margins(&mut self,
builder: &DisplayListBuilder, _: bool,
dirty: &Rect<Au>, _: &mut bool,
list: &Cell<DisplayList<E>>) _: &mut Au,
-> bool { _: &mut Au,
collapsing: &mut Au,
//TODO: implement iframe size messaging _: &mut Au) {
if self.common.node.is_iframe_element() { // Margins between a floated box and any other box do not collapse.
error!("float iframe size messaging not implemented yet"); *collapsing = Au::new(0);
}
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;
}
}
false
} }
} }

View file

@ -11,7 +11,7 @@ use std::vec;
use std::i32::max_value; use std::i32::max_value;
#[deriving(Clone)] #[deriving(Clone)]
pub enum FloatType{ pub enum FloatType {
FloatLeft, FloatLeft,
FloatRight FloatRight
} }
@ -22,11 +22,12 @@ pub enum ClearType {
ClearBoth ClearBoth
} }
struct FloatContextBase{ struct FloatContextBase {
float_data: ~[Option<FloatData>], /// This is an option of a vector to avoid allocation in the fast path (no floats).
float_data: Option<~[Option<FloatData>]>,
floats_used: uint, floats_used: uint,
max_y : Au, max_y: Au,
offset: Point2D<Au> offset: Point2D<Au>,
} }
#[deriving(Clone)] #[deriving(Clone)]
@ -48,12 +49,12 @@ pub struct PlacementInfo{
/// destroy the context on modification. /// destroy the context on modification.
pub enum FloatContext { pub enum FloatContext {
Invalid, Invalid,
Valid(~FloatContextBase) Valid(FloatContextBase)
} }
impl FloatContext { impl FloatContext {
pub fn new(num_floats: uint) -> FloatContext { pub fn new(num_floats: uint) -> FloatContext {
Valid(~FloatContextBase::new(num_floats)) Valid(FloatContextBase::new(num_floats))
} }
#[inline(always)] #[inline(always)]
@ -68,7 +69,7 @@ impl FloatContext {
fn with_mut_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R { fn with_mut_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R {
match *self { match *self {
Invalid => fail!("Float context no longer available"), 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 { pub fn with_base<R>(&self, callback: &fn(&FloatContextBase) -> R) -> R {
match *self { match *self {
Invalid => fail!("Float context no longer available"), 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{ impl FloatContextBase{
fn new(num_floats: uint) -> FloatContextBase { fn new(num_floats: uint) -> FloatContextBase {
debug!("Creating float context of size %?", num_floats); debug!("Creating float context of size %?", num_floats);
let new_data = vec::from_elem(num_floats, None);
FloatContextBase { FloatContextBase {
float_data: new_data, float_data: if num_floats == 0 {
None
} else {
Some(vec::from_elem(num_floats, None))
},
floats_used: 0, floats_used: 0,
max_y: Au(0), max_y: Au(0),
offset: Point2D(Au(0), Au(0)) offset: Point2D(Au(0), Au(0))
@ -144,7 +148,7 @@ impl FloatContextBase{
fn last_float_pos(&self) -> Point2D<Au> { fn last_float_pos(&self) -> Point2D<Au> {
assert!(self.floats_used > 0, "Error: tried to access FloatContext with no floats in it"); 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"), None => fail!("FloatContext error: floats should never be None here"),
Some(float) => { Some(float) => {
debug!("Returning float position: %?", float.bounds.origin + self.offset); debug!("Returning float position: %?", float.bounds.origin + self.offset);
@ -176,36 +180,38 @@ impl FloatContextBase{
let mut r_bottom = None; let mut r_bottom = None;
// Find the float collisions for the given vertical range. // Find the float collisions for the given vertical range.
for float in self.float_data.iter() { for floats in self.float_data.iter() {
debug!("available_rect: Checking for collision against float"); for float in floats.iter() {
match *float{ debug!("available_rect: Checking for collision against float");
None => (), match *float {
Some(data) => { None => (),
let float_pos = data.bounds.origin; Some(data) => {
let float_size = data.bounds.size; let float_pos = data.bounds.origin;
debug!("float_pos: %?, float_size: %?", float_pos, float_size); let float_size = data.bounds.size;
match data.f_type { debug!("float_pos: %?, float_size: %?", float_pos, float_size);
FloatLeft => { match data.f_type {
if(float_pos.x + float_size.width > max_left && FloatLeft => {
float_pos.y + float_size.height > top && float_pos.y < top + height) { if(float_pos.x + float_size.width > max_left &&
max_left = float_pos.x + float_size.width; 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_top = Some(float_pos.y);
l_bottom = Some(float_pos.y + float_size.height); l_bottom = Some(float_pos.y + float_size.height);
debug!("available_rect: collision with left float: new max_left is %?", debug!("available_rect: collision with left float: new max_left is %?",
max_left); max_left);
}
} }
} FloatRight => {
FloatRight => { if(float_pos.x < min_right &&
if(float_pos.x < min_right && float_pos.y + float_size.height > top && float_pos.y < top + height) {
float_pos.y + float_size.height > top && float_pos.y < top + height) { min_right = float_pos.x;
min_right = float_pos.x;
r_top = Some(float_pos.y); r_top = Some(float_pos.y);
r_bottom = Some(float_pos.y + float_size.height); r_bottom = Some(float_pos.y + float_size.height);
debug!("available_rect: collision with right float: new min_right is %?", debug!("available_rect: collision with right float: new min_right is %?",
min_right); min_right);
}
} }
} }
} }
@ -242,9 +248,12 @@ impl FloatContextBase{
} }
fn add_float(&mut self, info: &PlacementInfo) { fn add_float(&mut self, info: &PlacementInfo) {
debug!("Floats_used: %?, Floats available: %?", self.floats_used, self.float_data.len()); assert!(self.float_data.is_some());
assert!(self.floats_used < self.float_data.len() && debug!("Floats_used: %?, Floats available: %?",
self.float_data[self.floats_used].is_none()); 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 { let new_info = PlacementInfo {
width: info.width, width: info.width,
@ -263,22 +272,24 @@ impl FloatContextBase{
}, },
f_type: info.f_type 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.max_y = max(self.max_y, new_float.bounds.origin.y);
self.floats_used += 1; self.floats_used += 1;
} }
/// Returns true if the given rect overlaps with any floats. /// Returns true if the given rect overlaps with any floats.
fn collides_with_float(&self, bounds: &Rect<Au>) -> bool { fn collides_with_float(&self, bounds: &Rect<Au>) -> bool {
for float in self.float_data.iter() { for floats in self.float_data.iter() {
match *float{ for float in floats.iter() {
None => (), match *float {
Some(data) => { None => (),
if data.bounds.translate(&self.offset).intersects(bounds) { Some(data) => {
return true; if data.bounds.translate(&self.offset).intersects(bounds) {
return true;
}
} }
} };
}; }
} }
return false; return false;
@ -292,16 +303,18 @@ impl FloatContextBase{
let left = left - self.offset.x; let left = left - self.offset.x;
let mut max_height = None; let mut max_height = None;
for float in self.float_data.iter() { for floats in self.float_data.iter() {
match *float { for float in floats.iter() {
None => (), match *float {
Some(f_data) => { None => (),
if f_data.bounds.origin.y + f_data.bounds.size.height > top && Some(f_data) => {
f_data.bounds.origin.x + f_data.bounds.size.width > left && if f_data.bounds.origin.y + f_data.bounds.size.height > top &&
f_data.bounds.origin.x < left + width { f_data.bounds.origin.x + f_data.bounds.size.width > left &&
let new_y = f_data.bounds.origin.y; f_data.bounds.origin.x < left + width {
max_height = Some(min(max_height.unwrap_or(new_y), new_y)); 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 { fn clearance(&self, clear: ClearType) -> Au {
let mut clearance = Au(0); let mut clearance = Au(0);
for float in self.float_data.iter() { for floats in self.float_data.iter() {
match *float { for float in floats.iter() {
None => (), match *float {
Some(f_data) => { None => (),
match (clear, f_data.f_type) { Some(f_data) => {
(ClearLeft, FloatLeft) | match (clear, f_data.f_type) {
(ClearRight, FloatRight) | (ClearLeft, FloatLeft) |
(ClearBoth, _) => { (ClearRight, FloatRight) |
clearance = max( (ClearBoth, _) => {
clearance, clearance = max(
self.offset.y + f_data.bounds.origin.y + f_data.bounds.size.height); clearance,
self.offset.y + f_data.bounds.origin.y + f_data.bounds.size.height);
}
_ => ()
} }
_ => ()
} }
} }
} }

View file

@ -25,207 +25,275 @@
/// line breaks and mapping to CSS boxes, for the purpose of handling `getClientRects()` and /// line breaks and mapping to CSS boxes, for the purpose of handling `getClientRects()` and
/// similar methods. /// similar methods.
use layout::block::BlockFlowData; use css::node_style::StyledNode;
use layout::float::FloatFlowData; use layout::block::BlockFlow;
use layout::box::RenderBox; use layout::box::RenderBox;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::float::FloatFlow;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::inline::{InlineFlowData}; use layout::float_context::{FloatContext, Invalid};
use layout::float_context::{FloatContext, Invalid, FloatType};
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use css::node_style::StyledNode; use layout::inline::InlineFlow;
use extra::dlist::{DList,MutDListIterator}; use extra::dlist::{DList,MutDListIterator};
use extra::container::Deque; use extra::container::Deque;
use std::cell::Cell;
use std::io::stderr;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use script::dom::node::{AbstractNode, LayoutView}; 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 /// Virtual methods that make up a float context.
/// structures or float lists. ///
pub enum FlowContext { /// Note that virtual methods have a cost; we should not overuse them in Servo. Consider adding
AbsoluteFlow(~FlowData), /// methods to `ImmutableFlowUtils` or `MutableFlowUtils` before adding more methods here.
BlockFlow(~BlockFlowData), pub trait FlowContext {
FloatFlow(~FloatFlowData), // RTTI
InlineBlockFlow(~FlowData), //
InlineFlow(~InlineFlowData), // TODO(pcwalton): Use Rust's RTTI, once that works.
TableFlow(~FlowData),
}
pub enum FlowContextType { /// Returns the class of flow that this is.
Flow_Absolute, fn class(&self) -> FlowClass;
Flow_Block,
Flow_Float(FloatType),
Flow_InlineBlock,
Flow_Inline,
Flow_Root,
Flow_Table
}
impl FlowContext { /// If this is a block flow, returns the underlying object. Fails otherwise.
pub fn each_bu_sub_inorder (&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool { fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
for kid in self.child_iter() { fail!("called as_block() on a non-block flow")
// 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 !self.is_inorder() {
callback(self)
} else {
true
}
} }
pub fn each_preorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool, /// If this is an inline flow, returns the underlying object, borrowed immutably. Fails
callback: &fn(&mut FlowContext) -> bool) /// otherwise.
-> bool { fn as_immutable_inline<'a>(&'a self) -> &'a InlineFlow {
if prune(self) { fail!("called as_immutable_inline() on a non-inline flow")
return true; }
}
if !callback(self) { /// If this is an inline flow, returns the underlying object. Fails otherwise.
return false; fn as_inline<'a>(&'a mut self) -> &'a mut InlineFlow {
} fail!("called as_inline() on a non-inline flow")
}
for kid in self.child_iter() { /// If this is a float flow, returns the underlying object. Fails otherwise.
// FIXME: Work around rust#2202. We should be able to pass the callback directly. fn as_float<'a>(&'a mut self) -> &'a mut FloatFlow {
if !kid.each_preorder_prune(|a| prune(a), |a| callback(a)) { fail!("called as_float() on a non-float flow")
return false; }
}
}
// Main methods
/// Pass 1 of reflow: computes minimum and preferred widths.
fn bubble_widths(&mut self, _ctx: &mut LayoutContext) {
fail!("bubble_widths not yet implemented")
}
/// Pass 2 of reflow: computes width.
fn assign_widths(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_widths not yet implemented")
}
/// Pass 3 of reflow: computes height.
fn assign_height(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_height not yet implemented")
}
/// 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")
}
/// 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")
}
/// Returns a debugging string describing this flow.
fn debug_str(&self) -> ~str {
~"???"
}
}
// Base access
#[inline(always)]
pub fn base<'a>(this: &'a FlowContext) -> &'a FlowData {
unsafe {
let (_, ptr): (uint, &FlowData) = cast::transmute(this);
ptr
}
}
#[inline(always)]
pub fn mut_base<'a>(this: &'a mut FlowContext) -> &'a mut FlowData {
unsafe {
let (_, ptr): (uint, &mut FlowData) = cast::transmute(this);
ptr
}
}
/// 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,
}
}
}
impl FlowContext for AbsoluteFlow {
fn class(&self) -> FlowClass {
AbsoluteFlowClass
}
}
pub struct InlineBlockFlow {
base: FlowData,
}
impl InlineBlockFlow {
pub fn new(base: FlowData) -> InlineBlockFlow {
InlineBlockFlow {
base: base,
}
}
}
impl FlowContext for InlineBlockFlow {
fn class(&self) -> FlowClass {
InlineBlockFlowClass
}
}
pub struct TableFlow {
base: FlowData,
}
impl TableFlow {
pub fn new(base: FlowData) -> TableFlow {
TableFlow {
base: base,
}
}
}
impl FlowContext for TableFlow {
fn class(&self) -> FlowClass {
TableFlowClass
}
}
/// 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
}
}
/// 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 true
} }
pub fn each_postorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool, /// Returns true if this node should be pruned. If this returns true, we skip the operation
callback: &fn(&mut FlowContext) -> bool) /// entirely and do not process any descendant nodes. This is called *before* child nodes are
-> bool { /// visited. The default implementation never prunes any nodes.
if prune(self) { fn should_prune(&mut self, _flow: &mut FlowContext) -> bool {
return true; false
}
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;
}
}
callback(self)
}
pub fn each_preorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
self.each_preorder_prune(|_| false, callback)
}
pub fn each_postorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool {
self.each_postorder_prune(|_| false, callback)
}
}
impl<'self> FlowContext {
pub fn is_block_like(&self) -> bool {
match *self {
BlockFlow(*) | FloatFlow(*) => true,
_ => false,
}
}
pub fn is_leaf(&self) -> bool {
do self.with_base |base| {
base.children.len() == 0
}
}
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());
}
}
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())
}
}
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())
}
}
pub fn last_child(&'self mut self) -> Option<&'self mut FlowContext> {
self.mut_base().children.back_mut()
}
pub fn remove_first(&mut self) {
do self.with_mut_base |base| {
base.children.pop_front();
}
}
pub fn remove_last(&mut self) {
do self.with_mut_base |base| {
base.children.pop_back();
}
}
pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a, FlowContext> {
self.mut_base().children.mut_iter()
}
}
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)
}
}
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),
}
}
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),
}
} }
} }
@ -237,7 +305,7 @@ pub struct FlowData {
node: AbstractNode<LayoutView>, node: AbstractNode<LayoutView>,
restyle_damage: RestyleDamage, restyle_damage: RestyleDamage,
children: DList<FlowContext>, children: DList<~FlowContext:>,
/* TODO (Issue #87): debug only */ /* TODO (Issue #87): debug only */
id: int, id: int,
@ -256,11 +324,12 @@ pub struct FlowData {
} }
pub struct BoxIterator { pub struct BoxIterator {
priv boxes: ~[RenderBox], priv boxes: ~[@RenderBox],
priv index: uint, 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() { if self.index >= self.boxes.len() {
None None
} else { } else {
@ -270,6 +339,7 @@ impl Iterator<RenderBox> for BoxIterator {
} }
} }
} }
impl FlowData { impl FlowData {
pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData { pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData {
FlowData { FlowData {
@ -280,237 +350,135 @@ impl FlowData {
id: id, id: id,
min_width: Au(0), min_width: Au::new(0),
pref_width: Au(0), pref_width: Au::new(0),
position: Au::zero_rect(), position: Au::zero_rect(),
floats_in: Invalid, floats_in: Invalid,
floats_out: Invalid, floats_out: Invalid,
num_floats: 0, num_floats: 0,
abs_position: Point2D(Au(0), Au(0)), abs_position: Point2D(Au::new(0), Au::new(0)),
is_inorder: false 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() self.children.mut_iter()
} }
} }
impl<'self> FlowContext { impl<'self> ImmutableFlowUtils for &'self FlowContext {
/// A convenience method to return the position of this flow. Fails if the flow is currently /// Returns true if this flow is a block or a float flow.
/// being borrowed mutably. fn is_block_like(self) -> bool {
#[inline(always)] match self.class() {
pub fn position(&self) -> Rect<Au> { BlockFlowClass | FloatFlowClass => true,
do self.with_base |common_info| { AbsoluteFlowClass | InlineBlockFlowClass | InlineFlowClass | TableFlowClass => false,
common_info.position
} }
} }
#[inline(always)] /// Returns true if this flow has no children.
pub fn is_inorder(&self) -> bool { fn is_leaf(self) -> bool {
do self.with_base |common_info| { base(self).children.len() == 0
common_info.is_inorder }
/// 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,
} }
} }
/// A convenience method to return the ID of this flow. Fails if the flow is currently being /// Returns true if this flow is a block flow, an inline flow, or a float flow.
/// borrowed mutably. fn starts_inline_flow(self) -> bool {
#[inline(always)] match self.class() {
pub fn id(&self) -> int { InlineFlowClass => true,
do self.with_base |info| { AbsoluteFlowClass | BlockFlowClass | FloatFlowClass | InlineBlockFlowClass |
info.id TableFlowClass => false,
}
}
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()))
}
}
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()))
}
}
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()))
}
}
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()))
}
}
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()))
}
}
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()))
}
}
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()))
}
}
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()))
}
}
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)
}
}
}
/// 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
}
}
// 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)),
}
}
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
}
}
}
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,
} }
} }
/// Dumps the flow tree for debugging. /// Dumps the flow tree for debugging.
pub fn dump(&mut self) { fn dump(self) {
self.dump_indent(0); // TODO(pcwalton): Fill this in.
} }
}
/// Dumps the flow tree, for debugging, with indentation. impl<'self> MutableFlowUtils for &'self mut FlowContext {
pub fn dump_indent(&mut self, indent: uint) { /// Traverses the tree in preorder.
let mut s = ~"|"; fn traverse_preorder<T:PreorderFlowTraversal>(self, traversal: &mut T) -> bool {
for _ in range(0, indent) { if traversal.should_prune(self) {
s.push_str("---- "); return true
} }
s.push_str(self.debug_str()); if !traversal.process(self) {
stderr().write_line(s); return false
// FIXME: this should have a pure/const version?
for child in self.child_iter() {
child.dump_indent(indent + 1)
} }
for kid in child_iter(self) {
if !kid.traverse_preorder(traversal) {
return false
}
}
true
} }
pub fn debug_str(&self) -> ~str { /// Traverses the tree in postorder.
let repr = match *self { fn traverse_postorder<T:PostorderFlowTraversal>(self, traversal: &mut T) -> bool {
InlineFlow(ref inline) => { if traversal.should_prune(self) {
let mut s = inline.boxes.iter().fold(~"InlineFlow(children=", |s, box| { return true
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| { for kid in child_iter(self) {
fmt!("f%? %? floats %? size %? damage %?", base.id, repr, base.num_floats, if !kid.traverse_postorder(traversal) {
base.position, base.restyle_damage) return false
}
}
if !traversal.should_process(self) {
return true
}
traversal.process(self)
}
/// 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)
}
/// 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())
}
/// 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())
}
/// Removes the first child of this flow and destroys it.
fn remove_first(self) {
let _ = mut_base(self).children.pop_front();
}
/// Removes the last child of this flow and destroys it.
fn remove_last(self) {
let _ = mut_base(self).children.pop_back();
}
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),
} }
} }
} }

View file

@ -3,28 +3,27 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode; use css::node_style::StyledNode;
use std::cell::Cell;
use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox}; 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::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; 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::float_context::FloatContext;
use layout::util::{ElementMapping}; use layout::util::{ElementMapping};
use layout::float_context::{PlacementInfo, FloatLeft}; use layout::float_context::{PlacementInfo, FloatLeft};
use std::u16;
use std::util;
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::range::Range;
use servo_util::tree::TreeNodeRef;
use extra::container::Deque; use extra::container::Deque;
use extra::ringbuf::RingBuf; use extra::ringbuf::RingBuf;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayList;
use style::computed_values::text_align;
use style::computed_values::vertical_align;
use servo_util::geometry::Au;
use servo_util::range::Range;
use std::cell::Cell;
use std::u16;
use std::util;
/* /*
Lineboxes are represented as offsets into the child list, rather than Lineboxes are represented as offsets into the child list, rather than
@ -62,13 +61,15 @@ struct LineBox {
struct LineboxScanner { struct LineboxScanner {
floats: FloatContext, floats: FloatContext,
new_boxes: ~[RenderBox], new_boxes: ~[@RenderBox],
work_list: @mut RingBuf<RenderBox>, work_list: @mut RingBuf<@RenderBox>,
pending_line: LineBox, pending_line: LineBox,
lines: ~[LineBox], lines: ~[LineBox],
cur_y: Au, cur_y: Au,
} }
local_data_key!(local_linebox_scanner: LineboxScanner)
impl LineboxScanner { impl LineboxScanner {
pub fn new(float_ctx: FloatContext) -> LineboxScanner { pub fn new(float_ctx: FloatContext) -> LineboxScanner {
LineboxScanner { LineboxScanner {
@ -77,33 +78,45 @@ impl LineboxScanner {
work_list: @mut RingBuf::new(), work_list: @mut RingBuf::new(),
pending_line: LineBox { pending_line: LineBox {
range: Range::empty(), range: Range::empty(),
bounds: Rect(Point2D(Au(0), Au(0)), 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(0), Au(0)) green_zone: Size2D(Au::new(0), Au::new(0))
}, },
lines: ~[], 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 { pub fn floats_out(&mut self) -> FloatContext {
self.floats.clone() self.floats.clone()
} }
fn reset_scanner(&mut self, flow: &mut InlineFlowData) { fn reset_scanner(&mut self, flow: &mut InlineFlow) {
debug!("Resetting line box scanner's state for flow f%d.", flow.common.id); debug!("Resetting line box scanner's state for flow f%d.", flow.base.id);
self.lines = ~[]; self.lines = ~[];
self.new_boxes = ~[]; self.new_boxes = ~[];
self.cur_y = Au(0); self.cur_y = Au::new(0);
self.reset_linebox(); self.reset_linebox();
} }
fn reset_linebox(&mut self) { fn reset_linebox(&mut self) {
self.pending_line.range.reset(0,0); self.pending_line.range.reset(0,0);
self.pending_line.bounds = Rect(Point2D(Au(0), self.cur_y), 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(0), Au(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); self.reset_scanner(flow);
let mut i = 0u; let mut i = 0u;
@ -115,11 +128,11 @@ impl LineboxScanner {
break break
} }
let box = flow.boxes[i]; i += 1; let box = flow.boxes[i]; i += 1;
debug!("LineboxScanner: Working with box from box list: b%d", box.id()); debug!("LineboxScanner: Working with box from box list: b%d", box.base().id());
box box
} else { } else {
let box = self.work_list.pop_front().unwrap(); let box = self.work_list.pop_front().unwrap();
debug!("LineboxScanner: Working with box from work list: b%d", box.id()); debug!("LineboxScanner: Working with box from work list: b%d", box.base().id());
box box
}; };
@ -139,16 +152,15 @@ impl LineboxScanner {
self.flush_current_line(); self.flush_current_line();
} }
flow.elems.repair_for_box_changes(flow.boxes, self.new_boxes); flow.elems.repair_for_box_changes(flow.boxes, self.new_boxes);
self.swap_out_results(flow); 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", debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d",
self.lines.len(), self.lines.len(),
flow.common.id); flow.base.id);
util::swap(&mut flow.boxes, &mut self.new_boxes); util::swap(&mut flow.boxes, &mut self.new_boxes);
util::swap(&mut flow.lines, &mut self.lines); util::swap(&mut flow.lines, &mut self.lines);
@ -165,47 +177,10 @@ impl LineboxScanner {
self.reset_linebox(); 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 // FIXME(eatkinson): this assumes that the tallest box in the line determines the line height
// This might not be the case with some weird text fonts. // This might not be the case with some weird text fonts.
fn new_height_for_line(&self, new_box: RenderBox) -> Au { fn new_height_for_line(&self, new_box: &RenderBox) -> Au {
let box_height = self.box_height(new_box); let box_height = new_box.box_height();
if box_height > self.pending_line.bounds.size.height { if box_height > self.pending_line.bounds.size.height {
box_height box_height
} else { } else {
@ -216,9 +191,12 @@ impl LineboxScanner {
/// Computes the position of a line that has only the provided RenderBox. /// Computes the position of a line that has only the provided RenderBox.
/// Returns: the bounding rect of the line's green zone (whose origin coincides /// 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. /// 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: 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 splitable = first_box.can_split();
let line_is_empty: bool = self.pending_line.range.length() == 0; 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 // We will move it later if it has nonzero width
// and that causes problems. // and that causes problems.
let placement_width = if splitable { let placement_width = if splitable {
Au(0) Au::new(0)
} else { } else {
first_box.position().size.width first_box_size.width
}; };
let mut info = PlacementInfo { let mut info = PlacementInfo {
width: placement_width, width: placement_width,
height: first_box.position().size.height, height: first_box_size.height,
ceiling: ceiling, ceiling: ceiling,
max_width: flow.common.position.size.width, max_width: flow.base.position.size.width,
f_type: FloatLeft f_type: FloatLeft
}; };
let line_bounds = self.floats.place_between_floats(&info); 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 // 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"); 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 // If not, but we can't split the box, then we'll place
// the line here and it will overflow. // the line here and it will overflow.
if !splitable { if !splitable {
debug!("LineboxScanner: case=line doesn't fit, but is unsplittable"); 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 // Otherwise, try and split the box
@ -264,15 +244,15 @@ impl LineboxScanner {
CannotSplit(_) => { CannotSplit(_) => {
error!("LineboxScanner: Tried to split unsplittable render box! %s", error!("LineboxScanner: Tried to split unsplittable render box! %s",
first_box.debug_str()); first_box.debug_str());
return (line_bounds, first_box.position().size.width); return (line_bounds, first_box_size.width);
} }
SplitDidFit(left, right) => { SplitDidFit(left, right) => {
debug!("LineboxScanner: case=box split and fit"); debug!("LineboxScanner: case=box split and fit");
let actual_box_width = match (left, right) { let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.position().size.width, (Some(l_box), Some(_)) => l_box.base().position.get().size.width,
(Some(l_box), None) => l_box.position().size.width, (Some(l_box), None) => l_box.base().position.get().size.width,
(None, Some(r_box)) => r_box.position().size.width, (None, Some(r_box)) => r_box.base().position.get().size.width,
(None, None) => fail!("This case makes no sense.") (None, None) => fail!("This case makes no sense.")
}; };
return (line_bounds, actual_box_width); return (line_bounds, actual_box_width);
@ -284,9 +264,9 @@ impl LineboxScanner {
debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down"); debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down");
let actual_box_width = match (left, right) { let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.position().size.width, (Some(l_box), Some(_)) => l_box.base().position.get().size.width,
(Some(l_box), None) => l_box.position().size.width, (Some(l_box), None) => l_box.base().position.get().size.width,
(None, Some(r_box)) => r_box.position().size.width, (None, Some(r_box)) => r_box.base().position.get().size.width,
(None, None) => fail!("This case makes no sense.") (None, None) => fail!("This case makes no sense.")
}; };
@ -301,7 +281,7 @@ impl LineboxScanner {
} }
/// Returns false only if we should break the line. /// Returns false only if we should break the line.
fn try_append_to_line(&mut self, in_box: RenderBox, flow: &mut 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; let line_is_empty: bool = self.pending_line.range.length() == 0;
if line_is_empty { if line_is_empty {
@ -313,7 +293,7 @@ impl LineboxScanner {
debug!("LineboxScanner: Trying to append box to line %u (box size: %?, green zone: \ debug!("LineboxScanner: Trying to append box to line %u (box size: %?, green zone: \
%?): %s", %?): %s",
self.lines.len(), self.lines.len(),
in_box.position().size, in_box.base().position.get().size,
self.pending_line.green_zone, self.pending_line.green_zone,
in_box.debug_str()); in_box.debug_str());
@ -326,6 +306,8 @@ impl LineboxScanner {
let new_height = self.new_height_for_line(in_box); let new_height = self.new_height_for_line(in_box);
if new_height > green_zone.height { if new_height > green_zone.height {
debug!("LineboxScanner: entering float collision avoider!");
// Uh-oh. Adding this box is going to increase the height, // Uh-oh. Adding this box is going to increase the height,
// and because of that we will collide with some floats. // 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 // horizontally. We'll try to place the whole box on this line and break somewhere
// if it doesn't fit. // if it doesn't fit.
let new_width = self.pending_line.bounds.size.width + 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"); debug!("LineboxScanner: case=box fits without splitting");
self.push_box_to_line(in_box); self.push_box_to_line(in_box);
return true; return true;
@ -441,28 +424,29 @@ impl LineboxScanner {
} }
// unconditional push // unconditional push
fn push_box_to_line(&mut self, box: RenderBox) { fn push_box_to_line(&mut self, box: @RenderBox) {
debug!("LineboxScanner: Pushing box b%d to line %u", box.id(), self.lines.len()); debug!("LineboxScanner: Pushing box b%d to line %u", box.base().id(), self.lines.len());
if self.pending_line.range.length() == 0 { if self.pending_line.range.length() == 0 {
assert!(self.new_boxes.len() <= (u16::max_value as uint)); assert!(self.new_boxes.len() <= (u16::max_value as uint));
self.pending_line.range.reset(self.new_boxes.len(), 0); self.pending_line.range.reset(self.new_boxes.len(), 0);
} }
self.pending_line.range.extend_by(1); self.pending_line.range.extend_by(1);
self.pending_line.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, 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); self.new_boxes.push(box);
} }
} }
pub struct InlineFlowData { pub struct InlineFlow {
/// Data common to all flows. /// Data common to all flows.
common: FlowData, base: FlowData,
// A vec of all inline render boxes. Several boxes may // A vec of all inline render boxes. Several boxes may
// correspond to one Node/Element. // correspond to one Node/Element.
boxes: ~[RenderBox], boxes: ~[@RenderBox],
// vec of ranges into boxes that represents line positions. // vec of ranges into boxes that represents line positions.
// these ranges are disjoint, and are the result of inline layout. // these ranges are disjoint, and are the result of inline layout.
// also some metadata used for positioning lines // also some metadata used for positioning lines
@ -473,10 +457,10 @@ pub struct InlineFlowData {
elems: ElementMapping elems: ElementMapping
} }
impl InlineFlowData { impl InlineFlow {
pub fn new(common: FlowData) -> InlineFlowData { pub fn new(base: FlowData) -> InlineFlow {
InlineFlowData { InlineFlow {
common: common, base: base,
boxes: ~[], boxes: ~[],
lines: ~[], lines: ~[],
elems: ElementMapping::new(), elems: ElementMapping::new(),
@ -489,87 +473,103 @@ impl InlineFlowData {
} }
self.boxes = ~[]; self.boxes = ~[];
} }
}
pub trait InlineLayout { pub fn build_display_list_inline<E:ExtraDisplayListData>(&self,
fn starts_inline_flow(&self) -> bool; builder: &DisplayListBuilder,
} dirty: &Rect<Au>,
list: &Cell<DisplayList<E>>)
-> bool {
impl InlineLayout for FlowContext { //TODO: implement inline iframe size messaging
fn starts_inline_flow(&self) -> bool { if self.base.node.is_iframe_element() {
match *self { error!("inline iframe size messaging not implemented yet");
InlineFlow(*) => true,
_ => false
} }
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 { impl FlowContext for InlineFlow {
pub fn bubble_widths_inline(&mut self, ctx: &mut LayoutContext) { 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; let mut num_floats = 0;
for kid in self.common.child_iter() { for kid in self.base.child_iter() {
do kid.with_mut_base |base| { let child_base = flow::mut_base(*kid);
num_floats += base.num_floats; num_floats += child_base.num_floats;
base.floats_in = FloatContext::new(base.num_floats); child_base.floats_in = FloatContext::new(child_base.num_floats);
}
} }
{ {
let this = &mut *self; let this = &mut *self;
let mut min_width = Au(0); let mut min_width = Au::new(0);
let mut pref_width = Au(0); let mut pref_width = Au::new(0);
for box in this.boxes.iter() { for box in this.boxes.iter() {
debug!("FlowContext[%d]: measuring %s", self.common.id, box.debug_str()); debug!("FlowContext[%d]: measuring %s", self.base.id, box.debug_str());
min_width = Au::max(min_width, box.get_min_width(ctx)); let (this_minimum_width, this_preferred_width) =
pref_width = Au::max(pref_width, box.get_pref_width(ctx)); 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.base.min_width = min_width;
this.common.pref_width = pref_width; this.base.pref_width = pref_width;
this.common.num_floats = num_floats; this.base.num_floats = num_floats;
} }
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// 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. /// 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. // Initialize content box widths if they haven't been initialized already.
// //
// TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into // TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into
// `RenderBox`. // `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; let this = &mut *self;
for &box in this.boxes.iter() { for &box in this.boxes.iter() {
match box { box.assign_width();
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.
} }
for kid in self.common.child_iter() { for kid in self.base.child_iter() {
do kid.with_mut_base |base| { let child_base = flow::mut_base(*kid);
base.position.size.width = self.common.position.size.width; child_base.position.size.width = self.base.position.size.width;
base.is_inorder = self.common.is_inorder; child_base.is_inorder = self.base.is_inorder;
}
} }
// There are no child contexts, so stop here. // There are no child contexts, so stop here.
@ -580,16 +580,15 @@ impl InlineFlowData {
// 'inline-block' box that created this flow before recursing. // 'inline-block' box that created this flow before recursing.
} }
pub fn assign_height_inorder_inline(&mut self, ctx: &mut LayoutContext) { fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
for kid in self.common.child_iter() { for kid in self.base.child_iter() {
kid.assign_height_inorder(ctx); kid.assign_height_inorder(ctx);
} }
self.assign_height_inline(ctx); self.assign_height(ctx);
} }
pub fn assign_height_inline(&mut self, _: &LayoutContext) { fn assign_height(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow %?", self.base.id);
debug!("assign_height_inline: assigning height for flow %?", self.common.id);
// Divide the boxes into lines // Divide the boxes into lines
// TODO(#226): Get the CSS `line-height` property from the containing block's style to // 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 // TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to
// determine its height for computing linebox height. // 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); let mut scanner = LineboxScanner::new(scanner_floats);
// Access the linebox scanner.
scanner.scan_for_lines(self); 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 // Now, go through each line and lay out the boxes inside
for line in self.lines.mut_iter() { for line in self.lines.mut_iter() {
// We need to distribute extra width based on text-align. // We need to distribute extra width based on text-align.
let mut slack_width = line.green_zone.width - line.bounds.size.width; let mut slack_width = line.green_zone.width - line.bounds.size.width;
if slack_width < Au(0) { if slack_width < Au::new(0) {
slack_width = Au(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. // Get the text alignment.
// TODO(Issue #222): use 'text-align' property from InlineFlow's // TODO(Issue #222): use 'text-align' property from InlineFlow's
// block container, not from the style of the first box child. // block container, not from the style of the first box child.
let linebox_align; let linebox_align = if line.range.begin() < self.boxes.len() {
if line.range.begin() < self.boxes.len() {
let first_box = self.boxes[line.range.begin()]; 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 { } else {
// Nothing to lay out, so assume left alignment. // Nothing to lay out, so assume left alignment.
linebox_align = text_align::left; text_align::left
} };
// Set the box x positions // Set the box x positions
let mut offset_x = line.bounds.origin.x; let mut offset_x = line.bounds.origin.x;
@ -632,28 +635,25 @@ impl InlineFlowData {
// TODO(Issue #213): implement `text-align: justify` // TODO(Issue #213): implement `text-align: justify`
text_align::left | text_align::justify => { text_align::left | text_align::justify => {
for i in line.range.eachi() { for i in line.range.eachi() {
do self.boxes[i].with_mut_base |base| { let box = self.boxes[i].base();
base.position.origin.x = offset_x; box.position.mutate().ptr.origin.x = offset_x;
offset_x = offset_x + base.position.size.width; offset_x = offset_x + box.position.get().size.width;
}
} }
} }
text_align::center => { text_align::center => {
offset_x = offset_x + slack_width.scale_by(0.5); offset_x = offset_x + slack_width.scale_by(0.5);
for i in line.range.eachi() { for i in line.range.eachi() {
do self.boxes[i].with_mut_base |base| { let box = self.boxes[i].base();
base.position.origin.x = offset_x; box.position.mutate().ptr.origin.x = offset_x;
offset_x = offset_x + base.position.size.width; offset_x = offset_x + box.position.get().size.width;
}
} }
} }
text_align::right => { text_align::right => {
offset_x = offset_x + slack_width; offset_x = offset_x + slack_width;
for i in line.range.eachi() { for i in line.range.eachi() {
do self.boxes[i].with_mut_base |base| { let box = self.boxes[i].base();
base.position.origin.x = offset_x; box.position.mutate().ptr.origin.x = offset_x;
offset_x = offset_x + base.position.size.width; 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; line.bounds.origin.y = line.bounds.origin.y + line_height_offset;
// Calculate the distance from baseline to the top of the linebox. // 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. // 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. // 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. // 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() { for box_i in line.range.eachi() {
let cur_box = self.boxes[box_i]; let cur_box = self.boxes[box_i];
let (top_from_base, bottom_from_base, ascent) = match cur_box { let (top_from_base, bottom_from_base, ascent) = match cur_box.class() {
ImageRenderBoxClass(image_box) => { ImageRenderBoxClass => {
let mut height = cur_box.image_height(image_box); 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, // TODO: margin, border, padding's top and bottom should be calculated in advance,
// since baseline of image is bottom margin edge. // since baseline of image is bottom margin edge.
let mut top = Au(0); let mut top;
let mut bottom = Au(0); let mut bottom;
do cur_box.with_model |model| { {
let model = image_box.base.model.get();
top = model.border.top + model.padding.top + model.margin.top; 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; let noncontent_height = top + bottom;
height = height + noncontent_height; 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; 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 range = &text_box.range;
let run = &text_box.run; let run = &text_box.run;
// Compute the height based on the line-height and font size // Compute the height based on the line-height and font size
let text_bounds = run.metrics_for_range(range).bounding_box; let text_bounds = run.metrics_for_range(range).bounding_box;
let em_size = text_bounds.size.height; let em_size = text_bounds.size.height;
let line_height = 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. // Find the top and bottom of the content area.
// Those are used in text-top and text-bottom value of 'vertical-align' // Those are used in text-top and text-bottom value of 'vertical-align'
@ -710,12 +717,15 @@ impl InlineFlowData {
// Offset from the top of the box is 1/2 of the leading + ascent // Offset from the top of the box is 1/2 of the leading + ascent
let text_offset = text_ascent + (line_height - em_size).scale_by(0.5); let text_offset = text_ascent + (line_height - em_size).scale_by(0.5);
text_bounds.translate(&Point2D(text_box.base.position.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) (text_offset, line_height - text_offset, text_ascent)
}, },
GenericRenderBoxClass(generic_box) => { GenericRenderBoxClass => {
(generic_box.position.size.height, Au(0), generic_box.position.size.height) 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! // 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. // 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. // 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. // It should calculate the distance from baseline to the bottom of parent's content area.
// But, it is assumed now as 0. // But, it is assumed now as 0.
let parent_text_bottom = Au(0); let parent_text_bottom = Au::new(0);
do cur_box.with_mut_base |base| { let cur_box_base = cur_box.base();
// Get parent node // Get parent node
let parent = match base.node.parent_node() { let parent = cur_box_base.node.parent_node();
None => base.node,
Some(parent) => parent,
};
let font_size = parent.style().Font.font_size; let font_size = parent.unwrap().style().Font.font_size;
parent_text_top = font_size; parent_text_top = font_size;
}
// This flag decides whether topmost and bottommost are updated or not. // 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. // That is, if the box has top or bottom value, no_update_flag becomes true.
let mut no_update_flag = false; let mut no_update_flag = false;
// Calculate a relative offset from baseline. // Calculate a relative offset from baseline.
let offset = match cur_box.vertical_align() { let offset = match cur_box_base.vertical_align() {
vertical_align::baseline => { vertical_align::baseline => {
-ascent -ascent
}, },
vertical_align::middle => { vertical_align::middle => {
// TODO: x-height value should be used from font info. // TODO: x-height value should be used from font info.
let xheight = Au(0); let xheight = Au::new(0);
-(xheight + scanner.box_height(cur_box)).scale_by(0.5) -(xheight + cur_box.box_height()).scale_by(0.5)
}, },
vertical_align::sub => { vertical_align::sub => {
// TODO: The proper position for subscripts should be used. // TODO: The proper position for subscripts should be used.
// Lower the baseline to the proper position for subscripts // Lower the baseline to the proper position for subscripts
let sub_offset = Au(0); let sub_offset = Au::new(0);
(sub_offset - ascent) (sub_offset - ascent)
}, },
vertical_align::super_ => { vertical_align::super_ => {
// TODO: The proper position for superscripts should be used. // TODO: The proper position for superscripts should be used.
// Raise the baseline to the proper position for superscripts // Raise the baseline to the proper position for superscripts
let super_offset = Au(0); let super_offset = Au::new(0);
(-super_offset - ascent) (-super_offset - ascent)
}, },
vertical_align::text_top => { vertical_align::text_top => {
@ -808,8 +814,9 @@ impl InlineFlowData {
-(length + ascent) -(length + ascent)
}, },
vertical_align::Percentage(p) => { vertical_align::Percentage(p) => {
let pt_size = cur_box.font_style().pt_size; let pt_size = cur_box.base().font_style().pt_size;
let line_height = scanner.calculate_line_height(cur_box, Au::from_pt(pt_size)); let line_height = cur_box.base()
.calculate_line_height(Au::from_pt(pt_size));
let percent_offset = line_height.scale_by(p); let percent_offset = line_height.scale_by(p);
-(percent_offset + ascent) -(percent_offset + ascent)
} }
@ -824,9 +831,7 @@ impl InlineFlowData {
bottommost = bottom_from_base; bottommost = bottom_from_base;
} }
do cur_box.with_mut_base |base| { cur_box.base().position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
base.position.origin.y = line.bounds.origin.y + offset;
}
} }
// Calculate the distance from baseline to the top of the biggest box with 'bottom' value. // Calculate the distance from baseline to the top of the biggest box with 'bottom' value.
@ -849,21 +854,15 @@ impl InlineFlowData {
// All boxes' y position is updated following the new baseline offset. // All boxes' y position is updated following the new baseline offset.
for box_i in line.range.eachi() { for box_i in line.range.eachi() {
let cur_box = self.boxes[box_i]; let cur_box = self.boxes[box_i];
let adjust_offset = match cur_box.vertical_align() { let cur_box_base = cur_box.base();
vertical_align::top => { let adjust_offset = match cur_box_base.vertical_align() {
Au(0) vertical_align::top => Au::new(0),
}, vertical_align::bottom => baseline_offset + bottommost,
vertical_align::bottom => { _ => baseline_offset,
baseline_offset + bottommost
},
_ => {
baseline_offset
}
}; };
do cur_box.with_mut_base |base| { cur_box_base.position.mutate().ptr.origin.y =
base.position.origin.y = base.position.origin.y + adjust_offset; 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. // 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; line.bounds.size.height = topmost + bottommost;
} // End of `lines.each` loop. } // End of `lines.each` loop.
self.common.position.size.height = self.base.position.size.height =
if self.lines.len() > 0 { if self.lines.len() > 0 {
self.lines.last().bounds.origin.y + self.lines.last().bounds.size.height self.lines.last().bounds.origin.y + self.lines.last().bounds.size.height
} else { } else {
Au(0) Au::new(0)
}; };
self.common.floats_out = scanner.floats_out().translate(Point2D(Au(0), self.base.floats_out = scanner.floats_out()
-self.common.position.size.height)); .translate(Point2D(Au::new(0),
-self.base.position.size.height));
} }
pub fn build_display_list_inline<E:ExtraDisplayListData>(&self, fn collapse_margins(&mut self,
builder: &DisplayListBuilder, _: bool,
dirty: &Rect<Au>, _: &mut bool,
list: &Cell<DisplayList<E>>) _: &mut Au,
-> bool { _: &mut Au,
collapsing: &mut Au,
//TODO: implement inline iframe size messaging collapsible: &mut Au) {
if self.common.node.is_iframe_element() { *collapsing = Au::new(0);
error!("inline iframe size messaging not implemented yet"); // 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);
} }
let abs_rect = Rect(self.common.abs_position, self.common.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.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
} }
} }

View file

@ -11,7 +11,9 @@ use layout::aux::LayoutAuxMethods;
use layout::box_builder::LayoutTreeBuilder; use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder}; 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 layout::incremental::{RestyleDamage, BubbleWidths};
use std::cast::transmute; use std::cast::transmute;
@ -42,7 +44,7 @@ use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg}; use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; 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::tree::TreeNodeRef;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
@ -67,6 +69,129 @@ struct LayoutTask {
profiler_chan: ProfilerChan, 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 { impl LayoutTask {
pub fn create(id: PipelineId, pub fn create(id: PipelineId,
port: Port<Msg>, port: Port<Msg>,
@ -180,14 +305,13 @@ impl LayoutTask {
// FIXME: Bad copy! // FIXME: Bad copy!
let doc_url = data.url.clone(); 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: received layout request for: %s", doc_url.to_str());
debug!("layout: damage is %?", data.damage); debug!("layout: damage is %?", data.damage);
debug!("layout: parsed Node tree"); debug!("layout: parsed Node tree");
debug!("%?", node.dump()); debug!("%?", node.dump());
// Reset the image cache. // 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); self.doc_url = Some(doc_url);
let screen_size = Size2D(Au::from_px(data.window_size.width as int), let screen_size = Size2D(Au::from_px(data.window_size.width as int),
@ -218,10 +342,10 @@ impl LayoutTask {
} }
// Construct the flow tree. // Construct the flow tree.
let mut layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory, let mut layout_root: ~FlowContext: = do profile(time::LayoutTreeBuilderCategory,
self.profiler_chan.clone()) { self.profiler_chan.clone()) {
let mut builder = LayoutTreeBuilder::new(); 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, Ok(root) => root,
Err(*) => fail!(~"Root flow should always exist") Err(*) => fail!(~"Root flow should always exist")
}; };
@ -229,41 +353,11 @@ impl LayoutTask {
layout_root layout_root
}; };
// Propagate restyle damage up and down the tree, as appropriate. // Propagate damage.
// FIXME: Merge this with flow tree building and/or the other traversals. layout_root.traverse_preorder(&mut PropagateDamageTraversal {
do layout_root.each_preorder |flow| { resized: resized,
// Also set any damage implied by resize. });
if resized { layout_root.traverse_postorder(&mut ComputeDamageTraversal.clone());
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
};
debug!("layout: constructed Flow tree"); debug!("layout: constructed Flow tree");
debug!("%?", layout_root.dump()); 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 // Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes. // the boxes.
do profile(time::LayoutMainCategory, self.profiler_chan.clone()) { do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
do layout_root.each_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) |flow| { let _ = layout_root.traverse_postorder(&mut BubbleWidthsTraversal(&mut layout_ctx));
flow.bubble_widths(&mut layout_ctx);
true
};
// 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)) // 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. // 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. // NOTE: this currently computes borders, so any pruning should separate that operation out.
debug!("assigning widths"); let _ = layout_root.traverse_preorder(&mut AssignWidthsTraversal(&mut layout_ctx));
do layout_root.each_preorder |flow| {
flow.assign_widths(&mut layout_ctx);
true
};
// For now, this is an inorder traversal // For now, this is an inorder traversal
// FIXME: prune this traversal as well // FIXME: prune this traversal as well
debug!("assigning height"); let _ = layout_root.traverse_postorder(&mut AssignHeightsTraversal(&mut layout_ctx));
do layout_root.each_bu_sub_inorder |flow| {
flow.assign_height(&mut layout_ctx);
true
};
} }
// Build the display list if necessary, and send it to the renderer. // Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay { if data.goal == ReflowForDisplay {
do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) { 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: Set options on the builder before building.
// TODO: Be smarter about what needs painting. // TODO: Be smarter about what needs painting.
let root_pos = &layout_root.position().clone(); let mut traversal = DisplayListBuildingTraversal {
layout_root.each_preorder_prune(|flow| { builder: DisplayListBuilder {
flow.build_display_list(&builder, root_pos, display_list) ctx: &layout_ctx,
}, |_| { true } ); },
root_pos: flow::base(layout_root).position.clone(),
let root_size = do layout_root.with_base |base| { display_list: ~Cell::new(DisplayList::<AbstractNode<()>>::new()),
base.position.size
}; };
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()) { for i in range(0,display_list.get().list.len()) {
let node: AbstractNode<LayoutView> = unsafe { let node: AbstractNode<LayoutView> = unsafe {
@ -445,7 +525,7 @@ impl LayoutTask {
Some(ref list) => { Some(ref list) => {
let display_list = list.get(); let display_list = list.get();
let (x, y) = (Au::from_frac_px(point.x as f64), 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(()); let mut resp = Err(());
// iterate in reverse to ensure we have the most recently painted render box // iterate in reverse to ensure we have the most recently painted render box
for display_item in display_list.list.rev_iter() { 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 // 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 // re-requested. We probably don't need to go all the way back to
// the script task for this. // the script task for this.
fn make_on_image_available_cb(&self, script_chan: ScriptChan) fn make_on_image_available_cb(&self) -> @ImageResponder {
-> ~fn() -> ~fn(ImageResponseMsg) {
// This has a crazy signature because the image cache needs to // This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event // make multiple copies of the callback, and the dom event
// channel is not a copyable type, so this is actually a // channel is not a copyable type, so this is actually a
// little factory to produce callbacks // little factory to produce callbacks
let id = self.id.clone(); @LayoutImageResponder {
let f: ~fn() -> ~fn(ImageResponseMsg) = || { id: self.id.clone(),
let script_chan = script_chan.clone(); script_chan: self.script_chan.clone(),
let f: ~fn(ImageResponseMsg) = |_| { } as @ImageResponder
script_chan.send(SendEventMsg(id.clone(), ReflowEvent))
};
f
};
return f;
} }
} }

View file

@ -11,6 +11,7 @@ use style::ComputedValues;
use style::properties::common_types::computed; use style::properties::common_types::computed;
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model". /// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
#[deriving(Clone)]
pub struct BoxModel { pub struct BoxModel {
border: SideOffsets2D<Au>, border: SideOffsets2D<Au>,
padding: SideOffsets2D<Au>, padding: SideOffsets2D<Au>,
@ -26,7 +27,9 @@ pub enum MaybeAuto {
} }
impl 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 { match length {
computed::LPA_Auto => Auto, computed::LPA_Auto => Auto,
computed::LPA_Percentage(percent) => Specified(containing_length.scale_by(percent)), 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 { pub fn specified_or_default(&self, default: Au) -> Au {
match *self { match *self {
Auto => default, Auto => default,
@ -41,8 +45,9 @@ impl MaybeAuto {
} }
} }
#[inline]
pub fn specified_or_zero(&self) -> Au { 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 { 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) { pub fn compute_borders(&mut self, style: &ComputedValues) {
// Compute the borders.
self.border.top = style.Border.border_top_width; self.border.top = style.Border.border_top_width;
self.border.right = style.Border.border_right_width; self.border.right = style.Border.border_right_width;
self.border.bottom = style.Border.border_bottom_width; self.border.bottom = style.Border.border_bottom_width;
self.border.left = style.Border.border_left_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) { 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.top = self.compute_padding_length(style.Padding.padding_top,
self.padding.right = self.compute_padding_length(style.Padding.padding_right, containing_width); containing_width);
self.padding.bottom = self.compute_padding_length(style.Padding.padding_bottom, containing_width); self.padding.right = self.compute_padding_length(style.Padding.padding_right,
self.padding.left = self.compute_padding_length(style.Padding.padding_left, containing_width); 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 { pub fn noncontent_width(&self) -> Au {

View file

@ -8,51 +8,11 @@ use std::vec;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text}; use gfx::text::util::{CompressWhitespaceNewline, transform_text};
use layout::box::{RenderBox, RenderBoxBase, TextRenderBox}; use layout::box::{RenderBox, RenderBoxUtils, TextRenderBox, UnscannedTextRenderBoxClass};
use layout::box::{TextRenderBoxClass, UnscannedTextRenderBoxClass};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::flow::FlowContext; use layout::flow::FlowContext;
use servo_util::range::Range; 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. /// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
struct TextRunScanner { struct TextRunScanner {
clump: Range, clump: Range,
@ -67,7 +27,7 @@ impl TextRunScanner {
pub fn scan_for_runs(&mut self, ctx: &LayoutContext, flow: &mut FlowContext) { 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 // FIXME: this assertion fails on wikipedia, but doesn't seem
// to cause problems. // to cause problems.
// assert!(inline.boxes.len() > 0); // assert!(inline.boxes.len() > 0);
@ -76,9 +36,11 @@ impl TextRunScanner {
let mut last_whitespace = true; let mut last_whitespace = true;
let mut out_boxes = ~[]; let mut out_boxes = ~[];
for box_i in range(0, flow.imm_inline().boxes.len()) { for box_i in range(0, flow.as_immutable_inline().boxes.len()) {
debug!("TextRunScanner: considering box: %?", flow.imm_inline().boxes[box_i].debug_str()); debug!("TextRunScanner: considering box: %u", box_i);
if box_i > 0 && !can_coalesce_text_nodes(flow.imm_inline().boxes, box_i-1, 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); last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
} }
self.clump.extend_by(1); self.clump.extend_by(1);
@ -91,17 +53,17 @@ impl TextRunScanner {
debug!("TextRunScanner: swapping out boxes."); debug!("TextRunScanner: swapping out boxes.");
// Swap out the old and new box list of the flow. // 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. // 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!(left_i < boxes.len());
assert!(right_i > 0 && right_i < boxes.len()); assert!(right_i > 0 && right_i < boxes.len());
assert!(left_i != right_i); assert!(left_i != right_i);
let (left, right) = (boxes[left_i], boxes[right_i]); let (left, right) = (boxes[left_i], boxes[right_i]);
match (left, right) { match (left.class(), right.class()) {
(UnscannedTextRenderBoxClass(*), UnscannedTextRenderBoxClass(*)) => { (UnscannedTextRenderBoxClass, UnscannedTextRenderBoxClass) => {
left.can_merge_with_box(right) left.can_merge_with_box(right)
} }
(_, _) => false (_, _) => false
@ -123,18 +85,17 @@ impl TextRunScanner {
ctx: &LayoutContext, ctx: &LayoutContext,
flow: &mut FlowContext, flow: &mut FlowContext,
last_whitespace: bool, last_whitespace: bool,
out_boxes: &mut ~[RenderBox]) -> bool { out_boxes: &mut ~[@RenderBox])
let inline = flow.inline(); -> bool {
let inline = flow.as_inline();
let in_boxes = &inline.boxes; let in_boxes = &inline.boxes;
assert!(self.clump.length() > 0); assert!(self.clump.length() > 0);
debug!("TextRunScanner: flushing boxes in range=%?", self.clump); debug!("TextRunScanner: flushing boxes in range=%?", self.clump);
let is_singleton = self.clump.length() == 1; let is_singleton = self.clump.length() == 1;
let is_text_clump = match in_boxes[self.clump.begin()] { let possible_text_clump = in_boxes[self.clump.begin()]; // FIXME(pcwalton): Rust bug
UnscannedTextRenderBoxClass(*) => true, let is_text_clump = possible_text_clump.class() == UnscannedTextRenderBoxClass;
_ => false
};
let mut new_whitespace = last_whitespace; let mut new_whitespace = last_whitespace;
@ -148,9 +109,9 @@ impl TextRunScanner {
}, },
(true, true) => { (true, true) => {
let old_box = in_boxes[self.clump.begin()]; let old_box = in_boxes[self.clump.begin()];
let text = old_box.raw_text(); let text = old_box.as_unscanned_text_render_box().raw_text();
let font_style = old_box.font_style(); let font_style = old_box.base().font_style();
let decoration = old_box.text_decoration(); let decoration = old_box.base().text_decoration();
// TODO(#115): Use the actual CSS `white-space` property of the relevant style. // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
let compression = CompressWhitespaceNewline; let compression = CompressWhitespaceNewline;
@ -166,12 +127,10 @@ impl TextRunScanner {
let run = @fontgroup.create_textrun(transformed_text, decoration); let run = @fontgroup.create_textrun(transformed_text, decoration);
debug!("TextRunScanner: pushing single text box in range: %? (%?)", self.clump, text); debug!("TextRunScanner: pushing single text box in range: %? (%?)", self.clump, text);
let new_box = do old_box.with_base |old_box_base| { let range = Range::new(0, run.char_len());
let range = Range::new(0, run.char_len()); let new_box = @TextRenderBox::new((*old_box.base()).clone(), run, range);
@mut adapt_textbox_with_range(*old_box_base, run, range)
};
out_boxes.push(TextRenderBoxClass(new_box)); out_boxes.push(new_box as @RenderBox);
} }
}, },
(false, true) => { (false, true) => {
@ -185,7 +144,8 @@ impl TextRunScanner {
// `transform_text`, so that boxes starting and/or ending with whitespace can // `transform_text`, so that boxes starting and/or ending with whitespace can
// be compressed correctly with respect to the text run. // be compressed correctly with respect to the text run.
let idx = i + self.clump.begin(); let idx = i + self.clump.begin();
let (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, compression,
last_whitespace_in_clump); last_whitespace_in_clump);
last_whitespace_in_clump = new_whitespace; 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 // 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 // font group fonts. This is probably achieved by creating the font group above
// and then letting `FontGroup` decide which `Font` to stick into the text run. // and then letting `FontGroup` decide which `Font` to stick into the text run.
let 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 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 // TextRuns contain a cycle which is usually resolved by the teardown
// sequence. If no clump takes ownership, however, it will leak. // sequence. If no clump takes ownership, however, it will leak.
@ -234,10 +195,10 @@ impl TextRunScanner {
continue continue
} }
do in_boxes[i].with_base |base| { let new_box = @TextRenderBox::new((*in_boxes[i].base()).clone(),
let new_box = @mut adapt_textbox_with_range(*base, run.unwrap(), range); run.unwrap(),
out_boxes.push(TextRenderBoxClass(new_box)); range);
} out_boxes.push(new_box as @RenderBox);
} }
} }
} // End of match. } // End of match.

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::box::{RenderBox}; use layout::box::{RenderBox, RenderBoxUtils};
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use servo_util::range::Range; use servo_util::range::Range;
@ -46,7 +46,7 @@ impl ElementMapping {
self.entries.iter().enumerate() self.entries.iter().enumerate()
} }
pub fn repair_for_box_changes(&mut self, old_boxes: &[RenderBox], new_boxes: &[RenderBox]) { pub fn repair_for_box_changes(&mut self, old_boxes: &[@RenderBox], new_boxes: &[@RenderBox]) {
let entries = &mut self.entries; let entries = &mut self.entries;
debug!("--- Old boxes: ---"); debug!("--- Old boxes: ---");
@ -88,18 +88,8 @@ impl ElementMapping {
repair_stack.push(item); repair_stack.push(item);
entries_k += 1; entries_k += 1;
} }
// XXX: the following loop form causes segfaults; assigning to locals doesn't. while new_j < new_boxes.len() &&
// while new_j < new_boxes.len() && old_boxes[old_i].d().node != new_boxes[new_j].d().node { old_boxes[old_i].base().node != new_boxes[new_j].base().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
}
debug!("repair_for_box_changes: Slide through new box %u", new_j); debug!("repair_for_box_changes: Slide through new box %u", new_j);
new_j += 1; new_j += 1;
} }

View file

@ -44,7 +44,6 @@ impl Pipeline {
opts: Opts, opts: Opts,
script_pipeline: &Pipeline, script_pipeline: &Pipeline,
size_future: Future<Size2D<uint>>) -> Pipeline { size_future: Future<Size2D<uint>>) -> Pipeline {
let (layout_port, layout_chan) = special_stream!(LayoutChan); let (layout_port, layout_chan) = special_stream!(LayoutChan);
let (render_port, render_chan) = special_stream!(RenderChan); let (render_port, render_chan) = special_stream!(RenderChan);
@ -78,7 +77,6 @@ impl Pipeline {
script_pipeline.script_chan.clone(), script_pipeline.script_chan.clone(),
layout_chan, layout_chan,
render_chan) render_chan)
} }
pub fn create(id: PipelineId, pub fn create(id: PipelineId,

View file

@ -11,6 +11,8 @@ use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEven
use windowing::{Forward, Back}; use windowing::{Forward, Back};
use alert::{Alert, AlertMethods}; use alert::{Alert, AlertMethods};
use extra::time::Timespec;
use extra::time;
use std::libc::c_int; use std::libc::c_int;
use std::local_data; use std::local_data;
use geom::point::Point2D; use geom::point::Point2D;
@ -20,8 +22,6 @@ use servo_msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayou
use glfw; use glfw;
static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];
/// A structure responsible for setting up and tearing down the entire windowing system. /// A structure responsible for setting up and tearing down the entire windowing system.
pub struct Application; pub struct Application;
@ -57,7 +57,8 @@ pub struct Window {
ready_state: ReadyState, ready_state: ReadyState,
render_state: RenderState, render_state: RenderState,
throbber_frame: u8,
last_title_set_time: Timespec,
} }
impl WindowMethods<Application> for Window { impl WindowMethods<Application> for Window {
@ -81,7 +82,8 @@ impl WindowMethods<Application> for Window {
ready_state: Blank, ready_state: Blank,
render_state: IdleRenderState, render_state: IdleRenderState,
throbber_frame: 0,
last_title_set_time: Timespec::new(0, 0),
}; };
install_local_window(window); install_local_window(window);
@ -141,8 +143,7 @@ impl WindowMethods<Application> for Window {
return self.event_queue.shift() return self.event_queue.shift()
} }
glfw::poll_events(); 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() { if self.glfw_window.should_close() {
QuitWindowEvent QuitWindowEvent
} else if !self.event_queue.is_empty() { } else if !self.event_queue.is_empty() {
@ -155,7 +156,7 @@ impl WindowMethods<Application> for Window {
/// Sets the ready state. /// Sets the ready state.
fn set_ready_state(@mut self, ready_state: ReadyState) { fn set_ready_state(@mut self, ready_state: ReadyState) {
self.ready_state = ready_state; self.ready_state = ready_state;
self.update_window_title(true) self.update_window_title()
} }
/// Sets the render state. /// Sets the render state.
@ -168,7 +169,7 @@ impl WindowMethods<Application> for Window {
} }
self.render_state = render_state; self.render_state = render_state;
self.update_window_title(true) self.update_window_title()
} }
fn hidpi_factor(@mut self) -> f32 { fn hidpi_factor(@mut self) -> f32 {
@ -180,25 +181,30 @@ impl WindowMethods<Application> for Window {
impl Window { impl Window {
/// Helper function to set the window title in accordance with the ready state. /// Helper function to set the window title in accordance with the ready state.
fn update_window_title(&self, state_change: bool) { fn update_window_title(&mut self) {
let throbber = THROBBER[self.throbber_frame]; 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 { match self.ready_state {
Blank => { Blank => {
if state_change { self.glfw_window.set_title(fmt!("blank — Servo")) } self.glfw_window.set_title(fmt!("blank — Servo"))
} }
Loading => { Loading => {
self.glfw_window.set_title(fmt!("%c Loading — Servo", throbber)) self.glfw_window.set_title(fmt!("Loading — Servo"))
} }
PerformingLayout => { PerformingLayout => {
self.glfw_window.set_title(fmt!("%c Performing Layout — Servo", throbber)) self.glfw_window.set_title(fmt!("Performing Layout — Servo"))
} }
FinishedLoading => { FinishedLoading => {
match self.render_state { match self.render_state {
RenderingRenderState => { RenderingRenderState => {
self.glfw_window.set_title(fmt!("%c Rendering — Servo", throbber)) self.glfw_window.set_title(fmt!("Rendering — Servo"))
} }
IdleRenderState => { IdleRenderState => {
if state_change { self.glfw_window.set_title("Servo") } self.glfw_window.set_title("Servo")
} }
} }
} }

View file

@ -17,6 +17,10 @@ use std::task;
use servo_util::url::{UrlMap, url_map}; use servo_util::url::{UrlMap, url_map};
use extra::url::Url; use extra::url::Url;
pub trait ImageResponder {
fn respond(&self) -> ~fn(ImageResponseMsg);
}
pub fn LocalImageCache(image_cache_task: ImageCacheTask) -> LocalImageCache { pub fn LocalImageCache(image_cache_task: ImageCacheTask) -> LocalImageCache {
LocalImageCache { LocalImageCache {
image_cache_task: image_cache_task, image_cache_task: image_cache_task,
@ -29,7 +33,7 @@ pub fn LocalImageCache(image_cache_task: ImageCacheTask) -> LocalImageCache {
pub struct LocalImageCache { pub struct LocalImageCache {
priv image_cache_task: ImageCacheTask, priv image_cache_task: ImageCacheTask,
priv round_number: uint, priv round_number: uint,
priv on_image_available: Option<~fn() -> ~fn(ImageResponseMsg)>, priv on_image_available: Option<@ImageResponder>,
priv state_map: UrlMap<@mut ImageState> priv state_map: UrlMap<@mut ImageState>
} }
@ -43,7 +47,7 @@ struct ImageState {
impl LocalImageCache { impl LocalImageCache {
/// The local cache will only do a single remote request for a given /// 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 /// 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.round_number += 1;
self.on_image_available = Some(on_image_available); self.on_image_available = Some(on_image_available);
} }
@ -109,7 +113,7 @@ impl LocalImageCache {
// on the image to load and triggering layout // on the image to load and triggering layout
let image_cache_task = self.image_cache_task.clone(); let image_cache_task = self.image_cache_task.clone();
assert!(self.on_image_available.is_some()); 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(); let url = (*url).clone();
do task::spawn { do task::spawn {
let (response_port, response_chan) = comm::stream(); let (response_port, response_chan) = comm::stream();

View file

@ -882,7 +882,7 @@ pub struct DisplayBoxes {
/// Data that layout associates with a node. /// Data that layout associates with a node.
pub struct LayoutData { pub struct LayoutData {
/// The results of CSS matching for this node. /// The results of CSS matching for this node.
applicable_declarations: ~[@[PropertyDeclaration]], applicable_declarations: ~[Arc<~[PropertyDeclaration]>],
/// The results of CSS styling for this node. /// The results of CSS styling for this node.
style: Option<ComputedValues>, style: Option<ComputedValues>,
@ -902,11 +902,21 @@ impl LayoutData {
applicable_declarations: ~[], applicable_declarations: ~[],
style: None, style: None,
restyle_damage: 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> { impl AbstractNode<LayoutView> {
// These accessors take a continuation rather than returning a reference, because // These accessors take a continuation rather than returning a reference, because
// an AbstractNode doesn't have a lifetime parameter relating to the underlying // an AbstractNode doesn't have a lifetime parameter relating to the underlying

View file

@ -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_net::resource_task::{Load, Payload, Done, ResourceTask, load_whole_resource};
use servo_util::tree::{TreeNodeRef, ElementLike}; use servo_util::tree::{TreeNodeRef, ElementLike};
use servo_util::url::make_url; use servo_util::url::make_url;
use extra::url::Url;
use extra::future::Future; use extra::future::Future;
use extra::url::Url;
use geom::size::Size2D; use geom::size::Size2D;
macro_rules! handle_element( macro_rules! handle_element(

View file

@ -5,6 +5,7 @@
// This file is a Mako template: http://www.makotemplates.org/ // This file is a Mako template: http://www.makotemplates.org/
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
pub use extra::arc::Arc;
pub use std::iter; pub use std::iter;
pub use cssparser::*; pub use cssparser::*;
pub use errors::{ErrorLoggerIterator, log_css_error}; pub use errors::{ErrorLoggerIterator, log_css_error};
@ -979,7 +980,7 @@ fn get_initial_values() -> ComputedValues {
// Most specific/important declarations last // Most specific/important declarations last
pub fn cascade(applicable_declarations: &[@[PropertyDeclaration]], pub fn cascade(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
parent_style: Option< &ComputedValues>) parent_style: Option< &ComputedValues>)
-> ComputedValues { -> ComputedValues {
let initial_keep_alive; let initial_keep_alive;
@ -1002,7 +1003,7 @@ pub fn cascade(applicable_declarations: &[@[PropertyDeclaration]],
% endfor % endfor
}; };
for sub_list in applicable_declarations.iter() { for sub_list in applicable_declarations.iter() {
for declaration in sub_list.iter() { for declaration in sub_list.get().iter() {
match declaration { match declaration {
% for property in LONGHANDS: % for property in LONGHANDS:
&${property.ident}_declaration(ref value) => { &${property.ident}_declaration(ref value) => {

View file

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::at_vec;
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
use extra::arc::Arc;
use extra::sort::tim_sort; use extra::sort::tim_sort;
use selectors::*; use selectors::*;
@ -54,7 +54,7 @@ impl Stylist {
// TODO: avoid copying? // TODO: avoid copying?
rules.$priority.push(Rule { rules.$priority.push(Rule {
selector: @(*selector).clone(), 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>( pub fn get_applicable_declarations<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
&self, element: &T, style_attribute: Option<&PropertyDeclarationBlock>, &self, element: &T, style_attribute: Option<&PropertyDeclarationBlock>,
pseudo_element: Option<PseudoElement>) -> ~[@[PropertyDeclaration]] { pseudo_element: Option<PseudoElement>) -> ~[Arc<~[PropertyDeclaration]>] {
assert!(element.is_element()) assert!(element.is_element())
assert!(style_attribute.is_none() || pseudo_element.is_none(), assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements") "Style attributes do not apply to pseudo-elements")
@ -89,7 +89,7 @@ impl Stylist {
($rules: expr) => { ($rules: expr) => {
for rule in $rules.iter() { for rule in $rules.iter() {
if matches_selector::<N, T, E>(rule.selector, element, pseudo_element) { 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. // Style attributes have author origin but higher specificity than style rules.
append!(self.author_rules.normal); append!(self.author_rules.normal);
// TODO: avoid copying? // 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); append!(self.author_rules.important);
// TODO: avoid copying? // 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.user_rules.important);
append!(self.ua_rules.important); append!(self.ua_rules.important);
@ -131,7 +131,7 @@ impl PerOriginRules {
#[deriving(Clone)] #[deriving(Clone)]
struct Rule { struct Rule {
selector: @Selector, selector: @Selector,
declarations: @[PropertyDeclaration], declarations: Arc<~[PropertyDeclaration]>,
} }

View file

@ -8,52 +8,78 @@ use geom::size::Size2D;
use std::num::{NumCast, One, Zero}; use std::num::{NumCast, One, Zero};
#[deriving(Clone,Eq)] #[deriving(Clone)]
pub struct Au(i32); 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 { impl Add<Au,Au> for Au {
#[inline]
fn add(&self, other: &Au) -> Au { Au(**self + **other) } fn add(&self, other: &Au) -> Au { Au(**self + **other) }
} }
impl Sub<Au,Au> for Au { impl Sub<Au,Au> for Au {
#[inline]
fn sub(&self, other: &Au) -> Au { Au(**self - **other) } fn sub(&self, other: &Au) -> Au { Au(**self - **other) }
} }
impl Mul<Au,Au> for Au { impl Mul<Au,Au> for Au {
#[inline]
fn mul(&self, other: &Au) -> Au { Au(**self * **other) } fn mul(&self, other: &Au) -> Au { Au(**self * **other) }
} }
impl Div<Au,Au> for Au { impl Div<Au,Au> for Au {
#[inline]
fn div(&self, other: &Au) -> Au { Au(**self / **other) } fn div(&self, other: &Au) -> Au { Au(**self / **other) }
} }
impl Rem<Au,Au> for Au { impl Rem<Au,Au> for Au {
#[inline]
fn rem(&self, other: &Au) -> Au { Au(**self % **other) } fn rem(&self, other: &Au) -> Au { Au(**self % **other) }
} }
impl Neg<Au> for Au { impl Neg<Au> for Au {
#[inline]
fn neg(&self) -> Au { Au(-**self) } fn neg(&self) -> Au { Au(-**self) }
} }
impl Ord for Au { impl Ord for Au {
#[inline]
fn lt(&self, other: &Au) -> bool { **self < **other } fn lt(&self, other: &Au) -> bool { **self < **other }
#[inline]
fn le(&self, other: &Au) -> bool { **self <= **other } fn le(&self, other: &Au) -> bool { **self <= **other }
#[inline]
fn ge(&self, other: &Au) -> bool { **self >= **other } fn ge(&self, other: &Au) -> bool { **self >= **other }
#[inline]
fn gt(&self, other: &Au) -> bool { **self > **other } fn gt(&self, other: &Au) -> bool { **self > **other }
} }
impl One for Au { impl One for Au {
#[inline]
fn one() -> Au { Au(1) } fn one() -> Au { Au(1) }
} }
impl Zero for Au { impl Zero for Au {
#[inline]
fn zero() -> Au { Au(0) } fn zero() -> Au { Au(0) }
#[inline]
fn is_zero(&self) -> bool { **self == 0 } fn is_zero(&self) -> bool { **self == 0 }
} }
impl Num for Au {} impl Num for Au {}
#[inline]
pub fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } } 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 } } pub fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } }
impl NumCast for Au { impl NumCast for Au {
@ -70,6 +96,14 @@ impl ToPrimitive for Au {
fn to_u64(&self) -> Option<u64> { fn to_u64(&self) -> Option<u64> {
Some(**self as 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> { 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 { impl Au {
pub fn scale_by(self, factor: f64) -> Au { /// FIXME(pcwalton): Workaround for lack of cross crate inlining of newtype structs!
Au(((*self as f64) * factor).round() as i32) #[inline]
pub fn new(value: i32) -> Au {
Au(value)
} }
#[inline]
pub fn scale_by(self, factor: f64) -> Au {
Au(((*self as f64) * factor) as i32)
}
#[inline]
pub fn from_px(px: int) -> Au { pub fn from_px(px: int) -> Au {
NumCast::from(px * 60).unwrap() NumCast::from(px * 60).unwrap()
} }
#[inline]
pub fn to_nearest_px(&self) -> int { pub fn to_nearest_px(&self) -> int {
((**self as f64) / 60f64).round() as int ((**self as f64) / 60f64).round() as int
} }
#[inline]
pub fn to_snapped(&self) -> Au { pub fn to_snapped(&self) -> Au {
let res = **self % 60i32; let res = **self % 60i32;
return if res >= 30i32 { return Au(**self - res + 60i32) } return if res >= 30i32 { return Au(**self - res + 60i32) }
else { return Au(**self - res) }; else { return Au(**self - res) };
} }
#[inline]
pub fn zero_point() -> Point2D<Au> { pub fn zero_point() -> Point2D<Au> {
Point2D(Au(0), Au(0)) Point2D(Au(0), Au(0))
} }
#[inline]
pub fn zero_rect() -> Rect<Au> { pub fn zero_rect() -> Rect<Au> {
let z = Au(0); let z = Au(0);
Rect(Point2D(z, z), Size2D(z, z)) Rect(Point2D(z, z), Size2D(z, z))
} }
#[inline]
pub fn from_pt(pt: f64) -> Au { pub fn from_pt(pt: f64) -> Au {
from_px(pt_to_px(pt) as int) from_px(pt_to_px(pt) as int)
} }
#[inline]
pub fn from_frac_px(px: f64) -> Au { pub fn from_frac_px(px: f64) -> Au {
Au((px * 60f64) as i32) Au((px * 60f64) as i32)
} }
#[inline]
pub fn min(x: Au, y: Au) -> Au { if *x < *y { x } else { y } } 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 } } 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 { pub fn from_pt(pt: f64) -> Au {
from_px((pt / 72f64 * 96f64) as int) from_px((pt / 72f64 * 96f64) as int)
} }

View file

@ -22,58 +22,76 @@ pub struct Range {
} }
impl Range { impl Range {
#[inline]
pub fn new(off: uint, len: uint) -> Range { pub fn new(off: uint, len: uint) -> Range {
Range { off: off, len: len } Range {
off: off,
len: len,
}
} }
#[inline]
pub fn empty() -> Range { pub fn empty() -> Range {
Range::new(0, 0) Range::new(0, 0)
} }
} }
impl Range { impl Range {
#[inline]
pub fn begin(&self) -> uint { self.off } pub fn begin(&self) -> uint { self.off }
#[inline]
pub fn length(&self) -> uint { self.len } pub fn length(&self) -> uint { self.len }
#[inline]
pub fn end(&self) -> uint { self.off + self.len } pub fn end(&self) -> uint { self.off + self.len }
#[inline]
pub fn eachi(&self) -> iter::Range<uint> { pub fn eachi(&self) -> iter::Range<uint> {
range(self.off, self.off + self.len) range(self.off, self.off + self.len)
} }
#[inline]
pub fn contains(&self, i: uint) -> bool { pub fn contains(&self, i: uint) -> bool {
i >= self.begin() && i < self.end() i >= self.begin() && i < self.end()
} }
#[inline]
pub fn is_valid_for_string(&self, s: &str) -> bool { pub fn is_valid_for_string(&self, s: &str) -> bool {
self.begin() < s.len() && self.end() <= s.len() && self.length() <= s.len() self.begin() < s.len() && self.end() <= s.len() && self.length() <= s.len()
} }
#[inline]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len == 0 self.len == 0
} }
#[inline]
pub fn shift_by(&mut self, i: int) { pub fn shift_by(&mut self, i: int) {
self.off = ((self.off as int) + i) as uint; self.off = ((self.off as int) + i) as uint;
} }
#[inline]
pub fn extend_by(&mut self, i: int) { pub fn extend_by(&mut self, i: int) {
self.len = ((self.len as int) + i) as uint; self.len = ((self.len as int) + i) as uint;
} }
#[inline]
pub fn extend_to(&mut self, i: uint) { pub fn extend_to(&mut self, i: uint) {
self.len = i - self.off; self.len = i - self.off;
} }
#[inline]
pub fn adjust_by(&mut self, off_i: int, len_i: int) { pub fn adjust_by(&mut self, off_i: int, len_i: int) {
self.off = ((self.off as int) + off_i) as uint; self.off = ((self.off as int) + off_i) as uint;
self.len = ((self.len as int) + len_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) { pub fn reset(&mut self, off_i: uint, len_i: uint) {
self.off = off_i; self.off = off_i;
self.len = len_i; self.len = len_i;
} }
#[inline]
pub fn intersect(&self, other: &Range) -> Range { pub fn intersect(&self, other: &Range) -> Range {
let begin = max(self.begin(), other.begin()); let begin = max(self.begin(), other.begin());
let end = min(self.end(), other.end()); let end = min(self.end(), other.end());
@ -88,6 +106,7 @@ impl Range {
/// Computes the relationship between two ranges (`self` and `other`), /// Computes the relationship between two ranges (`self` and `other`),
/// from the point of view of `self`. So, 'EntirelyBefore' means /// from the point of view of `self`. So, 'EntirelyBefore' means
/// that the `self` range is entirely before `other` range. /// that the `self` range is entirely before `other` range.
#[inline]
pub fn relation_to_range(&self, other: &Range) -> RangeRelation { pub fn relation_to_range(&self, other: &Range) -> RangeRelation {
if other.begin() > self.end() { if other.begin() > self.end() {
return EntirelyBefore; return EntirelyBefore;
@ -116,6 +135,7 @@ impl Range {
self, other)); self, other));
} }
#[inline]
pub fn repair_after_coalesced_range(&mut self, other: &Range) { pub fn repair_after_coalesced_range(&mut self, other: &Range) {
let relation = self.relation_to_range(other); let relation = self.relation_to_range(other);
debug!("repair_after_coalesced_range: possibly repairing range %?", self); debug!("repair_after_coalesced_range: possibly repairing range %?", self);

127
src/components/util/slot.rs Normal file
View file

@ -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()
}
}

View file

@ -2,23 +2,28 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Timing functions. //! Timing functions.
use std::comm::{Port, SharedChan};
use std::iter::AdditiveIterator;
use std::rt::io::timer::Timer;
use std::task::spawn_with;
use extra::sort::tim_sort; use extra::sort::tim_sort;
use extra::time::precise_time_ns; use extra::time::precise_time_ns;
use extra::treemap::TreeMap; 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 // front-end representation of the profiler used to communicate with the profiler
#[deriving(Clone)] #[deriving(Clone)]
pub struct ProfilerChan(SharedChan<ProfilerMsg>); pub struct ProfilerChan(SharedChan<ProfilerMsg>);
impl ProfilerChan { impl ProfilerChan {
pub fn new(chan: Chan<ProfilerMsg>) -> ProfilerChan { pub fn new(chan: Chan<ProfilerMsg>) -> ProfilerChan {
ProfilerChan(SharedChan::new(chan)) ProfilerChan(SharedChan::new(chan))
} }
pub fn send_deferred(&self, msg: ProfilerMsg) {
(**self).send_deferred(msg);
}
} }
pub enum ProfilerMsg { pub enum ProfilerMsg {
@ -185,7 +190,7 @@ pub fn profile<T>(category: ProfilerCategory,
let val = callback(); let val = callback();
let end_time = precise_time_ns(); let end_time = precise_time_ns();
let ms = ((end_time - start_time) as f64 / 1000000f64); let ms = ((end_time - start_time) as f64 / 1000000f64);
profiler_chan.send(TimeMsg(category, ms)); profiler_chan.send_deferred(TimeMsg(category, ms));
return val; return val;
} }

View file

@ -16,6 +16,7 @@ extern mod geom;
pub mod cache; pub mod cache;
pub mod geometry; pub mod geometry;
pub mod range; pub mod range;
pub mod slot;
pub mod time; pub mod time;
pub mod tree; pub mod tree;
pub mod url; pub mod url;

@ -1 +1 @@
Subproject commit 81aeed0b6159acdedf7dd530fb6c3bde5ddbb70a Subproject commit d57edb998865f9616d9164d561f26d54ade49642

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 463 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before After
Before After

View file

@ -37,11 +37,11 @@ function run() {
var now = new Date(); var now = new Date();
var div = document.getElementsByTagName('div')[0]; var div = document.getElementsByTagName('div')[0];
var elems = []; var elems = [];
for (var i = 0; i < 900; i++) { for (var i = 0; i < 600; i++) {
elems.push(i); elems.push(i);
} }
var outer = []; var outer = [];
for (var i = 0; i < 900; i++) { for (var i = 0; i < 600; i++) {
outer.push(elems); outer.push(elems);
} }
var a = new Matrix(outer); var a = new Matrix(outer);
@ -56,10 +56,10 @@ function run() {
setTimeout(function forever() { setTimeout(function forever() {
run(); run();
setTimeout(forever, 3000); setTimeout(forever, 1000);
}, 0); }, 0);
</script> </script>
<p>Time required to multiply two 900x900 matrices:</p> <p>Time required to multiply two 600x600 matrices:</p>
<div></div> <div></div>
</body> </body>
</html> </html>