use std::cmp::{max, min};
use std::ops::Range;
use app_units::{Au, MAX_AU};
use euclid::default::Point2D;
use log::debug;
use serde::Serialize;
use style::computed_values::flex_direction::T as FlexDirection;
use style::computed_values::flex_wrap::T as FlexWrap;
use style::logical_geometry::{Direction, LogicalSize};
use style::properties::ComputedValues;
use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::computed::flex::FlexBasis;
use style::values::computed::{MaxSize, Size};
use style::values::specified::align::AlignFlags;
use crate::block::{AbsoluteAssignBSizesTraversal, BlockFlow, MarginsMayCollapseFlag};
use crate::context::LayoutContext;
use crate::display_list::{
BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
};
use crate::floats::FloatKind;
use crate::flow::{Flow, FlowClass, FlowFlags, GetBaseFlow, ImmutableFlowUtils, OpaqueFlow};
use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use crate::model::{
self, AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MaybeAuto, SizeConstraint,
};
use crate::traversal::PreorderFlowTraversal;
use crate::{layout_debug, layout_debug_scope};
#[derive(Debug, Serialize)]
enum AxisSize {
Definite(Au),
MinMax(SizeConstraint),
Infinite,
}
impl AxisSize {
pub fn new(size: &Size, content_size: Option<Au>, min: &Size, max: &MaxSize) -> AxisSize {
match size {
Size::LengthPercentage(ref lp) => match lp.maybe_to_used_value(content_size) {
Some(length) => AxisSize::Definite(length),
None => AxisSize::Infinite,
},
_ => AxisSize::MinMax(SizeConstraint::new(content_size, min, max, None)),
}
}
}
fn from_flex_basis(flex_basis: &FlexBasis, main_length: &Size, containing_length: Au) -> MaybeAuto {
let width = match flex_basis {
FlexBasis::Content => return MaybeAuto::Auto,
FlexBasis::Size(ref width) => width,
};
let width = match width {
Size::Auto => main_length,
_ => width,
};
MaybeAuto::from_option(width.to_used_value(containing_length))
}
#[derive(Debug, Serialize)]
struct FlexItem {
pub main_size: Au,
pub base_size: Au,
pub min_size: Au,
pub max_size: Au,
pub index: usize,
pub flex_grow: f32,
pub flex_shrink: f32,
pub order: i32,
pub is_frozen: bool,
pub is_strut: bool,
}
impl FlexItem {
pub fn new(index: usize, flow: &dyn Flow) -> FlexItem {
let style = &flow.as_block().fragment.style;
let flex_grow = style.get_position().flex_grow;
let flex_shrink = style.get_position().flex_shrink;
let order = style.get_position().order;
FlexItem {
main_size: Au(0),
base_size: Au(0),
min_size: Au(0),
max_size: MAX_AU,
index,
flex_grow: flex_grow.into(),
flex_shrink: flex_shrink.into(),
order,
is_frozen: false,
is_strut: false,
}
}
pub fn init_sizes(&mut self, flow: &mut dyn Flow, containing_length: Au, direction: Direction) {
let block = flow.as_mut_block();
match direction {
Direction::Inline => {
let basis = from_flex_basis(
&block.fragment.style.get_position().flex_basis,
block.fragment.style.content_inline_size(),
containing_length,
);
block.fragment.compute_border_and_padding(containing_length);
block
.fragment
.compute_inline_direction_margins(containing_length);
block
.fragment
.compute_block_direction_margins(containing_length);
let (border_padding, margin) = block.fragment.surrounding_intrinsic_inline_size();
let content_size = block.base.intrinsic_inline_sizes.preferred_inline_size -
border_padding -
margin +
block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size);
self.max_size = block
.fragment
.style
.max_inline_size()
.to_used_value(containing_length)
.unwrap_or(MAX_AU);
self.min_size = block
.fragment
.style
.min_inline_size()
.to_used_value(containing_length)
.unwrap_or(Au(0));
},
Direction::Block => {
let basis = from_flex_basis(
&block.fragment.style.get_position().flex_basis,
block.fragment.style.content_block_size(),
containing_length,
);
let content_size = block.fragment.border_box.size.block -
block.fragment.border_padding.block_start_end() +
block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size);
self.max_size = block
.fragment
.style
.max_block_size()
.to_used_value(containing_length)
.unwrap_or(MAX_AU);
self.min_size = block
.fragment
.style
.min_block_size()
.to_used_value(containing_length)
.unwrap_or(Au(0));
},
}
}
pub fn outer_main_size(&self, flow: &dyn Flow, direction: Direction) -> Au {
let fragment = &flow.as_block().fragment;
let outer_width = match direction {
Direction::Inline => {
fragment.border_padding.inline_start_end() + fragment.margin.inline_start_end()
},
Direction::Block => {
fragment.border_padding.block_start_end() + fragment.margin.block_start_end()
},
};
max(self.min_size, min(self.base_size, self.max_size)) -
fragment.box_sizing_boundary(direction) +
outer_width
}
pub fn auto_margin_count(&self, flow: &dyn Flow, direction: Direction) -> i32 {
let margin = flow.as_block().fragment.style.logical_margin();
let mut margin_count = 0;
match direction {
Direction::Inline => {
if margin.inline_start.is_auto() {
margin_count += 1;
}
if margin.inline_end.is_auto() {
margin_count += 1;
}
},
Direction::Block => {
if margin.block_start.is_auto() {
margin_count += 1;
}
if margin.block_end.is_auto() {
margin_count += 1;
}
},
}
margin_count
}
}
#[derive(Debug, Serialize)]
struct FlexLine {
pub range: Range<usize>,
pub free_space: Au,
pub auto_margin_count: i32,
pub cross_size: Au,
}
impl FlexLine {
pub fn new(range: Range<usize>, free_space: Au, auto_margin_count: i32) -> FlexLine {
FlexLine {
range,
auto_margin_count,
free_space,
cross_size: Au(0),
}
}
pub fn flex_resolve(&mut self, items: &mut [FlexItem], collapse: bool) {
let mut total_grow = 0.0;
let mut total_shrink = 0.0;
let mut total_scaled = 0.0;
let mut active_count = 0;
for item in items.iter_mut().filter(|i| !(i.is_strut && collapse)) {
item.main_size = max(item.min_size, min(item.base_size, item.max_size));
if (self.free_space > Au(0) &&
(item.flex_grow == 0.0 || item.base_size >= item.max_size)) ||
(self.free_space < Au(0) &&
(item.flex_shrink == 0.0 || item.base_size <= item.min_size))
{
item.is_frozen = true;
} else {
item.is_frozen = false;
total_grow += item.flex_grow;
total_shrink += item.flex_shrink;
total_scaled += item.flex_shrink * item.base_size.0 as f32;
active_count += 1;
}
}
let initial_free_space = self.free_space;
let mut total_variation = Au(1);
while total_variation != Au(0) && self.free_space != Au(0) && active_count > 0 {
self.free_space =
if self.free_space > Au(0) {
min(initial_free_space.scale_by(total_grow), self.free_space)
} else {
max(initial_free_space.scale_by(total_shrink), self.free_space)
};
total_variation = Au(0);
for item in items
.iter_mut()
.filter(|i| !i.is_frozen)
.filter(|i| !(i.is_strut && collapse))
{
let (factor, end_size) = if self.free_space > Au(0) {
(item.flex_grow / total_grow, item.max_size)
} else {
(
item.flex_shrink * item.base_size.0 as f32 / total_scaled,
item.min_size,
)
};
let variation = self.free_space.scale_by(factor);
if variation.0.abs() >= (end_size - item.main_size).0.abs() {
total_variation += end_size - item.main_size;
item.main_size = end_size;
item.is_frozen = true;
active_count -= 1;
total_shrink -= item.flex_shrink;
total_grow -= item.flex_grow;
total_scaled -= item.flex_shrink * item.base_size.0 as f32;
} else {
total_variation += variation;
item.main_size += variation;
}
}
self.free_space -= total_variation;
}
}
}
#[allow(unsafe_code)]
unsafe impl crate::flow::HasBaseFlow for FlexFlow {}
#[derive(Debug, Serialize)]
#[repr(C)]
pub struct FlexFlow {
block_flow: BlockFlow,
main_mode: Direction,
available_main_size: AxisSize,
available_cross_size: AxisSize,
lines: Vec<FlexLine>,
items: Vec<FlexItem>,
main_reverse: bool,
is_wrappable: bool,
cross_reverse: bool,
}
impl FlexFlow {
pub fn from_fragment(fragment: Fragment, flotation: Option<FloatKind>) -> FlexFlow {
let main_mode;
let main_reverse;
let is_wrappable;
let cross_reverse;
{
let style = fragment.style();
let (mode, reverse) = match style.get_position().flex_direction {
FlexDirection::Row => (Direction::Inline, false),
FlexDirection::RowReverse => (Direction::Inline, true),
FlexDirection::Column => (Direction::Block, false),
FlexDirection::ColumnReverse => (Direction::Block, true),
};
main_mode = mode;
main_reverse = reverse == style.writing_mode.is_bidi_ltr();
let (wrappable, reverse) = match fragment.style.get_position().flex_wrap {
FlexWrap::Nowrap => (false, false),
FlexWrap::Wrap => (true, false),
FlexWrap::WrapReverse => (true, true),
};
is_wrappable = wrappable;
cross_reverse = reverse;
}
FlexFlow {
block_flow: BlockFlow::from_fragment_and_float_kind(fragment, flotation),
main_mode,
available_main_size: AxisSize::Infinite,
available_cross_size: AxisSize::Infinite,
lines: Vec::new(),
items: Vec::new(),
main_reverse,
is_wrappable,
cross_reverse,
}
}
pub fn main_mode(&self) -> Direction {
self.main_mode
}
fn get_flex_line(&mut self, container_size: Au) -> Option<FlexLine> {
let start = self.lines.last().map(|line| line.range.end).unwrap_or(0);
if start == self.items.len() {
return None;
}
let mut end = start;
let mut total_line_size = Au(0);
let mut margin_count = 0;
let items = &mut self.items[start..];
let mut children = self.block_flow.base.children.random_access_mut();
for item in items {
let kid = children.get(item.index);
item.init_sizes(kid, container_size, self.main_mode);
let outer_main_size = item.outer_main_size(kid, self.main_mode);
if total_line_size + outer_main_size > container_size &&
end != start &&
self.is_wrappable
{
break;
}
margin_count += item.auto_margin_count(kid, self.main_mode);
total_line_size += outer_main_size;
end += 1;
}
let line = FlexLine::new(start..end, container_size - total_line_size, margin_count);
Some(line)
}
fn inline_mode_bubble_inline_sizes(&mut self) {
let fixed_width =
!model::style_length(&self.block_flow.fragment.style().get_position().width, None)
.is_auto();
let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
if !fixed_width {
for kid in self.block_flow.base.children.iter_mut() {
let base = kid.mut_base();
let is_absolutely_positioned =
base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
if !is_absolutely_positioned {
let flex_item_inline_sizes = IntrinsicISizes {
minimum_inline_size: base.intrinsic_inline_sizes.minimum_inline_size,
preferred_inline_size: base.intrinsic_inline_sizes.preferred_inline_size,
};
computation.union_nonbreaking_inline(&flex_item_inline_sizes);
}
}
}
self.block_flow.base.intrinsic_inline_sizes = computation.finish();
}
fn block_mode_bubble_inline_sizes(&mut self) {
let fixed_width =
!model::style_length(&self.block_flow.fragment.style().get_position().width, None)
.is_auto();
let mut computation = self.block_flow.fragment.compute_intrinsic_inline_sizes();
if !fixed_width {
for kid in self.block_flow.base.children.iter_mut() {
let base = kid.mut_base();
let is_absolutely_positioned =
base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
if !is_absolutely_positioned {
computation.content_intrinsic_sizes.minimum_inline_size = max(
computation.content_intrinsic_sizes.minimum_inline_size,
base.intrinsic_inline_sizes.minimum_inline_size,
);
computation.content_intrinsic_sizes.preferred_inline_size = max(
computation.content_intrinsic_sizes.preferred_inline_size,
base.intrinsic_inline_sizes.preferred_inline_size,
);
}
}
}
self.block_flow.base.intrinsic_inline_sizes = computation.finish();
}
fn block_mode_assign_inline_sizes(
&mut self,
_layout_context: &LayoutContext,
inline_start_content_edge: Au,
inline_end_content_edge: Au,
content_inline_size: Au,
) {
let _scope = layout_debug_scope!("flex::block_mode_assign_inline_sizes");
debug!("flex::block_mode_assign_inline_sizes");
let containing_block_mode = self.block_flow.base.writing_mode;
let container_block_size = match self.available_main_size {
AxisSize::Definite(length) => Some(length),
_ => None,
};
let container_inline_size = match self.available_cross_size {
AxisSize::Definite(length) => length,
AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
AxisSize::Infinite => content_inline_size,
};
let mut children = self.block_flow.base.children.random_access_mut();
for kid in &mut self.items {
let kid_base = children.get(kid.index).mut_base();
kid_base.block_container_explicit_block_size = container_block_size;
if kid_base
.flags
.contains(FlowFlags::INLINE_POSITION_IS_STATIC)
{
kid_base.position.start.i =
if kid_base.writing_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
inline_start_content_edge
} else {
inline_end_content_edge
};
}
kid_base.block_container_inline_size = container_inline_size;
kid_base.block_container_writing_mode = containing_block_mode;
kid_base.position.start.i = inline_start_content_edge;
}
}
fn inline_mode_assign_inline_sizes(
&mut self,
layout_context: &LayoutContext,
inline_start_content_edge: Au,
_inline_end_content_edge: Au,
content_inline_size: Au,
) {
let _scope = layout_debug_scope!("flex::inline_mode_assign_inline_sizes");
debug!("inline_mode_assign_inline_sizes");
debug!("content_inline_size = {:?}", content_inline_size);
let child_count = ImmutableFlowUtils::child_count(self as &dyn Flow) as i32;
debug!("child_count = {:?}", child_count);
if child_count == 0 {
return;
}
let inline_size = match self.available_main_size {
AxisSize::Definite(length) => length,
AxisSize::MinMax(ref constraint) => constraint.clamp(content_inline_size),
AxisSize::Infinite => content_inline_size,
};
let container_mode = self.block_flow.base.block_container_writing_mode;
self.block_flow.base.position.size.inline = inline_size;
let box_border = self
.block_flow
.fragment
.box_sizing_boundary(Direction::Block);
let parent_container_size = self
.block_flow
.explicit_block_containing_size(layout_context.shared_context());
let explicit_content_size = self
.block_flow
.explicit_block_size(parent_container_size)
.map(|x| max(x - box_border, Au(0)));
let containing_block_text_align = self
.block_flow
.fragment
.style()
.get_inherited_text()
.text_align;
while let Some(mut line) = self.get_flex_line(inline_size) {
let items = &mut self.items[line.range.clone()];
line.flex_resolve(items, false);
let justify_content = {
let justify_content = self
.block_flow
.fragment
.style()
.get_position()
.justify_content
.0
.primary()
.value();
match justify_content {
AlignFlags::AUTO | AlignFlags::NORMAL => AlignFlags::STRETCH,
_ => justify_content,
}
};
let item_count = items.len() as i32;
let mut cur_i = inline_start_content_edge;
let item_interval = if line.free_space >= Au(0) && line.auto_margin_count == 0 {
match justify_content {
AlignFlags::SPACE_BETWEEN => {
if item_count == 1 {
Au(0)
} else {
line.free_space / (item_count - 1)
}
},
AlignFlags::SPACE_AROUND => line.free_space / item_count,
_ => Au(0),
}
} else {
Au(0)
};
match justify_content {
AlignFlags::CENTER | AlignFlags::SPACE_AROUND => {
cur_i += (line.free_space - item_interval * (item_count - 1)) / 2;
},
AlignFlags::FLEX_END => {
cur_i += line.free_space;
},
_ => {},
}
let mut children = self.block_flow.base.children.random_access_mut();
for item in items.iter_mut() {
let block = children.get(item.index).as_mut_block();
block.base.block_container_writing_mode = container_mode;
block.base.block_container_inline_size = inline_size;
block.base.block_container_explicit_block_size = explicit_content_size;
block.base.text_align = containing_block_text_align;
let margin = block.fragment.style().logical_margin();
let auto_len = if line.auto_margin_count == 0 || line.free_space <= Au(0) {
Au(0)
} else {
line.free_space / line.auto_margin_count
};
let margin_inline_start = MaybeAuto::from_margin(margin.inline_start, inline_size)
.specified_or_default(auto_len);
let margin_inline_end = MaybeAuto::from_margin(margin.inline_end, inline_size)
.specified_or_default(auto_len);
let item_inline_size = item.main_size -
block.fragment.box_sizing_boundary(self.main_mode) +
block.fragment.border_padding.inline_start_end();
let item_outer_size = item_inline_size + block.fragment.margin.inline_start_end();
block.fragment.margin.inline_start = margin_inline_start;
block.fragment.margin.inline_end = margin_inline_end;
block.fragment.border_box.start.i = margin_inline_start;
block.fragment.border_box.size.inline = item_inline_size;
block.base.position.start.i = if !self.main_reverse {
cur_i
} else {
inline_start_content_edge * 2 + content_inline_size - cur_i - item_outer_size
};
block.base.position.size.inline = item_outer_size;
cur_i += item_outer_size + item_interval;
}
self.lines.push(line);
}
}
fn block_mode_assign_block_size(&mut self) {
let mut cur_b = if !self.main_reverse {
self.block_flow.fragment.border_padding.block_start
} else {
self.block_flow.fragment.border_box.size.block
};
let mut children = self.block_flow.base.children.random_access_mut();
for item in &mut self.items {
let base = children.get(item.index).mut_base();
if !self.main_reverse {
base.position.start.b = cur_b;
cur_b += base.position.size.block;
} else {
cur_b -= base.position.size.block;
base.position.start.b = cur_b;
}
}
}
fn inline_mode_assign_block_size(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug_scope!("flex::inline_mode_assign_block_size");
let line_count = self.lines.len() as i32;
let line_align = {
let line_align = self
.block_flow
.fragment
.style()
.get_position()
.align_content
.0
.primary()
.value();
match line_align {
AlignFlags::AUTO | AlignFlags::NORMAL => AlignFlags::STRETCH,
_ => line_align,
}
};
let mut cur_b = self.block_flow.fragment.border_padding.block_start;
let mut total_cross_size = Au(0);
let mut line_interval = Au(0);
{
let mut children = self.block_flow.base.children.random_access_mut();
for line in self.lines.iter_mut() {
for item in &self.items[line.range.clone()] {
let fragment = &children.get(item.index).as_block().fragment;
line.cross_size = max(
line.cross_size,
fragment.border_box.size.block + fragment.margin.block_start_end(),
);
}
total_cross_size += line.cross_size;
}
}
let box_border = self
.block_flow
.fragment
.box_sizing_boundary(Direction::Block);
let parent_container_size = self
.block_flow
.explicit_block_containing_size(layout_context.shared_context());
let explicit_content_size = self
.block_flow
.explicit_block_size(parent_container_size)
.map(|x| max(x - box_border, Au(0)));
if let Some(container_block_size) = explicit_content_size {
let free_space = container_block_size - total_cross_size;
total_cross_size = container_block_size;
if line_align == AlignFlags::STRETCH && free_space > Au(0) {
for line in self.lines.iter_mut() {
line.cross_size += free_space / line_count;
}
}
line_interval = match line_align {
AlignFlags::SPACE_BETWEEN => {
if line_count <= 1 {
Au(0)
} else {
free_space / (line_count - 1)
}
},
AlignFlags::SPACE_AROUND => {
if line_count == 0 {
Au(0)
} else {
free_space / line_count
}
},
_ => Au(0),
};
match line_align {
AlignFlags::CENTER | AlignFlags::SPACE_AROUND => {
cur_b += (free_space - line_interval * (line_count - 1)) / 2;
},
AlignFlags::FLEX_END => {
cur_b += free_space;
},
_ => {},
}
}
let align_items = {
let align_items = self
.block_flow
.fragment
.style()
.clone_align_items()
.0
.value();
match align_items {
AlignFlags::AUTO | AlignFlags::NORMAL => AlignFlags::STRETCH,
_ => align_items,
}
};
let mut children = self.block_flow.base.children.random_access_mut();
for line in &self.lines {
for item in self.items[line.range.clone()].iter_mut() {
let block = children.get(item.index).as_mut_block();
let auto_margin_count = item.auto_margin_count(block, Direction::Block);
let margin = block.fragment.style().logical_margin();
let mut margin_block_start = block.fragment.margin.block_start;
let mut margin_block_end = block.fragment.margin.block_end;
let mut free_space = line.cross_size -
block.base.position.size.block -
block.fragment.margin.block_start_end();
if auto_margin_count > 0 {
if margin.block_start.is_auto() {
margin_block_start = if free_space < Au(0) {
Au(0)
} else {
free_space / auto_margin_count
};
}
margin_block_end =
line.cross_size - margin_block_start - block.base.position.size.block;
free_space = Au(0);
}
let self_align = {
let self_align = block
.fragment
.style()
.get_position()
.align_self
.0
.0
.value();
match self_align {
AlignFlags::AUTO | AlignFlags::NORMAL => align_items,
_ => self_align,
}
};
if self_align == AlignFlags::STRETCH &&
block.fragment.style().content_block_size().is_auto()
{
free_space = Au(0);
block.base.block_container_explicit_block_size = Some(line.cross_size);
block.base.position.size.block =
line.cross_size - margin_block_start - margin_block_end;
block.fragment.border_box.size.block = block.base.position.size.block;
}
block.base.position.start.b = margin_block_start +
if !self.cross_reverse {
cur_b
} else {
self.block_flow.fragment.border_padding.block_start * 2 + total_cross_size -
cur_b -
line.cross_size
};
if free_space != Au(0) {
let flex_cross = match self_align {
AlignFlags::FLEX_END => free_space,
AlignFlags::CENTER => free_space / 2,
_ => Au(0),
};
block.base.position.start.b += if !self.cross_reverse {
flex_cross
} else {
free_space - flex_cross
};
}
}
cur_b += line_interval + line.cross_size;
}
let total_block_size =
total_cross_size + self.block_flow.fragment.border_padding.block_start_end();
self.block_flow.fragment.border_box.size.block = total_block_size;
self.block_flow.base.position.size.block = total_block_size;
}
}
impl Flow for FlexFlow {
fn class(&self) -> FlowClass {
FlowClass::Flex
}
fn as_flex(&self) -> &FlexFlow {
self
}
fn as_block(&self) -> &BlockFlow {
&self.block_flow
}
fn as_mut_block(&mut self) -> &mut BlockFlow {
&mut self.block_flow
}
fn mark_as_root(&mut self) {
self.block_flow.mark_as_root();
}
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!(
"flex::bubble_inline_sizes {:x}",
self.block_flow.base.debug_id()
);
let mut items: Vec<FlexItem> = self
.block_flow
.base
.children
.iter()
.enumerate()
.filter(|&(_, flow)| {
!flow
.as_block()
.base
.flags
.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
})
.map(|(index, flow)| FlexItem::new(index, flow))
.collect();
items.sort_by_key(|item| item.order);
self.items = items;
match self.main_mode {
Direction::Inline => self.inline_mode_bubble_inline_sizes(),
Direction::Block => self.block_mode_bubble_inline_sizes(),
}
}
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug_scope!(
"flex::assign_inline_sizes {:x}",
self.block_flow.base.debug_id()
);
debug!("assign_inline_sizes");
if !self
.block_flow
.base
.restyle_damage
.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW)
{
return;
}
self.block_flow
.initialize_container_size_for_root(layout_context.shared_context());
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
self.block_flow.compute_used_inline_size(
layout_context.shared_context(),
containing_block_inline_size,
);
if self.block_flow.base.flags.is_float() {
self.block_flow
.float
.as_mut()
.unwrap()
.containing_inline_size = containing_block_inline_size
}
let (available_block_size, available_inline_size) = {
let style = &self.block_flow.fragment.style;
let (specified_block_size, specified_inline_size) = if style.writing_mode.is_vertical()
{
(&style.get_position().width, &style.get_position().height)
} else {
(&style.get_position().height, &style.get_position().width)
};
let available_inline_size = AxisSize::new(
specified_inline_size,
Some(self.block_flow.base.block_container_inline_size),
style.min_inline_size(),
style.max_inline_size(),
);
let available_block_size = AxisSize::new(
specified_block_size,
self.block_flow.base.block_container_explicit_block_size,
style.min_block_size(),
style.max_block_size(),
);
(available_block_size, available_inline_size)
};
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
self.block_flow.fragment.border_padding.inline_start;
debug!(
"inline_start_content_edge = {:?}",
inline_start_content_edge
);
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
let inline_end_content_edge = self.block_flow.fragment.margin.inline_end +
self.block_flow.fragment.border_padding.inline_end;
debug!("padding_and_borders = {:?}", padding_and_borders);
debug!(
"self.block_flow.fragment.border_box.size.inline = {:?}",
self.block_flow.fragment.border_box.size.inline
);
let content_inline_size =
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
match self.main_mode {
Direction::Inline => {
self.available_main_size = available_inline_size;
self.available_cross_size = available_block_size;
self.inline_mode_assign_inline_sizes(
layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
)
},
Direction::Block => {
self.available_main_size = available_block_size;
self.available_cross_size = available_inline_size;
self.block_mode_assign_inline_sizes(
layout_context,
inline_start_content_edge,
inline_end_content_edge,
content_inline_size,
)
},
}
}
fn assign_block_size(&mut self, layout_context: &LayoutContext) {
match self.main_mode {
Direction::Inline => {
self.inline_mode_assign_block_size(layout_context);
let block_start =
AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_start);
let block_end =
AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_end);
self.block_flow.base.collapsible_margins =
CollapsibleMargins::Collapse(block_start, block_end);
if (&*self as &dyn Flow).contains_roots_of_absolute_flow_tree() {
let assign_abs_b_sizes =
AbsoluteAssignBSizesTraversal(layout_context.shared_context());
assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
}
},
Direction::Block => {
self.block_flow.assign_block_size_block_base(
layout_context,
None,
MarginsMayCollapseFlag::MarginsMayNotCollapse,
);
self.block_mode_assign_block_size();
},
}
}
fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
self.block_flow
.compute_stacking_relative_position(layout_context)
}
fn place_float_if_applicable<'a>(&mut self) {
self.block_flow.place_float_if_applicable()
}
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
self.block_flow
.update_late_computed_inline_position_if_necessary(inline_position)
}
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
self.block_flow
.update_late_computed_block_position_if_necessary(block_position)
}
fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
self.as_mut_block()
.build_display_list_for_block(state, BorderPaintingMode::Separate)
}
fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
self.block_flow.collect_stacking_contexts(state);
}
fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
self.block_flow.repair_style(new_style)
}
fn compute_overflow(&self) -> Overflow {
self.block_flow.compute_overflow()
}
fn contains_roots_of_absolute_flow_tree(&self) -> bool {
self.block_flow.contains_roots_of_absolute_flow_tree()
}
fn is_absolute_containing_block(&self) -> bool {
self.block_flow.is_absolute_containing_block()
}
fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
self.block_flow.generated_containing_block_size(flow)
}
fn iterate_through_fragment_border_boxes(
&self,
iterator: &mut dyn FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>,
) {
self.block_flow.iterate_through_fragment_border_boxes(
iterator,
level,
stacking_context_position,
);
}
fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
self.block_flow.mutate_fragments(mutator);
}
}