summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/Layout
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2023-05-09 09:56:30 +0200
committerAndreas Kling <kling@serenityos.org>2023-05-09 12:11:28 +0200
commit197efc898540f0e1ab0a3f697508a67d09336bf6 (patch)
tree691022ddf33ba7a1abed409e3124642c0e97f6e3 /Userland/Libraries/LibWeb/Layout
parent90b21890c599e2b524add8130825722efa028c2e (diff)
downloadserenity-197efc898540f0e1ab0a3f697508a67d09336bf6.zip
LibWeb: Improve handling of min/max constraint violations on images
Instead of bailing after resolving one violated constraint, we have to continue down the list of remaining constraints. We now also call the constraint solver for all replaced elements with "auto" for both width and height. Co-authored-by: 0GreenClover0 <clovers02123@gmail.com>
Diffstat (limited to 'Userland/Libraries/LibWeb/Layout')
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.cpp79
1 files changed, 56 insertions, 23 deletions
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
index 3151640722..5d6ea5e744 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
@@ -258,7 +258,7 @@ FormattingContext::ShrinkToFitResult FormattingContext::calculate_shrink_to_fit_
};
}
-static CSSPixelSize solve_replaced_size_constraint(LayoutState const& state, CSSPixels w, CSSPixels h, ReplacedBox const& box)
+static CSSPixelSize solve_replaced_size_constraint(LayoutState const& state, CSSPixels input_width, CSSPixels input_height, ReplacedBox const& box)
{
// 10.4 Minimum and maximum widths: 'min-width' and 'max-width'
@@ -268,35 +268,42 @@ static CSSPixelSize solve_replaced_size_constraint(LayoutState const& state, CSS
auto height_of_containing_block = containing_block_state.content_height();
CSSPixels specified_min_width = box.computed_values().min_width().is_auto() ? 0 : box.computed_values().min_width().to_px(box, width_of_containing_block);
- CSSPixels specified_max_width = box.computed_values().max_width().is_none() ? w : box.computed_values().max_width().to_px(box, width_of_containing_block);
+ CSSPixels specified_max_width = box.computed_values().max_width().is_none() ? input_width : box.computed_values().max_width().to_px(box, width_of_containing_block);
CSSPixels specified_min_height = box.computed_values().min_height().is_auto() ? 0 : box.computed_values().min_height().to_px(box, height_of_containing_block);
- CSSPixels specified_max_height = box.computed_values().max_height().is_none() ? h : box.computed_values().max_height().to_px(box, height_of_containing_block);
+ CSSPixels specified_max_height = box.computed_values().max_height().is_none() ? input_height : box.computed_values().max_height().to_px(box, height_of_containing_block);
auto min_width = min(specified_min_width, specified_max_width);
auto max_width = max(specified_min_width, specified_max_width);
auto min_height = min(specified_min_height, specified_max_height);
auto max_height = max(specified_min_height, specified_max_height);
+ struct Size {
+ CSSPixels width;
+ CSSPixels height;
+ } size = { input_width, input_height };
+ auto& w = size.width;
+ auto& h = size.height;
+
if (w > max_width)
- return { w, max(max_width * (h / w), min_height) };
+ size = { max_width, max(max_width * (h / w), min_height) };
if (w < min_width)
- return { max_width, min(min_width * (h / w), max_height) };
+ size = { min_width, min(min_width * (h / w), max_height) };
if (h > max_height)
- return { max(max_height * (w / h), min_width), max_height };
+ size = { max(max_height * (w / h), min_width), max_height };
if (h < min_height)
- return { min(min_height * (w / h), max_width), min_height };
- if ((w > max_width && h > max_height) && (max_width / w < max_height / h))
- return { max_width, max(min_height, max_width * (h / w)) };
+ size = { min(min_height * (w / h), max_width), min_height };
+ if ((w > max_width && h > max_height) && (max_width / w <= max_height / h))
+ size = { max_width, max(min_height, max_width * (h / w)) };
if ((w > max_width && h > max_height) && (max_width / w > max_height / h))
- return { max(min_width, max_height * (w / h)), max_height };
- if ((w < min_width && h < min_height) && (min_width / w < min_height / h))
- return { min(max_width, min_height * (w / h)), min_height };
+ size = { max(min_width, max_height * (w / h)), max_height };
+ if ((w < min_width && h < min_height) && (min_width / w <= min_height / h))
+ size = { min(max_width, min_height * (w / h)), min_height };
if ((w < min_width && h < min_height) && (min_width / w > min_height / h))
- return { min_width, min(max_height, min_width * (h / w)) };
+ size = { min_width, min(max_height, min_width * (h / w)) };
if (w < min_width && h > max_height)
- return { min_width, max_height };
+ size = { min_width, max_height };
if (w > max_width && h < min_height)
- return { max_width, min_height };
+ size = { max_width, min_height };
return { w, h };
}
@@ -430,10 +437,18 @@ CSSPixels FormattingContext::compute_width_for_replaced_element(LayoutState cons
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto computed_width = should_treat_width_as_auto(box, available_space) ? CSS::Size::make_auto() : box.computed_values().width();
+ auto computed_height = should_treat_height_as_auto(box, available_space) ? CSS::Size::make_auto() : box.computed_values().height();
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
auto used_width = tentative_width_for_replaced_element(state, box, computed_width, available_space);
+ if (computed_width.is_auto() && computed_height.is_auto() && box.has_intrinsic_aspect_ratio()) {
+ CSSPixels w = used_width;
+ CSSPixels h = tentative_height_for_replaced_element(state, box, computed_height, available_space);
+ used_width = solve_replaced_size_constraint(state, w, h, box).width();
+ return used_width;
+ }
+
// 2. The tentative used width is greater than 'max-width', the rules above are applied again,
// but this time using the computed value of 'max-width' as the computed value for 'width'.
auto computed_max_width = box.computed_values().max_width();
@@ -485,28 +500,46 @@ CSSPixels FormattingContext::tentative_height_for_replaced_element(LayoutState c
CSSPixels FormattingContext::compute_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box, AvailableSpace const& available_space)
{
- // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow,
- // 'inline-block' replaced elements in normal flow and floating replaced elements
+ // 10.6.2 Inline replaced elements
+ // 10.6.4 Block-level replaced elements in normal flow
+ // 10.6.6 Floating replaced elements
+ // 10.6.10 'inline-block' replaced elements in normal flow
auto height_of_containing_block = state.get(*box.containing_block()).content_height();
auto computed_width = should_treat_width_as_auto(box, available_space) ? CSS::Size::make_auto() : box.computed_values().width();
auto computed_height = should_treat_height_as_auto(box, available_space) ? CSS::Size::make_auto() : box.computed_values().height();
+ // 1. The tentative used height is calculated (without 'min-height' and 'max-height')
CSSPixels used_height = tentative_height_for_replaced_element(state, box, computed_height, available_space);
+ // However, for replaced elements with both 'width' and 'height' computed as 'auto',
+ // use the algorithm under 'Minimum and maximum widths'
+ // https://www.w3.org/TR/CSS22/visudet.html#min-max-widths
+ // to find the used width and height.
if (computed_width.is_auto() && computed_height.is_auto() && box.has_intrinsic_aspect_ratio()) {
CSSPixels w = tentative_width_for_replaced_element(state, box, computed_width, available_space);
CSSPixels h = used_height;
used_height = solve_replaced_size_constraint(state, w, h, box).height();
+ return used_height;
}
- auto const& computed_min_height = box.computed_values().min_height();
- auto const& computed_max_height = box.computed_values().max_height();
+ // 2. If this tentative height is greater than 'max-height', the rules above are applied again,
+ // but this time using the value of 'max-height' as the computed value for 'height'.
+ auto computed_max_height = box.computed_values().max_height();
+ if (!computed_max_height.is_none()) {
+ if (used_height > computed_max_height.to_px(box, height_of_containing_block)) {
+ used_height = tentative_height_for_replaced_element(state, box, computed_max_height, available_space);
+ }
+ }
- if (!computed_max_height.is_none())
- used_height = min(used_height, computed_max_height.to_px(box, height_of_containing_block));
- if (!computed_min_height.is_auto())
- used_height = max(used_height, computed_min_height.to_px(box, height_of_containing_block));
+ // 3. If the resulting height is smaller than 'min-height', the rules above are applied again,
+ // but this time using the value of 'min-height' as the computed value for 'height'.
+ auto computed_min_height = box.computed_values().min_height();
+ if (!computed_min_height.is_auto()) {
+ if (used_height < computed_min_height.to_px(box, height_of_containing_block)) {
+ used_height = tentative_height_for_replaced_element(state, box, computed_min_height, available_space);
+ }
+ }
return used_height;
}