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>
This commit is contained in:
parent
90b21890c5
commit
197efc8985
Notes:
sideshowbarker
2024-07-17 11:29:41 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/197efc8985 Pull-request: https://github.com/SerenityOS/serenity/pull/18741
3 changed files with 71 additions and 23 deletions
|
@ -0,0 +1,6 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (1,1) content-size 798x80 [BFC] children: not-inline
|
||||
BlockContainer <body> at (10,10) content-size 780x62 children: inline
|
||||
line 0 width: 82, height: 62, bottom: 62, baseline: 62
|
||||
frag 0 from ImageBox start: 0, length: 0, rect: [11,11 80x60]
|
||||
ImageBox <img> at (11,11) content-size 80x60 children: not-inline
|
|
@ -0,0 +1,9 @@
|
|||
<!doctype html><style>
|
||||
* { border: 1px solid black; }
|
||||
body { background: #444; }
|
||||
img {
|
||||
max-height: 60px;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style><img src="data:image/gif;base64,R0lGODdhgALgAYAAAP///////ywAAAAAgALgAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2Oz+v3/L7/DxgoOEhYaHiImKi4yNjo+AgZKTlJWWl5iZmpucnZ6fkJGio6SlpqeoqaqrrK2ur6ChsrO0tba3uLm6u7y9vr+wscLDxMXGx8jJysvMzc7PwMHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fLz9PX29/j5+vv8/f7/8PMKDAgQQLGjyIMKHChQwbOnwIMaLEiRQrWryIMaPG/o0cO3r8CDKkyJEkS5o8iTKlypUsW7p8CTOmzJk0a9q8iTOnzp08e/r8CTSo0KFEixo9ijSp0qVMmzp9CjWq1KlUq1q9ijWr1q1cu3r9Cjas2LFky5o9izat2rVs27p9Czeu3Ll069q9izev3r18+/r9Cziw4MGECxs+jDix4sWMGzt+DDmy5MmUK1u+jDmz5s2cO3v+DDq06NGkS5s+jTq16tWsW7t+DTu27Nm0a9u+jTu37t28e/v+DTy48OHEixs/jjy58uXMmzt/Dj269OnUq1u/jj279u3cu3v/Dj68+PHky5s/jz69+vXs27t/Dz++/Pn069u/jz+//v38/vv7/w9ggAIOSGCBBh6IYIIKLshggw4+CGGEEk5IYYUWXohhhhpuyGGHHn4IYogijkhiiSaeiGKKKq7IYosuvghjjDLOSGONNt6IY4467shjjz7+CGSQQg5JZJFGHolkkkouyWSTTj4JZZRSTklllVZeiWWWWm7JZZdefglmmGKOSWaZZp6JZppqrslmm26+CWeccs5JZ5123olnnnruyWeffv4JaKCCDkpooYYeimiiii7KaKOOPgpppJJOSmmlll6Kaaaabsppp55+Cmqooo5Kaqmmnopqqqquymqrrr4Ka6yyzkprrbbeimuuuu7Ka6++/gpssMIOS2yxxh6LYWyyyi7LbLPOPgtttNJOS2211l6Lbbbabsttt95+C2644o5Lbrnmnotuuuquy2677r4Lb7zyzktvvfbei2+++u7Lb7/+/gtwwAIPTHDBBh+McMIKL8xwww4/DHHEEk/MUAEAOw==" />
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue