Blame servo/components/layout/table.rs

Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
//! CSS table formatting contexts.
Packit f0b94e
Packit f0b94e
#![deny(unsafe_code)]
Packit f0b94e
Packit f0b94e
use app_units::Au;
Packit f0b94e
use block::{BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer};
Packit f0b94e
use block::{ISizeConstraintInput, ISizeConstraintSolution};
Packit f0b94e
use context::LayoutContext;
Packit f0b94e
use display_list::{BlockFlowDisplayListBuilding, BorderPaintingMode};
Packit f0b94e
use display_list::{DisplayListBuildState, StackingContextCollectionFlags, StackingContextCollectionState};
Packit f0b94e
use euclid::Point2D;
Packit f0b94e
use flow::{BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, GetBaseFlow, OpaqueFlow};
Packit f0b94e
use flow_list::{FlowListIterator, MutFlowListIterator};
Packit f0b94e
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
Packit f0b94e
use gfx_traits::print_tree::PrintTree;
Packit f0b94e
use layout_debug;
Packit f0b94e
use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto};
Packit f0b94e
use std::{cmp, fmt};
Packit f0b94e
use style::computed_values::{border_collapse, border_spacing, table_layout};
Packit f0b94e
use style::context::SharedStyleContext;
Packit f0b94e
use style::logical_geometry::LogicalSize;
Packit f0b94e
use style::properties::ComputedValues;
Packit f0b94e
use style::properties::style_structs::Background;
Packit f0b94e
use style::servo::restyle_damage::ServoRestyleDamage;
Packit f0b94e
use style::values::CSSFloat;
Packit f0b94e
use style::values::computed::LengthOrPercentageOrAuto;
Packit f0b94e
use table_cell::TableCellFlow;
Packit f0b94e
use table_row::{self, CellIntrinsicInlineSize, CollapsedBorder, CollapsedBorderProvenance};
Packit f0b94e
use table_row::{TableRowFlow, TableRowSizeData};
Packit f0b94e
use table_wrapper::TableLayout;
Packit f0b94e
Packit f0b94e
#[allow(unsafe_code)]
Packit f0b94e
unsafe impl ::flow::HasBaseFlow for TableFlow {}
Packit f0b94e
Packit f0b94e
/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
Packit f0b94e
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
Packit f0b94e
/// not table fragment per CSS 2.1 ยง 10.5.
Packit f0b94e
#[derive(Serialize)]
Packit f0b94e
#[repr(C)]
Packit f0b94e
pub struct TableFlow {
Packit f0b94e
    pub block_flow: BlockFlow,
Packit f0b94e
Packit f0b94e
    /// Information about the intrinsic inline-sizes of each column, computed bottom-up during
Packit f0b94e
    /// intrinsic inline-size bubbling.
Packit f0b94e
    pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
Packit f0b94e
Packit f0b94e
    /// Information about the actual inline sizes of each column, computed top-down during actual
Packit f0b94e
    /// inline-size bubbling.
Packit f0b94e
    pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
Packit f0b94e
Packit f0b94e
    /// The final width of the borders in the inline direction for each cell, computed by the
Packit f0b94e
    /// entire table and pushed down into each row during inline size computation.
Packit f0b94e
    pub collapsed_inline_direction_border_widths_for_table: Vec<Au>,
Packit f0b94e
Packit f0b94e
    /// The final width of the borders in the block direction for each cell, computed by the
Packit f0b94e
    /// entire table and pushed down into each row during inline size computation.
Packit f0b94e
    pub collapsed_block_direction_border_widths_for_table: Vec<Au>,
Packit f0b94e
Packit f0b94e
    /// Table-layout property
Packit f0b94e
    pub table_layout: TableLayout,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl TableFlow {
Packit f0b94e
    pub fn from_fragment(fragment: Fragment) -> TableFlow {
Packit f0b94e
        let mut block_flow = BlockFlow::from_fragment(fragment);
Packit f0b94e
        let table_layout =
Packit f0b94e
            if block_flow.fragment().style().get_table().table_layout == table_layout::T::Fixed {
Packit f0b94e
                TableLayout::Fixed
Packit f0b94e
            } else {
Packit f0b94e
                TableLayout::Auto
Packit f0b94e
            };
Packit f0b94e
        TableFlow {
Packit f0b94e
            block_flow: block_flow,
Packit f0b94e
            column_intrinsic_inline_sizes: Vec::new(),
Packit f0b94e
            column_computed_inline_sizes: Vec::new(),
Packit f0b94e
            collapsed_inline_direction_border_widths_for_table: Vec::new(),
Packit f0b94e
            collapsed_block_direction_border_widths_for_table: Vec::new(),
Packit f0b94e
            table_layout: table_layout
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has
Packit f0b94e
    /// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline
Packit f0b94e
    /// sizes.
Packit f0b94e
    fn update_automatic_column_inline_sizes(
Packit f0b94e
            parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
Packit f0b94e
            child_cell_inline_sizes: &[CellIntrinsicInlineSize],
Packit f0b94e
            surrounding_size: Au)
Packit f0b94e
            -> IntrinsicISizes {
Packit f0b94e
        let mut total_inline_sizes = IntrinsicISizes {
Packit f0b94e
            minimum_inline_size: surrounding_size,
Packit f0b94e
            preferred_inline_size: surrounding_size,
Packit f0b94e
        };
Packit f0b94e
        let mut column_index = 0;
Packit f0b94e
        let mut incoming_rowspan = vec![];
Packit f0b94e
Packit f0b94e
        for child_cell_inline_size in child_cell_inline_sizes {
Packit f0b94e
            // Skip any column occupied by a cell from a previous row.
Packit f0b94e
            while column_index < incoming_rowspan.len() && incoming_rowspan[column_index] != 1 {
Packit f0b94e
                if incoming_rowspan[column_index] > 1 {
Packit f0b94e
                    incoming_rowspan[column_index] -= 1;
Packit f0b94e
                }
Packit f0b94e
                column_index += 1;
Packit f0b94e
            }
Packit f0b94e
            for _ in 0..child_cell_inline_size.column_span {
Packit f0b94e
                if column_index < parent_inline_sizes.len() {
Packit f0b94e
                    // We already have some intrinsic size information for this column. Merge it in
Packit f0b94e
                    // according to the rules specified in INTRINSIC ยง 4.
Packit f0b94e
                    let parent_sizes = &mut parent_inline_sizes[column_index];
Packit f0b94e
                    if child_cell_inline_size.column_span > 1 {
Packit f0b94e
                        // TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC ยง
Packit f0b94e
                        // 4. For now we make this column contribute no width.
Packit f0b94e
                    } else {
Packit f0b94e
                        let column_size = &child_cell_inline_size.column_size;
Packit f0b94e
                        *parent_sizes = ColumnIntrinsicInlineSize {
Packit f0b94e
                            minimum_length: cmp::max(parent_sizes.minimum_length,
Packit f0b94e
                                                     column_size.minimum_length),
Packit f0b94e
                            percentage: parent_sizes.greatest_percentage(column_size),
Packit f0b94e
                            preferred: cmp::max(parent_sizes.preferred, column_size.preferred),
Packit f0b94e
                            constrained: parent_sizes.constrained || column_size.constrained,
Packit f0b94e
                        }
Packit f0b94e
                    }
Packit f0b94e
                } else {
Packit f0b94e
                    // We discovered a new column. Initialize its data.
Packit f0b94e
                    debug_assert_eq!(column_index, parent_inline_sizes.len());
Packit f0b94e
                    if child_cell_inline_size.column_span > 1 {
Packit f0b94e
                        // TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC ยง
Packit f0b94e
                        // 4. For now we make this column contribute no width.
Packit f0b94e
                        parent_inline_sizes.push(ColumnIntrinsicInlineSize::new())
Packit f0b94e
                    } else {
Packit f0b94e
                        parent_inline_sizes.push(child_cell_inline_size.column_size)
Packit f0b94e
                    }
Packit f0b94e
                }
Packit f0b94e
Packit f0b94e
                total_inline_sizes.minimum_inline_size +=
Packit f0b94e
                    parent_inline_sizes[column_index].minimum_length;
Packit f0b94e
                total_inline_sizes.preferred_inline_size +=
Packit f0b94e
                    parent_inline_sizes[column_index].preferred;
Packit f0b94e
Packit f0b94e
                // If this cell spans later rows, record its rowspan.
Packit f0b94e
                if child_cell_inline_size.row_span > 1 {
Packit f0b94e
                    if incoming_rowspan.len() < column_index + 1 {
Packit f0b94e
                        incoming_rowspan.resize(column_index + 1, 0);
Packit f0b94e
                    }
Packit f0b94e
                    incoming_rowspan[column_index] = child_cell_inline_size.row_span;
Packit f0b94e
                }
Packit f0b94e
Packit f0b94e
                column_index += 1
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        total_inline_sizes
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Updates the minimum and preferred inline-size calculation for a single row. This is
Packit f0b94e
    /// factored out into a separate function because we process children of rowgroups too.
Packit f0b94e
    fn update_column_inline_sizes_for_row(row: &TableRowFlow,
Packit f0b94e
                                          column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
Packit f0b94e
                                          computation: &mut IntrinsicISizesContribution,
Packit f0b94e
                                          first_row: bool,
Packit f0b94e
                                          table_layout: TableLayout,
Packit f0b94e
                                          surrounding_inline_size: Au) {
Packit f0b94e
        // Read column inline-sizes from the table-row, and assign inline-size=0 for the columns
Packit f0b94e
        // not defined in the column group.
Packit f0b94e
        //
Packit f0b94e
        // FIXME: Need to read inline-sizes from either table-header-group OR the first table-row.
Packit f0b94e
        match table_layout {
Packit f0b94e
            TableLayout::Fixed => {
Packit f0b94e
                // Fixed table layout only looks at the first row.
Packit f0b94e
                //
Packit f0b94e
                // FIXME(pcwalton): This is really inefficient. We should stop after the first row!
Packit f0b94e
                if first_row {
Packit f0b94e
                    for cell_inline_size in &row.cell_intrinsic_inline_sizes {
Packit f0b94e
                        column_inline_sizes.push(cell_inline_size.column_size);
Packit f0b94e
                    }
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            TableLayout::Auto => {
Packit f0b94e
                computation.union_block(&TableFlow::update_automatic_column_inline_sizes(
Packit f0b94e
                    column_inline_sizes,
Packit f0b94e
                    &row.cell_intrinsic_inline_sizes,
Packit f0b94e
                    surrounding_inline_size))
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns the effective spacing per cell, taking the value of `border-collapse` into account.
Packit f0b94e
    pub fn spacing(&self) -> border_spacing::T {
Packit f0b94e
        let style = self.block_flow.fragment.style();
Packit f0b94e
        match style.get_inheritedtable().border_collapse {
Packit f0b94e
            border_collapse::T::Separate => style.get_inheritedtable().border_spacing,
Packit f0b94e
            border_collapse::T::Collapse => border_spacing::T::zero(),
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    pub fn total_horizontal_spacing(&self) -> Au {
Packit f0b94e
        let num_columns = self.column_intrinsic_inline_sizes.len();
Packit f0b94e
        if num_columns == 0 {
Packit f0b94e
            return Au(0);
Packit f0b94e
        }
Packit f0b94e
        self.spacing().horizontal() * (num_columns as i32 + 1)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn column_styles(&self) -> Vec<ColumnStyle> {
Packit f0b94e
        let mut styles = vec![];
Packit f0b94e
        for group in self.block_flow.base.child_iter()
Packit f0b94e
                       .filter(|kid| kid.is_table_colgroup()) {
Packit f0b94e
            // XXXManishearth these as_foo methods should return options
Packit f0b94e
            // so that we can filter_map
Packit f0b94e
            let group = group.as_table_colgroup();
Packit f0b94e
            let colgroup_style = group.fragment.as_ref()
Packit f0b94e
                                      .map(|f| f.style());
Packit f0b94e
Packit f0b94e
            // The colgroup's span attribute is only relevant when
Packit f0b94e
            // it has no children
Packit f0b94e
            // https://html.spec.whatwg.org/multipage/#forming-a-table
Packit f0b94e
            if group.cols.is_empty() {
Packit f0b94e
                let span = group.fragment.as_ref()
Packit f0b94e
                                .map(|f| f.column_span()).unwrap_or(1);
Packit f0b94e
                styles.push(ColumnStyle { span, colgroup_style, col_style: None });
Packit f0b94e
            } else {
Packit f0b94e
                for col in &group.cols {
Packit f0b94e
                    // XXXManishearth Arc-cloning colgroup_style is suboptimal
Packit f0b94e
                    styles.push(ColumnStyle {
Packit f0b94e
                        span: col.column_span(),
Packit f0b94e
                        colgroup_style: colgroup_style,
Packit f0b94e
                        col_style: Some(col.style()),
Packit f0b94e
                    })
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
        styles
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl Flow for TableFlow {
Packit f0b94e
    fn class(&self) -> FlowClass {
Packit f0b94e
        FlowClass::Table
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn as_mut_table(&mut self) -> &mut TableFlow {
Packit f0b94e
        self
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn as_table(&self) -> &TableFlow {
Packit f0b94e
        self
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn as_mut_block(&mut self) -> &mut BlockFlow {
Packit f0b94e
        &mut self.block_flow
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn as_block(&self) -> &BlockFlow {
Packit f0b94e
        &self.block_flow
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn mark_as_root(&mut self) {
Packit f0b94e
        self.block_flow.mark_as_root();
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// The specified column inline-sizes are set from column group and the first row for the fixed
Packit f0b94e
    /// table layout calculation.
Packit f0b94e
    /// The maximum min/pref inline-sizes of each column are set from the rows for the automatic
Packit f0b94e
    /// table layout calculation.
Packit f0b94e
    fn bubble_inline_sizes(&mut self) {
Packit f0b94e
        let _scope = layout_debug_scope!("table::bubble_inline_sizes {:x}",
Packit f0b94e
                                         self.block_flow.base.debug_id());
Packit f0b94e
Packit f0b94e
        // Get column inline sizes from colgroups
Packit f0b94e
        for kid in self.block_flow.base.child_iter_mut().filter(|kid| kid.is_table_colgroup()) {
Packit f0b94e
            for specified_inline_size in &kid.as_mut_table_colgroup().inline_sizes {
Packit f0b94e
                self.column_intrinsic_inline_sizes.push(ColumnIntrinsicInlineSize {
Packit f0b94e
                    minimum_length: match *specified_inline_size {
Packit f0b94e
                        LengthOrPercentageOrAuto::Auto |
Packit f0b94e
                        LengthOrPercentageOrAuto::Calc(_) |
Packit f0b94e
                        LengthOrPercentageOrAuto::Percentage(_) => Au(0),
Packit f0b94e
                        LengthOrPercentageOrAuto::Length(length) => Au::from(length),
Packit f0b94e
                    },
Packit f0b94e
                    percentage: match *specified_inline_size {
Packit f0b94e
                        LengthOrPercentageOrAuto::Auto |
Packit f0b94e
                        LengthOrPercentageOrAuto::Calc(_) |
Packit f0b94e
                        LengthOrPercentageOrAuto::Length(_) => 0.0,
Packit f0b94e
                        LengthOrPercentageOrAuto::Percentage(percentage) => percentage.0,
Packit f0b94e
                    },
Packit f0b94e
                    preferred: Au(0),
Packit f0b94e
                    constrained: false,
Packit f0b94e
                })
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        self.collapsed_inline_direction_border_widths_for_table = Vec::new();
Packit f0b94e
        self.collapsed_block_direction_border_widths_for_table = vec![Au(0)];
Packit f0b94e
Packit f0b94e
        let collapsing_borders = self.block_flow
Packit f0b94e
                                     .fragment
Packit f0b94e
                                     .style
Packit f0b94e
                                     .get_inheritedtable()
Packit f0b94e
                                     .border_collapse == border_collapse::T::Collapse;
Packit f0b94e
        let table_inline_collapsed_borders = if collapsing_borders {
Packit f0b94e
            Some(TableInlineCollapsedBorders {
Packit f0b94e
                start: CollapsedBorder::inline_start(&*self.block_flow.fragment.style,
Packit f0b94e
                                                     CollapsedBorderProvenance::FromTable),
Packit f0b94e
                end: CollapsedBorder::inline_end(&*self.block_flow.fragment.style,
Packit f0b94e
                                                 CollapsedBorderProvenance::FromTable),
Packit f0b94e
            })
Packit f0b94e
        } else {
Packit f0b94e
            None
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        let mut computation = IntrinsicISizesContribution::new();
Packit f0b94e
        let mut previous_collapsed_block_end_borders =
Packit f0b94e
            PreviousBlockCollapsedBorders::FromTable(CollapsedBorder::block_start(
Packit f0b94e
                    &*self.block_flow.fragment.style,
Packit f0b94e
                    CollapsedBorderProvenance::FromTable));
Packit f0b94e
        let mut first_row = true;
Packit f0b94e
        let (border_padding, _) = self.block_flow.fragment.surrounding_intrinsic_inline_size();
Packit f0b94e
Packit f0b94e
        {
Packit f0b94e
            let mut iterator = TableRowIterator::new(&mut self.block_flow.base).peekable();
Packit f0b94e
            while let Some(row) = iterator.next() {
Packit f0b94e
                TableFlow::update_column_inline_sizes_for_row(
Packit f0b94e
                        row,
Packit f0b94e
                        &mut self.column_intrinsic_inline_sizes,
Packit f0b94e
                        &mut computation,
Packit f0b94e
                        first_row,
Packit f0b94e
                        self.table_layout,
Packit f0b94e
                        border_padding);
Packit f0b94e
                if collapsing_borders {
Packit f0b94e
                    let next_index_and_sibling = iterator.peek();
Packit f0b94e
                    let next_collapsed_borders_in_block_direction =
Packit f0b94e
                        match next_index_and_sibling {
Packit f0b94e
                            Some(next_sibling) => {
Packit f0b94e
                                NextBlockCollapsedBorders::FromNextRow(
Packit f0b94e
                                    &next_sibling.as_table_row()
Packit f0b94e
                                                 .preliminary_collapsed_borders
Packit f0b94e
                                                 .block_start)
Packit f0b94e
                            }
Packit f0b94e
                            None => {
Packit f0b94e
                                NextBlockCollapsedBorders::FromTable(
Packit f0b94e
                                    CollapsedBorder::block_end(&*self.block_flow.fragment.style,
Packit f0b94e
                                                               CollapsedBorderProvenance::FromTable))
Packit f0b94e
                            }
Packit f0b94e
                        };
Packit f0b94e
                    perform_border_collapse_for_row(row,
Packit f0b94e
                        table_inline_collapsed_borders.as_ref().unwrap(),
Packit f0b94e
                        previous_collapsed_block_end_borders,
Packit f0b94e
                        next_collapsed_borders_in_block_direction,
Packit f0b94e
                        &mut self.collapsed_inline_direction_border_widths_for_table,
Packit f0b94e
                        &mut self.collapsed_block_direction_border_widths_for_table);
Packit f0b94e
                    previous_collapsed_block_end_borders =
Packit f0b94e
                        PreviousBlockCollapsedBorders::FromPreviousRow(
Packit f0b94e
                            row.final_collapsed_borders.block_end.clone());
Packit f0b94e
                }
Packit f0b94e
                first_row = false
Packit f0b94e
            };
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let total_horizontal_spacing = self.total_horizontal_spacing();
Packit f0b94e
        let mut style_specified_intrinsic_inline_size =
Packit f0b94e
            self.block_flow
Packit f0b94e
                .fragment
Packit f0b94e
                .style_specified_intrinsic_inline_size()
Packit f0b94e
                .finish();
Packit f0b94e
        style_specified_intrinsic_inline_size.minimum_inline_size -= total_horizontal_spacing;
Packit f0b94e
        style_specified_intrinsic_inline_size.preferred_inline_size -= total_horizontal_spacing;
Packit f0b94e
        computation.union_block(&style_specified_intrinsic_inline_size);
Packit f0b94e
        computation.surrounding_size += total_horizontal_spacing;
Packit f0b94e
Packit f0b94e
        self.block_flow.base.intrinsic_inline_sizes = computation.finish()
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
Packit f0b94e
    /// When called on this context, the context has had its inline-size set by the parent context.
Packit f0b94e
    fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
Packit f0b94e
        let _scope = layout_debug_scope!("table::assign_inline_sizes {:x}",
Packit f0b94e
                                            self.block_flow.base.debug_id());
Packit f0b94e
        debug!("assign_inline_sizes({}): assigning inline_size for flow", "table");
Packit f0b94e
Packit f0b94e
        let shared_context = layout_context.shared_context();
Packit f0b94e
        // The position was set to the containing block by the flow's parent.
Packit f0b94e
        // FIXME: The code for distributing column widths should really be placed under table_wrapper.rs.
Packit f0b94e
        let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
Packit f0b94e
Packit f0b94e
        let mut constrained_column_inline_sizes_indices = vec![];
Packit f0b94e
        let mut unspecified_inline_sizes_indices = vec![];
Packit f0b94e
        for (idx, column_inline_size) in self.column_intrinsic_inline_sizes.iter().enumerate() {
Packit f0b94e
            if column_inline_size.constrained {
Packit f0b94e
                constrained_column_inline_sizes_indices.push(idx);
Packit f0b94e
            } else if column_inline_size.percentage == 0.0 {
Packit f0b94e
                unspecified_inline_sizes_indices.push(idx);
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let inline_size_computer = InternalTable;
Packit f0b94e
        inline_size_computer.compute_used_inline_size(&mut self.block_flow,
Packit f0b94e
                                                      shared_context,
Packit f0b94e
                                                      containing_block_inline_size);
Packit f0b94e
Packit f0b94e
        let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
Packit f0b94e
        let inline_end_content_edge = self.block_flow.fragment.border_padding.inline_end;
Packit f0b94e
        let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
Packit f0b94e
        let spacing_per_cell = self.spacing();
Packit f0b94e
        let total_horizontal_spacing = self.total_horizontal_spacing();
Packit f0b94e
        let content_inline_size = self.block_flow.fragment.border_box.size.inline -
Packit f0b94e
            padding_and_borders - total_horizontal_spacing;
Packit f0b94e
        let mut remaining_inline_size = content_inline_size;
Packit f0b94e
Packit f0b94e
        match self.table_layout {
Packit f0b94e
            TableLayout::Fixed => {
Packit f0b94e
                self.column_computed_inline_sizes.clear();
Packit f0b94e
Packit f0b94e
                // https://drafts.csswg.org/css2/tables.html#fixed-table-layout
Packit f0b94e
                for column_inline_size in &self.column_intrinsic_inline_sizes {
Packit f0b94e
                    if column_inline_size.constrained {
Packit f0b94e
                        self.column_computed_inline_sizes.push(ColumnComputedInlineSize {
Packit f0b94e
                            size: column_inline_size.minimum_length,
Packit f0b94e
                        });
Packit f0b94e
                        remaining_inline_size -= column_inline_size.minimum_length;
Packit f0b94e
                    } else if column_inline_size.percentage != 0.0 {
Packit f0b94e
                        let size = remaining_inline_size.scale_by(column_inline_size.percentage);
Packit f0b94e
                        self.column_computed_inline_sizes.push(ColumnComputedInlineSize {
Packit f0b94e
                            size: size,
Packit f0b94e
                        });
Packit f0b94e
                        remaining_inline_size -= size;
Packit f0b94e
                    } else {
Packit f0b94e
                        // Set the size to 0 now, distribute the remaining widths later
Packit f0b94e
                        self.column_computed_inline_sizes.push(ColumnComputedInlineSize {
Packit f0b94e
                            size: Au(0),
Packit f0b94e
                        });
Packit f0b94e
                    }
Packit f0b94e
                }
Packit f0b94e
Packit f0b94e
                // Distribute remaining content inline size
Packit f0b94e
                if unspecified_inline_sizes_indices.len() > 0 {
Packit f0b94e
                    for &index in &unspecified_inline_sizes_indices {
Packit f0b94e
                        self.column_computed_inline_sizes[index].size =
Packit f0b94e
                            remaining_inline_size.scale_by(1.0 / unspecified_inline_sizes_indices.len() as f32);
Packit f0b94e
                    }
Packit f0b94e
                } else {
Packit f0b94e
                    let total_minimum_size = self.column_intrinsic_inline_sizes
Packit f0b94e
                        .iter()
Packit f0b94e
                        .filter(|size| size.constrained)
Packit f0b94e
                        .map(|size| size.minimum_length.0 as f32)
Packit f0b94e
                        .sum::<f32>();
Packit f0b94e
Packit f0b94e
                    for &index in &constrained_column_inline_sizes_indices {
Packit f0b94e
                        self.column_computed_inline_sizes[index].size +=
Packit f0b94e
                            remaining_inline_size.scale_by(
Packit f0b94e
                                self.column_computed_inline_sizes[index].size.0 as f32 / total_minimum_size);
Packit f0b94e
                    }
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            _ => {
Packit f0b94e
                // The table wrapper already computed the inline-sizes and propagated them down
Packit f0b94e
                // to us.
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let column_computed_inline_sizes = &self.column_computed_inline_sizes;
Packit f0b94e
        let collapsed_inline_direction_border_widths_for_table =
Packit f0b94e
            &self.collapsed_inline_direction_border_widths_for_table;
Packit f0b94e
        let mut collapsed_block_direction_border_widths_for_table =
Packit f0b94e
            self.collapsed_block_direction_border_widths_for_table.iter().peekable();
Packit f0b94e
        let mut incoming_rowspan = vec![];
Packit f0b94e
        self.block_flow.propagate_assigned_inline_size_to_children(shared_context,
Packit f0b94e
                                                                   inline_start_content_edge,
Packit f0b94e
                                                                   inline_end_content_edge,
Packit f0b94e
                                                                   content_inline_size,
Packit f0b94e
                                                                   |child_flow,
Packit f0b94e
                                                                    _child_index,
Packit f0b94e
                                                                    _content_inline_size,
Packit f0b94e
                                                                    writing_mode,
Packit f0b94e
                                                                    _inline_start_margin_edge,
Packit f0b94e
                                                                    _inline_end_margin_edge| {
Packit f0b94e
            table_row::propagate_column_inline_sizes_to_child(
Packit f0b94e
                child_flow,
Packit f0b94e
                writing_mode,
Packit f0b94e
                column_computed_inline_sizes,
Packit f0b94e
                &spacing_per_cell,
Packit f0b94e
                &mut incoming_rowspan);
Packit f0b94e
            if child_flow.is_table_row() {
Packit f0b94e
                let child_table_row = child_flow.as_mut_table_row();
Packit f0b94e
                child_table_row.populate_collapsed_border_spacing(
Packit f0b94e
                    collapsed_inline_direction_border_widths_for_table,
Packit f0b94e
                    &mut collapsed_block_direction_border_widths_for_table);
Packit f0b94e
            } else if child_flow.is_table_rowgroup() {
Packit f0b94e
                let child_table_rowgroup = child_flow.as_mut_table_rowgroup();
Packit f0b94e
                child_table_rowgroup.populate_collapsed_border_spacing(
Packit f0b94e
                    collapsed_inline_direction_border_widths_for_table,
Packit f0b94e
                    &mut collapsed_block_direction_border_widths_for_table);
Packit f0b94e
            }
Packit f0b94e
        });
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn assign_block_size(&mut self, lc: &LayoutContext) {
Packit f0b94e
        debug!("assign_block_size: assigning block_size for table");
Packit f0b94e
        let vertical_spacing = self.spacing().vertical();
Packit f0b94e
        self.block_flow.assign_block_size_for_table_like_flow(vertical_spacing, lc)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
Packit f0b94e
        self.block_flow.compute_stacking_relative_position(layout_context)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
Packit f0b94e
        self.block_flow.generated_containing_block_size(flow)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
Packit f0b94e
        self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
Packit f0b94e
        self.block_flow.update_late_computed_block_position_if_necessary(block_position)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
Packit f0b94e
        let border_painting_mode = match self.block_flow
Packit f0b94e
                                             .fragment
Packit f0b94e
                                             .style
Packit f0b94e
                                             .get_inheritedtable()
Packit f0b94e
                                             .border_collapse {
Packit f0b94e
            border_collapse::T::Separate => BorderPaintingMode::Separate,
Packit f0b94e
            border_collapse::T::Collapse => BorderPaintingMode::Hidden,
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        self.block_flow.build_display_list_for_block(state, border_painting_mode);
Packit f0b94e
Packit f0b94e
        let iter = TableCellStyleIterator::new(&self);
Packit f0b94e
        for mut style in iter {
Packit f0b94e
            style.build_display_list(state)
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
Packit f0b94e
        // Stacking contexts are collected by the table wrapper.
Packit f0b94e
        self.block_flow.collect_stacking_contexts_for_block(state,
Packit f0b94e
            StackingContextCollectionFlags::NEVER_CREATES_STACKING_CONTEXT);
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
Packit f0b94e
        self.block_flow.repair_style(new_style)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn compute_overflow(&self) -> Overflow {
Packit f0b94e
        self.block_flow.compute_overflow()
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn iterate_through_fragment_border_boxes(&self,
Packit f0b94e
                                             iterator: &mut FragmentBorderBoxIterator,
Packit f0b94e
                                             level: i32,
Packit f0b94e
                                             stacking_context_position: &Point2D<Au>) {
Packit f0b94e
        self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
Packit f0b94e
        self.block_flow.mutate_fragments(mutator)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
Packit f0b94e
        self.block_flow.print_extra_flow_children(print_tree);
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
#[derive(Debug)]
Packit f0b94e
struct ColumnStyle<'table> {
Packit f0b94e
    span: u32,
Packit f0b94e
    colgroup_style: Option<&'table ComputedValues>,
Packit f0b94e
    col_style: Option<&'table ComputedValues>,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl fmt::Debug for TableFlow {
Packit f0b94e
    /// Outputs a debugging string describing this table flow.
Packit f0b94e
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Packit f0b94e
        write!(f, "TableFlow: {:?}", self.block_flow)
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Table, TableRowGroup, TableRow, TableCell types.
Packit f0b94e
/// Their inline-sizes are calculated in the same way and do not have margins.
Packit f0b94e
pub struct InternalTable;
Packit f0b94e
Packit f0b94e
impl ISizeAndMarginsComputer for InternalTable {
Packit f0b94e
    /// Compute the used value of inline-size, taking care of min-inline-size and max-inline-size.
Packit f0b94e
    ///
Packit f0b94e
    /// CSS Section 10.4: Minimum and Maximum inline-sizes
Packit f0b94e
    fn compute_used_inline_size(
Packit f0b94e
        &self,
Packit f0b94e
        block: &mut BlockFlow,
Packit f0b94e
        shared_context: &SharedStyleContext,
Packit f0b94e
        parent_flow_inline_size: Au
Packit f0b94e
    ) {
Packit f0b94e
        let mut input = self.compute_inline_size_constraint_inputs(block,
Packit f0b94e
                                                                   parent_flow_inline_size,
Packit f0b94e
                                                                   shared_context);
Packit f0b94e
Packit f0b94e
        // Tables are always at least as wide as their minimum inline size.
Packit f0b94e
        let minimum_inline_size =
Packit f0b94e
            block.base.intrinsic_inline_sizes.minimum_inline_size -
Packit f0b94e
            block.fragment.border_padding.inline_start_end();
Packit f0b94e
        input.available_inline_size = cmp::max(input.available_inline_size, minimum_inline_size);
Packit f0b94e
Packit f0b94e
        let solution = self.solve_inline_size_constraints(block, &input);
Packit f0b94e
        self.set_inline_size_constraint_solutions(block, solution);
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Solve the inline-size and margins constraints for this block flow.
Packit f0b94e
    fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
Packit f0b94e
                                     -> ISizeConstraintSolution {
Packit f0b94e
        ISizeConstraintSolution::new(input.available_inline_size, Au(0), Au(0))
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Information about the intrinsic inline sizes of columns within a table.
Packit f0b94e
///
Packit f0b94e
/// During table inline-size bubbling, we might need to store both a percentage constraint and a
Packit f0b94e
/// specific width constraint. For instance, one cell might say that it wants to be 100 pixels wide
Packit f0b94e
/// in the inline direction and another cell might say that it wants to take up 20% of the inline-
Packit f0b94e
/// size of the table. Now because we bubble up these constraints during the bubble-inline-sizes
Packit f0b94e
/// phase of layout, we don't know yet how wide the table is ultimately going to be in the inline
Packit f0b94e
/// direction. As we need to pick the maximum width of all cells for a column (in this case, the
Packit f0b94e
/// maximum of 100 pixels and 20% of the table), the preceding constraint means that we must
Packit f0b94e
/// potentially store both a specified width *and* a specified percentage, so that the inline-size
Packit f0b94e
/// assignment phase of layout will know which one to pick.
Packit f0b94e
#[derive(Clone, Copy, Debug, Serialize)]
Packit f0b94e
pub struct ColumnIntrinsicInlineSize {
Packit f0b94e
    /// The preferred intrinsic inline size.
Packit f0b94e
    pub preferred: Au,
Packit f0b94e
    /// The largest specified size of this column as a length.
Packit f0b94e
    pub minimum_length: Au,
Packit f0b94e
    /// The largest specified size of this column as a percentage (`width` property).
Packit f0b94e
    pub percentage: CSSFloat,
Packit f0b94e
    /// Whether the column inline size is *constrained* per INTRINSIC ยง 4.1.
Packit f0b94e
    pub constrained: bool,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl ColumnIntrinsicInlineSize {
Packit f0b94e
    /// Returns a newly-initialized `ColumnIntrinsicInlineSize` with all fields blank.
Packit f0b94e
    pub fn new() -> ColumnIntrinsicInlineSize {
Packit f0b94e
        ColumnIntrinsicInlineSize {
Packit f0b94e
            preferred: Au(0),
Packit f0b94e
            minimum_length: Au(0),
Packit f0b94e
            percentage: 0.0,
Packit f0b94e
            constrained: false,
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns the higher of the two percentages specified in `self` and `other`.
Packit f0b94e
    pub fn greatest_percentage(&self, other: &ColumnIntrinsicInlineSize) -> CSSFloat {
Packit f0b94e
        if self.percentage > other.percentage {
Packit f0b94e
            self.percentage
Packit f0b94e
        } else {
Packit f0b94e
            other.percentage
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// The actual inline size for each column.
Packit f0b94e
///
Packit f0b94e
/// TODO(pcwalton): There will probably be some `border-collapse`-related info in here too
Packit f0b94e
/// eventually.
Packit f0b94e
#[derive(Clone, Copy, Debug, Serialize)]
Packit f0b94e
pub struct ColumnComputedInlineSize {
Packit f0b94e
    /// The computed size of this inline column.
Packit f0b94e
    pub size: Au,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
pub trait VecExt<T> {
Packit f0b94e
    fn push_or_set(&mut self, index: usize, value: T) -> &mut T;
Packit f0b94e
    fn get_mut_or_push(&mut self, index: usize, zero: T) -> &mut T;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<T> VecExt<T> for Vec<T> {
Packit f0b94e
    fn push_or_set(&mut self, index: usize, value: T) -> &mut T {
Packit f0b94e
        if index < self.len() {
Packit f0b94e
            self[index] = value
Packit f0b94e
        } else {
Packit f0b94e
            debug_assert_eq!(index, self.len());
Packit f0b94e
            self.push(value)
Packit f0b94e
        }
Packit f0b94e
        &mut self[index]
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn get_mut_or_push(&mut self, index: usize, zero: T) -> &mut T {
Packit f0b94e
        if index >= self.len() {
Packit f0b94e
            debug_assert_eq!(index, self.len());
Packit f0b94e
            self.push(zero)
Packit f0b94e
        }
Packit f0b94e
        &mut self[index]
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Updates the border styles in the block direction for a single row. This function should
Packit f0b94e
/// only be called if border collapsing is on. It is factored out into a separate function
Packit f0b94e
/// because we process children of rowgroups too.
Packit f0b94e
fn perform_border_collapse_for_row(child_table_row: &mut TableRowFlow,
Packit f0b94e
                                   table_inline_borders: &TableInlineCollapsedBorders,
Packit f0b94e
                                   previous_block_borders: PreviousBlockCollapsedBorders,
Packit f0b94e
                                   next_block_borders: NextBlockCollapsedBorders,
Packit f0b94e
                                   inline_spacing: &mut Vec<Au>,
Packit f0b94e
                                   block_spacing: &mut Vec<Au>) {
Packit f0b94e
    // TODO mbrubeck: Take rowspan and colspan into account.
Packit f0b94e
    let number_of_borders_inline_direction = child_table_row.preliminary_collapsed_borders.inline.len();
Packit f0b94e
    // Compute interior inline borders.
Packit f0b94e
    for (i, this_inline_border) in child_table_row.preliminary_collapsed_borders
Packit f0b94e
                                                  .inline
Packit f0b94e
                                                  .iter_mut()
Packit f0b94e
                                                  .enumerate() {
Packit f0b94e
        child_table_row.final_collapsed_borders.inline.push_or_set(i, *this_inline_border);
Packit f0b94e
        if i == 0 {
Packit f0b94e
            child_table_row.final_collapsed_borders.inline[i].combine(&table_inline_borders.start);
Packit f0b94e
        } else if i + 1 == number_of_borders_inline_direction {
Packit f0b94e
            child_table_row.final_collapsed_borders.inline[i].combine(&table_inline_borders.end);
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let inline_spacing = inline_spacing.get_mut_or_push(i, Au(0));
Packit f0b94e
        *inline_spacing = cmp::max(*inline_spacing, child_table_row.final_collapsed_borders.inline[i].width)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // Compute block-start borders.
Packit f0b94e
    let block_start_borders = &mut child_table_row.final_collapsed_borders.block_start;
Packit f0b94e
    *block_start_borders = child_table_row.preliminary_collapsed_borders.block_start.clone();
Packit f0b94e
    for (i, this_border) in block_start_borders.iter_mut().enumerate() {
Packit f0b94e
        match previous_block_borders {
Packit f0b94e
            PreviousBlockCollapsedBorders::FromPreviousRow(ref previous_block_borders) => {
Packit f0b94e
                if previous_block_borders.len() > i {
Packit f0b94e
                    this_border.combine(&previous_block_borders[i]);
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            PreviousBlockCollapsedBorders::FromTable(table_border) => {
Packit f0b94e
                this_border.combine(&table_border);
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // Compute block-end borders.
Packit f0b94e
    let next_block = &mut child_table_row.final_collapsed_borders.block_end;
Packit f0b94e
    block_spacing.push(Au(0));
Packit f0b94e
    let block_spacing = block_spacing.last_mut().unwrap();
Packit f0b94e
    for (i, this_block_border) in child_table_row.preliminary_collapsed_borders
Packit f0b94e
                                                 .block_end
Packit f0b94e
                                                 .iter()
Packit f0b94e
                                                 .enumerate() {
Packit f0b94e
        let next_block = next_block.push_or_set(i, *this_block_border);
Packit f0b94e
        match next_block_borders {
Packit f0b94e
            NextBlockCollapsedBorders::FromNextRow(next_block_borders) => {
Packit f0b94e
                if next_block_borders.len() > i {
Packit f0b94e
                    next_block.combine(&next_block_borders[i])
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            NextBlockCollapsedBorders::FromTable(ref next_block_borders) => {
Packit f0b94e
                next_block.combine(next_block_borders);
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
        *block_spacing = cmp::max(*block_spacing, next_block.width)
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Encapsulates functionality shared among all table-like flows: for now, tables and table
Packit f0b94e
/// rowgroups.
Packit f0b94e
pub trait TableLikeFlow {
Packit f0b94e
    /// Lays out the rows of a table.
Packit f0b94e
    fn assign_block_size_for_table_like_flow(&mut self, block_direction_spacing: Au,
Packit f0b94e
                                             layout_context: &LayoutContext);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl TableLikeFlow for BlockFlow {
Packit f0b94e
    fn assign_block_size_for_table_like_flow(&mut self, block_direction_spacing: Au,
Packit f0b94e
                                             layout_context: &LayoutContext) {
Packit f0b94e
        debug_assert!(self.fragment.style.get_inheritedtable().border_collapse ==
Packit f0b94e
                      border_collapse::T::Separate || block_direction_spacing == Au(0));
Packit f0b94e
Packit f0b94e
        fn border_spacing_for_row(fragment: &Fragment, row: &TableRowFlow,
Packit f0b94e
                                  block_direction_spacing: Au) -> Au {
Packit f0b94e
            match fragment.style.get_inheritedtable().border_collapse {
Packit f0b94e
                border_collapse::T::Separate => block_direction_spacing,
Packit f0b94e
                border_collapse::T::Collapse => {
Packit f0b94e
                    row.collapsed_border_spacing.block_start
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        if self.base.restyle_damage.contains(ServoRestyleDamage::REFLOW) {
Packit f0b94e
            let mut sizes = vec![Default::default()];
Packit f0b94e
            // The amount of border spacing up to and including this row,
Packit f0b94e
            // but not including the spacing beneath it
Packit f0b94e
            let mut cumulative_border_spacing = Au(0);
Packit f0b94e
            let mut incoming_rowspan_data = vec![];
Packit f0b94e
            let mut rowgroup_id = 0;
Packit f0b94e
            let mut first = true;
Packit f0b94e
Packit f0b94e
            // First pass: Compute block-direction border spacings
Packit f0b94e
            // XXXManishearth this can be done in tandem with the second pass,
Packit f0b94e
            // provided we never hit any rowspan cases
Packit f0b94e
            for kid in self.base.child_iter_mut() {
Packit f0b94e
                if kid.is_table_row() {
Packit f0b94e
                    // skip the first row, it is accounted for
Packit f0b94e
                    if first {
Packit f0b94e
                        first = false;
Packit f0b94e
                        continue;
Packit f0b94e
                    }
Packit f0b94e
                    cumulative_border_spacing +=
Packit f0b94e
                        border_spacing_for_row(&self.fragment, kid.as_table_row(),
Packit f0b94e
                                               block_direction_spacing);
Packit f0b94e
                    sizes.push(TableRowSizeData {
Packit f0b94e
                        // we haven't calculated sizes yet
Packit f0b94e
                        size: Au(0),
Packit f0b94e
                        cumulative_border_spacing,
Packit f0b94e
                        rowgroup_id
Packit f0b94e
                    });
Packit f0b94e
                } else if kid.is_table_rowgroup() && !first {
Packit f0b94e
                    rowgroup_id += 1;
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
Packit f0b94e
            // Second pass: Compute row block sizes
Packit f0b94e
            // [expensive: iterates over cells]
Packit f0b94e
            let mut i = 0;
Packit f0b94e
            for kid in self.base.child_iter_mut() {
Packit f0b94e
                if kid.is_table_row() {
Packit f0b94e
                    let size = kid.as_mut_table_row()
Packit f0b94e
                        .compute_block_size_table_row_base(layout_context,
Packit f0b94e
                                                           &mut incoming_rowspan_data,
Packit f0b94e
                                                           &sizes,
Packit f0b94e
                                                           i);
Packit f0b94e
                    sizes[i].size = size;
Packit f0b94e
                    i += 1;
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
Packit f0b94e
Packit f0b94e
            // Our current border-box position.
Packit f0b94e
            let block_start_border_padding = self.fragment.border_padding.block_start;
Packit f0b94e
            let mut current_block_offset = block_start_border_padding;
Packit f0b94e
            let mut has_rows = false;
Packit f0b94e
Packit f0b94e
            // Third pass: Assign block sizes and positions to rows, cells, and other children
Packit f0b94e
            // [expensive: iterates over cells]
Packit f0b94e
            // At this point, `current_block_offset` is at the content edge of our box. Now iterate
Packit f0b94e
            // over children.
Packit f0b94e
            let mut i = 0;
Packit f0b94e
            for kid in self.base.child_iter_mut() {
Packit f0b94e
                if kid.is_table_row() {
Packit f0b94e
                    has_rows = true;
Packit f0b94e
                    let row = kid.as_mut_table_row();
Packit f0b94e
                    row.assign_block_size_to_self_and_children(&sizes, i);
Packit f0b94e
                    row.mut_base().restyle_damage
Packit f0b94e
                        .remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
Packit f0b94e
                                ServoRestyleDamage::REFLOW);
Packit f0b94e
                    current_block_offset = current_block_offset +
Packit f0b94e
                        border_spacing_for_row(&self.fragment, row,
Packit f0b94e
                                               block_direction_spacing);
Packit f0b94e
                    i += 1;
Packit f0b94e
                }
Packit f0b94e
Packit f0b94e
                // At this point, `current_block_offset` is at the border edge of the child.
Packit f0b94e
                kid.mut_base().position.start.b = current_block_offset;
Packit f0b94e
Packit f0b94e
                // Move past the child's border box. Do not use the `translate_including_floats`
Packit f0b94e
                // function here because the child has already translated floats past its border
Packit f0b94e
                // box.
Packit f0b94e
                let kid_base = kid.mut_base();
Packit f0b94e
                current_block_offset = current_block_offset + kid_base.position.size.block;
Packit f0b94e
            }
Packit f0b94e
Packit f0b94e
            // Compute any explicitly-specified block size.
Packit f0b94e
            // Can't use `for` because we assign to
Packit f0b94e
            // `candidate_block_size_iterator.candidate_value`.
Packit f0b94e
            let mut block_size = current_block_offset - block_start_border_padding;
Packit f0b94e
            let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
Packit f0b94e
                &self.fragment,
Packit f0b94e
                self.base.block_container_explicit_block_size);
Packit f0b94e
            while let Some(candidate_block_size) = candidate_block_size_iterator.next() {
Packit f0b94e
                candidate_block_size_iterator.candidate_value =
Packit f0b94e
                    match candidate_block_size {
Packit f0b94e
                        MaybeAuto::Auto => block_size,
Packit f0b94e
                        MaybeAuto::Specified(value) => value
Packit f0b94e
                    };
Packit f0b94e
            }
Packit f0b94e
Packit f0b94e
            // Adjust `current_block_offset` as necessary to account for the explicitly-specified
Packit f0b94e
            // block-size.
Packit f0b94e
            block_size = candidate_block_size_iterator.candidate_value;
Packit f0b94e
            let delta = block_size - (current_block_offset - block_start_border_padding);
Packit f0b94e
            current_block_offset = current_block_offset + delta;
Packit f0b94e
Packit f0b94e
            // Take border, padding, and spacing into account.
Packit f0b94e
            let block_end_offset = self.fragment.border_padding.block_end +
Packit f0b94e
                if has_rows { block_direction_spacing } else { Au(0) };
Packit f0b94e
            current_block_offset = current_block_offset + block_end_offset;
Packit f0b94e
Packit f0b94e
            // Now that `current_block_offset` is at the block-end of the border box, compute the
Packit f0b94e
            // final border box position.
Packit f0b94e
            self.fragment.border_box.size.block = current_block_offset;
Packit f0b94e
            self.fragment.border_box.start.b = Au(0);
Packit f0b94e
            self.base.position.size.block = current_block_offset;
Packit f0b94e
Packit f0b94e
            // Fourth pass: Assign absolute position info
Packit f0b94e
            // Write in the size of the relative containing block for children. (This information
Packit f0b94e
            // is also needed to handle RTL.)
Packit f0b94e
            for kid in self.base.child_iter_mut() {
Packit f0b94e
                kid.mut_base().early_absolute_position_info = EarlyAbsolutePositionInfo {
Packit f0b94e
                    relative_containing_block_size: self.fragment.content_box().size,
Packit f0b94e
                    relative_containing_block_mode: self.fragment.style().writing_mode,
Packit f0b94e
                };
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        self.base.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Inline collapsed borders for the table itself.
Packit f0b94e
#[derive(Debug)]
Packit f0b94e
struct TableInlineCollapsedBorders {
Packit f0b94e
    /// The table border at the start of the inline direction.
Packit f0b94e
    start: CollapsedBorder,
Packit f0b94e
    /// The table border at the end of the inline direction.
Packit f0b94e
    end: CollapsedBorder,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
enum PreviousBlockCollapsedBorders {
Packit f0b94e
    FromPreviousRow(Vec<CollapsedBorder>),
Packit f0b94e
    FromTable(CollapsedBorder),
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
enum NextBlockCollapsedBorders<'a> {
Packit f0b94e
    FromNextRow(&'a [CollapsedBorder]),
Packit f0b94e
    FromTable(CollapsedBorder),
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Iterator over all the rows of a table, which also
Packit f0b94e
/// provides the Fragment for rowgroups if any
Packit f0b94e
struct TableRowAndGroupIterator<'a> {
Packit f0b94e
    kids: FlowListIterator<'a>,
Packit f0b94e
    group: Option<(&'a Fragment, FlowListIterator<'a>)>
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'a> TableRowAndGroupIterator<'a> {
Packit f0b94e
    fn new(base: &'a BaseFlow) -> Self {
Packit f0b94e
        TableRowAndGroupIterator {
Packit f0b94e
            kids: base.child_iter(),
Packit f0b94e
            group: None,
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'a> Iterator for TableRowAndGroupIterator<'a> {
Packit f0b94e
    type Item = (Option<&'a Fragment>, &'a TableRowFlow);
Packit f0b94e
    #[inline]
Packit f0b94e
    fn next(&mut self) -> Option<Self::Item> {
Packit f0b94e
        // If we're inside a rowgroup, iterate through the rowgroup's children.
Packit f0b94e
        if let Some(ref mut group) = self.group {
Packit f0b94e
            if let Some(grandkid) = group.1.next() {
Packit f0b94e
                return Some((Some(group.0), grandkid.as_table_row()))
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
        // Otherwise, iterate through the table's children.
Packit f0b94e
        self.group = None;
Packit f0b94e
        match self.kids.next() {
Packit f0b94e
            Some(kid) => {
Packit f0b94e
                if kid.is_table_rowgroup() {
Packit f0b94e
                    let mut rowgroup = kid.as_table_rowgroup();
Packit f0b94e
                    let iter = rowgroup.block_flow.base.child_iter();
Packit f0b94e
                    self.group = Some((&rowgroup.block_flow.fragment, iter));
Packit f0b94e
                    self.next()
Packit f0b94e
                } else if kid.is_table_row() {
Packit f0b94e
                    Some((None, kid.as_table_row()))
Packit f0b94e
                } else {
Packit f0b94e
                    self.next() // Skip children that are not rows or rowgroups
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            None => None
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Iterator over all the rows of a table, which also
Packit f0b94e
/// provides the Fragment for rowgroups if any
Packit f0b94e
struct MutTableRowAndGroupIterator<'a> {
Packit f0b94e
    kids: MutFlowListIterator<'a>,
Packit f0b94e
    group: Option<(&'a Fragment, MutFlowListIterator<'a>)>
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'a> MutTableRowAndGroupIterator<'a> {
Packit f0b94e
    fn new(base: &'a mut BaseFlow) -> Self {
Packit f0b94e
        MutTableRowAndGroupIterator {
Packit f0b94e
            kids: base.child_iter_mut(),
Packit f0b94e
            group: None,
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'a> Iterator for MutTableRowAndGroupIterator<'a> {
Packit f0b94e
    type Item = (Option<&'a Fragment>, &'a mut TableRowFlow);
Packit f0b94e
    #[inline]
Packit f0b94e
    fn next(&mut self) -> Option<Self::Item> {
Packit f0b94e
        // If we're inside a rowgroup, iterate through the rowgroup's children.
Packit f0b94e
        if let Some(ref mut group) = self.group {
Packit f0b94e
            if let Some(grandkid) = group.1.next() {
Packit f0b94e
                return Some((Some(group.0), grandkid.as_mut_table_row()))
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
        // Otherwise, iterate through the table's children.
Packit f0b94e
        self.group = None;
Packit f0b94e
        match self.kids.next() {
Packit f0b94e
            Some(kid) => {
Packit f0b94e
                if kid.is_table_rowgroup() {
Packit f0b94e
                    let mut rowgroup = kid.as_mut_table_rowgroup();
Packit f0b94e
                    let iter = rowgroup.block_flow.base.child_iter_mut();
Packit f0b94e
                    self.group = Some((&rowgroup.block_flow.fragment, iter));
Packit f0b94e
                    self.next()
Packit f0b94e
                } else if kid.is_table_row() {
Packit f0b94e
                    Some((None, kid.as_mut_table_row()))
Packit f0b94e
                } else {
Packit f0b94e
                    self.next() // Skip children that are not rows or rowgroups
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            None => None
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Iterator over all the rows of a table
Packit f0b94e
struct TableRowIterator<'a>(MutTableRowAndGroupIterator<'a>);
Packit f0b94e
Packit f0b94e
impl<'a> TableRowIterator<'a> {
Packit f0b94e
    fn new(base: &'a mut BaseFlow) -> Self {
Packit f0b94e
        TableRowIterator(MutTableRowAndGroupIterator::new(base))
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'a> Iterator for TableRowIterator<'a> {
Packit f0b94e
    type Item = &'a mut TableRowFlow;
Packit f0b94e
    #[inline]
Packit f0b94e
    fn next(&mut self) -> Option<Self::Item> {
Packit f0b94e
        self.0.next().map(|n| n.1)
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// An iterator over table cells, yielding all relevant style objects
Packit f0b94e
/// for each cell
Packit f0b94e
///
Packit f0b94e
/// Used for correctly handling table layers from
Packit f0b94e
/// https://drafts.csswg.org/css2/tables.html#table-layers
Packit f0b94e
struct TableCellStyleIterator<'table> {
Packit f0b94e
    column_styles: Vec<ColumnStyle<'table>>,
Packit f0b94e
    row_iterator: TableRowAndGroupIterator<'table>,
Packit f0b94e
    row_info: Option<TableCellStyleIteratorRowInfo<'table>>,
Packit f0b94e
    column_index: TableCellColumnIndexData,
Packit f0b94e
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
struct TableCellStyleIteratorRowInfo<'table> {
Packit f0b94e
    row: &'table TableRowFlow,
Packit f0b94e
    rowgroup: Option<&'table Fragment>,
Packit f0b94e
    cell_iterator: FlowListIterator<'table>,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'table> TableCellStyleIterator<'table> {
Packit f0b94e
    fn new(table: &'table TableFlow) -> Self {
Packit f0b94e
        let column_styles = table.column_styles();
Packit f0b94e
        let mut row_iterator = TableRowAndGroupIterator::new(&table.block_flow.base);
Packit f0b94e
        let row_info = if let Some((group, row)) = row_iterator.next() {
Packit f0b94e
            Some(TableCellStyleIteratorRowInfo {
Packit f0b94e
                row: &row,
Packit f0b94e
                rowgroup: group,
Packit f0b94e
                cell_iterator: row.block_flow.base.child_iter()
Packit f0b94e
            })
Packit f0b94e
        } else {
Packit f0b94e
            None
Packit f0b94e
        };
Packit f0b94e
        TableCellStyleIterator {
Packit f0b94e
            column_styles, row_iterator, row_info,
Packit f0b94e
            column_index: Default::default(),
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
struct TableCellStyleInfo<'table> {
Packit f0b94e
    cell: &'table TableCellFlow,
Packit f0b94e
    colgroup_style: Option<&'table ComputedValues>,
Packit f0b94e
    col_style: Option<&'table ComputedValues>,
Packit f0b94e
    rowgroup_style: Option<&'table ComputedValues>,
Packit f0b94e
    row_style: &'table ComputedValues,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
struct TableCellColumnIndexData {
Packit f0b94e
    /// Which column this is in the table
Packit f0b94e
    pub absolute: u32,
Packit f0b94e
    /// The index of the current column in column_styles
Packit f0b94e
    /// (i.e. which  element it is)
Packit f0b94e
    pub relative: u32,
Packit f0b94e
    /// In case of multispan s, where we are in the
Packit f0b94e
    /// span of the current  element
Packit f0b94e
    pub relative_offset: u32,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl Default for TableCellColumnIndexData {
Packit f0b94e
    fn default() -> Self {
Packit f0b94e
        TableCellColumnIndexData {
Packit f0b94e
            absolute: 0,
Packit f0b94e
            relative: 0,
Packit f0b94e
            relative_offset: 0,
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl TableCellColumnIndexData {
Packit f0b94e
    /// Moves forward by `amount` columns, updating the various indices used
Packit f0b94e
    ///
Packit f0b94e
    /// This totally ignores rowspan -- if colspan and rowspan clash,
Packit f0b94e
    /// they just overlap, so we ignore it.
Packit f0b94e
    fn advance(&mut self, amount: u32, column_styles: &[ColumnStyle]) {
Packit f0b94e
        self.absolute += amount;
Packit f0b94e
        self.relative_offset += amount;
Packit f0b94e
        if let Some(mut current_col) =
Packit f0b94e
            column_styles.get(self.relative as usize) {
Packit f0b94e
            while self.relative_offset >= current_col.span {
Packit f0b94e
                // move to the next column
Packit f0b94e
                self.relative += 1;
Packit f0b94e
                self.relative_offset -= current_col.span;
Packit f0b94e
                if let Some(column_style) =
Packit f0b94e
                    column_styles.get(self.relative as usize) {
Packit f0b94e
                    current_col = column_style;
Packit f0b94e
                } else {
Packit f0b94e
                    // we ran out of column_styles,
Packit f0b94e
                    // so we don't need to update the indices
Packit f0b94e
                    break;
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'table> Iterator for TableCellStyleIterator<'table> {
Packit f0b94e
    type Item = TableCellStyleInfo<'table>;
Packit f0b94e
    #[inline]
Packit f0b94e
    fn next(&mut self) -> Option<Self::Item> {
Packit f0b94e
        // FIXME We do this awkward .take() followed by shoving it back in
Packit f0b94e
        // because without NLL the row_info borrow lasts too long
Packit f0b94e
        if let Some(mut row_info) = self.row_info.take() {
Packit f0b94e
            if let Some(rowspan) = row_info.row.incoming_rowspan.get(self.column_index.absolute as usize) {
Packit f0b94e
                // we are not allowed to use this column as a starting point. Try the next one.
Packit f0b94e
                if *rowspan > 1 {
Packit f0b94e
                    self.column_index.advance(1, &self.column_styles);
Packit f0b94e
                    // put row_info back in
Packit f0b94e
                    self.row_info = Some(row_info);
Packit f0b94e
                    // try again
Packit f0b94e
                    return self.next();
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            if let Some(cell) = row_info.cell_iterator.next() {
Packit f0b94e
                let rowgroup_style = row_info.rowgroup.map(|r| r.style());
Packit f0b94e
                let row_style = row_info.row.block_flow.fragment.style();
Packit f0b94e
                let cell = cell.as_table_cell();
Packit f0b94e
                let (col_style, colgroup_style) = if let Some(column_style) =
Packit f0b94e
                        self.column_styles.get(self.column_index.relative as usize) {
Packit f0b94e
                    let styles = (column_style.col_style.clone(), column_style.colgroup_style.clone());
Packit f0b94e
                    self.column_index.advance(cell.column_span, &self.column_styles);
Packit f0b94e
Packit f0b94e
                    styles
Packit f0b94e
                } else {
Packit f0b94e
                    (None, None)
Packit f0b94e
                };
Packit f0b94e
                // put row_info back in
Packit f0b94e
                self.row_info = Some(row_info);
Packit f0b94e
                return Some(TableCellStyleInfo {
Packit f0b94e
                    cell,
Packit f0b94e
                    colgroup_style,
Packit f0b94e
                    col_style,
Packit f0b94e
                    rowgroup_style,
Packit f0b94e
                    row_style,
Packit f0b94e
                })
Packit f0b94e
            } else {
Packit f0b94e
                // next row
Packit f0b94e
                if let Some((group, row)) = self.row_iterator.next() {
Packit f0b94e
                    self.row_info = Some(TableCellStyleIteratorRowInfo {
Packit f0b94e
                        row: &row,
Packit f0b94e
                        rowgroup: group,
Packit f0b94e
                        cell_iterator: row.block_flow.base.child_iter()
Packit f0b94e
                    });
Packit f0b94e
                    self.column_index = Default::default();
Packit f0b94e
                    self.next()
Packit f0b94e
                } else {
Packit f0b94e
                    // out of rows
Packit f0b94e
                    // row_info stays None
Packit f0b94e
                    None
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
        } else {
Packit f0b94e
            // empty table
Packit f0b94e
            None
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<'table> TableCellStyleInfo<'table> {
Packit f0b94e
    fn build_display_list(&self, mut state: &mut DisplayListBuildState) {
Packit f0b94e
        use style::computed_values::visibility::T as Visibility;
Packit f0b94e
Packit f0b94e
        if !self.cell.visible || self.cell.block_flow.fragment.style()
Packit f0b94e
                                     .get_inheritedbox().visibility != Visibility::Visible {
Packit f0b94e
            return
Packit f0b94e
        }
Packit f0b94e
        let border_painting_mode = match self.cell.block_flow
Packit f0b94e
                                             .fragment
Packit f0b94e
                                             .style
Packit f0b94e
                                             .get_inheritedtable()
Packit f0b94e
                                             .border_collapse {
Packit f0b94e
            border_collapse::T::Separate => BorderPaintingMode::Separate,
Packit f0b94e
            border_collapse::T::Collapse => BorderPaintingMode::Collapse(&self.cell.collapsed_borders),
Packit f0b94e
        };
Packit f0b94e
        {
Packit f0b94e
            let cell_flow = &self.cell.block_flow;
Packit f0b94e
            let initial = ComputedValues::initial_values();
Packit f0b94e
Packit f0b94e
            let build_dl = |sty: &ComputedValues, state: &mut &mut DisplayListBuildState| {
Packit f0b94e
                let background = sty.get_background();
Packit f0b94e
                // Don't redraw backgrounds that we've already drawn
Packit f0b94e
                if background as *const Background == initial.get_background() as *const _ {
Packit f0b94e
                    return;
Packit f0b94e
                }
Packit f0b94e
                let background_color = sty.resolve_color(background.background_color);
Packit f0b94e
                cell_flow.build_display_list_for_background_if_applicable_with_background(
Packit f0b94e
                    state, background, background_color
Packit f0b94e
                );
Packit f0b94e
            };
Packit f0b94e
Packit f0b94e
            if let Some(ref sty) = self.colgroup_style {
Packit f0b94e
                build_dl(&sty, &mut state);
Packit f0b94e
            }
Packit f0b94e
            if let Some(ref sty) = self.col_style {
Packit f0b94e
                build_dl(&sty, &mut state);
Packit f0b94e
            }
Packit f0b94e
            if let Some(ref sty) = self.rowgroup_style {
Packit f0b94e
                build_dl(sty, &mut state);
Packit f0b94e
            }
Packit f0b94e
            build_dl(self.row_style, &mut state);
Packit f0b94e
        }
Packit f0b94e
        // the restyle damage will be set in TableCellFlow::build_display_list()
Packit f0b94e
        self.cell.block_flow.build_display_list_for_block_no_damage(state, border_painting_mode)
Packit f0b94e
    }
Packit f0b94e
}