Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 0 additions & 127 deletions Libraries/LibWeb/Layout/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,128 +42,6 @@

namespace Web::Layout {

static RustFFI::FfiTableDisplay table_display(CSS::Display display)
{
if (display.is_table_inside())
return RustFFI::FfiTableDisplay::TableRoot;
if (display.is_table_row_group())
return RustFFI::FfiTableDisplay::TableRowGroup;
if (display.is_table_header_group())
return RustFFI::FfiTableDisplay::TableHeaderGroup;
if (display.is_table_footer_group())
return RustFFI::FfiTableDisplay::TableFooterGroup;
if (display.is_table_column_group())
return RustFFI::FfiTableDisplay::TableColumnGroup;
if (display.is_table_column())
return RustFFI::FfiTableDisplay::TableColumn;
if (display.is_table_row())
return RustFFI::FfiTableDisplay::TableRow;
if (display.is_table_cell())
return RustFFI::FfiTableDisplay::TableCell;
if (display.is_table_caption())
return RustFFI::FfiTableDisplay::TableCaption;
return RustFFI::FfiTableDisplay::Other;
}

static u8 display_bits(CSS::ComputedValues const& computed_values)
{
auto display = computed_values.display();
u8 bits = 0;
auto set = [&](RustFFI::NodeDisplayFlag flag, bool value) {
if (value)
bits |= static_cast<u8>(flag);
};
set(RustFFI::NodeDisplayFlag::InlineOutside, display.is_inline_outside());
set(RustFFI::NodeDisplayFlag::FlowInside, display.is_flow_inside());
set(RustFFI::NodeDisplayFlag::FlexInside, display.is_flex_inside());
set(RustFFI::NodeDisplayFlag::GridInside, display.is_grid_inside());
set(RustFFI::NodeDisplayFlag::MathInside, display.is_math_inside());
set(RustFFI::NodeDisplayFlag::Floating, computed_values.float_() != CSS::Float::None);
auto position = computed_values.position();
set(RustFFI::NodeDisplayFlag::AbsolutelyPositioned,
position == CSS::Positioning::Absolute || position == CSS::Positioning::Fixed);
set(RustFFI::NodeDisplayFlag::BlockOutsideBeforeBoxTypeTransformation,
computed_values.display_before_box_type_transformation().is_block_outside());
return bits;
}

// https://drafts.csswg.org/css-contain-2/#containment-types
// Mirrors NodeWithStyle::has_layout_containment() / has_paint_containment() with the
// is_replaced_box() escape dropped: the stamped flag is only consulted after the Rust
// side has already excluded replaced boxes from creating a block formatting context.
static bool containment_applies_to_principal_box(CSS::Display display)
{
if (display.is_internal_table() && !display.is_table_cell())
return false;
if (display.is_inline_outside() && display.is_flow_inside())
return false;
return true;
}

// The computed-style-only half of the block-formatting-context predicate. Terms that
// need the node kind, the DOM, or the parent (replaced boxes, SVG foreignObject,
// table/flex/grid insides, the root element, fieldsets, button layout, and flex/grid
// parents) are evaluated by the Rust side over stamped NodeData instead: the first
// mirror runs from the NodeWithStyle constructor, before the most-derived class has
// assigned its node kind.
static bool own_computed_style_establishes_block_formatting_context(CSS::ComputedValues const& computed_values)
{
auto display = computed_values.display();

// The float term is deliberately absent: floating only establishes a block
// formatting context for non-flex-items, and the IsFlexItem flag is written
// during layout, after this stamp. The Rust composite evaluates that term
// from the Floating display bit and the live IsFlexItem flag instead.

// Absolutely positioned elements (elements where position is absolute or fixed).
auto position = computed_values.position();
if (position == CSS::Positioning::Absolute || position == CSS::Positioning::Fixed)
return true;

// Inline-blocks (elements with display: inline-block).
if (display.is_inline_block())
return true;

// Table cells and table captions.
if (display.is_table_cell() || display.is_table_caption())
return true;

// Block elements where overflow has a value other than visible and clip.
CSS::Overflow overflow_x = computed_values.overflow_x();
if (overflow_x != CSS::Overflow::Visible && overflow_x != CSS::Overflow::Clip)
return true;
CSS::Overflow overflow_y = computed_values.overflow_y();
if (overflow_y != CSS::Overflow::Visible && overflow_y != CSS::Overflow::Clip)
return true;

// display: flow-root.
if (display.is_flow_root_inside())
return true;

// https://drafts.csswg.org/css-contain-2/#containment-types
// 1. The layout containment box establishes an independent formatting context.
// 4. The paint containment box establishes an independent formatting context.
bool content_visibility_forces_containment = computed_values.content_visibility() == CSS::ContentVisibility::Auto;
if ((computed_values.contain().layout_containment || computed_values.contain().paint_containment
|| content_visibility_forces_containment)
&& containment_applies_to_principal_box(display))
return true;

// https://drafts.csswg.org/css-conditional-5/#valdef-container-type-size
// Applies style containment and size containment to the principal box, and establishes an independent formatting
// context.
if (computed_values.container_type().is_size_container || computed_values.container_type().is_inline_size_container)
return true;

// https://drafts.csswg.org/css-multicol-2/#the-multi-column-model
// An element whose 'column-width', 'column-count', or 'column-height' property is not 'auto' establishes a multi-
// column container (or multicol container for short), and therefore acts as a container for multi-column layout.
if (!computed_values.column_width().is_auto() || !computed_values.column_count().is_auto())
return true;

return false;
}

NodeArenaAllocation::NodeArenaAllocation(DOM::Document& document)
: m_arena(document.layout_node_arena())
{
Expand Down Expand Up @@ -1089,11 +967,6 @@ void NodeWithStyle::set_computed_values(NonnullRefPtr<CSS::ComputedValues const>
void NodeWithStyle::mirror_computed_values_to_node_data()
{
node_data().style = m_computed_values.ptr();
node_data().table_display = table_display(m_computed_values->display());
node_data().table_display_before = table_display(m_computed_values->display_before_box_type_transformation());
node_data().display_bits = display_bits(*m_computed_values);
set_flag(RustFFI::NodeFlag::OwnStyleEstablishesBlockFormattingContext,
own_computed_style_establishes_block_formatting_context(*m_computed_values));

RustFFI::FfiStylePayloads style_payloads {};
m_computed_values->fill_style_group_payloads({ style_payloads.groups, array_size(style_payloads.groups) });
Expand Down
11 changes: 3 additions & 8 deletions Libraries/LibWeb/Layout/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,14 @@ static_assert(offsetof(RustFFI::NodeData, generated_for) == 29);
static_assert(offsetof(RustFFI::NodeData, intrinsic_cache_epoch) == 30);
static_assert(offsetof(RustFFI::NodeData, flags) == 32);
static_assert(offsetof(RustFFI::NodeData, initial_quote_nesting_level) == 36);
static_assert(offsetof(RustFFI::NodeData, table_display) == 40);
static_assert(offsetof(RustFFI::NodeData, table_display_before) == 41);
static_assert(offsetof(RustFFI::NodeData, display_bits) == 42);
static_assert(offsetof(RustFFI::NodeData, slot_generation) == 43);
static_assert(offsetof(RustFFI::NodeData, table_column_span) == 44);
static_assert(offsetof(RustFFI::NodeData, table_row_span) == 46);
static_assert(offsetof(RustFFI::NodeData, slot_generation) == 40);
static_assert(offsetof(RustFFI::NodeData, table_column_span) == 42);
static_assert(offsetof(RustFFI::NodeData, table_row_span) == 44);
static_assert(offsetof(RustFFI::NodeData, style) == 48);
static_assert(offsetof(RustFFI::NodeData, shell) == 56);

static_assert(sizeof(RustFFI::NodeKind) == sizeof(u8));
static_assert(sizeof(RustFFI::NodeFlag) == sizeof(u32));
static_assert(sizeof(RustFFI::NodeDisplayFlag) == sizeof(u8));
static_assert(sizeof(RustFFI::FfiTableDisplay) == sizeof(u8));

class NodeKindSetter;

Expand Down
2 changes: 0 additions & 2 deletions Libraries/LibWeb/Rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -988,10 +988,8 @@ fn main() -> Result<(), Box<dyn Error>> {
tree_builder_config.export.include = vec![
"FfiNodeKindFacts".to_string(),
"FfiStylePayloads".to_string(),
"FfiTableDisplay".to_string(),
"NodeAllocation".to_string(),
"NodeData".to_string(),
"NodeDisplayFlag".to_string(),
"NodeFlag".to_string(),
"NodeKind".to_string(),
"NodeSlotId".to_string(),
Expand Down
56 changes: 37 additions & 19 deletions Libraries/LibWeb/Rust/src/layout/formatting_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2907,6 +2907,14 @@ impl FfiLayoutFcCallbacks {
unsafe { &*std::ptr::from_ref(payloads) }
}

pub(crate) fn style_reader_if_styled(&self, node: Node) -> Option<StyleReader<'static>> {
let payloads = self.arena().style_payloads(node)?;
// SAFETY: The document arena outlives the layout pass, and the mirror
// is only rewritten between passes: set_computed_values verifies no
// pass is running and no layout node is created mid-pass.
Some(StyleReader::new(unsafe { &*std::ptr::from_ref(payloads) }))
}

pub(crate) fn can_skip_is_anonymous_text_run(&self, node: Node) -> bool {
let data = self.node_data(node);
if !crate::layout::has_flag(data, NodeFlag::Anonymous) || data.generated_for != 0 {
Expand Down Expand Up @@ -3070,7 +3078,8 @@ impl std::ops::DerefMut for FormattingContextInstance<'_> {

pub(crate) fn formatting_context_type_created_by_node_data(
data: &NodeData,
parent_data: Option<&NodeData>,
style: Option<StyleReader<'_>>,
parent_style: Option<StyleReader<'_>>,
) -> Option<FfiFormattingContextType> {
if data.kind == crate::layout::node_data::NodeKind::SVGSVGBox {
return Some(FfiFormattingContextType::Svg);
Expand All @@ -3087,49 +3096,55 @@ pub(crate) fn formatting_context_type_created_by_node_data(
return None;
}
if crate::layout::has_flag(data, NodeFlag::IsReplacedElement)
&& data.table_display_before != crate::layout::node_data::FfiTableDisplay::Other
&& style.is_some_and(|style| style.table_display_before() != FfiTableDisplay::Other)
{
return Some(if crate::layout::kind_is_block_container(data.kind) {
FfiFormattingContextType::Block
} else {
FfiFormattingContextType::InternalReplaced
});
}
if crate::layout::has_display_flag(data, crate::layout::node_data::NodeDisplayFlag::FlexInside) {
let display = style.map(|style| style.display());
if display.is_some_and(|display| display.is_flex_inside()) {
return Some(FfiFormattingContextType::Flex);
}
if data.table_display == crate::layout::node_data::FfiTableDisplay::TableRoot {
let table_display = display.map_or(FfiTableDisplay::Other, crate::layout::table_display_of);
if table_display == FfiTableDisplay::TableRoot {
return Some(FfiFormattingContextType::Table);
}
if crate::layout::has_display_flag(data, crate::layout::node_data::NodeDisplayFlag::GridInside) {
if display.is_some_and(|display| display.is_grid_inside()) {
return Some(FfiFormattingContextType::Grid);
}
if crate::layout::has_display_flag(data, crate::layout::node_data::NodeDisplayFlag::MathInside)
|| crate::layout::node_creates_block_formatting_context(data, parent_data)
if display.is_some_and(|display| display.is_math_inside())
|| crate::layout::node_creates_block_formatting_context(data, style, parent_style)
{
return Some(FfiFormattingContextType::Block);
}
if crate::layout::has_flag(data, NodeFlag::ChildrenAreInline)
|| matches!(
data.table_display,
crate::layout::node_data::FfiTableDisplay::TableColumn
| crate::layout::node_data::FfiTableDisplay::TableColumnGroup
| crate::layout::node_data::FfiTableDisplay::TableRow
| crate::layout::node_data::FfiTableDisplay::TableRowGroup
| crate::layout::node_data::FfiTableDisplay::TableHeaderGroup
| crate::layout::node_data::FfiTableDisplay::TableFooterGroup
table_display,
FfiTableDisplay::TableColumn
| FfiTableDisplay::TableColumnGroup
| FfiTableDisplay::TableRow
| FfiTableDisplay::TableRowGroup
| FfiTableDisplay::TableHeaderGroup
| FfiTableDisplay::TableFooterGroup
)
{
return None;
}
if !crate::layout::has_display_flag(data, crate::layout::node_data::NodeDisplayFlag::FlowInside) {
if !display.is_some_and(|display| display.is_flow_inside()) {
return Some(FfiFormattingContextType::InternalDummy);
}
None
}

pub(crate) fn formatting_context_type_created_by_box(facts: NodeFacts<'_>) -> Option<FfiFormattingContextType> {
formatting_context_type_created_by_node_data(facts.data(), facts.parent_data())
formatting_context_type_created_by_node_data(
facts.data(),
facts.style_reader_if_styled(),
facts.parent_style_reader_if_styled(),
)
}

#[derive(Clone, Copy)]
Expand All @@ -3147,9 +3162,12 @@ pub extern "C" fn rust_layout_formatting_context_type_for_box(facts: FfiFormatti
let arena = unsafe { LayoutNodeArena::from_handle(facts.arena) };
// SAFETY: The caller supplies the live box's arena slot.
let data = unsafe { &*arena.data(facts.node) };
// SAFETY: Parent links resolve within the same live arena.
let parent_data = (!data.parent.is_invalid()).then(|| unsafe { &*arena.data(data.parent) });
formatting_context_type_created_by_node_data(data, parent_data)
let style = arena.style_payloads(facts.node).map(StyleReader::new);
let parent_style = (!data.parent.is_invalid())
.then(|| arena.style_payloads(data.parent))
.flatten()
.map(StyleReader::new);
formatting_context_type_created_by_node_data(data, style, parent_style)
.map(|type_| type_ as u8)
.unwrap_or(NO_FORMATTING_CONTEXT)
})
Expand Down
Loading