/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/layout/flow/root.rs
230 строк
9 KB
Oriol Brufau
layout: Implement block-axis centering for buttons (#46590)
19 июл 2026, 10:25
Не верифицирован
19 июл 2026, 10:25
5f837eb
Код
Авторство
О чём код?
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use app_units::Au; use euclid::Rect; use euclid::default::Size2D as UntypedSize2D; use layout_api::{AxesOverflow, LayoutElement, LayoutNode}; use malloc_size_of_derive::MallocSizeOf; use paint_api::display_list::AxesScrollSensitivity; use script::layout_dom::ServoLayoutNode; 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 { /// Contains typically exactly one block-level box, which was generated by the root element. /// There may be zero if that element has `display: none`. root: BlockFormattingContext, /// Whether or not the viewport should be sensitive to scrolling input events in two axes 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 boxes = construct_for_root_element(context, root_element); // Zero box for `:root { display: none }`, one for the root element otherwise. 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, }, // From https://www.w3.org/TR/css-overflow-3/#overflow-propagation: // > If visible is applied to the viewport, it must be interpreted as auto. // > If clip is applied to the viewport, it must be interpreted as hidden. viewport_overflow: viewport_overflow.to_scrollable(), } } fn viewport_overflow( root_element: ServoLayoutNode<'_>, root_box: Option<&ArcRefCell<BlockLevelBox>>, ) -> AxesOverflow { // From https://www.w3.org/TR/css-overflow-3/#overflow-propagation: // > UAs must apply the overflow-* values set on the root element to the viewport when the // > root element’s display value is not none. However, when the root element is an [HTML] // > html element (including XML syntax for HTML) whose overflow value is visible (in both // > axes), and that element has as a child a body element whose display value is also not // > none, user agents must instead apply the overflow-* values of the first such child // > element to the viewport. The element from which the value is propagated must then have a // > used overflow value of visible. // If there is no root box, the root element has `display: none`, so don't propagate. // The spec isn't very clear about what value to use, but the initial value seems fine. // See https://github.com/w3c/csswg-drafts/issues/12649 let Some(root_box) = root_box else { return AxesOverflow::default(); }; let propagate_from_body = || { // Unlike what the spec implies, we stop iterating when we find the first <body>, // even if it's not suitable because it lacks a box. This matches other browsers. // See https://github.com/w3c/csswg-drafts/issues/12644 let body = root_element.flat_tree_children().find(|child| { child .as_element() .is_some_and(|element| element.is_body_element_of_html_element_root()) })?; // We only propagate from the <body> if it generates a box. The spec only checks for // `display: none`, but other browsers don't propagate for `display: contents` either. // See https://github.com/w3c/csswg-drafts/issues/12643 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 && 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: ServoLayoutNode<'_>, ) -> 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 because the style crate adjusts the computed values: // https://drafts.csswg.org/css-display-3/#transformations // “'display' of 'contents' computes to 'block' on the root element” unreachable!() }, // The root element is blockified, ignore DisplayOutside 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_arc(); // FIXME: use the document’s mode: // https://drafts.csswg.org/css-writing-modes/#principal-flow 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(), &initial_containing_block.size.block.into(), None, ); let mut root_fragments = independent_layout.fragments; // Zero box for `:root { display: none }`, one for the root element otherwise. assert!(root_fragments.len() <= 1); // There may be more fragments at the top-level // (for positioned boxes whose containing is the initial containing block) // but only if there was one fragment for the root element. 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( root_fragments, physical_containing_block, viewport_scroll_sensitivity, ) } }