aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout')
-rw-r--r--components/layout/block.rs238
-rw-r--r--components/layout/construct.rs4
-rw-r--r--components/layout/flow.rs18
-rw-r--r--components/layout/fragment.rs217
-rw-r--r--components/layout/inline.rs27
-rw-r--r--components/layout/model.rs66
-rw-r--r--components/layout/table.rs291
-rw-r--r--components/layout/table_cell.rs24
-rw-r--r--components/layout/table_colgroup.rs31
-rw-r--r--components/layout/table_row.rs146
-rw-r--r--components/layout/table_rowgroup.rs107
-rw-r--r--components/layout/table_wrapper.rs448
12 files changed, 1001 insertions, 616 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs
index 2797245f03b..d5352d622dc 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -2,15 +2,28 @@
* 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/. */
-//! CSS block formatting contexts.
+//! Layout for CSS block-level elements.
//!
-//! Terminology Note:
-//! As per the CSS Spec, the term 'absolute positioning' here refers to
-//! elements with position = 'absolute' or 'fixed'.
-//! The term 'positioned element' refers to elements with position =
-//! 'relative', 'absolute', or 'fixed'.
+//! As a terminology note, the term *absolute positioning* here refers to elements with position
+//! `absolute` or `fixed`. The term *positioned element* refers to elements with position
+//! `relative`, `absolute`, and `fixed`. The term *containing block* (occasionally abbreviated as
+//! *CB*) is the containing block for the current flow, which differs from the static containing
+//! 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)]
@@ -22,11 +35,10 @@ use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_
use flow;
use fragment::{Fragment, ImageFragment, InlineBlockFragment, ScannedTextFragment};
use layout_debug;
-use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse};
-use model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified};
-use model::{specified_or_none};
+use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse, MarginsCollapseThrough};
+use model::{MaybeAuto, NoCollapsibleMargins, Specified, specified, specified_or_none};
+use table::ColumnInlineSize;
use wrapper::ThreadSafeLayoutNode;
-use style::computed_values::{clear, position};
use collections::dlist::DList;
use geom::{Size2D, Point2D, Rect};
@@ -43,8 +55,8 @@ use std::cmp::{max, min};
use std::fmt;
use std::mem;
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::{display, float, overflow};
+use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, box_sizing, clear};
+use style::computed_values::{display, float, overflow, position};
use sync::Arc;
/// Information specific to floated blocks.
@@ -762,9 +774,10 @@ impl BlockFlow {
///
/// This is where we use the preferred inline-sizes and minimum inline-sizes
/// calculated in the bubble-inline-sizes traversal.
- fn get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au {
- min(self.base.intrinsic_inline_sizes.preferred_inline_size,
- max(self.base.intrinsic_inline_sizes.minimum_inline_size, available_inline_size))
+ pub fn get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au {
+ let content_intrinsic_inline_sizes = self.content_intrinsic_inline_sizes();
+ 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
@@ -1291,10 +1304,11 @@ impl BlockFlow {
/// `#[inline(always)]` because this is called only from block or table inline-size assignment
/// and the code for block layout is significantly simpler.
#[inline(always)]
- pub fn propagate_assigned_inline_size_to_children(&mut self,
- inline_start_content_edge: Au,
- content_inline_size: Au,
- opt_col_inline_sizes: Option<Vec<Au>>) {
+ pub fn propagate_assigned_inline_size_to_children(
+ &mut self,
+ inline_start_content_edge: Au,
+ content_inline_size: Au,
+ optional_column_inline_sizes: Option<&[ColumnInlineSize]>) {
// 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_end_floats_impact_child = self.base.flags.impacted_by_right_floats();
@@ -1403,13 +1417,13 @@ impl BlockFlow {
}
// Handle tables.
- match opt_col_inline_sizes {
- Some(ref col_inline_sizes) => {
+ match optional_column_inline_sizes {
+ Some(ref column_inline_sizes) => {
propagate_column_inline_sizes_to_child(kid,
- i,
- content_inline_size,
- col_inline_sizes.as_slice(),
- &mut inline_start_margin_edge)
+ i,
+ content_inline_size,
+ *column_inline_sizes,
+ &mut inline_start_margin_edge)
}
None => {}
}
@@ -1469,6 +1483,23 @@ impl BlockFlow {
// TODO(pcwalton): If the inline-size of this flow is different from the size we estimated
// 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 {
@@ -1515,35 +1546,31 @@ impl Flow for BlockFlow {
};
// 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 right_float_width = Au(0);
- for child_ctx in self.base.child_iter() {
- assert!(child_ctx.is_block_flow() ||
- child_ctx.is_inline_flow() ||
- child_ctx.is_table_kind());
-
- let float_kind = child_ctx.float_kind();
- let child_base = flow::mut_base(child_ctx);
-
- 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());
+ for kid in self.base.child_iter() {
+ let is_absolutely_positioned = kid.is_absolutely_positioned();
+ let float_kind = kid.float_kind();
+ let child_base = flow::mut_base(kid);
+ if !is_absolutely_positioned && !fixed_width {
+ computation.content_intrinsic_sizes.minimum_inline_size =
+ max(computation.content_intrinsic_sizes.minimum_inline_size,
+ child_base.intrinsic_inline_sizes.minimum_inline_size);
match float_kind {
float::none => {
- intrinsic_inline_sizes.preferred_inline_size =
- max(intrinsic_inline_sizes.preferred_inline_size,
- child_base.intrinsic_inline_sizes.total_preferred_inline_size());
+ computation.content_intrinsic_sizes.preferred_inline_size =
+ max(computation.content_intrinsic_sizes.preferred_inline_size,
+ child_base.intrinsic_inline_sizes.preferred_inline_size);
}
float::left => {
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 => {
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);
}
- intrinsic_inline_sizes.preferred_inline_size =
- max(intrinsic_inline_sizes.preferred_inline_size,
- left_float_width + right_float_width);
+ // FIXME(pcwalton): This should consider all float descendants, not just children.
+ // FIXME(pcwalton): This is not well-spec'd; INTRINSIC specifies to do this, but CSS-SIZING
+ // 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();
- 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;
+ self.base.intrinsic_inline_sizes = computation.finish();
match self.fragment.style().get_box().float {
float::none => {}
@@ -1575,11 +1595,11 @@ impl Flow for BlockFlow {
self.base.flags = flags
}
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent context.
+ /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
+ /// When called on this context, the context has had its inline-size set by the parent context.
///
- /// Dual fragments consume some inline-size first, and the remainder is assigned to all child (block)
- /// contexts.
+ /// Dual fragments consume some inline-size first, and the remainder is assigned to all child
+ /// (block) contexts.
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
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.
- 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 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
@@ -1686,7 +1709,7 @@ impl Flow for BlockFlow {
self.base.block_container_explicit_block_size.unwrap_or(Au(0));
self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size);
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.
debug!("assign_block_size: assigning block_size for root flow");
self.assign_block_size_block_base(ctx, MarginsMayNotCollapse);
@@ -1882,6 +1905,7 @@ impl ISizeConstraintInput {
}
/// The solutions for the inline-size-and-margins constraint equation.
+#[deriving(Show)]
pub struct ISizeConstraintSolution {
pub inline_start: Au,
pub inline_end: Au,
@@ -1923,9 +1947,8 @@ impl ISizeConstraintSolution {
pub trait ISizeAndMarginsComputer {
/// Compute the inputs for the ISize constraint equation.
///
- /// This is called only once to compute the initial inputs. For
- /// calculation involving min-inline-size and max-inline-size, we don't need to
- /// recompute these.
+ /// This is called only once to compute the initial inputs. For calculations involving
+ /// minimum and maximum inline-size, we don't need to recompute these.
fn compute_inline_size_constraint_inputs(&self,
block: &mut BlockFlow,
parent_flow_inline_size: Au,
@@ -1934,7 +1957,9 @@ pub trait ISizeAndMarginsComputer {
let containing_block_inline_size =
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,
parent_flow_inline_size,
@@ -1955,7 +1980,8 @@ pub trait ISizeAndMarginsComputer {
let margin = style.logical_margin();
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(
computed_inline_size,
MaybeAuto::from_style(margin.inline_start, containing_block_inline_size),
@@ -2015,10 +2041,10 @@ pub trait ISizeAndMarginsComputer {
-> ISizeConstraintSolution;
fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- ctx: &LayoutContext)
- -> MaybeAuto {
+ block: &mut BlockFlow,
+ parent_flow_inline_size: Au,
+ ctx: &LayoutContext)
+ -> MaybeAuto {
MaybeAuto::from_style(block.fragment().style().content_inline_size(),
self.containing_block_inline_size(block,
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.
fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- _: Au,
- ctx: &LayoutContext)
- -> MaybeAuto {
- let containing_block_inline_size = block.containing_block_size(ctx.shared.screen_size).inline;
+ block: &mut BlockFlow,
+ _: Au,
+ layout_context: &LayoutContext)
+ -> MaybeAuto {
+ let containing_block_inline_size =
+ block.containing_block_size(layout_context.shared.screen_size).inline;
let fragment = block.fragment();
fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size);
// 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.
fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- _: &LayoutContext)
- -> MaybeAuto {
+ block: &mut BlockFlow,
+ parent_flow_inline_size: Au,
+ _: &LayoutContext)
+ -> MaybeAuto {
let fragment = block.fragment();
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
// 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.
fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- _: &LayoutContext)
- -> MaybeAuto {
+ block: &mut BlockFlow,
+ parent_flow_inline_size: Au,
+ _: &LayoutContext)
+ -> MaybeAuto {
let fragment = block.fragment();
fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
// 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,
- child_index: uint,
- content_inline_size: Au,
- column_inline_sizes: &[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
- // parent.
+ child_index: uint,
+ content_inline_size: Au,
+ column_inline_sizes: &[ColumnInlineSize],
+ inline_start_margin_edge: &mut Au) {
+ // If kid is table_rowgroup or table_row, the column inline-sizes info should be copied from
+ // its parent.
//
// FIXME(pcwalton): This seems inefficient. Reference count it instead?
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.
content_inline_size
} else if kid.is_table_cell() {
- // If kid is table_cell, the x offset and inline-size for each cell should be
- // 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]
+ column_inline_sizes[child_index].minimum_length
} else {
// ISize of kid flow is our content inline-size.
content_inline_size
};
- let kid_base = flow::mut_base(kid);
- kid_base.position.start.i = *inline_start_margin_edge;
- kid_base.block_container_inline_size = inline_size;
+ {
+ let kid_base = flow::mut_base(kid);
+ 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
+ }
}
+
diff --git a/components/layout/construct.rs b/components/layout/construct.rs
index 4233c565dba..7d4b6d24b81 100644
--- a/components/layout/construct.rs
+++ b/components/layout/construct.rs
@@ -767,8 +767,8 @@ impl<'a> FlowConstructor<'a> {
flow.add_new_child(anonymous_flow);
}
- /// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with possibly
- /// other `TableCaptionFlow`s or `TableFlow`s underneath it.
+ /// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
+ /// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode,
float_value: float::T) -> ConstructionResult {
let fragment = Fragment::new_from_specific_info(node, TableWrapperFragment);
diff --git a/components/layout/flow.rs b/components/layout/flow.rs
index c33f7528c6b..8c75cb27eec 100644
--- a/components/layout/flow.rs
+++ b/components/layout/flow.rs
@@ -36,7 +36,7 @@ use incremental::RestyleDamage;
use inline::InlineFlow;
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
use parallel::FlowParallelInfo;
-use table::TableFlow;
+use table::{ColumnInlineSize, TableFlow};
use table_caption::TableCaptionFlow;
use table_cell::TableCellFlow;
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.
/// Fails otherwise.
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- fail!("called col_inline_sizes() on an other flow than table-row/table-rowgroup/table")
- }
-
- /// If this is a table row flow or table rowgroup flow or table flow, returns column min
- /// 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")
+ fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
+ fail!("called column_inline_sizes() on non-table flow")
}
// Main methods
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index e9bd51b3ece..b2c9c23293c 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -14,7 +14,7 @@ use flow::Flow;
use flow_ref::FlowRef;
use inline::{InlineFragmentContext, InlineMetrics};
use layout_debug;
-use model::{Auto, IntrinsicISizes, MaybeAuto, Specified, specified};
+use model::{Auto, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, Specified, specified};
use model;
use text;
use util::{OpaqueNodeMethods, ToGfxColor};
@@ -529,51 +529,93 @@ impl Fragment {
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
- /// or replaced elements.
- fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes {
- let (use_margins, use_padding) = match self.specific {
+ /// Determines which quantities (border/padding/margin/specified) should be included in the
+ /// intrinsic inline size of this fragment.
+ fn quantities_included_in_intrinsic_inline_size(&self)
+ -> QuantitiesIncludedInIntrinsicInlineSizes {
+ match self.specific {
GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) |
- InputFragment => (true, true),
- TableFragment | TableCellFragment => (false, true),
- TableWrapperFragment => (true, false),
- TableRowFragment => (false, false),
+ InputFragment => QuantitiesIncludedInIntrinsicInlineSizes::all(),
+ TableFragment | TableCellFragment => {
+ IntrinsicInlineSizeIncludesPadding |
+ IntrinsicInlineSizeIncludesBorder |
+ IntrinsicInlineSizeIncludesSpecified
+ }
+ TableWrapperFragment => {
+ IntrinsicInlineSizeIncludesMargins |
+ IntrinsicInlineSizeIncludesBorder |
+ IntrinsicInlineSizeIncludesSpecified
+ }
+ TableRowFragment => {
+ IntrinsicInlineSizeIncludesBorder |
+ IntrinsicInlineSizeIncludesSpecified
+ }
ScannedTextFragment(_) | TableColumnFragment(_) | UnscannedTextFragment(_) |
InlineAbsoluteHypotheticalFragment(_) => {
- // Styles are irrelevant for these kinds of fragments.
- return IntrinsicISizes::new()
+ QuantitiesIncludedInIntrinsicInlineSizes::empty()
}
- };
+ }
+ }
+ /// 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 inline_size = MaybeAuto::from_style(style.content_inline_size(),
- Au(0)).specified_or_zero();
- let margin = style.logical_margin();
- let (margin_inline_start, margin_inline_end) = if use_margins {
- (MaybeAuto::from_style(margin.inline_start, Au(0)).specified_or_zero(),
+ // FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
+ // This will likely need to be done by pushing down definite sizes during selector
+ // 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())
} else {
- (Au(0), Au(0))
+ Au(0)
};
- let padding = style.logical_padding();
- let (padding_inline_start, padding_inline_end) = if use_padding {
- (model::specified(padding.inline_start, Au(0)),
+ // FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
+ // This will likely need to be done by pushing down definite sizes during selector
+ // 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)))
} 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?
- let border = self.border_width();
- let surround_inline_size = margin_inline_start + margin_inline_end + padding_inline_start + padding_inline_end +
- border.inline_start_end();
+ let surrounding_inline_size = self.surrounding_intrinsic_inline_size();
- IntrinsicISizes {
- minimum_inline_size: inline_size,
- preferred_inline_size: inline_size,
- surround_inline_size: surround_inline_size,
+ IntrinsicISizesContribution {
+ content_intrinsic_sizes: IntrinsicISizes {
+ minimum_inline_size: specified,
+ 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
- /// style. After this call, the `border_padding` and the vertical direction of the `margin`
- /// field will be correct.
- pub fn compute_border_padding_margins(&mut self,
- containing_block_inline_size: Au) {
- // Compute vertical margins. Note that this value will be ignored by layout if the style
- // specifies `auto`.
+ /// Computes the margins in the inline direction from the containing block inline-size and the
+ /// style. After this call, the inline direction of the `margin` field will be correct.
+ ///
+ /// Do not use this method if the inline direction margins are to be computed some other way
+ /// (for example, via constraint solving for blocks).
+ pub fn compute_inline_direction_margins(&mut self, containing_block_inline_size: Au) {
+ 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 {
TableFragment | TableCellFragment | TableRowFragment | TableColumnFragment(_) => {
self.margin.block_start = 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();
- 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();
- 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.
let border = self.border_width();
@@ -1241,28 +1313,23 @@ impl Fragment {
}
}
- /// Returns the intrinsic inline-sizes of this fragment.
- pub fn intrinsic_inline_sizes(&mut self) -> IntrinsicISizes {
+ /// Computes the intrinsic inline-sizes of this fragment.
+ pub fn compute_intrinsic_inline_sizes(&mut self) -> IntrinsicISizesContribution {
let mut result = self.style_specified_intrinsic_inline_size();
-
match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
TableColumnFragment(_) | TableRowFragment | TableWrapperFragment |
InlineAbsoluteHypotheticalFragment(_) | InputFragment => {}
InlineBlockFragment(ref mut info) => {
let block_flow = info.flow_ref.get_mut().as_block();
- result.minimum_inline_size = max(result.minimum_inline_size,
- 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);
+ result.union_block(&block_flow.base.intrinsic_inline_sizes)
}
ImageFragment(ref mut image_fragment_info) => {
let image_inline_size = image_fragment_info.image_inline_size();
- result.minimum_inline_size = max(result.minimum_inline_size, image_inline_size);
- result.preferred_inline_size = max(result.preferred_inline_size,
- image_inline_size);
+ result.union_block(&IntrinsicISizes {
+ minimum_inline_size: image_inline_size,
+ preferred_inline_size: image_inline_size,
+ })
}
ScannedTextFragment(ref text_fragment_info) => {
let range = &text_fragment_info.range;
@@ -1274,10 +1341,10 @@ impl Fragment {
.metrics_for_range(range)
.advance_width;
- result.minimum_inline_size = max(result.minimum_inline_size,
- min_line_inline_size);
- result.preferred_inline_size = max(result.preferred_inline_size,
- max_line_inline_size);
+ result.union_block(&IntrinsicISizes {
+ minimum_inline_size: min_line_inline_size,
+ preferred_inline_size: max_line_inline_size,
+ })
}
UnscannedTextFragment(..) => {
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 padding_inline_size =
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;
- result.preferred_inline_size = result.preferred_inline_size +
- border_width + padding_inline_size;
}
}
}
@@ -1477,7 +1542,9 @@ impl Fragment {
} else {
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()))
}
@@ -1502,8 +1569,7 @@ impl Fragment {
/// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced
/// content per CSS 2.1 § 10.3.2.
- pub fn assign_replaced_inline_size_if_necessary(&mut self,
- container_inline_size: Au) {
+ pub fn assign_replaced_inline_size_if_necessary(&mut self, container_inline_size: Au) {
match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InputFragment => return,
@@ -1515,8 +1581,6 @@ impl Fragment {
InlineAbsoluteHypotheticalFragment(_) => {}
};
- self.compute_border_padding_margins(container_inline_size);
-
let style_inline_size = self.style().content_inline_size();
let style_block_size = self.style().content_block_size();
let style_min_inline_size = self.style().min_inline_size();
@@ -1528,17 +1592,16 @@ impl Fragment {
match self.specific {
InlineAbsoluteHypotheticalFragment(ref mut info) => {
let block_flow = info.flow_ref.get_mut().as_block();
- block_flow.base.block_container_inline_size =
- block_flow.base.intrinsic_inline_sizes.preferred_inline_size +
- block_flow.base.intrinsic_inline_sizes.surround_inline_size;
+ block_flow.base.position.size.inline =
+ block_flow.base.intrinsic_inline_sizes.preferred_inline_size;
// This is a hypothetical box, so it takes up no space.
self.border_box.size.inline = Au(0);
}
InlineBlockFragment(ref mut info) => {
let block_flow = info.flow_ref.get_mut().as_block();
- self.border_box.size.inline = block_flow.base.intrinsic_inline_sizes.preferred_inline_size +
- block_flow.base.intrinsic_inline_sizes.surround_inline_size;
+ self.border_box.size.inline =
+ block_flow.base.intrinsic_inline_sizes.preferred_inline_size;
block_flow.base.block_container_inline_size = self.border_box.size.inline;
}
ScannedTextFragment(_) => {
@@ -1652,7 +1715,8 @@ impl Fragment {
InlineBlockFragment(ref mut info) => {
// Not the primary fragment, so we do not take the noncontent size into account.
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) => {
// 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_metrics = text::font_metrics_for_style(layout_context.font_context(),
&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(_) => {
// 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,
+ }
+}
+
diff --git a/components/layout/inline.rs b/components/layout/inline.rs
index 5c55bc600c2..26956155015 100644
--- a/components/layout/inline.rs
+++ b/components/layout/inline.rs
@@ -12,16 +12,15 @@ use flow;
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
use layout_debug;
-use model::IntrinsicISizes;
+use model::IntrinsicISizesContribution;
use text;
use wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf};
-use geom::Rect;
+use geom::{Rect, Size2D};
use gfx::display_list::{ContentLevel, DisplayList};
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
-use geom::Size2D;
use gfx::text::glyph::CharIndex;
use servo_util::geometry::Au;
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
@@ -922,24 +921,12 @@ impl Flow for InlineFlow {
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() {
debug!("Flow: measuring {}", *fragment);
-
- 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;
+ computation.union_inline(&fragment.compute_intrinsic_inline_sizes().finish())
}
-
- self.base.intrinsic_inline_sizes = intrinsic_inline_sizes;
+ self.base.intrinsic_inline_sizes = computation.finish()
}
/// 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 this = &mut *self;
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);
}
}
@@ -1130,6 +1120,7 @@ impl Flow for InlineFlow {
let block_flow = info.flow_ref.get_mut().as_block();
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
+
block_flow.base.abs_position =
self.base.abs_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
diff --git a/components/layout/model.rs b/components/layout/model.rs
index fca00da5994..e6b916a9545 100644
--- a/components/layout/model.rs
+++ b/components/layout/model.rs
@@ -249,14 +249,11 @@ pub struct IntrinsicISizes {
pub minimum_inline_size: Au,
/// The *preferred inline-size* of the content.
pub preferred_inline_size: Au,
- /// The estimated sum of borders, padding, and margins. Some calculations use this information
- /// when computing intrinsic inline-sizes.
- pub surround_inline_size: Au,
}
impl fmt::Show for IntrinsicISizes {
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 {
minimum_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,
+}
- pub fn total_minimum_inline_size(&self) -> Au {
- self.minimum_inline_size + self.surround_inline_size
+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_preferred_inline_size(&self) -> Au {
- self.preferred_inline_size + self.surround_inline_size
+ /// Adds the content intrinsic sizes and the surrounding size together to yield the final
+ /// 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,
+ }
+ }
+
+ /// Updates the computation so that the minimum is the maximum of the current minimum and the
+ /// 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.
+#[deriving(PartialEq)]
pub enum MaybeAuto {
Auto,
Specified(Au),
@@ -290,7 +334,9 @@ impl MaybeAuto {
-> MaybeAuto {
match length {
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)
}
}
diff --git a/components/layout/table.rs b/components/layout/table.rs
index 7aca57f7fc4..e066e7adc6a 100644
--- a/components/layout/table.rs
+++ b/components/layout/table.rs
@@ -14,6 +14,7 @@ use floats::FloatKind;
use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use fragment::Fragment;
use layout_debug;
+use model::{IntrinsicISizes, IntrinsicISizesContribution};
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
use wrapper::ThreadSafeLayoutNode;
@@ -21,7 +22,8 @@ use servo_util::geometry::Au;
use servo_util::logical_geometry::LogicalRect;
use std::cmp::max;
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.
/// 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 block_flow: BlockFlow,
- /// Column inline-sizes
- pub col_inline_sizes: Vec<Au>,
-
- /// Column min inline-sizes.
- pub col_min_inline_sizes: Vec<Au>,
-
- /// Column pref inline-sizes.
- pub col_pref_inline_sizes: Vec<Au>,
+ /// Information about the inline-sizes of each column.
+ pub column_inline_sizes: Vec<ColumnInlineSize>,
/// Table-layout property
pub table_layout: TableLayout,
@@ -56,9 +52,7 @@ impl TableFlow {
};
TableFlow {
block_flow: block_flow,
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
+ column_inline_sizes: Vec::new(),
table_layout: table_layout
}
}
@@ -75,9 +69,7 @@ impl TableFlow {
};
TableFlow {
block_flow: block_flow,
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
+ column_inline_sizes: Vec::new(),
table_layout: table_layout
}
}
@@ -95,30 +87,33 @@ impl TableFlow {
};
TableFlow {
block_flow: block_flow,
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
+ column_inline_sizes: Vec::new(),
table_layout: table_layout
}
}
- /// Update the corresponding value of self_inline-sizes if a value of kid_inline-sizes has larger value
- /// than one of self_inline-sizes.
- pub fn update_col_inline_sizes(self_inline_sizes: &mut Vec<Au>, kid_inline_sizes: &Vec<Au>) -> Au {
- let mut sum_inline_sizes = Au(0);
- let mut kid_inline_sizes_it = kid_inline_sizes.iter();
- for self_inline_size in self_inline_sizes.iter_mut() {
- match kid_inline_sizes_it.next() {
- Some(kid_inline_size) => {
- if *self_inline_size < *kid_inline_size {
- *self_inline_size = *kid_inline_size;
- }
- },
- None => {}
- }
- sum_inline_sizes = sum_inline_sizes + *self_inline_size;
+ /// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has
+ /// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline
+ /// sizes.
+ pub fn update_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnInlineSize>,
+ child_inline_sizes: &Vec<ColumnInlineSize>)
+ -> IntrinsicISizes {
+ let mut total_inline_sizes = IntrinsicISizes::new();
+ for (parent_sizes, child_sizes) in parent_inline_sizes.iter_mut()
+ .zip(child_inline_sizes.iter()) {
+ *parent_sizes = ColumnInlineSize {
+ minimum_length: max(parent_sizes.minimum_length, child_sizes.minimum_length),
+ percentage: parent_sizes.greatest_percentage(child_sizes),
+ preferred: max(parent_sizes.preferred, child_sizes.preferred),
+ constrained: parent_sizes.constrained || child_sizes.constrained
+ };
+
+ 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.
@@ -155,16 +150,8 @@ impl Flow for TableFlow {
&mut self.block_flow
}
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- &mut self.col_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
+ fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
+ &mut self.column_inline_sizes
}
/// 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}",
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();
let mut did_first_row = false;
-
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() {
- self.col_inline_sizes.push_all(kid.as_table_colgroup().inline_sizes.as_slice());
- self.col_min_inline_sizes = self.col_inline_sizes.clone();
- self.col_pref_inline_sizes = self.col_inline_sizes.clone();
+ for specified_inline_size in kid.as_table_colgroup().inline_sizes.iter() {
+ self.column_inline_sizes.push(ColumnInlineSize {
+ 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() {
- // read column inline-sizes from table-row-group/table-row, and assign
- // inline-size=0 for the columns not defined in column-group
- // FIXME: need to read inline-sizes from either table-header-group OR
- // first table-row
+ // Read column inline-sizes from the table-row-group/table-row, and assign
+ // inline-size=0 for the columns not defined in the column group.
+ // FIXME: Need to read inline-sizes from either table-header-group OR the first
+ // table-row.
match self.table_layout {
FixedLayout => {
- let kid_col_inline_sizes = kid.col_inline_sizes();
+ // Fixed table layout only looks at the first row.
if !did_first_row {
did_first_row = true;
- let mut child_inline_sizes = kid_col_inline_sizes.iter();
- for col_inline_size in self.col_inline_sizes.iter_mut() {
- match child_inline_sizes.next() {
- Some(child_inline_size) => {
- if *col_inline_size == Au::new(0) {
- *col_inline_size = *child_inline_size;
- }
- },
- None => break
- }
+ for child_column_inline_size in kid.column_inline_sizes().iter() {
+ self.column_inline_sizes.push(*child_column_inline_size);
}
}
- 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 => {
- min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
- pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
-
- // update the number of column inline-sizes from table-rows.
- let num_cols = self.col_min_inline_sizes.len();
- let num_child_cols = kid.col_min_inline_sizes().len();
- debug!("table until the previous row has {} column(s) and this row has {} column(s)",
- num_cols, num_child_cols);
- for i in range(num_cols, num_child_cols) {
- self.col_inline_sizes.push(Au::new(0));
- let new_kid_min = kid.col_min_inline_sizes()[i];
- self.col_min_inline_sizes.push( new_kid_min );
- let new_kid_pref = kid.col_pref_inline_sizes()[i];
- self.col_pref_inline_sizes.push( new_kid_pref );
- min_inline_size = min_inline_size + new_kid_min;
- pref_inline_size = pref_inline_size + new_kid_pref;
+ let child_column_inline_sizes = kid.column_inline_sizes();
+ let mut child_intrinsic_sizes =
+ TableFlow::update_column_inline_sizes(&mut self.column_inline_sizes,
+ child_column_inline_sizes);
+
+ // Add new columns if processing this row caused us to discover them.
+ let child_column_count = child_column_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)",
+ parent_column_count,
+ child_column_count);
+ self.column_inline_sizes.reserve(child_column_count);
+ for i in range(parent_column_count, child_column_count) {
+ let inline_size_for_new_column = (*child_column_inline_sizes)[i];
+ child_intrinsic_sizes.minimum_inline_size =
+ child_intrinsic_sizes.minimum_inline_size +
+ 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.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;
+ self.block_flow.base.intrinsic_inline_sizes = computation.finish()
}
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
+ /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
+ /// When called on this context, the context has had its inline-size set by the parent context.
+ fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug_scope!("table::assign_inline_sizes {:s}",
self.block_flow.base.debug_id());
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 mut num_unspecified_inline_sizes = 0;
- let mut total_column_inline_size = Au::new(0);
- for col_inline_size in self.col_inline_sizes.iter() {
- if *col_inline_size == Au::new(0) {
- num_unspecified_inline_sizes += 1;
+ let mut total_column_inline_size = Au(0);
+ for column_inline_size in self.column_inline_sizes.iter() {
+ let this_column_inline_size = column_inline_size.minimum_length;
+ if this_column_inline_size == Au(0) {
+ num_unspecified_inline_sizes += 1
} 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;
- 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 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 {
FixedLayout => {
- // In fixed table layout, we distribute extra space among the unspecified columns if there are
- // any, or among all the columns if all are specified.
- if (total_column_inline_size < content_inline_size) && (num_unspecified_inline_sizes == 0) {
- let ratio = content_inline_size.to_f64().unwrap() / total_column_inline_size.to_f64().unwrap();
- for col_inline_size in self.col_inline_sizes.iter_mut() {
- *col_inline_size = (*col_inline_size).scale_by(ratio);
+ // In fixed table layout, we distribute extra space among the unspecified columns
+ // if there are any, or among all the columns if all are specified.
+ if total_column_inline_size < content_inline_size &&
+ num_unspecified_inline_sizes == 0 {
+ let extra_column_inline_size = content_inline_size;
+ (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 {
- let extra_column_inline_size = (content_inline_size - total_column_inline_size) / num_unspecified_inline_sizes;
- for col_inline_size in self.col_inline_sizes.iter_mut() {
- if *col_inline_size == Au(0) {
- *col_inline_size = extra_column_inline_size;
+ let extra_column_inline_size =
+ (content_inline_size - total_column_inline_size) /
+ num_unspecified_inline_sizes;
+ 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_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>) {
@@ -340,10 +339,12 @@ impl ISizeAndMarginsComputer for InternalTable {
///
/// CSS Section 10.4: Minimum and Maximum inline-sizes
fn compute_used_inline_size(&self,
- block: &mut BlockFlow,
- ctx: &LayoutContext,
- parent_flow_inline_size: Au) {
- let input = self.compute_inline_size_constraint_inputs(block, parent_flow_inline_size, ctx);
+ block: &mut BlockFlow,
+ ctx: &LayoutContext,
+ parent_flow_inline_size: Au) {
+ let input = self.compute_inline_size_constraint_inputs(block,
+ parent_flow_inline_size,
+ ctx);
let solution = self.solve_inline_size_constraints(block, &input);
self.set_inline_size_constraint_solutions(block, solution);
}
@@ -351,6 +352,48 @@ impl ISizeAndMarginsComputer for InternalTable {
/// Solve the inline-size and margins constraints for this block flow.
fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
-> ISizeConstraintSolution {
- ISizeConstraintSolution::new(input.available_inline_size, Au::new(0), Au::new(0))
+ 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
+ }
}
}
+
diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs
index e1581e2d642..0cda4fb922a 100644
--- a/components/layout/table_cell.rs
+++ b/components/layout/table_cell.rs
@@ -26,7 +26,8 @@ pub struct 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 {
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
}
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.minimum_inline_size;
}
}
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent table row.
+ /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
+ /// When called on this context, the context has had its inline-size set by the parent table
+ /// row.
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
let _scope = layout_debug_scope!("table_cell::assign_inline_sizes {:s}",
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 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;
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,
- content_inline_size,
- None);
+ content_inline_size,
+ None);
}
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs
index 427cd6f5f1f..664d28e8100 100644
--- a/components/layout/table_colgroup.rs
+++ b/components/layout/table_colgroup.rs
@@ -10,11 +10,11 @@ use context::LayoutContext;
use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow};
use fragment::{Fragment, TableColumnFragment};
use layout_debug;
-use model::{MaybeAuto};
use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
use std::fmt;
+use style::computed_values::LengthOrPercentageOrAuto;
/// A table formatting context.
pub struct TableColGroupFlow {
@@ -27,14 +27,17 @@ pub struct TableColGroupFlow {
/// The table column fragments
pub cols: Vec<Fragment>,
- /// The specified inline-sizes of table columns
- pub inline_sizes: Vec<Au>,
+ /// The specified inline-sizes of table columns. (We use `LengthOrPercentageOrAuto` here in
+ /// lieu of `ColumnInlineSize` because column groups do not establish minimum or preferred
+ /// inline sizes.)
+ pub inline_sizes: Vec<LengthOrPercentageOrAuto>,
}
impl TableColGroupFlow {
pub fn from_node_and_fragments(node: &ThreadSafeLayoutNode,
fragment: Fragment,
- fragments: Vec<Fragment>) -> TableColGroupFlow {
+ fragments: Vec<Fragment>)
+ -> TableColGroupFlow {
TableColGroupFlow {
base: BaseFlow::new((*node).clone()),
fragment: Some(fragment),
@@ -58,27 +61,25 @@ impl Flow for TableColGroupFlow {
self.base.debug_id());
for fragment in self.cols.iter() {
- // get the specified value from inline-size property
- let inline_size = MaybeAuto::from_style(fragment.style().content_inline_size(),
- Au::new(0)).specified_or_zero();
-
+ // Retrieve the specified value from the appropriate CSS property.
+ let inline_size = fragment.style().content_inline_size();
let span: int = match fragment.specific {
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) {
- 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.
- /// Therefore, table colgroup flow does not need to assign its inline-size.
- fn assign_inline_sizes(&mut self, _ctx: &LayoutContext) {
+ /// Table column inline-sizes are assigned in the table flow and propagated to table row flows
+ /// and/or rowgroup flows. Therefore, table colgroup flows do not need to assign inline-sizes.
+ fn assign_inline_sizes(&mut self, _: &LayoutContext) {
}
- /// Table column do not have block-size.
- fn assign_block_size(&mut self, _ctx: &LayoutContext) {
+ /// Table columns do not have block-size.
+ fn assign_block_size(&mut self, _: &LayoutContext) {
}
fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs
index d7236c5c41b..0ee0441e3ca 100644
--- a/components/layout/table_row.rs
+++ b/components/layout/table_row.rs
@@ -14,27 +14,22 @@ use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use flow;
use fragment::Fragment;
use layout_debug;
-use table::InternalTable;
+use table::{ColumnInlineSize, InternalTable};
use model::{MaybeAuto, Specified, Auto};
use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
use std::cmp::max;
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)]
pub struct TableRowFlow {
pub block_flow: BlockFlow,
- /// Column inline-sizes.
- pub col_inline_sizes: Vec<Au>,
-
- /// Column min inline-sizes.
- pub col_min_inline_sizes: Vec<Au>,
-
- /// Column pref inline-sizes.
- pub col_pref_inline_sizes: Vec<Au>,
+ /// Information about the inline-sizes of each column.
+ pub column_inline_sizes: Vec<ColumnInlineSize>,
}
impl TableRowFlow {
@@ -43,9 +38,7 @@ impl TableRowFlow {
-> TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
+ column_inline_sizes: Vec::new()
}
}
@@ -54,9 +47,7 @@ impl TableRowFlow {
-> TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node(constructor, node),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
+ column_inline_sizes: Vec::new()
}
}
@@ -65,8 +56,8 @@ impl TableRowFlow {
}
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
- // TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
- // should be updated. Currently, they are set as Au(0).
+ // TODO: If border-collapse: collapse, block_start_offset, block_end_offset, and
+ // inline_start_offset should be updated. Currently, they are set as Au(0).
(Au(0), Au(0), Au(0))
}
@@ -82,19 +73,21 @@ impl TableRowFlow {
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 )
- let mut max_y = Au::new(0);
+ // Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of all
+ // cells).
+ let mut max_y = Au(0);
for kid in self.block_flow.base.child_iter() {
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
{
let child_fragment = kid.as_table_cell().fragment();
// TODO: Percentage block-size
- let child_specified_block_size = MaybeAuto::from_style(child_fragment.style().content_block_size(),
- Au::new(0)).specified_or_zero();
- max_y =
- max(max_y,
- child_specified_block_size + child_fragment.border_padding.block_start_end());
+ let child_specified_block_size =
+ MaybeAuto::from_style(child_fragment.style().content_block_size(),
+ Au::new(0)).specified_or_zero();
+ max_y = max(max_y,
+ child_specified_block_size +
+ child_fragment.border_padding.block_start_end());
}
let child_node = flow::mut_base(kid);
child_node.position.start.b = cur_y;
@@ -103,7 +96,11 @@ impl TableRowFlow {
let mut block_size = max_y;
// TODO: Percentage block-size
- block_size = match MaybeAuto::from_style(self.block_flow.fragment.style().content_block_size(), Au(0)) {
+ block_size = match MaybeAuto::from_style(self.block_flow
+ .fragment
+ .style()
+ .content_block_size(),
+ Au(0)) {
Auto => block_size,
Specified(value) => max(value, block_size)
};
@@ -153,70 +150,84 @@ impl Flow for TableRowFlow {
&mut self.block_flow
}
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- &mut self.col_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
+ fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
+ &mut self.column_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.
+ /// 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.
- /// 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) {
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);
- let mut pref_inline_size = Au(0);
- /* find the specified inline_sizes from child table-cell contexts */
+ // Bubble up the specified inline-sizes from child table cells.
+ let (mut min_inline_size, mut pref_inline_size) = (Au(0), Au(0));
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_cell());
- // collect the specified column inline-sizes of cells. These are used in fixed table layout calculation.
- {
- let child_fragment = kid.as_table_cell().fragment();
- let child_specified_inline_size = MaybeAuto::from_style(child_fragment.style().content_inline_size(),
- Au::new(0)).specified_or_zero();
- self.col_inline_sizes.push(child_specified_inline_size);
- }
+ // Collect the specified column inline-size of the cell. This is used in both fixed and
+ // automatic table layout calculation.
+ let child_specified_inline_size = kid.as_table_cell()
+ .fragment()
+ .style()
+ .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);
- self.col_min_inline_sizes.push(child_base.intrinsic_inline_sizes.minimum_inline_size);
- self.col_pref_inline_sizes.push(child_base.intrinsic_inline_sizes.preferred_inline_size);
- min_inline_size = min_inline_size + child_base.intrinsic_inline_sizes.minimum_inline_size;
- pref_inline_size = pref_inline_size + child_base.intrinsic_inline_sizes.preferred_inline_size;
+ let child_column_inline_size = ColumnInlineSize {
+ minimum_length: match child_specified_inline_size {
+ LPA_Auto | LPA_Percentage(_) => {
+ 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.preferred_inline_size = max(
- min_inline_size, pref_inline_size);
+ 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 called
- /// on this context, the context has had its inline-size set by the parent context.
+ /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
+ /// When called on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
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");
// 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;
- // FIXME: In case of border-collapse: collapse, inline-start_content_edge should be border-inline-start
- let inline_start_content_edge = Au::new(0);
+ // FIXME: In case of border-collapse: collapse, inline_start_content_edge should be
+ // border_inline_start.
+ let inline_start_content_edge = Au(0);
let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
-
- self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, Au(0), Some(self.col_inline_sizes.clone()));
+ 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,
+ containing_block_inline_size,
+ Some(self.column_inline_sizes.as_slice()));
}
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)
}
}
+
diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs
index 7efb2e96d09..1a4b1ce374a 100644
--- a/components/layout/table_rowgroup.rs
+++ b/components/layout/table_rowgroup.rs
@@ -14,11 +14,11 @@ use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use flow;
use fragment::Fragment;
use layout_debug;
-use table::{InternalTable, TableFlow};
+use model::IntrinsicISizesContribution;
+use table::{ColumnInlineSize, InternalTable, TableFlow};
use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
-use std::cmp::max;
use std::fmt;
/// A table formatting context.
@@ -26,14 +26,8 @@ use std::fmt;
pub struct TableRowGroupFlow {
pub block_flow: BlockFlow,
- /// Column inline-sizes
- pub col_inline_sizes: Vec<Au>,
-
- /// Column min inline-sizes.
- pub col_min_inline_sizes: Vec<Au>,
-
- /// Column pref inline-sizes.
- pub col_pref_inline_sizes: Vec<Au>,
+ /// Information about the inline-sizes of each column.
+ pub column_inline_sizes: Vec<ColumnInlineSize>,
}
impl TableRowGroupFlow {
@@ -42,9 +36,7 @@ impl TableRowGroupFlow {
-> TableRowGroupFlow {
TableRowGroupFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
+ column_inline_sizes: Vec::new(),
}
}
@@ -53,9 +45,7 @@ impl TableRowGroupFlow {
-> TableRowGroupFlow {
TableRowGroupFlow {
block_flow: BlockFlow::from_node(constructor, node),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
+ column_inline_sizes: Vec::new(),
}
}
@@ -64,8 +54,8 @@ impl TableRowGroupFlow {
}
fn initialize_offsets(&mut self) -> (Au, Au, Au) {
- // TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
- // should be updated. Currently, they are set as Au(0).
+ // TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and
+ // inline-start_offset should be updated. Currently, they are set as Au(0).
(Au(0), Au(0), Au(0))
}
@@ -120,16 +110,8 @@ impl Flow for TableRowGroupFlow {
&mut self.block_flow
}
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- &mut self.col_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
+ fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> {
+ &mut self.column_inline_sizes
}
/// 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.
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:s}",
- self.block_flow.base.debug_id());
-
- let mut min_inline_size = Au(0);
- let mut pref_inline_size = Au(0);
+ self.block_flow.base.debug_id());
+ let mut computation = IntrinsicISizesContribution::new();
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_row());
- // calculate min_inline-size & pref_inline-size for automatic table layout calculation
- // 'self.col_min_inline-sizes' collects the maximum value of cells' min-inline-sizes for each column.
- // 'self.col_pref_inline-sizes' collects the maximum value of cells' pref-inline-sizes for each column.
- if self.col_inline_sizes.is_empty() { // First Row
- assert!(self.col_min_inline_sizes.is_empty() && self.col_pref_inline_sizes.is_empty());
- // 'self.col_inline-sizes' collects the specified column inline-sizes from the first table-row for fixed table layout calculation.
- self.col_inline_sizes = kid.col_inline_sizes().clone();
- self.col_min_inline_sizes = kid.col_min_inline_sizes().clone();
- self.col_pref_inline_sizes = kid.col_pref_inline_sizes().clone();
+ // Calculate minimum and preferred inline sizes for automatic table layout.
+ if self.column_inline_sizes.is_empty() {
+ // We're the first row.
+ debug_assert!(self.column_inline_sizes.is_empty());
+ self.column_inline_sizes = kid.column_inline_sizes().clone();
} else {
- min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
- pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
+ let mut child_intrinsic_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.
- let num_cols = self.col_inline_sizes.len();
- let num_child_cols = kid.col_min_inline_sizes().len();
- for i in range(num_cols, num_child_cols) {
- self.col_inline_sizes.push(Au::new(0));
- let new_kid_min = kid.col_min_inline_sizes()[i];
- self.col_min_inline_sizes.push(kid.col_min_inline_sizes()[i]);
- let new_kid_pref = kid.col_pref_inline_sizes()[i];
- self.col_pref_inline_sizes.push(kid.col_pref_inline_sizes()[i]);
- min_inline_size = min_inline_size + new_kid_min;
- pref_inline_size = pref_inline_size + new_kid_pref;
+ let column_count = self.column_inline_sizes.len();
+ let child_column_count = kid.column_inline_sizes().len();
+ for i in range(column_count, child_column_count) {
+ let this_column_inline_size = (*kid.column_inline_sizes())[i];
+
+ // FIXME(pcwalton): Ignoring the percentage here seems dubious.
+ child_intrinsic_sizes.minimum_inline_size =
+ child_intrinsic_sizes.minimum_inline_size +
+ this_column_inline_size.minimum_length;
+ 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.preferred_inline_size = max(
- min_inline_size, pref_inline_size);
+ self.block_flow.base.intrinsic_inline_sizes = computation.finish()
}
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent context.
+ /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
+ /// When called on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
let _scope = layout_debug_scope!("table_rowgroup::assign_inline_sizes {:s}",
self.block_flow.base.debug_id());
@@ -199,9 +179,14 @@ impl Flow for TableRowGroupFlow {
let content_inline_size = containing_block_inline_size;
let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
-
- self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, Some(self.col_inline_sizes.clone()));
+ 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.column_inline_sizes.as_slice()));
}
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs
index 8938bd190ee..646a8a58913 100644
--- a/components/layout/table_wrapper.rs
+++ b/components/layout/table_wrapper.rs
@@ -3,22 +3,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! 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)]
use block::{BlockFlow, BlockNonReplaced, FloatNonReplaced, ISizeAndMarginsComputer};
-use block::{ISizeConstraintInput, MarginsMayNotCollapse};
+use block::{MarginsMayNotCollapse};
use construct::FlowConstructor;
use context::LayoutContext;
use floats::FloatKind;
use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use fragment::Fragment;
-use model::{Specified, Auto, specified};
+use table::ColumnInlineSize;
use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
-use std::cmp::max;
+use std::cmp::{max, min};
use std::fmt;
+use style::CSSFloat;
use style::computed_values::{clear, float, table_layout};
#[deriving(Encodable)]
@@ -32,8 +40,8 @@ pub enum TableLayout {
pub struct TableWrapperFlow {
pub block_flow: BlockFlow,
- /// Column inline-sizes
- pub col_inline_sizes: Vec<Au>,
+ /// Inline-size information for each column.
+ pub column_inline_sizes: Vec<ColumnInlineSize>,
/// Table-layout property
pub table_layout: TableLayout,
@@ -52,7 +60,7 @@ impl TableWrapperFlow {
};
TableWrapperFlow {
block_flow: block_flow,
- col_inline_sizes: vec!(),
+ column_inline_sizes: vec!(),
table_layout: table_layout
}
}
@@ -69,7 +77,7 @@ impl TableWrapperFlow {
};
TableWrapperFlow {
block_flow: block_flow,
- col_inline_sizes: vec!(),
+ column_inline_sizes: vec!(),
table_layout: table_layout
}
}
@@ -87,7 +95,7 @@ impl TableWrapperFlow {
};
TableWrapperFlow {
block_flow: block_flow,
- col_inline_sizes: vec!(),
+ column_inline_sizes: vec!(),
table_layout: table_layout
}
}
@@ -97,113 +105,87 @@ impl TableWrapperFlow {
self.block_flow.build_display_list_block(layout_context);
}
- fn calculate_table_column_sizes(&mut self, mut input: ISizeConstraintInput)
- -> ISizeConstraintInput {
- let style = self.block_flow.fragment.style();
-
- // Get inline-start and inline-end paddings, borders for table.
- // We get these values from the fragment's style since table_wrapper doesn't have its own
- // border or padding. input.available_inline_size is same as containing_block_inline_size
- // in table_wrapper.
- let padding = style.logical_padding();
- let border = style.logical_border_width();
- let padding_and_borders =
- specified(padding.inline_start, input.available_inline_size) +
- specified(padding.inline_end, input.available_inline_size) +
- border.inline_start +
- border.inline_end;
-
- let computed_inline_size = match self.table_layout {
- FixedLayout => {
- 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
+ /// Calculates table column sizes for automatic layout per INTRINSIC § 4.3.
+ fn calculate_table_column_sizes_for_automatic_layout(&mut self) {
+ // Find the padding and border of our first child, which is the table itself.
+ //
+ // This is a little weird because we're computing border/padding/margins for our child,
+ // when normally the child computes it itself. But it has to be this way because the
+ // padding will affect where we place the child. This is an odd artifact of the way that
+ // tables are separated into table flows and table wrapper flows.
+ let available_inline_size = self.block_flow.fragment.border_box.size.inline;
+ let mut table_border_padding = Au(0);
+ for kid in self.block_flow.base.child_iter() {
+ if kid.is_table() {
+ let kid_block = kid.as_block();
+ kid_block.fragment.compute_border_and_padding(available_inline_size);
+ kid_block.fragment.compute_block_direction_margins(available_inline_size);
+ kid_block.fragment.compute_inline_direction_margins(available_inline_size);
+ table_border_padding = kid_block.fragment.border_padding.inline_start_end();
+ break
}
- };
- 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,
layout_context: &LayoutContext,
parent_flow_inline_size: Au) {
// 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,
parent_flow_inline_size,
layout_context)
@@ -213,9 +195,6 @@ impl TableWrapperFlow {
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.
if self.is_float() {
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) {
- // 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() {
- assert!(kid.is_table_caption() || kid.is_table());
-
+ debug_assert!(kid.is_table_caption() || 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);
+ 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 content_inline_size = self.block_flow.fragment.border_box.size.inline;
// 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,
- 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,
content_inline_size,
- assigned_col_inline_sizes);
+ assigned_column_inline_sizes);
}
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
+ }
+}
+