use app_units::Au;
use euclid::Rect;
use euclid::default::Size2D as UntypedSize2D;
use layout_api::AxesOverflow;
use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use malloc_size_of_derive::MallocSizeOf;
use paint_api::display_list::AxesScrollSensitivity;
use script::layout_dom::{ServoLayoutNode, ServoThreadSafeLayoutNode};
use style::values::computed::Overflow;
use style_traits::CSSPixel;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::dom::{LayoutBox, NodeExt};
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::flow::float::FloatBox;
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::{FragmentFlags, FragmentTree};
use crate::geom::{LogicalVec2, PhysicalSize};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::style_ext::Display;
use crate::{DefiniteContainingBlock, PropagatedBoxTreeData};
#[derive(MallocSizeOf)]
pub struct BoxTree {
root: BlockFormattingContext,
pub(crate) viewport_overflow: AxesOverflow,
}
impl BoxTree {
#[servo_tracing::instrument(name = "Box Tree Construction", skip_all)]
pub(crate) fn construct(context: &LayoutContext, root_element: ServoLayoutNode<'_>) -> Self {
let root_element = root_element.to_threadsafe();
let boxes = construct_for_root_element(context, root_element);
assert!(boxes.len() <= 1);
let viewport_overflow = Self::viewport_overflow(root_element, boxes.first());
let contents = BlockContainer::BlockLevelBoxes(boxes);
let contains_floats = contents.contains_floats();
Self {
root: BlockFormattingContext {
contents,
contains_floats,
},
viewport_overflow: viewport_overflow.to_scrollable(),
}
}
fn viewport_overflow(
root_element: ServoThreadSafeLayoutNode<'_>,
root_box: Option<&ArcRefCell<BlockLevelBox>>,
) -> AxesOverflow {
let Some(root_box) = root_box else {
return AxesOverflow::default();
};
let propagate_from_body = || {
let body = root_element.children().find(|child| {
child
.as_element()
.is_some_and(|element| element.is_body_element_of_html_element_root())
})?;
let body_layout_data = body.inner_layout_data()?;
let mut body_box = body_layout_data.self_box.borrow_mut();
body_box.as_mut()?.with_base_mut(|base| {
base.base_fragment_info
.flags
.insert(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT);
AxesOverflow::from(&*base.style)
})
};
root_box.borrow_mut().with_base_mut(|base| {
let root_overflow = AxesOverflow::from(&*base.style);
if root_overflow.x == Overflow::Visible && root_overflow.y == Overflow::Visible {
if let Some(body_overflow) = propagate_from_body() {
return body_overflow;
}
}
base.base_fragment_info
.flags
.insert(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT);
root_overflow
})
}
}
fn construct_for_root_element(
context: &LayoutContext,
root_element: ServoThreadSafeLayoutNode<'_>,
) -> Vec<ArcRefCell<BlockLevelBox>> {
let info = NodeAndStyleInfo::new(root_element, root_element.style(&context.style_context));
let box_style = info.style.get_box();
let display_inside = match Display::from(box_style.display) {
Display::None => return Vec::new(),
Display::Contents => {
unreachable!()
},
Display::GeneratingBox(display_generating_box) => display_generating_box.display_inside(),
};
let contents = Contents::for_element(root_element, context);
let propagated_data = PropagatedBoxTreeData::default();
let root_box = if box_style.position.is_absolutely_positioned() {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(ArcRefCell::new(
AbsolutelyPositionedBox::construct(context, &info, display_inside, contents),
))
} else if box_style.float.is_floating() {
BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
context,
&info,
display_inside,
contents,
propagated_data,
))
} else {
BlockLevelBox::Independent(IndependentFormattingContext::construct(
context,
&info,
display_inside,
contents,
propagated_data,
))
};
let root_box = ArcRefCell::new(root_box);
root_element
.box_slot()
.set(LayoutBox::BlockLevel(root_box.clone()));
vec![root_box]
}
impl BoxTree {
#[servo_tracing::instrument(name = "Fragment Tree Construction", skip_all)]
pub(crate) fn layout(
&self,
layout_context: &LayoutContext,
viewport: UntypedSize2D<Au>,
) -> FragmentTree {
let style = layout_context
.style_context
.stylist
.device()
.default_computed_values();
let physical_containing_block: Rect<Au, CSSPixel> =
PhysicalSize::from_untyped(viewport).into();
let initial_containing_block = DefiniteContainingBlock {
size: LogicalVec2 {
inline: physical_containing_block.size.width,
block: physical_containing_block.size.height,
},
style,
};
let mut positioning_context = PositioningContext::default();
let independent_layout = self.root.layout(
layout_context,
&mut positioning_context,
&(&initial_containing_block).into(),
);
let mut root_fragments = independent_layout.fragments;
assert!(root_fragments.len() <= 1);
positioning_context.layout_initial_containing_block_children(
layout_context,
&initial_containing_block,
&mut root_fragments,
);
let viewport_scroll_sensitivity = AxesScrollSensitivity {
x: self.viewport_overflow.x.into(),
y: self.viewport_overflow.y.into(),
};
FragmentTree::new(
layout_context,
root_fragments,
physical_containing_block,
viewport_scroll_sensitivity,
)
}
}