Prechádzať zdrojové kódy

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 :^)
Andreas Kling 2 rokov pred
rodič
commit
610a7603a2

+ 9 - 0
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 <html> at (0,0) content-size 800x16 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x0 children: not-inline
+      TableWrapper <(anonymous)> at (8,8) content-size 0x0 [BFC] children: not-inline
+        TableBox <table> at (8,8) content-size 0x0 [TFC] children: not-inline
+          TableRowGroupBox <tbody> at (8,8) content-size 0x0 children: not-inline
+            TableRowBox <tr> at (8,8) content-size 0x0 children: not-inline
+              TableCellBox <(anonymous)> at (8,8) content-size 0x0 [BFC] children: not-inline
+                BlockContainer <td> at (9,9) content-size 0x0 positioned [BFC] children: not-inline

+ 5 - 0
Tests/LibWeb/Layout/input/blockify-layout-internal-box-without-crashing.html

@@ -0,0 +1,5 @@
+<!doctype html><style>
+td {
+    position: absolute; /* force blockification */
+}
+</style><table><td>

+ 41 - 16
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).
     // 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.)
     // (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();
     auto display = style.display();
     if (display.is_none() || display.is_contents())
     if (display.is_none() || display.is_contents())
         return;
         return;
 
 
+    auto new_display = display;
+
     switch (required_box_type_transformation(style, element, pseudo_element)) {
     switch (required_box_type_transformation(style, element, pseudo_element)) {
     case BoxTypeTransformation::None:
     case BoxTypeTransformation::None:
         break;
         break;
     case BoxTypeTransformation::Blockify:
     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;
         break;
     case BoxTypeTransformation::Inlinify:
     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;
         break;
     }
     }
+
+    if (new_display != display)
+        style.set_property(CSS::PropertyID::Display, DisplayStyleValue::create(new_display));
 }
 }
 
 
 NonnullRefPtr<StyleProperties> StyleComputer::create_document_style() const
 NonnullRefPtr<StyleProperties> StyleComputer::create_document_style() const