Blame servo/components/layout/flex.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
//! Layout for elements with a CSS `display` property of `flex`.
Packit f0b94e
Packit f0b94e
#![deny(unsafe_code)]
Packit f0b94e
Packit f0b94e
use app_units::{Au, MAX_AU};
Packit f0b94e
use block::{AbsoluteAssignBSizesTraversal, BlockFlow, MarginsMayCollapseFlag};
Packit f0b94e
use context::LayoutContext;
Packit f0b94e
use display_list::{DisplayListBuildState, FlexFlowDisplayListBuilding};
Packit f0b94e
use display_list::StackingContextCollectionState;
Packit f0b94e
use euclid::Point2D;
Packit f0b94e
use floats::FloatKind;
Packit f0b94e
use flow::{Flow, FlowClass, GetBaseFlow, ImmutableFlowUtils, OpaqueFlow, FlowFlags};
Packit f0b94e
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
Packit f0b94e
use layout_debug;
Packit f0b94e
use model::{AdjoiningMargins, CollapsibleMargins};
Packit f0b94e
use model::{IntrinsicISizes, MaybeAuto, SizeConstraint};
Packit f0b94e
use std::cmp::{max, min};
Packit f0b94e
use std::ops::Range;
Packit f0b94e
use style::computed_values::align_content::T as AlignContent;
Packit f0b94e
use style::computed_values::align_self::T as AlignSelf;
Packit f0b94e
use style::computed_values::flex_direction::T as FlexDirection;
Packit f0b94e
use style::computed_values::flex_wrap::T as FlexWrap;
Packit f0b94e
use style::computed_values::justify_content::T as JustifyContent;
Packit f0b94e
use style::logical_geometry::{Direction, LogicalSize};
Packit f0b94e
use style::properties::ComputedValues;
Packit f0b94e
use style::servo::restyle_damage::ServoRestyleDamage;
Packit f0b94e
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
Packit f0b94e
use style::values::computed::flex::FlexBasis;
Packit f0b94e
use style::values::generics::flex::FlexBasis as GenericFlexBasis;
Packit f0b94e
use traversal::PreorderFlowTraversal;
Packit f0b94e
Packit f0b94e
/// The size of an axis. May be a specified size, a min/max
Packit f0b94e
/// constraint, or an unlimited size
Packit f0b94e
#[derive(Debug, Serialize)]
Packit f0b94e
enum AxisSize {
Packit f0b94e
    Definite(Au),
Packit f0b94e
    MinMax(SizeConstraint),
Packit f0b94e
    Infinite,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl AxisSize {
Packit f0b94e
    /// Generate a new available cross or main axis size from the specified size of the container,
Packit f0b94e
    /// containing block size, min constraint, and max constraint
Packit f0b94e
    pub fn new(size: LengthOrPercentageOrAuto, content_size: Option<Au>, min: LengthOrPercentage,
Packit f0b94e
               max: LengthOrPercentageOrNone) -> AxisSize {
Packit f0b94e
        match size {
Packit f0b94e
            LengthOrPercentageOrAuto::Length(length) => AxisSize::Definite(Au::from(length)),
Packit f0b94e
            LengthOrPercentageOrAuto::Percentage(percent) => {
Packit f0b94e
                match content_size {
Packit f0b94e
                    Some(size) => AxisSize::Definite(size.scale_by(percent.0)),
Packit f0b94e
                    None => AxisSize::Infinite
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            LengthOrPercentageOrAuto::Calc(calc) => {
Packit f0b94e
                match calc.to_used_value(content_size) {
Packit f0b94e
                    Some(length) => AxisSize::Definite(length),
Packit f0b94e
                    None => AxisSize::Infinite,
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            LengthOrPercentageOrAuto::Auto => {
Packit f0b94e
                AxisSize::MinMax(SizeConstraint::new(content_size, min, max, None))
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// This function accepts the flex-basis and the size property in main direction from style,
Packit f0b94e
/// and the container size, then return the used value of flex basis. it can be used to help
Packit f0b94e
/// determining the flex base size and to indicate whether the main size of the item
Packit f0b94e
/// is definite after flex size resolving.
Packit f0b94e
fn from_flex_basis(
Packit f0b94e
    flex_basis: FlexBasis,
Packit f0b94e
    main_length: LengthOrPercentageOrAuto,
Packit f0b94e
    containing_length: Option<Au>
Packit f0b94e
) -> MaybeAuto {
Packit f0b94e
    match (flex_basis, containing_length) {
Packit f0b94e
        (GenericFlexBasis::Length(LengthOrPercentage::Length(length)), _) =>
Packit f0b94e
            MaybeAuto::Specified(Au::from(length)),
Packit f0b94e
        (GenericFlexBasis::Length(LengthOrPercentage::Percentage(percent)), Some(size)) =>
Packit f0b94e
            MaybeAuto::Specified(size.scale_by(percent.0)),
Packit f0b94e
        (GenericFlexBasis::Length(LengthOrPercentage::Percentage(_)), None) =>
Packit f0b94e
            MaybeAuto::Auto,
Packit f0b94e
        (GenericFlexBasis::Length(LengthOrPercentage::Calc(calc)), _) =>
Packit f0b94e
            MaybeAuto::from_option(calc.to_used_value(containing_length)),
Packit f0b94e
        (GenericFlexBasis::Content, _) =>
Packit f0b94e
            MaybeAuto::Auto,
Packit f0b94e
        (GenericFlexBasis::Auto, Some(size)) =>
Packit f0b94e
            MaybeAuto::from_style(main_length, size),
Packit f0b94e
        (GenericFlexBasis::Auto, None) => {
Packit f0b94e
            if let LengthOrPercentageOrAuto::Length(length) = main_length {
Packit f0b94e
                MaybeAuto::Specified(Au::from(length))
Packit f0b94e
            } else {
Packit f0b94e
                MaybeAuto::Auto
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// Represents a child in a flex container. Most fields here are used in
Packit f0b94e
/// flex size resolving, and items are sorted by the 'order' property.
Packit f0b94e
#[derive(Debug, Serialize)]
Packit f0b94e
struct FlexItem {
Packit f0b94e
    /// Main size of a flex item, used to store results of flexible length calcuation.
Packit f0b94e
    pub main_size: Au,
Packit f0b94e
    /// Used flex base size.
Packit f0b94e
    pub base_size: Au,
Packit f0b94e
    /// The minimal size in main direction.
Packit f0b94e
    pub min_size: Au,
Packit f0b94e
    /// The maximal main size. If this property is not actually set by style
Packit f0b94e
    /// It will be the largest size available for code reuse.
Packit f0b94e
    pub max_size: Au,
Packit f0b94e
    /// The index of the actual flow in our child list.
Packit f0b94e
    pub index: usize,
Packit f0b94e
    /// The 'flex-grow' property of this item.
Packit f0b94e
    pub flex_grow: f32,
Packit f0b94e
    /// The 'flex-shrink' property of this item.
Packit f0b94e
    pub flex_shrink: f32,
Packit f0b94e
    /// The 'order' property of this item.
Packit f0b94e
    pub order: i32,
Packit f0b94e
    /// Whether the main size has met its constraint.
Packit f0b94e
    pub is_frozen: bool,
Packit f0b94e
    /// True if this flow has property 'visibility::collapse'.
Packit f0b94e
    pub is_strut: bool
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl FlexItem {
Packit f0b94e
    pub fn new(index: usize, flow: &Flow) -> FlexItem {
Packit f0b94e
        let style = &flow.as_block().fragment.style;
Packit f0b94e
        let flex_grow = style.get_position().flex_grow;
Packit f0b94e
        let flex_shrink = style.get_position().flex_shrink;
Packit f0b94e
        let order = style.get_position().order;
Packit f0b94e
        // TODO(stshine): for item with 'visibility:collapse', set is_strut to true.
Packit f0b94e
Packit f0b94e
        FlexItem {
Packit f0b94e
            main_size: Au(0),
Packit f0b94e
            base_size: Au(0),
Packit f0b94e
            min_size: Au(0),
Packit f0b94e
            max_size: MAX_AU,
Packit f0b94e
            index: index,
Packit f0b94e
            flex_grow: flex_grow.into(),
Packit f0b94e
            flex_shrink: flex_shrink.into(),
Packit f0b94e
            order: order,
Packit f0b94e
            is_frozen: false,
Packit f0b94e
            is_strut: false
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Initialize the used flex base size, minimal main size and maximal main size.
Packit f0b94e
    /// For block mode container this method should be called in assign_block_size()
Packit f0b94e
    /// pass so that the item has already been layouted.
Packit f0b94e
    pub fn init_sizes(&mut self, flow: &mut Flow, containing_length: Au, direction: Direction) {
Packit f0b94e
        let block = flow.as_mut_block();
Packit f0b94e
        match direction {
Packit f0b94e
            // TODO(stshine): the definition of min-{width, height} in style component
Packit f0b94e
            // should change to LengthOrPercentageOrAuto for automatic implied minimal size.
Packit f0b94e
            // https://drafts.csswg.org/css-flexbox-1/#min-size-auto
Packit f0b94e
            Direction::Inline => {
Packit f0b94e
                let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
Packit f0b94e
                                            block.fragment.style.content_inline_size(),
Packit f0b94e
                                            Some(containing_length));
Packit f0b94e
Packit f0b94e
                // These methods compute auto margins to zero length, which is exactly what we want.
Packit f0b94e
                block.fragment.compute_border_and_padding(containing_length);
Packit f0b94e
                block.fragment.compute_inline_direction_margins(containing_length);
Packit f0b94e
                block.fragment.compute_block_direction_margins(containing_length);
Packit f0b94e
Packit f0b94e
                let (border_padding, margin) = block.fragment.surrounding_intrinsic_inline_size();
Packit f0b94e
                let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size
Packit f0b94e
                    - border_padding
Packit f0b94e
                    - margin
Packit f0b94e
                    + block.fragment.box_sizing_boundary(direction);
Packit f0b94e
                self.base_size = basis.specified_or_default(content_size);
Packit f0b94e
                self.max_size =
Packit f0b94e
                    block.fragment.style.max_inline_size()
Packit f0b94e
                         .to_used_value(containing_length)
Packit f0b94e
                         .unwrap_or(MAX_AU);
Packit f0b94e
                self.min_size = block.fragment.style.min_inline_size().to_used_value(containing_length);
Packit f0b94e
            }
Packit f0b94e
            Direction::Block => {
Packit f0b94e
                let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
Packit f0b94e
                                            block.fragment.style.content_block_size(),
Packit f0b94e
                                            Some(containing_length));
Packit f0b94e
                let content_size = block.fragment.border_box.size.block
Packit f0b94e
                    - block.fragment.border_padding.block_start_end()
Packit f0b94e
                    + block.fragment.box_sizing_boundary(direction);
Packit f0b94e
                self.base_size = basis.specified_or_default(content_size);
Packit f0b94e
                self.max_size =
Packit f0b94e
                    block.fragment.style.max_block_size()
Packit f0b94e
                         .to_used_value(containing_length)
Packit f0b94e
                         .unwrap_or(MAX_AU);
Packit f0b94e
                self.min_size = block.fragment.style.min_block_size().to_used_value(containing_length);
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns the outer main size of the item, including paddings and margins,
Packit f0b94e
    /// clamped by max and min size.
Packit f0b94e
    pub fn outer_main_size(&self, flow: &Flow, direction: Direction) -> Au {
Packit f0b94e
        let ref fragment = flow.as_block().fragment;
Packit f0b94e
        let outer_width = match direction {
Packit f0b94e
            Direction::Inline => {
Packit f0b94e
                fragment.border_padding.inline_start_end() + fragment.margin.inline_start_end()
Packit f0b94e
            }
Packit f0b94e
            Direction::Block => {
Packit f0b94e
                fragment.border_padding.block_start_end() + fragment.margin.block_start_end()
Packit f0b94e
            }
Packit f0b94e
        };
Packit f0b94e
        max(self.min_size, min(self.base_size, self.max_size))
Packit f0b94e
            - fragment.box_sizing_boundary(direction) + outer_width
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns the number of auto margins in given direction.
Packit f0b94e
    pub fn auto_margin_count(&self, flow: &Flow, direction: Direction) -> i32 {
Packit f0b94e
        let margin = flow.as_block().fragment.style.logical_margin();
Packit f0b94e
        let mut margin_count = 0;
Packit f0b94e
        match direction {
Packit f0b94e
            Direction::Inline => {
Packit f0b94e
                if margin.inline_start == LengthOrPercentageOrAuto::Auto {
Packit f0b94e
                    margin_count += 1;
Packit f0b94e
                }
Packit f0b94e
                if margin.inline_end == LengthOrPercentageOrAuto::Auto {
Packit f0b94e
                    margin_count += 1;
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            Direction::Block => {
Packit f0b94e
                if margin.block_start == LengthOrPercentageOrAuto::Auto {
Packit f0b94e
                    margin_count += 1;
Packit f0b94e
                }
Packit f0b94e
                if margin.block_end == LengthOrPercentageOrAuto::Auto {
Packit f0b94e
                    margin_count += 1;
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
        margin_count
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// A line in a flex container.
Packit f0b94e
// TODO(stshine): More fields are required to handle collapsed items and baseline alignment.
Packit f0b94e
#[derive(Debug, Serialize)]
Packit f0b94e
struct FlexLine {
Packit f0b94e
    /// Range of items belong to this line in 'self.items'.
Packit f0b94e
    pub range: Range<usize>,
Packit f0b94e
    /// Remaining free space of this line, items will grow or shrink based on it being positive or negative.
Packit f0b94e
    pub free_space: Au,
Packit f0b94e
    /// The number of auto margins of items.
Packit f0b94e
    pub auto_margin_count: i32,
Packit f0b94e
    /// Line size in the block direction.
Packit f0b94e
    pub cross_size: Au,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl FlexLine {
Packit f0b94e
    pub fn new(range: Range<usize>, free_space: Au, auto_margin_count: i32) -> FlexLine {
Packit f0b94e
        FlexLine {
Packit f0b94e
            range: range,
Packit f0b94e
            auto_margin_count: auto_margin_count,
Packit f0b94e
            free_space: free_space,
Packit f0b94e
            cross_size: Au(0)
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// This method implements the flexible lengths resolving algorithm.
Packit f0b94e
    /// The 'collapse' parameter is used to indicate whether items with 'visibility: collapse'
Packit f0b94e
    /// is included in length resolving. The result main size is stored in 'item.main_size'.
Packit f0b94e
    /// <https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths>
Packit f0b94e
    pub fn flex_resolve(&mut self, items: &mut [FlexItem], collapse: bool) {
Packit f0b94e
        let mut total_grow = 0.0;
Packit f0b94e
        let mut total_shrink = 0.0;
Packit f0b94e
        let mut total_scaled = 0.0;
Packit f0b94e
        let mut active_count = 0;
Packit f0b94e
        // Iterate through items, collect total factors and freeze those that have already met
Packit f0b94e
        // their constraints or won't grow/shrink in corresponding scenario.
Packit f0b94e
        // https://drafts.csswg.org/css-flexbox/#resolve-flexible-lengths
Packit f0b94e
        for item in items.iter_mut().filter(|i| !(i.is_strut && collapse)) {
Packit f0b94e
            item.main_size = max(item.min_size, min(item.base_size, item.max_size));
Packit f0b94e
            if (self.free_space > Au(0) && (item.flex_grow == 0.0 || item.base_size >= item.max_size)) ||
Packit f0b94e
                (self.free_space < Au(0) && (item.flex_shrink == 0.0 || item.base_size <= item.min_size)) {
Packit f0b94e
                    item.is_frozen = true;
Packit f0b94e
                } else {
Packit f0b94e
                    item.is_frozen = false;
Packit f0b94e
                    total_grow += item.flex_grow;
Packit f0b94e
                    total_shrink += item.flex_shrink;
Packit f0b94e
                    // The scaled factor is used to calculate flex shrink
Packit f0b94e
                    total_scaled += item.flex_shrink * item.base_size.0 as f32;
Packit f0b94e
                    active_count += 1;
Packit f0b94e
                }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let initial_free_space = self.free_space;
Packit f0b94e
        let mut total_variation = Au(1);
Packit f0b94e
        // If there is no remaining free space or all items are frozen, stop loop.
Packit f0b94e
        while total_variation != Au(0) && self.free_space != Au(0) && active_count > 0 {
Packit f0b94e
            self.free_space =
Packit f0b94e
                // https://drafts.csswg.org/css-flexbox/#remaining-free-space
Packit f0b94e
                if self.free_space > Au(0) {
Packit f0b94e
                    min(initial_free_space.scale_by(total_grow), self.free_space)
Packit f0b94e
                } else {
Packit f0b94e
                    max(initial_free_space.scale_by(total_shrink), self.free_space)
Packit f0b94e
                };
Packit f0b94e
Packit f0b94e
            total_variation = Au(0);
Packit f0b94e
            for item in items.iter_mut().filter(|i| !i.is_frozen).filter(|i| !(i.is_strut && collapse)) {
Packit f0b94e
                // Use this and the 'abs()' below to make the code work in both grow and shrink scenarios.
Packit f0b94e
                let (factor, end_size) = if self.free_space > Au(0) {
Packit f0b94e
                    (item.flex_grow / total_grow, item.max_size)
Packit f0b94e
                } else {
Packit f0b94e
                    (item.flex_shrink * item.base_size.0 as f32 / total_scaled, item.min_size)
Packit f0b94e
                };
Packit f0b94e
                let variation = self.free_space.scale_by(factor);
Packit f0b94e
                if variation.0.abs() >= (end_size - item.main_size).0.abs() {
Packit f0b94e
                    // Use constraint as the target main size, and freeze item.
Packit f0b94e
                    total_variation += end_size - item.main_size;
Packit f0b94e
                    item.main_size = end_size;
Packit f0b94e
                    item.is_frozen = true;
Packit f0b94e
                    active_count -= 1;
Packit f0b94e
                    total_shrink -= item.flex_shrink;
Packit f0b94e
                    total_grow -= item.flex_grow;
Packit f0b94e
                    total_scaled -= item.flex_shrink * item.base_size.0 as f32;
Packit f0b94e
                } else {
Packit f0b94e
                    total_variation += variation;
Packit f0b94e
                    item.main_size += variation;
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            self.free_space -= total_variation;
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
#[allow(unsafe_code)]
Packit f0b94e
unsafe impl ::flow::HasBaseFlow for FlexFlow {}
Packit f0b94e
Packit f0b94e
/// A block with the CSS `display` property equal to `flex`.
Packit f0b94e
#[derive(Debug, Serialize)]
Packit f0b94e
#[repr(C)]
Packit f0b94e
pub struct FlexFlow {
Packit f0b94e
    /// Data common to all block flows.
Packit f0b94e
    block_flow: BlockFlow,
Packit f0b94e
    /// The logical axis which the main axis will be parallel with.
Packit f0b94e
    /// The cross axis will be parallel with the opposite logical axis.
Packit f0b94e
    main_mode: Direction,
Packit f0b94e
    /// The available main axis size
Packit f0b94e
    available_main_size: AxisSize,
Packit f0b94e
    /// The available cross axis size
Packit f0b94e
    available_cross_size: AxisSize,
Packit f0b94e
    /// List of flex lines in the container.
Packit f0b94e
    lines: Vec<FlexLine>,
Packit f0b94e
    /// List of flex-items that belong to this flex-container
Packit f0b94e
    items: Vec<FlexItem>,
Packit f0b94e
    /// True if the flex-direction is *-reversed
Packit f0b94e
    main_reverse: bool,
Packit f0b94e
    /// True if this flex container can be multiline.
Packit f0b94e
    is_wrappable: bool,
Packit f0b94e
    /// True if the cross direction is reversed.
Packit f0b94e
    cross_reverse: bool
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl FlexFlow {
Packit f0b94e
    pub fn from_fragment(fragment: Fragment,
Packit f0b94e
                         flotation: Option<FloatKind>)
Packit f0b94e
                         -> FlexFlow {
Packit f0b94e
        let main_mode;
Packit f0b94e
        let main_reverse;
Packit f0b94e
        let is_wrappable;
Packit f0b94e
        let cross_reverse;
Packit f0b94e
        {
Packit f0b94e
            let style = fragment.style();
Packit f0b94e
            let (mode, reverse) = match style.get_position().flex_direction {
Packit f0b94e
                FlexDirection::Row => (Direction::Inline, false),
Packit f0b94e
                FlexDirection::RowReverse => (Direction::Inline, true),
Packit f0b94e
                FlexDirection::Column => (Direction::Block, false),
Packit f0b94e
                FlexDirection::ColumnReverse => (Direction::Block, true),
Packit f0b94e
            };
Packit f0b94e
            main_mode = mode;
Packit f0b94e
            main_reverse =
Packit f0b94e
                reverse == style.writing_mode.is_bidi_ltr();
Packit f0b94e
            let (wrappable, reverse) = match fragment.style.get_position().flex_wrap {
Packit f0b94e
                FlexWrap::Nowrap => (false, false),
Packit f0b94e
                FlexWrap::Wrap => (true, false),
Packit f0b94e
                FlexWrap::WrapReverse => (true, true),
Packit f0b94e
            };
Packit f0b94e
            is_wrappable = wrappable;
Packit f0b94e
            // TODO(stshine): Handle vertical writing mode.
Packit f0b94e
            cross_reverse = reverse;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        FlexFlow {
Packit f0b94e
            block_flow: BlockFlow::from_fragment_and_float_kind(fragment, flotation),
Packit f0b94e
            main_mode: main_mode,
Packit f0b94e
            available_main_size: AxisSize::Infinite,
Packit f0b94e
            available_cross_size: AxisSize::Infinite,
Packit f0b94e
            lines: Vec::new(),
Packit f0b94e
            items: Vec::new(),
Packit f0b94e
            main_reverse: main_reverse,
Packit f0b94e
            is_wrappable: is_wrappable,
Packit f0b94e
            cross_reverse: cross_reverse
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    pub fn main_mode(&self) -> Direction {
Packit f0b94e
        self.main_mode
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns a line start after the last item that is already in a line.
Packit f0b94e
    /// Note that when the container main size is infinite(i.e. A column flexbox with auto height),
Packit f0b94e
    /// we do not need to do flex resolving and this can be considered as a fast-path, so the
Packit f0b94e
    /// 'container_size' param does not need to be 'None'. A line has to contain at least one item;
Packit f0b94e
    /// (except this) if the container can be multi-line the sum of outer main size of items should
Packit f0b94e
    /// be less than the container size; a line should be filled by items as much as possible.
Packit f0b94e
    /// After been collected in a line a item should have its main sizes initialized.
Packit f0b94e
    fn get_flex_line(&mut self, container_size: Au) -> Option<FlexLine> {
Packit f0b94e
        let start = self.lines.last().map(|line| line.range.end).unwrap_or(0);
Packit f0b94e
        if start == self.items.len() {
Packit f0b94e
            return None;
Packit f0b94e
        }
Packit f0b94e
        let mut end = start;
Packit f0b94e
        let mut total_line_size = Au(0);
Packit f0b94e
        let mut margin_count = 0;
Packit f0b94e
Packit f0b94e
        let items = &mut self.items[start..];
Packit f0b94e
        let mut children = self.block_flow.base.children.random_access_mut();
Packit f0b94e
        for item in items {
Packit f0b94e
            let kid = children.get(item.index);
Packit f0b94e
            item.init_sizes(kid, container_size, self.main_mode);
Packit f0b94e
            let outer_main_size = item.outer_main_size(kid, self.main_mode);
Packit f0b94e
            if total_line_size + outer_main_size > container_size && end != start && self.is_wrappable {
Packit f0b94e
                break;
Packit f0b94e
            }
Packit f0b94e
            margin_count += item.auto_margin_count(kid, self.main_mode);
Packit f0b94e
            total_line_size += outer_main_size;
Packit f0b94e
            end += 1;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let line = FlexLine::new(start..end, container_size - total_line_size, margin_count);
Packit f0b94e
        Some(line)
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // TODO(zentner): This function should use flex-basis.
Packit f0b94e
    // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
Packit f0b94e
    // stripped out, and max replaced with union_nonbreaking_inline.
Packit f0b94e
    fn inline_mode_bubble_inline_sizes(&mut self) {
Packit f0b94e
        let fixed_width = match self.block_flow.fragment.style().get_position().width {
Packit f0b94e
            LengthOrPercentageOrAuto::Length(_) => true,
Packit f0b94e
            _ => false,
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
Packit f0b94e
        if !fixed_width {
Packit f0b94e
            for kid in self.block_flow.base.children.iter_mut() {
Packit f0b94e
                let base = kid.mut_base();
Packit f0b94e
                let is_absolutely_positioned = base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
Packit f0b94e
                if !is_absolutely_positioned {
Packit f0b94e
                    let flex_item_inline_sizes = IntrinsicISizes {
Packit f0b94e
                        minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size,
Packit f0b94e
                        preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size,
Packit f0b94e
                    };
Packit f0b94e
                    computation.union_nonbreaking_inline(&flex_item_inline_sizes);
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
        self.block_flow.base.intrinsic_inline_sizes = computation.finish();
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // TODO(zentner): This function should use flex-basis.
Packit f0b94e
    // Currently, this is the core of BlockFlow::bubble_inline_sizes() with all float logic
Packit f0b94e
    // stripped out.
Packit f0b94e
    fn block_mode_bubble_inline_sizes(&mut self) {
Packit f0b94e
        let fixed_width = match self.block_flow.fragment.style().get_position().width {
Packit f0b94e
            LengthOrPercentageOrAuto::Length(_) => true,
Packit f0b94e
            _ => false,
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
Packit f0b94e
        if !fixed_width {
Packit f0b94e
            for kid in self.block_flow.base.children.iter_mut() {
Packit f0b94e
                let base = kid.mut_base();
Packit f0b94e
                let is_absolutely_positioned = base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
Packit f0b94e
                if !is_absolutely_positioned {
Packit f0b94e
                    computation.content_intrinsic_sizes.minimum_inline_size =
Packit f0b94e
                        max(computation.content_intrinsic_sizes.minimum_inline_size,
Packit f0b94e
                            base.intrinsic_inline_sizes.minimum_inline_size);
Packit f0b94e
Packit f0b94e
                    computation.content_intrinsic_sizes.preferred_inline_size =
Packit f0b94e
                        max(computation.content_intrinsic_sizes.preferred_inline_size,
Packit f0b94e
                            base.intrinsic_inline_sizes.preferred_inline_size);
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
        self.block_flow.base.intrinsic_inline_sizes = computation.finish();
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // TODO(zentner): This function needs to be radically different for multi-line flexbox.
Packit f0b94e
    // Currently, this is the core of BlockFlow::propagate_assigned_inline_size_to_children() with
Packit f0b94e
    // all float and table logic stripped out.
Packit f0b94e
    fn block_mode_assign_inline_sizes(&mut self,
Packit f0b94e
                                      _layout_context: &LayoutContext,
Packit f0b94e
                                      inline_start_content_edge: Au,
Packit f0b94e
                                      inline_end_content_edge: Au,
Packit f0b94e
                                      content_inline_size: Au) {
Packit f0b94e
        let _scope = layout_debug_scope!("flex::block_mode_assign_inline_sizes");
Packit f0b94e
        debug!("flex::block_mode_assign_inline_sizes");
Packit f0b94e
Packit f0b94e
        // FIXME (mbrubeck): Get correct mode for absolute containing block
Packit f0b94e
        let containing_block_mode = self.block_flow.base.writing_mode;
Packit f0b94e
Packit f0b94e
        let container_block_size = match self.available_main_size {
Packit f0b94e
            AxisSize::Definite(length) => Some(length),
Packit f0b94e
            _ => None
Packit f0b94e
        };
Packit f0b94e
        let container_inline_size = match self.available_cross_size {
Packit f0b94e
            AxisSize::Definite(length) => length,
Packit f0b94e
            AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
Packit f0b94e
            AxisSize::Infinite => content_inline_size
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        let mut children = self.block_flow.base.children.random_access_mut();
Packit f0b94e
        for kid in &mut self.items {
Packit f0b94e
            let kid_base = children.get(kid.index).mut_base();
Packit f0b94e
            kid_base.block_container_explicit_block_size = container_block_size;
Packit f0b94e
            if kid_base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) {
Packit f0b94e
                // The inline-start margin edge of the child flow is at our inline-start content
Packit f0b94e
                // edge, and its inline-size is our content inline-size.
Packit f0b94e
                kid_base.position.start.i =
Packit f0b94e
                    if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
Packit f0b94e
                        inline_start_content_edge
Packit f0b94e
                    } else {
Packit f0b94e
                        // The kid's inline 'start' is at the parent's 'end'
Packit f0b94e
                        inline_end_content_edge
Packit f0b94e
                    };
Packit f0b94e
            }
Packit f0b94e
            kid_base.block_container_inline_size = container_inline_size;
Packit f0b94e
            kid_base.block_container_writing_mode = containing_block_mode;
Packit f0b94e
            kid_base.position.start.i = inline_start_content_edge;
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn inline_mode_assign_inline_sizes(&mut self,
Packit f0b94e
                                       layout_context: &LayoutContext,
Packit f0b94e
                                       inline_start_content_edge: Au,
Packit f0b94e
                                       _inline_end_content_edge: Au,
Packit f0b94e
                                       content_inline_size: Au) {
Packit f0b94e
        let _scope = layout_debug_scope!("flex::inline_mode_assign_inline_sizes");
Packit f0b94e
        debug!("inline_mode_assign_inline_sizes");
Packit f0b94e
Packit f0b94e
        debug!("content_inline_size = {:?}", content_inline_size);
Packit f0b94e
Packit f0b94e
        let child_count = ImmutableFlowUtils::child_count(self as &Flow) as i32;
Packit f0b94e
        debug!("child_count = {:?}", child_count);
Packit f0b94e
        if child_count == 0 {
Packit f0b94e
            return;
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let inline_size = match self.available_main_size {
Packit f0b94e
            AxisSize::Definite(length) => length,
Packit f0b94e
            AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
Packit f0b94e
            AxisSize::Infinite => content_inline_size,
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        let container_mode = self.block_flow.base.block_container_writing_mode;
Packit f0b94e
        self.block_flow.base.position.size.inline = inline_size;
Packit f0b94e
Packit f0b94e
        // Calculate non-auto block size to pass to children.
Packit f0b94e
        let box_border = self.block_flow.fragment.box_sizing_boundary(Direction::Block);
Packit f0b94e
Packit f0b94e
        let parent_container_size =
Packit f0b94e
            self.block_flow.explicit_block_containing_size(layout_context.shared_context());
Packit f0b94e
        // https://drafts.csswg.org/css-ui-3/#box-sizing
Packit f0b94e
        let explicit_content_size = self
Packit f0b94e
                                    .block_flow
Packit f0b94e
                                    .explicit_block_size(parent_container_size)
Packit f0b94e
                                    .map(|x| max(x - box_border, Au(0)));
Packit f0b94e
        let containing_block_text_align =
Packit f0b94e
            self.block_flow.fragment.style().get_inheritedtext().text_align;
Packit f0b94e
Packit f0b94e
        while let Some(mut line) = self.get_flex_line(inline_size) {
Packit f0b94e
            let items = &mut self.items[line.range.clone()];
Packit f0b94e
            line.flex_resolve(items, false);
Packit f0b94e
            // TODO(stshine): if this flex line contain children that have
Packit f0b94e
            // property visibility:collapse, exclude them and resolve again.
Packit f0b94e
Packit f0b94e
            let item_count = items.len() as i32;
Packit f0b94e
            let mut cur_i = inline_start_content_edge;
Packit f0b94e
            let item_interval = if line.free_space >= Au(0) && line.auto_margin_count == 0 {
Packit f0b94e
                match self.block_flow.fragment.style().get_position().justify_content {
Packit f0b94e
                    JustifyContent::SpaceBetween => {
Packit f0b94e
                        if item_count == 1 {
Packit f0b94e
                            Au(0)
Packit f0b94e
                        } else {
Packit f0b94e
                            line.free_space / (item_count - 1)
Packit f0b94e
                        }
Packit f0b94e
                    }
Packit f0b94e
                    JustifyContent::SpaceAround => {
Packit f0b94e
                        line.free_space / item_count
Packit f0b94e
                    }
Packit f0b94e
                    _ => Au(0),
Packit f0b94e
                }
Packit f0b94e
            } else {
Packit f0b94e
                Au(0)
Packit f0b94e
            };
Packit f0b94e
Packit f0b94e
            match self.block_flow.fragment.style().get_position().justify_content {
Packit f0b94e
                // Overflow equally in both ends of line.
Packit f0b94e
                JustifyContent::Center | JustifyContent::SpaceAround => {
Packit f0b94e
                    cur_i += (line.free_space - item_interval * (item_count - 1)) / 2;
Packit f0b94e
                }
Packit f0b94e
                JustifyContent::FlexEnd => {
Packit f0b94e
                    cur_i += line.free_space;
Packit f0b94e
                }
Packit f0b94e
                _ => {}
Packit f0b94e
            }
Packit f0b94e
Packit f0b94e
            let mut children = self.block_flow.base.children.random_access_mut();
Packit f0b94e
            for item in items.iter_mut() {
Packit f0b94e
                let block = children.get(item.index).as_mut_block();
Packit f0b94e
Packit f0b94e
                block.base.block_container_writing_mode = container_mode;
Packit f0b94e
                block.base.block_container_inline_size = inline_size;
Packit f0b94e
                block.base.block_container_explicit_block_size = explicit_content_size;
Packit f0b94e
                // Per CSS 2.1 ยง 16.3.1, text alignment propagates to all children in flow.
Packit f0b94e
                //
Packit f0b94e
                // TODO(#2265, pcwalton): Do this in the cascade instead.
Packit f0b94e
                block.base.flags.set_text_align(containing_block_text_align);
Packit f0b94e
Packit f0b94e
                let margin = block.fragment.style().logical_margin();
Packit f0b94e
                let auto_len =
Packit f0b94e
                    if line.auto_margin_count == 0 || line.free_space <= Au(0) {
Packit f0b94e
                        Au(0)
Packit f0b94e
                    } else {
Packit f0b94e
                        line.free_space / line.auto_margin_count
Packit f0b94e
                    };
Packit f0b94e
                let margin_inline_start = MaybeAuto::from_style(margin.inline_start, inline_size)
Packit f0b94e
                    .specified_or_default(auto_len);
Packit f0b94e
                let margin_inline_end = MaybeAuto::from_style(margin.inline_end, inline_size)
Packit f0b94e
                    .specified_or_default(auto_len);
Packit f0b94e
                let item_inline_size = item.main_size
Packit f0b94e
                    - block.fragment.box_sizing_boundary(self.main_mode)
Packit f0b94e
                    + block.fragment.border_padding.inline_start_end();
Packit f0b94e
                let item_outer_size = item_inline_size + block.fragment.margin.inline_start_end();
Packit f0b94e
Packit f0b94e
                block.fragment.margin.inline_start = margin_inline_start;
Packit f0b94e
                block.fragment.margin.inline_end = margin_inline_end;
Packit f0b94e
                block.fragment.border_box.start.i = margin_inline_start;
Packit f0b94e
                block.fragment.border_box.size.inline = item_inline_size;
Packit f0b94e
                block.base.position.start.i = if !self.main_reverse {
Packit f0b94e
                    cur_i
Packit f0b94e
                } else {
Packit f0b94e
                    inline_start_content_edge * 2 + content_inline_size - cur_i  - item_outer_size
Packit f0b94e
                };
Packit f0b94e
                block.base.position.size.inline = item_outer_size;
Packit f0b94e
                cur_i += item_outer_size + item_interval;
Packit f0b94e
            }
Packit f0b94e
            self.lines.push(line);
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // TODO(zentner): This function should actually flex elements!
Packit f0b94e
    fn block_mode_assign_block_size(&mut self) {
Packit f0b94e
        let mut cur_b = if !self.main_reverse {
Packit f0b94e
            self.block_flow.fragment.border_padding.block_start
Packit f0b94e
        } else {
Packit f0b94e
            self.block_flow.fragment.border_box.size.block
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        let mut children = self.block_flow.base.children.random_access_mut();
Packit f0b94e
        for item in &mut self.items {
Packit f0b94e
            let base = children.get(item.index).mut_base();
Packit f0b94e
            if !self.main_reverse {
Packit f0b94e
                base.position.start.b = cur_b;
Packit f0b94e
                cur_b = cur_b + base.position.size.block;
Packit f0b94e
            } else {
Packit f0b94e
                cur_b = cur_b - base.position.size.block;
Packit f0b94e
                base.position.start.b = cur_b;
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn inline_mode_assign_block_size(&mut self, layout_context: &LayoutContext) {
Packit f0b94e
        let _scope = layout_debug_scope!("flex::inline_mode_assign_block_size");
Packit f0b94e
Packit f0b94e
        let line_count = self.lines.len() as i32;
Packit f0b94e
        let line_align = self.block_flow.fragment.style().get_position().align_content;
Packit f0b94e
        let mut cur_b = self.block_flow.fragment.border_padding.block_start;
Packit f0b94e
        let mut total_cross_size = Au(0);
Packit f0b94e
        let mut line_interval = Au(0);
Packit f0b94e
Packit f0b94e
        {
Packit f0b94e
            let mut children = self.block_flow.base.children.random_access_mut();
Packit f0b94e
            for line in self.lines.iter_mut() {
Packit f0b94e
                for item in &self.items[line.range.clone()] {
Packit f0b94e
                    let fragment = &children.get(item.index).as_block().fragment;
Packit f0b94e
                    line.cross_size = max(line.cross_size,
Packit f0b94e
                                          fragment.border_box.size.block +
Packit f0b94e
                                          fragment.margin.block_start_end());
Packit f0b94e
                }
Packit f0b94e
                total_cross_size += line.cross_size;
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let box_border = self.block_flow.fragment.box_sizing_boundary(Direction::Block);
Packit f0b94e
        let parent_container_size =
Packit f0b94e
            self.block_flow.explicit_block_containing_size(layout_context.shared_context());
Packit f0b94e
        // https://drafts.csswg.org/css-ui-3/#box-sizing
Packit f0b94e
        let explicit_content_size = self
Packit f0b94e
                                    .block_flow
Packit f0b94e
                                    .explicit_block_size(parent_container_size)
Packit f0b94e
                                    .map(|x| max(x - box_border, Au(0)));
Packit f0b94e
Packit f0b94e
        if let Some(container_block_size) = explicit_content_size {
Packit f0b94e
            let free_space = container_block_size - total_cross_size;
Packit f0b94e
            total_cross_size = container_block_size;
Packit f0b94e
Packit f0b94e
            if line_align == AlignContent::Stretch && free_space > Au(0) {
Packit f0b94e
                for line in self.lines.iter_mut() {
Packit f0b94e
                    line.cross_size += free_space / line_count;
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
Packit f0b94e
            line_interval = match line_align {
Packit f0b94e
                AlignContent::SpaceBetween => {
Packit f0b94e
                    if line_count <= 1 {
Packit f0b94e
                        Au(0)
Packit f0b94e
                    } else {
Packit f0b94e
                        free_space / (line_count - 1)
Packit f0b94e
                    }
Packit f0b94e
                }
Packit f0b94e
                AlignContent::SpaceAround => {
Packit f0b94e
                    if line_count == 0 {
Packit f0b94e
                        Au(0)
Packit f0b94e
                    } else {
Packit f0b94e
                        free_space / line_count
Packit f0b94e
                    }
Packit f0b94e
                }
Packit f0b94e
                _ => Au(0),
Packit f0b94e
            };
Packit f0b94e
Packit f0b94e
            match line_align {
Packit f0b94e
                AlignContent::Center | AlignContent::SpaceAround => {
Packit f0b94e
                    cur_b += (free_space - line_interval * (line_count - 1)) / 2;
Packit f0b94e
                }
Packit f0b94e
                AlignContent::FlexEnd => {
Packit f0b94e
                    cur_b += free_space;
Packit f0b94e
                }
Packit f0b94e
                _ => {}
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let mut children = self.block_flow.base.children.random_access_mut();
Packit f0b94e
        for line in &self.lines {
Packit f0b94e
            for item in self.items[line.range.clone()].iter_mut() {
Packit f0b94e
                let block = children.get(item.index).as_mut_block();
Packit f0b94e
                let auto_margin_count = item.auto_margin_count(block, Direction::Block);
Packit f0b94e
                let margin = block.fragment.style().logical_margin();
Packit f0b94e
Packit f0b94e
                let mut margin_block_start = block.fragment.margin.block_start;
Packit f0b94e
                let mut margin_block_end = block.fragment.margin.block_end;
Packit f0b94e
                let mut free_space = line.cross_size - block.base.position.size.block
Packit f0b94e
                    - block.fragment.margin.block_start_end();
Packit f0b94e
Packit f0b94e
                // The spec is a little vague here, but if I understand it correctly, the outer
Packit f0b94e
                // cross size of item should equal to the line size if any auto margin exists.
Packit f0b94e
                // https://drafts.csswg.org/css-flexbox/#algo-cross-margins
Packit f0b94e
                if auto_margin_count > 0 {
Packit f0b94e
                    if margin.block_start == LengthOrPercentageOrAuto::Auto {
Packit f0b94e
                        margin_block_start = if free_space < Au(0) {
Packit f0b94e
                            Au(0)
Packit f0b94e
                        } else {
Packit f0b94e
                            free_space / auto_margin_count
Packit f0b94e
                        };
Packit f0b94e
                    }
Packit f0b94e
                    margin_block_end = line.cross_size - margin_block_start - block.base.position.size.block;
Packit f0b94e
                    free_space = Au(0);
Packit f0b94e
                }
Packit f0b94e
Packit f0b94e
                let self_align = block.fragment.style().get_position().align_self;
Packit f0b94e
                if self_align == AlignSelf::Stretch &&
Packit f0b94e
                    block.fragment.style().content_block_size() == LengthOrPercentageOrAuto::Auto {
Packit f0b94e
                        free_space = Au(0);
Packit f0b94e
                        block.base.block_container_explicit_block_size = Some(line.cross_size);
Packit f0b94e
                        block.base.position.size.block =
Packit f0b94e
                            line.cross_size - margin_block_start - margin_block_end;
Packit f0b94e
                        block.fragment.border_box.size.block = block.base.position.size.block;
Packit f0b94e
                        // FIXME(stshine): item with 'align-self: stretch' and auto cross size should act
Packit f0b94e
                        // as if it has a fixed cross size, all child blocks should resolve against it.
Packit f0b94e
                        // block.assign_block_size(layout_context);
Packit f0b94e
                    }
Packit f0b94e
                block.base.position.start.b = margin_block_start +
Packit f0b94e
                    if !self.cross_reverse {
Packit f0b94e
                        cur_b
Packit f0b94e
                    } else {
Packit f0b94e
                        self.block_flow.fragment.border_padding.block_start * 2
Packit f0b94e
                            + total_cross_size - cur_b - line.cross_size
Packit f0b94e
                    };
Packit f0b94e
                // TODO(stshine): support baseline alignment.
Packit f0b94e
                if free_space != Au(0) {
Packit f0b94e
                    let flex_cross = match self_align {
Packit f0b94e
                        AlignSelf::FlexEnd => free_space,
Packit f0b94e
                        AlignSelf::Center => free_space / 2,
Packit f0b94e
                        _ => Au(0),
Packit f0b94e
                    };
Packit f0b94e
                    block.base.position.start.b +=
Packit f0b94e
                        if !self.cross_reverse {
Packit f0b94e
                            flex_cross
Packit f0b94e
                        } else {
Packit f0b94e
                            free_space - flex_cross
Packit f0b94e
                        };
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            cur_b += line_interval + line.cross_size;
Packit f0b94e
        }
Packit f0b94e
        let total_block_size = total_cross_size + self.block_flow.fragment.border_padding.block_start_end();
Packit f0b94e
        self.block_flow.fragment.border_box.size.block = total_block_size;
Packit f0b94e
        self.block_flow.base.position.size.block = total_block_size;
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl Flow for FlexFlow {
Packit f0b94e
    fn class(&self) -> FlowClass {
Packit f0b94e
        FlowClass::Flex
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn as_mut_flex(&mut self) -> &mut FlexFlow {
Packit f0b94e
        self
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn as_flex(&self) -> &FlexFlow {
Packit f0b94e
        self
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn as_block(&self) -> &BlockFlow {
Packit f0b94e
        &self.block_flow
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 mark_as_root(&mut self) {
Packit f0b94e
        self.block_flow.mark_as_root();
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn bubble_inline_sizes(&mut self) {
Packit f0b94e
        let _scope = layout_debug_scope!("flex::bubble_inline_sizes {:x}",
Packit f0b94e
                                         self.block_flow.base.debug_id());
Packit f0b94e
Packit f0b94e
        // Flexbox Section 9.0: Generate anonymous flex items:
Packit f0b94e
        // This part was handled in the flow constructor.
Packit f0b94e
Packit f0b94e
        // Flexbox Section 9.1: Re-order flex items according to their order.
Packit f0b94e
        // FIXME(stshine): This should be done during flow construction.
Packit f0b94e
        let mut items: Vec<FlexItem> =
Packit f0b94e
            self.block_flow
Packit f0b94e
                .base
Packit f0b94e
                .children
Packit f0b94e
                .iter()
Packit f0b94e
                .enumerate()
Packit f0b94e
                .filter(|&(_, flow)| {
Packit f0b94e
                    !flow.as_block().base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
Packit f0b94e
                })
Packit f0b94e
                .map(|(index, flow)| FlexItem::new(index, flow))
Packit f0b94e
                .collect();
Packit f0b94e
Packit f0b94e
        items.sort_by_key(|item| item.order);
Packit f0b94e
        self.items = items;
Packit f0b94e
Packit f0b94e
        match self.main_mode {
Packit f0b94e
            Direction::Inline => self.inline_mode_bubble_inline_sizes(),
Packit f0b94e
            Direction::Block  => self.block_mode_bubble_inline_sizes()
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
Packit f0b94e
        let _scope = layout_debug_scope!("flex::assign_inline_sizes {:x}", self.block_flow.base.debug_id());
Packit f0b94e
        debug!("assign_inline_sizes");
Packit f0b94e
Packit f0b94e
        if !self.block_flow.base.restyle_damage.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
Packit f0b94e
                                                           ServoRestyleDamage::REFLOW) {
Packit f0b94e
            return
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        self.block_flow.initialize_container_size_for_root(layout_context.shared_context());
Packit f0b94e
Packit f0b94e
        // Our inline-size was set to the inline-size of the containing block by the flow's parent.
Packit f0b94e
        // Now compute the real value.
Packit f0b94e
        let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
Packit f0b94e
        self.block_flow.compute_used_inline_size(layout_context.shared_context(),
Packit f0b94e
                                                 containing_block_inline_size);
Packit f0b94e
        if self.block_flow.base.flags.is_float() {
Packit f0b94e
            self.block_flow.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let (available_block_size, available_inline_size) = {
Packit f0b94e
            let style = &self.block_flow.fragment.style;
Packit f0b94e
            let (specified_block_size, specified_inline_size) = if style.writing_mode.is_vertical() {
Packit f0b94e
                (style.get_position().width, style.get_position().height)
Packit f0b94e
            } else {
Packit f0b94e
                (style.get_position().height, style.get_position().width)
Packit f0b94e
            };
Packit f0b94e
Packit f0b94e
            let available_inline_size = AxisSize::new(specified_inline_size,
Packit f0b94e
                                                      Some(self.block_flow.base.block_container_inline_size),
Packit f0b94e
                                                      style.min_inline_size(),
Packit f0b94e
                                                      style.max_inline_size());
Packit f0b94e
Packit f0b94e
            let available_block_size = AxisSize::new(specified_block_size,
Packit f0b94e
                                                     self.block_flow.base.block_container_explicit_block_size,
Packit f0b94e
                                                     style.min_block_size(),
Packit f0b94e
                                                     style.max_block_size());
Packit f0b94e
            (available_block_size, available_inline_size)
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        // Move in from the inline-start border edge.
Packit f0b94e
        let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
Packit f0b94e
            self.block_flow.fragment.border_padding.inline_start;
Packit f0b94e
Packit f0b94e
        debug!("inline_start_content_edge = {:?}", inline_start_content_edge);
Packit f0b94e
Packit f0b94e
        let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
Packit f0b94e
Packit f0b94e
        // Distance from the inline-end margin edge to the inline-end content edge.
Packit f0b94e
        let inline_end_content_edge =
Packit f0b94e
            self.block_flow.fragment.margin.inline_end +
Packit f0b94e
            self.block_flow.fragment.border_padding.inline_end;
Packit f0b94e
Packit f0b94e
        debug!("padding_and_borders = {:?}", padding_and_borders);
Packit f0b94e
        debug!("self.block_flow.fragment.border_box.size.inline = {:?}",
Packit f0b94e
               self.block_flow.fragment.border_box.size.inline);
Packit f0b94e
        let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
Packit f0b94e
Packit f0b94e
        match self.main_mode {
Packit f0b94e
            Direction::Inline => {
Packit f0b94e
                self.available_main_size = available_inline_size;
Packit f0b94e
                self.available_cross_size = available_block_size;
Packit f0b94e
                self.inline_mode_assign_inline_sizes(layout_context,
Packit f0b94e
                                                     inline_start_content_edge,
Packit f0b94e
                                                     inline_end_content_edge,
Packit f0b94e
                                                     content_inline_size)
Packit f0b94e
            }
Packit f0b94e
            Direction::Block  => {
Packit f0b94e
                self.available_main_size = available_block_size;
Packit f0b94e
                self.available_cross_size = available_inline_size;
Packit f0b94e
                self.block_mode_assign_inline_sizes(layout_context,
Packit f0b94e
                                                    inline_start_content_edge,
Packit f0b94e
                                                    inline_end_content_edge,
Packit f0b94e
                                                    content_inline_size)
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn assign_block_size(&mut self, layout_context: &LayoutContext) {
Packit f0b94e
        match self.main_mode {
Packit f0b94e
            Direction::Inline => {
Packit f0b94e
                self.inline_mode_assign_block_size(layout_context);
Packit f0b94e
                let block_start = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_start);
Packit f0b94e
                let block_end = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_end);
Packit f0b94e
                self.block_flow.base.collapsible_margins = CollapsibleMargins::Collapse(block_start, block_end);
Packit f0b94e
Packit f0b94e
                // TODO(stshine): assign proper static position for absolute descendants.
Packit f0b94e
                if (&*self as &Flow).contains_roots_of_absolute_flow_tree() {
Packit f0b94e
                    // Assign block-sizes for all flows in this absolute flow tree.
Packit f0b94e
                    // This is preorder because the block-size of an absolute flow may depend on
Packit f0b94e
                    // the block-size of its containing block, which may also be an absolute flow.
Packit f0b94e
                    let assign_abs_b_sizes = AbsoluteAssignBSizesTraversal(layout_context.shared_context());
Packit f0b94e
                    assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
Packit f0b94e
                }
Packit f0b94e
            }
Packit f0b94e
            Direction::Block =>{
Packit f0b94e
                self.block_flow
Packit f0b94e
                    .assign_block_size_block_base(layout_context,
Packit f0b94e
                                                  None,
Packit f0b94e
                                                  MarginsMayCollapseFlag::MarginsMayNotCollapse);
Packit f0b94e
                self.block_mode_assign_block_size();
Packit f0b94e
            }
Packit f0b94e
        }
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 place_float_if_applicable<'a>(&mut self) {
Packit f0b94e
        self.block_flow.place_float_if_applicable()
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
        self.build_display_list_for_flex(state);
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
Packit f0b94e
        self.block_flow.collect_stacking_contexts(state);
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 contains_roots_of_absolute_flow_tree(&self) -> bool {
Packit f0b94e
        self.block_flow.contains_roots_of_absolute_flow_tree()
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn is_absolute_containing_block(&self) -> bool {
Packit f0b94e
        self.block_flow.is_absolute_containing_block()
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 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
}