Forráskód Böngészése

LibWeb: Forbid usage of indefinite width in calculate_min{max}_height

Changing `calculate_min_content_heigh()` and
`calculate_min_content_heigh()` to accept width as `CSSPixels`, instead
of `AvailableSize` that might be indefinite, makes it more explicit
that width is supposed to be known by the time height is measured.

This change has a bit of collateral damage which is rows height
calculation regression in `table/inline-table-width` that worked before
by accident.
Aliaksandr Kalenik 2 éve
szülő
commit
e25b1f76e1

+ 21 - 21
Tests/LibWeb/Layout/expected/table/inline-table-width.txt

@@ -1,12 +1,12 @@
 Viewport <#document> at (0,0) content-size 800x600 children: not-inline
   BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
-    BlockContainer <body> at (8,8) content-size 784x46.9375 children: inline
-      line 0 width: 137.984375, height: 46.9375, bottom: 46.9375, baseline: 46.9375
-        frag 0 from BlockContainer start: 0, length: 0, rect: [9,9 135.984375x44.9375]
-      BlockContainer <table> at (9,9) content-size 135.984375x44.9375 inline-block [BFC] children: not-inline
-        TableWrapper <(anonymous)> at (9,9) content-size 135.984375x44.9375 inline-block [BFC] children: not-inline
-          Box <(anonymous)> at (9,9) content-size 135.984375x44.9375 inline-table table-box [TFC] children: not-inline
-            Box <tbody> at (9,9) content-size 129.984375x38.9375 table-row-group children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x64.875 children: inline
+      line 0 width: 137.984375, height: 64.875, bottom: 64.875, baseline: 64.875
+        frag 0 from BlockContainer start: 0, length: 0, rect: [9,9 135.984375x62.875]
+      BlockContainer <table> at (9,9) content-size 135.984375x62.875 inline-block [BFC] children: not-inline
+        TableWrapper <(anonymous)> at (9,9) content-size 135.984375x62.875 inline-block [BFC] children: not-inline
+          Box <(anonymous)> at (9,9) content-size 135.984375x62.875 inline-table table-box [TFC] children: not-inline
+            Box <tbody> at (9,9) content-size 129.984375x56.875 table-row-group children: not-inline
               Box <tr> at (11,11) content-size 129.984375x19.46875 table-row children: not-inline
                 BlockContainer <td> at (12,12) content-size 87.90625x17.46875 table-cell [BFC] children: inline
                   line 0 width: 15.734375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
@@ -18,32 +18,32 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
                     frag 0 from TextNode start: 0, length: 4, rect: [103.90625,12 27.84375x17.46875]
                       "null"
                   TextNode <#text>
-              Box <tr> at (11,32.46875) content-size 129.984375x19.46875 table-row children: not-inline
-                BlockContainer <td> at (12,33.46875) content-size 87.90625x17.46875 table-cell [BFC] children: inline
+              Box <tr> at (11,32.46875) content-size 129.984375x37.40625 table-row children: not-inline
+                BlockContainer <td> at (12,42.4375) content-size 87.90625x17.46875 table-cell [BFC] children: inline
                   line 0 width: 87.90625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
-                    frag 0 from TextNode start: 0, length: 11, rect: [12,33.46875 87.90625x17.46875]
+                    frag 0 from TextNode start: 0, length: 11, rect: [12,42.4375 87.90625x17.46875]
                       "Is Selected"
                   TextNode <#text>
-                BlockContainer <td> at (103.90625,33.46875) content-size 38.078125x17.46875 table-cell [BFC] children: inline
+                BlockContainer <td> at (103.90625,42.4375) content-size 38.078125x17.46875 table-cell [BFC] children: inline
                   line 0 width: 38.078125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
-                    frag 0 from TextNode start: 0, length: 5, rect: [103.90625,33.46875 38.078125x17.46875]
+                    frag 0 from TextNode start: 0, length: 5, rect: [103.90625,42.4375 38.078125x17.46875]
                       "false"
                   TextNode <#text>
 
 PaintableWithLines (Viewport<#document>) [0,0 800x600]
   PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
-    PaintableWithLines (BlockContainer<BODY>) [8,8 784x46.9375]
-      PaintableWithLines (BlockContainer<TABLE>) [8,8 137.984375x46.9375]
-        PaintableWithLines (TableWrapper(anonymous)) [9,9 135.984375x44.9375]
-          PaintableBox (Box(anonymous)) [9,9 135.984375x44.9375]
-            PaintableBox (Box<TBODY>) [9,9 129.984375x38.9375] overflow: [9,9 133.984375x42.9375]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x64.875]
+      PaintableWithLines (BlockContainer<TABLE>) [8,8 137.984375x64.875]
+        PaintableWithLines (TableWrapper(anonymous)) [9,9 135.984375x62.875]
+          PaintableBox (Box(anonymous)) [9,9 135.984375x62.875]
+            PaintableBox (Box<TBODY>) [9,9 129.984375x56.875] overflow: [9,9 133.984375x60.875]
               PaintableBox (Box<TR>) [11,11 129.984375x19.46875] overflow: [11,11 131.984375x19.46875]
                 PaintableWithLines (BlockContainer<TD>) [11,11 89.90625x19.46875]
                   TextPaintable (TextNode<#text>)
                 PaintableWithLines (BlockContainer<TD>) [102.90625,11 40.078125x19.46875]
                   TextPaintable (TextNode<#text>)
-              PaintableBox (Box<TR>) [11,32.46875 129.984375x19.46875] overflow: [11,32.46875 131.984375x19.46875]
-                PaintableWithLines (BlockContainer<TD>) [11,32.46875 89.90625x19.46875]
-                  TextPaintable (TextNode<#text>)
-                PaintableWithLines (BlockContainer<TD>) [102.90625,32.46875 40.078125x19.46875]
+              PaintableBox (Box<TR>) [11,32.46875 129.984375x37.40625] overflow: [11,32.46875 131.984375x37.40625]
+                PaintableWithLines (BlockContainer<TD>) [11,32.46875 89.90625x37.40625]
                   TextPaintable (TextNode<#text>)
+                PaintableWithLines (BlockContainer<TD>) [102.90625,32.46875 40.078125x37.40625]
+                  TextPaintable (TextNode<#text>)

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

@@ -550,16 +550,16 @@ CSSPixels BlockFormattingContext::compute_auto_height_for_block_level_element(Bo
     if (display.is_flex_inside()) {
         // https://drafts.csswg.org/css-flexbox-1/#algo-main-container
         // NOTE: The automatic block size of a block-level flex container is its max-content size.
-        return calculate_max_content_height(box, available_space.width);
+        return calculate_max_content_height(box, available_space.width.to_px_or_zero());
     }
     if (display.is_grid_inside()) {
         // https://www.w3.org/TR/css-grid-2/#intrinsic-sizes
         // In both inline and block formatting contexts, the grid container’s auto block size is its
         // max-content size.
-        return calculate_max_content_height(box, available_space.width);
+        return calculate_max_content_height(box, available_space.width.to_px_or_zero());
     }
     if (display.is_table_inside()) {
-        return calculate_max_content_height(box, available_space.width);
+        return calculate_max_content_height(box, available_space.width.to_px_or_zero());
     }
 
     // https://www.w3.org/TR/CSS22/visudet.html#normal-block

+ 5 - 5
Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp

@@ -834,7 +834,7 @@ void FlexFormattingContext::determine_main_size_of_flex_container()
         }
     } else {
         if (!has_definite_main_size(flex_container()))
-            set_main_size(flex_container(), calculate_max_content_height(flex_container(), m_available_space_for_flex_container->space.width));
+            set_main_size(flex_container(), calculate_max_content_height(flex_container(), m_available_space_for_flex_container->space.width.to_px()));
     }
 }
 
@@ -2012,7 +2012,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_main_size(FlexItem const&
     if (available_space.width.is_indefinite()) {
         available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
     }
-    return calculate_min_content_height(item.box, available_space.width);
+    return calculate_min_content_height(item.box, available_space.width.to_px_or_zero());
 }
 
 CSSPixels FlexFormattingContext::calculate_max_content_main_size(FlexItem const& item) const
@@ -2024,7 +2024,7 @@ CSSPixels FlexFormattingContext::calculate_max_content_main_size(FlexItem const&
     if (available_space.width.is_indefinite()) {
         available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
     }
-    return calculate_max_content_height(item.box, available_space.width);
+    return calculate_max_content_height(item.box, available_space.width.to_px_or_zero());
 }
 
 CSSPixels FlexFormattingContext::calculate_fit_content_main_size(FlexItem const& item) const
@@ -2048,7 +2048,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_cross_size(FlexItem const
         if (available_space.width.is_indefinite()) {
             available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
         }
-        return calculate_min_content_height(item.box, available_space.width);
+        return calculate_min_content_height(item.box, available_space.width.to_px_or_zero());
     }
     return calculate_min_content_width(item.box);
 }
@@ -2060,7 +2060,7 @@ CSSPixels FlexFormattingContext::calculate_max_content_cross_size(FlexItem const
         if (available_space.width.is_indefinite()) {
             available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
         }
-        return calculate_max_content_height(item.box, available_space.width);
+        return calculate_max_content_height(item.box, available_space.width.to_px_or_zero());
     }
     return calculate_max_content_width(item.box);
 }

+ 15 - 37
Userland/Libraries/LibWeb/Layout/FormattingContext.cpp

@@ -1265,17 +1265,17 @@ CSSPixels FormattingContext::calculate_fit_content_height(Layout::Box const& box
     // equal to clamp(min-content size, stretch-fit size, max-content size)
     // (i.e. max(min-content size, min(max-content size, stretch-fit size))).
     if (available_space.height.is_definite()) {
-        return max(calculate_min_content_height(box, available_space.width),
+        return max(calculate_min_content_height(box, available_space.width.to_px_or_zero()),
             min(calculate_stretch_fit_height(box, available_space.height),
-                calculate_max_content_height(box, available_space.width)));
+                calculate_max_content_height(box, available_space.width.to_px_or_zero())));
     }
 
     // When sizing under a min-content constraint, equal to the min-content size.
     if (available_space.height.is_min_content())
-        return calculate_min_content_height(box, available_space.width);
+        return calculate_min_content_height(box, available_space.width.to_px_or_zero());
 
     // Otherwise, equal to the max-content size in that axis.
-    return calculate_max_content_height(box, available_space.width);
+    return calculate_max_content_height(box, available_space.width.to_px_or_zero());
 }
 
 CSSPixels FormattingContext::calculate_min_content_width(Layout::Box const& box) const
@@ -1355,29 +1355,19 @@ CSSPixels FormattingContext::calculate_max_content_width(Layout::Box const& box)
 }
 
 // https://www.w3.org/TR/css-sizing-3/#min-content-block-size
-CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box, AvailableSize const& available_width) const
+CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box, CSSPixels width) const
 {
     // For block containers, tables, and inline boxes, this is equivalent to the max-content block size.
     if (box.is_block_container() || box.display().is_table_inside())
-        return calculate_max_content_height(box, available_width);
+        return calculate_max_content_height(box, width);
 
     if (box.has_natural_height())
         return *box.natural_height();
 
-    bool is_cacheable = available_width.is_definite() || available_width.is_intrinsic_sizing_constraint();
-
     auto get_cache_slot = [&]() -> Optional<CSSPixels>* {
-        if (!is_cacheable)
-            return {};
         auto& root_state = m_state.m_root;
         auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new LayoutState::IntrinsicSizes); });
-        if (available_width.is_definite())
-            return &cache.min_content_height_with_definite_available_width.ensure(available_width.to_px());
-        if (available_width.is_min_content())
-            return &cache.min_content_height_with_min_content_available_width;
-        if (available_width.is_max_content())
-            return &cache.min_content_height_with_max_content_available_width;
-        return {};
+        return &cache.min_content_height.ensure(width);
     };
 
     if (auto* cache_slot = get_cache_slot(); cache_slot && cache_slot->has_value())
@@ -1388,15 +1378,14 @@ CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box
     auto& box_state = throwaway_state.get_mutable(box);
     box_state.height_constraint = SizeConstraint::MinContent;
     box_state.set_indefinite_content_height();
-    if (available_width.is_definite())
-        box_state.set_content_width(available_width.to_px());
+    box_state.set_content_width(width);
 
     auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
     if (!context) {
         context = make<BlockFormattingContext>(throwaway_state, verify_cast<BlockContainer>(box), nullptr);
     }
 
-    context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(available_width, AvailableSize::make_min_content()));
+    context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_min_content()));
 
     auto min_content_height = context->automatic_content_height();
     if (!isfinite(min_content_height.to_double())) {
@@ -1411,28 +1400,18 @@ CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box
     return min_content_height;
 }
 
-CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box, AvailableSize const& available_width) const
+CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box, CSSPixels width) const
 {
-    if (box.has_preferred_aspect_ratio() && available_width.is_definite())
-        return available_width.to_px() / static_cast<double>(*box.preferred_aspect_ratio());
+    if (box.has_preferred_aspect_ratio())
+        return width / static_cast<double>(*box.preferred_aspect_ratio());
 
     if (box.has_natural_height())
         return *box.natural_height();
 
-    bool is_cacheable = available_width.is_definite() || available_width.is_intrinsic_sizing_constraint();
-
     auto get_cache_slot = [&]() -> Optional<CSSPixels>* {
-        if (!is_cacheable)
-            return {};
         auto& root_state = m_state.m_root;
         auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new LayoutState::IntrinsicSizes); });
-        if (available_width.is_definite())
-            return &cache.max_content_height_with_definite_available_width.ensure(available_width.to_px());
-        if (available_width.is_min_content())
-            return &cache.max_content_height_with_min_content_available_width;
-        if (available_width.is_max_content())
-            return &cache.max_content_height_with_max_content_available_width;
-        return {};
+        return &cache.max_content_height.ensure(width);
     };
 
     if (auto* cache_slot = get_cache_slot(); cache_slot && cache_slot->has_value())
@@ -1443,15 +1422,14 @@ CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box
     auto& box_state = throwaway_state.get_mutable(box);
     box_state.height_constraint = SizeConstraint::MaxContent;
     box_state.set_indefinite_content_height();
-    if (available_width.is_definite())
-        box_state.set_content_width(available_width.to_px());
+    box_state.set_content_width(width);
 
     auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
     if (!context) {
         context = make<BlockFormattingContext>(throwaway_state, verify_cast<BlockContainer>(box), nullptr);
     }
 
-    context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(available_width, AvailableSize::make_max_content()));
+    context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_max_content()));
 
     auto max_content_height = context->automatic_content_height();
 

+ 2 - 2
Userland/Libraries/LibWeb/Layout/FormattingContext.h

@@ -59,8 +59,8 @@ public:
 
     CSSPixels calculate_min_content_width(Layout::Box const&) const;
     CSSPixels calculate_max_content_width(Layout::Box const&) const;
-    CSSPixels calculate_min_content_height(Layout::Box const&, AvailableSize const& available_width) const;
-    CSSPixels calculate_max_content_height(Layout::Box const&, AvailableSize const& available_width) const;
+    CSSPixels calculate_min_content_height(Layout::Box const&, CSSPixels width) const;
+    CSSPixels calculate_max_content_height(Layout::Box const&, CSSPixels width) const;
 
     CSSPixels calculate_fit_content_height(Layout::Box const&, AvailableSpace const&) const;
     CSSPixels calculate_fit_content_width(Layout::Box const&, AvailableSpace const&) const;

+ 2 - 2
Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp

@@ -1954,7 +1954,7 @@ CSSPixels GridFormattingContext::calculate_min_content_size(GridItem const& item
     if (dimension == GridDimension::Column) {
         return calculate_min_content_width(item.box);
     } else {
-        return calculate_min_content_height(item.box, get_available_space_for_item(item).width);
+        return calculate_min_content_height(item.box, get_available_space_for_item(item).width.to_px_or_zero());
     }
 }
 
@@ -1963,7 +1963,7 @@ CSSPixels GridFormattingContext::calculate_max_content_size(GridItem const& item
     if (dimension == GridDimension::Column) {
         return calculate_max_content_width(item.box);
     } else {
-        return calculate_max_content_height(item.box, get_available_space_for_item(item).width);
+        return calculate_max_content_height(item.box, get_available_space_for_item(item).width.to_px_or_zero());
     }
 }
 

+ 2 - 8
Userland/Libraries/LibWeb/Layout/LayoutState.h

@@ -164,14 +164,8 @@ struct LayoutState {
         Optional<CSSPixels> min_content_width;
         Optional<CSSPixels> max_content_width;
 
-        // NOTE: Since intrinsic heights depend on the amount of available width, we have to cache
-        //       three separate kinds of results, depending on the available width at the time of calculation.
-        HashMap<CSSPixels, Optional<CSSPixels>> min_content_height_with_definite_available_width;
-        HashMap<CSSPixels, Optional<CSSPixels>> max_content_height_with_definite_available_width;
-        Optional<CSSPixels> min_content_height_with_min_content_available_width;
-        Optional<CSSPixels> max_content_height_with_min_content_available_width;
-        Optional<CSSPixels> min_content_height_with_max_content_available_width;
-        Optional<CSSPixels> max_content_height_with_max_content_available_width;
+        HashMap<CSSPixels, Optional<CSSPixels>> min_content_height;
+        HashMap<CSSPixels, Optional<CSSPixels>> max_content_height;
     };
 
     HashMap<JS::GCPtr<NodeWithStyleAndBoxModelMetrics const>, NonnullOwnPtr<IntrinsicSizes>> mutable intrinsic_sizes;

+ 2 - 2
Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp

@@ -241,8 +241,8 @@ void TableFormattingContext::compute_cell_measures(AvailableSpace const& availab
         CSSPixels border_left = use_collapsing_borders_model ? round(cell_state.border_left / 2) : computed_values.border_left().width;
         CSSPixels border_right = use_collapsing_borders_model ? round(cell_state.border_right / 2) : computed_values.border_right().width;
 
-        auto min_content_height = calculate_min_content_height(cell.box, available_space.width);
-        auto max_content_height = calculate_max_content_height(cell.box, available_space.width);
+        auto min_content_height = calculate_min_content_height(cell.box, available_space.width.to_px_or_zero());
+        auto max_content_height = calculate_max_content_height(cell.box, available_space.width.to_px_or_zero());
         auto min_content_width = calculate_min_content_width(cell.box);
         auto max_content_width = calculate_max_content_width(cell.box);