Merge pull request #2869 from SimonSapin/writing-modes-rebase

Convert layout code to use logical directions (rebase)
This commit is contained in:
Patrick Walton 2014-07-18 13:27:23 -07:00
commit 45379a6a61
28 changed files with 2678 additions and 2408 deletions

View file

@ -56,13 +56,14 @@ pub extern "C" fn cef_run_message_loop() {
device_pixels_per_px: None, device_pixels_per_px: None,
time_profiler_period: None, time_profiler_period: None,
memory_profiler_period: None, memory_profiler_period: None,
enable_experimental: false,
layout_threads: 1, layout_threads: 1,
//layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1), //layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1),
exit_after_load: false, exit_after_load: false,
output_file: None, output_file: None,
headless: false, headless: false,
hard_fail: false, hard_fail: false,
bubble_widths_separately: false, bubble_inline_sizes_separately: false,
}; };
native::start(0, 0 as **u8, proc() { native::start(0, 0 as **u8, proc() {
servo::run(opts); 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 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()); 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; inline_flow.minimum_depth_below_baseline = descent;
let mut inline_flow = inline_flow as Box<Flow>; let mut inline_flow = inline_flow as Box<Flow>;
TextRunScanner::new().scan_for_runs(self.font_context(), inline_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); 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. /// 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) /// This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic -- inline-size)
/// calculation, unless the global `bubble_widths_separately` flag is on. /// 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.) /// properly computed. (This is not, however, a memory safety problem.)
fn finish(&mut self, context: &mut LayoutContext); 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 /// 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 -- /// it. This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic --
/// width) calculation, unless the global `bubble_widths_separately` flag is on. /// 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.) /// properly computed. (This is not, however, a memory safety problem.)
/// ///
/// This must not be public because only the layout constructor can do this. /// This must not be public because only the layout constructor can do this.
fn finish(&mut self, context: &mut LayoutContext) { fn finish(&mut self, context: &mut LayoutContext) {
if !context.opts.bubble_widths_separately { if !context.opts.bubble_inline_sizes_separately {
self.get_mut().bubble_widths(context) self.get_mut().bubble_inline_sizes(context)
} }
} }
} }

View file

@ -6,8 +6,7 @@
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use geom::rect::Rect; use geom::{Rect, Size2D};
use geom::size::Size2D;
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use gfx::font_cache_task::FontCacheTask; 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use servo_util::geometry::{Au, max, min}; 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::i32;
use std::fmt; use std::fmt;
use style::computed_values::float; use style::computed_values::float;
@ -39,7 +38,7 @@ pub enum ClearType {
#[deriving(Clone)] #[deriving(Clone)]
struct Float { struct Float {
/// The boundaries of this float. /// The boundaries of this float.
bounds: Rect<Au>, bounds: LogicalRect<Au>,
/// The kind of float: left or right. /// The kind of float: left or right.
kind: FloatKind, kind: FloatKind,
} }
@ -58,22 +57,22 @@ impl fmt::Show for Float {
struct FloatList { struct FloatList {
/// Information about each of the floats here. /// Information about each of the floats here.
floats: Vec<Float>, floats: Vec<Float>,
/// Cached copy of the maximum top offset of the float. /// Cached copy of the maximum block-start offset of the float.
max_top: Au, max_block_start: Au,
} }
impl FloatList { impl FloatList {
fn new() -> FloatList { fn new() -> FloatList {
FloatList { FloatList {
floats: vec!(), floats: vec!(),
max_top: Au(0), max_block_start: Au(0),
} }
} }
} }
impl fmt::Show for FloatList { impl fmt::Show for FloatList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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. /// All the information necessary to place a float.
pub struct PlacementInfo { pub struct PlacementInfo {
/// The dimensions of the float. /// The dimensions of the float.
pub size: Size2D<Au>, pub size: LogicalSize<Au>,
/// The minimum top of the float, as determined by earlier elements. /// The minimum block-start of the float, as determined by earlier elements.
pub ceiling: Au, pub ceiling: Au,
/// The maximum right position of the float, generally determined by the containing block. /// The maximum inline-end position of the float, generally determined by the containing block.
pub max_width: Au, pub max_inline_size: Au,
/// The kind of float. /// The kind of float.
pub kind: FloatKind pub kind: FloatKind
} }
impl fmt::Show for PlacementInfo { impl fmt::Show for PlacementInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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) { fn range_intersect(block_start_1: Au, block_end_1: Au, block_start_2: Au, block_end_2: Au) -> (Au, Au) {
(max(top_1, top_2), min(bottom_1, bottom_2)) (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 /// Encapsulates information about floats. This is optimized to avoid allocation if there are
@ -145,7 +144,8 @@ pub struct Floats {
/// The list of floats. /// The list of floats.
list: FloatListRef, list: FloatListRef,
/// The offset of the flow relative to the first float. /// 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 { impl fmt::Show for Floats {
@ -163,79 +163,80 @@ impl fmt::Show for Floats {
impl Floats { impl Floats {
/// Creates a new `Floats` object. /// Creates a new `Floats` object.
pub fn new() -> Floats { pub fn new(writing_mode: WritingMode) -> Floats {
Floats { Floats {
list: FloatListRef::new(), 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. /// 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 self.offset = self.offset + delta
} }
/// Returns the position of the last float in flow coordinates. /// 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() { match self.list.get() {
None => None, None => None,
Some(list) => { Some(list) => {
match list.floats.last() { match list.floats.last() {
None => None, 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 /// 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.) /// floats have no effect. (Generally this is the containing block inline-size.)
pub fn available_rect(&self, top: Au, height: Au, max_x: Au) -> Option<Rect<Au>> { pub fn available_rect(&self, block_start: Au, block_size: Au, max_x: Au) -> Option<LogicalRect<Au>> {
let list = match self.list.get() { let list = match self.list.get() {
None => return None, None => return None,
Some(list) => list, 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 // Relevant dimensions for the inline-end-most inline-start float
let mut max_left = Au(0) - self.offset.x; let mut max_inline_start = Au(0) - self.offset.inline;
let mut l_top = None; let mut l_block_start = None;
let mut l_bottom = None; let mut l_block_end = None;
// Relevant dimensions for the left-most right float // Relevant dimensions for the inline-start-most inline-end float
let mut min_right = max_x - self.offset.x; let mut min_inline_end = max_x - self.offset.inline;
let mut r_top = None; let mut r_block_start = None;
let mut r_bottom = None; let mut r_block_end = None;
// Find the float collisions for the given vertical range. // Find the float collisions for the given vertical range.
for float in list.floats.iter() { for float in list.floats.iter() {
debug!("available_rect: Checking for collision against float"); 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; let float_size = float.bounds.size;
debug!("float_pos: {}, float_size: {}", float_pos, float_size); debug!("float_pos: {}, float_size: {}", float_pos, float_size);
match float.kind { match float.kind {
FloatLeft if float_pos.x + float_size.width > max_left && FloatLeft if float_pos.i + float_size.inline > max_inline_start &&
float_pos.y + float_size.height > top && float_pos.y < top + height => { float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
max_left = float_pos.x + float_size.width; max_inline_start = float_pos.i + float_size.inline;
l_top = Some(float_pos.y); l_block_start = Some(float_pos.b);
l_bottom = Some(float_pos.y + float_size.height); l_block_end = Some(float_pos.b + float_size.block);
debug!("available_rect: collision with left float: new max_left is {}", debug!("available_rect: collision with inline_start float: new max_inline_start is {}",
max_left); max_inline_start);
} }
FloatRight if float_pos.x < min_right && FloatRight if float_pos.i < min_inline_end &&
float_pos.y + float_size.height > top && float_pos.y < top + height => { float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
min_right = float_pos.x; min_inline_end = float_pos.i;
r_top = Some(float_pos.y); r_block_start = Some(float_pos.b);
r_bottom = Some(float_pos.y + float_size.height); r_block_end = Some(float_pos.b + float_size.block);
debug!("available_rect: collision with right float: new min_right is {}", debug!("available_rect: collision with inline_end float: new min_inline_end is {}",
min_right); min_inline_end);
} }
FloatLeft | FloatRight => {} FloatLeft | FloatRight => {}
} }
@ -243,29 +244,29 @@ impl Floats {
// Extend the vertical range of the rectangle to the closest floats. // Extend the vertical range of the rectangle to the closest floats.
// If there are floats on both sides, take the intersection of the // 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. // given upper bound.
let (top, bottom) = match (r_top, r_bottom, l_top, l_bottom) { let (block_start, block_end) = match (r_block_start, r_block_end, l_block_start, l_block_end) {
(Some(r_top), Some(r_bottom), Some(l_top), Some(l_bottom)) => (Some(r_block_start), Some(r_block_end), Some(l_block_start), Some(l_block_end)) =>
range_intersect(max(top, r_top), r_bottom, max(top, l_top), l_bottom), 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), (None, None, Some(l_block_start), Some(l_block_end)) => (max(block_start, l_block_start), l_block_end),
(Some(r_top), Some(r_bottom), None, None) => (max(top, r_top), r_bottom), (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, (None, None, None, None) => return None,
_ => fail!("Reached unreachable state when computing float area") _ => fail!("Reached unreachable state when computing float area")
}; };
// FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to // 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. // 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 { Some(LogicalRect::new(
origin: Point2D(max_left, top) + self.offset, self.writing_mode, max_inline_start + self.offset.inline, block_start + self.offset.block,
size: Size2D(min_right - max_left, bottom - top) min_inline_end - max_inline_start, block_end - block_start
}) ))
} }
/// Adds a new float to the list. /// Adds a new float to the list.
@ -275,8 +276,8 @@ impl Floats {
let list = self.list.get_mut(); let list = self.list.get_mut();
new_info = PlacementInfo { new_info = PlacementInfo {
size: info.size, size: info.size,
ceiling: max(info.ceiling, list.max_top + self.offset.y), ceiling: max(info.ceiling, list.max_block_start + self.offset.block),
max_width: info.max_width, max_inline_size: info.max_inline_size,
kind: info.kind kind: info.kind
} }
} }
@ -284,109 +285,131 @@ impl Floats {
debug!("add_float: added float with info {:?}", new_info); debug!("add_float: added float with info {:?}", new_info);
let new_float = Float { let new_float = Float {
bounds: Rect { bounds: LogicalRect::from_point_size(
origin: self.place_between_floats(&new_info).origin - self.offset, self.writing_mode,
size: info.size, self.place_between_floats(&new_info).start - self.offset,
}, info.size,
),
kind: info.kind kind: info.kind
}; };
let list = self.list.get_mut(); let list = self.list.get_mut();
list.floats.push(new_float); 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 /// 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 height is infinite. /// rectangle not colliding with any floats. Returns None if that block-size is infinite.
fn max_height_for_bounds(&self, left: Au, top: Au, width: Au) -> Option<Au> { fn max_block_size_for_bounds(&self, inline_start: Au, block_start: Au, inline_size: Au) -> Option<Au> {
let list = match self.list.get() { let list = match self.list.get() {
None => return None, None => return None,
Some(list) => list, Some(list) => list,
}; };
let top = top - self.offset.y; let block_start = block_start - self.offset.block;
let left = left - self.offset.x; let inline_start = inline_start - self.offset.inline;
let mut max_height = None; let mut max_block_size = None;
for float in list.floats.iter() { for float in list.floats.iter() {
if float.bounds.origin.y + float.bounds.size.height > top && if float.bounds.start.b + float.bounds.size.block > block_start &&
float.bounds.origin.x + float.bounds.size.width > left && float.bounds.start.i + float.bounds.size.inline > inline_start &&
float.bounds.origin.x < left + width { float.bounds.start.i < inline_start + inline_size {
let new_y = float.bounds.origin.y; let new_y = float.bounds.start.b;
max_height = Some(min(max_height.unwrap_or(new_y), new_y)); 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 /// Given placement information, finds the closest place a fragment can be positioned without
/// colliding with any floats. /// colliding with any floats.
pub fn place_between_floats(&self, info: &PlacementInfo) -> Rect<Au> { pub fn place_between_floats(&self, info: &PlacementInfo) -> LogicalRect<Au> {
debug!("place_between_floats: Placing object with width {} and height {}", debug!("place_between_floats: Placing object with {}", info.size);
info.size.width,
info.size.height);
// If no floats, use this fast path. // If no floats, use this fast path.
if !self.list.is_present() { if !self.list.is_present() {
match info.kind { match info.kind {
FloatLeft => { FloatLeft => {
return Rect(Point2D(Au(0), info.ceiling), return LogicalRect::new(
Size2D(info.max_width, Au(i32::MAX))) self.writing_mode,
Au(0),
info.ceiling,
info.max_inline_size,
Au(i32::MAX))
} }
FloatRight => { FloatRight => {
return Rect(Point2D(info.max_width - info.size.width, info.ceiling), return LogicalRect::new(
Size2D(info.max_width, Au(i32::MAX))) 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. // 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 { loop {
let maybe_location = self.available_rect(float_y, info.size.height, info.max_width); 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_y); debug!("place_float: Got available rect: {:?} for y-pos: {}", maybe_location, float_b);
match maybe_location { match maybe_location {
// If there are no floats blocking us, return the current location // If there are no floats blocking us, return the current location
// TODO(eatkinson): integrate with overflow // TODO(eatkinson): integrate with overflow
None => { None => {
return match info.kind { return match info.kind {
FloatLeft => { FloatLeft => {
Rect(Point2D(Au(0), float_y), LogicalRect::new(
Size2D(info.max_width, Au(i32::MAX))) self.writing_mode,
Au(0),
float_b,
info.max_inline_size,
Au(i32::MAX))
} }
FloatRight => { FloatRight => {
Rect(Point2D(info.max_width - info.size.width, float_y), LogicalRect::new(
Size2D(info.max_width, Au(i32::MAX))) self.writing_mode,
info.max_inline_size - info.size.inline,
float_b,
info.max_inline_size,
Au(i32::MAX))
} }
} }
} }
Some(rect) => { Some(rect) => {
assert!(rect.origin.y + rect.size.height != float_y, assert!(rect.start.b + rect.size.block != float_b,
"Non-terminating float placement"); "Non-terminating float placement");
// Place here if there is enough room // Place here if there is enough room
if rect.size.width >= info.size.width { if rect.size.inline >= info.size.inline {
let height = self.max_height_for_bounds(rect.origin.x, let block_size = self.max_block_size_for_bounds(rect.start.i,
rect.origin.y, rect.start.b,
rect.size.width); rect.size.inline);
let height = height.unwrap_or(Au(i32::MAX)); let block_size = block_size.unwrap_or(Au(i32::MAX));
return match info.kind { return match info.kind {
FloatLeft => { FloatLeft => {
Rect(Point2D(rect.origin.x, float_y), LogicalRect::new(
Size2D(rect.size.width, height)) self.writing_mode,
rect.start.i,
float_b,
rect.size.inline,
block_size)
} }
FloatRight => { FloatRight => {
Rect(Point2D(rect.origin.x + rect.size.width - info.size.width, LogicalRect::new(
float_y), self.writing_mode,
Size2D(rect.size.width, height)) rect.start.i + rect.size.inline - info.size.inline,
float_b,
rect.size.inline,
block_size)
} }
} }
} }
// Try to place at the next-lowest location. // Try to place at the next-lowest location.
// Need to be careful of fencepost errors. // 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) | (ClearLeft, FloatLeft) |
(ClearRight, FloatRight) | (ClearRight, FloatRight) |
(ClearBoth, _) => { (ClearBoth, _) => {
let y = self.offset.y + float.bounds.origin.y + float.bounds.size.height; let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
clearance = max(clearance, y); clearance = max(clearance, b);
} }
_ => {} _ => {}
} }

View file

@ -34,7 +34,7 @@ use flow_ref::FlowRef;
use fragment::{Fragment, TableRowFragment, TableCellFragment}; use fragment::{Fragment, TableRowFragment, TableCellFragment};
use incremental::RestyleDamage; use incremental::RestyleDamage;
use inline::InlineFlow; use inline::InlineFlow;
use model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo}; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
use parallel::FlowParallelInfo; use parallel::FlowParallelInfo;
use table_wrapper::TableWrapperFlow; use table_wrapper::TableWrapperFlow;
use table::TableFlow; use table::TableFlow;
@ -46,17 +46,15 @@ use table_cell::TableCellFlow;
use wrapper::ThreadSafeLayoutNode; use wrapper::ThreadSafeLayoutNode;
use collections::dlist::DList; use collections::dlist::DList;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::DisplayList; use gfx::display_list::DisplayList;
use gfx::render_task::RenderLayer; use gfx::render_task::RenderLayer;
use servo_msg::compositor_msg::LayerId; use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::logical_geometry::WritingMode;
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use std::mem; use std::mem;
use std::fmt; use std::fmt;
use std::iter::Zip; use std::iter::Zip;
use std::num::Zero;
use std::sync::atomics::{AtomicUint, Relaxed, SeqCst}; use std::sync::atomics::{AtomicUint, Relaxed, SeqCst};
use std::slice::MutItems; use std::slice::MutItems;
use style::computed_values::{clear, position, text_align}; 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") 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. /// Fails otherwise.
fn col_widths<'a>(&'a mut self) -> &'a mut Vec<Au> { fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
fail!("called col_widths() on an other flow than table-row/table-rowgroup/table") 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. /// Fails otherwise.
fn col_min_widths<'a>(&'a self) -> &'a Vec<Au> { fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_min_widths() on an other flow than table-row/table-rowgroup/table") 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. /// Fails otherwise.
fn col_pref_widths<'a>(&'a self) -> &'a Vec<Au> { fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_pref_widths() on an other flow than table-row/table-rowgroup/table") fail!("called col_pref_inline_sizes() on an other flow than table-row/table-rowgroup/table")
} }
// Main methods // 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 /// 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 widths set. This function /// this flow, all child flows have had their minimum and preferred inline-sizes set. This function
/// must decide minimum/preferred widths based on its children's widths and the dimensions of /// must decide minimum/preferred inline-sizes based on its children's inline-sizes and the dimensions of
/// any boxes it is responsible for flowing. /// any boxes it is responsible for flowing.
fn bubble_widths(&mut self, _ctx: &mut LayoutContext) { fn bubble_inline_sizes(&mut self, _ctx: &mut LayoutContext) {
fail!("bubble_widths not yet implemented") fail!("bubble_inline_sizes not yet implemented")
} }
/// Pass 2 of reflow: computes width. /// Pass 2 of reflow: computes inline-size.
fn assign_widths(&mut self, _ctx: &mut LayoutContext) { fn assign_inline_sizes(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_widths not yet implemented") fail!("assign_inline_sizes not yet implemented")
} }
/// Pass 3a of reflow: computes height. /// Pass 3a of reflow: computes block-size.
fn assign_height(&mut self, _ctx: &mut LayoutContext) { fn assign_block_size(&mut self, _ctx: &mut LayoutContext) {
fail!("assign_height not yet implemented") fail!("assign_block_size not yet implemented")
} }
/// Assigns heights in-order; or, if this is a float, places the float. The default /// Assigns block-sizes 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 /// implementation simply assigns block-sizes if this flow is impacted by floats. Returns true if
/// this child was impacted by floats or false otherwise. /// 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 { -> bool {
let impacted = base(&*self).flags.impacted_by_floats(); let impacted = base(&*self).flags.impacted_by_floats();
if impacted { if impacted {
self.assign_height(layout_context); self.assign_block_size(layout_context);
} }
impacted impacted
} }
@ -193,7 +191,7 @@ pub trait Flow: fmt::Show + ToStr + Share {
false false
} }
fn compute_collapsible_top_margin(&mut self, fn compute_collapsible_block_start_margin(&mut self,
_layout_context: &mut LayoutContext, _layout_context: &mut LayoutContext,
_margin_collapse_info: &mut MarginCollapseInfo) { _margin_collapse_info: &mut MarginCollapseInfo) {
// The default implementation is a no-op. // 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- /// Return the dimensions of the containing block generated by this flow for absolutely-
/// positioned descendants. For block flows, this is the padding box. /// 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") 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) 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. // 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) 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. // 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) bitfield!(FlowFlags, impacted_by_right_floats, set_impacted_by_right_floats, 0b0000_1000)
/// The bitmask of flags that represent the text alignment field. /// The bitmask of flags that represent the text alignment field.
@ -526,14 +524,14 @@ pub struct Descendants {
descendant_links: Vec<FlowRef>, descendant_links: Vec<FlowRef>,
/// Static y offsets of all descendants from the start of this flow box. /// 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 { impl Descendants {
pub fn new() -> Descendants { pub fn new() -> Descendants {
Descendants { Descendants {
descendant_links: Vec::new(), descendant_links: Vec::new(),
static_y_offsets: Vec::new(), static_b_offsets: Vec::new(),
} }
} }
@ -566,7 +564,7 @@ impl Descendants {
let descendant_iter = DescendantIter { let descendant_iter = DescendantIter {
iter: self.descendant_links.mut_slice_from(0).mut_iter(), 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). /// confused with absolutely-positioned flows).
pub struct AbsolutePositionInfo { pub struct AbsolutePositionInfo {
/// The size of the containing block for relatively-positioned descendants. /// 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. /// 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. /// Whether the absolute containing block forces positioned descendants to be layerized.
/// ///
/// FIXME(pcwalton): Move into `FlowFlags`. /// FIXME(pcwalton): Move into `FlowFlags`.
@ -606,12 +604,12 @@ pub struct AbsolutePositionInfo {
} }
impl AbsolutePositionInfo { impl AbsolutePositionInfo {
pub fn new() -> AbsolutePositionInfo { pub fn new(writing_mode: WritingMode) -> AbsolutePositionInfo {
// FIXME(pcwalton): The initial relative containing block size should be equal to the size // FIXME(pcwalton): The initial relative containing block-size should be equal to the size
// of the root layer. // of the root layer.
AbsolutePositionInfo { AbsolutePositionInfo {
relative_containing_block_size: Size2D::zero(), relative_containing_block_size: LogicalSize::zero(writing_mode),
absolute_containing_block_position: Zero::zero(), absolute_containing_block_position: LogicalPoint::zero(writing_mode),
layers_needed_for_positioned_flows: false, layers_needed_for_positioned_flows: false,
} }
} }
@ -634,7 +632,7 @@ pub struct BaseFlow {
/* layout computations */ /* layout computations */
// TODO: min/pref and position are used during disjoint phases of // TODO: min/pref and position are used during disjoint phases of
// layout; maybe combine into a single enum to save space. // 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 /// The upper left corner of the box representing this flow, relative to the box representing
/// its parent flow. /// 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 /// 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 /// for the block direction (usually vertical), this represents the *border box*. For the
/// inline direction (usually horizontal), this represents the *margin box*. /// 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 /// 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. /// pixels of all the display list items for correct invalidation.
pub overflow: Rect<Au>, pub overflow: LogicalRect<Au>,
/// Data used during parallel traversals. /// Data used during parallel traversals.
/// ///
@ -662,7 +660,7 @@ pub struct BaseFlow {
pub collapsible_margins: CollapsibleMargins, pub collapsible_margins: CollapsibleMargins,
/// The position of this flow in page coordinates, computed during display list construction. /// 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 /// 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. /// 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 /// Offset wrt the nearest positioned ancestor - aka the Containing Block
/// for any absolutely positioned elements. /// for any absolutely positioned elements.
pub absolute_static_x_offset: Au, pub absolute_static_i_offset: Au,
/// Offset wrt the Initial Containing Block. /// 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. /// Reference to the Containing Block, if this flow is absolutely positioned.
pub absolute_cb: ContainingBlockLink, 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 /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
/// confused with absolutely-positioned flows). /// 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, pub absolute_position_info: AbsolutePositionInfo,
/// The unflattened display items for this flow. /// The unflattened display items for this flow.
@ -692,6 +690,8 @@ pub struct BaseFlow {
/// Various flags for flows, tightly packed to save space. /// Various flags for flows, tightly packed to save space.
pub flags: FlowFlags, pub flags: FlowFlags,
pub writing_mode: WritingMode,
} }
#[unsafe_destructor] #[unsafe_destructor]
@ -706,6 +706,7 @@ impl Drop for BaseFlow {
impl BaseFlow { impl BaseFlow {
#[inline] #[inline]
pub fn new(node: ThreadSafeLayoutNode) -> BaseFlow { pub fn new(node: ThreadSafeLayoutNode) -> BaseFlow {
let writing_mode = node.style().writing_mode;
BaseFlow { BaseFlow {
ref_count: AtomicUint::new(1), ref_count: AtomicUint::new(1),
@ -715,24 +716,25 @@ impl BaseFlow {
next_sibling: None, next_sibling: None,
prev_sibling: None, prev_sibling: None,
intrinsic_widths: IntrinsicWidths::new(), intrinsic_inline_sizes: IntrinsicISizes::new(),
position: Rect::zero(), position: LogicalRect::zero(writing_mode),
overflow: Rect::zero(), overflow: LogicalRect::zero(writing_mode),
parallel: FlowParallelInfo::new(), parallel: FlowParallelInfo::new(),
floats: Floats::new(), floats: Floats::new(writing_mode),
collapsible_margins: CollapsibleMargins::new(), collapsible_margins: CollapsibleMargins::new(),
abs_position: Point2D(Au::new(0), Au::new(0)), abs_position: LogicalPoint::zero(writing_mode),
abs_descendants: Descendants::new(), abs_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0), absolute_static_i_offset: Au::new(0),
fixed_static_x_offset: Au::new(0), fixed_static_i_offset: Au::new(0),
absolute_cb: ContainingBlockLink::new(), absolute_cb: ContainingBlockLink::new(),
display_list: DisplayList::new(), display_list: DisplayList::new(),
layers: DList::new(), layers: DList::new(),
absolute_position_info: AbsolutePositionInfo::new(), absolute_position_info: AbsolutePositionInfo::new(writing_mode),
flags: FlowFlags::new(), flags: FlowFlags::new(),
writing_mode: writing_mode,
} }
} }
@ -973,14 +975,14 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
continue; continue;
} }
let mut kid_overflow = base(kid).overflow; 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) overflow = overflow.union(&kid_overflow)
} }
// FIXME(#2004, pcwalton): This is wrong for `position: fixed`. // FIXME(#2004, pcwalton): This is wrong for `position: fixed`.
for descendant_link in mut_base(self).abs_descendants.iter() { for descendant_link in mut_base(self).abs_descendants.iter() {
let mut kid_overflow = base(descendant_link).overflow; 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) overflow = overflow.union(&kid_overflow)
} }
} }
@ -1076,7 +1078,7 @@ impl ContainingBlockLink {
} }
#[inline] #[inline]
pub fn generated_containing_block_rect(&mut self) -> Rect<Au> { pub fn generated_containing_block_rect(&mut self) -> LogicalRect<Au> {
match self.link { match self.link {
None => fail!("haven't done it"), None => fail!("haven't done it"),
Some(ref mut link) => link.get_mut().generated_containing_block_rect(), 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."] #[doc = "Currently unused; need to decide how this propagates."]
static Repaint = 0x01, 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 = "Propagates down the flow tree because the computation is"]
#[doc = "bottom-up."] #[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 = "Propagates up the flow tree because the computation is"]
#[doc = "top-down."] #[doc = "top-down."]
static Reflow = 0x04 static Reflow = 0x04
@ -31,7 +31,7 @@ impl RestyleDamage {
/// Elements of self which should also get set on any child flows. /// Elements of self which should also get set on any child flows.
pub fn propagate_down(self) -> RestyleDamage { 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_top_color, get_border.border_right_color,
get_border.border_bottom_color, get_border.border_left_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_top_width, get_border.border_right_width,
get_border.border_bottom_width, get_border.border_left_width, get_border.border_bottom_width, get_border.border_left_width,
get_margin.margin_top, get_margin.margin_right, 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::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use flow; use flow;
use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo}; use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
use model::IntrinsicWidths; use model::IntrinsicISizes;
use model; use model;
use text; use text;
use wrapper::ThreadSafeLayoutNode; use wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf}; use collections::{Deque, RingBuf};
use geom::{Point2D, Rect, SideOffsets2D, Size2D}; use geom::Size2D;
use gfx::display_list::ContentLevel; use gfx::display_list::ContentLevel;
use gfx::font::FontMetrics; use gfx::font::FontMetrics;
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use gfx::text::glyph::CharIndex; use gfx::text::glyph::CharIndex;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::logical_geometry::{LogicalRect, LogicalMargin, LogicalSize};
use servo_util::range; use servo_util::range;
use servo_util::range::{EachIndex, Range, RangeIndex, IntRangeIndex}; use servo_util::range::{EachIndex, Range, RangeIndex, IntRangeIndex};
use std::iter::Enumerate; use std::iter::Enumerate;
@ -57,8 +58,8 @@ use sync::Arc;
/// ///
/// Line fragments also contain some metadata used during line breaking. The /// 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 /// 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 /// with a float or a horizontal wall of the containing block. The block-start
/// left corner of the green zone is the same as that of the line, but /// 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. /// the green zone can be taller and wider than the line itself.
pub struct Line { pub struct Line {
/// A range of line indices that describe line breaks. /// A range of line indices that describe line breaks.
@ -109,16 +110,16 @@ pub struct Line {
/// | v | /// | v |
/// |< - origin.x ->+ - - - - - - - - +---------+---- | /// |< - origin.x ->+ - - - - - - - - +---------+---- |
/// | | | | ^ | /// | | | | ^ |
/// | | | <img> | size.height | /// | | | <img> | size.block-size |
/// | I like truffles, | | v | /// | 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 /// The green zone is the greatest extent from wich a line can extend to
/// before it collides with a float. /// before it collides with a float.
/// ///
@ -140,7 +141,7 @@ pub struct Line {
/// ::: green zone /// ::: green zone
/// FFF float /// FFF float
/// ~~~ /// ~~~
pub green_zone: Size2D<Au> pub green_zone: LogicalSize<Au>
} }
int_range_index! { int_range_index! {
@ -263,22 +264,22 @@ struct LineBreaker {
pub work_list: RingBuf<Fragment>, pub work_list: RingBuf<Fragment>,
pub pending_line: Line, pub pending_line: Line,
pub lines: Vec<Line>, pub lines: Vec<Line>,
pub cur_y: Au, pub cur_b: Au, // Current position on the block direction
} }
impl LineBreaker { impl LineBreaker {
pub fn new(float_ctx: Floats) -> LineBreaker { pub fn new(float_ctx: Floats) -> LineBreaker {
LineBreaker { LineBreaker {
floats: float_ctx,
new_fragments: Vec::new(), new_fragments: Vec::new(),
work_list: RingBuf::new(), work_list: RingBuf::new(),
pending_line: Line { pending_line: Line {
range: Range::empty(), range: Range::empty(),
bounds: Rect(Point2D(Au::new(0), Au::new(0)), Size2D(Au::new(0), Au::new(0))), bounds: LogicalRect::zero(float_ctx.writing_mode),
green_zone: Size2D(Au::new(0), Au::new(0)) green_zone: LogicalSize::zero(float_ctx.writing_mode)
}, },
floats: float_ctx,
lines: Vec::new(), 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."); debug!("Resetting LineBreaker's state for flow.");
self.lines = Vec::new(); self.lines = Vec::new();
self.new_fragments = Vec::new(); self.new_fragments = Vec::new();
self.cur_y = Au(0); self.cur_b = Au(0);
self.reset_line(); self.reset_line();
} }
fn reset_line(&mut self) { fn reset_line(&mut self) {
self.pending_line.range.reset(num::zero(), num::zero()); 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.bounds = LogicalRect::new(
self.pending_line.green_zone = Size2D(Au::new(0), Au::new(0)) 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) { pub fn scan_for_lines(&mut self, flow: &mut InlineFlow) {
@ -340,7 +342,7 @@ impl LineBreaker {
} }
if self.pending_line.range.length() > num::zero() { 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.lines.len());
self.flush_current_line(); self.flush_current_line();
} }
@ -358,45 +360,46 @@ impl LineBreaker {
// clear line and add line mapping // clear line and add line mapping
debug!("LineBreaker: Saving information for flushed line {:u}.", self.lines.len()); debug!("LineBreaker: Saving information for flushed line {:u}.", self.lines.len());
self.lines.push(self.pending_line); 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(); 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. // This might not be the case with some weird text fonts.
fn new_height_for_line(&self, new_fragment: &Fragment) -> Au { fn new_block_size_for_line(&self, new_fragment: &Fragment) -> Au {
let fragment_height = new_fragment.content_height(); let fragment_block_size = new_fragment.content_block_size();
if fragment_height > self.pending_line.bounds.size.height { if fragment_block_size > self.pending_line.bounds.size.block {
fragment_height fragment_block_size
} else { } 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 /// 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 /// 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) 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()); debug!("LineBreaker: Trying to place first fragment of line {}", self.lines.len());
let first_fragment_size = first_fragment.border_box.size; let first_fragment_size = first_fragment.border_box.size;
let splittable = first_fragment.can_split(); let splittable = first_fragment.can_split();
debug!("LineBreaker: fragment size: {}, splittable: {}", first_fragment_size, splittable); debug!("LineBreaker: fragment size: {}, splittable: {}", first_fragment_size, splittable);
// Initally, pretend a splittable fragment has 0 width. // Initally, pretend a splittable fragment has 0 inline-size.
// We will move it later if it has nonzero width // We will move it later if it has nonzero inline-size
// and that causes problems. // and that causes problems.
let placement_width = if splittable { let placement_inline_size = if splittable {
Au::new(0) Au::new(0)
} else { } else {
first_fragment_size.width first_fragment_size.inline
}; };
let info = PlacementInfo { 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, ceiling: ceiling,
max_width: flow.base.position.size.width, max_inline_size: flow.base.position.size.inline,
kind: FloatLeft, kind: FloatLeft,
}; };
@ -407,24 +410,24 @@ impl LineBreaker {
info); info);
// Simple case: if the fragment fits, then we can stop here // 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"); 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 // If not, but we can't split the fragment, then we'll place
// the line here and it will overflow. // the line here and it will overflow.
if !splittable { if !splittable {
debug!("LineBreaker: case=line doesn't fit, but is unsplittable"); 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"); debug!("LineBreaker: used to call split_to_inline_size here");
return (line_bounds, first_fragment_size.width); return (line_bounds, first_fragment_size.inline);
} }
/// Performs float collision avoidance. This is called when adding a fragment is going to increase /// 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: /// We have two options here:
/// 1) Move the entire line so that it doesn't collide any more. /// 1) Move the entire line so that it doesn't collide any more.
@ -440,23 +443,23 @@ impl LineBreaker {
fn avoid_floats(&mut self, fn avoid_floats(&mut self,
in_fragment: Fragment, in_fragment: Fragment,
flow: &InlineFlow, flow: &InlineFlow,
new_height: Au, new_block_size: Au,
line_is_empty: bool) line_is_empty: bool)
-> bool { -> bool {
debug!("LineBreaker: entering float collision avoider!"); debug!("LineBreaker: entering float collision avoider!");
// First predict where the next line is going to be. // First predict where the next line is going to be.
let this_line_y = self.pending_line.bounds.origin.y; let this_line_y = self.pending_line.bounds.start.b;
let (next_line, first_fragment_width) = self.initial_line_placement(&in_fragment, this_line_y, flow); 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 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. // 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"); 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; self.pending_line.green_zone = next_green_zone;
assert!(!line_is_empty, "Non-terminating line breaking"); assert!(!line_is_empty, "Non-terminating line breaking");
@ -478,29 +481,31 @@ impl LineBreaker {
} else { } else {
debug!("LineBreaker: Found a new-line character, so splitting theline."); 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!"); .expect("LineBreaker: This split case makes no sense!");
let writing_mode = self.floats.writing_mode;
// TODO(bjz): Remove fragment splitting // TODO(bjz): Remove fragment splitting
let split_fragment = |split: SplitInfo| { let split_fragment = |split: SplitInfo| {
let info = ScannedTextFragmentInfo::new(run.clone(), split.range); let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
let specific = ScannedTextFragment(info); 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) 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."); to the line.");
let mut left = split_fragment(left); let mut inline_start = split_fragment(inline_start);
left.new_line_pos = vec![]; inline_start.new_line_pos = vec![];
self.push_fragment_to_line(left); self.push_fragment_to_line(inline_start);
for right in right.move_iter() { for inline_end in inline_end.move_iter() {
debug!("LineBreaker: Deferring the fragment to the right of the new-line \ debug!("LineBreaker: Deferring the fragment to the inline_end of the new-line \
character to the line."); character to the line.");
let mut right = split_fragment(right); let mut inline_end = split_fragment(inline_end);
right.new_line_pos = in_fragment.new_line_pos.clone(); inline_end.new_line_pos = in_fragment.new_line_pos.clone();
self.work_list.push_front(right); self.work_list.push_front(inline_end);
} }
false false
} }
@ -511,8 +516,8 @@ impl LineBreaker {
fn try_append_to_line(&mut self, in_fragment: Fragment, flow: &InlineFlow) -> bool { fn try_append_to_line(&mut self, in_fragment: Fragment, flow: &InlineFlow) -> bool {
let line_is_empty = self.pending_line.range.length() == num::zero(); let line_is_empty = self.pending_line.range.length() == num::zero();
if line_is_empty { if line_is_empty {
let (line_bounds, _) = self.initial_line_placement(&in_fragment, self.cur_y, flow); let (line_bounds, _) = self.initial_line_placement(&in_fragment, self.cur_b, flow);
self.pending_line.bounds.origin = line_bounds.origin; self.pending_line.bounds.start = line_bounds.start;
self.pending_line.green_zone = line_bounds.size; self.pending_line.green_zone = line_bounds.size;
} }
@ -525,22 +530,22 @@ impl LineBreaker {
let green_zone = self.pending_line.green_zone; let green_zone = self.pending_line.green_zone;
// NB: At this point, if `green_zone.width < self.pending_line.bounds.size.width` or // NB: At this point, if `green_zone.inline-size < self.pending_line.bounds.size.inline-size` or
// `green_zone.height < self.pending_line.bounds.size.height`, then we committed a line // `green_zone.block-size < self.pending_line.bounds.size.block-size`, then we committed a line
// that overlaps with floats. // that overlaps with floats.
let new_height = self.new_height_for_line(&in_fragment); let new_block_size = self.new_block_size_for_line(&in_fragment);
if new_height > green_zone.height { if new_block_size > green_zone.block {
// Uh-oh. Float collision imminent. Enter the float collision avoider // 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 // 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 // horizontally. We'll try to place the whole fragment on this line and break somewhere if it
// doesn't fit. // doesn't fit.
let new_width = self.pending_line.bounds.size.width + in_fragment.border_box.size.width; let new_inline_size = self.pending_line.bounds.size.inline + in_fragment.border_box.size.inline;
if new_width <= green_zone.width { if new_inline_size <= green_zone.inline {
debug!("LineBreaker: case=fragment fits without splitting"); debug!("LineBreaker: case=fragment fits without splitting");
self.push_fragment_to_line(in_fragment); self.push_fragment_to_line(in_fragment);
return true return true
@ -557,19 +562,20 @@ impl LineBreaker {
} }
} }
let available_width = green_zone.width - self.pending_line.bounds.size.width; let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline;
let split = in_fragment.find_split_info_for_width(CharIndex(0), available_width, line_is_empty); let split = in_fragment.find_split_info_for_inline_size(CharIndex(0), available_inline_size, line_is_empty);
match split.map(|(left, right, run)| { match split.map(|(inline_start, inline_end, run)| {
// TODO(bjz): Remove fragment splitting // TODO(bjz): Remove fragment splitting
let split_fragment = |split: SplitInfo| { let split_fragment = |split: SplitInfo| {
let info = ScannedTextFragmentInfo::new(run.clone(), split.range); let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
let specific = ScannedTextFragment(info); 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) in_fragment.transform(size, specific)
}; };
(left.map(|x| { debug!("LineBreaker: Left split {}", x); split_fragment(x) }), (inline_start.map(|x| { debug!("LineBreaker: Left split {}", x); split_fragment(x) }),
right.map(|x| { debug!("LineBreaker: Right split {}", x); split_fragment(x) })) inline_end.map(|x| { debug!("LineBreaker: Right split {}", x); split_fragment(x) }))
}) { }) {
None => { None => {
debug!("LineBreaker: Tried to split unsplittable render fragment! Deferring to next \ debug!("LineBreaker: Tried to split unsplittable render fragment! Deferring to next \
@ -577,21 +583,21 @@ impl LineBreaker {
self.work_list.push_front(in_fragment); self.work_list.push_front(in_fragment);
false false
}, },
Some((Some(left_fragment), Some(right_fragment))) => { Some((Some(inline_start_fragment), Some(inline_end_fragment))) => {
debug!("LineBreaker: Line break found! Pushing left fragment to line and deferring \ debug!("LineBreaker: Line break found! Pushing inline_start fragment to line and deferring \
right fragment to next line."); inline_end fragment to next line.");
self.push_fragment_to_line(left_fragment); self.push_fragment_to_line(inline_start_fragment);
self.work_list.push_front(right_fragment); self.work_list.push_front(inline_end_fragment);
true true
}, },
Some((Some(left_fragment), None)) => { Some((Some(inline_start_fragment), None)) => {
debug!("LineBreaker: Pushing left fragment to line."); debug!("LineBreaker: Pushing inline_start fragment to line.");
self.push_fragment_to_line(left_fragment); self.push_fragment_to_line(inline_start_fragment);
true true
}, },
Some((None, Some(right_fragment))) => { Some((None, Some(inline_end_fragment))) => {
debug!("LineBreaker: Pushing right fragment to line."); debug!("LineBreaker: Pushing inline_end fragment to line.");
self.push_fragment_to_line(right_fragment); self.push_fragment_to_line(inline_end_fragment);
true true
}, },
Some((None, None)) => { Some((None, None)) => {
@ -619,10 +625,10 @@ impl LineBreaker {
fragment_index: FragmentIndex(1), fragment_index: FragmentIndex(1),
char_index: CharIndex(0) /* unused for now */ , char_index: CharIndex(0) /* unused for now */ ,
}); });
self.pending_line.bounds.size.width = self.pending_line.bounds.size.width + self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline +
fragment.border_box.size.width; fragment.border_box.size.inline;
self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height, self.pending_line.bounds.size.block = Au::max(self.pending_line.bounds.size.block,
fragment.border_box.size.height); fragment.border_box.size.block);
self.new_fragments.push(fragment); self.new_fragments.push(fragment);
} }
} }
@ -901,11 +907,11 @@ pub struct InlineFlow {
/// lines. /// lines.
pub lines: Vec<Line>, 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. /// 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. /// font style.
pub minimum_depth_below_baseline: Au, pub minimum_depth_below_baseline: Au,
} }
@ -916,14 +922,18 @@ impl InlineFlow {
base: BaseFlow::new(node), base: BaseFlow::new(node),
fragments: fragments, fragments: fragments,
lines: Vec::new(), lines: Vec::new(),
minimum_height_above_baseline: Au(0), minimum_block_size_above_baseline: Au(0),
minimum_depth_below_baseline: Au(0), minimum_depth_below_baseline: Au(0),
} }
} }
pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) { pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) {
let abs_rect = Rect(self.base.abs_position, self.base.position.size); let abs_rect = LogicalRect::from_point_size(
if !abs_rect.intersects(&layout_context.dirty) { 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 return
} }
@ -949,31 +959,31 @@ impl InlineFlow {
// For now, don't traverse the subtree rooted here. // 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 /// 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. /// 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 `top` or `bottom` value, true is returned. /// That is, if the box has a `block-start` or `block-end` value, true is returned.
fn distance_from_baseline(fragment: &Fragment, fn distance_from_baseline(fragment: &Fragment,
ascent: Au, ascent: Au,
parent_text_top: Au, parent_text_block_start: Au,
parent_text_bottom: Au, parent_text_block_end: Au,
height_above_baseline: &mut Au, block_size_above_baseline: &mut Au,
depth_below_baseline: &mut Au, depth_below_baseline: &mut Au,
largest_height_for_top_fragments: &mut Au, largest_block_size_for_top_fragments: &mut Au,
largest_height_for_bottom_fragments: &mut Au) largest_block_size_for_bottom_fragments: &mut Au)
-> (Au, bool) { -> (Au, bool) {
match fragment.vertical_align() { match fragment.vertical_align() {
vertical_align::baseline => (-ascent, false), vertical_align::baseline => (-ascent, false),
vertical_align::middle => { vertical_align::middle => {
// TODO: x-height value should be used from font info. // TODO: x-block-size value should be used from font info.
let xheight = Au(0); let xblock_size = Au(0);
let fragment_height = fragment.content_height(); let fragment_block_size = fragment.content_block_size();
let offset_top = -(xheight + fragment_height).scale_by(0.5); let offset_block_start = -(xblock_size + fragment_block_size).scale_by(0.5);
*height_above_baseline = offset_top.scale_by(-1.0); *block_size_above_baseline = offset_block_start.scale_by(-1.0);
*depth_below_baseline = fragment_height - *height_above_baseline; *depth_below_baseline = fragment_block_size - *block_size_above_baseline;
(offset_top, false) (offset_block_start, false)
}, },
vertical_align::sub => { vertical_align::sub => {
// TODO: The proper position for subscripts should be used. Lower the baseline to // TODO: The proper position for subscripts should be used. Lower the baseline to
@ -988,30 +998,30 @@ impl InlineFlow {
(-super_offset - ascent, false) (-super_offset - ascent, false)
}, },
vertical_align::text_top => { 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; let prev_depth_below_baseline = *depth_below_baseline;
*height_above_baseline = parent_text_top; *block_size_above_baseline = parent_text_block_start;
*depth_below_baseline = fragment_height - *height_above_baseline; *depth_below_baseline = fragment_block_size - *block_size_above_baseline;
(*depth_below_baseline - prev_depth_below_baseline - ascent, false) (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
}, },
vertical_align::text_bottom => { 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; let prev_depth_below_baseline = *depth_below_baseline;
*depth_below_baseline = parent_text_bottom; *depth_below_baseline = parent_text_block_end;
*height_above_baseline = fragment_height - *depth_below_baseline; *block_size_above_baseline = fragment_block_size - *depth_below_baseline;
(*depth_below_baseline - prev_depth_below_baseline - ascent, false) (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
}, },
vertical_align::top => { vertical_align::top => {
*largest_height_for_top_fragments = *largest_block_size_for_top_fragments =
Au::max(*largest_height_for_top_fragments, Au::max(*largest_block_size_for_top_fragments,
*height_above_baseline + *depth_below_baseline); *block_size_above_baseline + *depth_below_baseline);
let offset_top = *height_above_baseline - ascent; let offset_top = *block_size_above_baseline - ascent;
(offset_top, true) (offset_top, true)
}, },
vertical_align::bottom => { vertical_align::bottom => {
*largest_height_for_bottom_fragments = *largest_block_size_for_bottom_fragments =
Au::max(*largest_height_for_bottom_fragments, Au::max(*largest_block_size_for_bottom_fragments,
*height_above_baseline + *depth_below_baseline); *block_size_above_baseline + *depth_below_baseline);
let offset_bottom = -(*depth_below_baseline + ascent); let offset_bottom = -(*depth_below_baseline + ascent);
(offset_bottom, true) (offset_bottom, true)
}, },
@ -1029,26 +1039,28 @@ impl InlineFlow {
fn set_horizontal_fragment_positions(fragments: &mut InlineFragments, fn set_horizontal_fragment_positions(fragments: &mut InlineFragments,
line: &Line, line: &Line,
line_align: text_align::T) { line_align: text_align::T) {
// Figure out how much width we have. // Figure out how much inline-size we have.
let slack_width = Au::max(Au(0), line.green_zone.width - line.bounds.size.width); 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. // 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 { offset_x = offset_x + match line_align {
// So sorry, but justified text is more complicated than shuffling line // So sorry, but justified text is more complicated than shuffling line
// coordinates. // coordinates.
// //
// TODO(burg, issue #213): Implement `text-align: justify`. // TODO(burg, issue #213): Implement `text-align: justify`.
text_align::left | text_align::justify => Au(0), text_align::left | text_align::justify => Au(0),
text_align::center => slack_width.scale_by(0.5), text_align::center => slack_inline_size.scale_by(0.5),
text_align::right => slack_width, text_align::right => slack_inline_size,
}; };
for i in each_fragment_index(&line.range) { for i in each_fragment_index(&line.range) {
let fragment = fragments.get_mut(i.to_uint()); let fragment = fragments.get_mut(i.to_uint());
let size = fragment.border_box.size; let size = fragment.border_box.size;
fragment.border_box = Rect(Point2D(offset_x, fragment.border_box.origin.y), size); fragment.border_box = LogicalRect::new(
offset_x = offset_x + size.width; 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 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 line_height = text::line_height_from_style(style, style.get_font().font_size);
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height); 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 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() { 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() { for (fragment, context) in self.fragments.mut_iter() {
debug!("Flow: measuring {}", *fragment); debug!("Flow: measuring {}", *fragment);
let fragment_intrinsic_widths = fragment.intrinsic_widths(Some(context)); let fragment_intrinsic_inline_sizes =
intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width, fragment.intrinsic_inline_sizes(Some(context));
fragment_intrinsic_widths.minimum_width); intrinsic_inline_sizes.minimum_inline_size = geometry::max(
intrinsic_widths.preferred_width = intrinsic_widths.preferred_width + intrinsic_inline_sizes.minimum_inline_size,
fragment_intrinsic_widths.preferred_width; 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 /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
/// on this context, the context has had its width set by the parent context. /// on this context, the context has had its inline-size set by the parent context.
fn assign_widths(&mut self, _: &mut LayoutContext) { fn assign_inline_sizes(&mut self, _: &mut LayoutContext) {
// Initialize content fragment widths if they haven't been initialized already. // 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`. // 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; let this = &mut *self;
for (fragment, context) in this.fragments.mut_iter() { 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)) Some(context))
} }
} }
@ -1123,30 +1139,30 @@ impl Flow for InlineFlow {
// There are no child contexts, so stop here. // There are no child contexts, so stop here.
// TODO(Issue #225): once there are 'inline-block' elements, this won't be // TODO(Issue #225): once there are 'inline-block' elements, this won't be
// true. In that case, set the InlineBlockFragment's width to the // true. In that case, set the InlineBlockFragment's inline-size to the
// shrink-to-fit width, perform inline flow, and set the block // shrink-to-fit inline-size, perform inline flow, and set the block
// flow context's width as the assigned width of the // flow context's inline-size as the assigned inline-size of the
// 'inline-block' fragment that created this flow before recursing. // 'inline-block' fragment that created this flow before recursing.
} }
/// Calculate and set the height of this flow. See CSS 2.1 § 10.6.1. /// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
fn assign_height(&mut self, _: &mut LayoutContext) { fn assign_block_size(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow"); debug!("assign_block_size_inline: assigning block_size for flow");
// Divide the fragments into lines. // Divide the fragments into lines.
// //
// TODO(#226): Get the CSS `line-height` property from the containing block's style to // TODO(#226): Get the CSS `line-block-size` property from the containing block's style to
// determine minimum line height. // determine minimum line block-size.
// //
// TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to // TODO(#226): Get the CSS `line-block-size` property from each non-replaced inline element to
// determine its height for computing line height. // determine its block-size for computing line block-size.
// //
// TODO(pcwalton): Cache the line scanner? // 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() { 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(); let scanner_floats = self.base.floats.clone();
@ -1157,29 +1173,29 @@ impl Flow for InlineFlow {
let text_align = self.base.flags.text_align(); let text_align = self.base.flags.text_align();
// Now, go through each line and lay out the fragments inside. // 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() { for line in self.lines.mut_iter() {
// Lay out fragments horizontally. // Lay out fragments horizontally.
InlineFlow::set_horizontal_fragment_positions(&mut self.fragments, line, text_align); 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_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. // Calculate the distance from the baseline to the block-start and block-end of the line.
let mut largest_height_above_baseline = self.minimum_height_above_baseline; let mut largest_block_size_above_baseline = self.minimum_block_size_above_baseline;
let mut largest_depth_below_baseline = self.minimum_depth_below_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. // 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)); (Au(0), Au(0));
for fragment_i in each_fragment_index(&line.range) { for fragment_i in each_fragment_index(&line.range) {
let fragment = self.fragments.fragments.get_mut(fragment_i.to_uint()); let fragment = self.fragments.fragments.get_mut(fragment_i.to_uint());
let InlineMetrics { 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, depth_below_baseline: mut depth_below_baseline,
ascent ascent
} = fragment.inline_metrics(); } = fragment.inline_metrics();
@ -1189,7 +1205,7 @@ impl Flow for InlineFlow {
// "Content area" is defined in CSS 2.1 § 10.6.1. // "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 // 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. // parent's content area.
// We should calculate the distance from baseline to the top of parent's content // 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. // content area. But for now we assume it's zero.
let parent_text_bottom = Au(0); 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 // The no-update flag decides whether `largest_block-size_for_top_fragments` and
// `largest_height_for_bottom_fragments` are to be updated or not. This will be set // `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`. // if and only if the fragment has `vertical-align` set to `top` or `bottom`.
let (distance_from_baseline, no_update_flag) = let (distance_from_baseline, no_update_flag) =
InlineFlow::distance_from_baseline( InlineFlow::distance_from_baseline(
@ -1214,77 +1230,78 @@ impl Flow for InlineFlow {
ascent, ascent,
parent_text_top, parent_text_top,
parent_text_bottom, parent_text_bottom,
&mut height_above_baseline, &mut block_size_above_baseline,
&mut depth_below_baseline, &mut depth_below_baseline,
&mut largest_height_for_top_fragments, &mut largest_block_size_for_top_fragments,
&mut largest_height_for_bottom_fragments); &mut largest_block_size_for_bottom_fragments);
// Unless the current fragment has `vertical-align` set to `top` or `bottom`, // 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 { if !no_update_flag {
largest_height_above_baseline = Au::max(height_above_baseline, largest_block_size_above_baseline = Au::max(block_size_above_baseline,
largest_height_above_baseline); largest_block_size_above_baseline);
largest_depth_below_baseline = Au::max(depth_below_baseline, largest_depth_below_baseline = Au::max(depth_below_baseline,
largest_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. // 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 // 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`. // value for `bottom`. Then, if necessary, update `largest_block-size_above_baseline`.
largest_height_above_baseline = largest_block_size_above_baseline =
Au::max(largest_height_above_baseline, Au::max(largest_block_size_above_baseline,
largest_height_for_bottom_fragments - largest_depth_below_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 // 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`. // for `top`. Then, if necessary, update `largest_depth_below_baseline`.
largest_depth_below_baseline = largest_depth_below_baseline =
Au::max(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 // Now, the distance from the logical block-start of the line to the baseline can be
// computed as `largest_height_above_baseline`. // computed as `largest_block-size_above_baseline`.
let baseline_distance_from_top = largest_height_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 // 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) { for fragment_i in each_fragment_index(&line.range) {
let fragment = self.fragments.get_mut(fragment_i.to_uint()); let fragment = self.fragments.get_mut(fragment_i.to_uint());
match fragment.vertical_align() { match fragment.vertical_align() {
vertical_align::top => { vertical_align::top => {
fragment.border_box.origin.y = fragment.border_box.origin.y + fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_top line_distance_from_flow_block_start
} }
vertical_align::bottom => { vertical_align::bottom => {
fragment.border_box.origin.y = fragment.border_box.origin.y + fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_top + baseline_distance_from_top + line_distance_from_flow_block_start + baseline_distance_from_block_start +
largest_depth_below_baseline largest_depth_below_baseline
} }
_ => { _ => {
fragment.border_box.origin.y = fragment.border_box.origin.y + fragment.border_box.start.b = fragment.border_box.start.b +
line_distance_from_flow_top + baseline_distance_from_top 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. // This is used to set the block-start y position of the next line in the next loop.
line.bounds.size.height = largest_height_above_baseline + largest_depth_below_baseline; line.bounds.size.block = largest_block_size_above_baseline + largest_depth_below_baseline;
line_distance_from_flow_top = line_distance_from_flow_top + line.bounds.size.height; line_distance_from_flow_block_start = line_distance_from_flow_block_start + line.bounds.size.block;
} // End of `lines.each` loop. } // End of `lines.each` loop.
self.base.position.size.height = self.base.position.size.block =
if self.lines.len() > 0 { if self.lines.len() > 0 {
self.lines.as_slice().last().get_ref().bounds.origin.y + self.lines.as_slice().last().get_ref().bounds.start.b +
self.lines.as_slice().last().get_ref().bounds.size.height self.lines.as_slice().last().get_ref().bounds.size.block
} else { } else {
Au::new(0) Au::new(0)
}; };
self.base.floats = scanner.floats(); 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. /// Returns the dimensions of the border in this fragment range.
pub fn border(&self) -> SideOffsets2D<Au> { #[inline]
model::border_from_style(&*self.style) pub fn border(&self) -> LogicalMargin<Au> {
self.style.logical_border_width()
} }
/// Returns the dimensions of the padding in this fragment range. /// 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? // FIXME(#2266, pcwalton): Is Au(0) right here for the containing block?
model::padding_from_style(&*self.style, Au(0)) 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. /// 10.8.1.
pub struct InlineMetrics { pub struct InlineMetrics {
pub height_above_baseline: Au, pub block_size_above_baseline: Au,
pub depth_below_baseline: Au, pub depth_below_baseline: Au,
pub ascent: Au, pub ascent: Au,
} }
impl InlineMetrics { 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] #[inline]
pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics { pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
let leading = line_height - (font_metrics.ascent + font_metrics.descent); let leading = line_height - (font_metrics.ascent + font_metrics.descent);
InlineMetrics { 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), depth_below_baseline: font_metrics.descent + leading.scale_by(0.5),
ascent: font_metrics.ascent, 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 /// The bubble-inline-sizes traversal, the first part of layout computation. This computes preferred
/// and intrinsic widths and bubbles them up the tree. /// and intrinsic inline-sizes and bubbles them up the tree.
pub struct BubbleWidthsTraversal<'a> { pub struct BubbleISizesTraversal<'a> {
pub layout_context: &'a mut LayoutContext, pub layout_context: &'a mut LayoutContext,
} }
impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> { impl<'a> PostorderFlowTraversal for BubbleISizesTraversal<'a> {
#[inline] #[inline]
fn process(&mut self, flow: &mut Flow) -> bool { fn process(&mut self, flow: &mut Flow) -> bool {
flow.bubble_widths(self.layout_context); flow.bubble_inline_sizes(self.layout_context);
true true
} }
@ -188,35 +188,35 @@ impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> {
/* /*
#[inline] #[inline]
fn should_prune(&mut self, flow: &mut Flow) -> bool { 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`. /// The assign-inline-sizes traversal. In Gecko this corresponds to `Reflow`.
pub struct AssignWidthsTraversal<'a> { pub struct AssignISizesTraversal<'a> {
pub layout_context: &'a mut LayoutContext, pub layout_context: &'a mut LayoutContext,
} }
impl<'a> PreorderFlowTraversal for AssignWidthsTraversal<'a> { impl<'a> PreorderFlowTraversal for AssignISizesTraversal<'a> {
#[inline] #[inline]
fn process(&mut self, flow: &mut Flow) -> bool { fn process(&mut self, flow: &mut Flow) -> bool {
flow.assign_widths(self.layout_context); flow.assign_inline_sizes(self.layout_context);
true true
} }
} }
/// The assign-heights-and-store-overflow traversal, the last (and most expensive) part of layout /// The assign-block-sizes-and-store-overflow traversal, the last (and most expensive) part of layout
/// computation. Determines the final heights for all layout objects, computes positions, and /// computation. Determines the final block-sizes for all layout objects, computes positions, and
/// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`. /// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`.
pub struct AssignHeightsAndStoreOverflowTraversal<'a> { pub struct AssignBSizesAndStoreOverflowTraversal<'a> {
pub layout_context: &'a mut LayoutContext, pub layout_context: &'a mut LayoutContext,
} }
impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> { impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflowTraversal<'a> {
#[inline] #[inline]
fn process(&mut self, flow: &mut Flow) -> bool { 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 // Skip store-overflow for absolutely positioned flows. That will be
// done in a separate traversal. // done in a separate traversal.
if !flow.is_store_overflow_delayed() { if !flow.is_store_overflow_delayed() {
@ -482,8 +482,8 @@ impl LayoutTask {
fn solve_constraints(&mut self, fn solve_constraints(&mut self,
layout_root: &mut Flow, layout_root: &mut Flow,
layout_context: &mut LayoutContext) { layout_context: &mut LayoutContext) {
if layout_context.opts.bubble_widths_separately { if layout_context.opts.bubble_inline_sizes_separately {
let mut traversal = BubbleWidthsTraversal { let mut traversal = BubbleISizesTraversal {
layout_context: layout_context, layout_context: layout_context,
}; };
layout_root.traverse_postorder(&mut traversal); layout_root.traverse_postorder(&mut traversal);
@ -495,7 +495,7 @@ impl LayoutTask {
// NOTE: this currently computes borders, so any pruning should separate that operation // NOTE: this currently computes borders, so any pruning should separate that operation
// out. // out.
{ {
let mut traversal = AssignWidthsTraversal { let mut traversal = AssignISizesTraversal {
layout_context: layout_context, layout_context: layout_context,
}; };
layout_root.traverse_preorder(&mut traversal); layout_root.traverse_preorder(&mut traversal);
@ -503,7 +503,7 @@ impl LayoutTask {
// FIXME(pcwalton): Prune this pass as well. // FIXME(pcwalton): Prune this pass as well.
{ {
let mut traversal = AssignHeightsAndStoreOverflowTraversal { let mut traversal = AssignBSizesAndStoreOverflowTraversal {
layout_context: layout_context, layout_context: layout_context,
}; };
layout_root.traverse_postorder(&mut traversal); layout_root.traverse_postorder(&mut traversal);
@ -518,8 +518,8 @@ impl LayoutTask {
fn solve_constraints_parallel(&mut self, fn solve_constraints_parallel(&mut self,
layout_root: &mut FlowRef, layout_root: &mut FlowRef,
layout_context: &mut LayoutContext) { layout_context: &mut LayoutContext) {
if layout_context.opts.bubble_widths_separately { if layout_context.opts.bubble_inline_sizes_separately {
let mut traversal = BubbleWidthsTraversal { let mut traversal = BubbleISizesTraversal {
layout_context: layout_context, layout_context: layout_context,
}; };
layout_root.get_mut().traverse_postorder(&mut traversal); 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. // Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay { if data.goal == ReflowForDisplay {
let writing_mode = flow::base(layout_root.get()).writing_mode;
profile(time::LayoutDispListBuildCategory, self.time_profiler_chan.clone(), || { 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 { match self.parallel_traversal {
None => { 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, let root_size = Size2D(root_size.width.to_nearest_px() as uint,
root_size.height.to_nearest_px() as uint); root_size.height.to_nearest_px() as uint);
let render_layer = RenderLayer { 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 style::ComputedValues;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::logical_geometry::LogicalMargin;
use std::fmt; use std::fmt;
/// A collapsible margin. See CSS 2.1 § 8.3.1. /// 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 { pub enum CollapsibleMargins {
/// Margins may not collapse with this flow. /// Margins may not collapse with this flow.
NoCollapsibleMargins(Au, Au), 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. /// margins do not collapse through this flow.
MarginsCollapse(AdjoiningMargins, AdjoiningMargins), MarginsCollapse(AdjoiningMargins, AdjoiningMargins),
@ -85,7 +86,7 @@ enum FinalMarginState {
pub struct MarginCollapseInfo { pub struct MarginCollapseInfo {
pub state: MarginCollapseState, pub state: MarginCollapseState,
pub top_margin: AdjoiningMargins, pub block_start_margin: AdjoiningMargins,
pub margin_in: AdjoiningMargins, pub margin_in: AdjoiningMargins,
} }
@ -94,42 +95,42 @@ impl MarginCollapseInfo {
pub fn new() -> MarginCollapseInfo { pub fn new() -> MarginCollapseInfo {
MarginCollapseInfo { MarginCollapseInfo {
state: AccumulatingCollapsibleTopMargin, state: AccumulatingCollapsibleTopMargin,
top_margin: AdjoiningMargins::new(), block_start_margin: AdjoiningMargins::new(),
margin_in: AdjoiningMargins::new(), margin_in: AdjoiningMargins::new(),
} }
} }
pub fn initialize_top_margin(&mut self, pub fn initialize_block_start_margin(&mut self,
fragment: &Fragment, fragment: &Fragment,
can_collapse_top_margin_with_kids: bool) { can_collapse_block_start_margin_with_kids: bool) {
if !can_collapse_top_margin_with_kids { if !can_collapse_block_start_margin_with_kids {
self.state = AccumulatingMarginIn 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, pub fn finish_and_compute_collapsible_margins(mut self,
fragment: &Fragment, fragment: &Fragment,
can_collapse_bottom_margin_with_kids: bool) can_collapse_block_end_margin_with_kids: bool)
-> (CollapsibleMargins, Au) { -> (CollapsibleMargins, Au) {
let state = match self.state { let state = match self.state {
AccumulatingCollapsibleTopMargin => { AccumulatingCollapsibleTopMargin => {
match fragment.style().get_box().height { match fragment.style().content_block_size() {
LPA_Auto | LPA_Length(Au(0)) | LPA_Percentage(0.) => { 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.) => { LP_Length(Au(0)) | LP_Percentage(0.) => {
MarginsCollapseThroughFinalMarginState 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. // collapse through it.
BottomMarginCollapsesFinalMarginState 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. // collapse through it.
BottomMarginCollapsesFinalMarginState BottomMarginCollapsesFinalMarginState
} }
@ -138,31 +139,31 @@ impl MarginCollapseInfo {
AccumulatingMarginIn => BottomMarginCollapsesFinalMarginState, 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. // margin with its children.
let bottom_margin = fragment.margin.bottom; let block_end_margin = fragment.margin.block_end;
if !can_collapse_bottom_margin_with_kids { if !can_collapse_block_end_margin_with_kids {
match state { match state {
MarginsCollapseThroughFinalMarginState => { MarginsCollapseThroughFinalMarginState => {
let advance = self.top_margin.collapse(); let advance = self.block_start_margin.collapse();
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin)); self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapse(self.top_margin, self.margin_in), advance) (MarginsCollapse(self.block_start_margin, self.margin_in), advance)
} }
BottomMarginCollapsesFinalMarginState => { BottomMarginCollapsesFinalMarginState => {
let advance = self.margin_in.collapse(); let advance = self.margin_in.collapse();
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin)); self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapse(self.top_margin, self.margin_in), advance) (MarginsCollapse(self.block_start_margin, self.margin_in), advance)
} }
} }
} else { } else {
match state { match state {
MarginsCollapseThroughFinalMarginState => { MarginsCollapseThroughFinalMarginState => {
self.top_margin.union(AdjoiningMargins::from_margin(bottom_margin)); self.block_start_margin.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapseThrough(self.top_margin), Au(0)) (MarginsCollapseThrough(self.block_start_margin), Au(0))
} }
BottomMarginCollapsesFinalMarginState => { BottomMarginCollapsesFinalMarginState => {
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin)); self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
(MarginsCollapse(self.top_margin, self.margin_in), Au(0)) (MarginsCollapse(self.block_start_margin, self.margin_in), Au(0))
} }
} }
} }
@ -170,66 +171,66 @@ impl MarginCollapseInfo {
pub fn current_float_ceiling(&mut self) -> Au { pub fn current_float_ceiling(&mut self) -> Au {
match self.state { match self.state {
AccumulatingCollapsibleTopMargin => self.top_margin.collapse(), AccumulatingCollapsibleTopMargin => self.block_start_margin.collapse(),
AccumulatingMarginIn => self.margin_in.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 /// 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. /// 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) { match (self.state, *child_collapsible_margins) {
(AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(top, _)) => { (AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(block_start, _)) => {
self.state = AccumulatingMarginIn; self.state = AccumulatingMarginIn;
top block_start
} }
(AccumulatingCollapsibleTopMargin, MarginsCollapse(top, _)) => { (AccumulatingCollapsibleTopMargin, MarginsCollapse(block_start, _)) => {
self.top_margin.union(top); self.block_start_margin.union(block_start);
self.state = AccumulatingMarginIn; self.state = AccumulatingMarginIn;
Au(0) Au(0)
} }
(AccumulatingMarginIn, NoCollapsibleMargins(top, _)) => { (AccumulatingMarginIn, NoCollapsibleMargins(block_start, _)) => {
let previous_margin_value = self.margin_in.collapse(); let previous_margin_value = self.margin_in.collapse();
self.margin_in = AdjoiningMargins::new(); self.margin_in = AdjoiningMargins::new();
previous_margin_value + top previous_margin_value + block_start
} }
(AccumulatingMarginIn, MarginsCollapse(top, _)) => { (AccumulatingMarginIn, MarginsCollapse(block_start, _)) => {
self.margin_in.union(top); self.margin_in.union(block_start);
let margin_value = self.margin_in.collapse(); let margin_value = self.margin_in.collapse();
self.margin_in = AdjoiningMargins::new(); self.margin_in = AdjoiningMargins::new();
margin_value margin_value
} }
(_, MarginsCollapseThrough(_)) => { (_, 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) 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 /// 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. /// 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) { match (self.state, *child_collapsible_margins) {
(AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(..)) | (AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(..)) |
(AccumulatingCollapsibleTopMargin, MarginsCollapse(..)) => { (AccumulatingCollapsibleTopMargin, MarginsCollapse(..)) => {
// Can't happen because the state will have been replaced with // Can't happen because the state will have been replaced with
// `AccumulatingMarginIn` above. // `AccumulatingMarginIn` above.
fail!("should not be accumulating collapsible top margins anymore!") fail!("should not be accumulating collapsible block_start margins anymore!")
} }
(AccumulatingCollapsibleTopMargin, MarginsCollapseThrough(margin)) => { (AccumulatingCollapsibleTopMargin, MarginsCollapseThrough(margin)) => {
self.top_margin.union(margin); self.block_start_margin.union(margin);
Au(0) Au(0)
} }
(AccumulatingMarginIn, NoCollapsibleMargins(_, bottom)) => { (AccumulatingMarginIn, NoCollapsibleMargins(_, block_end)) => {
assert_eq!(self.margin_in.most_positive, Au(0)); assert_eq!(self.margin_in.most_positive, Au(0));
assert_eq!(self.margin_in.most_negative, Au(0)); assert_eq!(self.margin_in.most_negative, Au(0));
bottom block_end
} }
(AccumulatingMarginIn, MarginsCollapse(_, bottom)) | (AccumulatingMarginIn, MarginsCollapse(_, block_end)) |
(AccumulatingMarginIn, MarginsCollapseThrough(bottom)) => { (AccumulatingMarginIn, MarginsCollapseThrough(block_end)) => {
self.margin_in.union(bottom); self.margin_in.union(block_end);
Au(0) Au(0)
} }
} }
@ -241,38 +242,38 @@ pub enum MarginCollapseState {
AccumulatingMarginIn, AccumulatingMarginIn,
} }
/// Intrinsic widths, which consist of minimum and preferred. /// Intrinsic inline-sizes, which consist of minimum and preferred.
pub struct IntrinsicWidths { pub struct IntrinsicISizes {
/// The *minimum width* of the content. /// The *minimum inline-size* of the content.
pub minimum_width: Au, pub minimum_inline_size: Au,
/// The *preferred width* of the content. /// The *preferred inline-size* of the content.
pub preferred_width: Au, pub preferred_inline_size: Au,
/// The estimated sum of borders, padding, and margins. Some calculations use this information /// The estimated sum of borders, padding, and margins. Some calculations use this information
/// when computing intrinsic widths. /// when computing intrinsic inline-sizes.
pub surround_width: Au, pub surround_inline_size: Au,
} }
impl fmt::Show for IntrinsicWidths { impl fmt::Show for IntrinsicISizes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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 { impl IntrinsicISizes {
pub fn new() -> IntrinsicWidths { pub fn new() -> IntrinsicISizes {
IntrinsicWidths { IntrinsicISizes {
minimum_width: Au(0), minimum_inline_size: Au(0),
preferred_width: Au(0), preferred_inline_size: Au(0),
surround_width: Au(0), surround_inline_size: Au(0),
} }
} }
pub fn total_minimum_width(&self) -> Au { pub fn total_minimum_inline_size(&self) -> Au {
self.minimum_width + self.surround_width self.minimum_inline_size + self.surround_inline_size
} }
pub fn total_preferred_width(&self) -> Au { pub fn total_preferred_inline_size(&self) -> Au {
self.preferred_width + self.surround_width self.preferred_inline_size + self.surround_inline_size
} }
} }
@ -323,21 +324,13 @@ pub fn specified(length: computed::LengthOrPercentage, containing_length: Au) ->
} }
#[inline] #[inline]
pub fn border_from_style(style: &ComputedValues) -> SideOffsets2D<Au> { pub fn padding_from_style(style: &ComputedValues, containing_block_inline_size: Au)
let border_style = style.get_border(); -> LogicalMargin<Au> {
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> {
let padding_style = style.get_padding(); let padding_style = style.get_padding();
SideOffsets2D::new(specified(padding_style.padding_top, containing_block_width), LogicalMargin::from_physical(style.writing_mode, SideOffsets2D::new(
specified(padding_style.padding_right, containing_block_width), specified(padding_style.padding_top, containing_block_inline_size),
specified(padding_style.padding_bottom, containing_block_width), specified(padding_style.padding_right, containing_block_inline_size),
specified(padding_style.padding_left, containing_block_width)) 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::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
use flow; use flow;
use flow_ref::FlowRef; use flow_ref::FlowRef;
use layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal}; use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal};
use layout_task::{BubbleWidthsTraversal}; use layout_task::{BubbleISizesTraversal};
use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods}; use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods};
use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal}; use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal};
use wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode}; 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 { if !had_children {
bottom_up_func(unsafe_flow, proxy) 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, fn run_parallel(&mut self,
unsafe_flow: UnsafeFlow, unsafe_flow: UnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) { proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
self.run_parallel_helper(unsafe_flow, self.run_parallel_helper(unsafe_flow,
proxy, proxy,
assign_widths, assign_inline_sizes,
assign_heights_and_store_overflow) 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, fn recalc_style_for_node(unsafe_layout_node: UnsafeLayoutNode,
proxy: &mut WorkerProxy<*mut LayoutContext,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>) { proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
let layout_context = unsafe { &mut **proxy.user_data() }; 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, 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>) { proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
let layout_context = unsafe { &mut **proxy.user_data() }; 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, 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, 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, || { profile(time::LayoutParallelWarmupCategory, time_profiler_chan, || {
queue.push(WorkUnit { queue.push(WorkUnit {
fun: assign_widths, fun: assign_inline_sizes,
data: mut_owned_flow_to_unsafe_flow(root), data: mut_owned_flow_to_unsafe_flow(root),
}) })
}); });

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,8 +6,8 @@
#![deny(unsafe_block)] #![deny(unsafe_block)]
use block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
use block::{WidthConstraintInput, WidthConstraintSolution}; use block::{ISizeConstraintInput, ISizeConstraintSolution};
use construct::FlowConstructor; use construct::FlowConstructor;
use context::LayoutContext; use context::LayoutContext;
use floats::FloatKind; use floats::FloatKind;
@ -30,8 +30,8 @@ pub enum TableLayout {
pub struct TableWrapperFlow { pub struct TableWrapperFlow {
pub block_flow: BlockFlow, pub block_flow: BlockFlow,
/// Column widths /// Column inline-sizes
pub col_widths: Vec<Au>, pub col_inline_sizes: Vec<Au>,
/// Table-layout property /// Table-layout property
pub table_layout: TableLayout, pub table_layout: TableLayout,
@ -50,7 +50,7 @@ impl TableWrapperFlow {
}; };
TableWrapperFlow { TableWrapperFlow {
block_flow: block_flow, block_flow: block_flow,
col_widths: vec!(), col_inline_sizes: vec!(),
table_layout: table_layout table_layout: table_layout
} }
} }
@ -67,7 +67,7 @@ impl TableWrapperFlow {
}; };
TableWrapperFlow { TableWrapperFlow {
block_flow: block_flow, block_flow: block_flow,
col_widths: vec!(), col_inline_sizes: vec!(),
table_layout: table_layout table_layout: table_layout
} }
} }
@ -85,7 +85,7 @@ impl TableWrapperFlow {
}; };
TableWrapperFlow { TableWrapperFlow {
block_flow: block_flow, block_flow: block_flow,
col_widths: vec!(), col_inline_sizes: vec!(),
table_layout: table_layout table_layout: table_layout
} }
} }
@ -94,14 +94,14 @@ impl TableWrapperFlow {
self.block_flow.float.is_some() self.block_flow.float.is_some()
} }
/// Assign height for table-wrapper flow. /// Assign block-size for table-wrapper flow.
/// `Assign height` of table-wrapper flow follows a similar process to that of block 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 /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_wrapper_base(&mut self, layout_context: &mut LayoutContext) { fn assign_block_size_table_wrapper_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse); self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
} }
pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) { 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 /* Recursively (bottom-up) determine the context's preferred and
minimum widths. When called on this context, all child contexts minimum inline_sizes. When called on this context, all child contexts
have had their min/pref widths set. This function must decide have had their min/pref inline_sizes set. This function must decide
min/pref widths based on child context widths and dimensions of min/pref inline_sizes based on child context inline_sizes and dimensions of
any fragments it is responsible for flowing. */ any fragments it is responsible for flowing. */
fn bubble_widths(&mut self, ctx: &mut LayoutContext) { fn bubble_inline_sizes(&mut self, ctx: &mut LayoutContext) {
// get column widths info from table flow // get column inline-sizes info from table flow
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_caption() || kid.is_table()); assert!(kid.is_table_caption() || kid.is_table());
if 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 /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
/// called on this context, the context has had its width set by the parent context. /// 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. /// contexts.
fn assign_widths(&mut self, ctx: &mut LayoutContext) { fn assign_inline_sizes(&mut self, ctx: &mut LayoutContext) {
debug!("assign_widths({}): assigning width for flow", debug!("assign_inline_sizes({}): assigning inline_size for flow",
if self.is_float() { if self.is_float() {
"floated table_wrapper" "floated table_wrapper"
} else { } else {
@ -156,35 +156,35 @@ impl Flow for TableWrapperFlow {
}); });
// The position was set to the containing block by the flow's parent. // 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; let inline_size_computer = TableWrapper;
width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width); 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 inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
let content_width = self.block_flow.fragment.border_box.size.width; let content_inline_size = self.block_flow.fragment.border_box.size.inline;
match self.table_layout { match self.table_layout {
FixedLayout | _ if self.is_float() => 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. // In case of fixed layout, column inline-sizes are calculated in table flow.
let assigned_col_widths = match self.table_layout { let assigned_col_inline_sizes = match self.table_layout {
FixedLayout => None, 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() { if self.is_float() {
debug!("assign_height_float: assigning height for floated table_wrapper"); debug!("assign_block_size_float: assigning block_size for floated table_wrapper");
self.block_flow.assign_height_float(ctx); self.block_flow.assign_block_size_float(ctx);
} else { } else {
debug!("assign_height: assigning height for table_wrapper"); debug!("assign_block_size: assigning block_size for table_wrapper");
self.assign_height_table_wrapper_base(ctx); self.assign_block_size_table_wrapper_base(ctx);
} }
} }
@ -206,119 +206,120 @@ impl fmt::Show for TableWrapperFlow {
struct TableWrapper; struct TableWrapper;
impl TableWrapper { impl TableWrapper {
fn compute_used_width_table_wrapper(&self, fn compute_used_inline_size_table_wrapper(&self,
table_wrapper: &mut TableWrapperFlow, table_wrapper: &mut TableWrapperFlow,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
parent_flow_width: Au) { parent_flow_inline_size: Au) {
let input = self.compute_width_constraint_inputs_table_wrapper(table_wrapper, let input = self.compute_inline_size_constraint_inputs_table_wrapper(table_wrapper,
parent_flow_width, parent_flow_inline_size,
ctx); 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); 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, table_wrapper: &mut TableWrapperFlow,
parent_flow_width: Au, parent_flow_inline_size: Au,
ctx: &mut LayoutContext) ctx: &mut LayoutContext)
-> WidthConstraintInput { -> ISizeConstraintInput {
let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow, let mut input = self.compute_inline_size_constraint_inputs(&mut table_wrapper.block_flow,
parent_flow_width, parent_flow_inline_size,
ctx); ctx);
let computed_width = match table_wrapper.table_layout { let computed_inline_size = match table_wrapper.table_layout {
FixedLayout => { FixedLayout => {
let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0), let fixed_cells_inline_size = table_wrapper.col_inline_sizes.iter().fold(Au(0),
|sum, width| sum.add(width)); |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(); 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. // 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. // input.available_inline-size is same as containing_block_inline-size in table_wrapper.
let padding_left = specified(style.get_padding().padding_left, let padding = style.logical_padding();
input.available_width); let border = style.logical_border_width();
let padding_right = specified(style.get_padding().padding_right, let padding_and_borders =
input.available_width); specified(padding.inline_start, input.available_inline_size) +
let border_left = style.get_border().border_left_width; specified(padding.inline_end, input.available_inline_size) +
let border_right = style.get_border().border_right_width; border.inline_start +
let padding_and_borders = padding_left + padding_right + border_left + border_right; border.inline_end;
// Compare border-edge widths. Because fixed_cells_width indicates content-width, // Compare border-edge inline-sizes. Because fixed_cells_inline-size indicates content-inline-size,
// padding and border values are added to fixed_cells_width. // padding and border values are added to fixed_cells_inline-size.
computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width); computed_inline_size = geometry::max(
computed_width fixed_cells_inline_size + padding_and_borders, computed_inline_size);
computed_inline_size
}, },
AutoLayout => { AutoLayout => {
// Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2. // Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2.
let mut cap_min = Au(0); let mut cap_min = Au(0);
let mut cols_min = Au(0); let mut cols_min = Au(0);
let mut cols_max = Au(0); let mut cols_max = Au(0);
let mut col_min_widths = &vec!(); let mut col_min_inline_sizes = &vec!();
let mut col_pref_widths = &vec!(); let mut col_pref_inline_sizes = &vec!();
for kid in table_wrapper.block_flow.base.child_iter() { for kid in table_wrapper.block_flow.base.child_iter() {
if kid.is_table_caption() { 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 { } else {
assert!(kid.is_table()); assert!(kid.is_table());
cols_min = kid.as_block().base.intrinsic_widths.minimum_width; cols_min = kid.as_block().base.intrinsic_inline_sizes.minimum_inline_size;
cols_max = kid.as_block().base.intrinsic_widths.preferred_width; cols_max = kid.as_block().base.intrinsic_inline_sizes.preferred_inline_size;
col_min_widths = kid.col_min_widths(); col_min_inline_sizes = kid.col_min_inline_sizes();
col_pref_widths = kid.col_pref_widths(); 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. // 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 => { 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 { 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)) (cols_max, Au(0))
} else { } else {
(cap_min, cap_min - cols_min) (cap_min, cap_min - cols_min)
} }
} else { } else {
let max = if cols_min >= input.available_width && cols_min >= cap_min { let max = if cols_min >= input.available_inline_size && cols_min >= cap_min {
table_wrapper.col_widths = col_min_widths.clone(); table_wrapper.col_inline_sizes = col_min_inline_sizes.clone();
cols_min cols_min
} else { } else {
geometry::max(input.available_width, cap_min) geometry::max(input.available_inline_size, cap_min)
}; };
(max, max - cols_min) (max, max - cols_min)
} }
}, },
Specified(width) => { Specified(inline_size) => {
let max = if cols_min >= width && cols_min >= cap_min { let max = if cols_min >= inline_size && cols_min >= cap_min {
table_wrapper.col_widths = col_min_widths.clone(); table_wrapper.col_inline_sizes = col_min_inline_sizes.clone();
cols_min cols_min
} else { } else {
geometry::max(width, cap_min) geometry::max(inline_size, cap_min)
}; };
(max, max - cols_min) (max, max - cols_min)
} }
}; };
// The extra width is distributed over the columns // The extra inline-size is distributed over the columns
if extra_width > Au(0) { if extra_inline_size > Au(0) {
let cell_len = table_wrapper.col_widths.len() as f64; let cell_len = table_wrapper.col_inline_sizes.len() as f64;
table_wrapper.col_widths = col_min_widths.iter().map(|width| { table_wrapper.col_inline_sizes = col_min_inline_sizes.iter().map(|inline_size| {
width + extra_width.scale_by(1.0 / cell_len) inline_size + extra_inline_size.scale_by(1.0 / cell_len)
}).collect(); }).collect();
} }
width inline_size
} }
}; };
input.computed_width = Specified(computed_width); input.computed_inline_size = Specified(computed_inline_size);
input input
} }
} }
impl WidthAndMarginsComputer for TableWrapper { impl ISizeAndMarginsComputer for TableWrapper {
/// Solve the width and margins constraints for this block flow. /// Solve the inline-size and margins constraints for this block flow.
fn solve_width_constraints(&self, block: &mut BlockFlow, input: &WidthConstraintInput) fn solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput)
-> WidthConstraintSolution { -> ISizeConstraintSolution {
self.solve_block_width_constraints(block, input) 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::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone}; use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::logical_geometry::LogicalSize;
use servo_util::range::Range; use servo_util::range::Range;
use style::ComputedValues; use style::ComputedValues;
use style::computed_values::{font_family, line_height, white_space}; use style::computed_values::{font_family, line_height, white_space};
@ -151,9 +152,11 @@ impl TextRunScanner {
*text); *text);
let range = Range::new(CharIndex(0), run.char_len()); let range = Range::new(CharIndex(0), run.char_len());
let new_metrics = run.metrics_for_range(&range); 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 new_text_fragment_info = ScannedTextFragmentInfo::new(Arc::new(run), range);
let mut new_fragment = old_fragment.transform(new_metrics.bounding_box.size, let mut new_fragment = old_fragment.transform(
ScannedTextFragment(new_text_fragment_info)); bounding_box_size, ScannedTextFragment(new_text_fragment_info));
new_fragment.new_line_pos = new_line_pos; new_fragment.new_line_pos = new_line_pos;
out_fragments.push(new_fragment) out_fragments.push(new_fragment)
} }
@ -236,9 +239,12 @@ impl TextRunScanner {
} }
let new_text_fragment_info = ScannedTextFragmentInfo::new(run.get_ref().clone(), *range); 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 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, let bounding_box_size = LogicalSize::from_physical(
ScannedTextFragment(new_text_fragment_info)); 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(); new_fragment.new_line_pos = new_line_positions.get(logical_offset.to_uint()).new_line_pos.clone();
out_fragments.push(new_fragment) 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. /// 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 { pub fn line_height_from_style(style: &ComputedValues, font_size: Au) -> Au {

View file

@ -97,6 +97,7 @@ pub extern "C" fn android_start(argc: int, argv: **u8) -> int {
#[cfg(not(test))] #[cfg(not(test))]
pub fn run(opts: opts::Opts) { pub fn run(opts: opts::Opts) {
::servo_util::opts::set_experimental_enabled(opts.enable_experimental);
RegisterBindings::RegisterProxyHandlers(); RegisterBindings::RegisterProxyHandlers();
let mut pool_config = green::PoolConfig::new(); let mut pool_config = green::PoolConfig::new();

View file

@ -8,11 +8,13 @@ pub use std::ascii::StrAsciiExt;
use serialize::{Encodable, Encoder}; use serialize::{Encodable, Encoder};
pub use servo_util::url::parse_url; pub use servo_util::url::parse_url;
use servo_util::logical_geometry::{WritingMode, LogicalMargin};
use sync::Arc; use sync::Arc;
pub use url::Url; pub use url::Url;
pub use cssparser::*; pub use cssparser::*;
pub use cssparser::ast::*; pub use cssparser::ast::*;
pub use geom::SideOffsets2D;
use errors::{ErrorLoggerIterator, log_css_error}; use errors::{ErrorLoggerIterator, log_css_error};
pub use parsing_utils::*; pub use parsing_utils::*;
@ -35,7 +37,7 @@ def to_rust_ident(name):
return name return name
class Longhand(object): class Longhand(object):
def __init__(self, name, derived_from=None): def __init__(self, name, derived_from=None, experimental=False):
self.name = name self.name = name
self.ident = to_rust_ident(name) self.ident = to_rust_ident(name)
self.camel_case, _ = re.subn( self.camel_case, _ = re.subn(
@ -43,6 +45,7 @@ class Longhand(object):
lambda m: m.group(1).upper(), lambda m: m.group(1).upper(),
self.ident.strip("_").capitalize()) self.ident.strip("_").capitalize())
self.style_struct = THIS_STYLE_STRUCT self.style_struct = THIS_STYLE_STRUCT
self.experimental = experimental
if derived_from is None: if derived_from is None:
self.derived_from = None self.derived_from = None
else: else:
@ -94,12 +97,12 @@ pub mod longhands {
value value
} }
<%def name="raw_longhand(name, no_super=False, derived_from=None)"> <%def name="raw_longhand(name, no_super=False, derived_from=None, experimental=False)">
<% <%
if derived_from is not None: if derived_from is not None:
derived_from = derived_from.split() derived_from = derived_from.split()
property = Longhand(name, derived_from=derived_from) property = Longhand(name, derived_from=derived_from, experimental=experimental)
THIS_STYLE_STRUCT.longhands.append(property) THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property) LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property LONGHANDS_BY_NAME[name] = property
@ -128,8 +131,9 @@ pub mod longhands {
} }
</%def> </%def>
<%def name="longhand(name, no_super=False, derived_from=None)"> <%def name="longhand(name, no_super=False, derived_from=None, experimental=False)">
<%self:raw_longhand name="${name}" derived_from="${derived_from}"> <%self:raw_longhand name="${name}" derived_from="${derived_from}"
experimental="${experimental}">
${caller.body()} ${caller.body()}
% if derived_from is None: % if derived_from is None:
pub fn parse_specified(_input: &[ComponentValue], _base_url: &Url) pub fn parse_specified(_input: &[ComponentValue], _base_url: &Url)
@ -140,8 +144,9 @@ pub mod longhands {
</%self:raw_longhand> </%self:raw_longhand>
</%def> </%def>
<%def name="single_component_value(name, derived_from=None)"> <%def name="single_component_value(name, derived_from=None, experimental=False)">
<%self:longhand name="${name}" derived_from="${derived_from}"> <%self:longhand name="${name}" derived_from="${derived_from}"
experimental="${experimental}">
${caller.body()} ${caller.body()}
pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> { pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> {
one_component_value(input).and_then(|c| from_component_value(c, base_url)) one_component_value(input).and_then(|c| from_component_value(c, base_url))
@ -149,8 +154,8 @@ pub mod longhands {
</%self:longhand> </%self:longhand>
</%def> </%def>
<%def name="single_keyword_computed(name, values)"> <%def name="single_keyword_computed(name, values, experimental=False)">
<%self:single_component_value name="${name}"> <%self:single_component_value name="${name}" experimental="${experimental}">
${caller.body()} ${caller.body()}
pub mod computed_value { pub mod computed_value {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@ -179,9 +184,10 @@ pub mod longhands {
</%self:single_component_value> </%self:single_component_value>
</%def> </%def>
<%def name="single_keyword(name, values)"> <%def name="single_keyword(name, values, experimental=False)">
<%self:single_keyword_computed name="${name}" <%self:single_keyword_computed name="${name}"
values="${values}"> values="${values}"
experimental="${experimental}">
// The computed value is the same as the specified value. // The computed value is the same as the specified value.
pub use to_computed_value = super::computed_as_specified; pub use to_computed_value = super::computed_as_specified;
</%self:single_keyword_computed> </%self:single_keyword_computed>
@ -323,7 +329,7 @@ pub mod longhands {
${new_style_struct("InheritedBox", is_inherited=True)} ${new_style_struct("InheritedBox", is_inherited=True)}
${single_keyword("direction", "ltr rtl")} ${single_keyword("direction", "ltr rtl", experimental=True)}
// CSS 2.1, Section 10 - Visual formatting model details // CSS 2.1, Section 10 - Visual formatting model details
@ -1041,11 +1047,11 @@ pub mod longhands {
// http://dev.w3.org/csswg/css-writing-modes/ // http://dev.w3.org/csswg/css-writing-modes/
${switch_to_style_struct("InheritedBox")} ${switch_to_style_struct("InheritedBox")}
${single_keyword("writing-mode", "horizontal-tb vertical-rl vertical-lr")} ${single_keyword("writing-mode", "horizontal-tb vertical-rl vertical-lr", experimental=True)}
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support) // FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented // FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
${single_keyword("text-orientation", "sideways sideways-left sideways-right")} ${single_keyword("text-orientation", "sideways sideways-left sideways-right", experimental=True)}
} }
@ -1436,6 +1442,10 @@ pub fn parse_property_declaration_list<I: Iterator<Node>>(input: I, base_url: &U
match PropertyDeclaration::parse(n.as_slice(), v.as_slice(), list, base_url, seen) { match PropertyDeclaration::parse(n.as_slice(), v.as_slice(), list, base_url, seen) {
UnknownProperty => log_css_error(l, format!( UnknownProperty => log_css_error(l, format!(
"Unsupported property: {}:{}", n, v.iter().to_css()).as_slice()), "Unsupported property: {}:{}", n, v.iter().to_css()).as_slice()),
ExperimentalProperty => log_css_error(l, format!(
"Experimental property, use `servo --enable_experimental` \
or `servo -e` to enable: {}:{}",
n, v.iter().to_css()).as_slice()),
InvalidValue => log_css_error(l, format!( InvalidValue => log_css_error(l, format!(
"Invalid value: {}:{}", n, v.iter().to_css()).as_slice()), "Invalid value: {}:{}", n, v.iter().to_css()).as_slice()),
ValidOrIgnoredDeclaration => (), ValidOrIgnoredDeclaration => (),
@ -1486,6 +1496,7 @@ pub enum PropertyDeclaration {
pub enum PropertyDeclarationParseResult { pub enum PropertyDeclarationParseResult {
UnknownProperty, UnknownProperty,
ExperimentalProperty,
InvalidValue, InvalidValue,
ValidOrIgnoredDeclaration, ValidOrIgnoredDeclaration,
} }
@ -1502,6 +1513,11 @@ impl PropertyDeclaration {
% for property in LONGHANDS: % for property in LONGHANDS:
% if property.derived_from is None: % if property.derived_from is None:
"${property.name}" => { "${property.name}" => {
% if property.experimental:
if !::servo_util::opts::experimental_enabled() {
return ExperimentalProperty
}
% endif
if seen.get_${property.ident}() { if seen.get_${property.ident}() {
return ValidOrIgnoredDeclaration return ValidOrIgnoredDeclaration
} }
@ -1576,8 +1592,6 @@ impl PropertyDeclaration {
pub mod style_structs { pub mod style_structs {
use super::longhands; use super::longhands;
use super::computed_values;
use servo_util::logical_geometry::WritingMode;
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
#[deriving(PartialEq, Clone)] #[deriving(PartialEq, Clone)]
@ -1587,42 +1601,6 @@ pub mod style_structs {
% endfor % endfor
} }
% 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)] #[deriving(Clone)]
@ -1631,6 +1609,7 @@ pub struct ComputedValues {
${style_struct.ident}: Arc<style_structs::${style_struct.name}>, ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor % endfor
shareable: bool, shareable: bool,
pub writing_mode: WritingMode,
} }
impl ComputedValues { impl ComputedValues {
@ -1648,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: % for style_struct in STYLE_STRUCTS:
#[inline]
pub fn get_${style_struct.name.lower()} pub fn get_${style_struct.name.lower()}
<'a>(&'a self) -> &'a style_structs::${style_struct.name} { <'a>(&'a self) -> &'a style_structs::${style_struct.name} {
&*self.${style_struct.ident} &*self.${style_struct.ident}
@ -1656,6 +1717,42 @@ impl ComputedValues {
% endfor % 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. /// The initial values for all style structs as defined by the specification.
lazy_init! { lazy_init! {
static ref INITIAL_VALUES: ComputedValues = ComputedValues { static ref INITIAL_VALUES: ComputedValues = ComputedValues {
@ -1667,10 +1764,17 @@ lazy_init! {
}), }),
% endfor % endfor
shareable: true, 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)] /// This only exists to limit the scope of #[allow(experimental)]
/// FIXME: remove this when Arc::make_unique() is not experimental anymore. /// FIXME: remove this when Arc::make_unique() is not experimental anymore.
trait ArcExperimental<T> { trait ArcExperimental<T> {
@ -1766,6 +1870,7 @@ fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
} }
ComputedValues { ComputedValues {
writing_mode: get_writing_mode(&*style_inheritedbox),
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
${style_struct.ident}: style_${style_struct.ident}, ${style_struct.ident}: style_${style_struct.ident},
% endfor % endfor
@ -1984,6 +2089,7 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
} }
(ComputedValues { (ComputedValues {
writing_mode: get_writing_mode(&*style_inheritedbox),
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
${style_struct.ident}: style_${style_struct.ident}, ${style_struct.ident}: style_${style_struct.ident},
% endfor % endfor
@ -2009,6 +2115,7 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues {
.${style_struct.ident}.clone(), .${style_struct.ident}.clone(),
% endfor % endfor
shareable: false, shareable: false,
writing_mode: parent_style.writing_mode,
}; };
{ {
let border = result.border.make_unique_experimental(); let border = result.border.make_unique_experimental();

View file

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

View file

@ -67,7 +67,7 @@ pub enum PagePx {}
// See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info. // See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info.
// //
// FIXME: Implement Au using Length and ScaleFactor instead of a custom type. // FIXME: Implement Au using Length and ScaleFactor instead of a custom type.
#[deriving(Clone, PartialEq, PartialOrd, Zero)] #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Zero)]
pub struct Au(pub i32); pub struct Au(pub i32);
impl Default for Au { impl Default for Au {

View file

@ -5,6 +5,7 @@
/// Geometry in flow-relative space. /// Geometry in flow-relative space.
use geom::{Size2D, Point2D, SideOffsets2D, Rect}; use geom::{Size2D, Point2D, SideOffsets2D, Rect};
use std::cmp::{min, max};
use std::fmt::{Show, Formatter, FormatError}; use std::fmt::{Show, Formatter, FormatError};
use std::num::Zero; use std::num::Zero;
@ -130,15 +131,15 @@ impl Show for DebugWritingMode {
/// A 2D size in flow-relative dimensions /// A 2D size in flow-relative dimensions
#[deriving(PartialEq, Eq, Clone)] #[deriving(PartialEq, Eq, Clone)]
pub struct LogicalSize<T> { pub struct LogicalSize<T> {
pub isize: T, // inline-size (a.k.a. logical width) pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
pub bsize: T, // block-size (a.k.a. logical height) pub block: T, // block-size, a.k.a. logical height, a.k.a. extent
debug_writing_mode: DebugWritingMode, debug_writing_mode: DebugWritingMode,
} }
impl<T: Show> Show for LogicalSize<T> { impl<T: Show> Show for LogicalSize<T> {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> { fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "LogicalSize[{}, {}, {}]", write!(formatter, "LogicalSize[{}, {}, {}]",
self.debug_writing_mode, self.isize, self.bsize) self.debug_writing_mode, self.inline, self.block)
} }
} }
@ -147,24 +148,24 @@ impl<T: Zero> LogicalSize<T> {
#[inline] #[inline]
pub fn zero(mode: WritingMode) -> LogicalSize<T> { pub fn zero(mode: WritingMode) -> LogicalSize<T> {
LogicalSize { LogicalSize {
isize: Zero::zero(), inline: Zero::zero(),
bsize: Zero::zero(), block: Zero::zero(),
debug_writing_mode: DebugWritingMode::new(mode), debug_writing_mode: DebugWritingMode::new(mode),
} }
} }
#[inline] #[inline]
pub fn is_zero(&self) -> bool { pub fn is_zero(&self) -> bool {
self.isize.is_zero() && self.bsize.is_zero() self.inline.is_zero() && self.block.is_zero()
} }
} }
impl<T: Copy> LogicalSize<T> { impl<T: Copy> LogicalSize<T> {
#[inline] #[inline]
pub fn new(mode: WritingMode, isize: T, bsize: T) -> LogicalSize<T> { pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
LogicalSize { LogicalSize {
isize: isize, inline: inline,
bsize: bsize, block: block,
debug_writing_mode: DebugWritingMode::new(mode), debug_writing_mode: DebugWritingMode::new(mode),
} }
} }
@ -182,9 +183,9 @@ impl<T: Copy> LogicalSize<T> {
pub fn width(&self, mode: WritingMode) -> T { pub fn width(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
self.bsize self.block
} else { } else {
self.isize self.inline
} }
} }
@ -192,9 +193,9 @@ impl<T: Copy> LogicalSize<T> {
pub fn set_width(&mut self, mode: WritingMode, width: T) { pub fn set_width(&mut self, mode: WritingMode, width: T) {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
self.bsize = width self.block = width
} else { } else {
self.isize = width self.inline = width
} }
} }
@ -202,9 +203,9 @@ impl<T: Copy> LogicalSize<T> {
pub fn height(&self, mode: WritingMode) -> T { pub fn height(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
self.isize self.inline
} else { } else {
self.bsize self.block
} }
} }
@ -212,9 +213,9 @@ impl<T: Copy> LogicalSize<T> {
pub fn set_height(&mut self, mode: WritingMode, height: T) { pub fn set_height(&mut self, mode: WritingMode, height: T) {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
self.isize = height self.inline = height
} else { } else {
self.bsize = height self.block = height
} }
} }
@ -222,9 +223,9 @@ impl<T: Copy> LogicalSize<T> {
pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> { pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
Size2D { width: self.bsize, height: self.isize } Size2D { width: self.block, height: self.inline }
} else { } else {
Size2D { width: self.isize, height: self.bsize } Size2D { width: self.inline, height: self.block }
} }
} }
@ -245,8 +246,8 @@ impl<T: Add<T, T>> Add<LogicalSize<T>, LogicalSize<T>> for LogicalSize<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode); self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalSize { LogicalSize {
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
isize: self.isize + other.isize, inline: self.inline + other.inline,
bsize: self.bsize + other.bsize, block: self.block + other.block,
} }
} }
} }
@ -257,8 +258,8 @@ impl<T: Sub<T, T>> Sub<LogicalSize<T>, LogicalSize<T>> for LogicalSize<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode); self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalSize { LogicalSize {
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
isize: self.isize - other.isize, inline: self.inline - other.inline,
bsize: self.bsize - other.bsize, block: self.block - other.block,
} }
} }
} }
@ -395,14 +396,28 @@ impl<T: Copy + Sub<T, T>> LogicalPoint<T> {
} }
} }
impl<T: Add<T,T>> LogicalPoint<T> {
/// This doesnt really makes sense,
/// but happens when dealing with mutliple origins.
#[inline]
pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalPoint {
debug_writing_mode: self.debug_writing_mode,
i: self.i + other.i,
b: self.b + other.b,
}
}
}
impl<T: Add<T,T>> Add<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> { impl<T: Add<T,T>> Add<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> {
#[inline] #[inline]
fn add(&self, other: &LogicalSize<T>) -> LogicalPoint<T> { fn add(&self, other: &LogicalSize<T>) -> LogicalPoint<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode); self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalPoint { LogicalPoint {
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
i: self.i + other.isize, i: self.i + other.inline,
b: self.b + other.bsize, b: self.b + other.block,
} }
} }
} }
@ -413,8 +428,8 @@ impl<T: Sub<T,T>> Sub<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode); self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalPoint { LogicalPoint {
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
i: self.i - other.isize, i: self.i - other.inline,
b: self.b - other.bsize, b: self.b - other.block,
} }
} }
} }
@ -426,17 +441,20 @@ impl<T: Sub<T,T>> Sub<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> {
/// A positive "margin" can be added to a rectangle to obtain a bigger rectangle. /// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
#[deriving(PartialEq, Eq, Clone)] #[deriving(PartialEq, Eq, Clone)]
pub struct LogicalMargin<T> { pub struct LogicalMargin<T> {
pub bstart: T, pub block_start: T,
pub iend: T, pub inline_end: T,
pub bend: T, pub block_end: T,
pub istart: T, pub inline_start: T,
debug_writing_mode: DebugWritingMode, debug_writing_mode: DebugWritingMode,
} }
impl<T: Show> Show for LogicalMargin<T> { impl<T: Show> Show for LogicalMargin<T> {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> { fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "LogicalMargin[{}, bstart: {}, iend: {}, bend: {}, istart: {}]", write!(formatter,
self.debug_writing_mode, self.bstart, self.iend, self.bend, self.istart) "LogicalMargin[{}, block_start: {}, inline_end: {}, \
block_end: {}, inline_start: {}]",
self.debug_writing_mode, self.block_start,
self.inline_end, self.block_end, self.inline_start)
} }
} }
@ -444,77 +462,83 @@ impl<T: Zero> LogicalMargin<T> {
#[inline] #[inline]
pub fn zero(mode: WritingMode) -> LogicalMargin<T> { pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
LogicalMargin { LogicalMargin {
bstart: Zero::zero(), block_start: Zero::zero(),
iend: Zero::zero(), inline_end: Zero::zero(),
bend: Zero::zero(), block_end: Zero::zero(),
istart: Zero::zero(), inline_start: Zero::zero(),
debug_writing_mode: DebugWritingMode::new(mode), debug_writing_mode: DebugWritingMode::new(mode),
} }
} }
#[inline] #[inline]
pub fn is_zero(&self) -> bool { pub fn is_zero(&self) -> bool {
self.bstart.is_zero() && self.block_start.is_zero() &&
self.iend.is_zero() && self.inline_end.is_zero() &&
self.bend.is_zero() && self.block_end.is_zero() &&
self.istart.is_zero() self.inline_start.is_zero()
} }
} }
impl<T: Copy> LogicalMargin<T> { impl<T: Copy> LogicalMargin<T> {
#[inline] #[inline]
pub fn new(mode: WritingMode, bstart: T, iend: T, bend: T, istart: T) -> LogicalMargin<T> { pub fn new(mode: WritingMode, block_start: T, inline_end: T, block_end: T, inline_start: T)
-> LogicalMargin<T> {
LogicalMargin { LogicalMargin {
bstart: bstart, block_start: block_start,
iend: iend, inline_end: inline_end,
bend: bend, block_end: block_end,
istart: istart, inline_start: inline_start,
debug_writing_mode: DebugWritingMode::new(mode), debug_writing_mode: DebugWritingMode::new(mode),
} }
} }
#[inline]
pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
LogicalMargin::new(mode, value, value, value, value)
}
#[inline] #[inline]
pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> { pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
let bstart; let block_start;
let iend; let inline_end;
let bend; let block_end;
let istart; let inline_start;
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_vertical_lr() { if mode.is_vertical_lr() {
bstart = offsets.left; block_start = offsets.left;
bend = offsets.right; block_end = offsets.right;
} else { } else {
bstart = offsets.right; block_start = offsets.right;
bend = offsets.left; block_end = offsets.left;
} }
if mode.is_inline_tb() { if mode.is_inline_tb() {
istart = offsets.top; inline_start = offsets.top;
iend = offsets.bottom; inline_end = offsets.bottom;
} else { } else {
istart = offsets.bottom; inline_start = offsets.bottom;
iend = offsets.top; inline_end = offsets.top;
} }
} else { } else {
bstart = offsets.top; block_start = offsets.top;
bend = offsets.bottom; block_end = offsets.bottom;
if mode.is_bidi_ltr() { if mode.is_bidi_ltr() {
istart = offsets.left; inline_start = offsets.left;
iend = offsets.right; inline_end = offsets.right;
} else { } else {
istart = offsets.right; inline_start = offsets.right;
iend = offsets.left; inline_end = offsets.left;
} }
} }
LogicalMargin::new(mode, bstart, iend, bend, istart) LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
} }
#[inline] #[inline]
pub fn top(&self, mode: WritingMode) -> T { pub fn top(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_inline_tb() { self.istart } else { self.iend } if mode.is_inline_tb() { self.inline_start } else { self.inline_end }
} else { } else {
self.bstart self.block_start
} }
} }
@ -522,9 +546,9 @@ impl<T: Copy> LogicalMargin<T> {
pub fn set_top(&mut self, mode: WritingMode, top: T) { pub fn set_top(&mut self, mode: WritingMode, top: T) {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_inline_tb() { self.istart = top } else { self.iend = top } if mode.is_inline_tb() { self.inline_start = top } else { self.inline_end = top }
} else { } else {
self.bstart = top self.block_start = top
} }
} }
@ -532,9 +556,9 @@ impl<T: Copy> LogicalMargin<T> {
pub fn right(&self, mode: WritingMode) -> T { pub fn right(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_vertical_lr() { self.bend } else { self.bstart } if mode.is_vertical_lr() { self.block_end } else { self.block_start }
} else { } else {
if mode.is_bidi_ltr() { self.iend } else { self.istart } if mode.is_bidi_ltr() { self.inline_end } else { self.inline_start }
} }
} }
@ -542,9 +566,9 @@ impl<T: Copy> LogicalMargin<T> {
pub fn set_right(&mut self, mode: WritingMode, right: T) { pub fn set_right(&mut self, mode: WritingMode, right: T) {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_vertical_lr() { self.bend = right } else { self.bstart = right } if mode.is_vertical_lr() { self.block_end = right } else { self.block_start = right }
} else { } else {
if mode.is_bidi_ltr() { self.iend = right } else { self.istart = right } if mode.is_bidi_ltr() { self.inline_end = right } else { self.inline_start = right }
} }
} }
@ -552,9 +576,9 @@ impl<T: Copy> LogicalMargin<T> {
pub fn bottom(&self, mode: WritingMode) -> T { pub fn bottom(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_inline_tb() { self.iend } else { self.istart } if mode.is_inline_tb() { self.inline_end } else { self.inline_start }
} else { } else {
self.bend self.block_end
} }
} }
@ -562,9 +586,9 @@ impl<T: Copy> LogicalMargin<T> {
pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) { pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_inline_tb() { self.iend = bottom } else { self.istart = bottom } if mode.is_inline_tb() { self.inline_end = bottom } else { self.inline_start = bottom }
} else { } else {
self.bend = bottom self.block_end = bottom
} }
} }
@ -572,9 +596,9 @@ impl<T: Copy> LogicalMargin<T> {
pub fn left(&self, mode: WritingMode) -> T { pub fn left(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_vertical_lr() { self.bstart } else { self.bend } if mode.is_vertical_lr() { self.block_start } else { self.block_end }
} else { } else {
if mode.is_bidi_ltr() { self.istart } else { self.iend } if mode.is_bidi_ltr() { self.inline_start } else { self.inline_end }
} }
} }
@ -582,9 +606,9 @@ impl<T: Copy> LogicalMargin<T> {
pub fn set_left(&mut self, mode: WritingMode, left: T) { pub fn set_left(&mut self, mode: WritingMode, left: T) {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_vertical_lr() { self.bstart = left } else { self.bend = left } if mode.is_vertical_lr() { self.block_start = left } else { self.block_end = left }
} else { } else {
if mode.is_bidi_ltr() { self.istart = left } else { self.iend = left } if mode.is_bidi_ltr() { self.inline_start = left } else { self.inline_end = left }
} }
} }
@ -597,28 +621,28 @@ impl<T: Copy> LogicalMargin<T> {
let left; let left;
if mode.is_vertical() { if mode.is_vertical() {
if mode.is_vertical_lr() { if mode.is_vertical_lr() {
left = self.bstart; left = self.block_start;
right = self.bend; right = self.block_end;
} else { } else {
right = self.bstart; right = self.block_start;
left = self.bend; left = self.block_end;
} }
if mode.is_inline_tb() { if mode.is_inline_tb() {
top = self.istart; top = self.inline_start;
bottom = self.iend; bottom = self.inline_end;
} else { } else {
bottom = self.istart; bottom = self.inline_start;
top = self.iend; top = self.inline_end;
} }
} else { } else {
top = self.bstart; top = self.block_start;
bottom = self.bend; bottom = self.block_end;
if mode.is_bidi_ltr() { if mode.is_bidi_ltr() {
left = self.istart; left = self.inline_start;
right = self.iend; right = self.inline_end;
} else { } else {
right = self.istart; right = self.inline_start;
left = self.iend; left = self.inline_end;
} }
} }
SideOffsets2D::new(top, right, bottom, left) SideOffsets2D::new(top, right, bottom, left)
@ -637,22 +661,22 @@ impl<T: Copy> LogicalMargin<T> {
impl<T: Add<T, T>> LogicalMargin<T> { impl<T: Add<T, T>> LogicalMargin<T> {
#[inline] #[inline]
pub fn istart_end(&self) -> T { pub fn inline_start_end(&self) -> T {
self.istart + self.iend self.inline_start + self.inline_end
} }
#[inline] #[inline]
pub fn bstart_end(&self) -> T { pub fn block_start_end(&self) -> T {
self.bstart + self.bend self.block_start + self.block_end
} }
#[inline] #[inline]
pub fn top_bottom(&self, mode: WritingMode) -> T { pub fn top_bottom(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
self.istart_end() self.inline_start_end()
} else { } else {
self.bstart_end() self.block_start_end()
} }
} }
@ -660,9 +684,9 @@ impl<T: Add<T, T>> LogicalMargin<T> {
pub fn left_right(&self, mode: WritingMode) -> T { pub fn left_right(&self, mode: WritingMode) -> T {
self.debug_writing_mode.check(mode); self.debug_writing_mode.check(mode);
if mode.is_vertical() { if mode.is_vertical() {
self.bstart_end() self.block_start_end()
} else { } else {
self.istart_end() self.inline_start_end()
} }
} }
} }
@ -673,10 +697,10 @@ impl<T: Add<T, T>> Add<LogicalMargin<T>, LogicalMargin<T>> for LogicalMargin<T>
self.debug_writing_mode.check_debug(other.debug_writing_mode); self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalMargin { LogicalMargin {
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
bstart: self.bstart + other.bstart, block_start: self.block_start + other.block_start,
iend: self.iend + other.iend, inline_end: self.inline_end + other.inline_end,
bend: self.bend + other.bend, block_end: self.block_end + other.block_end,
istart: self.istart + other.istart, inline_start: self.inline_start + other.inline_start,
} }
} }
} }
@ -687,10 +711,10 @@ impl<T: Sub<T, T>> Sub<LogicalMargin<T>, LogicalMargin<T>> for LogicalMargin<T>
self.debug_writing_mode.check_debug(other.debug_writing_mode); self.debug_writing_mode.check_debug(other.debug_writing_mode);
LogicalMargin { LogicalMargin {
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
bstart: self.bstart - other.bstart, block_start: self.block_start - other.block_start,
iend: self.iend - other.iend, inline_end: self.inline_end - other.inline_end,
bend: self.bend - other.bend, block_end: self.block_end - other.block_end,
istart: self.istart - other.istart, inline_start: self.inline_start - other.inline_start,
} }
} }
} }
@ -706,9 +730,11 @@ pub struct LogicalRect<T> {
impl<T: Show> Show for LogicalRect<T> { impl<T: Show> Show for LogicalRect<T> {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> { fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
write!(formatter, "LogicalRect[{}, istart: {}, bstart: {}, isize: {}, bsize: {}]", write!(formatter,
"LogicalRect[{}, inline_start: {}, block_start: {}, \
inline: {}, block: {}]",
self.debug_writing_mode, self.start.i, self.start.b, self.debug_writing_mode, self.start.i, self.start.b,
self.size.isize, self.size.bsize) self.size.inline, self.size.block)
} }
} }
@ -730,10 +756,23 @@ impl<T: Zero> LogicalRect<T> {
impl<T: Copy> LogicalRect<T> { impl<T: Copy> LogicalRect<T> {
#[inline] #[inline]
pub fn new(mode: WritingMode, istart: T, bstart: T, isize: T, bsize: T) -> LogicalRect<T> { pub fn new(mode: WritingMode, inline_start: T, block_start: T, inline: T, block: T)
-> LogicalRect<T> {
LogicalRect { LogicalRect {
start: LogicalPoint::new(mode, istart, bstart), start: LogicalPoint::new(mode, inline_start, block_start),
size: LogicalSize::new(mode, isize, bsize), size: LogicalSize::new(mode, inline, block),
debug_writing_mode: DebugWritingMode::new(mode),
}
}
#[inline]
pub fn from_point_size(mode: WritingMode, start: LogicalPoint<T>, size: LogicalSize<T>)
-> LogicalRect<T> {
start.debug_writing_mode.check(mode);
size.debug_writing_mode.check(mode);
LogicalRect {
start: start,
size: size,
debug_writing_mode: DebugWritingMode::new(mode), debug_writing_mode: DebugWritingMode::new(mode),
} }
} }
@ -743,48 +782,48 @@ impl<T: Copy + Add<T, T> + Sub<T, T>> LogicalRect<T> {
#[inline] #[inline]
pub fn from_physical(mode: WritingMode, rect: Rect<T>, container_size: Size2D<T>) pub fn from_physical(mode: WritingMode, rect: Rect<T>, container_size: Size2D<T>)
-> LogicalRect<T> { -> LogicalRect<T> {
let istart; let inline_start;
let bstart; let block_start;
let isize; let inline;
let bsize; let block;
if mode.is_vertical() { if mode.is_vertical() {
isize = rect.size.height; inline = rect.size.height;
bsize = rect.size.width; block = rect.size.width;
if mode.is_vertical_lr() { if mode.is_vertical_lr() {
bstart = rect.origin.x; block_start = rect.origin.x;
} else { } else {
bstart = container_size.width - (rect.origin.x + rect.size.width); block_start = container_size.width - (rect.origin.x + rect.size.width);
} }
if mode.is_inline_tb() { if mode.is_inline_tb() {
istart = rect.origin.y; inline_start = rect.origin.y;
} else { } else {
istart = container_size.height - (rect.origin.y + rect.size.height); inline_start = container_size.height - (rect.origin.y + rect.size.height);
} }
} else { } else {
isize = rect.size.width; inline = rect.size.width;
bsize = rect.size.height; block = rect.size.height;
bstart = rect.origin.y; block_start = rect.origin.y;
if mode.is_bidi_ltr() { if mode.is_bidi_ltr() {
istart = rect.origin.x; inline_start = rect.origin.x;
} else { } else {
istart = container_size.width - (rect.origin.x + rect.size.width); inline_start = container_size.width - (rect.origin.x + rect.size.width);
} }
} }
LogicalRect { LogicalRect {
start: LogicalPoint::new(mode, istart, bstart), start: LogicalPoint::new(mode, inline_start, block_start),
size: LogicalSize::new(mode, isize, bsize), size: LogicalSize::new(mode, inline, block),
debug_writing_mode: DebugWritingMode::new(mode), debug_writing_mode: DebugWritingMode::new(mode),
} }
} }
#[inline] #[inline]
pub fn iend(&self) -> T { pub fn inline_end(&self) -> T {
self.start.i + self.size.isize self.start.i + self.size.inline
} }
#[inline] #[inline]
pub fn bend(&self) -> T { pub fn block_end(&self) -> T {
self.start.b + self.size.bsize self.start.b + self.size.block
} }
#[inline] #[inline]
@ -795,26 +834,26 @@ impl<T: Copy + Add<T, T> + Sub<T, T>> LogicalRect<T> {
let width; let width;
let height; let height;
if mode.is_vertical() { if mode.is_vertical() {
width = self.size.bsize; width = self.size.block;
height = self.size.isize; height = self.size.inline;
if mode.is_vertical_lr() { if mode.is_vertical_lr() {
x = self.start.b; x = self.start.b;
} else { } else {
x = container_size.width - self.bend(); x = container_size.width - self.block_end();
} }
if mode.is_inline_tb() { if mode.is_inline_tb() {
y = self.start.i; y = self.start.i;
} else { } else {
y = container_size.height - self.iend(); y = container_size.height - self.inline_end();
} }
} else { } else {
width = self.size.isize; width = self.size.inline;
height = self.size.bsize; height = self.size.block;
y = self.start.b; y = self.start.b;
if mode.is_bidi_ltr() { if mode.is_bidi_ltr() {
x = self.start.i; x = self.start.i;
} else { } else {
x = container_size.width - self.iend(); x = container_size.width - self.inline_end();
} }
} }
Rect { Rect {
@ -834,6 +873,41 @@ impl<T: Copy + Add<T, T> + Sub<T, T>> LogicalRect<T> {
mode_to, self.to_physical(mode_from, container_size), container_size) mode_to, self.to_physical(mode_from, container_size), container_size)
} }
} }
pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
LogicalRect {
start: self.start + LogicalSize {
inline: offset.i,
block: offset.b,
debug_writing_mode: offset.debug_writing_mode,
},
size: self.size,
debug_writing_mode: self.debug_writing_mode,
}
}
}
impl<T: Copy + Ord + Add<T, T> + Sub<T, T>> LogicalRect<T> {
#[inline]
pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
self.debug_writing_mode.check_debug(other.debug_writing_mode);
let inline_start = min(self.start.i, other.start.i);
let block_start = min(self.start.b, other.start.b);
LogicalRect {
start: LogicalPoint {
i: inline_start,
b: block_start,
debug_writing_mode: self.debug_writing_mode,
},
size: LogicalSize {
inline: max(self.inline_end(), other.inline_end()) - inline_start,
block: max(self.block_end(), other.block_end()) - block_start,
debug_writing_mode: self.debug_writing_mode,
},
debug_writing_mode: self.debug_writing_mode,
}
}
} }
impl<T: Add<T, T> + Sub<T, T>> Add<LogicalMargin<T>, LogicalRect<T>> for LogicalRect<T> { impl<T: Add<T, T> + Sub<T, T>> Add<LogicalMargin<T>, LogicalRect<T>> for LogicalRect<T> {
@ -844,13 +918,13 @@ impl<T: Add<T, T> + Sub<T, T>> Add<LogicalMargin<T>, LogicalRect<T>> for Logical
start: LogicalPoint { start: LogicalPoint {
// Growing a rectangle on the start side means pushing its // Growing a rectangle on the start side means pushing its
// start point on the negative direction. // start point on the negative direction.
i: self.start.i - other.istart, i: self.start.i - other.inline_start,
b: self.start.b - other.bstart, b: self.start.b - other.block_start,
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
}, },
size: LogicalSize { size: LogicalSize {
isize: self.size.isize + other.istart_end(), inline: self.size.inline + other.inline_start_end(),
bsize: self.size.bsize + other.bstart_end(), block: self.size.block + other.block_start_end(),
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
}, },
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
@ -867,13 +941,13 @@ impl<T: Add<T, T> + Sub<T, T>> Sub<LogicalMargin<T>, LogicalRect<T>> for Logical
start: LogicalPoint { start: LogicalPoint {
// Shrinking a rectangle on the start side means pushing its // Shrinking a rectangle on the start side means pushing its
// start point on the positive direction. // start point on the positive direction.
i: self.start.i + other.istart, i: self.start.i + other.inline_start,
b: self.start.b + other.bstart, b: self.start.b + other.block_start,
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
}, },
size: LogicalSize { size: LogicalSize {
isize: self.size.isize - other.istart_end(), inline: self.size.inline - other.inline_start_end(),
bsize: self.size.bsize - other.bstart_end(), block: self.size.block - other.block_start_end(),
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,
}, },
debug_writing_mode: self.debug_writing_mode, debug_writing_mode: self.debug_writing_mode,

View file

@ -49,6 +49,9 @@ pub struct Opts {
/// and cause it to produce output on that interval (`-m`). /// and cause it to produce output on that interval (`-m`).
pub memory_profiler_period: Option<f64>, pub memory_profiler_period: Option<f64>,
/// Enable experimental web features (`-e`).
pub enable_experimental: bool,
/// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive
/// sequential algorithm. /// sequential algorithm.
pub layout_threads: uint, pub layout_threads: uint,
@ -64,7 +67,7 @@ pub struct Opts {
/// intrinsic widths are computed as a separate pass instead of during flow construction. You /// 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 /// may wish to turn this flag on in order to benchmark style recalculation against other
/// browser engines. /// browser engines.
pub bubble_widths_separately: bool, pub bubble_inline_sizes_separately: bool,
} }
fn print_usage(app: &str, opts: &[getopts::OptGroup]) { fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
@ -87,6 +90,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
getopts::optopt("r", "rendering", "Rendering backend", "direct2d|core-graphics|core-graphics-accelerated|cairo|skia."), getopts::optopt("r", "rendering", "Rendering backend", "direct2d|core-graphics|core-graphics-accelerated|cairo|skia."),
getopts::optopt("s", "size", "Size of tiles", "512"), getopts::optopt("s", "size", "Size of tiles", "512"),
getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""), getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""),
getopts::optflag("e", "experimental", "Enable experimental web features"),
getopts::optopt("t", "threads", "Number of render threads", "1"), getopts::optopt("t", "threads", "Number of render threads", "1"),
getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"), getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"), getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"),
@ -176,11 +180,26 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
device_pixels_per_px: device_pixels_per_px, device_pixels_per_px: device_pixels_per_px,
time_profiler_period: time_profiler_period, time_profiler_period: time_profiler_period,
memory_profiler_period: memory_profiler_period, memory_profiler_period: memory_profiler_period,
enable_experimental: opt_match.opt_present("e"),
layout_threads: layout_threads, layout_threads: layout_threads,
exit_after_load: opt_match.opt_present("x"), exit_after_load: opt_match.opt_present("x"),
output_file: opt_match.opt_str("o"), output_file: opt_match.opt_str("o"),
headless: opt_match.opt_present("z"), headless: opt_match.opt_present("z"),
hard_fail: opt_match.opt_present("f"), hard_fail: opt_match.opt_present("f"),
bubble_widths_separately: opt_match.opt_present("b"), bubble_inline_sizes_separately: opt_match.opt_present("b"),
}) })
} }
static mut EXPERIMENTAL_ENABLED: bool = false;
pub fn set_experimental_enabled(new_value: bool) {
unsafe {
EXPERIMENTAL_ENABLED = new_value;
}
}
pub fn experimental_enabled() -> bool {
unsafe {
EXPERIMENTAL_ENABLED
}
}

View file

@ -82,8 +82,8 @@ fn parse_lists(file: &String, servo_args: &[String]) -> Vec<TestDescAndFn> {
}; };
for line in contents.as_slice().lines() { for line in contents.as_slice().lines() {
// ignore comments // ignore comments or empty lines
if line.starts_with("#") { if line.starts_with("#") || line.is_empty() {
continue; continue;
} }

View file

@ -75,7 +75,10 @@
== pseudo_element_a.html pseudo_element_b.html == pseudo_element_a.html pseudo_element_b.html
== linebreak_simple_a.html linebreak_simple_b.html == linebreak_simple_a.html linebreak_simple_b.html
== linebreak_inline_span_a.html linebreak_inline_span_b.html == linebreak_inline_span_a.html linebreak_inline_span_b.html
== overconstrained_block.html overconstrained_block_ref.html
# Should be == with expected failure. See #2797
!= overconstrained_block.html overconstrained_block_ref.html
== overflow_auto.html overflow_simple_b.html == overflow_auto.html overflow_simple_b.html
== overflow_scroll.html overflow_simple_b.html == overflow_scroll.html overflow_simple_b.html
== overflow_simple_a.html overflow_simple_b.html == overflow_simple_a.html overflow_simple_b.html