mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
auto merge of #3609 : pcwalton/servo/tables, r=SimonSapin
http://dbaron.org/css/intrinsic/ Column spans are not yet supported. This effectively adds support for percentage widths, and it also fixes many bugs, improving the layout of Google and Wikipedia. r? @SimonSapin
This commit is contained in:
commit
e2d7777c41
20 changed files with 1081 additions and 618 deletions
|
@ -2,15 +2,28 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
//! CSS block formatting contexts.
|
//! Layout for CSS block-level elements.
|
||||||
//!
|
//!
|
||||||
//! Terminology Note:
|
//! As a terminology note, the term *absolute positioning* here refers to elements with position
|
||||||
//! As per the CSS Spec, the term 'absolute positioning' here refers to
|
//! `absolute` or `fixed`. The term *positioned element* refers to elements with position
|
||||||
//! elements with position = 'absolute' or 'fixed'.
|
//! `relative`, `absolute`, and `fixed`. The term *containing block* (occasionally abbreviated as
|
||||||
//! The term 'positioned element' refers to elements with position =
|
//! *CB*) is the containing block for the current flow, which differs from the static containing
|
||||||
//! 'relative', 'absolute', or 'fixed'.
|
//! block if the flow is absolutely-positioned.
|
||||||
//!
|
//!
|
||||||
//! CB: Containing Block of the current flow.
|
//! "CSS 2.1" or "CSS 2.2" refers to the editor's draft of the W3C "Cascading Style Sheets Level 2
|
||||||
|
//! Revision 2 (CSS 2.2) Specification" available here:
|
||||||
|
//!
|
||||||
|
//! http://dev.w3.org/csswg/css2/
|
||||||
|
//!
|
||||||
|
//! "INTRINSIC" refers to L. David Baron's "More Precise Definitions of Inline Layout and Table
|
||||||
|
//! Layout" available here:
|
||||||
|
//!
|
||||||
|
//! http://dbaron.org/css/intrinsic/
|
||||||
|
//!
|
||||||
|
//! "CSS-SIZING" refers to the W3C "CSS Intrinsic & Extrinsic Sizing Module Level 3" document
|
||||||
|
//! available here:
|
||||||
|
//!
|
||||||
|
//! http://dev.w3.org/csswg/css-sizing/
|
||||||
|
|
||||||
#![deny(unsafe_block)]
|
#![deny(unsafe_block)]
|
||||||
|
|
||||||
|
@ -22,11 +35,10 @@ use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::{Fragment, ImageFragment, InlineBlockFragment, ScannedTextFragment};
|
use fragment::{Fragment, ImageFragment, InlineBlockFragment, ScannedTextFragment};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse};
|
use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse, MarginsCollapseThrough};
|
||||||
use model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified};
|
use model::{MaybeAuto, NoCollapsibleMargins, Specified, specified, specified_or_none};
|
||||||
use model::{specified_or_none};
|
use table::ColumnInlineSize;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
use style::computed_values::{clear, position};
|
|
||||||
|
|
||||||
use collections::dlist::DList;
|
use collections::dlist::DList;
|
||||||
use geom::{Size2D, Point2D, Rect};
|
use geom::{Size2D, Point2D, Rect};
|
||||||
|
@ -43,8 +55,8 @@ use std::cmp::{max, min};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
|
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
|
||||||
use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, box_sizing};
|
use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, box_sizing, clear};
|
||||||
use style::computed_values::{display, float, overflow};
|
use style::computed_values::{display, float, overflow, position};
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
|
|
||||||
/// Information specific to floated blocks.
|
/// Information specific to floated blocks.
|
||||||
|
@ -762,9 +774,10 @@ impl BlockFlow {
|
||||||
///
|
///
|
||||||
/// This is where we use the preferred inline-sizes and minimum inline-sizes
|
/// This is where we use the preferred inline-sizes and minimum inline-sizes
|
||||||
/// calculated in the bubble-inline-sizes traversal.
|
/// calculated in the bubble-inline-sizes traversal.
|
||||||
fn get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au {
|
pub fn get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au {
|
||||||
min(self.base.intrinsic_inline_sizes.preferred_inline_size,
|
let content_intrinsic_inline_sizes = self.content_intrinsic_inline_sizes();
|
||||||
max(self.base.intrinsic_inline_sizes.minimum_inline_size, available_inline_size))
|
min(content_intrinsic_inline_sizes.preferred_inline_size,
|
||||||
|
max(content_intrinsic_inline_sizes.minimum_inline_size, available_inline_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this is the root flow, shifts all kids down and adjusts our size to account for
|
/// If this is the root flow, shifts all kids down and adjusts our size to account for
|
||||||
|
@ -1291,10 +1304,11 @@ impl BlockFlow {
|
||||||
/// `#[inline(always)]` because this is called only from block or table inline-size assignment
|
/// `#[inline(always)]` because this is called only from block or table inline-size assignment
|
||||||
/// and the code for block layout is significantly simpler.
|
/// and the code for block layout is significantly simpler.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn propagate_assigned_inline_size_to_children(&mut self,
|
pub fn propagate_assigned_inline_size_to_children(
|
||||||
inline_start_content_edge: Au,
|
&mut self,
|
||||||
content_inline_size: Au,
|
inline_start_content_edge: Au,
|
||||||
opt_col_inline_sizes: Option<Vec<Au>>) {
|
content_inline_size: Au,
|
||||||
|
optional_column_inline_sizes: Option<&[ColumnInlineSize]>) {
|
||||||
// Keep track of whether floats could impact each child.
|
// Keep track of whether floats could impact each child.
|
||||||
let mut inline_start_floats_impact_child = self.base.flags.impacted_by_left_floats();
|
let mut inline_start_floats_impact_child = self.base.flags.impacted_by_left_floats();
|
||||||
let mut inline_end_floats_impact_child = self.base.flags.impacted_by_right_floats();
|
let mut inline_end_floats_impact_child = self.base.flags.impacted_by_right_floats();
|
||||||
|
@ -1403,13 +1417,13 @@ impl BlockFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle tables.
|
// Handle tables.
|
||||||
match opt_col_inline_sizes {
|
match optional_column_inline_sizes {
|
||||||
Some(ref col_inline_sizes) => {
|
Some(ref column_inline_sizes) => {
|
||||||
propagate_column_inline_sizes_to_child(kid,
|
propagate_column_inline_sizes_to_child(kid,
|
||||||
i,
|
i,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
col_inline_sizes.as_slice(),
|
*column_inline_sizes,
|
||||||
&mut inline_start_margin_edge)
|
&mut inline_start_margin_edge)
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -1469,6 +1483,23 @@ impl BlockFlow {
|
||||||
// TODO(pcwalton): If the inline-size of this flow is different from the size we estimated
|
// TODO(pcwalton): If the inline-size of this flow is different from the size we estimated
|
||||||
// earlier, lay it out again.
|
// earlier, lay it out again.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_inline_block(&self) -> bool {
|
||||||
|
self.fragment.style().get_box().display == display::inline_block
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the content portion (only) of the intrinsic inline sizes of this flow. This is
|
||||||
|
/// used for calculating shrink-to-fit width. Assumes that intrinsic sizes have already been
|
||||||
|
/// computed for this flow.
|
||||||
|
fn content_intrinsic_inline_sizes(&self) -> IntrinsicISizes {
|
||||||
|
let surrounding_inline_size = self.fragment.surrounding_intrinsic_inline_size();
|
||||||
|
IntrinsicISizes {
|
||||||
|
minimum_inline_size: self.base.intrinsic_inline_sizes.minimum_inline_size -
|
||||||
|
surrounding_inline_size,
|
||||||
|
preferred_inline_size: self.base.intrinsic_inline_sizes.preferred_inline_size -
|
||||||
|
surrounding_inline_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flow for BlockFlow {
|
impl Flow for BlockFlow {
|
||||||
|
@ -1515,35 +1546,31 @@ impl Flow for BlockFlow {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find the maximum inline-size from children.
|
// Find the maximum inline-size from children.
|
||||||
let mut intrinsic_inline_sizes = IntrinsicISizes::new();
|
let mut computation = self.fragment.compute_intrinsic_inline_sizes();
|
||||||
let mut left_float_width = Au(0);
|
let mut left_float_width = Au(0);
|
||||||
let mut right_float_width = Au(0);
|
let mut right_float_width = Au(0);
|
||||||
for child_ctx in self.base.child_iter() {
|
for kid in self.base.child_iter() {
|
||||||
assert!(child_ctx.is_block_flow() ||
|
let is_absolutely_positioned = kid.is_absolutely_positioned();
|
||||||
child_ctx.is_inline_flow() ||
|
let float_kind = kid.float_kind();
|
||||||
child_ctx.is_table_kind());
|
let child_base = flow::mut_base(kid);
|
||||||
|
if !is_absolutely_positioned && !fixed_width {
|
||||||
let float_kind = child_ctx.float_kind();
|
computation.content_intrinsic_sizes.minimum_inline_size =
|
||||||
let child_base = flow::mut_base(child_ctx);
|
max(computation.content_intrinsic_sizes.minimum_inline_size,
|
||||||
|
child_base.intrinsic_inline_sizes.minimum_inline_size);
|
||||||
if !fixed_width {
|
|
||||||
intrinsic_inline_sizes.minimum_inline_size =
|
|
||||||
max(intrinsic_inline_sizes.minimum_inline_size,
|
|
||||||
child_base.intrinsic_inline_sizes.total_minimum_inline_size());
|
|
||||||
|
|
||||||
match float_kind {
|
match float_kind {
|
||||||
float::none => {
|
float::none => {
|
||||||
intrinsic_inline_sizes.preferred_inline_size =
|
computation.content_intrinsic_sizes.preferred_inline_size =
|
||||||
max(intrinsic_inline_sizes.preferred_inline_size,
|
max(computation.content_intrinsic_sizes.preferred_inline_size,
|
||||||
child_base.intrinsic_inline_sizes.total_preferred_inline_size());
|
child_base.intrinsic_inline_sizes.preferred_inline_size);
|
||||||
}
|
}
|
||||||
float::left => {
|
float::left => {
|
||||||
left_float_width = left_float_width +
|
left_float_width = left_float_width +
|
||||||
child_base.intrinsic_inline_sizes.total_preferred_inline_size();
|
child_base.intrinsic_inline_sizes.preferred_inline_size;
|
||||||
}
|
}
|
||||||
float::right => {
|
float::right => {
|
||||||
right_float_width = right_float_width +
|
right_float_width = right_float_width +
|
||||||
child_base.intrinsic_inline_sizes.total_preferred_inline_size();
|
child_base.intrinsic_inline_sizes.preferred_inline_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1551,21 +1578,14 @@ impl Flow for BlockFlow {
|
||||||
flags.union_floated_descendants_flags(child_base.flags);
|
flags.union_floated_descendants_flags(child_base.flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
intrinsic_inline_sizes.preferred_inline_size =
|
// FIXME(pcwalton): This should consider all float descendants, not just children.
|
||||||
max(intrinsic_inline_sizes.preferred_inline_size,
|
// FIXME(pcwalton): This is not well-spec'd; INTRINSIC specifies to do this, but CSS-SIZING
|
||||||
left_float_width + right_float_width);
|
// says not to. In practice, Gecko and WebKit both do this.
|
||||||
|
computation.content_intrinsic_sizes.preferred_inline_size =
|
||||||
|
max(computation.content_intrinsic_sizes.preferred_inline_size,
|
||||||
|
left_float_width + right_float_width);
|
||||||
|
|
||||||
let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes();
|
self.base.intrinsic_inline_sizes = computation.finish();
|
||||||
intrinsic_inline_sizes.minimum_inline_size =
|
|
||||||
max(intrinsic_inline_sizes.minimum_inline_size,
|
|
||||||
fragment_intrinsic_inline_sizes.minimum_inline_size);
|
|
||||||
intrinsic_inline_sizes.preferred_inline_size =
|
|
||||||
max(intrinsic_inline_sizes.preferred_inline_size,
|
|
||||||
fragment_intrinsic_inline_sizes.preferred_inline_size);
|
|
||||||
intrinsic_inline_sizes.surround_inline_size =
|
|
||||||
intrinsic_inline_sizes.surround_inline_size +
|
|
||||||
fragment_intrinsic_inline_sizes.surround_inline_size;
|
|
||||||
self.base.intrinsic_inline_sizes = intrinsic_inline_sizes;
|
|
||||||
|
|
||||||
match self.fragment.style().get_box().float {
|
match self.fragment.style().get_box().float {
|
||||||
float::none => {}
|
float::none => {}
|
||||||
|
@ -1575,11 +1595,11 @@ impl Flow for BlockFlow {
|
||||||
self.base.flags = flags
|
self.base.flags = flags
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
/// called on this context, the context has had its inline-size set by the parent context.
|
/// When called on this context, the context has had its inline-size set by the parent context.
|
||||||
///
|
///
|
||||||
/// Dual fragments consume some inline-size 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
|
||||||
/// contexts.
|
/// (block) contexts.
|
||||||
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("block::assign_inline_sizes {:s}", self.base.debug_id());
|
let _scope = layout_debug_scope!("block::assign_inline_sizes {:s}", self.base.debug_id());
|
||||||
|
|
||||||
|
@ -1632,11 +1652,14 @@ impl Flow for BlockFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move in from the inline-start border edge.
|
// Move in from the inline-start border edge.
|
||||||
let inline_start_content_edge = self.fragment.border_box.start.i + self.fragment.border_padding.inline_start;
|
let inline_start_content_edge = self.fragment.border_box.start.i +
|
||||||
|
self.fragment.border_padding.inline_start;
|
||||||
let padding_and_borders = self.fragment.border_padding.inline_start_end();
|
let padding_and_borders = self.fragment.border_padding.inline_start_end();
|
||||||
let content_inline_size = self.fragment.border_box.size.inline - padding_and_borders;
|
let content_inline_size = self.fragment.border_box.size.inline - padding_and_borders;
|
||||||
|
|
||||||
self.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, None);
|
self.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
||||||
|
content_inline_size,
|
||||||
|
None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assigns block-sizes 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
|
||||||
|
@ -1686,7 +1709,7 @@ impl Flow for BlockFlow {
|
||||||
self.base.block_container_explicit_block_size.unwrap_or(Au(0));
|
self.base.block_container_explicit_block_size.unwrap_or(Au(0));
|
||||||
self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size);
|
self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size);
|
||||||
self.base.position.size.block = self.fragment.border_box.size.block;
|
self.base.position.size.block = self.fragment.border_box.size.block;
|
||||||
} else if self.is_root() || self.is_float() {
|
} else if self.is_root() || self.is_float() || self.is_inline_block() {
|
||||||
// Root element margins should never be collapsed according to CSS § 8.3.1.
|
// Root element margins should never be collapsed according to CSS § 8.3.1.
|
||||||
debug!("assign_block_size: assigning block_size for root flow");
|
debug!("assign_block_size: assigning block_size for root flow");
|
||||||
self.assign_block_size_block_base(ctx, MarginsMayNotCollapse);
|
self.assign_block_size_block_base(ctx, MarginsMayNotCollapse);
|
||||||
|
@ -1882,6 +1905,7 @@ impl ISizeConstraintInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The solutions for the inline-size-and-margins constraint equation.
|
/// The solutions for the inline-size-and-margins constraint equation.
|
||||||
|
#[deriving(Show)]
|
||||||
pub struct ISizeConstraintSolution {
|
pub struct ISizeConstraintSolution {
|
||||||
pub inline_start: Au,
|
pub inline_start: Au,
|
||||||
pub inline_end: Au,
|
pub inline_end: Au,
|
||||||
|
@ -1923,9 +1947,8 @@ impl ISizeConstraintSolution {
|
||||||
pub trait ISizeAndMarginsComputer {
|
pub trait ISizeAndMarginsComputer {
|
||||||
/// Compute the inputs for the ISize constraint equation.
|
/// Compute the inputs for the ISize constraint equation.
|
||||||
///
|
///
|
||||||
/// This is called only once to compute the initial inputs. For
|
/// This is called only once to compute the initial inputs. For calculations involving
|
||||||
/// calculation involving min-inline-size and max-inline-size, we don't need to
|
/// minimum and maximum inline-size, we don't need to recompute these.
|
||||||
/// recompute these.
|
|
||||||
fn compute_inline_size_constraint_inputs(&self,
|
fn compute_inline_size_constraint_inputs(&self,
|
||||||
block: &mut BlockFlow,
|
block: &mut BlockFlow,
|
||||||
parent_flow_inline_size: Au,
|
parent_flow_inline_size: Au,
|
||||||
|
@ -1934,7 +1957,9 @@ pub trait ISizeAndMarginsComputer {
|
||||||
let containing_block_inline_size =
|
let containing_block_inline_size =
|
||||||
self.containing_block_inline_size(block, parent_flow_inline_size, layout_context);
|
self.containing_block_inline_size(block, parent_flow_inline_size, layout_context);
|
||||||
|
|
||||||
block.fragment.compute_border_padding_margins(containing_block_inline_size);
|
block.fragment.compute_block_direction_margins(containing_block_inline_size);
|
||||||
|
block.fragment.compute_inline_direction_margins(containing_block_inline_size);
|
||||||
|
block.fragment.compute_border_and_padding(containing_block_inline_size);
|
||||||
|
|
||||||
let mut computed_inline_size = self.initial_computed_inline_size(block,
|
let mut computed_inline_size = self.initial_computed_inline_size(block,
|
||||||
parent_flow_inline_size,
|
parent_flow_inline_size,
|
||||||
|
@ -1955,7 +1980,8 @@ pub trait ISizeAndMarginsComputer {
|
||||||
let margin = style.logical_margin();
|
let margin = style.logical_margin();
|
||||||
let position = style.logical_position();
|
let position = style.logical_position();
|
||||||
|
|
||||||
let available_inline_size = containing_block_inline_size - block.fragment.border_padding.inline_start_end();
|
let available_inline_size = containing_block_inline_size -
|
||||||
|
block.fragment.border_padding.inline_start_end();
|
||||||
return ISizeConstraintInput::new(
|
return ISizeConstraintInput::new(
|
||||||
computed_inline_size,
|
computed_inline_size,
|
||||||
MaybeAuto::from_style(margin.inline_start, containing_block_inline_size),
|
MaybeAuto::from_style(margin.inline_start, containing_block_inline_size),
|
||||||
|
@ -2015,10 +2041,10 @@ pub trait ISizeAndMarginsComputer {
|
||||||
-> ISizeConstraintSolution;
|
-> ISizeConstraintSolution;
|
||||||
|
|
||||||
fn initial_computed_inline_size(&self,
|
fn initial_computed_inline_size(&self,
|
||||||
block: &mut BlockFlow,
|
block: &mut BlockFlow,
|
||||||
parent_flow_inline_size: Au,
|
parent_flow_inline_size: Au,
|
||||||
ctx: &LayoutContext)
|
ctx: &LayoutContext)
|
||||||
-> MaybeAuto {
|
-> MaybeAuto {
|
||||||
MaybeAuto::from_style(block.fragment().style().content_inline_size(),
|
MaybeAuto::from_style(block.fragment().style().content_inline_size(),
|
||||||
self.containing_block_inline_size(block,
|
self.containing_block_inline_size(block,
|
||||||
parent_flow_inline_size,
|
parent_flow_inline_size,
|
||||||
|
@ -2409,11 +2435,12 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced {
|
||||||
|
|
||||||
/// Calculate used value of inline-size just like we do for inline replaced elements.
|
/// Calculate used value of inline-size just like we do for inline replaced elements.
|
||||||
fn initial_computed_inline_size(&self,
|
fn initial_computed_inline_size(&self,
|
||||||
block: &mut BlockFlow,
|
block: &mut BlockFlow,
|
||||||
_: Au,
|
_: Au,
|
||||||
ctx: &LayoutContext)
|
layout_context: &LayoutContext)
|
||||||
-> MaybeAuto {
|
-> MaybeAuto {
|
||||||
let containing_block_inline_size = block.containing_block_size(ctx.shared.screen_size).inline;
|
let containing_block_inline_size =
|
||||||
|
block.containing_block_size(layout_context.shared.screen_size).inline;
|
||||||
let fragment = block.fragment();
|
let fragment = block.fragment();
|
||||||
fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size);
|
fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size);
|
||||||
// For replaced absolute flow, the rest of the constraint solving will
|
// For replaced absolute flow, the rest of the constraint solving will
|
||||||
|
@ -2459,10 +2486,10 @@ impl ISizeAndMarginsComputer for BlockReplaced {
|
||||||
|
|
||||||
/// Calculate used value of inline-size just like we do for inline replaced elements.
|
/// Calculate used value of inline-size just like we do for inline replaced elements.
|
||||||
fn initial_computed_inline_size(&self,
|
fn initial_computed_inline_size(&self,
|
||||||
block: &mut BlockFlow,
|
block: &mut BlockFlow,
|
||||||
parent_flow_inline_size: Au,
|
parent_flow_inline_size: Au,
|
||||||
_: &LayoutContext)
|
_: &LayoutContext)
|
||||||
-> MaybeAuto {
|
-> MaybeAuto {
|
||||||
let fragment = block.fragment();
|
let fragment = block.fragment();
|
||||||
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
|
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
|
||||||
// For replaced block flow, the rest of the constraint solving will
|
// For replaced block flow, the rest of the constraint solving will
|
||||||
|
@ -2515,10 +2542,10 @@ impl ISizeAndMarginsComputer for FloatReplaced {
|
||||||
|
|
||||||
/// Calculate used value of inline-size just like we do for inline replaced elements.
|
/// Calculate used value of inline-size just like we do for inline replaced elements.
|
||||||
fn initial_computed_inline_size(&self,
|
fn initial_computed_inline_size(&self,
|
||||||
block: &mut BlockFlow,
|
block: &mut BlockFlow,
|
||||||
parent_flow_inline_size: Au,
|
parent_flow_inline_size: Au,
|
||||||
_: &LayoutContext)
|
_: &LayoutContext)
|
||||||
-> MaybeAuto {
|
-> MaybeAuto {
|
||||||
let fragment = block.fragment();
|
let fragment = block.fragment();
|
||||||
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
|
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
|
||||||
// For replaced block flow, the rest of the constraint solving will
|
// For replaced block flow, the rest of the constraint solving will
|
||||||
|
@ -2528,35 +2555,34 @@ impl ISizeAndMarginsComputer for FloatReplaced {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propagate_column_inline_sizes_to_child(kid: &mut Flow,
|
fn propagate_column_inline_sizes_to_child(kid: &mut Flow,
|
||||||
child_index: uint,
|
child_index: uint,
|
||||||
content_inline_size: Au,
|
content_inline_size: Au,
|
||||||
column_inline_sizes: &[Au],
|
column_inline_sizes: &[ColumnInlineSize],
|
||||||
inline_start_margin_edge: &mut Au) {
|
inline_start_margin_edge: &mut Au) {
|
||||||
// If kid is table_rowgroup or table_row, the column inline-sizes info should be copied from its
|
// If kid is table_rowgroup or table_row, the column inline-sizes info should be copied from
|
||||||
// parent.
|
// its parent.
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton): This seems inefficient. Reference count it instead?
|
// FIXME(pcwalton): This seems inefficient. Reference count it instead?
|
||||||
let inline_size = if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() {
|
let inline_size = if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() {
|
||||||
*kid.col_inline_sizes() = column_inline_sizes.iter().map(|&x| x).collect();
|
*kid.column_inline_sizes() = column_inline_sizes.iter().map(|&x| x).collect();
|
||||||
|
|
||||||
// ISize of kid flow is our content inline-size.
|
// ISize of kid flow is our content inline-size.
|
||||||
content_inline_size
|
content_inline_size
|
||||||
} else if kid.is_table_cell() {
|
} else if kid.is_table_cell() {
|
||||||
// If kid is table_cell, the x offset and inline-size for each cell should be
|
column_inline_sizes[child_index].minimum_length
|
||||||
// calculated from parent's column inline-sizes info.
|
|
||||||
*inline_start_margin_edge = if child_index == 0 {
|
|
||||||
Au(0)
|
|
||||||
} else {
|
|
||||||
*inline_start_margin_edge + column_inline_sizes[child_index - 1]
|
|
||||||
};
|
|
||||||
|
|
||||||
column_inline_sizes[child_index]
|
|
||||||
} else {
|
} else {
|
||||||
// ISize of kid flow is our content inline-size.
|
// ISize of kid flow is our content inline-size.
|
||||||
content_inline_size
|
content_inline_size
|
||||||
};
|
};
|
||||||
|
|
||||||
let kid_base = flow::mut_base(kid);
|
{
|
||||||
kid_base.position.start.i = *inline_start_margin_edge;
|
let kid_base = flow::mut_base(kid);
|
||||||
kid_base.block_container_inline_size = inline_size;
|
kid_base.position.start.i = *inline_start_margin_edge;
|
||||||
|
kid_base.block_container_inline_size = inline_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if kid.is_table_cell() {
|
||||||
|
*inline_start_margin_edge = *inline_start_margin_edge + inline_size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -767,8 +767,8 @@ impl<'a> FlowConstructor<'a> {
|
||||||
flow.add_new_child(anonymous_flow);
|
flow.add_new_child(anonymous_flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with possibly
|
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
|
||||||
/// other `TableCaptionFlow`s or `TableFlow`s underneath it.
|
/// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
|
||||||
fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode,
|
fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode,
|
||||||
float_value: float::T) -> ConstructionResult {
|
float_value: float::T) -> ConstructionResult {
|
||||||
let fragment = Fragment::new_from_specific_info(node, TableWrapperFragment);
|
let fragment = Fragment::new_from_specific_info(node, TableWrapperFragment);
|
||||||
|
|
|
@ -36,7 +36,7 @@ use incremental::RestyleDamage;
|
||||||
use inline::InlineFlow;
|
use inline::InlineFlow;
|
||||||
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
||||||
use parallel::FlowParallelInfo;
|
use parallel::FlowParallelInfo;
|
||||||
use table::TableFlow;
|
use table::{ColumnInlineSize, TableFlow};
|
||||||
use table_caption::TableCaptionFlow;
|
use table_caption::TableCaptionFlow;
|
||||||
use table_cell::TableCellFlow;
|
use table_cell::TableCellFlow;
|
||||||
use table_colgroup::TableColGroupFlow;
|
use table_colgroup::TableColGroupFlow;
|
||||||
|
@ -164,20 +164,8 @@ pub trait Flow: fmt::Show + ToString + Sync {
|
||||||
|
|
||||||
/// If this is a table row or table rowgroup or table flow, returns column inline-sizes.
|
/// If this is a table row or table rowgroup or table flow, returns column inline-sizes.
|
||||||
/// Fails otherwise.
|
/// Fails otherwise.
|
||||||
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
|
fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
|
||||||
fail!("called col_inline_sizes() on an other flow than table-row/table-rowgroup/table")
|
fail!("called column_inline_sizes() on non-table flow")
|
||||||
}
|
|
||||||
|
|
||||||
/// If this is a table row flow or table rowgroup flow or table flow, returns column min
|
|
||||||
/// inline-sizes. Fails otherwise.
|
|
||||||
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
|
||||||
fail!("called col_min_inline_sizes() on an other flow than table-row/table-rowgroup/table")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If this is a table row flow or table rowgroup flow or table flow, returns column min
|
|
||||||
/// inline-sizes. Fails otherwise.
|
|
||||||
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
|
||||||
fail!("called col_pref_inline_sizes() on an other flow than table-row/table-rowgroup/table")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main methods
|
// Main methods
|
||||||
|
|
|
@ -14,7 +14,7 @@ use flow::Flow;
|
||||||
use flow_ref::FlowRef;
|
use flow_ref::FlowRef;
|
||||||
use inline::{InlineFragmentContext, InlineMetrics};
|
use inline::{InlineFragmentContext, InlineMetrics};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{Auto, IntrinsicISizes, MaybeAuto, Specified, specified};
|
use model::{Auto, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, Specified, specified};
|
||||||
use model;
|
use model;
|
||||||
use text;
|
use text;
|
||||||
use util::{OpaqueNodeMethods, ToGfxColor};
|
use util::{OpaqueNodeMethods, ToGfxColor};
|
||||||
|
@ -529,51 +529,93 @@ impl Fragment {
|
||||||
self.inline_context.as_mut().unwrap().styles.push(style.clone());
|
self.inline_context.as_mut().unwrap().styles.push(style.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text
|
/// Determines which quantities (border/padding/margin/specified) should be included in the
|
||||||
/// or replaced elements.
|
/// intrinsic inline size of this fragment.
|
||||||
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes {
|
fn quantities_included_in_intrinsic_inline_size(&self)
|
||||||
let (use_margins, use_padding) = match self.specific {
|
-> QuantitiesIncludedInIntrinsicInlineSizes {
|
||||||
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) |
|
GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) |
|
||||||
InputFragment => (true, true),
|
InputFragment => QuantitiesIncludedInIntrinsicInlineSizes::all(),
|
||||||
TableFragment | TableCellFragment => (false, true),
|
TableFragment | TableCellFragment => {
|
||||||
TableWrapperFragment => (true, false),
|
IntrinsicInlineSizeIncludesPadding |
|
||||||
TableRowFragment => (false, false),
|
IntrinsicInlineSizeIncludesBorder |
|
||||||
|
IntrinsicInlineSizeIncludesSpecified
|
||||||
|
}
|
||||||
|
TableWrapperFragment => {
|
||||||
|
IntrinsicInlineSizeIncludesMargins |
|
||||||
|
IntrinsicInlineSizeIncludesBorder |
|
||||||
|
IntrinsicInlineSizeIncludesSpecified
|
||||||
|
}
|
||||||
|
TableRowFragment => {
|
||||||
|
IntrinsicInlineSizeIncludesBorder |
|
||||||
|
IntrinsicInlineSizeIncludesSpecified
|
||||||
|
}
|
||||||
ScannedTextFragment(_) | TableColumnFragment(_) | UnscannedTextFragment(_) |
|
ScannedTextFragment(_) | TableColumnFragment(_) | UnscannedTextFragment(_) |
|
||||||
InlineAbsoluteHypotheticalFragment(_) => {
|
InlineAbsoluteHypotheticalFragment(_) => {
|
||||||
// Styles are irrelevant for these kinds of fragments.
|
QuantitiesIncludedInIntrinsicInlineSizes::empty()
|
||||||
return IntrinsicISizes::new()
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the portion of the intrinsic inline-size that consists of borders, padding, and/or
|
||||||
|
/// margins.
|
||||||
|
///
|
||||||
|
/// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
|
||||||
|
pub fn surrounding_intrinsic_inline_size(&self) -> Au {
|
||||||
|
let flags = self.quantities_included_in_intrinsic_inline_size();
|
||||||
let style = self.style();
|
let style = self.style();
|
||||||
let inline_size = MaybeAuto::from_style(style.content_inline_size(),
|
|
||||||
Au(0)).specified_or_zero();
|
|
||||||
|
|
||||||
let margin = style.logical_margin();
|
// FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
|
||||||
let (margin_inline_start, margin_inline_end) = if use_margins {
|
// This will likely need to be done by pushing down definite sizes during selector
|
||||||
(MaybeAuto::from_style(margin.inline_start, Au(0)).specified_or_zero(),
|
// cascading.
|
||||||
|
let margin = if flags.contains(IntrinsicInlineSizeIncludesMargins) {
|
||||||
|
let margin = style.logical_margin();
|
||||||
|
(MaybeAuto::from_style(margin.inline_start, Au(0)).specified_or_zero() +
|
||||||
MaybeAuto::from_style(margin.inline_end, Au(0)).specified_or_zero())
|
MaybeAuto::from_style(margin.inline_end, Au(0)).specified_or_zero())
|
||||||
} else {
|
} else {
|
||||||
(Au(0), Au(0))
|
Au(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
let padding = style.logical_padding();
|
// FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
|
||||||
let (padding_inline_start, padding_inline_end) = if use_padding {
|
// This will likely need to be done by pushing down definite sizes during selector
|
||||||
(model::specified(padding.inline_start, Au(0)),
|
// cascading.
|
||||||
|
let padding = if flags.contains(IntrinsicInlineSizeIncludesPadding) {
|
||||||
|
let padding = style.logical_padding();
|
||||||
|
(model::specified(padding.inline_start, Au(0)) +
|
||||||
model::specified(padding.inline_end, Au(0)))
|
model::specified(padding.inline_end, Au(0)))
|
||||||
} else {
|
} else {
|
||||||
(Au(0), Au(0))
|
Au(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let border = if flags.contains(IntrinsicInlineSizeIncludesBorder) {
|
||||||
|
self.border_width().inline_start_end()
|
||||||
|
} else {
|
||||||
|
Au(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
margin + padding + border
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text
|
||||||
|
/// or replaced elements.
|
||||||
|
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution {
|
||||||
|
let flags = self.quantities_included_in_intrinsic_inline_size();
|
||||||
|
let style = self.style();
|
||||||
|
let specified = if flags.contains(IntrinsicInlineSizeIncludesSpecified) {
|
||||||
|
MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero()
|
||||||
|
} else {
|
||||||
|
Au(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
|
// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
|
||||||
let border = self.border_width();
|
let surrounding_inline_size = self.surrounding_intrinsic_inline_size();
|
||||||
let surround_inline_size = margin_inline_start + margin_inline_end + padding_inline_start + padding_inline_end +
|
|
||||||
border.inline_start_end();
|
|
||||||
|
|
||||||
IntrinsicISizes {
|
IntrinsicISizesContribution {
|
||||||
minimum_inline_size: inline_size,
|
content_intrinsic_sizes: IntrinsicISizes {
|
||||||
preferred_inline_size: inline_size,
|
minimum_inline_size: specified,
|
||||||
surround_inline_size: surround_inline_size,
|
preferred_inline_size: specified,
|
||||||
|
},
|
||||||
|
surrounding_size: surrounding_inline_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,28 +643,58 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the border, padding, and vertical margins from the containing block inline-size and the
|
/// Computes the margins in the inline direction from the containing block inline-size and the
|
||||||
/// style. After this call, the `border_padding` and the vertical direction of the `margin`
|
/// style. After this call, the inline direction of the `margin` field will be correct.
|
||||||
/// field will be correct.
|
///
|
||||||
pub fn compute_border_padding_margins(&mut self,
|
/// Do not use this method if the inline direction margins are to be computed some other way
|
||||||
containing_block_inline_size: Au) {
|
/// (for example, via constraint solving for blocks).
|
||||||
// Compute vertical margins. Note that this value will be ignored by layout if the style
|
pub fn compute_inline_direction_margins(&mut self, containing_block_inline_size: Au) {
|
||||||
// specifies `auto`.
|
match self.specific {
|
||||||
|
TableFragment | TableCellFragment | TableRowFragment | TableColumnFragment(_) => {
|
||||||
|
self.margin.inline_start = Au(0);
|
||||||
|
self.margin.inline_end = Au(0)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let margin = self.style().logical_margin();
|
||||||
|
self.margin.inline_start =
|
||||||
|
MaybeAuto::from_style(margin.inline_start, containing_block_inline_size)
|
||||||
|
.specified_or_zero();
|
||||||
|
self.margin.inline_end =
|
||||||
|
MaybeAuto::from_style(margin.inline_end, containing_block_inline_size)
|
||||||
|
.specified_or_zero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the margins in the block direction from the containing block inline-size and the
|
||||||
|
/// style. After this call, the block direction of the `margin` field will be correct.
|
||||||
|
///
|
||||||
|
/// Do not use this method if the block direction margins are to be computed some other way
|
||||||
|
/// (for example, via constraint solving for absolutely-positioned flows).
|
||||||
|
pub fn compute_block_direction_margins(&mut self, containing_block_inline_size: Au) {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
TableFragment | TableCellFragment | TableRowFragment | TableColumnFragment(_) => {
|
TableFragment | TableCellFragment | TableRowFragment | TableColumnFragment(_) => {
|
||||||
self.margin.block_start = Au(0);
|
self.margin.block_start = Au(0);
|
||||||
self.margin.block_end = Au(0)
|
self.margin.block_end = Au(0)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// NB: Percentages are relative to containing block inline-size (not block-size) per CSS 2.1.
|
// NB: Percentages are relative to containing block inline-size (not block-size)
|
||||||
|
// per CSS 2.1.
|
||||||
let margin = self.style().logical_margin();
|
let margin = self.style().logical_margin();
|
||||||
self.margin.block_start = MaybeAuto::from_style(margin.block_start, containing_block_inline_size)
|
self.margin.block_start =
|
||||||
|
MaybeAuto::from_style(margin.block_start, containing_block_inline_size)
|
||||||
|
.specified_or_zero();
|
||||||
|
self.margin.block_end =
|
||||||
|
MaybeAuto::from_style(margin.block_end, containing_block_inline_size)
|
||||||
.specified_or_zero();
|
.specified_or_zero();
|
||||||
self.margin.block_end = MaybeAuto::from_style(margin.block_end, containing_block_inline_size)
|
|
||||||
.specified_or_zero()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the border and padding in both inline and block directions from the containing
|
||||||
|
/// block inline-size and the style. After this call, the `border_padding` field will be
|
||||||
|
/// correct.
|
||||||
|
pub fn compute_border_and_padding(&mut self, containing_block_inline_size: Au) {
|
||||||
// Compute border.
|
// Compute border.
|
||||||
let border = self.border_width();
|
let border = self.border_width();
|
||||||
|
|
||||||
|
@ -1241,28 +1313,23 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the intrinsic inline-sizes of this fragment.
|
/// Computes the intrinsic inline-sizes of this fragment.
|
||||||
pub fn intrinsic_inline_sizes(&mut self) -> IntrinsicISizes {
|
pub fn compute_intrinsic_inline_sizes(&mut self) -> IntrinsicISizesContribution {
|
||||||
let mut result = self.style_specified_intrinsic_inline_size();
|
let mut result = self.style_specified_intrinsic_inline_size();
|
||||||
|
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
||||||
TableColumnFragment(_) | TableRowFragment | TableWrapperFragment |
|
TableColumnFragment(_) | TableRowFragment | TableWrapperFragment |
|
||||||
InlineAbsoluteHypotheticalFragment(_) | InputFragment => {}
|
InlineAbsoluteHypotheticalFragment(_) | InputFragment => {}
|
||||||
InlineBlockFragment(ref mut info) => {
|
InlineBlockFragment(ref mut info) => {
|
||||||
let block_flow = info.flow_ref.get_mut().as_block();
|
let block_flow = info.flow_ref.get_mut().as_block();
|
||||||
result.minimum_inline_size = max(result.minimum_inline_size,
|
result.union_block(&block_flow.base.intrinsic_inline_sizes)
|
||||||
block_flow.base.intrinsic_inline_sizes.minimum_inline_size +
|
|
||||||
block_flow.base.intrinsic_inline_sizes.surround_inline_size);
|
|
||||||
result.preferred_inline_size = max(result.preferred_inline_size,
|
|
||||||
block_flow.base.intrinsic_inline_sizes.preferred_inline_size +
|
|
||||||
block_flow.base.intrinsic_inline_sizes.surround_inline_size);
|
|
||||||
}
|
}
|
||||||
ImageFragment(ref mut image_fragment_info) => {
|
ImageFragment(ref mut image_fragment_info) => {
|
||||||
let image_inline_size = image_fragment_info.image_inline_size();
|
let image_inline_size = image_fragment_info.image_inline_size();
|
||||||
result.minimum_inline_size = max(result.minimum_inline_size, image_inline_size);
|
result.union_block(&IntrinsicISizes {
|
||||||
result.preferred_inline_size = max(result.preferred_inline_size,
|
minimum_inline_size: image_inline_size,
|
||||||
image_inline_size);
|
preferred_inline_size: image_inline_size,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
ScannedTextFragment(ref text_fragment_info) => {
|
ScannedTextFragment(ref text_fragment_info) => {
|
||||||
let range = &text_fragment_info.range;
|
let range = &text_fragment_info.range;
|
||||||
|
@ -1274,10 +1341,10 @@ impl Fragment {
|
||||||
.metrics_for_range(range)
|
.metrics_for_range(range)
|
||||||
.advance_width;
|
.advance_width;
|
||||||
|
|
||||||
result.minimum_inline_size = max(result.minimum_inline_size,
|
result.union_block(&IntrinsicISizes {
|
||||||
min_line_inline_size);
|
minimum_inline_size: min_line_inline_size,
|
||||||
result.preferred_inline_size = max(result.preferred_inline_size,
|
preferred_inline_size: max_line_inline_size,
|
||||||
max_line_inline_size);
|
})
|
||||||
}
|
}
|
||||||
UnscannedTextFragment(..) => {
|
UnscannedTextFragment(..) => {
|
||||||
fail!("Unscanned text fragments should have been scanned by now!")
|
fail!("Unscanned text fragments should have been scanned by now!")
|
||||||
|
@ -1293,10 +1360,8 @@ impl Fragment {
|
||||||
let border_width = style.logical_border_width().inline_start_end();
|
let border_width = style.logical_border_width().inline_start_end();
|
||||||
let padding_inline_size =
|
let padding_inline_size =
|
||||||
model::padding_from_style(&**style, Au(0)).inline_start_end();
|
model::padding_from_style(&**style, Au(0)).inline_start_end();
|
||||||
result.minimum_inline_size = result.minimum_inline_size + border_width +
|
result.surrounding_size = result.surrounding_size + border_width +
|
||||||
padding_inline_size;
|
padding_inline_size;
|
||||||
result.preferred_inline_size = result.preferred_inline_size +
|
|
||||||
border_width + padding_inline_size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1477,7 +1542,9 @@ impl Fragment {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let inline_end = inline_end_range.map(|inline_end_range| SplitInfo::new(inline_end_range, text_fragment_info));
|
let inline_end = inline_end_range.map(|inline_end_range| {
|
||||||
|
SplitInfo::new(inline_end_range, text_fragment_info)
|
||||||
|
});
|
||||||
|
|
||||||
Some((inline_start, inline_end, text_fragment_info.run.clone()))
|
Some((inline_start, inline_end, text_fragment_info.run.clone()))
|
||||||
}
|
}
|
||||||
|
@ -1502,8 +1569,7 @@ impl Fragment {
|
||||||
|
|
||||||
/// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced
|
/// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced
|
||||||
/// content per CSS 2.1 § 10.3.2.
|
/// content per CSS 2.1 § 10.3.2.
|
||||||
pub fn assign_replaced_inline_size_if_necessary(&mut self,
|
pub fn assign_replaced_inline_size_if_necessary(&mut self, container_inline_size: Au) {
|
||||||
container_inline_size: Au) {
|
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
||||||
TableRowFragment | TableWrapperFragment | InputFragment => return,
|
TableRowFragment | TableWrapperFragment | InputFragment => return,
|
||||||
|
@ -1515,8 +1581,6 @@ impl Fragment {
|
||||||
InlineAbsoluteHypotheticalFragment(_) => {}
|
InlineAbsoluteHypotheticalFragment(_) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.compute_border_padding_margins(container_inline_size);
|
|
||||||
|
|
||||||
let style_inline_size = self.style().content_inline_size();
|
let style_inline_size = self.style().content_inline_size();
|
||||||
let style_block_size = self.style().content_block_size();
|
let style_block_size = self.style().content_block_size();
|
||||||
let style_min_inline_size = self.style().min_inline_size();
|
let style_min_inline_size = self.style().min_inline_size();
|
||||||
|
@ -1528,17 +1592,16 @@ impl Fragment {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
InlineAbsoluteHypotheticalFragment(ref mut info) => {
|
InlineAbsoluteHypotheticalFragment(ref mut info) => {
|
||||||
let block_flow = info.flow_ref.get_mut().as_block();
|
let block_flow = info.flow_ref.get_mut().as_block();
|
||||||
block_flow.base.block_container_inline_size =
|
block_flow.base.position.size.inline =
|
||||||
block_flow.base.intrinsic_inline_sizes.preferred_inline_size +
|
block_flow.base.intrinsic_inline_sizes.preferred_inline_size;
|
||||||
block_flow.base.intrinsic_inline_sizes.surround_inline_size;
|
|
||||||
|
|
||||||
// This is a hypothetical box, so it takes up no space.
|
// This is a hypothetical box, so it takes up no space.
|
||||||
self.border_box.size.inline = Au(0);
|
self.border_box.size.inline = Au(0);
|
||||||
}
|
}
|
||||||
InlineBlockFragment(ref mut info) => {
|
InlineBlockFragment(ref mut info) => {
|
||||||
let block_flow = info.flow_ref.get_mut().as_block();
|
let block_flow = info.flow_ref.get_mut().as_block();
|
||||||
self.border_box.size.inline = block_flow.base.intrinsic_inline_sizes.preferred_inline_size +
|
self.border_box.size.inline =
|
||||||
block_flow.base.intrinsic_inline_sizes.surround_inline_size;
|
block_flow.base.intrinsic_inline_sizes.preferred_inline_size;
|
||||||
block_flow.base.block_container_inline_size = self.border_box.size.inline;
|
block_flow.base.block_container_inline_size = self.border_box.size.inline;
|
||||||
}
|
}
|
||||||
ScannedTextFragment(_) => {
|
ScannedTextFragment(_) => {
|
||||||
|
@ -1652,7 +1715,8 @@ impl Fragment {
|
||||||
InlineBlockFragment(ref mut info) => {
|
InlineBlockFragment(ref mut info) => {
|
||||||
// Not the primary fragment, so we do not take the noncontent size into account.
|
// Not the primary fragment, so we do not take the noncontent size into account.
|
||||||
let block_flow = info.flow_ref.get_mut().as_block();
|
let block_flow = info.flow_ref.get_mut().as_block();
|
||||||
self.border_box.size.block = block_flow.base.position.size.block;
|
self.border_box.size.block = block_flow.base.position.size.block +
|
||||||
|
block_flow.fragment.margin.block_start_end()
|
||||||
}
|
}
|
||||||
InlineAbsoluteHypotheticalFragment(ref mut info) => {
|
InlineAbsoluteHypotheticalFragment(ref mut info) => {
|
||||||
// Not the primary fragment, so we do not take the noncontent size into account.
|
// Not the primary fragment, so we do not take the noncontent size into account.
|
||||||
|
@ -1686,7 +1750,9 @@ impl Fragment {
|
||||||
let font_style = text::computed_style_to_font_style(&*self.style);
|
let font_style = text::computed_style_to_font_style(&*self.style);
|
||||||
let font_metrics = text::font_metrics_for_style(layout_context.font_context(),
|
let font_metrics = text::font_metrics_for_style(layout_context.font_context(),
|
||||||
&font_style);
|
&font_style);
|
||||||
InlineMetrics::from_block_height(&font_metrics, block_flow.base.position.size.block)
|
InlineMetrics::from_block_height(&font_metrics,
|
||||||
|
block_flow.base.position.size.block +
|
||||||
|
block_flow.fragment.margin.block_start_end())
|
||||||
}
|
}
|
||||||
InlineAbsoluteHypotheticalFragment(_) => {
|
InlineAbsoluteHypotheticalFragment(_) => {
|
||||||
// Hypothetical boxes take up no space.
|
// Hypothetical boxes take up no space.
|
||||||
|
@ -1838,3 +1904,12 @@ impl fmt::Show for Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
flags QuantitiesIncludedInIntrinsicInlineSizes: u8 {
|
||||||
|
static IntrinsicInlineSizeIncludesMargins = 0x01,
|
||||||
|
static IntrinsicInlineSizeIncludesPadding = 0x02,
|
||||||
|
static IntrinsicInlineSizeIncludesBorder = 0x04,
|
||||||
|
static IntrinsicInlineSizeIncludesSpecified = 0x08,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,15 @@ use flow;
|
||||||
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
||||||
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
|
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::IntrinsicISizes;
|
use model::IntrinsicISizesContribution;
|
||||||
use text;
|
use text;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use collections::{Deque, RingBuf};
|
use collections::{Deque, RingBuf};
|
||||||
use geom::Rect;
|
use geom::{Rect, Size2D};
|
||||||
use gfx::display_list::{ContentLevel, DisplayList};
|
use gfx::display_list::{ContentLevel, DisplayList};
|
||||||
use gfx::font::FontMetrics;
|
use gfx::font::FontMetrics;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use geom::Size2D;
|
|
||||||
use gfx::text::glyph::CharIndex;
|
use gfx::text::glyph::CharIndex;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
|
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
|
||||||
|
@ -922,24 +921,12 @@ impl Flow for InlineFlow {
|
||||||
flow::mut_base(kid).floats = Floats::new(writing_mode);
|
flow::mut_base(kid).floats = Floats::new(writing_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut intrinsic_inline_sizes = IntrinsicISizes::new();
|
let mut computation = IntrinsicISizesContribution::new();
|
||||||
for fragment in self.fragments.fragments.iter_mut() {
|
for fragment in self.fragments.fragments.iter_mut() {
|
||||||
debug!("Flow: measuring {}", *fragment);
|
debug!("Flow: measuring {}", *fragment);
|
||||||
|
computation.union_inline(&fragment.compute_intrinsic_inline_sizes().finish())
|
||||||
let fragment_intrinsic_inline_sizes =
|
|
||||||
fragment.intrinsic_inline_sizes();
|
|
||||||
intrinsic_inline_sizes.minimum_inline_size = max(
|
|
||||||
intrinsic_inline_sizes.minimum_inline_size,
|
|
||||||
fragment_intrinsic_inline_sizes.minimum_inline_size);
|
|
||||||
intrinsic_inline_sizes.preferred_inline_size =
|
|
||||||
intrinsic_inline_sizes.preferred_inline_size +
|
|
||||||
fragment_intrinsic_inline_sizes.preferred_inline_size;
|
|
||||||
intrinsic_inline_sizes.surround_inline_size =
|
|
||||||
intrinsic_inline_sizes.surround_inline_size +
|
|
||||||
fragment_intrinsic_inline_sizes.surround_inline_size;
|
|
||||||
}
|
}
|
||||||
|
self.base.intrinsic_inline_sizes = computation.finish()
|
||||||
self.base.intrinsic_inline_sizes = intrinsic_inline_sizes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
|
@ -960,6 +947,9 @@ impl Flow for InlineFlow {
|
||||||
let inline_size = self.base.position.size.inline;
|
let inline_size = self.base.position.size.inline;
|
||||||
let this = &mut *self;
|
let this = &mut *self;
|
||||||
for fragment in this.fragments.fragments.iter_mut() {
|
for fragment in this.fragments.fragments.iter_mut() {
|
||||||
|
fragment.compute_border_and_padding(inline_size);
|
||||||
|
fragment.compute_block_direction_margins(inline_size);
|
||||||
|
fragment.compute_inline_direction_margins(inline_size);
|
||||||
fragment.assign_replaced_inline_size_if_necessary(inline_size);
|
fragment.assign_replaced_inline_size_if_necessary(inline_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1130,6 +1120,7 @@ impl Flow for InlineFlow {
|
||||||
let block_flow = info.flow_ref.get_mut().as_block();
|
let block_flow = info.flow_ref.get_mut().as_block();
|
||||||
// FIXME(#2795): Get the real container size
|
// FIXME(#2795): Get the real container size
|
||||||
let container_size = Size2D::zero();
|
let container_size = Size2D::zero();
|
||||||
|
|
||||||
block_flow.base.abs_position =
|
block_flow.base.abs_position =
|
||||||
self.base.abs_position +
|
self.base.abs_position +
|
||||||
fragment.border_box.start.to_physical(self.base.writing_mode,
|
fragment.border_box.start.to_physical(self.base.writing_mode,
|
||||||
|
|
|
@ -249,14 +249,11 @@ pub struct IntrinsicISizes {
|
||||||
pub minimum_inline_size: Au,
|
pub minimum_inline_size: Au,
|
||||||
/// The *preferred inline-size* of the content.
|
/// The *preferred inline-size* of the content.
|
||||||
pub preferred_inline_size: Au,
|
pub preferred_inline_size: Au,
|
||||||
/// The estimated sum of borders, padding, and margins. Some calculations use this information
|
|
||||||
/// when computing intrinsic inline-sizes.
|
|
||||||
pub surround_inline_size: Au,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for IntrinsicISizes {
|
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_inline_size, self.preferred_inline_size, self.surround_inline_size)
|
write!(f, "min={}, pref={}", self.minimum_inline_size, self.preferred_inline_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,20 +262,67 @@ impl IntrinsicISizes {
|
||||||
IntrinsicISizes {
|
IntrinsicISizes {
|
||||||
minimum_inline_size: Au(0),
|
minimum_inline_size: Au(0),
|
||||||
preferred_inline_size: Au(0),
|
preferred_inline_size: Au(0),
|
||||||
surround_inline_size: Au(0),
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The temporary result of the computation of intrinsic inline-sizes.
|
||||||
|
pub struct IntrinsicISizesContribution {
|
||||||
|
/// Intrinsic sizes for the content only (not counting borders, padding, or margins).
|
||||||
|
pub content_intrinsic_sizes: IntrinsicISizes,
|
||||||
|
/// The inline size of borders and padding, as well as margins if appropriate.
|
||||||
|
pub surrounding_size: Au,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntrinsicISizesContribution {
|
||||||
|
/// Creates and initializes an inline size computation with all sizes set to zero.
|
||||||
|
pub fn new() -> IntrinsicISizesContribution {
|
||||||
|
IntrinsicISizesContribution {
|
||||||
|
content_intrinsic_sizes: IntrinsicISizes::new(),
|
||||||
|
surrounding_size: Au(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_minimum_inline_size(&self) -> Au {
|
/// Adds the content intrinsic sizes and the surrounding size together to yield the final
|
||||||
self.minimum_inline_size + self.surround_inline_size
|
/// intrinsic size computation.
|
||||||
|
pub fn finish(self) -> IntrinsicISizes {
|
||||||
|
IntrinsicISizes {
|
||||||
|
minimum_inline_size: self.content_intrinsic_sizes.minimum_inline_size +
|
||||||
|
self.surrounding_size,
|
||||||
|
preferred_inline_size: self.content_intrinsic_sizes.preferred_inline_size +
|
||||||
|
self.surrounding_size,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_preferred_inline_size(&self) -> Au {
|
/// Updates the computation so that the minimum is the maximum of the current minimum and the
|
||||||
self.preferred_inline_size + self.surround_inline_size
|
/// given minimum and the preferred is the sum of the current preferred and the given
|
||||||
|
/// preferred. This is used when laying out fragments in the inline direction.
|
||||||
|
///
|
||||||
|
/// FIXME(pcwalton): This is incorrect when the inline fragment contains forced line breaks
|
||||||
|
/// (e.g. `<br>` or `white-space: pre`).
|
||||||
|
pub fn union_inline(&mut self, sizes: &IntrinsicISizes) {
|
||||||
|
self.content_intrinsic_sizes.minimum_inline_size =
|
||||||
|
max(self.content_intrinsic_sizes.minimum_inline_size, sizes.minimum_inline_size);
|
||||||
|
self.content_intrinsic_sizes.preferred_inline_size =
|
||||||
|
self.content_intrinsic_sizes.preferred_inline_size + sizes.preferred_inline_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the computation so that the minimum is the maximum of the current minimum and the
|
||||||
|
/// given minimum and the preferred is the maximum of the current preferred and the given
|
||||||
|
/// preferred. This can be useful when laying out fragments in the block direction (but note
|
||||||
|
/// that it does not take floats into account, so `BlockFlow` does not use it).
|
||||||
|
///
|
||||||
|
/// This is used when contributing the intrinsic sizes for individual fragments.
|
||||||
|
pub fn union_block(&mut self, sizes: &IntrinsicISizes) {
|
||||||
|
self.content_intrinsic_sizes.minimum_inline_size =
|
||||||
|
max(self.content_intrinsic_sizes.minimum_inline_size, sizes.minimum_inline_size);
|
||||||
|
self.content_intrinsic_sizes.preferred_inline_size =
|
||||||
|
max(self.content_intrinsic_sizes.preferred_inline_size, sizes.preferred_inline_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Useful helper data type when computing values for blocks and positioned elements.
|
/// Useful helper data type when computing values for blocks and positioned elements.
|
||||||
|
#[deriving(PartialEq)]
|
||||||
pub enum MaybeAuto {
|
pub enum MaybeAuto {
|
||||||
Auto,
|
Auto,
|
||||||
Specified(Au),
|
Specified(Au),
|
||||||
|
@ -290,7 +334,9 @@ impl MaybeAuto {
|
||||||
-> MaybeAuto {
|
-> MaybeAuto {
|
||||||
match length {
|
match length {
|
||||||
computed::LPA_Auto => Auto,
|
computed::LPA_Auto => Auto,
|
||||||
computed::LPA_Percentage(percent) => Specified(containing_length.scale_by(percent)),
|
computed::LPA_Percentage(percent) => {
|
||||||
|
Specified(containing_length.scale_by(percent))
|
||||||
|
}
|
||||||
computed::LPA_Length(length) => Specified(length)
|
computed::LPA_Length(length) => Specified(length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use floats::FloatKind;
|
||||||
use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use fragment::Fragment;
|
use fragment::Fragment;
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
|
use model::{IntrinsicISizes, IntrinsicISizesContribution};
|
||||||
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
|
@ -21,7 +22,8 @@ use servo_util::geometry::Au;
|
||||||
use servo_util::logical_geometry::LogicalRect;
|
use servo_util::logical_geometry::LogicalRect;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style::computed_values::table_layout;
|
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, table_layout};
|
||||||
|
use style::CSSFloat;
|
||||||
|
|
||||||
/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
|
/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
|
||||||
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
|
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
|
||||||
|
@ -30,14 +32,8 @@ use style::computed_values::table_layout;
|
||||||
pub struct TableFlow {
|
pub struct TableFlow {
|
||||||
pub block_flow: BlockFlow,
|
pub block_flow: BlockFlow,
|
||||||
|
|
||||||
/// Column inline-sizes
|
/// Information about the inline-sizes of each column.
|
||||||
pub col_inline_sizes: Vec<Au>,
|
pub column_inline_sizes: Vec<ColumnInlineSize>,
|
||||||
|
|
||||||
/// Column min inline-sizes.
|
|
||||||
pub col_min_inline_sizes: Vec<Au>,
|
|
||||||
|
|
||||||
/// Column pref inline-sizes.
|
|
||||||
pub col_pref_inline_sizes: Vec<Au>,
|
|
||||||
|
|
||||||
/// Table-layout property
|
/// Table-layout property
|
||||||
pub table_layout: TableLayout,
|
pub table_layout: TableLayout,
|
||||||
|
@ -56,9 +52,7 @@ impl TableFlow {
|
||||||
};
|
};
|
||||||
TableFlow {
|
TableFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: Vec::new(),
|
||||||
col_min_inline_sizes: vec!(),
|
|
||||||
col_pref_inline_sizes: vec!(),
|
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,9 +69,7 @@ impl TableFlow {
|
||||||
};
|
};
|
||||||
TableFlow {
|
TableFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: Vec::new(),
|
||||||
col_min_inline_sizes: vec!(),
|
|
||||||
col_pref_inline_sizes: vec!(),
|
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,30 +87,33 @@ impl TableFlow {
|
||||||
};
|
};
|
||||||
TableFlow {
|
TableFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: Vec::new(),
|
||||||
col_min_inline_sizes: vec!(),
|
|
||||||
col_pref_inline_sizes: vec!(),
|
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the corresponding value of self_inline-sizes if a value of kid_inline-sizes has larger value
|
/// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has
|
||||||
/// than one of self_inline-sizes.
|
/// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline
|
||||||
pub fn update_col_inline_sizes(self_inline_sizes: &mut Vec<Au>, kid_inline_sizes: &Vec<Au>) -> Au {
|
/// sizes.
|
||||||
let mut sum_inline_sizes = Au(0);
|
pub fn update_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnInlineSize>,
|
||||||
let mut kid_inline_sizes_it = kid_inline_sizes.iter();
|
child_inline_sizes: &Vec<ColumnInlineSize>)
|
||||||
for self_inline_size in self_inline_sizes.iter_mut() {
|
-> IntrinsicISizes {
|
||||||
match kid_inline_sizes_it.next() {
|
let mut total_inline_sizes = IntrinsicISizes::new();
|
||||||
Some(kid_inline_size) => {
|
for (parent_sizes, child_sizes) in parent_inline_sizes.iter_mut()
|
||||||
if *self_inline_size < *kid_inline_size {
|
.zip(child_inline_sizes.iter()) {
|
||||||
*self_inline_size = *kid_inline_size;
|
*parent_sizes = ColumnInlineSize {
|
||||||
}
|
minimum_length: max(parent_sizes.minimum_length, child_sizes.minimum_length),
|
||||||
},
|
percentage: parent_sizes.greatest_percentage(child_sizes),
|
||||||
None => {}
|
preferred: max(parent_sizes.preferred, child_sizes.preferred),
|
||||||
}
|
constrained: parent_sizes.constrained || child_sizes.constrained
|
||||||
sum_inline_sizes = sum_inline_sizes + *self_inline_size;
|
};
|
||||||
|
|
||||||
|
total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size +
|
||||||
|
parent_sizes.minimum_length;
|
||||||
|
total_inline_sizes.preferred_inline_size = total_inline_sizes.preferred_inline_size +
|
||||||
|
parent_sizes.preferred;
|
||||||
}
|
}
|
||||||
sum_inline_sizes
|
total_inline_sizes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign block-size for table flow.
|
/// Assign block-size for table flow.
|
||||||
|
@ -155,16 +150,8 @@ impl Flow for TableFlow {
|
||||||
&mut self.block_flow
|
&mut self.block_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
|
fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
|
||||||
&mut self.col_inline_sizes
|
&mut self.column_inline_sizes
|
||||||
}
|
|
||||||
|
|
||||||
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
|
||||||
&self.col_min_inline_sizes
|
|
||||||
}
|
|
||||||
|
|
||||||
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
|
||||||
&self.col_pref_inline_sizes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The specified column inline-sizes 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
|
||||||
|
@ -175,81 +162,77 @@ impl Flow for TableFlow {
|
||||||
let _scope = layout_debug_scope!("table::bubble_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table::bubble_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
|
||||||
let mut min_inline_size = Au(0);
|
let mut computation = IntrinsicISizesContribution::new();
|
||||||
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());
|
debug_assert!(kid.is_proper_table_child());
|
||||||
|
|
||||||
if kid.is_table_colgroup() {
|
if kid.is_table_colgroup() {
|
||||||
self.col_inline_sizes.push_all(kid.as_table_colgroup().inline_sizes.as_slice());
|
for specified_inline_size in kid.as_table_colgroup().inline_sizes.iter() {
|
||||||
self.col_min_inline_sizes = self.col_inline_sizes.clone();
|
self.column_inline_sizes.push(ColumnInlineSize {
|
||||||
self.col_pref_inline_sizes = self.col_inline_sizes.clone();
|
minimum_length: match *specified_inline_size {
|
||||||
|
LPA_Auto | LPA_Percentage(_) => Au(0),
|
||||||
|
LPA_Length(length) => length,
|
||||||
|
},
|
||||||
|
percentage: match *specified_inline_size {
|
||||||
|
LPA_Auto | LPA_Length(_) => 0.0,
|
||||||
|
LPA_Percentage(percentage) => percentage,
|
||||||
|
},
|
||||||
|
preferred: Au(0),
|
||||||
|
constrained: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
} else if kid.is_table_rowgroup() || kid.is_table_row() {
|
} else if kid.is_table_rowgroup() || kid.is_table_row() {
|
||||||
// read column inline-sizes from table-row-group/table-row, and assign
|
// Read column inline-sizes from the table-row-group/table-row, and assign
|
||||||
// inline-size=0 for the columns not defined in column-group
|
// inline-size=0 for the columns not defined in the column group.
|
||||||
// FIXME: need to read inline-sizes from either table-header-group OR
|
// FIXME: Need to read inline-sizes from either table-header-group OR the first
|
||||||
// first table-row
|
// table-row.
|
||||||
match self.table_layout {
|
match self.table_layout {
|
||||||
FixedLayout => {
|
FixedLayout => {
|
||||||
let kid_col_inline_sizes = kid.col_inline_sizes();
|
// Fixed table layout only looks at the first row.
|
||||||
if !did_first_row {
|
if !did_first_row {
|
||||||
did_first_row = true;
|
did_first_row = true;
|
||||||
let mut child_inline_sizes = kid_col_inline_sizes.iter();
|
for child_column_inline_size in kid.column_inline_sizes().iter() {
|
||||||
for col_inline_size in self.col_inline_sizes.iter_mut() {
|
self.column_inline_sizes.push(*child_column_inline_size);
|
||||||
match child_inline_sizes.next() {
|
|
||||||
Some(child_inline_size) => {
|
|
||||||
if *col_inline_size == Au::new(0) {
|
|
||||||
*col_inline_size = *child_inline_size;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let num_child_cols = kid_col_inline_sizes.len();
|
}
|
||||||
let num_cols = self.col_inline_sizes.len();
|
|
||||||
debug!("table until the previous row has {} column(s) and this row has {} column(s)",
|
|
||||||
num_cols, num_child_cols);
|
|
||||||
for i in range(num_cols, num_child_cols) {
|
|
||||||
self.col_inline_sizes.push((*kid_col_inline_sizes)[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AutoLayout => {
|
AutoLayout => {
|
||||||
min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
|
let child_column_inline_sizes = kid.column_inline_sizes();
|
||||||
pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
|
let mut child_intrinsic_sizes =
|
||||||
|
TableFlow::update_column_inline_sizes(&mut self.column_inline_sizes,
|
||||||
|
child_column_inline_sizes);
|
||||||
|
|
||||||
// update the number of column inline-sizes from table-rows.
|
// Add new columns if processing this row caused us to discover them.
|
||||||
let num_cols = self.col_min_inline_sizes.len();
|
let child_column_count = child_column_inline_sizes.len();
|
||||||
let num_child_cols = kid.col_min_inline_sizes().len();
|
let parent_column_count = self.column_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 {} \
|
||||||
num_cols, num_child_cols);
|
column(s)",
|
||||||
for i in range(num_cols, num_child_cols) {
|
parent_column_count,
|
||||||
self.col_inline_sizes.push(Au::new(0));
|
child_column_count);
|
||||||
let new_kid_min = kid.col_min_inline_sizes()[i];
|
self.column_inline_sizes.reserve(child_column_count);
|
||||||
self.col_min_inline_sizes.push( new_kid_min );
|
for i in range(parent_column_count, child_column_count) {
|
||||||
let new_kid_pref = kid.col_pref_inline_sizes()[i];
|
let inline_size_for_new_column = (*child_column_inline_sizes)[i];
|
||||||
self.col_pref_inline_sizes.push( new_kid_pref );
|
child_intrinsic_sizes.minimum_inline_size =
|
||||||
min_inline_size = min_inline_size + new_kid_min;
|
child_intrinsic_sizes.minimum_inline_size +
|
||||||
pref_inline_size = pref_inline_size + new_kid_pref;
|
inline_size_for_new_column.minimum_length;
|
||||||
|
child_intrinsic_sizes.preferred_inline_size =
|
||||||
|
child_intrinsic_sizes.preferred_inline_size +
|
||||||
|
inline_size_for_new_column.preferred;
|
||||||
|
self.column_inline_sizes.push(inline_size_for_new_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computation.union_block(&child_intrinsic_sizes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fragment_intrinsic_inline_sizes = self.block_flow.fragment.intrinsic_inline_sizes();
|
self.block_flow.base.intrinsic_inline_sizes = computation.finish()
|
||||||
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
|
|
||||||
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
|
|
||||||
max(min_inline_size, pref_inline_size);
|
|
||||||
self.block_flow.base.intrinsic_inline_sizes.surround_inline_size =
|
|
||||||
fragment_intrinsic_inline_sizes.surround_inline_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
/// called on this context, the context has had its inline-size set by the parent context.
|
/// When called on this context, the context has had its inline-size set by the parent context.
|
||||||
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table::assign_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table::assign_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table");
|
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table");
|
||||||
|
@ -258,37 +241,50 @@ impl Flow for TableFlow {
|
||||||
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
|
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
|
||||||
|
|
||||||
let mut num_unspecified_inline_sizes = 0;
|
let mut num_unspecified_inline_sizes = 0;
|
||||||
let mut total_column_inline_size = Au::new(0);
|
let mut total_column_inline_size = Au(0);
|
||||||
for col_inline_size in self.col_inline_sizes.iter() {
|
for column_inline_size in self.column_inline_sizes.iter() {
|
||||||
if *col_inline_size == Au::new(0) {
|
let this_column_inline_size = column_inline_size.minimum_length;
|
||||||
num_unspecified_inline_sizes += 1;
|
if this_column_inline_size == Au(0) {
|
||||||
|
num_unspecified_inline_sizes += 1
|
||||||
} else {
|
} else {
|
||||||
total_column_inline_size = total_column_inline_size.add(col_inline_size);
|
total_column_inline_size = total_column_inline_size + this_column_inline_size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let inline_size_computer = InternalTable;
|
let inline_size_computer = InternalTable;
|
||||||
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
|
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
||||||
|
layout_context,
|
||||||
|
containing_block_inline_size);
|
||||||
|
|
||||||
let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
|
let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
|
||||||
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
||||||
let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
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
|
||||||
// any, or among all the columns if all are specified.
|
// if there are any, or among all the columns if all are specified.
|
||||||
if (total_column_inline_size < content_inline_size) && (num_unspecified_inline_sizes == 0) {
|
if total_column_inline_size < content_inline_size &&
|
||||||
let ratio = content_inline_size.to_f64().unwrap() / total_column_inline_size.to_f64().unwrap();
|
num_unspecified_inline_sizes == 0 {
|
||||||
for col_inline_size in self.col_inline_sizes.iter_mut() {
|
let extra_column_inline_size = content_inline_size;
|
||||||
*col_inline_size = (*col_inline_size).scale_by(ratio);
|
(content_inline_size - total_column_inline_size) /
|
||||||
|
(self.column_inline_sizes.len() as i32);
|
||||||
|
for column_inline_size in self.column_inline_sizes.iter_mut() {
|
||||||
|
column_inline_size.minimum_length = column_inline_size.minimum_length +
|
||||||
|
extra_column_inline_size;
|
||||||
|
column_inline_size.percentage = 0.0;
|
||||||
}
|
}
|
||||||
} else if num_unspecified_inline_sizes != 0 {
|
} else if num_unspecified_inline_sizes != 0 {
|
||||||
let extra_column_inline_size = (content_inline_size - total_column_inline_size) / num_unspecified_inline_sizes;
|
let extra_column_inline_size =
|
||||||
for col_inline_size in self.col_inline_sizes.iter_mut() {
|
(content_inline_size - total_column_inline_size) /
|
||||||
if *col_inline_size == Au(0) {
|
num_unspecified_inline_sizes;
|
||||||
*col_inline_size = extra_column_inline_size;
|
for column_inline_size in self.column_inline_sizes.iter_mut() {
|
||||||
|
if column_inline_size.minimum_length == Au(0) &&
|
||||||
|
column_inline_size.percentage == 0.0 {
|
||||||
|
column_inline_size.minimum_length = extra_column_inline_size /
|
||||||
|
num_unspecified_inline_sizes
|
||||||
}
|
}
|
||||||
|
column_inline_size.percentage = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,7 +295,10 @@ impl Flow for TableFlow {
|
||||||
self.block_flow.base.flags.set_impacted_by_left_floats(false);
|
self.block_flow.base.flags.set_impacted_by_left_floats(false);
|
||||||
self.block_flow.base.flags.set_impacted_by_right_floats(false);
|
self.block_flow.base.flags.set_impacted_by_right_floats(false);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, Some(self.col_inline_sizes.clone()));
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
inline_start_content_edge,
|
||||||
|
content_inline_size,
|
||||||
|
Some(self.column_inline_sizes.as_slice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
|
@ -340,10 +339,12 @@ impl ISizeAndMarginsComputer for InternalTable {
|
||||||
///
|
///
|
||||||
/// CSS Section 10.4: Minimum and Maximum inline-sizes
|
/// CSS Section 10.4: Minimum and Maximum inline-sizes
|
||||||
fn compute_used_inline_size(&self,
|
fn compute_used_inline_size(&self,
|
||||||
block: &mut BlockFlow,
|
block: &mut BlockFlow,
|
||||||
ctx: &LayoutContext,
|
ctx: &LayoutContext,
|
||||||
parent_flow_inline_size: Au) {
|
parent_flow_inline_size: Au) {
|
||||||
let input = self.compute_inline_size_constraint_inputs(block, parent_flow_inline_size, ctx);
|
let input = self.compute_inline_size_constraint_inputs(block,
|
||||||
|
parent_flow_inline_size,
|
||||||
|
ctx);
|
||||||
let solution = self.solve_inline_size_constraints(block, &input);
|
let solution = self.solve_inline_size_constraints(block, &input);
|
||||||
self.set_inline_size_constraint_solutions(block, solution);
|
self.set_inline_size_constraint_solutions(block, solution);
|
||||||
}
|
}
|
||||||
|
@ -351,6 +352,48 @@ impl ISizeAndMarginsComputer for InternalTable {
|
||||||
/// Solve the inline-size and margins constraints for this block flow.
|
/// Solve the inline-size and margins constraints for this block flow.
|
||||||
fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
|
fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
|
||||||
-> ISizeConstraintSolution {
|
-> ISizeConstraintSolution {
|
||||||
ISizeConstraintSolution::new(input.available_inline_size, Au::new(0), Au::new(0))
|
ISizeConstraintSolution::new(input.available_inline_size, Au(0), Au(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about the inline sizes of columns within a table.
|
||||||
|
///
|
||||||
|
/// During table inline-size bubbling, we might need to store both a percentage constraint and a
|
||||||
|
/// specific width constraint. For instance, one cell might say that it wants to be 100 pixels wide
|
||||||
|
/// in the inline direction and another cell might say that it wants to take up 20% of the inline-
|
||||||
|
/// size of the table. Now because we bubble up these constraints during the bubble-inline-sizes
|
||||||
|
/// phase of layout, we don't know yet how wide the table is ultimately going to be in the inline
|
||||||
|
/// direction. As we need to pick the maximum width of all cells for a column (in this case, the
|
||||||
|
/// maximum of 100 pixels and 20% of the table), the preceding constraint means that we must
|
||||||
|
/// potentially store both a specified width *and* a specified percentage, so that the inline-size
|
||||||
|
/// assignment phase of layout will know which one to pick.
|
||||||
|
#[deriving(Clone, Encodable, Show)]
|
||||||
|
pub struct ColumnInlineSize {
|
||||||
|
/// The preferred intrinsic inline size.
|
||||||
|
pub preferred: Au,
|
||||||
|
/// The largest specified size of this column as a length.
|
||||||
|
pub minimum_length: Au,
|
||||||
|
/// The largest specified size of this column as a percentage (`width` property).
|
||||||
|
pub percentage: CSSFloat,
|
||||||
|
/// Whether the column inline size is *constrained* per INTRINSIC § 4.1.
|
||||||
|
pub constrained: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnInlineSize {
|
||||||
|
/// Returns the true minimum size of this column, given the containing block's inline size.
|
||||||
|
/// Beware that this is generally only correct for fixed table layout. (Compare CSS 2.1 §
|
||||||
|
/// 17.5.2.1 with the algorithm in INTRINSIC § 4.)
|
||||||
|
pub fn minimum(&self, containing_block_inline_size: Au) -> Au {
|
||||||
|
max(self.minimum_length, containing_block_inline_size.scale_by(self.percentage))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the higher of the two percentages specified in `self` and `other`.
|
||||||
|
pub fn greatest_percentage(&self, other: &ColumnInlineSize) -> CSSFloat {
|
||||||
|
if self.percentage > other.percentage {
|
||||||
|
self.percentage
|
||||||
|
} else {
|
||||||
|
other.percentage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,8 @@ pub struct TableCellFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableCellFlow {
|
impl TableCellFlow {
|
||||||
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) -> TableCellFlow {
|
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment)
|
||||||
|
-> TableCellFlow {
|
||||||
TableCellFlow {
|
TableCellFlow {
|
||||||
block_flow: BlockFlow::from_node_and_fragment(node, fragment)
|
block_flow: BlockFlow::from_node_and_fragment(node, fragment)
|
||||||
}
|
}
|
||||||
|
@ -91,14 +92,15 @@ impl Flow for TableCellFlow {
|
||||||
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = specified_inline_size
|
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = specified_inline_size
|
||||||
}
|
}
|
||||||
if self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size <
|
if self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size <
|
||||||
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size {
|
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size {
|
||||||
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
|
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
|
||||||
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size;
|
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
/// called on this context, the context has had its inline-size set by the parent table row.
|
/// When called on this context, the context has had its inline-size set by the parent table
|
||||||
|
/// row.
|
||||||
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table_cell::assign_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table_cell::assign_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
@ -108,16 +110,20 @@ impl Flow for TableCellFlow {
|
||||||
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
|
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
|
||||||
|
|
||||||
let inline_size_computer = InternalTable;
|
let inline_size_computer = InternalTable;
|
||||||
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
|
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
||||||
|
ctx,
|
||||||
|
containing_block_inline_size);
|
||||||
|
|
||||||
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
|
let inline_start_content_edge =
|
||||||
|
self.block_flow.fragment.border_box.start.i +
|
||||||
self.block_flow.fragment.border_padding.inline_start;
|
self.block_flow.fragment.border_padding.inline_start;
|
||||||
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
||||||
let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
let content_inline_size =
|
||||||
|
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
None);
|
None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
|
|
|
@ -10,11 +10,11 @@ use context::LayoutContext;
|
||||||
use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow};
|
use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow};
|
||||||
use fragment::{Fragment, TableColumnFragment};
|
use fragment::{Fragment, TableColumnFragment};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{MaybeAuto};
|
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use style::computed_values::LengthOrPercentageOrAuto;
|
||||||
|
|
||||||
/// A table formatting context.
|
/// A table formatting context.
|
||||||
pub struct TableColGroupFlow {
|
pub struct TableColGroupFlow {
|
||||||
|
@ -27,14 +27,17 @@ pub struct TableColGroupFlow {
|
||||||
/// The table column fragments
|
/// The table column fragments
|
||||||
pub cols: Vec<Fragment>,
|
pub cols: Vec<Fragment>,
|
||||||
|
|
||||||
/// The specified inline-sizes of table columns
|
/// The specified inline-sizes of table columns. (We use `LengthOrPercentageOrAuto` here in
|
||||||
pub inline_sizes: Vec<Au>,
|
/// lieu of `ColumnInlineSize` because column groups do not establish minimum or preferred
|
||||||
|
/// inline sizes.)
|
||||||
|
pub inline_sizes: Vec<LengthOrPercentageOrAuto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableColGroupFlow {
|
impl TableColGroupFlow {
|
||||||
pub fn from_node_and_fragments(node: &ThreadSafeLayoutNode,
|
pub fn from_node_and_fragments(node: &ThreadSafeLayoutNode,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
fragments: Vec<Fragment>) -> TableColGroupFlow {
|
fragments: Vec<Fragment>)
|
||||||
|
-> TableColGroupFlow {
|
||||||
TableColGroupFlow {
|
TableColGroupFlow {
|
||||||
base: BaseFlow::new((*node).clone()),
|
base: BaseFlow::new((*node).clone()),
|
||||||
fragment: Some(fragment),
|
fragment: Some(fragment),
|
||||||
|
@ -58,27 +61,25 @@ impl Flow for TableColGroupFlow {
|
||||||
self.base.debug_id());
|
self.base.debug_id());
|
||||||
|
|
||||||
for fragment in self.cols.iter() {
|
for fragment in self.cols.iter() {
|
||||||
// get the specified value from inline-size property
|
// Retrieve the specified value from the appropriate CSS property.
|
||||||
let inline_size = MaybeAuto::from_style(fragment.style().content_inline_size(),
|
let inline_size = fragment.style().content_inline_size();
|
||||||
Au::new(0)).specified_or_zero();
|
|
||||||
|
|
||||||
let span: int = match fragment.specific {
|
let span: int = match fragment.specific {
|
||||||
TableColumnFragment(col_fragment) => col_fragment.span.unwrap_or(1),
|
TableColumnFragment(col_fragment) => col_fragment.span.unwrap_or(1),
|
||||||
_ => fail!("Other fragment come out in TableColGroupFlow. {:?}", fragment.specific)
|
_ => fail!("non-table-column fragment inside table column?!"),
|
||||||
};
|
};
|
||||||
for _ in range(0, span) {
|
for _ in range(0, span) {
|
||||||
self.inline_sizes.push(inline_size);
|
self.inline_sizes.push(inline_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Table column inline-sizes are assigned in table flow and propagated to table row or rowgroup flow.
|
/// Table column inline-sizes are assigned in the table flow and propagated to table row flows
|
||||||
/// Therefore, table colgroup flow does not need to assign its inline-size.
|
/// and/or rowgroup flows. Therefore, table colgroup flows do not need to assign inline-sizes.
|
||||||
fn assign_inline_sizes(&mut self, _ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, _: &LayoutContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Table column do not have block-size.
|
/// Table columns do not have block-size.
|
||||||
fn assign_block_size(&mut self, _ctx: &LayoutContext) {
|
fn assign_block_size(&mut self, _: &LayoutContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
|
fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
|
||||||
|
|
|
@ -14,27 +14,22 @@ use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::Fragment;
|
use fragment::Fragment;
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use table::InternalTable;
|
use table::{ColumnInlineSize, InternalTable};
|
||||||
use model::{MaybeAuto, Specified, Auto};
|
use model::{MaybeAuto, Specified, Auto};
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage};
|
||||||
|
|
||||||
/// A table formatting context.
|
/// A single row of a table.
|
||||||
#[deriving(Encodable)]
|
#[deriving(Encodable)]
|
||||||
pub struct TableRowFlow {
|
pub struct TableRowFlow {
|
||||||
pub block_flow: BlockFlow,
|
pub block_flow: BlockFlow,
|
||||||
|
|
||||||
/// Column inline-sizes.
|
/// Information about the inline-sizes of each column.
|
||||||
pub col_inline_sizes: Vec<Au>,
|
pub column_inline_sizes: Vec<ColumnInlineSize>,
|
||||||
|
|
||||||
/// Column min inline-sizes.
|
|
||||||
pub col_min_inline_sizes: Vec<Au>,
|
|
||||||
|
|
||||||
/// Column pref inline-sizes.
|
|
||||||
pub col_pref_inline_sizes: Vec<Au>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableRowFlow {
|
impl TableRowFlow {
|
||||||
|
@ -43,9 +38,7 @@ 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_inline_sizes: vec!(),
|
column_inline_sizes: Vec::new()
|
||||||
col_min_inline_sizes: vec!(),
|
|
||||||
col_pref_inline_sizes: vec!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +47,7 @@ impl TableRowFlow {
|
||||||
-> TableRowFlow {
|
-> TableRowFlow {
|
||||||
TableRowFlow {
|
TableRowFlow {
|
||||||
block_flow: BlockFlow::from_node(constructor, node),
|
block_flow: BlockFlow::from_node(constructor, node),
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: Vec::new()
|
||||||
col_min_inline_sizes: vec!(),
|
|
||||||
col_pref_inline_sizes: vec!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +56,8 @@ impl TableRowFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
|
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
|
||||||
// TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
|
// TODO: If border-collapse: collapse, block_start_offset, block_end_offset, and
|
||||||
// should be updated. Currently, they are set as Au(0).
|
// inline_start_offset should be updated. Currently, they are set as Au(0).
|
||||||
(Au(0), Au(0), Au(0))
|
(Au(0), Au(0), Au(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,19 +73,21 @@ impl TableRowFlow {
|
||||||
|
|
||||||
let /* mut */ cur_y = block_start_offset;
|
let /* mut */ cur_y = block_start_offset;
|
||||||
|
|
||||||
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `block-size`, minimum block-size of all cells )
|
// Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of all
|
||||||
let mut max_y = Au::new(0);
|
// cells).
|
||||||
|
let mut max_y = Au(0);
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
kid.assign_block_size_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 block-size
|
// TODO: Percentage block-size
|
||||||
let child_specified_block_size = MaybeAuto::from_style(child_fragment.style().content_block_size(),
|
let child_specified_block_size =
|
||||||
Au::new(0)).specified_or_zero();
|
MaybeAuto::from_style(child_fragment.style().content_block_size(),
|
||||||
max_y =
|
Au::new(0)).specified_or_zero();
|
||||||
max(max_y,
|
max_y = max(max_y,
|
||||||
child_specified_block_size + child_fragment.border_padding.block_start_end());
|
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.start.b = cur_y;
|
child_node.position.start.b = cur_y;
|
||||||
|
@ -103,7 +96,11 @@ impl TableRowFlow {
|
||||||
|
|
||||||
let mut block_size = max_y;
|
let mut block_size = max_y;
|
||||||
// TODO: Percentage block-size
|
// TODO: Percentage block-size
|
||||||
block_size = match MaybeAuto::from_style(self.block_flow.fragment.style().content_block_size(), Au(0)) {
|
block_size = match MaybeAuto::from_style(self.block_flow
|
||||||
|
.fragment
|
||||||
|
.style()
|
||||||
|
.content_block_size(),
|
||||||
|
Au(0)) {
|
||||||
Auto => block_size,
|
Auto => block_size,
|
||||||
Specified(value) => max(value, block_size)
|
Specified(value) => max(value, block_size)
|
||||||
};
|
};
|
||||||
|
@ -153,70 +150,84 @@ impl Flow for TableRowFlow {
|
||||||
&mut self.block_flow
|
&mut self.block_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
|
fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
|
||||||
&mut self.col_inline_sizes
|
&mut self.column_inline_sizes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When
|
||||||
&self.col_min_inline_sizes
|
/// called on this context, all child contexts have had their min/pref inline-sizes set. This
|
||||||
}
|
/// function must decide min/pref inline-sizes based on child context inline-sizes and
|
||||||
|
/// dimensions of any fragments it is responsible for flowing.
|
||||||
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
|
||||||
&self.col_pref_inline_sizes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When called
|
|
||||||
/// on this context, all child contexts have had their min/pref inline-sizes set. This function must
|
|
||||||
/// decide min/pref inline-sizes based on child context inline-sizes and dimensions of any fragments it is
|
|
||||||
/// responsible for flowing.
|
|
||||||
/// Min/pref inline-sizes 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 inline-sizes 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_inline_sizes(&mut self) {
|
fn bubble_inline_sizes(&mut self) {
|
||||||
let _scope = layout_debug_scope!("table_row::bubble_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table_row::bubble_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
|
||||||
let mut min_inline_size = Au(0);
|
// Bubble up the specified inline-sizes from child table cells.
|
||||||
let mut pref_inline_size = Au(0);
|
let (mut min_inline_size, mut pref_inline_size) = (Au(0), Au(0));
|
||||||
/* 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 inline-sizes of cells. These are used in fixed table layout calculation.
|
// Collect the specified column inline-size of the cell. This is used in both fixed and
|
||||||
{
|
// automatic table layout calculation.
|
||||||
let child_fragment = kid.as_table_cell().fragment();
|
let child_specified_inline_size = kid.as_table_cell()
|
||||||
let child_specified_inline_size = MaybeAuto::from_style(child_fragment.style().content_inline_size(),
|
.fragment()
|
||||||
Au::new(0)).specified_or_zero();
|
.style()
|
||||||
self.col_inline_sizes.push(child_specified_inline_size);
|
.content_inline_size();
|
||||||
}
|
|
||||||
|
|
||||||
// collect min_inline-size & pref_inline-size of children cells for automatic table layout calculation.
|
// Collect minimum and preferred inline-sizes of the cell for automatic table layout
|
||||||
|
// calculation.
|
||||||
let child_base = flow::mut_base(kid);
|
let child_base = flow::mut_base(kid);
|
||||||
self.col_min_inline_sizes.push(child_base.intrinsic_inline_sizes.minimum_inline_size);
|
let child_column_inline_size = ColumnInlineSize {
|
||||||
self.col_pref_inline_sizes.push(child_base.intrinsic_inline_sizes.preferred_inline_size);
|
minimum_length: match child_specified_inline_size {
|
||||||
min_inline_size = min_inline_size + child_base.intrinsic_inline_sizes.minimum_inline_size;
|
LPA_Auto | LPA_Percentage(_) => {
|
||||||
pref_inline_size = pref_inline_size + child_base.intrinsic_inline_sizes.preferred_inline_size;
|
child_base.intrinsic_inline_sizes.minimum_inline_size
|
||||||
|
}
|
||||||
|
LPA_Length(length) => length,
|
||||||
|
},
|
||||||
|
percentage: match child_specified_inline_size {
|
||||||
|
LPA_Auto | LPA_Length(_) => 0.0,
|
||||||
|
LPA_Percentage(percentage) => percentage,
|
||||||
|
},
|
||||||
|
preferred: child_base.intrinsic_inline_sizes.preferred_inline_size,
|
||||||
|
constrained: match child_specified_inline_size {
|
||||||
|
LPA_Length(_) => true,
|
||||||
|
LPA_Auto | LPA_Percentage(_) => false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
|
||||||
|
pref_inline_size = pref_inline_size + child_column_inline_size.preferred;
|
||||||
|
self.column_inline_sizes.push(child_column_inline_size);
|
||||||
}
|
}
|
||||||
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
|
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
|
||||||
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(
|
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(min_inline_size,
|
||||||
min_inline_size, pref_inline_size);
|
pref_inline_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
/// on this context, the context has had its inline-size set by the parent context.
|
/// When called on this context, the context has had its inline-size set by the parent context.
|
||||||
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table_row::assign_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table_row::assign_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
debug!("assign_inline_sizes({}): assigning inline_size 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_inline_size = self.block_flow.base.block_container_inline_size;
|
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
|
||||||
// FIXME: In case of border-collapse: collapse, inline-start_content_edge should be border-inline-start
|
// FIXME: In case of border-collapse: collapse, inline_start_content_edge should be
|
||||||
let inline_start_content_edge = Au::new(0);
|
// border_inline_start.
|
||||||
|
let inline_start_content_edge = Au(0);
|
||||||
|
|
||||||
let inline_size_computer = InternalTable;
|
let inline_size_computer = InternalTable;
|
||||||
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
|
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
||||||
|
ctx,
|
||||||
|
containing_block_inline_size);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, Au(0), Some(self.col_inline_sizes.clone()));
|
self.block_flow
|
||||||
|
.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
||||||
|
containing_block_inline_size,
|
||||||
|
Some(self.column_inline_sizes.as_slice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
|
@ -242,3 +253,4 @@ impl fmt::Show for TableRowFlow {
|
||||||
write!(f, "TableRowFlow: {}", self.block_flow.fragment)
|
write!(f, "TableRowFlow: {}", self.block_flow.fragment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,11 @@ use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::Fragment;
|
use fragment::Fragment;
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use table::{InternalTable, TableFlow};
|
use model::IntrinsicISizesContribution;
|
||||||
|
use table::{ColumnInlineSize, InternalTable, TableFlow};
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use std::cmp::max;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// A table formatting context.
|
/// A table formatting context.
|
||||||
|
@ -26,14 +26,8 @@ use std::fmt;
|
||||||
pub struct TableRowGroupFlow {
|
pub struct TableRowGroupFlow {
|
||||||
pub block_flow: BlockFlow,
|
pub block_flow: BlockFlow,
|
||||||
|
|
||||||
/// Column inline-sizes
|
/// Information about the inline-sizes of each column.
|
||||||
pub col_inline_sizes: Vec<Au>,
|
pub column_inline_sizes: Vec<ColumnInlineSize>,
|
||||||
|
|
||||||
/// Column min inline-sizes.
|
|
||||||
pub col_min_inline_sizes: Vec<Au>,
|
|
||||||
|
|
||||||
/// Column pref inline-sizes.
|
|
||||||
pub col_pref_inline_sizes: Vec<Au>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableRowGroupFlow {
|
impl TableRowGroupFlow {
|
||||||
|
@ -42,9 +36,7 @@ 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_inline_sizes: vec!(),
|
column_inline_sizes: Vec::new(),
|
||||||
col_min_inline_sizes: vec!(),
|
|
||||||
col_pref_inline_sizes: vec!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +45,7 @@ impl TableRowGroupFlow {
|
||||||
-> TableRowGroupFlow {
|
-> TableRowGroupFlow {
|
||||||
TableRowGroupFlow {
|
TableRowGroupFlow {
|
||||||
block_flow: BlockFlow::from_node(constructor, node),
|
block_flow: BlockFlow::from_node(constructor, node),
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: Vec::new(),
|
||||||
col_min_inline_sizes: vec!(),
|
|
||||||
col_pref_inline_sizes: vec!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +54,8 @@ impl TableRowGroupFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
|
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
|
||||||
// TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
|
// TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and
|
||||||
// should be updated. Currently, they are set as Au(0).
|
// inline-start_offset should be updated. Currently, they are set as Au(0).
|
||||||
(Au(0), Au(0), Au(0))
|
(Au(0), Au(0), Au(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,16 +110,8 @@ impl Flow for TableRowGroupFlow {
|
||||||
&mut self.block_flow
|
&mut self.block_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
|
fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
|
||||||
&mut self.col_inline_sizes
|
&mut self.column_inline_sizes
|
||||||
}
|
|
||||||
|
|
||||||
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
|
||||||
&self.col_min_inline_sizes
|
|
||||||
}
|
|
||||||
|
|
||||||
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
|
|
||||||
&self.col_pref_inline_sizes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When
|
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When
|
||||||
|
@ -143,49 +125,47 @@ impl Flow for TableRowGroupFlow {
|
||||||
/// used in fixed table layout calculation.
|
/// used in fixed table layout calculation.
|
||||||
fn bubble_inline_sizes(&mut self) {
|
fn bubble_inline_sizes(&mut self) {
|
||||||
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
|
||||||
let mut min_inline_size = Au(0);
|
|
||||||
let mut pref_inline_size = Au(0);
|
|
||||||
|
|
||||||
|
let mut computation = IntrinsicISizesContribution::new();
|
||||||
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_inline-size & pref_inline-size for automatic table layout calculation
|
// Calculate minimum and preferred inline sizes for automatic table layout.
|
||||||
// 'self.col_min_inline-sizes' collects the maximum value of cells' min-inline-sizes for each column.
|
if self.column_inline_sizes.is_empty() {
|
||||||
// 'self.col_pref_inline-sizes' collects the maximum value of cells' pref-inline-sizes for each column.
|
// We're the first row.
|
||||||
if self.col_inline_sizes.is_empty() { // First Row
|
debug_assert!(self.column_inline_sizes.is_empty());
|
||||||
assert!(self.col_min_inline_sizes.is_empty() && self.col_pref_inline_sizes.is_empty());
|
self.column_inline_sizes = kid.column_inline_sizes().clone();
|
||||||
// 'self.col_inline-sizes' collects the specified column inline-sizes from the first table-row for fixed table layout calculation.
|
|
||||||
self.col_inline_sizes = kid.col_inline_sizes().clone();
|
|
||||||
self.col_min_inline_sizes = kid.col_min_inline_sizes().clone();
|
|
||||||
self.col_pref_inline_sizes = kid.col_pref_inline_sizes().clone();
|
|
||||||
} else {
|
} else {
|
||||||
min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
|
let mut child_intrinsic_sizes =
|
||||||
pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
|
TableFlow::update_column_inline_sizes(&mut self.column_inline_sizes,
|
||||||
|
kid.column_inline_sizes());
|
||||||
|
|
||||||
// update the number of column inline-sizes from table-rows.
|
// update the number of column inline-sizes from table-rows.
|
||||||
let num_cols = self.col_inline_sizes.len();
|
let column_count = self.column_inline_sizes.len();
|
||||||
let num_child_cols = kid.col_min_inline_sizes().len();
|
let child_column_count = kid.column_inline_sizes().len();
|
||||||
for i in range(num_cols, num_child_cols) {
|
for i in range(column_count, child_column_count) {
|
||||||
self.col_inline_sizes.push(Au::new(0));
|
let this_column_inline_size = (*kid.column_inline_sizes())[i];
|
||||||
let new_kid_min = kid.col_min_inline_sizes()[i];
|
|
||||||
self.col_min_inline_sizes.push(kid.col_min_inline_sizes()[i]);
|
// FIXME(pcwalton): Ignoring the percentage here seems dubious.
|
||||||
let new_kid_pref = kid.col_pref_inline_sizes()[i];
|
child_intrinsic_sizes.minimum_inline_size =
|
||||||
self.col_pref_inline_sizes.push(kid.col_pref_inline_sizes()[i]);
|
child_intrinsic_sizes.minimum_inline_size +
|
||||||
min_inline_size = min_inline_size + new_kid_min;
|
this_column_inline_size.minimum_length;
|
||||||
pref_inline_size = pref_inline_size + new_kid_pref;
|
child_intrinsic_sizes.preferred_inline_size =
|
||||||
|
child_intrinsic_sizes.preferred_inline_size +
|
||||||
|
this_column_inline_size.preferred;
|
||||||
|
self.column_inline_sizes.push(this_column_inline_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computation.union_block(&child_intrinsic_sizes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
|
self.block_flow.base.intrinsic_inline_sizes = computation.finish()
|
||||||
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(
|
|
||||||
min_inline_size, pref_inline_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
/// called on this context, the context has had its inline-size set by the parent context.
|
/// When called on this context, the context has had its inline-size set by the parent context.
|
||||||
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table_rowgroup::assign_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table_rowgroup::assign_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
@ -199,9 +179,14 @@ impl Flow for TableRowGroupFlow {
|
||||||
let content_inline_size = containing_block_inline_size;
|
let content_inline_size = containing_block_inline_size;
|
||||||
|
|
||||||
let inline_size_computer = InternalTable;
|
let inline_size_computer = InternalTable;
|
||||||
inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
|
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
||||||
|
ctx,
|
||||||
|
containing_block_inline_size);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, Some(self.col_inline_sizes.clone()));
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
inline_start_content_edge,
|
||||||
|
content_inline_size,
|
||||||
|
Some(self.column_inline_sizes.as_slice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
|
|
|
@ -3,22 +3,30 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
//! CSS tables.
|
//! CSS tables.
|
||||||
|
//!
|
||||||
|
//! This follows the "More Precise Definitions of Inline Layout and Table Layout" proposal written
|
||||||
|
//! by L. David Baron (Mozilla) here:
|
||||||
|
//!
|
||||||
|
//! http://dbaron.org/css/intrinsic/
|
||||||
|
//!
|
||||||
|
//! Hereafter this document is referred to as INTRINSIC.
|
||||||
|
|
||||||
#![deny(unsafe_block)]
|
#![deny(unsafe_block)]
|
||||||
|
|
||||||
use block::{BlockFlow, BlockNonReplaced, FloatNonReplaced, ISizeAndMarginsComputer};
|
use block::{BlockFlow, BlockNonReplaced, FloatNonReplaced, ISizeAndMarginsComputer};
|
||||||
use block::{ISizeConstraintInput, MarginsMayNotCollapse};
|
use block::{MarginsMayNotCollapse};
|
||||||
use construct::FlowConstructor;
|
use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use fragment::Fragment;
|
use fragment::Fragment;
|
||||||
use model::{Specified, Auto, specified};
|
use table::ColumnInlineSize;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use std::cmp::max;
|
use std::cmp::{max, min};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use style::CSSFloat;
|
||||||
use style::computed_values::{clear, float, table_layout};
|
use style::computed_values::{clear, float, table_layout};
|
||||||
|
|
||||||
#[deriving(Encodable)]
|
#[deriving(Encodable)]
|
||||||
|
@ -32,8 +40,8 @@ pub enum TableLayout {
|
||||||
pub struct TableWrapperFlow {
|
pub struct TableWrapperFlow {
|
||||||
pub block_flow: BlockFlow,
|
pub block_flow: BlockFlow,
|
||||||
|
|
||||||
/// Column inline-sizes
|
/// Inline-size information for each column.
|
||||||
pub col_inline_sizes: Vec<Au>,
|
pub column_inline_sizes: Vec<ColumnInlineSize>,
|
||||||
|
|
||||||
/// Table-layout property
|
/// Table-layout property
|
||||||
pub table_layout: TableLayout,
|
pub table_layout: TableLayout,
|
||||||
|
@ -52,7 +60,7 @@ impl TableWrapperFlow {
|
||||||
};
|
};
|
||||||
TableWrapperFlow {
|
TableWrapperFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: vec!(),
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +77,7 @@ impl TableWrapperFlow {
|
||||||
};
|
};
|
||||||
TableWrapperFlow {
|
TableWrapperFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: vec!(),
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +95,7 @@ impl TableWrapperFlow {
|
||||||
};
|
};
|
||||||
TableWrapperFlow {
|
TableWrapperFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_inline_sizes: vec!(),
|
column_inline_sizes: vec!(),
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,113 +105,87 @@ impl TableWrapperFlow {
|
||||||
self.block_flow.build_display_list_block(layout_context);
|
self.block_flow.build_display_list_block(layout_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_table_column_sizes(&mut self, mut input: ISizeConstraintInput)
|
/// Calculates table column sizes for automatic layout per INTRINSIC § 4.3.
|
||||||
-> ISizeConstraintInput {
|
fn calculate_table_column_sizes_for_automatic_layout(&mut self) {
|
||||||
let style = self.block_flow.fragment.style();
|
// Find the padding and border of our first child, which is the table itself.
|
||||||
|
//
|
||||||
// Get inline-start and inline-end paddings, borders for table.
|
// This is a little weird because we're computing border/padding/margins for our child,
|
||||||
// We get these values from the fragment's style since table_wrapper doesn't have its own
|
// when normally the child computes it itself. But it has to be this way because the
|
||||||
// border or padding. input.available_inline_size is same as containing_block_inline_size
|
// padding will affect where we place the child. This is an odd artifact of the way that
|
||||||
// in table_wrapper.
|
// tables are separated into table flows and table wrapper flows.
|
||||||
let padding = style.logical_padding();
|
let available_inline_size = self.block_flow.fragment.border_box.size.inline;
|
||||||
let border = style.logical_border_width();
|
let mut table_border_padding = Au(0);
|
||||||
let padding_and_borders =
|
for kid in self.block_flow.base.child_iter() {
|
||||||
specified(padding.inline_start, input.available_inline_size) +
|
if kid.is_table() {
|
||||||
specified(padding.inline_end, input.available_inline_size) +
|
let kid_block = kid.as_block();
|
||||||
border.inline_start +
|
kid_block.fragment.compute_border_and_padding(available_inline_size);
|
||||||
border.inline_end;
|
kid_block.fragment.compute_block_direction_margins(available_inline_size);
|
||||||
|
kid_block.fragment.compute_inline_direction_margins(available_inline_size);
|
||||||
let computed_inline_size = match self.table_layout {
|
table_border_padding = kid_block.fragment.border_padding.inline_start_end();
|
||||||
FixedLayout => {
|
break
|
||||||
let fixed_cells_inline_size = self.col_inline_sizes
|
|
||||||
.iter()
|
|
||||||
.fold(Au(0), |sum, inline_size| {
|
|
||||||
sum.add(inline_size)
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut computed_inline_size = input.computed_inline_size.specified_or_zero();
|
|
||||||
|
|
||||||
// Compare border-edge inline-sizes. Because fixed_cells_inline_size indicates
|
|
||||||
// content-inline-size, padding and border values are added to
|
|
||||||
// fixed_cells_inline_size.
|
|
||||||
computed_inline_size = max(
|
|
||||||
fixed_cells_inline_size + padding_and_borders, computed_inline_size);
|
|
||||||
computed_inline_size
|
|
||||||
},
|
|
||||||
AutoLayout => {
|
|
||||||
// Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2.
|
|
||||||
let mut cap_min = Au(0);
|
|
||||||
let mut cols_min = Au(0);
|
|
||||||
let mut cols_max = Au(0);
|
|
||||||
let mut col_min_inline_sizes = &vec!();
|
|
||||||
let mut col_pref_inline_sizes = &vec!();
|
|
||||||
for kid in self.block_flow.base.child_iter() {
|
|
||||||
if kid.is_table_caption() {
|
|
||||||
cap_min = kid.as_block().base.intrinsic_inline_sizes.minimum_inline_size;
|
|
||||||
} else {
|
|
||||||
assert!(kid.is_table());
|
|
||||||
cols_min = kid.as_block().base.intrinsic_inline_sizes.minimum_inline_size;
|
|
||||||
cols_max = kid.as_block()
|
|
||||||
.base
|
|
||||||
.intrinsic_inline_sizes
|
|
||||||
.preferred_inline_size;
|
|
||||||
col_min_inline_sizes = kid.col_min_inline_sizes();
|
|
||||||
col_pref_inline_sizes = kid.col_pref_inline_sizes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 'extra_inline-size': difference between the calculated table inline-size and
|
|
||||||
// minimum inline-size required by all columns. It will be distributed over the
|
|
||||||
// columns.
|
|
||||||
let (inline_size, extra_inline_size) = match input.computed_inline_size {
|
|
||||||
Auto => {
|
|
||||||
if input.available_inline_size > max(cols_max, cap_min) {
|
|
||||||
if cols_max > cap_min {
|
|
||||||
self.col_inline_sizes = col_pref_inline_sizes.clone();
|
|
||||||
(cols_max, Au(0))
|
|
||||||
} else {
|
|
||||||
(cap_min, cap_min - cols_min)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let max = if cols_min >= input.available_inline_size &&
|
|
||||||
cols_min >= cap_min {
|
|
||||||
self.col_inline_sizes = col_min_inline_sizes.clone();
|
|
||||||
cols_min
|
|
||||||
} else {
|
|
||||||
max(input.available_inline_size, cap_min)
|
|
||||||
};
|
|
||||||
(max, max - cols_min)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Specified(inline_size) => {
|
|
||||||
let max = if cols_min >= inline_size && cols_min >= cap_min {
|
|
||||||
self.col_inline_sizes = col_min_inline_sizes.clone();
|
|
||||||
cols_min
|
|
||||||
} else {
|
|
||||||
max(inline_size, cap_min)
|
|
||||||
};
|
|
||||||
(max, max - cols_min)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// The extra inline-size is distributed over the columns
|
|
||||||
if extra_inline_size > Au(0) {
|
|
||||||
let cell_len = self.col_inline_sizes.len() as f64;
|
|
||||||
self.col_inline_sizes = col_min_inline_sizes.iter()
|
|
||||||
.map(|inline_size| {
|
|
||||||
inline_size + extra_inline_size.scale_by(1.0 / cell_len)
|
|
||||||
}).collect();
|
|
||||||
}
|
|
||||||
inline_size + padding_and_borders
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
input.computed_inline_size = Specified(computed_inline_size);
|
|
||||||
input
|
// FIXME(pcwalton, spec): INTRINSIC § 8 does not properly define how to compute this, but
|
||||||
|
// says "the basic idea is the same as the shrink-to-fit width that CSS2.1 defines". So we
|
||||||
|
// just use the shrink-to-fit inline size.
|
||||||
|
let available_inline_size =
|
||||||
|
self.block_flow.get_shrink_to_fit_inline_size(available_inline_size);
|
||||||
|
|
||||||
|
// Compute all the guesses for the column sizes, and sum them.
|
||||||
|
let mut total_guess = AutoLayoutCandidateGuess::new();
|
||||||
|
let guesses: Vec<AutoLayoutCandidateGuess> =
|
||||||
|
self.column_inline_sizes.iter().map(|column_inline_size| {
|
||||||
|
let guess = AutoLayoutCandidateGuess::from_column_inline_size(
|
||||||
|
column_inline_size,
|
||||||
|
available_inline_size);
|
||||||
|
total_guess = total_guess + guess;
|
||||||
|
guess
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
// Assign inline sizes.
|
||||||
|
let selection = SelectedAutoLayoutCandidateGuess::select(&total_guess,
|
||||||
|
available_inline_size);
|
||||||
|
let mut total_used_inline_size = Au(0);
|
||||||
|
for (column_inline_size, guess) in self.column_inline_sizes
|
||||||
|
.iter_mut()
|
||||||
|
.zip(guesses.iter()) {
|
||||||
|
column_inline_size.minimum_length = guess.calculate(selection);
|
||||||
|
column_inline_size.percentage = 0.0;
|
||||||
|
total_used_inline_size = total_used_inline_size + column_inline_size.minimum_length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distribute excess inline-size if necessary per INTRINSIC § 4.4.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton, spec): How do I deal with fractional excess?
|
||||||
|
let excess_inline_size = available_inline_size - total_used_inline_size;
|
||||||
|
if excess_inline_size > Au(0) &&
|
||||||
|
selection == UsePreferredGuessAndDistributeExcessInlineSize {
|
||||||
|
let mut info = ExcessInlineSizeDistributionInfo::new();
|
||||||
|
for column_inline_size in self.column_inline_sizes.iter() {
|
||||||
|
info.update(column_inline_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut total_distributed_excess_size = Au(0);
|
||||||
|
for column_inline_size in self.column_inline_sizes.iter_mut() {
|
||||||
|
info.distribute_excess_inline_size_to_column(column_inline_size,
|
||||||
|
excess_inline_size,
|
||||||
|
&mut total_distributed_excess_size)
|
||||||
|
}
|
||||||
|
total_used_inline_size = available_inline_size
|
||||||
|
}
|
||||||
|
|
||||||
|
self.block_flow.fragment.border_box.size.inline = total_used_inline_size +
|
||||||
|
table_border_padding;
|
||||||
|
self.block_flow.base.position.size.inline = total_used_inline_size +
|
||||||
|
table_border_padding + self.block_flow.fragment.margin.inline_start_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_used_inline_size(&mut self,
|
fn compute_used_inline_size(&mut self,
|
||||||
layout_context: &LayoutContext,
|
layout_context: &LayoutContext,
|
||||||
parent_flow_inline_size: Au) {
|
parent_flow_inline_size: Au) {
|
||||||
// Delegate to the appropriate inline size computer to find the constraint inputs.
|
// Delegate to the appropriate inline size computer to find the constraint inputs.
|
||||||
let mut input = if self.is_float() {
|
let input = if self.is_float() {
|
||||||
FloatNonReplaced.compute_inline_size_constraint_inputs(&mut self.block_flow,
|
FloatNonReplaced.compute_inline_size_constraint_inputs(&mut self.block_flow,
|
||||||
parent_flow_inline_size,
|
parent_flow_inline_size,
|
||||||
layout_context)
|
layout_context)
|
||||||
|
@ -213,9 +195,6 @@ impl TableWrapperFlow {
|
||||||
layout_context)
|
layout_context)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute the inline sizes of the columns.
|
|
||||||
input = self.calculate_table_column_sizes(input);
|
|
||||||
|
|
||||||
// Delegate to the appropriate inline size computer to write the constraint solutions in.
|
// Delegate to the appropriate inline size computer to write the constraint solutions in.
|
||||||
if self.is_float() {
|
if self.is_float() {
|
||||||
let solution = FloatNonReplaced.solve_inline_size_constraints(&mut self.block_flow,
|
let solution = FloatNonReplaced.solve_inline_size_constraints(&mut self.block_flow,
|
||||||
|
@ -261,12 +240,11 @@ impl Flow for TableWrapperFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bubble_inline_sizes(&mut self) {
|
fn bubble_inline_sizes(&mut self) {
|
||||||
// get column inline-sizes info from table flow
|
// Get the column inline-sizes info from the 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());
|
debug_assert!(kid.is_table_caption() || kid.is_table());
|
||||||
|
|
||||||
if kid.is_table() {
|
if kid.is_table() {
|
||||||
self.col_inline_sizes.push_all(kid.as_table().col_inline_sizes.as_slice());
|
self.column_inline_sizes = kid.column_inline_sizes().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,18 +274,25 @@ impl Flow for TableWrapperFlow {
|
||||||
|
|
||||||
self.compute_used_inline_size(layout_context, containing_block_inline_size);
|
self.compute_used_inline_size(layout_context, containing_block_inline_size);
|
||||||
|
|
||||||
|
match self.table_layout {
|
||||||
|
FixedLayout => {}
|
||||||
|
AutoLayout => {
|
||||||
|
self.calculate_table_column_sizes_for_automatic_layout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
|
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
|
||||||
let content_inline_size = self.block_flow.fragment.border_box.size.inline;
|
let content_inline_size = self.block_flow.fragment.border_box.size.inline;
|
||||||
|
|
||||||
// In case of fixed layout, column inline-sizes are calculated in table flow.
|
// In case of fixed layout, column inline-sizes are calculated in table flow.
|
||||||
let assigned_col_inline_sizes = match self.table_layout {
|
let assigned_column_inline_sizes = match self.table_layout {
|
||||||
FixedLayout => None,
|
FixedLayout => None,
|
||||||
AutoLayout => Some(self.col_inline_sizes.clone())
|
AutoLayout => Some(self.column_inline_sizes.as_slice())
|
||||||
};
|
};
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
assigned_col_inline_sizes);
|
assigned_column_inline_sizes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
|
@ -353,3 +338,230 @@ impl fmt::Show for TableWrapperFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The layout "guesses" defined in INTRINSIC § 4.3.
|
||||||
|
struct AutoLayoutCandidateGuess {
|
||||||
|
/// The column inline-size assignment where each column is assigned its intrinsic minimum
|
||||||
|
/// inline-size.
|
||||||
|
minimum_guess: Au,
|
||||||
|
|
||||||
|
/// The column inline-size assignment where:
|
||||||
|
/// * A column with an intrinsic percentage inline-size greater than 0% is assigned the
|
||||||
|
/// larger of:
|
||||||
|
/// - Its intrinsic percentage inline-size times the assignable inline-size;
|
||||||
|
/// - Its intrinsic minimum inline-size;
|
||||||
|
/// * Other columns receive their intrinsic minimum inline-size.
|
||||||
|
minimum_percentage_guess: Au,
|
||||||
|
|
||||||
|
/// The column inline-size assignment where:
|
||||||
|
/// * Each column with an intrinsic percentage inline-size greater than 0% is assigned the
|
||||||
|
/// larger of:
|
||||||
|
/// - Its intrinsic percentage inline-size times the assignable inline-size;
|
||||||
|
/// - Its intrinsic minimum inline-size;
|
||||||
|
/// * Any other column that is constrained is assigned its intrinsic preferred inline-size;
|
||||||
|
/// * Other columns are assigned their intrinsic minimum inline-size.
|
||||||
|
minimum_specified_guess: Au,
|
||||||
|
|
||||||
|
/// The column inline-size assignment where:
|
||||||
|
/// * Each column with an intrinsic percentage inline-size greater than 0% is assigned the
|
||||||
|
/// larger of:
|
||||||
|
/// - Its intrinsic percentage inline-size times the assignable inline-size;
|
||||||
|
/// - Its intrinsic minimum inline-size;
|
||||||
|
/// * Other columns are assigned their intrinsic preferred inline-size.
|
||||||
|
preferred_guess: Au,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoLayoutCandidateGuess {
|
||||||
|
/// Creates a guess with all elements initialized to zero.
|
||||||
|
fn new() -> AutoLayoutCandidateGuess {
|
||||||
|
AutoLayoutCandidateGuess {
|
||||||
|
minimum_guess: Au(0),
|
||||||
|
minimum_percentage_guess: Au(0),
|
||||||
|
minimum_specified_guess: Au(0),
|
||||||
|
preferred_guess: Au(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills in the inline-size guesses for this column per INTRINSIC § 4.3.
|
||||||
|
fn from_column_inline_size(column_inline_size: &ColumnInlineSize, assignable_inline_size: Au)
|
||||||
|
-> AutoLayoutCandidateGuess {
|
||||||
|
let minimum_percentage_guess =
|
||||||
|
max(assignable_inline_size.scale_by(column_inline_size.percentage),
|
||||||
|
column_inline_size.minimum_length);
|
||||||
|
AutoLayoutCandidateGuess {
|
||||||
|
minimum_guess: column_inline_size.minimum_length,
|
||||||
|
minimum_percentage_guess: minimum_percentage_guess,
|
||||||
|
// FIXME(pcwalton): We need the notion of *constrainedness* per INTRINSIC § 4 to
|
||||||
|
// implement this one correctly.
|
||||||
|
minimum_specified_guess: if column_inline_size.percentage > 0.0 {
|
||||||
|
minimum_percentage_guess
|
||||||
|
} else if column_inline_size.constrained {
|
||||||
|
column_inline_size.preferred
|
||||||
|
} else {
|
||||||
|
column_inline_size.minimum_length
|
||||||
|
},
|
||||||
|
preferred_guess: if column_inline_size.percentage > 0.0 {
|
||||||
|
minimum_percentage_guess
|
||||||
|
} else {
|
||||||
|
column_inline_size.preferred
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the inline-size, interpolating appropriately based on the value of `selection`.
|
||||||
|
///
|
||||||
|
/// This does *not* distribute excess inline-size. That must be done later if necessary.
|
||||||
|
fn calculate(&self, selection: SelectedAutoLayoutCandidateGuess) -> Au {
|
||||||
|
match selection {
|
||||||
|
UseMinimumGuess => self.minimum_guess,
|
||||||
|
InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(weight) => {
|
||||||
|
interp(self.minimum_guess, self.minimum_percentage_guess, weight)
|
||||||
|
}
|
||||||
|
InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(weight) => {
|
||||||
|
interp(self.minimum_percentage_guess, self.minimum_specified_guess, weight)
|
||||||
|
}
|
||||||
|
InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(weight) => {
|
||||||
|
interp(self.minimum_specified_guess, self.preferred_guess, weight)
|
||||||
|
}
|
||||||
|
UsePreferredGuessAndDistributeExcessInlineSize => {
|
||||||
|
self.preferred_guess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<AutoLayoutCandidateGuess,AutoLayoutCandidateGuess> for AutoLayoutCandidateGuess {
|
||||||
|
#[inline]
|
||||||
|
fn add(&self, other: &AutoLayoutCandidateGuess) -> AutoLayoutCandidateGuess {
|
||||||
|
AutoLayoutCandidateGuess {
|
||||||
|
minimum_guess: self.minimum_guess + other.minimum_guess,
|
||||||
|
minimum_percentage_guess:
|
||||||
|
self.minimum_percentage_guess + other.minimum_percentage_guess,
|
||||||
|
minimum_specified_guess: self.minimum_specified_guess + other.minimum_specified_guess,
|
||||||
|
preferred_guess: self.preferred_guess + other.preferred_guess,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `CSSFloat` member specifies the weight of the smaller of the two guesses, on a scale from
|
||||||
|
/// 0.0 to 1.0.
|
||||||
|
#[deriving(PartialEq, Show)]
|
||||||
|
enum SelectedAutoLayoutCandidateGuess {
|
||||||
|
UseMinimumGuess,
|
||||||
|
InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(CSSFloat),
|
||||||
|
InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(CSSFloat),
|
||||||
|
InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(CSSFloat),
|
||||||
|
UsePreferredGuessAndDistributeExcessInlineSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectedAutoLayoutCandidateGuess {
|
||||||
|
/// See INTRINSIC § 4.3.
|
||||||
|
///
|
||||||
|
/// FIXME(pcwalton, INTRINSIC spec): INTRINSIC doesn't specify whether these are exclusive or
|
||||||
|
/// inclusive ranges.
|
||||||
|
fn select(guess: &AutoLayoutCandidateGuess, assignable_inline_size: Au)
|
||||||
|
-> SelectedAutoLayoutCandidateGuess {
|
||||||
|
if assignable_inline_size < guess.minimum_guess {
|
||||||
|
UseMinimumGuess
|
||||||
|
} else if assignable_inline_size < guess.minimum_percentage_guess {
|
||||||
|
let weight = weight(guess.minimum_guess,
|
||||||
|
assignable_inline_size,
|
||||||
|
guess.minimum_percentage_guess);
|
||||||
|
InterpolateBetweenMinimumGuessAndMinimumPercentageGuess(weight)
|
||||||
|
} else if assignable_inline_size < guess.minimum_specified_guess {
|
||||||
|
let weight = weight(guess.minimum_percentage_guess,
|
||||||
|
assignable_inline_size,
|
||||||
|
guess.minimum_specified_guess);
|
||||||
|
InterpolateBetweenMinimumPercentageGuessAndMinimumSpecifiedGuess(weight)
|
||||||
|
} else if assignable_inline_size < guess.preferred_guess {
|
||||||
|
let weight = weight(guess.minimum_specified_guess,
|
||||||
|
assignable_inline_size,
|
||||||
|
guess.preferred_guess);
|
||||||
|
InterpolateBetweenMinimumSpecifiedGuessAndPreferredGuess(weight)
|
||||||
|
} else {
|
||||||
|
UsePreferredGuessAndDistributeExcessInlineSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the weight needed to linearly interpolate `middle` between two guesses `low` and
|
||||||
|
/// `high` as specified by INTRINSIC § 4.3.
|
||||||
|
fn weight(low: Au, middle: Au, high: Au) -> CSSFloat {
|
||||||
|
(middle - low).to_subpx() / (high - low).to_subpx()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linearly interpolates between two guesses, as specified by INTRINSIC § 4.3.
|
||||||
|
fn interp(low: Au, high: Au, weight: CSSFloat) -> Au {
|
||||||
|
low + (high - low).scale_by(weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ExcessInlineSizeDistributionInfo {
|
||||||
|
preferred_inline_size_of_nonconstrained_columns_with_no_percentage: Au,
|
||||||
|
count_of_nonconstrained_columns_with_no_percentage: u32,
|
||||||
|
preferred_inline_size_of_constrained_columns_with_no_percentage: Au,
|
||||||
|
total_percentage: CSSFloat,
|
||||||
|
column_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExcessInlineSizeDistributionInfo {
|
||||||
|
fn new() -> ExcessInlineSizeDistributionInfo {
|
||||||
|
ExcessInlineSizeDistributionInfo {
|
||||||
|
preferred_inline_size_of_nonconstrained_columns_with_no_percentage: Au(0),
|
||||||
|
count_of_nonconstrained_columns_with_no_percentage: 0,
|
||||||
|
preferred_inline_size_of_constrained_columns_with_no_percentage: Au(0),
|
||||||
|
total_percentage: 0.0,
|
||||||
|
column_count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, column_inline_size: &ColumnInlineSize) {
|
||||||
|
if !column_inline_size.constrained && column_inline_size.percentage == 0.0 {
|
||||||
|
self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage =
|
||||||
|
self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage +
|
||||||
|
column_inline_size.preferred;
|
||||||
|
self.count_of_nonconstrained_columns_with_no_percentage += 1
|
||||||
|
}
|
||||||
|
if column_inline_size.constrained && column_inline_size.percentage == 0.0 {
|
||||||
|
self.preferred_inline_size_of_constrained_columns_with_no_percentage =
|
||||||
|
self.preferred_inline_size_of_constrained_columns_with_no_percentage +
|
||||||
|
column_inline_size.preferred
|
||||||
|
}
|
||||||
|
self.total_percentage += column_inline_size.percentage;
|
||||||
|
self.column_count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Based on the information here, distributes excess inline-size to the given column per
|
||||||
|
/// INTRINSIC § 4.4.
|
||||||
|
///
|
||||||
|
/// `#[inline]` so the compiler will hoist out the branch, which is loop-invariant.
|
||||||
|
#[inline]
|
||||||
|
fn distribute_excess_inline_size_to_column(&self,
|
||||||
|
column_inline_size: &mut ColumnInlineSize,
|
||||||
|
excess_inline_size: Au,
|
||||||
|
total_distributed_excess_size: &mut Au) {
|
||||||
|
let proportion =
|
||||||
|
if self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage > Au(0) {
|
||||||
|
column_inline_size.preferred.to_subpx() /
|
||||||
|
self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage
|
||||||
|
.to_subpx()
|
||||||
|
} else if self.count_of_nonconstrained_columns_with_no_percentage > 0 {
|
||||||
|
1.0 / (self.count_of_nonconstrained_columns_with_no_percentage as CSSFloat)
|
||||||
|
} else if self.preferred_inline_size_of_constrained_columns_with_no_percentage >
|
||||||
|
Au(0) {
|
||||||
|
column_inline_size.preferred.to_subpx() /
|
||||||
|
self.preferred_inline_size_of_constrained_columns_with_no_percentage.to_subpx()
|
||||||
|
} else if self.total_percentage > 0.0 {
|
||||||
|
column_inline_size.percentage / self.total_percentage
|
||||||
|
} else {
|
||||||
|
1.0 / (self.column_count as CSSFloat)
|
||||||
|
};
|
||||||
|
|
||||||
|
// The `min` here has the effect of throwing away fractional excess at the end of the
|
||||||
|
// table.
|
||||||
|
let amount_to_distribute = min(excess_inline_size.scale_by(proportion),
|
||||||
|
excess_inline_size - *total_distributed_excess_size);
|
||||||
|
*total_distributed_excess_size = *total_distributed_excess_size + amount_to_distribute;
|
||||||
|
column_inline_size.minimum_length = column_inline_size.minimum_length +
|
||||||
|
amount_to_distribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,8 @@ fragment=top != ../html/acid2.html acid2_ref.html
|
||||||
!= input_height_a.html input_height_ref.html
|
!= input_height_a.html input_height_ref.html
|
||||||
== pre_ignorable_whitespace_a.html pre_ignorable_whitespace_ref.html
|
== pre_ignorable_whitespace_a.html pre_ignorable_whitespace_ref.html
|
||||||
== many_brs_a.html many_brs_ref.html
|
== many_brs_a.html many_brs_ref.html
|
||||||
|
== table_expansion_to_fit_a.html table_expansion_to_fit_ref.html
|
||||||
|
== table_percentage_width_a.html table_percentage_width_ref.html
|
||||||
== legacy_input_size_attribute_override_a.html legacy_input_size_attribute_override_ref.html
|
== legacy_input_size_attribute_override_a.html legacy_input_size_attribute_override_ref.html
|
||||||
== legacy_td_width_attribute_a.html legacy_td_width_attribute_ref.html
|
== legacy_td_width_attribute_a.html legacy_td_width_attribute_ref.html
|
||||||
== box_sizing_sanity_check_a.html box_sizing_sanity_check_ref.html
|
== box_sizing_sanity_check_a.html box_sizing_sanity_check_ref.html
|
||||||
|
|
|
@ -16,10 +16,14 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: yellow;
|
color: yellow;
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
|
margin-right: 50px;
|
||||||
|
background: yellow;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div><span>X X</span></div>
|
<div><span></span><span></span></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -11,10 +11,11 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<p>Don't crash!</p>
|
||||||
<table class="rel">
|
<table class="rel">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="abs">
|
<tr class="abs">
|
||||||
<td style="padding: 0">Don't crash!</td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
|
<p>Don't crash!</p>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0">Don't crash!</td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
19
tests/ref/table_expansion_to_fit_a.html
Normal file
19
tests/ref/table_expansion_to_fit_a.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table style="width: 500px; text-align: center;">
|
||||||
|
<tr><td style="">Expanding...</td></tr>
|
||||||
|
<tr><td style="">to...</td></tr>
|
||||||
|
<tr><td style="">fit!</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
19
tests/ref/table_expansion_to_fit_ref.html
Normal file
19
tests/ref/table_expansion_to_fit_ref.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="text-align: center; width: 500px;">
|
||||||
|
<div>Expanding...</div>
|
||||||
|
<div>to...</div>
|
||||||
|
<div>fit!</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
17
tests/ref/table_percentage_width_a.html
Normal file
17
tests/ref/table_percentage_width_a.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table style="width: 500px; text-align: center;" cellpadding=0 cellspacing=0>
|
||||||
|
<tr><td style="width: 30%; height: 50px; background: blue;"></td><td style="width: 70%; background: green;"></td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
17
tests/ref/table_percentage_width_ref.html
Normal file
17
tests/ref/table_percentage_width_ref.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="width: 500px; text-align: center;">
|
||||||
|
<span style="display: inline-block; width: 150px; height: 50px; background: blue;"></span><span style="display: inline-block; width: 350px; height: 50px; background: green;"></span>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue