From 610a7603a269099e821c51a0f4ecd38177a8a496 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 3 May 2023 14:59:44 +0200 Subject: [PATCH] LibWeb: Implement more box type transformation edge cases In particular, we now blockify layout internal boxes (e.g table parts) by turning them into `block flow`. This fixes a crash when viewing our GitHub repo :^) --- ...y-layout-internal-box-without-crashing.txt | 9 +++ ...-layout-internal-box-without-crashing.html | 5 ++ .../Libraries/LibWeb/CSS/StyleComputer.cpp | 57 +++++++++++++------ 3 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 Tests/LibWeb/Layout/expected/blockify-layout-internal-box-without-crashing.txt create mode 100644 Tests/LibWeb/Layout/input/blockify-layout-internal-box-without-crashing.html diff --git a/Tests/LibWeb/Layout/expected/blockify-layout-internal-box-without-crashing.txt b/Tests/LibWeb/Layout/expected/blockify-layout-internal-box-without-crashing.txt new file mode 100644 index 00000000000..72466180d21 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/blockify-layout-internal-box-without-crashing.txt @@ -0,0 +1,9 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x16 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x0 children: not-inline + TableWrapper <(anonymous)> at (8,8) content-size 0x0 [BFC] children: not-inline + TableBox at (8,8) content-size 0x0 [TFC] children: not-inline + TableRowGroupBox at (8,8) content-size 0x0 children: not-inline + TableRowBox at (8,8) content-size 0x0 children: not-inline + TableCellBox <(anonymous)> at (8,8) content-size 0x0 [BFC] children: not-inline + BlockContainer
at (9,9) content-size 0x0 positioned [BFC] children: not-inline diff --git a/Tests/LibWeb/Layout/input/blockify-layout-internal-box-without-crashing.html b/Tests/LibWeb/Layout/input/blockify-layout-internal-box-without-crashing.html new file mode 100644 index 00000000000..e2ef9119ce8 --- /dev/null +++ b/Tests/LibWeb/Layout/input/blockify-layout-internal-box-without-crashing.html @@ -0,0 +1,5 @@ +
\ No newline at end of file diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 301dcedc88d..07d8cbb25bb 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -1466,35 +1466,60 @@ void StyleComputer::transform_box_type_if_needed(StyleProperties& style, DOM::El // which sets the box’s computed outer display type to block or inline (respectively). // (This has no effect on display types that generate no box at all, such as none or contents.) - // FIXME: If a block box (block flow) is inlinified, its inner display type is set to flow-root so that it remains a block container. - // - // FIXME: If an inline box (inline flow) is inlinified, it recursively inlinifies all of its in-flow children, - // so that no block-level descendants break up the inline formatting context in which it participates. - // - // FIXME: For legacy reasons, if an inline block box (inline flow-root) is blockified, it becomes a block box (losing its flow-root nature). - // For consistency, a run-in flow-root box also blockifies to a block box. - // - // FIXME: If a layout-internal box is blockified, its inner display type converts to flow so that it becomes a block container. - // Inlinification has no effect on layout-internal boxes. (However, placement in such an inline context will typically cause them - // to be wrapped in an appropriately-typed anonymous inline-level box.) - auto display = style.display(); if (display.is_none() || display.is_contents()) return; + auto new_display = display; + switch (required_box_type_transformation(style, element, pseudo_element)) { case BoxTypeTransformation::None: break; case BoxTypeTransformation::Blockify: - if (!display.is_block_outside()) { - style.set_property(CSS::PropertyID::Display, DisplayStyleValue::create({ CSS::Display::Outside::Block, display.inside(), display.list_item() })); + if (display.is_block_outside()) + return; + // If a layout-internal box is blockified, its inner display type converts to flow so that it becomes a block container. + if (display.is_internal()) { + new_display = CSS::Display::from_short(CSS::Display::Short::Block); + } else { + VERIFY(display.is_outside_and_inside()); + + // For legacy reasons, if an inline block box (inline flow-root) is blockified, it becomes a block box (losing its flow-root nature). + // For consistency, a run-in flow-root box also blockifies to a block box. + if (display.is_inline_block()) { + new_display = CSS::Display { CSS::Display::Outside::Block, CSS::Display::Inside::Flow, display.list_item() }; + } else { + new_display = CSS::Display { CSS::Display::Outside::Block, display.inside(), display.list_item() }; + } } break; case BoxTypeTransformation::Inlinify: - if (!display.is_inline_outside()) - style.set_property(CSS::PropertyID::Display, DisplayStyleValue::create({ CSS::Display::Outside::Inline, display.inside(), display.list_item() })); + if (display.is_inline_outside()) { + // FIXME: If an inline box (inline flow) is inlinified, it recursively inlinifies all of its in-flow children, + // so that no block-level descendants break up the inline formatting context in which it participates. + if (display.is_flow_inside()) { + dbgln("FIXME: Inlinify inline box children recursively"); + } + break; + } + if (display.is_internal()) { + // Inlinification has no effect on layout-internal boxes. (However, placement in such an inline context will typically cause them + // to be wrapped in an appropriately-typed anonymous inline-level box.) + } else { + VERIFY(display.is_outside_and_inside()); + + // If a block box (block flow) is inlinified, its inner display type is set to flow-root so that it remains a block container. + if (display.is_block_outside() && display.is_flow_inside()) { + new_display = CSS::Display { CSS::Display::Outside::Inline, CSS::Display::Inside::FlowRoot, display.list_item() }; + } + + new_display = CSS::Display { CSS::Display::Outside::Inline, display.inside(), display.list_item() }; + } break; } + + if (new_display != display) + style.set_property(CSS::PropertyID::Display, DisplayStyleValue::create(new_display)); } NonnullRefPtr StyleComputer::create_document_style() const