Convert layout code to use logical directions.

This commit is contained in:
Simon Sapin 2014-07-18 19:33:05 +01:00
parent 94b630e2ed
commit 1807c29f3e
23 changed files with 2397 additions and 2238 deletions

View file

@ -56,13 +56,14 @@ pub extern "C" fn cef_run_message_loop() {
device_pixels_per_px: None,
time_profiler_period: None,
memory_profiler_period: None,
enable_experimental: false,
layout_threads: 1,
//layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1),
exit_after_load: false,
output_file: None,
headless: false,
hard_fail: false,
bubble_widths_separately: false,
bubble_inline_sizes_separately: false,
};
native::start(0, 0 as **u8, proc() {
servo::run(opts);

File diff suppressed because it is too large Load diff

View file

@ -294,7 +294,7 @@ impl<'a> FlowConstructor<'a> {
let mut inline_flow = box InlineFlow::from_fragments((*node).clone(), fragments);
let (ascent, descent) = inline_flow.compute_minimum_ascent_and_descent(self.font_context(), &**node.style());
inline_flow.minimum_height_above_baseline = ascent;
inline_flow.minimum_block_size_above_baseline = ascent;
inline_flow.minimum_depth_below_baseline = descent;
let mut inline_flow = inline_flow as Box<Flow>;
TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow);
@ -1037,10 +1037,10 @@ pub trait FlowConstructionUtils {
fn add_new_child(&mut self, new_child: FlowRef);
/// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it.
/// This will normally run the bubble-widths (minimum and preferred -- i.e. intrinsic -- width)
/// calculation, unless the global `bubble_widths_separately` flag is on.
/// This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic -- inline-size)
/// calculation, unless the global `bubble_inline-sizes_separately` flag is on.
///
/// All flows must be finished at some point, or they will not have their intrinsic widths
/// All flows must be finished at some point, or they will not have their intrinsic inline-sizes
/// properly computed. (This is not, however, a memory safety problem.)
fn finish(&mut self, context: &mut LayoutContext);
}
@ -1062,16 +1062,16 @@ impl FlowConstructionUtils for FlowRef {
}
/// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to
/// it. This will normally run the bubble-widths (minimum and preferred -- i.e. intrinsic --
/// width) calculation, unless the global `bubble_widths_separately` flag is on.
/// it. This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic --
/// inline-size) calculation, unless the global `bubble_inline-sizes_separately` flag is on.
///
/// All flows must be finished at some point, or they will not have their intrinsic widths
/// All flows must be finished at some point, or they will not have their intrinsic inline-sizes
/// properly computed. (This is not, however, a memory safety problem.)
///
/// This must not be public because only the layout constructor can do this.
fn finish(&mut self, context: &mut LayoutContext) {
if !context.opts.bubble_widths_separately {
self.get_mut().bubble_widths(context)
if !context.opts.bubble_inline_sizes_separately {
self.get_mut().bubble_inline_sizes(context)
}
}
}

View file

@ -6,8 +6,7 @@
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use geom::rect::Rect;
use geom::size::Size2D;
use geom::{Rect, Size2D};
use gfx::display_list::OpaqueNode;
use gfx::font_context::FontContext;
use gfx::font_cache_task::FontCacheTask;

View file

@ -2,10 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use servo_util::geometry::{Au, max, min};
use servo_util::logical_geometry::WritingMode;
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use std::i32;
use std::fmt;
use style::computed_values::float;
@ -39,7 +38,7 @@ pub enum ClearType {
#[deriving(Clone)]
struct Float {
/// The boundaries of this float.
bounds: Rect<Au>,
bounds: LogicalRect<Au>,
/// The kind of float: left or right.
kind: FloatKind,
}
@ -58,22 +57,22 @@ impl fmt::Show for Float {
struct FloatList {
/// Information about each of the floats here.
floats: Vec<Float>,
/// Cached copy of the maximum top offset of the float.
max_top: Au,
/// Cached copy of the maximum block-start offset of the float.
max_block_start: Au,
}
impl FloatList {
fn new() -> FloatList {
FloatList {
floats: vec!(),
max_top: Au(0),
max_block_start: Au(0),
}
}
}
impl fmt::Show for FloatList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "max_top={} floats={:?}", self.max_top, self.floats)
write!(f, "max_block_start={} floats={:?}", self.max_block_start, self.floats)
}
}
@ -119,23 +118,23 @@ impl FloatListRef {
/// All the information necessary to place a float.
pub struct PlacementInfo {
/// The dimensions of the float.
pub size: Size2D<Au>,
/// The minimum top of the float, as determined by earlier elements.
pub size: LogicalSize<Au>,
/// The minimum block-start of the float, as determined by earlier elements.
pub ceiling: Au,
/// The maximum right position of the float, generally determined by the containing block.
pub max_width: Au,
/// The maximum inline-end position of the float, generally determined by the containing block.
pub max_inline_size: Au,
/// The kind of float.
pub kind: FloatKind
}
impl fmt::Show for PlacementInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "size={} ceiling={} max_width={} kind={:?}", self.size, self.ceiling, self.max_width, self.kind)
write!(f, "size={} ceiling={} max_inline_size={} kind={:?}", self.size, self.ceiling, self.max_inline_size, self.kind)
}
}
fn range_intersect(top_1: Au, bottom_1: Au, top_2: Au, bottom_2: Au) -> (Au, Au) {
(max(top_1, top_2), min(bottom_1, bottom_2))
fn range_intersect(block_start_1: Au, block_end_1: Au, block_start_2: Au, block_end_2: Au) -> (Au, Au) {
(max(block_start_1, block_start_2), min(block_end_1, block_end_2))
}
/// Encapsulates information about floats. This is optimized to avoid allocation if there are
@ -145,7 +144,8 @@ pub struct Floats {
/// The list of floats.
list: FloatListRef,
/// The offset of the flow relative to the first float.
offset: Point2D<Au>,
offset: LogicalSize<Au>,
pub writing_mode: WritingMode,
}
impl fmt::Show for Floats {
@ -163,79 +163,80 @@ impl fmt::Show for Floats {
impl Floats {
/// Creates a new `Floats` object.
pub fn new() -> Floats {
pub fn new(writing_mode: WritingMode) -> Floats {
Floats {
list: FloatListRef::new(),
offset: Point2D(Au(0), Au(0)),
offset: LogicalSize::zero(writing_mode),
writing_mode: writing_mode,
}
}
/// Adjusts the recorded offset of the flow relative to the first float.
pub fn translate(&mut self, delta: Point2D<Au>) {
pub fn translate(&mut self, delta: LogicalSize<Au>) {
self.offset = self.offset + delta
}
/// Returns the position of the last float in flow coordinates.
pub fn last_float_pos(&self) -> Option<Point2D<Au>> {
pub fn last_float_pos(&self) -> Option<LogicalPoint<Au>> {
match self.list.get() {
None => None,
Some(list) => {
match list.floats.last() {
None => None,
Some(float) => Some(float.bounds.origin + self.offset),
Some(float) => Some(float.bounds.start + self.offset),
}
}
}
}
/// Returns a rectangle that encloses the region from top to top + height, with width small
/// Returns a rectangle that encloses the region from block-start to block-start + block-size, with inline-size small
/// enough that it doesn't collide with any floats. max_x is the x-coordinate beyond which
/// floats have no effect. (Generally this is the containing block width.)
pub fn available_rect(&self, top: Au, height: Au, max_x: Au) -> Option<Rect<Au>> {
/// floats have no effect. (Generally this is the containing block inline-size.)
pub fn available_rect(&self, block_start: Au, block_size: Au, max_x: Au) -> Option<LogicalRect<Au>> {
let list = match self.list.get() {
None => return None,
Some(list) => list,
};
let top = top - self.offset.y;
let block_start = block_start - self.offset.block;
debug!("available_rect: trying to find space at {}", top);
debug!("available_rect: trying to find space at {}", block_start);
// Relevant dimensions for the right-most left float
let mut max_left = Au(0) - self.offset.x;
let mut l_top = None;
let mut l_bottom = None;
// Relevant dimensions for the left-most right float
let mut min_right = max_x - self.offset.x;
let mut r_top = None;
let mut r_bottom = None;
// Relevant dimensions for the inline-end-most inline-start float
let mut max_inline_start = Au(0) - self.offset.inline;
let mut l_block_start = None;
let mut l_block_end = None;
// Relevant dimensions for the inline-start-most inline-end float
let mut min_inline_end = max_x - self.offset.inline;
let mut r_block_start = None;
let mut r_block_end = None;
// Find the float collisions for the given vertical range.
for float in list.floats.iter() {
debug!("available_rect: Checking for collision against float");
let float_pos = float.bounds.origin;
let float_pos = float.bounds.start;
let float_size = float.bounds.size;
debug!("float_pos: {}, float_size: {}", float_pos, float_size);
match float.kind {
FloatLeft if float_pos.x + float_size.width > max_left &&
float_pos.y + float_size.height > top && float_pos.y < top + height => {
max_left = float_pos.x + float_size.width;
FloatLeft if float_pos.i + float_size.inline > max_inline_start &&
float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
max_inline_start = float_pos.i + float_size.inline;
l_top = Some(float_pos.y);
l_bottom = Some(float_pos.y + float_size.height);
l_block_start = Some(float_pos.b);
l_block_end = Some(float_pos.b + float_size.block);
debug!("available_rect: collision with left float: new max_left is {}",
max_left);
debug!("available_rect: collision with inline_start float: new max_inline_start is {}",
max_inline_start);
}
FloatRight if float_pos.x < min_right &&
float_pos.y + float_size.height > top && float_pos.y < top + height => {
min_right = float_pos.x;
FloatRight if float_pos.i < min_inline_end &&
float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
min_inline_end = float_pos.i;
r_top = Some(float_pos.y);
r_bottom = Some(float_pos.y + float_size.height);
debug!("available_rect: collision with right float: new min_right is {}",
min_right);
r_block_start = Some(float_pos.b);
r_block_end = Some(float_pos.b + float_size.block);
debug!("available_rect: collision with inline_end float: new min_inline_end is {}",
min_inline_end);
}
FloatLeft | FloatRight => {}
}
@ -243,29 +244,29 @@ impl Floats {
// Extend the vertical range of the rectangle to the closest floats.
// If there are floats on both sides, take the intersection of the
// two areas. Also make sure we never return a top smaller than the
// two areas. Also make sure we never return a block-start smaller than the
// given upper bound.
let (top, bottom) = match (r_top, r_bottom, l_top, l_bottom) {
(Some(r_top), Some(r_bottom), Some(l_top), Some(l_bottom)) =>
range_intersect(max(top, r_top), r_bottom, max(top, l_top), l_bottom),
let (block_start, block_end) = match (r_block_start, r_block_end, l_block_start, l_block_end) {
(Some(r_block_start), Some(r_block_end), Some(l_block_start), Some(l_block_end)) =>
range_intersect(max(block_start, r_block_start), r_block_end, max(block_start, l_block_start), l_block_end),
(None, None, Some(l_top), Some(l_bottom)) => (max(top, l_top), l_bottom),
(Some(r_top), Some(r_bottom), None, None) => (max(top, r_top), r_bottom),
(None, None, Some(l_block_start), Some(l_block_end)) => (max(block_start, l_block_start), l_block_end),
(Some(r_block_start), Some(r_block_end), None, None) => (max(block_start, r_block_start), r_block_end),
(None, None, None, None) => return None,
_ => fail!("Reached unreachable state when computing float area")
};
// FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to
// return negative widths since we check against that right away, but we should still
// return negative inline-sizes since we check against that inline-end away, but we should still
// undersrtand why they occur and add a stronger assertion here.
// assert!(max_left < min_right);
// assert!(max_inline-start < min_inline-end);
assert!(top <= bottom, "Float position error");
assert!(block_start <= block_end, "Float position error");
Some(Rect {
origin: Point2D(max_left, top) + self.offset,
size: Size2D(min_right - max_left, bottom - top)
})
Some(LogicalRect::new(
self.writing_mode, max_inline_start + self.offset.inline, block_start + self.offset.block,
min_inline_end - max_inline_start, block_end - block_start
))
}
/// Adds a new float to the list.
@ -275,8 +276,8 @@ impl Floats {
let list = self.list.get_mut();
new_info = PlacementInfo {
size: info.size,
ceiling: max(info.ceiling, list.max_top + self.offset.y),
max_width: info.max_width,
ceiling: max(info.ceiling, list.max_block_start + self.offset.block),
max_inline_size: info.max_inline_size,
kind: info.kind
}
}
@ -284,109 +285,131 @@ impl Floats {
debug!("add_float: added float with info {:?}", new_info);
let new_float = Float {
bounds: Rect {
origin: self.place_between_floats(&new_info).origin - self.offset,
size: info.size,
},
bounds: LogicalRect::from_point_size(
self.writing_mode,
self.place_between_floats(&new_info).start - self.offset,
info.size,
),
kind: info.kind
};
let list = self.list.get_mut();
list.floats.push(new_float);
list.max_top = max(list.max_top, new_float.bounds.origin.y);
list.max_block_start = max(list.max_block_start, new_float.bounds.start.b);
}
/// Given the top 3 sides of the rectangle, finds the largest height that will result in the
/// rectangle not colliding with any floats. Returns None if that height is infinite.
fn max_height_for_bounds(&self, left: Au, top: Au, width: Au) -> Option<Au> {
/// Given the block-start 3 sides of the rectangle, finds the largest block-size that will result in the
/// rectangle not colliding with any floats. Returns None if that block-size is infinite.
fn max_block_size_for_bounds(&self, inline_start: Au, block_start: Au, inline_size: Au) -> Option<Au> {
let list = match self.list.get() {
None => return None,
Some(list) => list,
};
let top = top - self.offset.y;
let left = left - self.offset.x;
let mut max_height = None;
let block_start = block_start - self.offset.block;
let inline_start = inline_start - self.offset.inline;
let mut max_block_size = None;
for float in list.floats.iter() {
if float.bounds.origin.y + float.bounds.size.height > top &&
float.bounds.origin.x + float.bounds.size.width > left &&
float.bounds.origin.x < left + width {
let new_y = float.bounds.origin.y;
max_height = Some(min(max_height.unwrap_or(new_y), new_y));
if float.bounds.start.b + float.bounds.size.block > block_start &&
float.bounds.start.i + float.bounds.size.inline > inline_start &&
float.bounds.start.i < inline_start + inline_size {
let new_y = float.bounds.start.b;
max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
}
}
max_height.map(|h| h + self.offset.y)
max_block_size.map(|h| h + self.offset.block)
}
/// Given placement information, finds the closest place a fragment can be positioned without
/// colliding with any floats.
pub fn place_between_floats(&self, info: &PlacementInfo) -> Rect<Au> {
debug!("place_between_floats: Placing object with width {} and height {}",
info.size.width,
info.size.height);
pub fn place_between_floats(&self, info: &PlacementInfo) -> LogicalRect<Au> {
debug!("place_between_floats: Placing object with {}", info.size);
// If no floats, use this fast path.
if !self.list.is_present() {
match info.kind {
FloatLeft => {
return Rect(Point2D(Au(0), info.ceiling),
Size2D(info.max_width, Au(i32::MAX)))
return LogicalRect::new(
self.writing_mode,
Au(0),
info.ceiling,
info.max_inline_size,
Au(i32::MAX))
}
FloatRight => {
return Rect(Point2D(info.max_width - info.size.width, info.ceiling),
Size2D(info.max_width, Au(i32::MAX)))
return LogicalRect::new(
self.writing_mode,
info.max_inline_size - info.size.inline,
info.ceiling,
info.max_inline_size,
Au(i32::MAX))
}
}
}
// Can't go any higher than previous floats or previous elements in the document.
let mut float_y = info.ceiling;
let mut float_b = info.ceiling;
loop {
let maybe_location = self.available_rect(float_y, info.size.height, info.max_width);
debug!("place_float: Got available rect: {:?} for y-pos: {}", maybe_location, float_y);
let maybe_location = self.available_rect(float_b, info.size.block, info.max_inline_size);
debug!("place_float: Got available rect: {:?} for y-pos: {}", maybe_location, float_b);
match maybe_location {
// If there are no floats blocking us, return the current location
// TODO(eatkinson): integrate with overflow
None => {
return match info.kind {
FloatLeft => {
Rect(Point2D(Au(0), float_y),
Size2D(info.max_width, Au(i32::MAX)))
LogicalRect::new(
self.writing_mode,
Au(0),
float_b,
info.max_inline_size,
Au(i32::MAX))
}
FloatRight => {
Rect(Point2D(info.max_width - info.size.width, float_y),
Size2D(info.max_width, Au(i32::MAX)))
LogicalRect::new(
self.writing_mode,
info.max_inline_size - info.size.inline,
float_b,
info.max_inline_size,
Au(i32::MAX))
}
}
}
Some(rect) => {
assert!(rect.origin.y + rect.size.height != float_y,
assert!(rect.start.b + rect.size.block != float_b,
"Non-terminating float placement");
// Place here if there is enough room
if rect.size.width >= info.size.width {
let height = self.max_height_for_bounds(rect.origin.x,
rect.origin.y,
rect.size.width);
let height = height.unwrap_or(Au(i32::MAX));
if rect.size.inline >= info.size.inline {
let block_size = self.max_block_size_for_bounds(rect.start.i,
rect.start.b,
rect.size.inline);
let block_size = block_size.unwrap_or(Au(i32::MAX));
return match info.kind {
FloatLeft => {
Rect(Point2D(rect.origin.x, float_y),
Size2D(rect.size.width, height))
LogicalRect::new(
self.writing_mode,
rect.start.i,
float_b,
rect.size.inline,
block_size)
}
FloatRight => {
Rect(Point2D(rect.origin.x + rect.size.width - info.size.width,
float_y),
Size2D(rect.size.width, height))
LogicalRect::new(
self.writing_mode,
rect.start.i + rect.size.inline - info.size.inline,
float_b,
rect.size.inline,
block_size)
}
}
}
// Try to place at the next-lowest location.
// Need to be careful of fencepost errors.
float_y = rect.origin.y + rect.size.height;
float_b = rect.start.b + rect.size.block;
}
}
}
@ -404,8 +427,8 @@ impl Floats {
(ClearLeft, FloatLeft) |
(ClearRight, FloatRight) |
(ClearBoth, _) => {
let y = self.offset.y + float.bounds.origin.y + float.bounds.size.height;
clearance = max(clearance, y);
let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
clearance = max(clearance, b);
}
_ => {}
}

View file

@ -34,7 +34,7 @@ use flow_ref::FlowRef;
use fragment::{Fragment, TableRowFragment, TableCellFragment};
use incremental::RestyleDamage;
use inline::InlineFlow;
use model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo};
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
use parallel::FlowParallelInfo;
use table_wrapper::TableWrapperFlow;
use table::TableFlow;
@ -46,17 +46,15 @@ use table_cell::TableCellFlow;
use wrapper::ThreadSafeLayoutNode;
use collections::dlist::DList;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::DisplayList;
use gfx::render_task::RenderLayer;
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au;
use servo_util::logical_geometry::WritingMode;
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use std::mem;
use std::fmt;
use std::iter::Zip;
use std::num::Zero;
use std::sync::atomics::{AtomicUint, Relaxed, SeqCst};
use std::slice::MutItems;
use style::computed_values::{clear, position, text_align};
@ -125,54 +123,54 @@ pub trait Flow: fmt::Show + ToStr + Share {
fail!("called as_table_cell() on a non-tablecell flow")
}
/// If this is a table row or table rowgroup or table flow, returns column widths.
/// If this is a table row or table rowgroup or table flow, returns column inline-sizes.
/// Fails otherwise.
fn col_widths<'a>(&'a mut self) -> &'a mut Vec<Au> {
fail!("called col_widths() on an other flow than table-row/table-rowgroup/table")
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
fail!("called col_inline_sizes() on an other flow than table-row/table-rowgroup/table")
}
/// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
/// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes.
/// Fails otherwise.
fn col_min_widths<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_min_widths() on an other flow than table-row/table-rowgroup/table")
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_min_inline_sizes() on an other flow than table-row/table-rowgroup/table")
}
/// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
/// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes.
/// Fails otherwise.
fn col_pref_widths<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_pref_widths() on an other flow than table-row/table-rowgroup/table")
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_pref_inline_sizes() on an other flow than table-row/table-rowgroup/table")
}
// Main methods
/// Pass 1 of reflow: computes minimum and preferred widths.
/// Pass 1 of reflow: computes minimum and preferred inline-sizes.
///
/// Recursively (bottom-up) determine the flow's minimum and preferred widths. When called on
/// this flow, all child flows have had their minimum and preferred widths set. This function
/// must decide minimum/preferred widths based on its children's widths and the dimensions of
/// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When called on
/// this flow, all child flows have had their minimum and preferred inline-sizes set. This function
/// must decide minimum/preferred inline-sizes based on its children's inline-sizes and the dimensions of
/// any boxes it is responsible for flowing.
fn bubble_widths(&mut self, _ctx: &mut LayoutContext) {
fail!("bubble_widths not yet implemented")
fn bubble_inline_sizes(&mut self, _ctx: &mut LayoutContext) {
fail!("bubble_inline_sizes not yet implemented")
}
/// Pass 2 of reflow: computes width.
fn assign_widths(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_widths not yet implemented")
/// Pass 2 of reflow: computes inline-size.
fn assign_inline_sizes(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_inline_sizes not yet implemented")
}
/// Pass 3a of reflow: computes height.
fn assign_height(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_height not yet implemented")
/// Pass 3a of reflow: computes block-size.
fn assign_block_size(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_block_size not yet implemented")
}
/// Assigns heights in-order; or, if this is a float, places the float. The default
/// implementation simply assigns heights if this flow is impacted by floats. Returns true if
/// Assigns block-sizes in-order; or, if this is a float, places the float. The default
/// implementation simply assigns block-sizes if this flow is impacted by floats. Returns true if
/// this child was impacted by floats or false otherwise.
fn assign_height_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext)
fn assign_block_size_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext)
-> bool {
let impacted = base(&*self).flags.impacted_by_floats();
if impacted {
self.assign_height(layout_context);
self.assign_block_size(layout_context);
}
impacted
}
@ -193,7 +191,7 @@ pub trait Flow: fmt::Show + ToStr + Share {
false
}
fn compute_collapsible_top_margin(&mut self,
fn compute_collapsible_block_start_margin(&mut self,
_layout_context: &mut LayoutContext,
_margin_collapse_info: &mut MarginCollapseInfo) {
// The default implementation is a no-op.
@ -256,7 +254,7 @@ pub trait Flow: fmt::Show + ToStr + Share {
/// Return the dimensions of the containing block generated by this flow for absolutely-
/// positioned descendants. For block flows, this is the padding box.
fn generated_containing_block_rect(&self) -> Rect<Au> {
fn generated_containing_block_rect(&self) -> LogicalRect<Au> {
fail!("generated_containing_block_position not yet implemented for this flow")
}
@ -443,11 +441,11 @@ bitfield!(FlowFlags, has_left_floated_descendants, set_has_left_floated_descenda
bitfield!(FlowFlags, has_right_floated_descendants, set_has_right_floated_descendants, 0b0000_0010)
// Whether this flow is impacted by floats to the left in the same block formatting context (i.e.
// its height depends on some prior flows with `float: left`).
// its block-size depends on some prior flows with `float: left`).
bitfield!(FlowFlags, impacted_by_left_floats, set_impacted_by_left_floats, 0b0000_0100)
// Whether this flow is impacted by floats to the right in the same block formatting context (i.e.
// its height depends on some prior flows with `float: right`).
// its block-size depends on some prior flows with `float: right`).
bitfield!(FlowFlags, impacted_by_right_floats, set_impacted_by_right_floats, 0b0000_1000)
/// The bitmask of flags that represent the text alignment field.
@ -526,14 +524,14 @@ pub struct Descendants {
descendant_links: Vec<FlowRef>,
/// Static y offsets of all descendants from the start of this flow box.
pub static_y_offsets: Vec<Au>,
pub static_b_offsets: Vec<Au>,
}
impl Descendants {
pub fn new() -> Descendants {
Descendants {
descendant_links: Vec::new(),
static_y_offsets: Vec::new(),
static_b_offsets: Vec::new(),
}
}
@ -566,7 +564,7 @@ impl Descendants {
let descendant_iter = DescendantIter {
iter: self.descendant_links.mut_slice_from(0).mut_iter(),
};
descendant_iter.zip(self.static_y_offsets.mut_slice_from(0).mut_iter())
descendant_iter.zip(self.static_b_offsets.mut_slice_from(0).mut_iter())
}
}
@ -596,9 +594,9 @@ pub type DescendantOffsetIter<'a> = Zip<DescendantIter<'a>, MutItems<'a, Au>>;
/// confused with absolutely-positioned flows).
pub struct AbsolutePositionInfo {
/// The size of the containing block for relatively-positioned descendants.
pub relative_containing_block_size: Size2D<Au>,
pub relative_containing_block_size: LogicalSize<Au>,
/// The position of the absolute containing block.
pub absolute_containing_block_position: Point2D<Au>,
pub absolute_containing_block_position: LogicalPoint<Au>,
/// Whether the absolute containing block forces positioned descendants to be layerized.
///
/// FIXME(pcwalton): Move into `FlowFlags`.
@ -606,12 +604,12 @@ pub struct AbsolutePositionInfo {
}
impl AbsolutePositionInfo {
pub fn new() -> AbsolutePositionInfo {
// FIXME(pcwalton): The initial relative containing block size should be equal to the size
pub fn new(writing_mode: WritingMode) -> AbsolutePositionInfo {
// FIXME(pcwalton): The initial relative containing block-size should be equal to the size
// of the root layer.
AbsolutePositionInfo {
relative_containing_block_size: Size2D::zero(),
absolute_containing_block_position: Zero::zero(),
relative_containing_block_size: LogicalSize::zero(writing_mode),
absolute_containing_block_position: LogicalPoint::zero(writing_mode),
layers_needed_for_positioned_flows: false,
}
}
@ -634,7 +632,7 @@ pub struct BaseFlow {
/* layout computations */
// TODO: min/pref and position are used during disjoint phases of
// layout; maybe combine into a single enum to save space.
pub intrinsic_widths: IntrinsicWidths,
pub intrinsic_inline_sizes: IntrinsicISizes,
/// The upper left corner of the box representing this flow, relative to the box representing
/// its parent flow.
@ -644,11 +642,11 @@ pub struct BaseFlow {
/// This does not include margins in the block flow direction, because those can collapse. So
/// for the block direction (usually vertical), this represents the *border box*. For the
/// inline direction (usually horizontal), this represents the *margin box*.
pub position: Rect<Au>,
pub position: LogicalRect<Au>,
/// The amount of overflow of this flow, relative to the containing block. Must include all the
/// pixels of all the display list items for correct invalidation.
pub overflow: Rect<Au>,
pub overflow: LogicalRect<Au>,
/// Data used during parallel traversals.
///
@ -662,7 +660,7 @@ pub struct BaseFlow {
pub collapsible_margins: CollapsibleMargins,
/// The position of this flow in page coordinates, computed during display list construction.
pub abs_position: Point2D<Au>,
pub abs_position: LogicalPoint<Au>,
/// Details about descendants with position 'absolute' or 'fixed' for which we are the
/// containing block. This is in tree order. This includes any direct children.
@ -670,10 +668,10 @@ pub struct BaseFlow {
/// Offset wrt the nearest positioned ancestor - aka the Containing Block
/// for any absolutely positioned elements.
pub absolute_static_x_offset: Au,
pub absolute_static_i_offset: Au,
/// Offset wrt the Initial Containing Block.
pub fixed_static_x_offset: Au,
pub fixed_static_i_offset: Au,
/// Reference to the Containing Block, if this flow is absolutely positioned.
pub absolute_cb: ContainingBlockLink,
@ -681,7 +679,7 @@ pub struct BaseFlow {
/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
/// confused with absolutely-positioned flows).
///
/// FIXME(pcwalton): Merge with `absolute_static_x_offset` and `fixed_static_x_offset` above?
/// FIXME(pcwalton): Merge with `absolute_static_i_offset` and `fixed_static_i_offset` above?
pub absolute_position_info: AbsolutePositionInfo,
/// The unflattened display items for this flow.
@ -692,6 +690,8 @@ pub struct BaseFlow {
/// Various flags for flows, tightly packed to save space.
pub flags: FlowFlags,
pub writing_mode: WritingMode,
}
#[unsafe_destructor]
@ -706,6 +706,7 @@ impl Drop for BaseFlow {
impl BaseFlow {
#[inline]
pub fn new(node: ThreadSafeLayoutNode) -> BaseFlow {
let writing_mode = node.style().writing_mode;
BaseFlow {
ref_count: AtomicUint::new(1),
@ -715,24 +716,25 @@ impl BaseFlow {
next_sibling: None,
prev_sibling: None,
intrinsic_widths: IntrinsicWidths::new(),
position: Rect::zero(),
overflow: Rect::zero(),
intrinsic_inline_sizes: IntrinsicISizes::new(),
position: LogicalRect::zero(writing_mode),
overflow: LogicalRect::zero(writing_mode),
parallel: FlowParallelInfo::new(),
floats: Floats::new(),
floats: Floats::new(writing_mode),
collapsible_margins: CollapsibleMargins::new(),
abs_position: Point2D(Au::new(0), Au::new(0)),
abs_position: LogicalPoint::zero(writing_mode),
abs_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0),
fixed_static_x_offset: Au::new(0),
absolute_static_i_offset: Au::new(0),
fixed_static_i_offset: Au::new(0),
absolute_cb: ContainingBlockLink::new(),
display_list: DisplayList::new(),
layers: DList::new(),
absolute_position_info: AbsolutePositionInfo::new(),
absolute_position_info: AbsolutePositionInfo::new(writing_mode),
flags: FlowFlags::new(),
writing_mode: writing_mode,
}
}
@ -973,14 +975,14 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
continue;
}
let mut kid_overflow = base(kid).overflow;
kid_overflow = kid_overflow.translate(&my_position.origin);
kid_overflow = kid_overflow.translate(&my_position.start);
overflow = overflow.union(&kid_overflow)
}
// FIXME(#2004, pcwalton): This is wrong for `position: fixed`.
for descendant_link in mut_base(self).abs_descendants.iter() {
let mut kid_overflow = base(descendant_link).overflow;
kid_overflow = kid_overflow.translate(&my_position.origin);
kid_overflow = kid_overflow.translate(&my_position.start);
overflow = overflow.union(&kid_overflow)
}
}
@ -1076,7 +1078,7 @@ impl ContainingBlockLink {
}
#[inline]
pub fn generated_containing_block_rect(&mut self) -> Rect<Au> {
pub fn generated_containing_block_rect(&mut self) -> LogicalRect<Au> {
match self.link {
None => fail!("haven't done it"),
Some(ref mut link) => link.get_mut().generated_containing_block_rect(),

File diff suppressed because it is too large Load diff

View file

@ -11,12 +11,12 @@ bitflags! {
#[doc = "Currently unused; need to decide how this propagates."]
static Repaint = 0x01,
#[doc = "Recompute intrinsic widths (minimum and preferred)."]
#[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
#[doc = "Propagates down the flow tree because the computation is"]
#[doc = "bottom-up."]
static BubbleWidths = 0x02,
static BubbleISizes = 0x02,
#[doc = "Recompute actual widths and heights."]
#[doc = "Recompute actual inline_sizes and block_sizes."]
#[doc = "Propagates up the flow tree because the computation is"]
#[doc = "top-down."]
static Reflow = 0x04
@ -31,7 +31,7 @@ impl RestyleDamage {
/// Elements of self which should also get set on any child flows.
pub fn propagate_down(self) -> RestyleDamage {
self & BubbleWidths
self & BubbleISizes
}
}
@ -61,7 +61,7 @@ pub fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDama
get_border.border_top_color, get_border.border_right_color,
get_border.border_bottom_color, get_border.border_left_color ]);
add_if_not_equal!(old, new, damage, [ Repaint, BubbleWidths, Reflow ],
add_if_not_equal!(old, new, damage, [ Repaint, BubbleISizes, Reflow ],
[ get_border.border_top_width, get_border.border_right_width,
get_border.border_bottom_width, get_border.border_left_width,
get_margin.margin_top, get_margin.margin_right,

View file

@ -10,19 +10,20 @@ use floats::{FloatLeft, Floats, PlacementInfo};
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use flow;
use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
use model::IntrinsicWidths;
use model::IntrinsicISizes;
use model;
use text;
use wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf};
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use geom::Size2D;
use gfx::display_list::ContentLevel;
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
use gfx::text::glyph::CharIndex;
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::logical_geometry::{LogicalRect, LogicalMargin, LogicalSize};
use servo_util::range;
use servo_util::range::{EachIndex, Range, RangeIndex, IntRangeIndex};
use std::iter::Enumerate;
@ -57,8 +58,8 @@ use sync::Arc;
///
/// Line fragments also contain some metadata used during line breaking. The
/// green zone is the area that the line can expand to before it collides
/// with a float or a horizontal wall of the containing block. The top
/// left corner of the green zone is the same as that of the line, but
/// with a float or a horizontal wall of the containing block. The block-start
/// inline-start corner of the green zone is the same as that of the line, but
/// the green zone can be taller and wider than the line itself.
pub struct Line {
/// A range of line indices that describe line breaks.
@ -109,16 +110,16 @@ pub struct Line {
/// | v |
/// |< - origin.x ->+ - - - - - - - - +---------+---- |
/// | | | | ^ |
/// | | | <img> | size.height |
/// | | | <img> | size.block-size |
/// | I like truffles, | | v |
/// | + - - - - - - - - +---------+---- |
/// | | | |
/// | |<------ size.width ------->| |
/// | |<------ size.inline-size ------->| |
/// | |
/// | |
/// +-----------------------------------------------------------+
/// ~~~
pub bounds: Rect<Au>,
pub bounds: LogicalRect<Au>,
/// The green zone is the greatest extent from wich a line can extend to
/// before it collides with a float.
///
@ -140,7 +141,7 @@ pub struct Line {
/// ::: green zone
/// FFF float
/// ~~~
pub green_zone: Size2D<Au>
pub green_zone: LogicalSize<Au>
}
int_range_index! {
@ -263,22 +264,22 @@ struct LineBreaker {
pub work_list: RingBuf<Fragment>,
pub pending_line: Line,
pub lines: Vec<Line>,
pub cur_y: Au,
pub cur_b: Au, // Current position on the block direction
}
impl LineBreaker {
pub fn new(float_ctx: Floats) -> LineBreaker {
LineBreaker {
floats: float_ctx,
new_fragments: Vec::new(),
work_list: RingBuf::new(),
pending_line: Line {
range: Range::empty(),
bounds: Rect(Point2D(Au::new(0), Au::new(0)), Size2D(Au::new(0), Au::new(0))),
green_zone: Size2D(Au::new(0), Au::new(0))
bounds: LogicalRect::zero(float_ctx.writing_mode),
green_zone: LogicalSize::zero(float_ctx.writing_mode)
},
floats: float_ctx,
lines: Vec::new(),
cur_y: Au::new(0)
cur_b: Au::new(0)
}
}
@ -290,14 +291,15 @@ impl LineBreaker {
debug!("Resetting LineBreaker's state for flow.");
self.lines = Vec::new();
self.new_fragments = Vec::new();
self.cur_y = Au(0);
self.cur_b = Au(0);
self.reset_line();
}
fn reset_line(&mut self) {
self.pending_line.range.reset(num::zero(), num::zero());
self.pending_line.bounds = Rect(Point2D(Au::new(0), self.cur_y), Size2D(Au::new(0), Au::new(0)));
self.pending_line.green_zone = Size2D(Au::new(0), Au::new(0))
self.pending_line.bounds = LogicalRect::new(
self.floats.writing_mode, Au::new(0), self.cur_b, Au::new(0), Au::new(0));
self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode)
}
pub fn scan_for_lines(&mut self, flow: &mut InlineFlow) {
@ -340,7 +342,7 @@ impl LineBreaker {
}
if self.pending_line.range.length() > num::zero() {
debug!("LineBreaker: Partially full line {:u} left at end of scanning.",
debug!("LineBreaker: Partially full line {:u} inline_start at end of scanning.",
self.lines.len());
self.flush_current_line();
}
@ -358,45 +360,46 @@ impl LineBreaker {
// clear line and add line mapping
debug!("LineBreaker: Saving information for flushed line {:u}.", self.lines.len());
self.lines.push(self.pending_line);
self.cur_y = self.pending_line.bounds.origin.y + self.pending_line.bounds.size.height;
self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block;
self.reset_line();
}
// FIXME(eatkinson): this assumes that the tallest fragment in the line determines the line height
// FIXME(eatkinson): this assumes that the tallest fragment in the line determines the line block-size
// This might not be the case with some weird text fonts.
fn new_height_for_line(&self, new_fragment: &Fragment) -> Au {
let fragment_height = new_fragment.content_height();
if fragment_height > self.pending_line.bounds.size.height {
fragment_height
fn new_block_size_for_line(&self, new_fragment: &Fragment) -> Au {
let fragment_block_size = new_fragment.content_block_size();
if fragment_block_size > self.pending_line.bounds.size.block {
fragment_block_size
} else {
self.pending_line.bounds.size.height
self.pending_line.bounds.size.block
}
}
/// Computes the position of a line that has only the provided fragment. 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 fragment after splitting.
/// inline-size of the first fragment after splitting.
fn initial_line_placement(&self, first_fragment: &Fragment, ceiling: Au, flow: &InlineFlow)
-> (Rect<Au>, Au) {
-> (LogicalRect<Au>, Au) {
debug!("LineBreaker: Trying to place first fragment of line {}", self.lines.len());
let first_fragment_size = first_fragment.border_box.size;
let splittable = first_fragment.can_split();
debug!("LineBreaker: fragment size: {}, splittable: {}", first_fragment_size, splittable);
// Initally, pretend a splittable fragment has 0 width.
// We will move it later if it has nonzero width
// Initally, pretend a splittable fragment has 0 inline-size.
// We will move it later if it has nonzero inline-size
// and that causes problems.
let placement_width = if splittable {
let placement_inline_size = if splittable {
Au::new(0)
} else {
first_fragment_size.width
first_fragment_size.inline
};
let info = PlacementInfo {
size: Size2D(placement_width, first_fragment_size.height),
size: LogicalSize::new(
self.floats.writing_mode, placement_inline_size, first_fragment_size.block),
ceiling: ceiling,
max_width: flow.base.position.size.width,
max_inline_size: flow.base.position.size.inline,
kind: FloatLeft,
};
@ -407,24 +410,24 @@ impl LineBreaker {
info);
// Simple case: if the fragment fits, then we can stop here
if line_bounds.size.width > first_fragment_size.width {
if line_bounds.size.inline > first_fragment_size.inline {
debug!("LineBreaker: case=fragment fits");
return (line_bounds, first_fragment_size.width);
return (line_bounds, first_fragment_size.inline);
}
// If not, but we can't split the fragment, then we'll place
// the line here and it will overflow.
if !splittable {
debug!("LineBreaker: case=line doesn't fit, but is unsplittable");
return (line_bounds, first_fragment_size.width);
return (line_bounds, first_fragment_size.inline);
}
debug!("LineBreaker: used to call split_to_width here");
return (line_bounds, first_fragment_size.width);
debug!("LineBreaker: used to call split_to_inline_size here");
return (line_bounds, first_fragment_size.inline);
}
/// Performs float collision avoidance. This is called when adding a fragment is going to increase
/// the height, and because of that we will collide with some floats.
/// the block-size, and because of that we will collide with some floats.
///
/// We have two options here:
/// 1) Move the entire line so that it doesn't collide any more.
@ -440,23 +443,23 @@ impl LineBreaker {
fn avoid_floats(&mut self,
in_fragment: Fragment,
flow: &InlineFlow,
new_height: Au,
new_block_size: Au,
line_is_empty: bool)
-> bool {
debug!("LineBreaker: entering float collision avoider!");
// First predict where the next line is going to be.
let this_line_y = self.pending_line.bounds.origin.y;
let (next_line, first_fragment_width) = self.initial_line_placement(&in_fragment, this_line_y, flow);
let this_line_y = self.pending_line.bounds.start.b;
let (next_line, first_fragment_inline_size) = self.initial_line_placement(&in_fragment, this_line_y, flow);
let next_green_zone = next_line.size;
let new_width = self.pending_line.bounds.size.width + first_fragment_width;
let new_inline_size = self.pending_line.bounds.size.inline + first_fragment_inline_size;
// Now, see if everything can fit at the new location.
if next_green_zone.width >= new_width && next_green_zone.height >= new_height {
if next_green_zone.inline >= new_inline_size && next_green_zone.block >= new_block_size {
debug!("LineBreaker: case=adding fragment collides vertically with floats: moving line");
self.pending_line.bounds.origin = next_line.origin;
self.pending_line.bounds.start = next_line.start;
self.pending_line.green_zone = next_green_zone;
assert!(!line_is_empty, "Non-terminating line breaking");
@ -478,29 +481,31 @@ impl LineBreaker {
} else {
debug!("LineBreaker: Found a new-line character, so splitting theline.");
let (left, right, run) = in_fragment.find_split_info_by_new_line()
let (inline_start, inline_end, run) = in_fragment.find_split_info_by_new_line()
.expect("LineBreaker: This split case makes no sense!");
let writing_mode = self.floats.writing_mode;
// TODO(bjz): Remove fragment splitting
let split_fragment = |split: SplitInfo| {
let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
let specific = ScannedTextFragment(info);
let size = Size2D(split.width, in_fragment.border_box.size.height);
let size = LogicalSize::new(
writing_mode, split.inline_size, in_fragment.border_box.size.block);
in_fragment.transform(size, specific)
};
debug!("LineBreaker: Pushing the fragment to the left of the new-line character \
debug!("LineBreaker: Pushing the fragment to the inline_start of the new-line character \
to the line.");
let mut left = split_fragment(left);
left.new_line_pos = vec![];
self.push_fragment_to_line(left);
let mut inline_start = split_fragment(inline_start);
inline_start.new_line_pos = vec![];
self.push_fragment_to_line(inline_start);
for right in right.move_iter() {
debug!("LineBreaker: Deferring the fragment to the right of the new-line \
for inline_end in inline_end.move_iter() {
debug!("LineBreaker: Deferring the fragment to the inline_end of the new-line \
character to the line.");
let mut right = split_fragment(right);
right.new_line_pos = in_fragment.new_line_pos.clone();
self.work_list.push_front(right);
let mut inline_end = split_fragment(inline_end);
inline_end.new_line_pos = in_fragment.new_line_pos.clone();
self.work_list.push_front(inline_end);
}
false
}
@ -511,8 +516,8 @@ impl LineBreaker {
fn try_append_to_line(&mut self, in_fragment: Fragment, flow: &InlineFlow) -> bool {
let line_is_empty = self.pending_line.range.length() == num::zero();
if line_is_empty {
let (line_bounds, _) = self.initial_line_placement(&in_fragment, self.cur_y, flow);
self.pending_line.bounds.origin = line_bounds.origin;
let (line_bounds, _) = self.initial_line_placement(&in_fragment, self.cur_b, flow);
self.pending_line.bounds.start = line_bounds.start;
self.pending_line.green_zone = line_bounds.size;
}
@ -525,22 +530,22 @@ impl LineBreaker {
let green_zone = self.pending_line.green_zone;
// NB: At this point, if `green_zone.width < self.pending_line.bounds.size.width` or
// `green_zone.height < self.pending_line.bounds.size.height`, then we committed a line
// NB: At this point, if `green_zone.inline-size < self.pending_line.bounds.size.inline-size` or
// `green_zone.block-size < self.pending_line.bounds.size.block-size`, then we committed a line
// that overlaps with floats.
let new_height = self.new_height_for_line(&in_fragment);
if new_height > green_zone.height {
let new_block_size = self.new_block_size_for_line(&in_fragment);
if new_block_size > green_zone.block {
// Uh-oh. Float collision imminent. Enter the float collision avoider
return self.avoid_floats(in_fragment, flow, new_height, line_is_empty)
return self.avoid_floats(in_fragment, flow, new_block_size, line_is_empty)
}
// If we're not going to overflow the green zone vertically, we might still do so
// horizontally. We'll try to place the whole fragment on this line and break somewhere if it
// doesn't fit.
let new_width = self.pending_line.bounds.size.width + in_fragment.border_box.size.width;
if new_width <= green_zone.width {
let new_inline_size = self.pending_line.bounds.size.inline + in_fragment.border_box.size.inline;
if new_inline_size <= green_zone.inline {
debug!("LineBreaker: case=fragment fits without splitting");
self.push_fragment_to_line(in_fragment);
return true
@ -557,19 +562,20 @@ impl LineBreaker {
}
}
let available_width = green_zone.width - self.pending_line.bounds.size.width;
let split = in_fragment.find_split_info_for_width(CharIndex(0), available_width, line_is_empty);
match split.map(|(left, right, run)| {
let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline;
let split = in_fragment.find_split_info_for_inline_size(CharIndex(0), available_inline_size, line_is_empty);
match split.map(|(inline_start, inline_end, run)| {
// TODO(bjz): Remove fragment splitting
let split_fragment = |split: SplitInfo| {
let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
let specific = ScannedTextFragment(info);
let size = Size2D(split.width, in_fragment.border_box.size.height);
let size = LogicalSize::new(
self.floats.writing_mode, split.inline_size, in_fragment.border_box.size.block);
in_fragment.transform(size, specific)
};
(left.map(|x| { debug!("LineBreaker: Left split {}", x); split_fragment(x) }),
right.map(|x| { debug!("LineBreaker: Right split {}", x); split_fragment(x) }))
(inline_start.map(|x| { debug!("LineBreaker: Left split {}", x); split_fragment(x) }),
inline_end.map(|x| { debug!("LineBreaker: Right split {}", x); split_fragment(x) }))
}) {
None => {
debug!("LineBreaker: Tried to split unsplittable render fragment! Deferring to next \
@ -577,21 +583,21 @@ impl LineBreaker {
self.work_list.push_front(in_fragment);
false
},
Some((Some(left_fragment), Some(right_fragment))) => {
debug!("LineBreaker: Line break found! Pushing left fragment to line and deferring \
right fragment to next line.");
self.push_fragment_to_line(left_fragment);
self.work_list.push_front(right_fragment);
Some((Some(inline_start_fragment), Some(inline_end_fragment))) => {
debug!("LineBreaker: Line break found! Pushing inline_start fragment to line and deferring \
inline_end fragment to next line.");
self.push_fragment_to_line(inline_start_fragment);
self.work_list.push_front(inline_end_fragment);
true
},
Some((Some(left_fragment), None)) => {
debug!("LineBreaker: Pushing left fragment to line.");
self.push_fragment_to_line(left_fragment);
Some((Some(inline_start_fragment), None)) => {
debug!("LineBreaker: Pushing inline_start fragment to line.");
self.push_fragment_to_line(inline_start_fragment);
true
},
Some((None, Some(right_fragment))) => {
debug!("LineBreaker: Pushing right fragment to line.");
self.push_fragment_to_line(right_fragment);
Some((None, Some(inline_end_fragment))) => {
debug!("LineBreaker: Pushing inline_end fragment to line.");
self.push_fragment_to_line(inline_end_fragment);
true
},
Some((None, None)) => {
@ -619,10 +625,10 @@ impl LineBreaker {
fragment_index: FragmentIndex(1),
char_index: CharIndex(0) /* unused for now */ ,
});
self.pending_line.bounds.size.width = self.pending_line.bounds.size.width +
fragment.border_box.size.width;
self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height,
fragment.border_box.size.height);
self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline +
fragment.border_box.size.inline;
self.pending_line.bounds.size.block = Au::max(self.pending_line.bounds.size.block,
fragment.border_box.size.block);
self.new_fragments.push(fragment);
}
}
@ -901,11 +907,11 @@ pub struct InlineFlow {
/// lines.
pub lines: Vec<Line>,
/// The minimum height above the baseline for each line, as specified by the line height and
/// The minimum block-size above the baseline for each line, as specified by the line block-size and
/// font style.
pub minimum_height_above_baseline: Au,
pub minimum_block_size_above_baseline: Au,
/// The minimum depth below the baseline for each line, as specified by the line height and
/// The minimum depth below the baseline for each line, as specified by the line block-size and
/// font style.
pub minimum_depth_below_baseline: Au,
}
@ -916,14 +922,18 @@ impl InlineFlow {
base: BaseFlow::new(node),
fragments: fragments,
lines: Vec::new(),
minimum_height_above_baseline: Au(0),
minimum_block_size_above_baseline: Au(0),
minimum_depth_below_baseline: Au(0),
}
}
pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) {
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
if !abs_rect.intersects(&layout_context.dirty) {
let abs_rect = LogicalRect::from_point_size(
self.base.writing_mode, self.base.abs_position, self.base.position.size);
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
if !abs_rect.to_physical(self.base.writing_mode, container_size)
.intersects(&layout_context.dirty) {
return
}
@ -949,31 +959,31 @@ impl InlineFlow {
// For now, don't traverse the subtree rooted here.
}
/// Returns the distance from the baseline for the logical top left corner of this fragment,
/// Returns the distance from the baseline for the logical block-start inline-start corner of this fragment,
/// taking into account the value of the CSS `vertical-align` property. Negative values mean
/// "toward the logical top" and positive values mean "toward the logical bottom".
/// "toward the logical block-start" and positive values mean "toward the logical block-end".
///
/// The extra boolean is set if and only if `biggest_top` and/or `biggest_bottom` were updated.
/// That is, if the box has a `top` or `bottom` value, true is returned.
/// The extra boolean is set if and only if `biggest_block-start` and/or `biggest_block-end` were updated.
/// That is, if the box has a `block-start` or `block-end` value, true is returned.
fn distance_from_baseline(fragment: &Fragment,
ascent: Au,
parent_text_top: Au,
parent_text_bottom: Au,
height_above_baseline: &mut Au,
parent_text_block_start: Au,
parent_text_block_end: Au,
block_size_above_baseline: &mut Au,
depth_below_baseline: &mut Au,
largest_height_for_top_fragments: &mut Au,
largest_height_for_bottom_fragments: &mut Au)
largest_block_size_for_top_fragments: &mut Au,
largest_block_size_for_bottom_fragments: &mut Au)
-> (Au, bool) {
match fragment.vertical_align() {
vertical_align::baseline => (-ascent, false),
vertical_align::middle => {
// TODO: x-height value should be used from font info.
let xheight = Au(0);
let fragment_height = fragment.content_height();
let offset_top = -(xheight + fragment_height).scale_by(0.5);
*height_above_baseline = offset_top.scale_by(-1.0);
*depth_below_baseline = fragment_height - *height_above_baseline;
(offset_top, false)
// TODO: x-block-size value should be used from font info.
let xblock_size = Au(0);
let fragment_block_size = fragment.content_block_size();
let offset_block_start = -(xblock_size + fragment_block_size).scale_by(0.5);
*block_size_above_baseline = offset_block_start.scale_by(-1.0);
*depth_below_baseline = fragment_block_size - *block_size_above_baseline;
(offset_block_start, false)
},
vertical_align::sub => {
// TODO: The proper position for subscripts should be used. Lower the baseline to
@ -988,30 +998,30 @@ impl InlineFlow {
(-super_offset - ascent, false)
},
vertical_align::text_top => {
let fragment_height = *height_above_baseline + *depth_below_baseline;
let fragment_block_size = *block_size_above_baseline + *depth_below_baseline;
let prev_depth_below_baseline = *depth_below_baseline;
*height_above_baseline = parent_text_top;
*depth_below_baseline = fragment_height - *height_above_baseline;
*block_size_above_baseline = parent_text_block_start;
*depth_below_baseline = fragment_block_size - *block_size_above_baseline;
(*depth_below_baseline - prev_depth_below_baseline - ascent, false)
},
vertical_align::text_bottom => {
let fragment_height = *height_above_baseline + *depth_below_baseline;
let fragment_block_size = *block_size_above_baseline + *depth_below_baseline;
let prev_depth_below_baseline = *depth_below_baseline;
*depth_below_baseline = parent_text_bottom;
*height_above_baseline = fragment_height - *depth_below_baseline;
*depth_below_baseline = parent_text_block_end;
*block_size_above_baseline = fragment_block_size - *depth_below_baseline;
(*depth_below_baseline - prev_depth_below_baseline - ascent, false)
},
vertical_align::top => {
*largest_height_for_top_fragments =
Au::max(*largest_height_for_top_fragments,
*height_above_baseline + *depth_below_baseline);
let offset_top = *height_above_baseline - ascent;
*largest_block_size_for_top_fragments =
Au::max(*largest_block_size_for_top_fragments,
*block_size_above_baseline + *depth_below_baseline);
let offset_top = *block_size_above_baseline - ascent;
(offset_top, true)
},
vertical_align::bottom => {
*largest_height_for_bottom_fragments =
Au::max(*largest_height_for_bottom_fragments,
*height_above_baseline + *depth_below_baseline);
*largest_block_size_for_bottom_fragments =
Au::max(*largest_block_size_for_bottom_fragments,
*block_size_above_baseline + *depth_below_baseline);
let offset_bottom = -(*depth_below_baseline + ascent);
(offset_bottom, true)
},
@ -1029,26 +1039,28 @@ impl InlineFlow {
fn set_horizontal_fragment_positions(fragments: &mut InlineFragments,
line: &Line,
line_align: text_align::T) {
// Figure out how much width we have.
let slack_width = Au::max(Au(0), line.green_zone.width - line.bounds.size.width);
// Figure out how much inline-size we have.
let slack_inline_size = Au::max(Au(0), line.green_zone.inline - line.bounds.size.inline);
// Set the fragment x positions based on that alignment.
let mut offset_x = line.bounds.origin.x;
let mut offset_x = line.bounds.start.i;
offset_x = offset_x + match line_align {
// So sorry, but justified text is more complicated than shuffling line
// coordinates.
//
// TODO(burg, issue #213): Implement `text-align: justify`.
text_align::left | text_align::justify => Au(0),
text_align::center => slack_width.scale_by(0.5),
text_align::right => slack_width,
text_align::center => slack_inline_size.scale_by(0.5),
text_align::right => slack_inline_size,
};
for i in each_fragment_index(&line.range) {
let fragment = fragments.get_mut(i.to_uint());
let size = fragment.border_box.size;
fragment.border_box = Rect(Point2D(offset_x, fragment.border_box.origin.y), size);
offset_x = offset_x + size.width;
fragment.border_box = LogicalRect::new(
fragment.style.writing_mode, offset_x, fragment.border_box.start.b,
size.inline, size.block);
offset_x = offset_x + size.inline;
}
}
@ -1063,7 +1075,7 @@ impl InlineFlow {
let font_metrics = text::font_metrics_for_style(font_context, &font_style);
let line_height = text::line_height_from_style(style, style.get_font().font_size);
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
(inline_metrics.height_above_baseline, inline_metrics.depth_below_baseline)
(inline_metrics.block_size_above_baseline, inline_metrics.depth_below_baseline)
}
}
@ -1080,39 +1092,43 @@ impl Flow for InlineFlow {
self
}
fn bubble_widths(&mut self, _: &mut LayoutContext) {
fn bubble_inline_sizes(&mut self, _: &mut LayoutContext) {
let writing_mode = self.base.writing_mode;
for kid in self.base.child_iter() {
flow::mut_base(kid).floats = Floats::new();
flow::mut_base(kid).floats = Floats::new(writing_mode);
}
let mut intrinsic_widths = IntrinsicWidths::new();
let mut intrinsic_inline_sizes = IntrinsicISizes::new();
for (fragment, context) in self.fragments.mut_iter() {
debug!("Flow: measuring {}", *fragment);
let fragment_intrinsic_widths = fragment.intrinsic_widths(Some(context));
intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width,
fragment_intrinsic_widths.minimum_width);
intrinsic_widths.preferred_width = intrinsic_widths.preferred_width +
fragment_intrinsic_widths.preferred_width;
let fragment_intrinsic_inline_sizes =
fragment.intrinsic_inline_sizes(Some(context));
intrinsic_inline_sizes.minimum_inline_size = geometry::max(
intrinsic_inline_sizes.minimum_inline_size,
fragment_intrinsic_inline_sizes.minimum_inline_size);
intrinsic_inline_sizes.preferred_inline_size =
intrinsic_inline_sizes.preferred_inline_size +
fragment_intrinsic_inline_sizes.preferred_inline_size;
}
self.base.intrinsic_widths = intrinsic_widths;
self.base.intrinsic_inline_sizes = intrinsic_inline_sizes;
}
/// Recursively (top-down) determines the actual width of child contexts and fragments. When called
/// on this context, the context has had its width set by the parent context.
fn assign_widths(&mut self, _: &mut LayoutContext) {
// Initialize content fragment widths if they haven't been initialized already.
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
/// on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, _: &mut LayoutContext) {
// Initialize content fragment inline-sizes if they haven't been initialized already.
//
// TODO: Combine this with `LineBreaker`'s walk in the fragment list, or put this into `Fragment`.
debug!("InlineFlow::assign_widths: floats in: {:?}", self.base.floats);
debug!("InlineFlow::assign_inline_sizes: floats in: {:?}", self.base.floats);
{
let width = self.base.position.size.width;
let inline_size = self.base.position.size.inline;
let this = &mut *self;
for (fragment, context) in this.fragments.mut_iter() {
fragment.assign_replaced_width_if_necessary(width,
fragment.assign_replaced_inline_size_if_necessary(inline_size,
Some(context))
}
}
@ -1123,30 +1139,30 @@ impl Flow for InlineFlow {
// There are no child contexts, so stop here.
// TODO(Issue #225): once there are 'inline-block' elements, this won't be
// true. In that case, set the InlineBlockFragment's width to the
// shrink-to-fit width, perform inline flow, and set the block
// flow context's width as the assigned width of the
// true. In that case, set the InlineBlockFragment's inline-size to the
// shrink-to-fit inline-size, perform inline flow, and set the block
// flow context's inline-size as the assigned inline-size of the
// 'inline-block' fragment that created this flow before recursing.
}
/// Calculate and set the height of this flow. See CSS 2.1 § 10.6.1.
fn assign_height(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow");
/// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
fn assign_block_size(&mut self, _: &mut LayoutContext) {
debug!("assign_block_size_inline: assigning block_size for flow");
// Divide the fragments into lines.
//
// TODO(#226): Get the CSS `line-height` property from the containing block's style to
// determine minimum line height.
// TODO(#226): Get the CSS `line-block-size` property from the containing block's style to
// determine minimum line block-size.
//
// TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to
// determine its height for computing line height.
// TODO(#226): Get the CSS `line-block-size` property from each non-replaced inline element to
// determine its block-size for computing line block-size.
//
// TODO(pcwalton): Cache the line scanner?
debug!("assign_height_inline: floats in: {:?}", self.base.floats);
debug!("assign_block_size_inline: floats in: {:?}", self.base.floats);
// assign height for inline fragments
// assign block-size for inline fragments
for (fragment, _) in self.fragments.mut_iter() {
fragment.assign_replaced_height_if_necessary();
fragment.assign_replaced_block_size_if_necessary();
}
let scanner_floats = self.base.floats.clone();
@ -1157,29 +1173,29 @@ impl Flow for InlineFlow {
let text_align = self.base.flags.text_align();
// Now, go through each line and lay out the fragments inside.
let mut line_distance_from_flow_top = Au(0);
let mut line_distance_from_flow_block_start = Au(0);
for line in self.lines.mut_iter() {
// Lay out fragments horizontally.
InlineFlow::set_horizontal_fragment_positions(&mut self.fragments, line, text_align);
// Set the top y position of the current line.
// Set the block-start y position of the current line.
// `line_height_offset` is updated at the end of the previous loop.
line.bounds.origin.y = line_distance_from_flow_top;
line.bounds.start.b = line_distance_from_flow_block_start;
// Calculate the distance from the baseline to the top and bottom of the line.
let mut largest_height_above_baseline = self.minimum_height_above_baseline;
// Calculate the distance from the baseline to the block-start and block-end of the line.
let mut largest_block_size_above_baseline = self.minimum_block_size_above_baseline;
let mut largest_depth_below_baseline = self.minimum_depth_below_baseline;
// Calculate the largest height among fragments with 'top' and 'bottom' values
// Calculate the largest block-size among fragments with 'top' and 'bottom' values
// respectively.
let (mut largest_height_for_top_fragments, mut largest_height_for_bottom_fragments) =
let (mut largest_block_size_for_top_fragments, mut largest_block_size_for_bottom_fragments) =
(Au(0), Au(0));
for fragment_i in each_fragment_index(&line.range) {
let fragment = self.fragments.fragments.get_mut(fragment_i.to_uint());
let InlineMetrics {
height_above_baseline: mut height_above_baseline,
block_size_above_baseline: mut block_size_above_baseline,
depth_below_baseline: mut depth_below_baseline,
ascent
} = fragment.inline_metrics();
@ -1189,7 +1205,7 @@ impl Flow for InlineFlow {
// "Content area" is defined in CSS 2.1 § 10.6.1.
//
// TODO: We should extract em-box info from the font size of the parent and
// calculate the distances from the baseline to the top and the bottom of the
// calculate the distances from the baseline to the block-start and the block-end of the
// parent's content area.
// We should calculate the distance from baseline to the top of parent's content
@ -1203,10 +1219,10 @@ impl Flow for InlineFlow {
// content area. But for now we assume it's zero.
let parent_text_bottom = Au(0);
// Calculate the final height above the baseline for this fragment.
// Calculate the final block-size above the baseline for this fragment.
//
// The no-update flag decides whether `largest_height_for_top_fragments` and
// `largest_height_for_bottom_fragments` are to be updated or not. This will be set
// The no-update flag decides whether `largest_block-size_for_top_fragments` and
// `largest_block-size_for_bottom_fragments` are to be updated or not. This will be set
// if and only if the fragment has `vertical-align` set to `top` or `bottom`.
let (distance_from_baseline, no_update_flag) =
InlineFlow::distance_from_baseline(
@ -1214,77 +1230,78 @@ impl Flow for InlineFlow {
ascent,
parent_text_top,
parent_text_bottom,
&mut height_above_baseline,
&mut block_size_above_baseline,
&mut depth_below_baseline,
&mut largest_height_for_top_fragments,
&mut largest_height_for_bottom_fragments);
&mut largest_block_size_for_top_fragments,
&mut largest_block_size_for_bottom_fragments);
// Unless the current fragment has `vertical-align` set to `top` or `bottom`,
// `largest_height_above_baseline` and `largest_depth_below_baseline` are updated.
// `largest_block-size_above_baseline` and `largest_depth_below_baseline` are updated.
if !no_update_flag {
largest_height_above_baseline = Au::max(height_above_baseline,
largest_height_above_baseline);
largest_block_size_above_baseline = Au::max(block_size_above_baseline,
largest_block_size_above_baseline);
largest_depth_below_baseline = Au::max(depth_below_baseline,
largest_depth_below_baseline);
}
// Temporarily use `fragment.border_box.origin.y` to mean "the distance from the
// Temporarily use `fragment.border_box.start.b` to mean "the distance from the
// baseline". We will assign the real value later.
fragment.border_box.origin.y = distance_from_baseline
fragment.border_box.start.b = distance_from_baseline
}
// Calculate the distance from the baseline to the top of the largest fragment with a
// value for `bottom`. Then, if necessary, update `largest_height_above_baseline`.
largest_height_above_baseline =
Au::max(largest_height_above_baseline,
largest_height_for_bottom_fragments - largest_depth_below_baseline);
// value for `bottom`. Then, if necessary, update `largest_block-size_above_baseline`.
largest_block_size_above_baseline =
Au::max(largest_block_size_above_baseline,
largest_block_size_for_bottom_fragments - largest_depth_below_baseline);
// Calculate the distance from baseline to the bottom of the largest fragment with a value
// for `top`. Then, if necessary, update `largest_depth_below_baseline`.
largest_depth_below_baseline =
Au::max(largest_depth_below_baseline,
largest_height_for_top_fragments - largest_height_above_baseline);
largest_block_size_for_top_fragments - largest_block_size_above_baseline);
// Now, the distance from the logical top of the line to the baseline can be
// computed as `largest_height_above_baseline`.
let baseline_distance_from_top = largest_height_above_baseline;
// Now, the distance from the logical block-start of the line to the baseline can be
// computed as `largest_block-size_above_baseline`.
let baseline_distance_from_block_start = largest_block_size_above_baseline;
// Compute the final positions in the block direction of each fragment. Recall that
// `fragment.border_box.origin.y` was set to the distance from the baseline above.
// `fragment.border_box.start.b` was set to the distance from the baseline above.
for fragment_i in each_fragment_index(&line.range) {
let fragment = self.fragments.get_mut(fragment_i.to_uint());
match fragment.vertical_align() {
vertical_align::top => {
fragment.border_box.origin.y = fragment.border_box.origin.y +
line_distance_from_flow_top
fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_block_start
}
vertical_align::bottom => {
fragment.border_box.origin.y = fragment.border_box.origin.y +
line_distance_from_flow_top + baseline_distance_from_top +
fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_block_start + baseline_distance_from_block_start +
largest_depth_below_baseline
}
_ => {
fragment.border_box.origin.y = fragment.border_box.origin.y +
line_distance_from_flow_top + baseline_distance_from_top
fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_block_start + baseline_distance_from_block_start
}
}
}
// This is used to set the top y position of the next line in the next loop.
line.bounds.size.height = largest_height_above_baseline + largest_depth_below_baseline;
line_distance_from_flow_top = line_distance_from_flow_top + line.bounds.size.height;
// This is used to set the block-start y position of the next line in the next loop.
line.bounds.size.block = largest_block_size_above_baseline + largest_depth_below_baseline;
line_distance_from_flow_block_start = line_distance_from_flow_block_start + line.bounds.size.block;
} // End of `lines.each` loop.
self.base.position.size.height =
self.base.position.size.block =
if self.lines.len() > 0 {
self.lines.as_slice().last().get_ref().bounds.origin.y +
self.lines.as_slice().last().get_ref().bounds.size.height
self.lines.as_slice().last().get_ref().bounds.start.b +
self.lines.as_slice().last().get_ref().bounds.size.block
} else {
Au::new(0)
};
self.base.floats = scanner.floats();
self.base.floats.translate(Point2D(Au::new(0), -self.base.position.size.height));
self.base.floats.translate(LogicalSize::new(
self.base.writing_mode, Au::new(0), -self.base.position.size.block));
}
}
@ -1321,12 +1338,14 @@ impl InlineFragmentRange {
}
/// Returns the dimensions of the border in this fragment range.
pub fn border(&self) -> SideOffsets2D<Au> {
model::border_from_style(&*self.style)
#[inline]
pub fn border(&self) -> LogicalMargin<Au> {
self.style.logical_border_width()
}
/// Returns the dimensions of the padding in this fragment range.
pub fn padding(&self) -> SideOffsets2D<Au> {
#[inline]
pub fn padding(&self) -> LogicalMargin<Au> {
// FIXME(#2266, pcwalton): Is Au(0) right here for the containing block?
model::padding_from_style(&*self.style, Au(0))
}
@ -1400,21 +1419,21 @@ impl<'a> InlineFragmentContext<'a> {
}
}
/// Height above the baseline, depth below the baseline, and ascent for a fragment. See CSS 2.1 §
/// BSize above the baseline, depth below the baseline, and ascent for a fragment. See CSS 2.1 §
/// 10.8.1.
pub struct InlineMetrics {
pub height_above_baseline: Au,
pub block_size_above_baseline: Au,
pub depth_below_baseline: Au,
pub ascent: Au,
}
impl InlineMetrics {
/// Calculates inline metrics from font metrics and line height per CSS 2.1 § 10.8.1.
/// Calculates inline metrics from font metrics and line block-size per CSS 2.1 § 10.8.1.
#[inline]
pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
let leading = line_height - (font_metrics.ascent + font_metrics.descent);
InlineMetrics {
height_above_baseline: font_metrics.ascent + leading.scale_by(0.5),
block_size_above_baseline: font_metrics.ascent + leading.scale_by(0.5),
depth_below_baseline: font_metrics.descent + leading.scale_by(0.5),
ascent: font_metrics.ascent,
}

View file

@ -171,16 +171,16 @@ impl PreorderFlowTraversal for FlowTreeVerificationTraversal {
}
}
/// The bubble-widths traversal, the first part of layout computation. This computes preferred
/// and intrinsic widths and bubbles them up the tree.
pub struct BubbleWidthsTraversal<'a> {
/// The bubble-inline-sizes traversal, the first part of layout computation. This computes preferred
/// and intrinsic inline-sizes and bubbles them up the tree.
pub struct BubbleISizesTraversal<'a> {
pub layout_context: &'a mut LayoutContext,
}
impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> {
impl<'a> PostorderFlowTraversal for BubbleISizesTraversal<'a> {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
flow.bubble_widths(self.layout_context);
flow.bubble_inline_sizes(self.layout_context);
true
}
@ -188,35 +188,35 @@ impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> {
/*
#[inline]
fn should_prune(&mut self, flow: &mut Flow) -> bool {
flow::mut_base(flow).restyle_damage.lacks(BubbleWidths)
flow::mut_base(flow).restyle_damage.lacks(BubbleISizes)
}
*/
}
/// The assign-widths traversal. In Gecko this corresponds to `Reflow`.
pub struct AssignWidthsTraversal<'a> {
/// The assign-inline-sizes traversal. In Gecko this corresponds to `Reflow`.
pub struct AssignISizesTraversal<'a> {
pub layout_context: &'a mut LayoutContext,
}
impl<'a> PreorderFlowTraversal for AssignWidthsTraversal<'a> {
impl<'a> PreorderFlowTraversal for AssignISizesTraversal<'a> {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
flow.assign_widths(self.layout_context);
flow.assign_inline_sizes(self.layout_context);
true
}
}
/// The assign-heights-and-store-overflow traversal, the last (and most expensive) part of layout
/// computation. Determines the final heights for all layout objects, computes positions, and
/// The assign-block-sizes-and-store-overflow traversal, the last (and most expensive) part of layout
/// computation. Determines the final block-sizes for all layout objects, computes positions, and
/// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`.
pub struct AssignHeightsAndStoreOverflowTraversal<'a> {
pub struct AssignBSizesAndStoreOverflowTraversal<'a> {
pub layout_context: &'a mut LayoutContext,
}
impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflowTraversal<'a> {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
flow.assign_height(self.layout_context);
flow.assign_block_size(self.layout_context);
// Skip store-overflow for absolutely positioned flows. That will be
// done in a separate traversal.
if !flow.is_store_overflow_delayed() {
@ -482,8 +482,8 @@ impl LayoutTask {
fn solve_constraints(&mut self,
layout_root: &mut Flow,
layout_context: &mut LayoutContext) {
if layout_context.opts.bubble_widths_separately {
let mut traversal = BubbleWidthsTraversal {
if layout_context.opts.bubble_inline_sizes_separately {
let mut traversal = BubbleISizesTraversal {
layout_context: layout_context,
};
layout_root.traverse_postorder(&mut traversal);
@ -495,7 +495,7 @@ impl LayoutTask {
// NOTE: this currently computes borders, so any pruning should separate that operation
// out.
{
let mut traversal = AssignWidthsTraversal {
let mut traversal = AssignISizesTraversal {
layout_context: layout_context,
};
layout_root.traverse_preorder(&mut traversal);
@ -503,7 +503,7 @@ impl LayoutTask {
// FIXME(pcwalton): Prune this pass as well.
{
let mut traversal = AssignHeightsAndStoreOverflowTraversal {
let mut traversal = AssignBSizesAndStoreOverflowTraversal {
layout_context: layout_context,
};
layout_root.traverse_postorder(&mut traversal);
@ -518,8 +518,8 @@ impl LayoutTask {
fn solve_constraints_parallel(&mut self,
layout_root: &mut FlowRef,
layout_context: &mut LayoutContext) {
if layout_context.opts.bubble_widths_separately {
let mut traversal = BubbleWidthsTraversal {
if layout_context.opts.bubble_inline_sizes_separately {
let mut traversal = BubbleISizesTraversal {
layout_context: layout_context,
};
layout_root.get_mut().traverse_postorder(&mut traversal);
@ -656,8 +656,12 @@ impl LayoutTask {
// Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay {
let writing_mode = flow::base(layout_root.get()).writing_mode;
profile(time::LayoutDispListBuildCategory, self.time_profiler_chan.clone(), || {
layout_ctx.dirty = flow::base(layout_root.get()).position.clone();
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
layout_ctx.dirty = flow::base(layout_root.get()).position.to_physical(
writing_mode, container_size);
match self.parallel_traversal {
None => {
@ -703,7 +707,10 @@ impl LayoutTask {
}
}
let root_size = flow::base(layout_root.get()).position.size;
let root_size = {
let root_flow = flow::base(layout_root.get());
root_flow.position.size.to_physical(root_flow.writing_mode)
};
let root_size = Size2D(root_size.width.to_nearest_px() as uint,
root_size.height.to_nearest_px() as uint);
let render_layer = RenderLayer {

View file

@ -14,6 +14,7 @@ use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LP_Length, LP
use style::ComputedValues;
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::logical_geometry::LogicalMargin;
use std::fmt;
/// A collapsible margin. See CSS 2.1 § 8.3.1.
@ -58,12 +59,12 @@ impl AdjoiningMargins {
}
}
/// Represents the top and bottom margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
/// Represents the block-start and block-end margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
pub enum CollapsibleMargins {
/// Margins may not collapse with this flow.
NoCollapsibleMargins(Au, Au),
/// Both the top and bottom margins (specified here in that order) may collapse, but the
/// Both the block-start and block-end margins (specified here in that order) may collapse, but the
/// margins do not collapse through this flow.
MarginsCollapse(AdjoiningMargins, AdjoiningMargins),
@ -85,7 +86,7 @@ enum FinalMarginState {
pub struct MarginCollapseInfo {
pub state: MarginCollapseState,
pub top_margin: AdjoiningMargins,
pub block_start_margin: AdjoiningMargins,
pub margin_in: AdjoiningMargins,
}
@ -94,42 +95,42 @@ impl MarginCollapseInfo {
pub fn new() -> MarginCollapseInfo {
MarginCollapseInfo {
state: AccumulatingCollapsibleTopMargin,
top_margin: AdjoiningMargins::new(),
block_start_margin: AdjoiningMargins::new(),
margin_in: AdjoiningMargins::new(),
}
}
pub fn initialize_top_margin(&mut self,
pub fn initialize_block_start_margin(&mut self,
fragment: &Fragment,
can_collapse_top_margin_with_kids: bool) {
if !can_collapse_top_margin_with_kids {
can_collapse_block_start_margin_with_kids: bool) {
if !can_collapse_block_start_margin_with_kids {
self.state = AccumulatingMarginIn
}
self.top_margin = AdjoiningMargins::from_margin(fragment.margin.top)
self.block_start_margin = AdjoiningMargins::from_margin(fragment.margin.block_start)
}
pub fn finish_and_compute_collapsible_margins(mut self,
fragment: &Fragment,
can_collapse_bottom_margin_with_kids: bool)
can_collapse_block_end_margin_with_kids: bool)
-> (CollapsibleMargins, Au) {
let state = match self.state {
AccumulatingCollapsibleTopMargin => {
match fragment.style().get_box().height {
match fragment.style().content_block_size() {
LPA_Auto | LPA_Length(Au(0)) | LPA_Percentage(0.) => {
match fragment.style().get_box().min_height {
match fragment.style().min_block_size() {
LP_Length(Au(0)) | LP_Percentage(0.) => {
MarginsCollapseThroughFinalMarginState
},
_ => {
// If the fragment has non-zero min-height, margins may not
// If the fragment has non-zero min-block-size, margins may not
// collapse through it.
BottomMarginCollapsesFinalMarginState
}
}
},
_ => {
// If the fragment has an explicitly specified height, margins may not
// If the fragment has an explicitly specified block-size, margins may not
// collapse through it.
BottomMarginCollapsesFinalMarginState
}
@ -138,31 +139,31 @@ impl MarginCollapseInfo {
AccumulatingMarginIn => BottomMarginCollapsesFinalMarginState,
};
// Different logic is needed here depending on whether this flow can collapse its bottom
// Different logic is needed here depending on whether this flow can collapse its block-end
// margin with its children.
let bottom_margin = fragment.margin.bottom;
if !can_collapse_bottom_margin_with_kids {
let block_end_margin = fragment.margin.block_end;
if !can_collapse_block_end_margin_with_kids {
match state {
MarginsCollapseThroughFinalMarginState => {
let advance = self.top_margin.collapse();
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapse(self.top_margin, self.margin_in), advance)
let advance = self.block_start_margin.collapse();
self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapse(self.block_start_margin, self.margin_in), advance)
}
BottomMarginCollapsesFinalMarginState => {
let advance = self.margin_in.collapse();
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapse(self.top_margin, self.margin_in), advance)
self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapse(self.block_start_margin, self.margin_in), advance)
}
}
} else {
match state {
MarginsCollapseThroughFinalMarginState => {
self.top_margin.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapseThrough(self.top_margin), Au(0))
self.block_start_margin.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapseThrough(self.block_start_margin), Au(0))
}
BottomMarginCollapsesFinalMarginState => {
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapse(self.top_margin, self.margin_in), Au(0))
self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapse(self.block_start_margin, self.margin_in), Au(0))
}
}
}
@ -170,66 +171,66 @@ impl MarginCollapseInfo {
pub fn current_float_ceiling(&mut self) -> Au {
match self.state {
AccumulatingCollapsibleTopMargin => self.top_margin.collapse(),
AccumulatingCollapsibleTopMargin => self.block_start_margin.collapse(),
AccumulatingMarginIn => self.margin_in.collapse(),
}
}
/// Adds the child's potentially collapsible top margin to the current margin state and
/// Adds the child's potentially collapsible block-start margin to the current margin state and
/// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
/// that should be added to the Y offset during block layout.
pub fn advance_top_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
pub fn advance_block_start_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
match (self.state, *child_collapsible_margins) {
(AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(top, _)) => {
(AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(block_start, _)) => {
self.state = AccumulatingMarginIn;
top
block_start
}
(AccumulatingCollapsibleTopMargin, MarginsCollapse(top, _)) => {
self.top_margin.union(top);
(AccumulatingCollapsibleTopMargin, MarginsCollapse(block_start, _)) => {
self.block_start_margin.union(block_start);
self.state = AccumulatingMarginIn;
Au(0)
}
(AccumulatingMarginIn, NoCollapsibleMargins(top, _)) => {
(AccumulatingMarginIn, NoCollapsibleMargins(block_start, _)) => {
let previous_margin_value = self.margin_in.collapse();
self.margin_in = AdjoiningMargins::new();
previous_margin_value + top
previous_margin_value + block_start
}
(AccumulatingMarginIn, MarginsCollapse(top, _)) => {
self.margin_in.union(top);
(AccumulatingMarginIn, MarginsCollapse(block_start, _)) => {
self.margin_in.union(block_start);
let margin_value = self.margin_in.collapse();
self.margin_in = AdjoiningMargins::new();
margin_value
}
(_, MarginsCollapseThrough(_)) => {
// For now, we ignore this; this will be handled by `advance_bottom_margin` below.
// For now, we ignore this; this will be handled by `advance_block-end_margin` below.
Au(0)
}
}
}
/// Adds the child's potentially collapsible bottom margin to the current margin state and
/// Adds the child's potentially collapsible block-end margin to the current margin state and
/// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
/// that should be added to the Y offset during block layout.
pub fn advance_bottom_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
pub fn advance_block_end_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
match (self.state, *child_collapsible_margins) {
(AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(..)) |
(AccumulatingCollapsibleTopMargin, MarginsCollapse(..)) => {
// Can't happen because the state will have been replaced with
// `AccumulatingMarginIn` above.
fail!("should not be accumulating collapsible top margins anymore!")
fail!("should not be accumulating collapsible block_start margins anymore!")
}
(AccumulatingCollapsibleTopMargin, MarginsCollapseThrough(margin)) => {
self.top_margin.union(margin);
self.block_start_margin.union(margin);
Au(0)
}
(AccumulatingMarginIn, NoCollapsibleMargins(_, bottom)) => {
(AccumulatingMarginIn, NoCollapsibleMargins(_, block_end)) => {
assert_eq!(self.margin_in.most_positive, Au(0));
assert_eq!(self.margin_in.most_negative, Au(0));
bottom
block_end
}
(AccumulatingMarginIn, MarginsCollapse(_, bottom)) |
(AccumulatingMarginIn, MarginsCollapseThrough(bottom)) => {
self.margin_in.union(bottom);
(AccumulatingMarginIn, MarginsCollapse(_, block_end)) |
(AccumulatingMarginIn, MarginsCollapseThrough(block_end)) => {
self.margin_in.union(block_end);
Au(0)
}
}
@ -241,38 +242,38 @@ pub enum MarginCollapseState {
AccumulatingMarginIn,
}
/// Intrinsic widths, which consist of minimum and preferred.
pub struct IntrinsicWidths {
/// The *minimum width* of the content.
pub minimum_width: Au,
/// The *preferred width* of the content.
pub preferred_width: Au,
/// Intrinsic inline-sizes, which consist of minimum and preferred.
pub struct IntrinsicISizes {
/// The *minimum inline-size* of the content.
pub minimum_inline_size: Au,
/// The *preferred inline-size* of the content.
pub preferred_inline_size: Au,
/// The estimated sum of borders, padding, and margins. Some calculations use this information
/// when computing intrinsic widths.
pub surround_width: Au,
/// when computing intrinsic inline-sizes.
pub surround_inline_size: Au,
}
impl fmt::Show for IntrinsicWidths {
impl fmt::Show for IntrinsicISizes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "min={}, pref={}, surr={}", self.minimum_width, self.preferred_width, self.surround_width)
write!(f, "min={}, pref={}, surr={}", self.minimum_inline_size, self.preferred_inline_size, self.surround_inline_size)
}
}
impl IntrinsicWidths {
pub fn new() -> IntrinsicWidths {
IntrinsicWidths {
minimum_width: Au(0),
preferred_width: Au(0),
surround_width: Au(0),
impl IntrinsicISizes {
pub fn new() -> IntrinsicISizes {
IntrinsicISizes {
minimum_inline_size: Au(0),
preferred_inline_size: Au(0),
surround_inline_size: Au(0),
}
}
pub fn total_minimum_width(&self) -> Au {
self.minimum_width + self.surround_width
pub fn total_minimum_inline_size(&self) -> Au {
self.minimum_inline_size + self.surround_inline_size
}
pub fn total_preferred_width(&self) -> Au {
self.preferred_width + self.surround_width
pub fn total_preferred_inline_size(&self) -> Au {
self.preferred_inline_size + self.surround_inline_size
}
}
@ -323,21 +324,13 @@ pub fn specified(length: computed::LengthOrPercentage, containing_length: Au) ->
}
#[inline]
pub fn border_from_style(style: &ComputedValues) -> SideOffsets2D<Au> {
let border_style = style.get_border();
SideOffsets2D::new(border_style.border_top_width,
border_style.border_right_width,
border_style.border_bottom_width,
border_style.border_left_width)
}
#[inline]
pub fn padding_from_style(style: &ComputedValues, containing_block_width: Au)
-> SideOffsets2D<Au> {
pub fn padding_from_style(style: &ComputedValues, containing_block_inline_size: Au)
-> LogicalMargin<Au> {
let padding_style = style.get_padding();
SideOffsets2D::new(specified(padding_style.padding_top, containing_block_width),
specified(padding_style.padding_right, containing_block_width),
specified(padding_style.padding_bottom, containing_block_width),
specified(padding_style.padding_left, containing_block_width))
LogicalMargin::from_physical(style.writing_mode, SideOffsets2D::new(
specified(padding_style.padding_top, containing_block_inline_size),
specified(padding_style.padding_right, containing_block_inline_size),
specified(padding_style.padding_bottom, containing_block_inline_size),
specified(padding_style.padding_left, containing_block_inline_size)))
}

View file

@ -13,8 +13,8 @@ use extra::LayoutAuxMethods;
use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
use flow;
use flow_ref::FlowRef;
use layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal};
use layout_task::{BubbleWidthsTraversal};
use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal};
use layout_task::{BubbleISizesTraversal};
use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods};
use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal};
use wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode};
@ -191,27 +191,27 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
}
// If there were no more children, start assigning heights.
// If there were no more children, start assigning block-sizes.
if !had_children {
bottom_up_func(unsafe_flow, proxy)
}
}
}
impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'a> {}
impl<'a> ParallelPostorderFlowTraversal for BubbleISizesTraversal<'a> {}
impl<'a> ParallelPreorderFlowTraversal for AssignWidthsTraversal<'a> {
impl<'a> ParallelPreorderFlowTraversal for AssignISizesTraversal<'a> {
fn run_parallel(&mut self,
unsafe_flow: UnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
self.run_parallel_helper(unsafe_flow,
proxy,
assign_widths,
assign_heights_and_store_overflow)
assign_inline_sizes,
assign_block_sizes_and_store_overflow)
}
}
impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}
impl<'a> ParallelPostorderFlowTraversal for AssignBSizesAndStoreOverflowTraversal<'a> {}
fn recalc_style_for_node(unsafe_layout_node: UnsafeLayoutNode,
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeLayoutNode>) {
@ -371,22 +371,22 @@ fn construct_flows(mut unsafe_layout_node: UnsafeLayoutNode,
}
}
fn assign_widths(unsafe_flow: UnsafeFlow,
fn assign_inline_sizes(unsafe_flow: UnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
let layout_context = unsafe { &mut **proxy.user_data() };
let mut assign_widths_traversal = AssignWidthsTraversal {
let mut assign_inline_sizes_traversal = AssignISizesTraversal {
layout_context: layout_context,
};
assign_widths_traversal.run_parallel(unsafe_flow, proxy)
assign_inline_sizes_traversal.run_parallel(unsafe_flow, proxy)
}
fn assign_heights_and_store_overflow(unsafe_flow: UnsafeFlow,
fn assign_block_sizes_and_store_overflow(unsafe_flow: UnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
let layout_context = unsafe { &mut **proxy.user_data() };
let mut assign_heights_traversal = AssignHeightsAndStoreOverflowTraversal {
let mut assign_block_sizes_traversal = AssignBSizesAndStoreOverflowTraversal {
layout_context: layout_context,
};
assign_heights_traversal.run_parallel(unsafe_flow, proxy)
assign_block_sizes_traversal.run_parallel(unsafe_flow, proxy)
}
fn compute_absolute_position(unsafe_flow: UnsafeFlow,
@ -525,7 +525,7 @@ pub fn traverse_flow_tree_preorder(root: &mut FlowRef,
profile(time::LayoutParallelWarmupCategory, time_profiler_chan, || {
queue.push(WorkUnit {
fun: assign_widths,
fun: assign_inline_sizes,
data: mut_owned_flow_to_unsafe_flow(root),
})
});

View file

@ -6,8 +6,8 @@
#![deny(unsafe_block)]
use block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use block::{WidthConstraintInput, WidthConstraintSolution};
use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
use block::{ISizeConstraintInput, ISizeConstraintSolution};
use construct::FlowConstructor;
use context::LayoutContext;
use floats::FloatKind;
@ -27,14 +27,14 @@ use style::computed_values::table_layout;
pub struct TableFlow {
pub block_flow: BlockFlow,
/// Column widths
pub col_widths: Vec<Au>,
/// Column inline-sizes
pub col_inline_sizes: Vec<Au>,
/// Column min widths.
pub col_min_widths: Vec<Au>,
/// Column min inline-sizes.
pub col_min_inline_sizes: Vec<Au>,
/// Column pref widths.
pub col_pref_widths: Vec<Au>,
/// Column pref inline-sizes.
pub col_pref_inline_sizes: Vec<Au>,
/// Table-layout property
pub table_layout: TableLayout,
@ -53,9 +53,9 @@ impl TableFlow {
};
TableFlow {
block_flow: block_flow,
col_widths: vec!(),
col_min_widths: vec!(),
col_pref_widths: vec!(),
col_inline_sizes: vec!(),
col_min_inline_sizes: vec!(),
col_pref_inline_sizes: vec!(),
table_layout: table_layout
}
}
@ -72,9 +72,9 @@ impl TableFlow {
};
TableFlow {
block_flow: block_flow,
col_widths: vec!(),
col_min_widths: vec!(),
col_pref_widths: vec!(),
col_inline_sizes: vec!(),
col_min_inline_sizes: vec!(),
col_pref_inline_sizes: vec!(),
table_layout: table_layout
}
}
@ -92,41 +92,41 @@ impl TableFlow {
};
TableFlow {
block_flow: block_flow,
col_widths: vec!(),
col_min_widths: vec!(),
col_pref_widths: vec!(),
col_inline_sizes: vec!(),
col_min_inline_sizes: vec!(),
col_pref_inline_sizes: vec!(),
table_layout: table_layout
}
}
/// Update the corresponding value of self_widths if a value of kid_widths has larger value
/// than one of self_widths.
pub fn update_col_widths(self_widths: &mut Vec<Au>, kid_widths: &Vec<Au>) -> Au {
let mut sum_widths = Au(0);
let mut kid_widths_it = kid_widths.iter();
for self_width in self_widths.mut_iter() {
match kid_widths_it.next() {
Some(kid_width) => {
if *self_width < *kid_width {
*self_width = *kid_width;
/// Update the corresponding value of self_inline-sizes if a value of kid_inline-sizes has larger value
/// than one of self_inline-sizes.
pub fn update_col_inline_sizes(self_inline_sizes: &mut Vec<Au>, kid_inline_sizes: &Vec<Au>) -> Au {
let mut sum_inline_sizes = Au(0);
let mut kid_inline_sizes_it = kid_inline_sizes.iter();
for self_inline_size in self_inline_sizes.mut_iter() {
match kid_inline_sizes_it.next() {
Some(kid_inline_size) => {
if *self_inline_size < *kid_inline_size {
*self_inline_size = *kid_inline_size;
}
},
None => {}
}
sum_widths = sum_widths + *self_width;
sum_inline_sizes = sum_inline_sizes + *self_inline_size;
}
sum_widths
sum_inline_sizes
}
/// Assign height for table flow.
/// Assign block-size for table flow.
///
/// TODO(#2014, pcwalton): This probably doesn't handle margin collapse right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse);
fn assign_block_size_table_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
}
pub fn build_display_list_table(&mut self, layout_context: &LayoutContext) {
@ -148,130 +148,130 @@ impl Flow for TableFlow {
&mut self.block_flow
}
fn col_widths<'a>(&'a mut self) -> &'a mut Vec<Au> {
&mut self.col_widths
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
&mut self.col_inline_sizes
}
fn col_min_widths<'a>(&'a self) -> &'a Vec<Au> {
&self.col_min_widths
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
&self.col_min_inline_sizes
}
fn col_pref_widths<'a>(&'a self) -> &'a Vec<Au> {
&self.col_pref_widths
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
&self.col_pref_inline_sizes
}
/// The specified column widths are set from column group and the first row for the fixed
/// The specified column inline-sizes are set from column group and the first row for the fixed
/// table layout calculation.
/// The maximum min/pref widths of each column are set from the rows for the automatic
/// The maximum min/pref inline-sizes of each column are set from the rows for the automatic
/// table layout calculation.
fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
fn bubble_inline_sizes(&mut self, _: &mut LayoutContext) {
let mut min_inline_size = Au(0);
let mut pref_inline_size = Au(0);
let mut did_first_row = false;
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_proper_table_child());
if kid.is_table_colgroup() {
self.col_widths.push_all(kid.as_table_colgroup().widths.as_slice());
self.col_min_widths = self.col_widths.clone();
self.col_pref_widths = self.col_widths.clone();
self.col_inline_sizes.push_all(kid.as_table_colgroup().inline_sizes.as_slice());
self.col_min_inline_sizes = self.col_inline_sizes.clone();
self.col_pref_inline_sizes = self.col_inline_sizes.clone();
} else if kid.is_table_rowgroup() || kid.is_table_row() {
// read column widths from table-row-group/table-row, and assign
// width=0 for the columns not defined in column-group
// FIXME: need to read widths from either table-header-group OR
// read column inline-sizes from table-row-group/table-row, and assign
// inline-size=0 for the columns not defined in column-group
// FIXME: need to read inline-sizes from either table-header-group OR
// first table-row
match self.table_layout {
FixedLayout => {
let kid_col_widths = kid.col_widths();
let kid_col_inline_sizes = kid.col_inline_sizes();
if !did_first_row {
did_first_row = true;
let mut child_widths = kid_col_widths.iter();
for col_width in self.col_widths.mut_iter() {
match child_widths.next() {
Some(child_width) => {
if *col_width == Au::new(0) {
*col_width = *child_width;
let mut child_inline_sizes = kid_col_inline_sizes.iter();
for col_inline_size in self.col_inline_sizes.mut_iter() {
match child_inline_sizes.next() {
Some(child_inline_size) => {
if *col_inline_size == Au::new(0) {
*col_inline_size = *child_inline_size;
}
},
None => break
}
}
}
let num_child_cols = kid_col_widths.len();
let num_cols = self.col_widths.len();
let num_child_cols = kid_col_inline_sizes.len();
let num_cols = self.col_inline_sizes.len();
debug!("table until the previous row has {} column(s) and this row has {} column(s)",
num_cols, num_child_cols);
for i in range(num_cols, num_child_cols) {
self.col_widths.push( *kid_col_widths.get(i) );
self.col_inline_sizes.push( *kid_col_inline_sizes.get(i) );
}
},
AutoLayout => {
min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
// update the number of column widths from table-rows.
let num_cols = self.col_min_widths.len();
let num_child_cols = kid.col_min_widths().len();
// update the number of column inline-sizes from table-rows.
let num_cols = self.col_min_inline_sizes.len();
let num_child_cols = kid.col_min_inline_sizes().len();
debug!("table until the previous row has {} column(s) and this row has {} column(s)",
num_cols, num_child_cols);
for i in range(num_cols, num_child_cols) {
self.col_widths.push(Au::new(0));
let new_kid_min = *kid.col_min_widths().get(i);
self.col_min_widths.push( new_kid_min );
let new_kid_pref = *kid.col_pref_widths().get(i);
self.col_pref_widths.push( new_kid_pref );
min_width = min_width + new_kid_min;
pref_width = pref_width + new_kid_pref;
self.col_inline_sizes.push(Au::new(0));
let new_kid_min = *kid.col_min_inline_sizes().get(i);
self.col_min_inline_sizes.push( new_kid_min );
let new_kid_pref = *kid.col_pref_inline_sizes().get(i);
self.col_pref_inline_sizes.push( new_kid_pref );
min_inline_size = min_inline_size + new_kid_min;
pref_inline_size = pref_inline_size + new_kid_pref;
}
}
}
}
}
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width =
geometry::max(min_width, pref_width);
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
geometry::max(min_inline_size, pref_inline_size);
}
/// Recursively (top-down) determines the actual width of child contexts and fragments. When
/// called on this context, the context has had its width set by the parent context.
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
debug!("assign_widths({}): assigning width for flow", "table");
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
/// called on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, ctx: &mut LayoutContext) {
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table");
// The position was set to the containing block by the flow's parent.
let containing_block_width = self.block_flow.base.position.size.width;
let containing_block_inline_size = self.block_flow.base.position.size.inline;
let mut num_unspecified_widths = 0;
let mut total_column_width = Au::new(0);
for col_width in self.col_widths.iter() {
if *col_width == Au::new(0) {
num_unspecified_widths += 1;
let mut num_unspecified_inline_sizes = 0;
let mut total_column_inline_size = Au::new(0);
for col_inline_size in self.col_inline_sizes.iter() {
if *col_inline_size == Au::new(0) {
num_unspecified_inline_sizes += 1;
} else {
total_column_width = total_column_width.add(col_width);
total_column_inline_size = total_column_inline_size.add(col_inline_size);
}
}
let width_computer = InternalTable;
width_computer.compute_used_width(&mut self.block_flow, ctx, containing_block_width);
let inline_size_computer = InternalTable;
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
let left_content_edge = self.block_flow.fragment.border_padding.left;
let padding_and_borders = self.block_flow.fragment.border_padding.horizontal();
let content_width = self.block_flow.fragment.border_box.size.width - padding_and_borders;
let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
match self.table_layout {
FixedLayout => {
// In fixed table layout, we distribute extra space among the unspecified columns if there are
// any, or among all the columns if all are specified.
if (total_column_width < content_width) && (num_unspecified_widths == 0) {
let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap();
for col_width in self.col_widths.mut_iter() {
*col_width = (*col_width).scale_by(ratio);
if (total_column_inline_size < content_inline_size) && (num_unspecified_inline_sizes == 0) {
let ratio = content_inline_size.to_f64().unwrap() / total_column_inline_size.to_f64().unwrap();
for col_inline_size in self.col_inline_sizes.mut_iter() {
*col_inline_size = (*col_inline_size).scale_by(ratio);
}
} else if num_unspecified_widths != 0 {
let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths);
for col_width in self.col_widths.mut_iter() {
if *col_width == Au(0) {
*col_width = extra_column_width;
} else if num_unspecified_inline_sizes != 0 {
let extra_column_inline_size = (content_inline_size - total_column_inline_size) / Au::new(num_unspecified_inline_sizes);
for col_inline_size in self.col_inline_sizes.mut_iter() {
if *col_inline_size == Au(0) {
*col_inline_size = extra_column_inline_size;
}
}
}
@ -279,12 +279,12 @@ impl Flow for TableFlow {
_ => {}
}
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, Some(self.col_inline_sizes.clone()));
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table");
self.assign_height_table_base(ctx);
fn assign_block_size(&mut self, ctx: &mut LayoutContext) {
debug!("assign_block_size: assigning block_size for table");
self.assign_block_size_table_base(ctx);
}
fn compute_absolute_position(&mut self) {
@ -300,25 +300,25 @@ impl fmt::Show for TableFlow {
}
/// Table, TableRowGroup, TableRow, TableCell types.
/// Their widths are calculated in the same way and do not have margins.
/// Their inline-sizes are calculated in the same way and do not have margins.
pub struct InternalTable;
impl WidthAndMarginsComputer for InternalTable {
/// Compute the used value of width, taking care of min-width and max-width.
impl ISizeAndMarginsComputer for InternalTable {
/// Compute the used value of inline-size, taking care of min-inline-size and max-inline-size.
///
/// CSS Section 10.4: Minimum and Maximum widths
fn compute_used_width(&self,
/// CSS Section 10.4: Minimum and Maximum inline-sizes
fn compute_used_inline_size(&self,
block: &mut BlockFlow,
ctx: &mut LayoutContext,
parent_flow_width: Au) {
let input = self.compute_width_constraint_inputs(block, parent_flow_width, ctx);
let solution = self.solve_width_constraints(block, &input);
self.set_width_constraint_solutions(block, solution);
parent_flow_inline_size: Au) {
let input = self.compute_inline_size_constraint_inputs(block, parent_flow_inline_size, ctx);
let solution = self.solve_inline_size_constraints(block, &input);
self.set_inline_size_constraint_solutions(block, solution);
}
/// Solve the width and margins constraints for this block flow.
fn solve_width_constraints(&self, _: &mut BlockFlow, input: &WidthConstraintInput)
-> WidthConstraintSolution {
WidthConstraintSolution::new(input.available_width, Au::new(0), Au::new(0))
/// Solve the inline-size and margins constraints for this block flow.
fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
-> ISizeConstraintSolution {
ISizeConstraintSolution::new(input.available_inline_size, Au::new(0), Au::new(0))
}
}

View file

@ -47,18 +47,18 @@ impl Flow for TableCaptionFlow {
&mut self.block_flow
}
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
self.block_flow.bubble_widths(ctx);
fn bubble_inline_sizes(&mut self, ctx: &mut LayoutContext) {
self.block_flow.bubble_inline_sizes(ctx);
}
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
debug!("assign_widths({}): assigning width for flow", "table_caption");
self.block_flow.assign_widths(ctx);
fn assign_inline_sizes(&mut self, ctx: &mut LayoutContext) {
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_caption");
self.block_flow.assign_inline_sizes(ctx);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_caption");
self.block_flow.assign_height(ctx);
fn assign_block_size(&mut self, ctx: &mut LayoutContext) {
debug!("assign_block_size: assigning block_size for table_caption");
self.block_flow.assign_block_size(ctx);
}
fn compute_absolute_position(&mut self) {

View file

@ -6,7 +6,7 @@
#![deny(unsafe_block)]
use block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
use context::LayoutContext;
use flow::{TableCellFlowClass, FlowClass, Flow};
use fragment::Fragment;
@ -38,15 +38,15 @@ impl TableCellFlow {
&mut self.block_flow.fragment
}
/// Assign height for table-cell flow.
/// Assign block-size for table-cell flow.
///
/// TODO(#2015, pcwalton): This doesn't handle floats right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
fn assign_height_table_cell_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse)
fn assign_block_size_table_cell_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse)
}
pub fn build_display_list_table_cell(&mut self, layout_context: &LayoutContext) {
@ -68,45 +68,45 @@ impl Flow for TableCellFlow {
&mut self.block_flow
}
/// Minimum/preferred widths set by this function are used in automatic table layout calculation.
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
self.block_flow.bubble_widths(ctx);
let specified_width = MaybeAuto::from_style(self.block_flow.fragment.style().get_box().width,
/// Minimum/preferred inline-sizes set by this function are used in automatic table layout calculation.
fn bubble_inline_sizes(&mut self, ctx: &mut LayoutContext) {
self.block_flow.bubble_inline_sizes(ctx);
let specified_inline_size = MaybeAuto::from_style(self.block_flow.fragment.style().content_inline_size(),
Au::new(0)).specified_or_zero();
if self.block_flow.base.intrinsic_widths.minimum_width < specified_width {
self.block_flow.base.intrinsic_widths.minimum_width = specified_width;
if self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size < specified_inline_size {
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = specified_inline_size;
}
if self.block_flow.base.intrinsic_widths.preferred_width <
self.block_flow.base.intrinsic_widths.minimum_width {
self.block_flow.base.intrinsic_widths.preferred_width =
self.block_flow.base.intrinsic_widths.minimum_width;
if self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size <
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size {
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size;
}
}
/// Recursively (top-down) determines the actual width of child contexts and fragments. When
/// called on this context, the context has had its width set by the parent table row.
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
debug!("assign_widths({}): assigning width for flow", "table_cell");
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
/// called on this context, the context has had its inline-size set by the parent table row.
fn assign_inline_sizes(&mut self, ctx: &mut LayoutContext) {
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_cell");
// The position was set to the column width by the parent flow, table row flow.
let containing_block_width = self.block_flow.base.position.size.width;
// The position was set to the column inline-size by the parent flow, table row flow.
let containing_block_inline_size = self.block_flow.base.position.size.inline;
let width_computer = InternalTable;
width_computer.compute_used_width(&mut self.block_flow, ctx, containing_block_width);
let inline_size_computer = InternalTable;
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
let left_content_edge = self.block_flow.fragment.border_box.origin.x +
self.block_flow.fragment.border_padding.left;
let padding_and_borders = self.block_flow.fragment.border_padding.horizontal();
let content_width = self.block_flow.fragment.border_box.size.width - padding_and_borders;
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
self.block_flow.fragment.border_padding.inline_start;
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
self.block_flow.propagate_assigned_width_to_children(left_content_edge,
content_width,
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge,
content_inline_size,
None);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_cell");
self.assign_height_table_cell_base(ctx);
fn assign_block_size(&mut self, ctx: &mut LayoutContext) {
debug!("assign_block_size: assigning block_size for table_cell");
self.assign_block_size_table_cell_base(ctx);
}
fn compute_absolute_position(&mut self) {

View file

@ -26,8 +26,8 @@ pub struct TableColGroupFlow {
/// The table column fragments
pub cols: Vec<Fragment>,
/// The specified widths of table columns
pub widths: Vec<Au>,
/// The specified inline-sizes of table columns
pub inline_sizes: Vec<Au>,
}
impl TableColGroupFlow {
@ -38,7 +38,7 @@ impl TableColGroupFlow {
base: BaseFlow::new((*node).clone()),
fragment: Some(fragment),
cols: fragments,
widths: vec!(),
inline_sizes: vec!(),
}
}
}
@ -52,10 +52,10 @@ impl Flow for TableColGroupFlow {
self
}
fn bubble_widths(&mut self, _: &mut LayoutContext) {
fn bubble_inline_sizes(&mut self, _: &mut LayoutContext) {
for fragment in self.cols.iter() {
// get the specified value from width property
let width = MaybeAuto::from_style(fragment.style().get_box().width,
// get the specified value from inline-size property
let inline_size = MaybeAuto::from_style(fragment.style().content_inline_size(),
Au::new(0)).specified_or_zero();
let span: int = match fragment.specific {
@ -63,18 +63,18 @@ impl Flow for TableColGroupFlow {
_ => fail!("Other fragment come out in TableColGroupFlow. {:?}", fragment.specific)
};
for _ in range(0, span) {
self.widths.push(width);
self.inline_sizes.push(inline_size);
}
}
}
/// Table column widths are assigned in table flow and propagated to table row or rowgroup flow.
/// Therefore, table colgroup flow does not need to assign its width.
fn assign_widths(&mut self, _ctx: &mut LayoutContext) {
/// Table column inline-sizes are assigned in table flow and propagated to table row or rowgroup flow.
/// Therefore, table colgroup flow does not need to assign its inline-size.
fn assign_inline_sizes(&mut self, _ctx: &mut LayoutContext) {
}
/// Table column do not have height.
fn assign_height(&mut self, _ctx: &mut LayoutContext) {
/// Table column do not have block-size.
fn assign_block_size(&mut self, _ctx: &mut LayoutContext) {
}
}

View file

@ -7,7 +7,7 @@
#![deny(unsafe_block)]
use block::BlockFlow;
use block::WidthAndMarginsComputer;
use block::ISizeAndMarginsComputer;
use construct::FlowConstructor;
use context::LayoutContext;
use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
@ -25,14 +25,14 @@ use std::fmt;
pub struct TableRowFlow {
pub block_flow: BlockFlow,
/// Column widths.
pub col_widths: Vec<Au>,
/// Column inline-sizes.
pub col_inline_sizes: Vec<Au>,
/// Column min widths.
pub col_min_widths: Vec<Au>,
/// Column min inline-sizes.
pub col_min_inline_sizes: Vec<Au>,
/// Column pref widths.
pub col_pref_widths: Vec<Au>,
/// Column pref inline-sizes.
pub col_pref_inline_sizes: Vec<Au>,
}
impl TableRowFlow {
@ -41,9 +41,9 @@ impl TableRowFlow {
-> TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
col_widths: vec!(),
col_min_widths: vec!(),
col_pref_widths: vec!(),
col_inline_sizes: vec!(),
col_min_inline_sizes: vec!(),
col_pref_inline_sizes: vec!(),
}
}
@ -52,9 +52,9 @@ impl TableRowFlow {
-> TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node(constructor, node),
col_widths: vec!(),
col_min_widths: vec!(),
col_pref_widths: vec!(),
col_inline_sizes: vec!(),
col_min_inline_sizes: vec!(),
col_pref_inline_sizes: vec!(),
}
}
@ -63,68 +63,68 @@ impl TableRowFlow {
}
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
// TODO: If border-collapse: collapse, top_offset, bottom_offset, and left_offset
// TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
// should be updated. Currently, they are set as Au(0).
(Au(0), Au(0), Au(0))
}
/// Assign height for table-row flow.
/// Assign block-size for table-row flow.
///
/// TODO(pcwalton): This doesn't handle floats and positioned elements right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext) {
let (top_offset, _, _) = self.initialize_offsets();
fn assign_block_size_table_row_base(&mut self, layout_context: &mut LayoutContext) {
let (block_start_offset, _, _) = self.initialize_offsets();
let /* mut */ cur_y = top_offset;
let /* mut */ cur_y = block_start_offset;
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells )
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `block-size`, minimum block-size of all cells )
let mut max_y = Au::new(0);
for kid in self.block_flow.base.child_iter() {
kid.assign_height_for_inorder_child_if_necessary(layout_context);
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
{
let child_fragment = kid.as_table_cell().fragment();
// TODO: Percentage height
let child_specified_height = MaybeAuto::from_style(child_fragment.style().get_box().height,
// TODO: Percentage block-size
let child_specified_block_size = MaybeAuto::from_style(child_fragment.style().content_block_size(),
Au::new(0)).specified_or_zero();
max_y =
geometry::max(max_y,
child_specified_height + child_fragment.border_padding.vertical());
child_specified_block_size + child_fragment.border_padding.block_start_end());
}
let child_node = flow::mut_base(kid);
child_node.position.origin.y = cur_y;
max_y = geometry::max(max_y, child_node.position.size.height);
child_node.position.start.b = cur_y;
max_y = geometry::max(max_y, child_node.position.size.block);
}
let mut height = max_y;
// TODO: Percentage height
height = match MaybeAuto::from_style(self.block_flow.fragment.style().get_box().height, Au(0)) {
Auto => height,
Specified(value) => geometry::max(value, height)
let mut block_size = max_y;
// TODO: Percentage block-size
block_size = match MaybeAuto::from_style(self.block_flow.fragment.style().content_block_size(), Au(0)) {
Auto => block_size,
Specified(value) => geometry::max(value, block_size)
};
// cur_y = cur_y + height;
// cur_y = cur_y + block-size;
// Assign the height of own fragment
// Assign the block-size of own fragment
//
// FIXME(pcwalton): Take `cur_y` into account.
let mut position = self.block_flow.fragment.border_box;
position.size.height = height;
position.size.block = block_size;
self.block_flow.fragment.border_box = position;
self.block_flow.base.position.size.height = height;
self.block_flow.base.position.size.block = block_size;
// Assign the height of kid fragments, which is the same value as own height.
// Assign the block-size of kid fragments, which is the same value as own block-size.
for kid in self.block_flow.base.child_iter() {
{
let kid_fragment = kid.as_table_cell().mut_fragment();
let mut position = kid_fragment.border_box;
position.size.height = height;
position.size.block = block_size;
kid_fragment.border_box = position;
}
let child_node = flow::mut_base(kid);
child_node.position.size.height = height;
child_node.position.size.block = block_size;
}
}
@ -147,70 +147,70 @@ impl Flow for TableRowFlow {
&mut self.block_flow
}
fn col_widths<'a>(&'a mut self) -> &'a mut Vec<Au> {
&mut self.col_widths
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
&mut self.col_inline_sizes
}
fn col_min_widths<'a>(&'a self) -> &'a Vec<Au> {
&self.col_min_widths
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
&self.col_min_inline_sizes
}
fn col_pref_widths<'a>(&'a self) -> &'a Vec<Au> {
&self.col_pref_widths
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
&self.col_pref_inline_sizes
}
/// Recursively (bottom-up) determines 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 fragments it is
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When called
/// on this context, all child contexts have had their min/pref inline-sizes set. This function must
/// decide min/pref inline-sizes based on child context inline-sizes and dimensions of any fragments it is
/// responsible for flowing.
/// Min/pref widths set by this function are used in automatic table layout calculation.
/// The specified column widths of children cells are used in fixed table layout calculation.
fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
/* find the specified widths from child table-cell contexts */
/// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
/// The specified column inline-sizes of children cells are used in fixed table layout calculation.
fn bubble_inline_sizes(&mut self, _: &mut LayoutContext) {
let mut min_inline_size = Au(0);
let mut pref_inline_size = Au(0);
/* find the specified inline_sizes from child table-cell contexts */
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_cell());
// collect the specified column widths of cells. These are used in fixed table layout calculation.
// collect the specified column inline-sizes of cells. These are used in fixed table layout calculation.
{
let child_fragment = kid.as_table_cell().fragment();
let child_specified_width = MaybeAuto::from_style(child_fragment.style().get_box().width,
let child_specified_inline_size = MaybeAuto::from_style(child_fragment.style().content_inline_size(),
Au::new(0)).specified_or_zero();
self.col_widths.push(child_specified_width);
self.col_inline_sizes.push(child_specified_inline_size);
}
// collect min_width & pref_width of children cells for automatic table layout calculation.
// collect min_inline-size & pref_inline-size of children cells for automatic table layout calculation.
let child_base = flow::mut_base(kid);
self.col_min_widths.push(child_base.intrinsic_widths.minimum_width);
self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width);
min_width = min_width + child_base.intrinsic_widths.minimum_width;
pref_width = pref_width + child_base.intrinsic_widths.preferred_width;
self.col_min_inline_sizes.push(child_base.intrinsic_inline_sizes.minimum_inline_size);
self.col_pref_inline_sizes.push(child_base.intrinsic_inline_sizes.preferred_inline_size);
min_inline_size = min_inline_size + child_base.intrinsic_inline_sizes.minimum_inline_size;
pref_inline_size = pref_inline_size + child_base.intrinsic_inline_sizes.preferred_inline_size;
}
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
pref_width);
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = geometry::max(min_inline_size,
pref_inline_size);
}
/// Recursively (top-down) determines the actual width of child contexts and fragments. When called
/// on this context, the context has had its width set by the parent context.
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
debug!("assign_widths({}): assigning width for flow", "table_row");
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
/// on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, ctx: &mut LayoutContext) {
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_row");
// The position was set to the containing block by the flow's parent.
let containing_block_width = self.block_flow.base.position.size.width;
// FIXME: In case of border-collapse: collapse, left_content_edge should be border-left
let left_content_edge = Au::new(0);
let containing_block_inline_size = self.block_flow.base.position.size.inline;
// FIXME: In case of border-collapse: collapse, inline-start_content_edge should be border-inline-start
let inline_start_content_edge = Au::new(0);
let width_computer = InternalTable;
width_computer.compute_used_width(&mut self.block_flow, ctx, containing_block_width);
let inline_size_computer = InternalTable;
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
self.block_flow.propagate_assigned_width_to_children(left_content_edge, Au(0), Some(self.col_widths.clone()));
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, Au(0), Some(self.col_inline_sizes.clone()));
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_row");
self.assign_height_table_row_base(ctx);
fn assign_block_size(&mut self, ctx: &mut LayoutContext) {
debug!("assign_block_size: assigning block_size for table_row");
self.assign_block_size_table_row_base(ctx);
}
fn compute_absolute_position(&mut self) {

View file

@ -7,7 +7,7 @@
#![deny(unsafe_block)]
use block::BlockFlow;
use block::WidthAndMarginsComputer;
use block::ISizeAndMarginsComputer;
use construct::FlowConstructor;
use context::LayoutContext;
use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
@ -24,14 +24,14 @@ use std::fmt;
pub struct TableRowGroupFlow {
pub block_flow: BlockFlow,
/// Column widths
pub col_widths: Vec<Au>,
/// Column inline-sizes
pub col_inline_sizes: Vec<Au>,
/// Column min widths.
pub col_min_widths: Vec<Au>,
/// Column min inline-sizes.
pub col_min_inline_sizes: Vec<Au>,
/// Column pref widths.
pub col_pref_widths: Vec<Au>,
/// Column pref inline-sizes.
pub col_pref_inline_sizes: Vec<Au>,
}
impl TableRowGroupFlow {
@ -40,9 +40,9 @@ impl TableRowGroupFlow {
-> TableRowGroupFlow {
TableRowGroupFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
col_widths: vec!(),
col_min_widths: vec!(),
col_pref_widths: vec!(),
col_inline_sizes: vec!(),
col_min_inline_sizes: vec!(),
col_pref_inline_sizes: vec!(),
}
}
@ -51,9 +51,9 @@ impl TableRowGroupFlow {
-> TableRowGroupFlow {
TableRowGroupFlow {
block_flow: BlockFlow::from_node(constructor, node),
col_widths: vec!(),
col_min_widths: vec!(),
col_pref_widths: vec!(),
col_inline_sizes: vec!(),
col_min_inline_sizes: vec!(),
col_pref_inline_sizes: vec!(),
}
}
@ -62,37 +62,37 @@ impl TableRowGroupFlow {
}
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
// TODO: If border-collapse: collapse, top_offset, bottom_offset, and left_offset
// TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
// should be updated. Currently, they are set as Au(0).
(Au(0), Au(0), Au(0))
}
/// Assign height for table-rowgroup flow.
/// Assign block-size for table-rowgroup flow.
///
/// FIXME(pcwalton): This doesn't handle floats right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
fn assign_height_table_rowgroup_base(&mut self, layout_context: &mut LayoutContext) {
let (top_offset, _, _) = self.initialize_offsets();
fn assign_block_size_table_rowgroup_base(&mut self, layout_context: &mut LayoutContext) {
let (block_start_offset, _, _) = self.initialize_offsets();
let mut cur_y = top_offset;
let mut cur_y = block_start_offset;
for kid in self.block_flow.base.child_iter() {
kid.assign_height_for_inorder_child_if_necessary(layout_context);
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
let child_node = flow::mut_base(kid);
child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height;
child_node.position.start.b = cur_y;
cur_y = cur_y + child_node.position.size.block;
}
let height = cur_y - top_offset;
let block_size = cur_y - block_start_offset;
let mut position = self.block_flow.fragment.border_box;
position.size.height = height;
position.size.block = block_size;
self.block_flow.fragment.border_box = position;
self.block_flow.base.position.size.height = height;
self.block_flow.base.position.size.block = block_size;
}
pub fn build_display_list_table_rowgroup(&mut self, layout_context: &LayoutContext) {
@ -114,85 +114,86 @@ impl Flow for TableRowGroupFlow {
&mut self.block_flow
}
fn col_widths<'a>(&'a mut self) -> &'a mut Vec<Au> {
&mut self.col_widths
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
&mut self.col_inline_sizes
}
fn col_min_widths<'a>(&'a self) -> &'a Vec<Au> {
&self.col_min_widths
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
&self.col_min_inline_sizes
}
fn col_pref_widths<'a>(&'a self) -> &'a Vec<Au> {
&self.col_pref_widths
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
&self.col_pref_inline_sizes
}
/// Recursively (bottom-up) determines 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 fragments it is
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When called
/// on this context, all child contexts have had their min/pref inline-sizes set. This function must
/// decide min/pref inline-sizes based on child context inline-sizes and dimensions of any fragments it is
/// responsible for flowing.
/// Min/pref widths set by this function are used in automatic table layout calculation.
/// Also, this function finds the specified column widths from the first row.
/// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
/// Also, this function finds the specified column inline-sizes from the first row.
/// Those are used in fixed table layout calculation
fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
fn bubble_inline_sizes(&mut self, _: &mut LayoutContext) {
let mut min_inline_size = Au(0);
let mut pref_inline_size = Au(0);
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_row());
// calculate min_width & pref_width for automatic table layout calculation
// 'self.col_min_widths' collects the maximum value of cells' min-widths for each column.
// 'self.col_pref_widths' collects the maximum value of cells' pref-widths for each column.
if self.col_widths.is_empty() { // First Row
assert!(self.col_min_widths.is_empty() && self.col_pref_widths.is_empty());
// 'self.col_widths' collects the specified column widths from the first table-row for fixed table layout calculation.
self.col_widths = kid.col_widths().clone();
self.col_min_widths = kid.col_min_widths().clone();
self.col_pref_widths = kid.col_pref_widths().clone();
// calculate min_inline-size & pref_inline-size for automatic table layout calculation
// 'self.col_min_inline-sizes' collects the maximum value of cells' min-inline-sizes for each column.
// 'self.col_pref_inline-sizes' collects the maximum value of cells' pref-inline-sizes for each column.
if self.col_inline_sizes.is_empty() { // First Row
assert!(self.col_min_inline_sizes.is_empty() && self.col_pref_inline_sizes.is_empty());
// 'self.col_inline-sizes' collects the specified column inline-sizes from the first table-row for fixed table layout calculation.
self.col_inline_sizes = kid.col_inline_sizes().clone();
self.col_min_inline_sizes = kid.col_min_inline_sizes().clone();
self.col_pref_inline_sizes = kid.col_pref_inline_sizes().clone();
} else {
min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
// update the number of column widths from table-rows.
let num_cols = self.col_widths.len();
let num_child_cols = kid.col_min_widths().len();
// update the number of column inline-sizes from table-rows.
let num_cols = self.col_inline_sizes.len();
let num_child_cols = kid.col_min_inline_sizes().len();
for i in range(num_cols, num_child_cols) {
self.col_widths.push(Au::new(0));
let new_kid_min = *kid.col_min_widths().get(i);
self.col_min_widths.push(*kid.col_min_widths().get(i));
let new_kid_pref = *kid.col_pref_widths().get(i);
self.col_pref_widths.push(*kid.col_pref_widths().get(i));
min_width = min_width + new_kid_min;
pref_width = pref_width + new_kid_pref;
self.col_inline_sizes.push(Au::new(0));
let new_kid_min = *kid.col_min_inline_sizes().get(i);
self.col_min_inline_sizes.push(*kid.col_min_inline_sizes().get(i));
let new_kid_pref = *kid.col_pref_inline_sizes().get(i);
self.col_pref_inline_sizes.push(*kid.col_pref_inline_sizes().get(i));
min_inline_size = min_inline_size + new_kid_min;
pref_inline_size = pref_inline_size + new_kid_pref;
}
}
}
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
pref_width);
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = geometry::max(min_inline_size,
pref_inline_size);
}
/// Recursively (top-down) determines the actual width of child contexts and fragments. When
/// called on this context, the context has had its width set by the parent context.
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
debug!("assign_widths({}): assigning width for flow", "table_rowgroup");
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
/// called on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, ctx: &mut LayoutContext) {
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_rowgroup");
// The position was set to the containing block by the flow's parent.
let containing_block_width = self.block_flow.base.position.size.width;
// FIXME: In case of border-collapse: collapse, left_content_edge should be border-left
let left_content_edge = Au::new(0);
let content_width = containing_block_width;
let containing_block_inline_size = self.block_flow.base.position.size.inline;
// FIXME: In case of border-collapse: collapse, inline-start_content_edge should be
// the border width on the inline-start side.
let inline_start_content_edge = Au::new(0);
let content_inline_size = containing_block_inline_size;
let width_computer = InternalTable;
width_computer.compute_used_width(&mut self.block_flow, ctx, containing_block_width);
let inline_size_computer = InternalTable;
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, Some(self.col_inline_sizes.clone()));
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_rowgroup");
self.assign_height_table_rowgroup_base(ctx);
fn assign_block_size(&mut self, ctx: &mut LayoutContext) {
debug!("assign_block_size: assigning block_size for table_rowgroup");
self.assign_block_size_table_rowgroup_base(ctx);
}
fn compute_absolute_position(&mut self) {

View file

@ -6,8 +6,8 @@
#![deny(unsafe_block)]
use block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use block::{WidthConstraintInput, WidthConstraintSolution};
use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
use block::{ISizeConstraintInput, ISizeConstraintSolution};
use construct::FlowConstructor;
use context::LayoutContext;
use floats::FloatKind;
@ -30,8 +30,8 @@ pub enum TableLayout {
pub struct TableWrapperFlow {
pub block_flow: BlockFlow,
/// Column widths
pub col_widths: Vec<Au>,
/// Column inline-sizes
pub col_inline_sizes: Vec<Au>,
/// Table-layout property
pub table_layout: TableLayout,
@ -50,7 +50,7 @@ impl TableWrapperFlow {
};
TableWrapperFlow {
block_flow: block_flow,
col_widths: vec!(),
col_inline_sizes: vec!(),
table_layout: table_layout
}
}
@ -67,7 +67,7 @@ impl TableWrapperFlow {
};
TableWrapperFlow {
block_flow: block_flow,
col_widths: vec!(),
col_inline_sizes: vec!(),
table_layout: table_layout
}
}
@ -85,7 +85,7 @@ impl TableWrapperFlow {
};
TableWrapperFlow {
block_flow: block_flow,
col_widths: vec!(),
col_inline_sizes: vec!(),
table_layout: table_layout
}
}
@ -94,14 +94,14 @@ impl TableWrapperFlow {
self.block_flow.float.is_some()
}
/// Assign height for table-wrapper flow.
/// `Assign height` of table-wrapper flow follows a similar process to that of block flow.
/// Assign block-size for table-wrapper flow.
/// `Assign block-size` of table-wrapper flow follows a similar process to that of block flow.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
fn assign_height_table_wrapper_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse);
fn assign_block_size_table_wrapper_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
}
pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) {
@ -124,31 +124,31 @@ impl Flow for TableWrapperFlow {
}
/* 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
minimum inline_sizes. When called on this context, all child contexts
have had their min/pref inline_sizes set. This function must decide
min/pref inline_sizes based on child context inline_sizes and dimensions of
any fragments it is responsible for flowing. */
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
// get column widths info from table flow
fn bubble_inline_sizes(&mut self, ctx: &mut LayoutContext) {
// get column inline-sizes info from table flow
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_caption() || kid.is_table());
if kid.is_table() {
self.col_widths.push_all(kid.as_table().col_widths.as_slice());
self.col_inline_sizes.push_all(kid.as_table().col_inline_sizes.as_slice());
}
}
self.block_flow.bubble_widths(ctx);
self.block_flow.bubble_inline_sizes(ctx);
}
/// Recursively (top-down) determines the actual width of child contexts and fragments. When
/// called on this context, the context has had its width set by the parent context.
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
/// called on this context, the context has had its inline-size set by the parent context.
///
/// Dual fragments consume some width first, and the remainder is assigned to all child (block)
/// Dual fragments consume some inline-size first, and the remainder is assigned to all child (block)
/// contexts.
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
debug!("assign_widths({}): assigning width for flow",
fn assign_inline_sizes(&mut self, ctx: &mut LayoutContext) {
debug!("assign_inline_sizes({}): assigning inline_size for flow",
if self.is_float() {
"floated table_wrapper"
} else {
@ -156,35 +156,35 @@ impl Flow for TableWrapperFlow {
});
// The position was set to the containing block by the flow's parent.
let containing_block_width = self.block_flow.base.position.size.width;
let containing_block_inline_size = self.block_flow.base.position.size.inline;
let width_computer = TableWrapper;
width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width);
let inline_size_computer = TableWrapper;
inline_size_computer.compute_used_inline_size_table_wrapper(self, ctx, containing_block_inline_size);
let left_content_edge = self.block_flow.fragment.border_box.origin.x;
let content_width = self.block_flow.fragment.border_box.size.width;
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
let content_inline_size = self.block_flow.fragment.border_box.size.inline;
match self.table_layout {
FixedLayout | _ if self.is_float() =>
self.block_flow.base.position.size.width = content_width,
self.block_flow.base.position.size.inline = content_inline_size,
_ => {}
}
// In case of fixed layout, column widths are calculated in table flow.
let assigned_col_widths = match self.table_layout {
// In case of fixed layout, column inline-sizes are calculated in table flow.
let assigned_col_inline_sizes = match self.table_layout {
FixedLayout => None,
AutoLayout => Some(self.col_widths.clone())
AutoLayout => Some(self.col_inline_sizes.clone())
};
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, assigned_col_widths);
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, assigned_col_inline_sizes);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
fn assign_block_size(&mut self, ctx: &mut LayoutContext) {
if self.is_float() {
debug!("assign_height_float: assigning height for floated table_wrapper");
self.block_flow.assign_height_float(ctx);
debug!("assign_block_size_float: assigning block_size for floated table_wrapper");
self.block_flow.assign_block_size_float(ctx);
} else {
debug!("assign_height: assigning height for table_wrapper");
self.assign_height_table_wrapper_base(ctx);
debug!("assign_block_size: assigning block_size for table_wrapper");
self.assign_block_size_table_wrapper_base(ctx);
}
}
@ -206,119 +206,120 @@ impl fmt::Show for TableWrapperFlow {
struct TableWrapper;
impl TableWrapper {
fn compute_used_width_table_wrapper(&self,
fn compute_used_inline_size_table_wrapper(&self,
table_wrapper: &mut TableWrapperFlow,
ctx: &mut LayoutContext,
parent_flow_width: Au) {
let input = self.compute_width_constraint_inputs_table_wrapper(table_wrapper,
parent_flow_width,
parent_flow_inline_size: Au) {
let input = self.compute_inline_size_constraint_inputs_table_wrapper(table_wrapper,
parent_flow_inline_size,
ctx);
let solution = self.solve_width_constraints(&mut table_wrapper.block_flow, &input);
let solution = self.solve_inline_size_constraints(&mut table_wrapper.block_flow, &input);
self.set_width_constraint_solutions(&mut table_wrapper.block_flow, solution);
self.set_inline_size_constraint_solutions(&mut table_wrapper.block_flow, solution);
self.set_flow_x_coord_if_necessary(&mut table_wrapper.block_flow, solution);
}
fn compute_width_constraint_inputs_table_wrapper(&self,
fn compute_inline_size_constraint_inputs_table_wrapper(&self,
table_wrapper: &mut TableWrapperFlow,
parent_flow_width: Au,
parent_flow_inline_size: Au,
ctx: &mut LayoutContext)
-> WidthConstraintInput {
let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow,
parent_flow_width,
-> ISizeConstraintInput {
let mut input = self.compute_inline_size_constraint_inputs(&mut table_wrapper.block_flow,
parent_flow_inline_size,
ctx);
let computed_width = match table_wrapper.table_layout {
let computed_inline_size = match table_wrapper.table_layout {
FixedLayout => {
let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0),
|sum, width| sum.add(width));
let fixed_cells_inline_size = table_wrapper.col_inline_sizes.iter().fold(Au(0),
|sum, inline_size| sum.add(inline_size));
let mut computed_width = input.computed_width.specified_or_zero();
let mut computed_inline_size = input.computed_inline_size.specified_or_zero();
let style = table_wrapper.block_flow.fragment.style();
// Get left and right paddings, borders for table.
// Get inline-start and inline-end paddings, borders for table.
// We get these values from the fragment's style since table_wrapper doesn't have it's own border or padding.
// input.available_width is same as containing_block_width in table_wrapper.
let padding_left = specified(style.get_padding().padding_left,
input.available_width);
let padding_right = specified(style.get_padding().padding_right,
input.available_width);
let border_left = style.get_border().border_left_width;
let border_right = style.get_border().border_right_width;
let padding_and_borders = padding_left + padding_right + border_left + border_right;
// Compare border-edge widths. Because fixed_cells_width indicates content-width,
// padding and border values are added to fixed_cells_width.
computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width);
computed_width
// input.available_inline-size is same as containing_block_inline-size in table_wrapper.
let padding = style.logical_padding();
let border = style.logical_border_width();
let padding_and_borders =
specified(padding.inline_start, input.available_inline_size) +
specified(padding.inline_end, input.available_inline_size) +
border.inline_start +
border.inline_end;
// Compare border-edge inline-sizes. Because fixed_cells_inline-size indicates content-inline-size,
// padding and border values are added to fixed_cells_inline-size.
computed_inline_size = geometry::max(
fixed_cells_inline_size + padding_and_borders, computed_inline_size);
computed_inline_size
},
AutoLayout => {
// Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2.
let mut cap_min = Au(0);
let mut cols_min = Au(0);
let mut cols_max = Au(0);
let mut col_min_widths = &vec!();
let mut col_pref_widths = &vec!();
let mut col_min_inline_sizes = &vec!();
let mut col_pref_inline_sizes = &vec!();
for kid in table_wrapper.block_flow.base.child_iter() {
if kid.is_table_caption() {
cap_min = kid.as_block().base.intrinsic_widths.minimum_width;
cap_min = kid.as_block().base.intrinsic_inline_sizes.minimum_inline_size;
} else {
assert!(kid.is_table());
cols_min = kid.as_block().base.intrinsic_widths.minimum_width;
cols_max = kid.as_block().base.intrinsic_widths.preferred_width;
col_min_widths = kid.col_min_widths();
col_pref_widths = kid.col_pref_widths();
cols_min = kid.as_block().base.intrinsic_inline_sizes.minimum_inline_size;
cols_max = kid.as_block().base.intrinsic_inline_sizes.preferred_inline_size;
col_min_inline_sizes = kid.col_min_inline_sizes();
col_pref_inline_sizes = kid.col_pref_inline_sizes();
}
}
// 'extra_width': difference between the calculated table width and minimum width
// 'extra_inline-size': difference between the calculated table inline-size and minimum inline-size
// required by all columns. It will be distributed over the columns.
let (width, extra_width) = match input.computed_width {
let (inline_size, extra_inline_size) = match input.computed_inline_size {
Auto => {
if input.available_width > geometry::max(cols_max, cap_min) {
if input.available_inline_size > geometry::max(cols_max, cap_min) {
if cols_max > cap_min {
table_wrapper.col_widths = col_pref_widths.clone();
table_wrapper.col_inline_sizes = col_pref_inline_sizes.clone();
(cols_max, Au(0))
} else {
(cap_min, cap_min - cols_min)
}
} else {
let max = if cols_min >= input.available_width && cols_min >= cap_min {
table_wrapper.col_widths = col_min_widths.clone();
let max = if cols_min >= input.available_inline_size && cols_min >= cap_min {
table_wrapper.col_inline_sizes = col_min_inline_sizes.clone();
cols_min
} else {
geometry::max(input.available_width, cap_min)
geometry::max(input.available_inline_size, cap_min)
};
(max, max - cols_min)
}
},
Specified(width) => {
let max = if cols_min >= width && cols_min >= cap_min {
table_wrapper.col_widths = col_min_widths.clone();
Specified(inline_size) => {
let max = if cols_min >= inline_size && cols_min >= cap_min {
table_wrapper.col_inline_sizes = col_min_inline_sizes.clone();
cols_min
} else {
geometry::max(width, cap_min)
geometry::max(inline_size, cap_min)
};
(max, max - cols_min)
}
};
// The extra width is distributed over the columns
if extra_width > Au(0) {
let cell_len = table_wrapper.col_widths.len() as f64;
table_wrapper.col_widths = col_min_widths.iter().map(|width| {
width + extra_width.scale_by(1.0 / cell_len)
// The extra inline-size is distributed over the columns
if extra_inline_size > Au(0) {
let cell_len = table_wrapper.col_inline_sizes.len() as f64;
table_wrapper.col_inline_sizes = col_min_inline_sizes.iter().map(|inline_size| {
inline_size + extra_inline_size.scale_by(1.0 / cell_len)
}).collect();
}
width
inline_size
}
};
input.computed_width = Specified(computed_width);
input.computed_inline_size = Specified(computed_inline_size);
input
}
}
impl WidthAndMarginsComputer for TableWrapper {
/// Solve the width and margins constraints for this block flow.
fn solve_width_constraints(&self, block: &mut BlockFlow, input: &WidthConstraintInput)
-> WidthConstraintSolution {
self.solve_block_width_constraints(block, input)
impl ISizeAndMarginsComputer for TableWrapper {
/// Solve the inline-size and margins constraints for this block flow.
fn solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput)
-> ISizeConstraintSolution {
self.solve_block_inline_size_constraints(block, input)
}
}

View file

@ -15,6 +15,7 @@ use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
use servo_util::geometry::Au;
use servo_util::logical_geometry::LogicalSize;
use servo_util::range::Range;
use style::ComputedValues;
use style::computed_values::{font_family, line_height, white_space};
@ -151,9 +152,11 @@ impl TextRunScanner {
*text);
let range = Range::new(CharIndex(0), run.char_len());
let new_metrics = run.metrics_for_range(&range);
let bounding_box_size = LogicalSize::from_physical(
old_fragment.style.writing_mode, new_metrics.bounding_box.size);
let new_text_fragment_info = ScannedTextFragmentInfo::new(Arc::new(run), range);
let mut new_fragment = old_fragment.transform(new_metrics.bounding_box.size,
ScannedTextFragment(new_text_fragment_info));
let mut new_fragment = old_fragment.transform(
bounding_box_size, ScannedTextFragment(new_text_fragment_info));
new_fragment.new_line_pos = new_line_pos;
out_fragments.push(new_fragment)
}
@ -236,9 +239,12 @@ impl TextRunScanner {
}
let new_text_fragment_info = ScannedTextFragmentInfo::new(run.get_ref().clone(), *range);
let old_fragment = &in_fragments[i.to_uint()];
let new_metrics = new_text_fragment_info.run.metrics_for_range(range);
let mut new_fragment = in_fragments[i.to_uint()].transform(new_metrics.bounding_box.size,
ScannedTextFragment(new_text_fragment_info));
let bounding_box_size = LogicalSize::from_physical(
old_fragment.style.writing_mode, new_metrics.bounding_box.size);
let mut new_fragment = old_fragment.transform(
bounding_box_size, ScannedTextFragment(new_text_fragment_info));
new_fragment.new_line_pos = new_line_positions.get(logical_offset.to_uint()).new_line_pos.clone();
out_fragments.push(new_fragment)
}
@ -288,7 +294,7 @@ pub fn computed_style_to_font_style(style: &ComputedValues) -> FontStyle {
}
}
/// Returns the line height needed by the given computed style and font size.
/// Returns the line block-size needed by the given computed style and font size.
///
/// FIXME(pcwalton): I believe this should not take a separate `font-size` parameter.
pub fn line_height_from_style(style: &ComputedValues, font_size: Au) -> Au {

View file

@ -8,11 +8,13 @@ pub use std::ascii::StrAsciiExt;
use serialize::{Encodable, Encoder};
pub use servo_util::url::parse_url;
use servo_util::logical_geometry::{WritingMode, LogicalMargin};
use sync::Arc;
pub use url::Url;
pub use cssparser::*;
pub use cssparser::ast::*;
pub use geom::SideOffsets2D;
use errors::{ErrorLoggerIterator, log_css_error};
pub use parsing_utils::*;
@ -1590,8 +1592,6 @@ impl PropertyDeclaration {
pub mod style_structs {
use super::longhands;
use super::computed_values;
use servo_util::logical_geometry::WritingMode;
% for style_struct in STYLE_STRUCTS:
#[deriving(PartialEq, Clone)]
@ -1601,42 +1601,6 @@ pub mod style_structs {
% endfor
}
% endfor
impl InheritedBox {
/// Return a WritingMode bitflags from the relevant CSS properties.
pub fn get_writing_mode(&self) -> WritingMode {
use servo_util::logical_geometry;
let mut flags = WritingMode::empty();
match self.direction {
computed_values::direction::ltr => {},
computed_values::direction::rtl => {
flags.insert(logical_geometry::FlagRTL);
},
}
match self.writing_mode {
computed_values::writing_mode::horizontal_tb => {},
computed_values::writing_mode::vertical_rl => {
flags.insert(logical_geometry::FlagVertical);
},
computed_values::writing_mode::vertical_lr => {
flags.insert(logical_geometry::FlagVertical);
flags.insert(logical_geometry::FlagVerticalLR);
},
}
match self.text_orientation {
computed_values::text_orientation::sideways_right => {},
computed_values::text_orientation::sideways_left => {
flags.insert(logical_geometry::FlagSidewaysLeft);
},
computed_values::text_orientation::sideways => {
if flags.intersects(logical_geometry::FlagVerticalLR) {
flags.insert(logical_geometry::FlagSidewaysLeft);
}
},
}
flags
}
}
}
#[deriving(Clone)]
@ -1645,6 +1609,7 @@ pub struct ComputedValues {
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
shareable: bool,
pub writing_mode: WritingMode,
}
impl ComputedValues {
@ -1662,7 +1627,89 @@ impl ComputedValues {
}
}
#[inline]
pub fn content_inline_size(&self) -> computed_values::LengthOrPercentageOrAuto {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.height } else { box_style.width }
}
#[inline]
pub fn content_block_size(&self) -> computed_values::LengthOrPercentageOrAuto {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.width } else { box_style.height }
}
#[inline]
pub fn min_inline_size(&self) -> computed_values::LengthOrPercentage {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.min_height } else { box_style.min_width }
}
#[inline]
pub fn min_block_size(&self) -> computed_values::LengthOrPercentage {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.min_width } else { box_style.min_height }
}
#[inline]
pub fn max_inline_size(&self) -> computed_values::LengthOrPercentageOrNone {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.max_height } else { box_style.max_width }
}
#[inline]
pub fn max_block_size(&self) -> computed_values::LengthOrPercentageOrNone {
let box_style = self.get_box();
if self.writing_mode.is_vertical() { box_style.max_width } else { box_style.max_height }
}
#[inline]
pub fn logical_padding(&self) -> LogicalMargin<computed_values::LengthOrPercentage> {
let padding_style = self.get_padding();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
padding_style.padding_top,
padding_style.padding_right,
padding_style.padding_bottom,
padding_style.padding_left,
))
}
#[inline]
pub fn logical_border_width(&self) -> LogicalMargin<Au> {
let border_style = self.get_border();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
border_style.border_top_width,
border_style.border_right_width,
border_style.border_bottom_width,
border_style.border_left_width,
))
}
#[inline]
pub fn logical_margin(&self) -> LogicalMargin<computed_values::LengthOrPercentageOrAuto> {
let margin_style = self.get_margin();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
margin_style.margin_top,
margin_style.margin_right,
margin_style.margin_bottom,
margin_style.margin_left,
))
}
#[inline]
pub fn logical_position(&self) -> LogicalMargin<computed_values::LengthOrPercentageOrAuto> {
// FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
let position_style = self.get_positionoffsets();
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
position_style.top,
position_style.right,
position_style.bottom,
position_style.left,
))
}
% for style_struct in STYLE_STRUCTS:
#[inline]
pub fn get_${style_struct.name.lower()}
<'a>(&'a self) -> &'a style_structs::${style_struct.name} {
&*self.${style_struct.ident}
@ -1670,6 +1717,42 @@ impl ComputedValues {
% endfor
}
/// Return a WritingMode bitflags from the relevant CSS properties.
fn get_writing_mode(inheritedbox_style: &style_structs::InheritedBox) -> WritingMode {
use servo_util::logical_geometry;
let mut flags = WritingMode::empty();
match inheritedbox_style.direction {
computed_values::direction::ltr => {},
computed_values::direction::rtl => {
flags.insert(logical_geometry::FlagRTL);
},
}
match inheritedbox_style.writing_mode {
computed_values::writing_mode::horizontal_tb => {},
computed_values::writing_mode::vertical_rl => {
flags.insert(logical_geometry::FlagVertical);
},
computed_values::writing_mode::vertical_lr => {
flags.insert(logical_geometry::FlagVertical);
flags.insert(logical_geometry::FlagVerticalLR);
},
}
match inheritedbox_style.text_orientation {
computed_values::text_orientation::sideways_right => {},
computed_values::text_orientation::sideways_left => {
flags.insert(logical_geometry::FlagSidewaysLeft);
},
computed_values::text_orientation::sideways => {
if flags.intersects(logical_geometry::FlagVerticalLR) {
flags.insert(logical_geometry::FlagSidewaysLeft);
}
},
}
flags
}
/// The initial values for all style structs as defined by the specification.
lazy_init! {
static ref INITIAL_VALUES: ComputedValues = ComputedValues {
@ -1681,10 +1764,17 @@ lazy_init! {
}),
% endfor
shareable: true,
writing_mode: WritingMode::empty()
};
}
#[test]
fn initial_writing_mode_is_empty() {
assert_eq!(get_writing_mode(INITIAL_VALUES.get_inheritedbox()), WritingMode::empty())
}
/// This only exists to limit the scope of #[allow(experimental)]
/// FIXME: remove this when Arc::make_unique() is not experimental anymore.
trait ArcExperimental<T> {
@ -1780,6 +1870,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
}
ComputedValues {
writing_mode: get_writing_mode(&*style_inheritedbox),
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: style_${style_struct.ident},
% endfor
@ -1998,6 +2089,7 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
}
(ComputedValues {
writing_mode: get_writing_mode(&*style_inheritedbox),
% for style_struct in STYLE_STRUCTS:
${style_struct.ident}: style_${style_struct.ident},
% endfor
@ -2023,6 +2115,7 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues {
.${style_struct.ident}.clone(),
% endfor
shareable: false,
writing_mode: parent_style.writing_mode,
};
{
let border = result.border.make_unique_experimental();

View file

@ -17,6 +17,7 @@
extern crate debug;
extern crate collections;
extern crate geom;
extern crate num;
extern crate serialize;
extern crate sync;

View file

@ -67,7 +67,7 @@ pub struct Opts {
/// intrinsic widths are computed as a separate pass instead of during flow construction. You
/// may wish to turn this flag on in order to benchmark style recalculation against other
/// browser engines.
pub bubble_widths_separately: bool,
pub bubble_inline_sizes_separately: bool,
}
fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
@ -186,7 +186,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
output_file: opt_match.opt_str("o"),
headless: opt_match.opt_present("z"),
hard_fail: opt_match.opt_present("f"),
bubble_widths_separately: opt_match.opt_present("b"),
bubble_inline_sizes_separately: opt_match.opt_present("b"),
})
}