@ -6,7 +6,7 @@ use core::cmp::Ordering;
use std ::mem ;
use std ::ops ::Range ;
use app_units ::{ Au , MAX_AU } ;
use app_units ::Au ;
use log ::warn ;
use rayon ::iter ::{ IndexedParallelIterator , IntoParallelRefIterator , ParallelIterator } ;
use servo_arc ::Arc ;
@ -99,7 +99,24 @@ struct ColumnLayout {
constrained : bool ,
has_originating_cells : bool ,
content_sizes : ContentSizes ,
percentage : Percentage ,
percentage : Option < Percentage > ,
}
fn max_two_optional_percentages (
a : Option < Percentage > ,
b : Option < Percentage > ,
) -> Option < Percentage > {
match ( a , b ) {
( Some ( a ) , Some ( b ) ) = > Some ( Percentage ( a . 0. max ( b . 0 ) ) ) ,
_ = > a . or ( b ) ,
}
}
impl ColumnLayout {
fn incorporate_cell_measure ( & mut self , cell_measure : & CellOrTrackMeasure ) {
self . content_sizes . max_assign ( cell_measure . content_sizes ) ;
self . percentage = max_two_optional_percentages ( self . percentage , cell_measure . percentage ) ;
}
}
impl CollapsedBorder {
@ -200,19 +217,19 @@ pub(crate) struct TableLayout<'a> {
#[ derive(Clone, Debug) ]
struct CellOrTrackMeasure {
content_sizes : ContentSizes ,
percentage : Percentage ,
percentage : Option < Percentage > ,
}
impl Zero for CellOrTrackMeasure {
fn zero ( ) -> Self {
Self {
content_sizes : ContentSizes ::zero ( ) ,
percentage : Percentage ( 0. ) ,
percentage : None ,
}
}
fn is_zero ( & self ) -> bool {
self . content_sizes . is_zero ( ) & & self . percentage . is_ zero ( )
self . content_sizes . is_zero ( ) & & self . percentage . is_ none ( )
}
}
@ -280,8 +297,13 @@ impl<'a> TableLayout<'a> {
block : padding . block_sum ( ) + border . block_sum ( ) ,
} ;
let ( size , min_size , max_size , inline_size_is_auto , percentage_contribution ) =
get_outer_sizes_for_measurement (
let CellOrColumnOuterSizes {
preferred : preferred_size ,
min : min_size ,
max : max_size ,
inline_preferred_size_is_auto ,
percentage : percentage_size ,
} = CellOrColumnOuterSizes ::new (
& cell . base . style ,
writing_mode ,
& padding_border_sums ,
@ -305,31 +327,30 @@ impl<'a> TableLayout<'a> {
// These formulas differ from the spec, but seem to match Gecko and Blink.
let outer_min_content_width = if is_in_fixed_mode {
if inline_ size_is_auto {
if inline_ preferred_ size_is_auto {
// This is an outer size, but we deliberately ignore borders and padding.
// This is like allowing the content-box width to be negative.
Au ::zero ( )
} else {
size . inline . min ( max_size . inline ) . max ( min_size . inline )
preferred_size
. inline
. clamp_between_extremums ( min_size . inline , max_size . inline )
}
} else {
inline_content_sizes
. min_content
. min ( max_size . inline )
. max ( min_size . inline )
. clamp_between_extremums ( min_size . inline , max_size . inline )
} ;
let outer_max_content_width = if self . columns [ column_index ] . constrained {
inline_content_sizes
. min_content
. max ( size . inline )
. min ( max_size . inline )
. max ( min_size . inline )
. max ( preferred_size . inline )
. clamp_between_extremums ( min_size . inline , max_size . inline )
} else {
inline_content_sizes
. max_content
. max ( size . inline )
. min ( max_size . inline )
. max ( min_size . inline )
. max ( preferred_size . inline )
. clamp_between_extremums ( min_size . inline , max_size . inline )
} ;
assert! ( outer_min_content_width < = outer_max_content_width ) ;
@ -338,7 +359,7 @@ impl<'a> TableLayout<'a> {
min_content : outer_min_content_width ,
max_content : outer_max_content_width ,
} ,
percentage : percentage_ contribution . inline ,
percentage : percentage_ size . inline ,
}
} ;
@ -346,8 +367,8 @@ impl<'a> TableLayout<'a> {
// These sizes are incorporated after the first row layout pass, when the block size
// of the layout is known.
let block_measure = CellOrTrackMeasure {
content_sizes : size. block . into ( ) ,
percentage : percentage_ contribution . block ,
content_sizes : preferred_ size. block . into ( ) ,
percentage : percentage_ size . block ,
} ;
self . cell_measures [ row_index ] [ column_index ] = LogicalVec2 {
@ -465,7 +486,7 @@ impl<'a> TableLayout<'a> {
//
// TODO: Take into account `table-column` and `table-column-group` lengths.
// TODO: Take into account changes to this computation for fixed table layout.
let mut next_span_n = usize ::MAX ;
let mut colspan_cell_constraints = Vec ::new ( ) ;
for column_index in 0 .. self . table . size . width {
let column = & mut self . columns [ column_index ] ;
@ -477,31 +498,34 @@ impl<'a> TableLayout<'a> {
for row_index in 0 .. self . table . size . height {
let coords = TableSlotCoordinates ::new ( column_index , row_index ) ;
match self . table . resolve_first_cell ( coords ) {
Some ( cell ) if cell . colspan = = 1 = > cell ,
Some ( cell ) = > {
next_span_n = next_span_n . min ( cell . colspan ) ;
continue ;
} ,
let cell_measure = & self . cell_measures [ row_index ] [ column_index ] . inline ;
let cell = match self . table . get_slot ( coords ) {
Some ( TableSlot ::Cell ( cell ) ) = > cell ,
_ = > continue ,
} ;
if cell . colspan ! = 1 {
colspan_cell_constraints . push ( ColspanToDistribute {
starting_column : column_index ,
span : cell . colspan ,
content_sizes : cell_measure . content_sizes ,
percentage : cell_measure . percentage ,
} ) ;
continue ;
}
// This takes the max of `min_content`, `max_content`, and
// intrinsic percentage width as described above.
let cell_measure = & self . cell_measures [ row_index ] [ column_index ] . inline ;
column . content_sizes . max_assign ( cell_measure . content_sizes ) ;
column . percentage =
Percentage ( column_measure . percentage . 0. max ( cell_measure . percentage . 0 ) ) ;
column . incorporate_cell_measure ( cell_measure ) ;
}
}
// Now we have the base computation complete, so iteratively take into account cells
// with higher colspan. Using `next_span_n` we can skip over span counts that don't
// correspond to any cells.
while next_span_n < usize ::MAX {
( next_span_n , self . columns ) =
self . compute_content_sizes_for_columns_with_span_up_to_n ( next_span_n ) ;
}
// Sort the colspanned cell constraints by their span and starting column.
colspan_cell_constraints . sort_by ( ColspanToDistribute ::comparison_for_sort ) ;
// Distribute constraints from cells with colspan != 1 to their component columns.
self . distribute_colspanned_cells_to_columns ( colspan_cell_constraints ) ;
// > intrinsic percentage width of a column:
// > the smaller of:
@ -511,216 +535,98 @@ impl<'a> TableLayout<'a> {
// > the table (further left when direction is "ltr" (right for "rtl"))
let mut total_intrinsic_percentage_width = 0. ;
for column in self . columns . iter_mut ( ) {
let final_intrinsic_percentage_width = column
. percentage
. 0
. min ( 1. - total_intrinsic_percentage_width ) ;
if let Some ( ref mut percentage ) = column . percentage {
let final_intrinsic_percentage_width =
percentage . 0. min ( 1. - total_intrinsic_percentage_width ) ;
total_intrinsic_percentage_width + = final_intrinsic_percentage_width ;
column . percentage = Percentage ( final_intrinsic_percentage_width ) ;
* percentage = Percentage ( final_intrinsic_percentage_width ) ;
}
}
}
fn compute_content_sizes_for_columns_with_span_up_to_n (
& self ,
n : usize ,
) -> ( usize , Vec < ColumnLayout > ) {
let mut next_span_n = usize ::MAX ;
let mut new_columns = Vec ::new ( ) ;
let border_spacing = self . table . border_spacing ( ) ;
fn distribute_colspanned_cells_to_columns (
& mut self ,
colspan_cell_constraints : Vec < ColspanToDistribute > ,
) {
for colspan_cell_constraints in colspan_cell_constraints {
self . distribute_colspanned_cell_to_columns ( colspan_cell_constraints ) ;
}
}
for column_index in 0 .. self . table . size . width {
let old_column = & self . columns [ column_index ] ;
let mut new_column_content_sizes = old_column . content_sizes ;
let mut new_column_intrinsic_percentage_width = old_column . percentage ;
/// Distribute the inline size from a cell with colspan != 1 to the columns that it spans.
/// This is heavily inspired by the approach that Chromium takes in redistributing colspan
/// cells' inline size to columns (`DistributeColspanCellToColumnsAuto` in
/// `blink/renderer/core/layout/table/table_layout_utils.cc`).
fn distribute_colspanned_cell_to_columns (
& mut self ,
colspan_cell_constraints : ColspanToDistribute ,
) {
let border_spacing = self . table . border_spacing ( ) . inline ;
let column_range = colspan_cell_constraints . range ( ) ;
let column_count = column_range . len ( ) ;
let total_border_spacing =
border_spacing . scale_by ( ( colspan_cell_constraints . span - 1 ) as f32 ) ;
for row_index in 0 .. self . table . size . height {
let coords = TableSlotCoordinates ::new ( column_index , row_index ) ;
let resolved_coords = match self . table . resolve_first_cell_coords ( coords ) {
Some ( resolved_coords ) = > resolved_coords ,
None = > continue ,
} ;
let mut percent_columns_count = 0 ;
let mut columns_percent_sum = 0. ;
let mut columns_non_percent_max_inline_size_sum = Au ::zero ( ) ;
for column in self . columns [ column_range . clone ( ) ] . iter ( ) {
if let Some ( percentage ) = column . percentage {
percent_columns_count + = 1 ;
columns_percent_sum + = percentage . 0 ;
} else {
columns_non_percent_max_inline_size_sum + = column . content_sizes . max_content ;
}
}
let cell = match self . table . resolve_first_cell ( resolved_coords ) {
Some ( cell ) if cell . colspan < = n = > cell ,
Some ( cell ) = > {
next_span_n = next_span_n . min ( cell . colspan ) ;
let colspan_percentage = colspan_cell_constraints . percentage . unwrap_or_default ( ) ;
let surplus_percent = colspan_percentage . 0 - columns_percent_sum ;
if surplus_percent > 0. & & column_count > percent_columns_count {
for column in self . columns [ column_range . clone ( ) ] . iter_mut ( ) {
if column . percentage . is_some ( ) {
continue ;
} ,
_ = > continue ,
}
let ratio = if columns_non_percent_max_inline_size_sum . is_zero ( ) {
1. / ( ( column_count - percent_columns_count ) as f32 )
} else {
column . content_sizes . max_content . to_f32_px ( ) /
columns_non_percent_max_inline_size_sum . to_f32_px ( )
} ;
column . percentage = Some ( Percentage ( surplus_percent * ratio ) ) ;
}
}
let cell_measures =
& self . cell_measures [ resolved_coords . y ] [ resolved_coords . x ] . inline ;
let cell_inline_content_sizes = cell_measures . content_sizes ;
let columns_spanned = resolved_coords . x .. resolved_coords . x + cell . colspan ;
let baseline_content_sizes : ContentSizes = columns_spanned . clone ( ) . fold (
ContentSizes ::zero ( ) ,
| total : ContentSizes , spanned_column_index | {
total + self . columns [ spanned_column_index ] . content_sizes
} ,
let colspan_cell_min_size = ( colspan_cell_constraints . content_sizes . min_content -
total_border_spacing )
. max ( Au ::zero ( ) ) ;
let distributed_minimum = Self ::distribute_width_to_columns (
colspan_cell_min_size ,
& self . columns [ column_range . clone ( ) ] ,
) ;
let old_column_content_size = old_column . content_sizes ;
// > **min-content width of a column based on cells of span up to N (N > 1)**
// >
// > the largest of the min-content width of the column based on cells of span up to
// > N-1 and the contributions of the cells in the column whose colSpan is N, where
// > the contribution of a cell is the result of taking the following steps:
// >
// > 1. Define the baseline min-content width as the sum of the max-content
// > widths based on cells of span up to N-1 of all columns that the cell spans.
//
// Note: This definition is likely a typo, so we use the sum of the min-content
// widths here instead.
let baseline_min_content_width = baseline_content_sizes . min_content ;
let baseline_max_content_width = baseline_content_sizes . max_content ;
// > 2. Define the baseline border spacing as the sum of the horizontal
// > border-spacing for any columns spanned by the cell, other than the one in
// > which the cell originates.
let baseline_border_spacing = border_spacing . inline * ( n as i32 - 1 ) ;
// > 3. The contribution of the cell is the sum of:
// > a. the min-content width of the column based on cells of span up to N-1
let a = old_column_content_size . min_content ;
// > b. the product of:
// > - the ratio of:
// > - the max-content width of the column based on cells of span up
// > to N-1 of the column minus the min-content width of the
// > column based on cells of span up to N-1 of the column, to
// > - the baseline max-content width minus the baseline min-content
// > width
// > or zero if this ratio is undefined, and
// > - the outer min-content width of the cell minus the baseline
// > min-content width and the baseline border spacing, clamped to be
// > at least 0 and at most the difference between the baseline
// > max-content width and the baseline min-content width
let old_content_size_difference =
old_column_content_size . max_content - old_column_content_size . min_content ;
let baseline_difference = baseline_min_content_width - baseline_max_content_width ;
let mut b =
old_content_size_difference . to_f32_px ( ) / baseline_difference . to_f32_px ( ) ;
if ! b . is_finite ( ) {
b = 0.0 ;
{
let column_span = & mut self . columns [ colspan_cell_constraints . range ( ) ] ;
for ( column , minimum_size ) in column_span . iter_mut ( ) . zip ( distributed_minimum ) {
column . content_sizes . min_content . max_assign ( minimum_size ) ;
}
}
let b = ( cell_inline_content_sizes . min_content -
baseline_content_sizes . min_content -
baseline_border_spacing )
. clamp_between_extremums ( Au ::zero ( ) , Some ( baseline_difference ) )
. scale_by ( b ) ;
// > c. the product of:
// > - the ratio of the max-content width based on cells of span up to
// > N-1 of the column to the baseline max-content width
// > - the outer min-content width of the cell minus the baseline
// > max-content width and baseline border spacing, or 0 if this is
// > negative
let c = ( cell_inline_content_sizes . min_content -
baseline_content_sizes . max_content -
baseline_border_spacing )
. min ( Au ::zero ( ) )
. scale_by (
old_column_content_size . max_content . to_f32_px ( ) /
baseline_content_sizes . max_content . to_f32_px ( ) ,
let colspan_cell_max_size = ( colspan_cell_constraints . content_sizes . max_content -
total_border_spacing )
. max ( Au ::zero ( ) ) ;
let distributed_maximum = Self ::distribute_width_to_columns (
colspan_cell_max_size ,
& self . columns [ colspan_cell_constraints . range ( ) ] ,
) ;
let new_column_min_content_width = a + b + c ;
// > **max-content width of a column based on cells of span up to N (N > 1)**
// >
// > The largest of the max-content width based on cells of span up to N-1 and the
// > contributions of the cells in the column whose colSpan is N, where the
// > contribution of a cell is the result of taking the following steps:
// > 1. Define the baseline max-content width as the sum of the max-content
// > widths based on cells of span up to N-1 of all columns that the cell spans.
//
// This is calculated above for the min-content width.
// > 2. Define the baseline border spacing as the sum of the horizontal
// > border-spacing for any columns spanned by the cell, other than the one in
// > which the cell originates.
//
// This is calculated above for min-content width.
// > 3. The contribution of the cell is the sum of:
// > a. the max-content width of the column based on cells of span up to N-1
let a = old_column_content_size . max_content ;
// > b. the product of:
// > 1. the ratio of the max-content width based on cells of span up to
// > N-1 of the column to the baseline max-content width
let b_1 = old_column_content_size . max_content . to_f32_px ( ) /
baseline_content_sizes . max_content . to_f32_px ( ) ;
// > 2. the outer max-content width of the cell minus the baseline
// > max-content width and the baseline border spacing, or 0 if this
// > is negative
let b_2 = ( cell_inline_content_sizes . max_content -
baseline_content_sizes . max_content -
baseline_border_spacing )
. min ( Au ::zero ( ) ) ;
let b = b_2 . scale_by ( b_1 ) ;
let new_column_max_content_width = a + b + c ;
// The computed values for the column are always the largest of any processed cell
// in that column.
new_column_content_sizes . max_assign ( ContentSizes {
min_content : new_column_min_content_width ,
max_content : new_column_max_content_width ,
} ) ;
// > If the intrinsic percentage width of a column based on cells of span up to N-1 is
// > greater than 0%, then the intrinsic percentage width of the column based on cells
// > of span up to N is the same as the intrinsic percentage width of the column based
// > on cells of span up to N-1.
// > Otherwise, it is the largest of the contributions of the cells in the column
// > whose colSpan is N, where the contribution of a cell is the result of taking
// > the following steps:
if old_column . percentage . 0 < = 0. & & cell_measures . percentage . 0 ! = 0. {
// > 1. Start with the percentage contribution of the cell.
// > 2. Subtract the intrinsic percentage width of the column based on cells
// > of span up to N-1 of all columns that the cell spans. If this gives a
// > negative result, change it to 0%.
let mut spanned_columns_with_zero = 0 ;
let other_column_percentages_sum =
( columns_spanned ) . fold ( 0. , | sum , spanned_column_index | {
let spanned_column_percentage =
self . columns [ spanned_column_index ] . percentage ;
if spanned_column_percentage . 0 = = 0. {
spanned_columns_with_zero + = 1 ;
}
sum + spanned_column_percentage . 0
} ) ;
let step_2 = ( cell_measures . percentage -
Percentage ( other_column_percentages_sum ) )
. clamp_to_non_negative ( ) ;
// > Multiply by the ratio of:
// > 1. the column’ s non-spanning max-content width to
// > 2. the sum of the non-spanning max-content widths of all columns
// > spanned by the cell that have an intrinsic percentage width of the column
// > based on cells of span up to N-1 equal to 0%.
// > However, if this ratio is undefined because the denominator is zero,
// > instead use the 1 divided by the number of columns spanned by the cell
// > that have an intrinsic percentage width of the column based on cells of
// > span up to N-1 equal to zero.
let step_3 = step_2 . 0 * ( 1.0 / spanned_columns_with_zero as f32 ) ;
new_column_intrinsic_percentage_width =
Percentage ( new_column_intrinsic_percentage_width . 0. max ( step_3 ) ) ;
{
let column_span = & mut self . columns [ colspan_cell_constraints . range ( ) ] ;
for ( column , maximum_size ) in column_span . iter_mut ( ) . zip ( distributed_maximum ) {
column
. content_sizes
. max_content
. max_assign ( maximum_size . max ( column . content_sizes . min_content ) ) ;
}
}
let mut new_column = old_column . clone ( ) ;
new_column . content_sizes = new_column_content_sizes ;
new_column . percentage = new_column_intrinsic_percentage_width ;
new_columns . push ( new_column ) ;
}
( next_span_n , new_columns )
}
/// Compute the GRIDMIN and GRIDMAX.
@ -819,14 +725,10 @@ impl<'a> TableLayout<'a> {
/// Distribute width to columns, performing step 2.4 of table layout from
/// <https://drafts.csswg.org/css-tables/#table-layout-algorithm>.
fn distribute_width_to_columns (
& self ,
target_inline_size : Au ,
columns : & [ ColumnLayout ] ,
) -> Vec < Au > {
fn distribute_width_to_columns ( target_inline_size : Au , columns : & [ ColumnLayout ] ) -> Vec < Au > {
// No need to do anything if there is no column.
// Note that tables without rows may still have columns.
if self . table . size . width . is_zero ( ) {
if columns . is_empty ( ) {
return Vec ::new ( ) ;
}
@ -872,8 +774,8 @@ impl<'a> TableLayout<'a> {
min_content_percentage_sizing_guess ,
min_content_specified_sizing_guess ,
max_content_sizing_guess ,
) = if ! column . percentage . is_zero ( ) {
let resolved = target_inline_size . scale_by ( column. percentage. 0 ) ;
) = if let Some ( percentage ) = column . percentage {
let resolved = target_inline_size . scale_by ( percentage. 0 ) ;
let percent_guess = min_content_width . max ( resolved ) ;
( percent_guess , percent_guess , percent_guess )
} else if constrained {
@ -901,9 +803,11 @@ impl<'a> TableLayout<'a> {
let max_content_sizing_sum = sum ( & max_content_sizing_guesses ) ;
if target_inline_size > = max_content_sizing_sum {
self . distribute_extra_width_to_columns (
Self ::distribute_extra_width_to_columns (
columns ,
& mut max_content_sizing_guesses ,
max_content_sizing_sum ,
target_inline_size ,
) ;
return max_content_sizing_guesses ;
}
@ -999,26 +903,29 @@ impl<'a> TableLayout<'a> {
/// This is an implementation of *Distributing excess width to columns* from
/// <https://drafts.csswg.org/css-tables/#distributing-width-to-columns>.
fn distribute_extra_width_to_columns ( & self , column_sizes : & mut [ Au ] , column_sizes_sum : Au ) {
let all_columns = 0 .. self . table . size . width ;
let extra_inline_size = self . assignable_width - column_sizes_sum ;
fn distribute_extra_width_to_columns (
columns : & [ ColumnLayout ] ,
column_sizes : & mut [ Au ] ,
column_sizes_sum : Au ,
assignable_width : Au ,
) {
let all_columns = 0 .. columns . len ( ) ;
let extra_inline_size = assignable_width - column_sizes_sum ;
let has_originating_cells =
| column_index : & usize | self . columns [ * column_index ] . has_originating_cells ;
let is_constrained = | column_index : & usize | self . columns [ * column_index ] . constrained ;
| column_index : & usize | columns [ * column_index ] . has_originating_cells ;
let is_constrained = | column_index : & usize | columns [ * column_index ] . constrained ;
let is_unconstrained = | column_index : & usize | ! is_constrained ( column_index ) ;
let has_percent_greater_than_zero =
| column_index : & usize | self . columns [ * column_index ] . percentage . 0 > 0. ;
let has_percent_zero = | column_index : & usize | ! has_percent_greater_than_zero ( column_index ) ;
let has_max_content = | column_index : & usize | {
! self . columns [ * column_index ]
. content_sizes
. max_content
. is_zero ( )
let has_percent_greater_than_zero = | column_index : & usize | {
columns [ * column_index ]
. percentage
. is_some_and ( | percentage | percentage . 0 > 0. )
} ;
let has_percent_zero = | column_index : & usize | ! has_percent_greater_than_zero ( column_index ) ;
let has_max_content =
| column_index : & usize | ! columns [ * column_index ] . content_sizes . max_content . is_zero ( ) ;
let max_content_sum =
| column_index : usize | self . columns [ column_index ] . content_sizes . max_content ;
let max_content_sum = | column_index : usize | columns [ column_index ] . content_sizes . max_content ;
// > If there are non-constrained columns that have originating cells with intrinsic
// > percentage width of 0% and with nonzero max-content width (aka the columns allowed to
@ -1038,10 +945,7 @@ impl<'a> TableLayout<'a> {
if total_max_content_width ! = Au ::zero ( ) {
for column_index in unconstrained_max_content_columns {
column_sizes [ column_index ] + = extra_inline_size . scale_by (
self . columns [ column_index ]
. content_sizes
. max_content
. to_f32_px ( ) /
columns [ column_index ] . content_sizes . max_content . to_f32_px ( ) /
total_max_content_width . to_f32_px ( ) ,
) ;
}
@ -1086,10 +990,7 @@ impl<'a> TableLayout<'a> {
if total_max_content_width ! = Au ::zero ( ) {
for column_index in constrained_max_content_columns {
column_sizes [ column_index ] + = extra_inline_size . scale_by (
self . columns [ column_index ]
. content_sizes
. max_content
. to_f32_px ( ) /
columns [ column_index ] . content_sizes . max_content . to_f32_px ( ) /
total_max_content_width . to_f32_px ( ) ,
) ;
}
@ -1104,12 +1005,13 @@ impl<'a> TableLayout<'a> {
let columns_with_percentage = all_columns . clone ( ) . filter ( has_percent_greater_than_zero ) ;
let total_percent = columns_with_percentage
. clone ( )
. map ( | column_index | self . columns [ column_index ] . percentage . 0 )
. map ( | column_index | columns [ column_index ] . percentage . unwrap_or_default ( ) . 0 )
. sum ::< f32 > ( ) ;
if total_percent > 0. {
for column_index in columns_with_percentage {
column_sizes [ column_index ] + = extra_inline_size
. scale_by ( self . columns [ column_index ] . percentage . 0 / total_percent ) ;
let column_percentage = columns [ column_index ] . percentage . unwrap_or_default ( ) ;
column_sizes [ column_index ] + =
extra_inline_size . scale_by ( column_percentage . 0 / total_percent ) ;
}
return ;
}
@ -1130,8 +1032,7 @@ impl<'a> TableLayout<'a> {
// > Otherwise, the distributed widths of all columns are increased by equal amounts so the
// total increase adds to the excess width.
let extra_space_for_all_columns =
extra_inline_size . scale_by ( 1.0 / self . table . size . width as f32 ) ;
let extra_space_for_all_columns = extra_inline_size . scale_by ( 1.0 / columns . len ( ) as f32 ) ;
for guess in column_sizes . iter_mut ( ) {
* guess + = extra_space_for_all_columns ;
}
@ -1320,11 +1221,12 @@ impl<'a> TableLayout<'a> {
. get_row_measure_for_row_at_index ( writing_mode , row_index ) ;
row_sizes [ row_index ] . max_assign ( row_measure . content_sizes . min_content ) ;
let mut percentage = row_measure . percentage . 0 ;
let mut percentage = row_measure . percentage . unwrap_or_default ( ) . 0 ;
for column_index in 0 .. self . table . size . width {
let cell_percentage = self . cell_measures [ row_index ] [ column_index ]
. block
. percentage
. unwrap_or_default ( )
. 0 ;
percentage = percentage . max ( cell_percentage ) ;
@ -1821,7 +1723,7 @@ impl<'a> TableLayout<'a> {
containing_block_for_children : & ContainingBlock ,
) -> BoxFragment {
self . distributed_column_widths =
self. distribute_width_to_columns ( self . assignable_width , & self . columns ) ;
Self:: distribute_width_to_columns ( self . assignable_width , & self . columns ) ;
self . layout_cells_in_row (
layout_context ,
containing_block_for_children ,
@ -2650,8 +2552,13 @@ impl Table {
None = > return CellOrTrackMeasure ::zero ( ) ,
} ;
let ( size , min_size , max_size , _ , percentage_contribution ) =
get_outer_sizes_for_measurement ( & column . style , writing_mode , & LogicalVec2 ::zero ( ) ) ;
let CellOrColumnOuterSizes {
preferred : preferred_size ,
min : min_size ,
max : max_size ,
percentage : percentage_size ,
..
} = CellOrColumnOuterSizes ::new ( & column . style , writing_mode , & Default ::default ( ) ) ;
CellOrTrackMeasure {
content_sizes : ContentSizes {
@ -2663,9 +2570,11 @@ impl Table {
// > The outer max-content width of a table-column or table-column-group is
// > max(min-width, min(max-width, width)).
// This matches Gecko, but Blink and WebKit ignore max_size.
max_content : min_size . inline . max ( max_size . inline . min ( size . inline ) ) ,
max_content : preferred_size
. inline
. clamp_between_extremums ( min_size . inline , max_size . inline ) ,
} ,
percentage : percentage_contribution . inline ,
percentage : percentage_ size . inline ,
}
}
@ -2910,7 +2819,7 @@ impl TableSlotCell {
fn get_size_percentage_contribution (
size : & LogicalVec2 < Size < ComputedLengthPercentage > > ,
max_size : & LogicalVec2 < Size < ComputedLengthPercentage > > ,
) -> LogicalVec2 < Percentage > {
) -> LogicalVec2 < Option < Percentage > > {
// From <https://drafts.csswg.org/css-tables/#percentage-contribution>
// > The percentage contribution of a table cell, column, or column group is defined
// > in terms of the computed values of width and max-width that have computed values
@ -2922,13 +2831,11 @@ fn get_size_percentage_contribution(
| size : & Size < ComputedLengthPercentage > , max_size : & Size < ComputedLengthPercentage > | {
let size_percentage = size
. to_numeric ( )
. and_then ( | length_percentage | length_percentage . to_percentage ( ) )
. unwrap_or ( Percentage ( 0. ) ) ;
. and_then ( | length_percentage | length_percentage . to_percentage ( ) ) ;
let max_size_percentage = max_size
. to_numeric ( )
. and_then ( | length_percentage | length_percentage . to_percentage ( ) )
. unwrap_or ( Percentage ( f32 ::INFINITY ) ) ;
Percentage ( size_percentage . 0. min ( max_size_percentage . 0 ) )
. and_then ( | length_percentage | length_percentage . to_percentage ( ) ) ;
max_two_optional_percentages ( size_percentage , max_size_percentage )
} ;
LogicalVec2 {
@ -2937,17 +2844,20 @@ fn get_size_percentage_contribution(
}
}
fn get_outer_sizes_for_measurement (
struct CellOrColumnOuterSizes {
min : LogicalVec2 < Au > ,
preferred : LogicalVec2 < Au > ,
max : LogicalVec2 < Option < Au > > ,
percentage : LogicalVec2 < Option < Percentage > > ,
inline_preferred_size_is_auto : bool ,
}
impl CellOrColumnOuterSizes {
fn new (
style : & Arc < ComputedValues > ,
writing_mode : WritingMode ,
padding_border_sums : & LogicalVec2 < Au > ,
) -> (
LogicalVec2 < Au > ,
LogicalVec2 < Au > ,
LogicalVec2 < Au > ,
bool ,
LogicalVec2 < Percentage > ,
) {
) -> Self {
let box_sizing = style . get_position ( ) . box_sizing ;
let outer_size = | size : LogicalVec2 < Au > | match box_sizing {
BoxSizing ::ContentBox = > size + * padding_border_sums ,
@ -2956,6 +2866,18 @@ fn get_outer_sizes_for_measurement(
block : size . block . max ( padding_border_sums . block ) ,
} ,
} ;
let outer_size_for_max = | size : LogicalVec2 < Option < Au > > | match box_sizing {
BoxSizing ::ContentBox = > size . map_inline_and_block_axes (
| inline | inline . map ( | inline | inline + padding_border_sums . inline ) ,
| block | block . map ( | block | block + padding_border_sums . block ) ,
) ,
BoxSizing ::BorderBox = > size . map_inline_and_block_axes (
| inline | inline . map ( | inline | inline . max ( padding_border_sums . inline ) ) ,
| block | block . map ( | block | block . max ( padding_border_sums . block ) ) ,
) ,
} ;
let get_size_for_axis = | size : & Size < ComputedLengthPercentage > | {
// Note that measures treat all size values other than <length>
// as the initial value of the property.
@ -2967,13 +2889,15 @@ fn get_outer_sizes_for_measurement(
let size = style . box_size ( writing_mode ) ;
let min_size = style . min_box_size ( writing_mode ) ;
let max_size = style . max_box_size ( writing_mode ) ;
(
outer_size ( size . map ( | v | get_size_for_axis ( v ) . unwrap_or_else ( Au ::zero ) ) ) ,
outer_size ( min_size . map ( | v | get_size_for_axis ( v ) . unwrap_or_else ( Au ::zero ) ) ) ,
outer_size ( max_size . map ( | v | get_size_for_axis ( v ) . unwrap_or ( MAX_AU ) ) ) ,
! size . inline . is_numeric ( ) ,
get_size_percentage_contribution ( & size , & max_size ) ,
)
Self {
min : outer_size ( min_size . map ( | v | get_size_for_axis ( v ) . unwrap_or_default ( ) ) ) ,
preferred : outer_size ( size . map ( | v | get_size_for_axis ( v ) . unwrap_or_default ( ) ) ) ,
max : outer_size_for_max ( max_size . map ( get_size_for_axis ) ) ,
inline_preferred_size_is_auto : ! size . inline . is_numeric ( ) ,
percentage : get_size_percentage_contribution ( & size , & max_size ) ,
}
}
}
struct RowspanToDistribute < ' a > {
@ -2991,3 +2915,28 @@ impl RowspanToDistribute<'_> {
other . coordinates . y > self . coordinates . y & & other . range ( ) . end < self . range ( ) . end
}
}
/// The inline size constraints provided by a cell that span multiple columns (`colspan` > 1).
/// These constraints are distributed to the individual columns that make up this cell's span.
#[ derive(Debug) ]
struct ColspanToDistribute {
starting_column : usize ,
span : usize ,
content_sizes : ContentSizes ,
percentage : Option < Percentage > ,
}
impl ColspanToDistribute {
/// A comparison function to sort the colspan cell constraints primarily by their span
/// width and secondarily by their starting column. This is not an implementation of
/// `PartialOrd` because we want to return [`Ordering::Equal`] even if `self != other`.
fn comparison_for_sort ( a : & Self , b : & Self ) -> Ordering {
a . span
. cmp ( & b . span )
. then_with ( | | b . starting_column . cmp ( & b . starting_column ) )
}
fn range ( & self ) -> Range < usize > {
self . starting_column .. self . starting_column + self . span
}
}