Optimize reflow by changing enums to traits and inlining more
|
@ -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))
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
}
|
}
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
@ -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>
|
||||||
|
|