Browse Source

LibWeb: Ensure preceding offset is non-negative in `float_box()`

When calculating the edge offset of the next floating item based on the
offset of the preceding floating item, we need to ensure that the
preceding offset is always > 0. This isn't explicitly written in the
spec, but all other popular engines do that.

Fixes https://github.com/SerenityOS/serenity/issues/21023
Aliaksandr Kalenik 1 year ago
parent
commit
d1e542999c

+ 28 - 0
Tests/LibWeb/Layout/expected/block-and-inline/floats-with-negative-percentage-margins.txt

@@ -0,0 +1,28 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (0,0) content-size 800x208 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 600x0 children: not-inline
+      BlockContainer <div#top> at (8,8) content-size 100x100 floating [BFC] children: inline
+        line 0 width: 26.640625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+          frag 0 from TextNode start: 0, length: 3, rect: [8,8 26.640625x17.46875]
+            "top"
+        TextNode <#text>
+      BlockContainer <div#left> at (8,108) content-size 100x100 floating [BFC] children: inline
+        line 0 width: 26.25, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+          frag 0 from TextNode start: 0, length: 4, rect: [8,108 26.25x17.46875]
+            "left"
+        TextNode <#text>
+      BlockContainer <div#right> at (208,108) content-size 100x100 floating [BFC] children: inline
+        line 0 width: 37.109375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+          frag 0 from TextNode start: 0, length: 5, rect: [208,108 37.109375x17.46875]
+            "right"
+        TextNode <#text>
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+  PaintableWithLines (BlockContainer<HTML>) [0,0 800x208]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 600x0] overflow: [8,8 300x200]
+      PaintableWithLines (BlockContainer<DIV>#top) [8,8 100x100]
+        TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<DIV>#left) [8,108 100x100]
+        TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<DIV>#right) [208,108 100x100]
+        TextPaintable (TextNode<#text>)

+ 22 - 0
Tests/LibWeb/Layout/input/block-and-inline/floats-with-negative-percentage-margins.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html><style>
+* { outline: 1px solid black; }
+body { width: 600px; }
+div {
+    width: 100px;
+    height: 100px;
+    float: left;
+}
+#top {
+    background: green;
+}
+#left {
+    background: pink;
+    margin-right: -100%;
+    clear: both;
+}
+#right {
+    background: orange;
+    margin-left: 200px;
+    margin-right: -100%;
+}
+</style><body><div id="top">top</div><div id="left">left</div><div id="right">right

+ 1 - 1
Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp

@@ -962,7 +962,7 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
                 CSSPixels tentative_offset_from_edge = 0;
                 bool fits_next_to_preceding_float = false;
                 if (side == FloatSide::Left) {
-                    tentative_offset_from_edge = preceding_float.offset_from_edge + preceding_float_state.content_width() + preceding_float_state.margin_box_right() + box_state.margin_box_left();
+                    tentative_offset_from_edge = max(preceding_float.offset_from_edge + preceding_float_state.content_width() + preceding_float_state.margin_box_right(), 0) + box_state.margin_box_left();
                     if (available_space.width.is_definite()) {
                         fits_next_to_preceding_float = (tentative_offset_from_edge + box_state.content_width() + box_state.margin_box_right()) <= available_space.width.to_px_or_zero();
                     } else if (available_space.width.is_max_content() || available_space.width.is_indefinite()) {