Browse Source

LibWeb: Account for box-sizing:border-box in layout-less definite sizes

When we determine that a size is definite because it can be resolved now
without performing layout, we also need to account for the box-sizing
property.

This lets us remove a hack from flex layout where box-sizing:border-box
was manually undone at one point in the layout algorithm.
Andreas Kling 2 years ago
parent
commit
00e3e82bbd

+ 11 - 0
Tests/LibWeb/Layout/expected/box-sizing-border-box-for-definite-sizes-without-layout.txt

@@ -0,0 +1,11 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (0,0) content-size 800x66 children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x50 children: inline
+      line 0 width: 89.726562, height: 50, bottom: 50, baseline: 16.914062
+        frag 0 from Box start: 0, length: 0, rect: [28,38 49.726562x0]
+      Box <div.button> at (28,38) content-size 49.726562x0 flex-container(row) children: not-inline
+        BlockContainer <(anonymous)> at (28,27.082031) content-size 49.726562x21.835937 flex-item children: inline
+          line 0 width: 49.726562, height: 21.835937, bottom: 21.835937, baseline: 16.914062
+            frag 0 from TextNode start: 0, length: 5, rect: [28,27.082031 49.726562x21.835937]
+              "Hello"
+          TextNode <#text>

+ 14 - 0
Tests/LibWeb/Layout/input/box-sizing-border-box-for-definite-sizes-without-layout.html

@@ -0,0 +1,14 @@
+<!doctype html><html><head><style>
+    * {
+        font: 20px SerenitySans;
+    }
+    .button {
+        align-items: center;
+        background-color: orange;
+        display: inline-flex;
+        height: 30px;
+        padding: 20px;
+        font-size: 20px;
+        box-sizing: border-box;
+    }
+</style></head><body><div class="button">Hello

+ 1 - 9
Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp

@@ -1088,15 +1088,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
             return;
         }
 
-        auto cross_size = [&]() {
-            if (item.box->computed_values().box_sizing() == CSS::BoxSizing::BorderBox && !should_treat_cross_size_as_auto(item.box)) {
-                return max(CSSPixels(0.0f), inner_cross_size(item.box) - item.padding.cross_before - item.padding.cross_after - item.borders.cross_before - item.borders.cross_after);
-            }
-
-            return inner_cross_size(item.box);
-        }();
-
-        item.hypothetical_cross_size = css_clamp(cross_size, clamp_min, clamp_max);
+        item.hypothetical_cross_size = css_clamp(inner_cross_size(item.box), clamp_min, clamp_max);
         return;
     }
 

+ 27 - 4
Userland/Libraries/LibWeb/Layout/LayoutState.cpp

@@ -229,6 +229,29 @@ void LayoutState::UsedValues::set_node(NodeWithStyleAndBoxModelMetrics& node, Us
 
     auto const& computed_values = node.computed_values();
 
+    auto adjust_for_box_sizing = [&](CSSPixels unadjusted_pixels, CSS::Size const& computed_size, bool width) -> CSSPixels {
+        // box-sizing: content-box and/or automatic size don't require any adjustment.
+        if (computed_values.box_sizing() == CSS::BoxSizing::ContentBox || computed_size.is_auto())
+            return unadjusted_pixels;
+
+        // box-sizing: border-box requires us to subtract the relevant border and padding from the size.
+        CSSPixels border_and_padding;
+
+        if (width) {
+            border_and_padding = CSSPixels(computed_values.border_left().width)
+                + computed_values.padding().left().resolved(*m_node, CSS::Length::make_px(containing_block_used_values->content_width())).resolved(*m_node).to_px(*m_node)
+                + CSSPixels(computed_values.border_right().width)
+                + computed_values.padding().right().resolved(*m_node, CSS::Length::make_px(containing_block_used_values->content_width())).resolved(*m_node).to_px(*m_node);
+        } else {
+            border_and_padding = CSSPixels(computed_values.border_top().width)
+                + computed_values.padding().top().resolved(*m_node, CSS::Length::make_px(containing_block_used_values->content_width())).resolved(*m_node).to_px(*m_node)
+                + CSSPixels(computed_values.border_bottom().width)
+                + computed_values.padding().bottom().resolved(*m_node, CSS::Length::make_px(containing_block_used_values->content_width())).resolved(*m_node).to_px(*m_node);
+        }
+
+        return unadjusted_pixels - border_and_padding;
+    };
+
     auto is_definite_size = [&](CSS::Size const& size, CSSPixels& resolved_definite_size, bool width) {
         // A size that can be determined without performing layout; that is,
         // a <length>,
@@ -270,22 +293,22 @@ void LayoutState::UsedValues::set_node(NodeWithStyleAndBoxModelMetrics& node, Us
                 auto containing_block_size_as_length = width
                     ? CSS::Length::make_px(containing_block_used_values->content_width())
                     : CSS::Length::make_px(containing_block_used_values->content_height());
-                resolved_definite_size = size.calculated().resolve_length_percentage(node, containing_block_size_as_length).value_or(CSS::Length::make_auto()).to_px(node);
+                resolved_definite_size = adjust_for_box_sizing(size.calculated().resolve_length_percentage(node, containing_block_size_as_length).value_or(CSS::Length::make_auto()).to_px(node), size, width);
                 return true;
             }
-            resolved_definite_size = size.calculated().resolve_length(node)->to_px(node);
+            resolved_definite_size = adjust_for_box_sizing(size.calculated().resolve_length(node)->to_px(node), size, width);
             return true;
         }
 
         if (size.is_length()) {
             VERIFY(!size.is_auto()); // This should have been covered by the Size::is_auto() branch above.
-            resolved_definite_size = size.length().to_px(node);
+            resolved_definite_size = adjust_for_box_sizing(size.length().to_px(node), size, width);
             return true;
         }
         if (size.is_percentage()) {
             if (containing_block_has_definite_size) {
                 auto containing_block_size = width ? containing_block_used_values->content_width() : containing_block_used_values->content_height();
-                resolved_definite_size = containing_block_size * size.percentage().as_fraction();
+                resolved_definite_size = adjust_for_box_sizing(containing_block_size * size.percentage().as_fraction(), size, width);
                 return true;
             }
             return false;