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
59 changes: 30 additions & 29 deletions Libraries/LibWeb/Layout/LayoutRustBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,31 @@ static OwnPtr<GridLayoutData> build_grid_layout_data(RustFFI::FfiGridLayoutData
return data;
}

static CSS::BorderData from_ffi_border_data(RustFFI::FfiBorderData const&);

static OwnPtr<Painting::CollapsedTableBorders> build_collapsed_table_borders(RustFFI::FfiCollapsedTableBorders const& ffi_borders)
{
auto borders = make<Painting::CollapsedTableBorders>();
borders->row_offsets.ensure_capacity(ffi_borders.row_count + 1);
for (size_t i = 0; i <= ffi_borders.row_count; ++i)
borders->row_offsets.unchecked_append(CSSPixels::from_raw(ffi_borders.row_offsets[i]));
borders->column_offsets.ensure_capacity(ffi_borders.column_count + 1);
for (size_t i = 0; i <= ffi_borders.column_count; ++i)
borders->column_offsets.unchecked_append(CSSPixels::from_raw(ffi_borders.column_offsets[i]));
auto build_edges = [](Vector<Painting::CollapsedBorderEdge>& edges, RustFFI::FfiCollapsedBorderEdge const* ffi_edges, size_t count) {
edges.ensure_capacity(count);
for (size_t i = 0; i < count; ++i) {
edges.unchecked_append({
.border = from_ffi_border_data(ffi_edges[i].border_data),
.source_order = ffi_edges[i].source_order,
});
}
};
build_edges(borders->horizontal_edges, ffi_borders.horizontal_edges, (ffi_borders.row_count + 1) * ffi_borders.column_count);
build_edges(borders->vertical_edges, ffi_borders.vertical_edges, (ffi_borders.column_count + 1) * ffi_borders.row_count);
return borders;
}

static RustFFI::FfiAffineTransform to_ffi_affine_transform(Gfx::AffineTransform const& transform)
{
return {
Expand Down Expand Up @@ -709,16 +734,6 @@ struct LayoutRustBridge::LineCommitContext {
Vector<Painting::InlineBoxPiece> pieces;
};

static CSS::BorderData from_ffi_border_data(RustFFI::FfiBorderData const&);

static Painting::Paintable::BorderDataWithElementKind from_ffi_border_data_with_element_kind(RustFFI::FfiBorderDataWithElementKind const& border)
{
return {
.border_data = from_ffi_border_data(border.border_data),
.element_kind = static_cast<Painting::Paintable::ConflictingElementKind>(border.element_kind),
};
}

RustFFI::FfiCommitSink LayoutRustBridge::commit_sink()
{
return {
Expand Down Expand Up @@ -833,19 +848,8 @@ RustFFI::FfiCommitSink LayoutRustBridge::commit_sink()
CSSPixels::from_raw(metrics.content_offset.y),
});
if (metrics.has_containing_line_box_index)
paintable.set_containing_line_box_index(metrics.containing_line_box_index); },
.set_override_borders = [](void*, void* paintable_pointer, RustFFI::FfiBordersData borders) { static_cast<Painting::Paintable*>(paintable_pointer)->set_override_borders_data({
.top = from_ffi_border_data_with_element_kind(borders.top),
.right = from_ffi_border_data_with_element_kind(borders.right),
.bottom = from_ffi_border_data_with_element_kind(borders.bottom),
.left = from_ffi_border_data_with_element_kind(borders.left),
}); },
.set_table_cell_coordinates = [](void*, void* paintable_pointer, RustFFI::FfiTableCellCoordinates coordinates) { static_cast<Painting::Paintable*>(paintable_pointer)->set_table_cell_coordinates({
.row_index = coordinates.row_index,
.column_index = coordinates.column_index,
.row_span = coordinates.row_span,
.column_span = coordinates.column_span,
}); },
paintable.set_containing_line_box_index(metrics.containing_line_box_index);
paintable.set_uses_collapsing_borders_model(metrics.uses_collapsing_borders_model); },
.begin_line_data = [](void* context, void* paintable_pointer) {
auto& bridge = *static_cast<LayoutRustBridge*>(context);
VERIFY(!bridge.m_line_commit_context);
Expand Down Expand Up @@ -965,6 +969,9 @@ RustFFI::FfiCommitSink LayoutRustBridge::commit_sink()
auto& paintable = *static_cast<Painting::Paintable*>(paintable_pointer);
paintable.set_used_values_for_grid_template_columns(build_used_grid_track_list(*columns));
paintable.set_used_values_for_grid_template_rows(build_used_grid_track_list(*rows)); },
.set_collapsed_table_borders = [](void*, void* paintable_pointer, RustFFI::FfiCollapsedTableBorders const* borders) {
VERIFY(borders);
static_cast<Painting::Paintable*>(paintable_pointer)->set_collapsed_table_borders(build_collapsed_table_borders(*borders)); },
.finish_node = [](void*, void* node_pointer, void* paintable_pointer, void* parent_paintable_pointer, void* insert_before_paintable_pointer) {
auto& node = *static_cast<Node*>(node_pointer);
auto* paintable = static_cast<Painting::Paintable*>(paintable_pointer);
Expand Down Expand Up @@ -1037,12 +1044,6 @@ bool can_replay_saved_abspos_layout_inputs_after_style_change(Box const& box)

RustFFI::FfiLayoutFcCallbacks LayoutRustBridge::formatting_context_callbacks()
{
static_assert(to_underlying(Painting::Paintable::ConflictingElementKind::Cell) == 0);
static_assert(to_underlying(Painting::Paintable::ConflictingElementKind::Row) == 1);
static_assert(to_underlying(Painting::Paintable::ConflictingElementKind::RowGroup) == 2);
static_assert(to_underlying(Painting::Paintable::ConflictingElementKind::Column) == 3);
static_assert(to_underlying(Painting::Paintable::ConflictingElementKind::ColumnGroup) == 4);
static_assert(to_underlying(Painting::Paintable::ConflictingElementKind::Table) == 5);
static_assert(to_underlying(FlexLayoutGrowthState::Growing) == 0);
static_assert(to_underlying(FlexLayoutGrowthState::Shrinking) == 1);
static_assert(to_underlying(FlexLayoutClampState::Unclamped) == 0);
Expand Down
30 changes: 30 additions & 0 deletions Libraries/LibWeb/Painting/CollapsedTableBorders.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <AK/Vector.h>
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/PixelUnits.h>

namespace Web::Painting {

struct CollapsedBorderEdge {
CSS::BorderData border;
u32 source_order { 0 };
};

struct CollapsedTableBorders {
Vector<CSSPixels> row_offsets;
Vector<CSSPixels> column_offsets;
Vector<CollapsedBorderEdge> horizontal_edges;
Vector<CollapsedBorderEdge> vertical_edges;

size_t row_count() const { return row_offsets.size() - 1; }
size_t column_count() const { return column_offsets.size() - 1; }
};

}
23 changes: 6 additions & 17 deletions Libraries/LibWeb/Painting/Paintable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1155,8 +1155,8 @@ void Paintable::reset_for_relayout()
m_box_model = {};

m_overflow_data.clear();
m_override_borders_data.clear();
m_table_cell_coordinates.clear();
m_collapsed_table_borders = nullptr;
m_uses_collapsing_borders_model = false;
m_containing_line_box_index.clear();
m_sticky_insets = nullptr;

Expand Down Expand Up @@ -1514,7 +1514,7 @@ CSSPixelRect Paintable::compute_absolute_border_box_rect() const
{
auto padded_rect = this->absolute_padding_box_rect();
CSSPixelRect rect;
auto use_collapsing_borders_model = override_borders_data().has_value();
auto use_collapsing_borders_model = uses_collapsing_borders_model();
// Implement the collapsing border model https://www.w3.org/TR/CSS22/tables.html#collapsing-borders.
auto border_top = box_model().border.top;
auto border_bottom = box_model().border.bottom;
Expand Down Expand Up @@ -1902,12 +1902,11 @@ void Paintable::paint(DisplayListRecordingContext& context, PaintPhase phase) co
paint_box_shadow(context);
}

auto const is_table_with_collapsed_borders = display().is_table_inside() && layout_node().border_collapse() == CSS::BorderCollapse::Collapse;
if (!display().is_table_cell() && !is_table_with_collapsed_borders && phase == PaintPhase::Border) {
if (phase == PaintPhase::Border && !uses_collapsing_borders_model() && !empty_cells_property_applies()) {
paint_border(context);
}

if ((display().is_table_inside() || layout_node().border_collapse() == CSS::BorderCollapse::Collapse) && phase == PaintPhase::TableCollapsedBorder) {
if (phase == PaintPhase::TableCollapsedBorder && collapsed_table_borders()) {
paint_table_borders(context, *this);
}

Expand Down Expand Up @@ -2284,16 +2283,6 @@ Optional<int> Paintable::effective_z_index() const
return {};
}

BordersData Paintable::remove_element_kind_from_borders_data(Paintable::BordersDataWithElementKind borders_data)
{
return {
.top = borders_data.top.border_data,
.right = borders_data.right.border_data,
.bottom = borders_data.bottom.border_data,
.left = borders_data.left.border_data,
};
}

enum class BorderImageTrack {
Start,
Center,
Expand Down Expand Up @@ -2633,7 +2622,7 @@ static bool paint_border_image(DisplayListRecordingContext& context, Paintable c

void Paintable::paint_border(DisplayListRecordingContext& context) const
{
auto borders_data = m_override_borders_data.has_value() ? remove_element_kind_from_borders_data(m_override_borders_data.value()) : BordersData {
BordersData borders_data {
.top = box_model().border.top == 0 ? CSS::BorderData() : layout_node().border_top(),
.right = box_model().border.right == 0 ? CSS::BorderData() : layout_node().border_right(),
.bottom = box_model().border.bottom == 0 ? CSS::BorderData() : layout_node().border_bottom(),
Expand Down
43 changes: 7 additions & 36 deletions Libraries/LibWeb/Painting/Paintable.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <LibWeb/Painting/BoxModelMetrics.h>
#include <LibWeb/Painting/ChromeMetrics.h>
#include <LibWeb/Painting/ChromeWidget.h>
#include <LibWeb/Painting/CollapsedTableBorders.h>
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListCommand.h>
#include <LibWeb/Painting/HitTestResult.h>
Expand Down Expand Up @@ -354,41 +355,11 @@ class WEB_API Paintable
RefPtr<Scrollbar> scrollbar(ScrollDirection) const;
NonnullRefPtr<Scrollbar> ensure_scrollbar(ScrollDirection);

enum class ConflictingElementKind {
Cell,
Row,
RowGroup,
Column,
ColumnGroup,
Table,
};

struct BorderDataWithElementKind {
CSS::BorderData border_data;
ConflictingElementKind element_kind;
};

struct BordersDataWithElementKind {
BorderDataWithElementKind top;
BorderDataWithElementKind right;
BorderDataWithElementKind bottom;
BorderDataWithElementKind left;
};

void set_override_borders_data(BordersDataWithElementKind const& override_borders_data) { m_override_borders_data = override_borders_data; }
Optional<BordersDataWithElementKind> const& override_borders_data() const { return m_override_borders_data; }

static BordersData remove_element_kind_from_borders_data(Paintable::BordersDataWithElementKind borders_data);

struct TableCellCoordinates {
size_t row_index;
size_t column_index;
size_t row_span;
size_t column_span;
};
void set_uses_collapsing_borders_model(bool value) { m_uses_collapsing_borders_model = value; }
bool uses_collapsing_borders_model() const { return m_uses_collapsing_borders_model; }

void set_table_cell_coordinates(TableCellCoordinates const& table_cell_coordinates) { m_table_cell_coordinates = table_cell_coordinates; }
auto const& table_cell_coordinates() const { return m_table_cell_coordinates; }
void set_collapsed_table_borders(OwnPtr<CollapsedTableBorders> collapsed_table_borders) { m_collapsed_table_borders = move(collapsed_table_borders); }
CollapsedTableBorders const* collapsed_table_borders() const { return m_collapsed_table_borders.ptr(); }

enum class ShrinkRadiiForBorders {
Yes,
Expand Down Expand Up @@ -560,6 +531,7 @@ class WEB_API Paintable
bool m_absolutely_positioned : 1 { false };
bool m_floating : 1 { false };
bool m_inline : 1 { false };
bool m_uses_collapsing_borders_model : 1 { false };
CSS::Display m_display;

RefPtr<StackingContext> m_stacking_context;
Expand All @@ -583,8 +555,7 @@ class WEB_API Paintable
size_t m_visual_context_nodes_begin { 0 };
size_t m_visual_context_nodes_end { 0 };

Optional<BordersDataWithElementKind> m_override_borders_data;
Optional<TableCellCoordinates> m_table_cell_coordinates;
OwnPtr<CollapsedTableBorders> m_collapsed_table_borders;
Optional<size_t> m_containing_line_box_index;

ResolvedCSSFilter m_filter;
Expand Down
25 changes: 13 additions & 12 deletions Libraries/LibWeb/Painting/StackingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,17 +248,23 @@ static bool is_pure_inline_box(Paintable const& paintable)
&& !paintable.is_positioned();
}

static void paint_inline_level_non_positioned_descendant(DisplayListRecordingContext& context, Paintable const& paintable)
static void paint_subtree_backgrounds_and_borders(DisplayListRecordingContext& context, Paintable const& paintable)
{
paint_node(paintable, context, PaintPhase::Background);
paint_node(paintable, context, PaintPhase::Border);
paint_node(paintable, context, PaintPhase::TableCollapsedBorder);
// A pure inline paintable paints its own background/border in the inline-level phase. Its block descendants, if
// any, are painted by the earlier BackgroundAndBorders descent through pure inline boxes. In today's layout trees,
// this subtree sweep is a no-op for InlineNodes: it can only find inline children, floats, or positioned boxes,
// all of which are skipped by the BackgroundAndBorders phase.
if (!is_pure_inline_box(paintable))
StackingContext::paint_descendants(context, paintable, StackingContext::StackingContextPaintPhase::BackgroundAndBorders);
if (paintable.collapsed_table_borders())
paint_node(paintable, context, PaintPhase::TableCollapsedBorder);
}

static void paint_inline_level_non_positioned_descendant(DisplayListRecordingContext& context, Paintable const& paintable)
{
paint_subtree_backgrounds_and_borders(context, paintable);

// https://drafts.csswg.org/css2/#elaborate-stacking-contexts
// "For inline-block and inline-table elements: [...] treat the element as if it created a new stacking context,
Expand All @@ -275,9 +281,7 @@ void StackingContext::paint_node_as_stacking_context(Paintable const& paintable,
return;
}

paint_node(paintable, context, PaintPhase::Background);
paint_node(paintable, context, PaintPhase::Border);
paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBorders);
paint_subtree_backgrounds_and_borders(context, paintable);
paint_descendants(context, paintable, StackingContextPaintPhase::Floats);
paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
paint_node(paintable, context, PaintPhase::Foreground);
Expand Down Expand Up @@ -344,19 +348,14 @@ void StackingContext::paint_descendants(DisplayListRecordingContext& context, Pa
switch (phase) {
case StackingContextPaintPhase::BackgroundAndBorders:
if (!child_is_inline_or_replaced && !child.is_floating()) {
paint_node(child, context, PaintPhase::Background);
paint_node(child, context, PaintPhase::Border);
paint_descendants(context, child, phase);
paint_node(child, context, PaintPhase::TableCollapsedBorder);
paint_subtree_backgrounds_and_borders(context, child);
} else if (is_pure_inline_box(child)) {
paint_descendants(context, child, phase);
}
break;
case StackingContextPaintPhase::Floats:
if (child.is_floating()) {
paint_node(child, context, PaintPhase::Background);
paint_node(child, context, PaintPhase::Border);
paint_descendants(context, child, StackingContextPaintPhase::BackgroundAndBorders);
paint_subtree_backgrounds_and_borders(context, child);
}
// Atomic inline-level descendants such as inline-blocks and inline tables participate in the parent's
// inline-level painting step, so their internal floats must not be painted early during the ancestor's
Expand Down Expand Up @@ -429,6 +428,8 @@ void StackingContext::paint_internal(DisplayListRecordingContext& context) const

// Draw the background and borders for block-level children (step 4)
paint_descendants(context, paintable_box(), StackingContextPaintPhase::BackgroundAndBorders);
if (paintable_box().collapsed_table_borders())
paint_node(paintable_box(), context, PaintPhase::TableCollapsedBorder);
// Draw the non-positioned floats (step 5)
if (!m_non_positioned_floating_descendants.is_empty())
paint_descendants(context, paintable_box(), StackingContextPaintPhase::Floats);
Expand Down
Loading
Loading