mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-27 21:21:50 +03:00
LibWeb: Obey CSS aspect-ratio property during layout
Calculate a "preferred aspect ratio" based on the value of `aspect-ratio` and the presence of a natural aspect ratio, and use that in layout. This is by no means complete or perfect, but we do now apply the given aspect-ratio to things. The spec is a bit vague, just saying to calculate sizes for aspect-ratio'ed boxes the same as you would for replaced elements. My naive solution here is to find everywhere we were checking for a ReplacedBox, and then also accept a regular Box with a preferred aspect ratio. This gets us pretty far. :^) https://www.w3.org/TR/css-sizing-4/#aspect-ratio-minimum is not at all implemented.
This commit is contained in:
parent
84e7216603
commit
1051624084
Notes:
sideshowbarker
2024-07-17 04:10:16 +09:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/SerenityOS/serenity/commit/1051624084 Pull-request: https://github.com/SerenityOS/serenity/pull/19323
@ -0,0 +1,9 @@
|
||||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x320 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x304 children: not-inline
|
||||
BlockContainer <div> at (9,9) content-size 200x100 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,110) content-size 784x202 children: inline
|
||||
line 0 width: 202, height: 202, bottom: 202, baseline: 202
|
||||
frag 0 from ImageBox start: 0, length: 0, rect: [9,111 200x200]
|
||||
ImageBox <img> at (9,111) content-size 200x200 children: not-inline
|
||||
TextNode <#text>
|
9
Tests/LibWeb/Layout/expected/aspect-ratio-auto.txt
Normal file
9
Tests/LibWeb/Layout/expected/aspect-ratio-auto.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x220 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x204 children: not-inline
|
||||
BlockContainer <div> at (9,9) content-size 200x0 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,10) content-size 784x202 children: inline
|
||||
line 0 width: 202, height: 202, bottom: 202, baseline: 202
|
||||
frag 0 from ImageBox start: 0, length: 0, rect: [9,11 200x200]
|
||||
ImageBox <img> at (9,11) content-size 200x200 children: not-inline
|
||||
TextNode <#text>
|
9
Tests/LibWeb/Layout/expected/aspect-ratio-ratio.txt
Normal file
9
Tests/LibWeb/Layout/expected/aspect-ratio-ratio.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x220 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x204 children: not-inline
|
||||
BlockContainer <div> at (9,9) content-size 200x100 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,110) content-size 784x102 children: inline
|
||||
line 0 width: 202, height: 102, bottom: 102, baseline: 102
|
||||
frag 0 from ImageBox start: 0, length: 0, rect: [9,111 200x100]
|
||||
ImageBox <img> at (9,111) content-size 200x100 children: not-inline
|
||||
TextNode <#text>
|
@ -0,0 +1,7 @@
|
||||
<!doctype html><style>
|
||||
div, img {
|
||||
width: 200px;
|
||||
aspect-ratio: auto 2/1;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style><div></div><img src="120.png"/>
|
7
Tests/LibWeb/Layout/input/aspect-ratio-auto.html
Normal file
7
Tests/LibWeb/Layout/input/aspect-ratio-auto.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!doctype html><style>
|
||||
div, img {
|
||||
width: 200px;
|
||||
aspect-ratio: auto;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style><div></div><img src="120.png"/>
|
7
Tests/LibWeb/Layout/input/aspect-ratio-ratio.html
Normal file
7
Tests/LibWeb/Layout/input/aspect-ratio-ratio.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!doctype html><style>
|
||||
div, img {
|
||||
width: 200px;
|
||||
aspect-ratio: 2/1;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style><div></div><img src="120.png"/>
|
@ -54,6 +54,9 @@ static bool margins_collapse_through(Box const& box, LayoutState& state)
|
||||
// nor top or bottom padding, and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and
|
||||
// all of its in-flow children's margins (if any) collapse.
|
||||
// https://www.w3.org/TR/CSS22/box.html#collapsing-margins
|
||||
// FIXME: For the purpose of margin collapsing (CSS 2 §8.3.1 Collapsing margins), if the block axis is the
|
||||
// ratio-dependent axis, it is not considered to have a computed block-size of auto.
|
||||
// https://www.w3.org/TR/css-sizing-4/#aspect-ratio-margin-collapse
|
||||
return state.get(box).border_box_height() == 0;
|
||||
}
|
||||
|
||||
@ -130,12 +133,14 @@ void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const&
|
||||
remaining_available_space.width = AvailableSize::make_definite(remaining_width);
|
||||
}
|
||||
|
||||
if (is<ReplacedBox>(box)) {
|
||||
if (box_is_sized_as_replaced_element(box)) {
|
||||
// FIXME: This should not be done *by* ReplacedBox
|
||||
auto& replaced = verify_cast<ReplacedBox>(box);
|
||||
// FIXME: This const_cast is gross.
|
||||
const_cast<ReplacedBox&>(replaced).prepare_for_replaced_layout();
|
||||
compute_width_for_block_level_replaced_element_in_normal_flow(replaced, remaining_available_space);
|
||||
if (is<ReplacedBox>(box)) {
|
||||
auto& replaced = verify_cast<ReplacedBox>(box);
|
||||
// FIXME: This const_cast is gross.
|
||||
const_cast<ReplacedBox&>(replaced).prepare_for_replaced_layout();
|
||||
}
|
||||
compute_width_for_block_level_replaced_element_in_normal_flow(box, remaining_available_space);
|
||||
if (box.is_floating()) {
|
||||
// 10.3.6 Floating, replaced elements:
|
||||
// https://www.w3.org/TR/CSS22/visudet.html#float-replaced-width
|
||||
@ -231,7 +236,7 @@ void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const&
|
||||
};
|
||||
|
||||
auto input_width = [&] {
|
||||
if (is<ReplacedBox>(box)) {
|
||||
if (box_is_sized_as_replaced_element(box)) {
|
||||
// NOTE: Replaced elements had their width calculated independently above.
|
||||
// We use that width as the input here to ensure that margins get resolved.
|
||||
return CSS::Length::make_px(box_state.content_width());
|
||||
@ -266,7 +271,7 @@ void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const&
|
||||
}
|
||||
}
|
||||
|
||||
if (!is<ReplacedBox>(box) && !used_width.is_auto())
|
||||
if (!box_is_sized_as_replaced_element(box) && !used_width.is_auto())
|
||||
box_state.set_content_width(used_width.to_px(box));
|
||||
|
||||
box_state.margin_left = margin_left.to_px(box);
|
||||
@ -350,7 +355,7 @@ void BlockFormattingContext::compute_width_for_floating_box(Box const& box, Avai
|
||||
box_state.padding_right = padding_right;
|
||||
}
|
||||
|
||||
void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const& box, AvailableSpace const& available_space)
|
||||
void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// 10.3.6 Floating, replaced elements
|
||||
auto& computed_values = box.computed_values();
|
||||
@ -436,8 +441,8 @@ void BlockFormattingContext::compute_height(Box const& box, AvailableSpace const
|
||||
|
||||
// Then work out what the height is, based on box type and CSS properties.
|
||||
CSSPixels height = 0;
|
||||
if (is<ReplacedBox>(box)) {
|
||||
height = compute_height_for_replaced_element(verify_cast<ReplacedBox>(box), available_space);
|
||||
if (box_is_sized_as_replaced_element(box)) {
|
||||
height = compute_height_for_replaced_element(box, available_space);
|
||||
} else {
|
||||
if (should_treat_height_as_auto(box, available_space)) {
|
||||
height = compute_auto_height_for_block_level_element(box, m_state.get(box).available_inner_space_or_constraints_from(available_space));
|
||||
|
@ -57,7 +57,7 @@ private:
|
||||
|
||||
void compute_width_for_floating_box(Box const&, AvailableSpace const&);
|
||||
|
||||
void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const&, AvailableSpace const&);
|
||||
void compute_width_for_block_level_replaced_element_in_normal_flow(Box const&, AvailableSpace const&);
|
||||
|
||||
CSSPixels compute_table_box_width_inside_table_wrapper(Box const&, AvailableSpace const&);
|
||||
|
||||
|
@ -91,4 +91,12 @@ Painting::PaintableBox const* Box::paintable_box() const
|
||||
return static_cast<Painting::PaintableBox const*>(Node::paintable());
|
||||
}
|
||||
|
||||
Optional<float> Box::preferred_aspect_ratio() const
|
||||
{
|
||||
auto computed_aspect_ratio = computed_values().aspect_ratio();
|
||||
if (computed_aspect_ratio.use_natural_aspect_ratio_if_available && natural_aspect_ratio().has_value())
|
||||
return natural_aspect_ratio();
|
||||
return computed_aspect_ratio.preferred_ratio.map([](CSS::Ratio const& ratio) { return ratio.value(); });
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ public:
|
||||
void set_natural_height(Optional<CSSPixels> height) { m_natural_height = height; }
|
||||
void set_natural_aspect_ratio(Optional<float> ratio) { m_natural_aspect_ratio = ratio; }
|
||||
|
||||
// https://www.w3.org/TR/css-sizing-4/#preferred-aspect-ratio
|
||||
Optional<float> preferred_aspect_ratio() const;
|
||||
bool has_preferred_aspect_ratio() const { return preferred_aspect_ratio().has_value(); }
|
||||
|
||||
virtual ~Box() override;
|
||||
|
||||
virtual void did_set_content_size() { }
|
||||
|
@ -590,12 +590,12 @@ CSSPixels FlexFormattingContext::adjust_main_size_through_aspect_ratio_for_cross
|
||||
{
|
||||
if (!max_cross_size.is_none()) {
|
||||
auto max_cross_size_px = max_cross_size.to_px(box, !is_row_layout() ? m_flex_container_state.content_width() : m_flex_container_state.content_height());
|
||||
main_size = min(main_size, calculate_main_size_from_cross_size_and_aspect_ratio(max_cross_size_px, box.natural_aspect_ratio().value()));
|
||||
main_size = min(main_size, calculate_main_size_from_cross_size_and_aspect_ratio(max_cross_size_px, box.preferred_aspect_ratio().value()));
|
||||
}
|
||||
|
||||
if (!min_cross_size.is_auto()) {
|
||||
auto min_cross_size_px = min_cross_size.to_px(box, !is_row_layout() ? m_flex_container_state.content_width() : m_flex_container_state.content_height());
|
||||
main_size = max(main_size, calculate_main_size_from_cross_size_and_aspect_ratio(min_cross_size_px, box.natural_aspect_ratio().value()));
|
||||
main_size = max(main_size, calculate_main_size_from_cross_size_and_aspect_ratio(min_cross_size_px, box.preferred_aspect_ratio().value()));
|
||||
}
|
||||
|
||||
return main_size;
|
||||
@ -647,13 +647,13 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
|
||||
// - an intrinsic aspect ratio,
|
||||
// - a used flex basis of content, and
|
||||
// - a definite cross size,
|
||||
if (item.box->has_natural_aspect_ratio()
|
||||
if (item.box->has_preferred_aspect_ratio()
|
||||
&& item.used_flex_basis.type == CSS::FlexBasis::Content
|
||||
&& has_definite_cross_size(item.box)) {
|
||||
// flex_base_size is calculated from definite cross size and intrinsic aspect ratio
|
||||
return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(
|
||||
item.box,
|
||||
calculate_main_size_from_cross_size_and_aspect_ratio(inner_cross_size(item.box), item.box->natural_aspect_ratio().value()),
|
||||
calculate_main_size_from_cross_size_and_aspect_ratio(inner_cross_size(item.box), item.box->preferred_aspect_ratio().value()),
|
||||
computed_cross_min_size(item.box),
|
||||
computed_cross_max_size(item.box));
|
||||
}
|
||||
@ -713,7 +713,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
|
||||
|
||||
// AD-HOC: This is not mentioned in the spec, but if the item has an aspect ratio,
|
||||
// we may need to adjust the main size in response to cross size min/max constraints.
|
||||
if (item.box->has_natural_aspect_ratio()) {
|
||||
if (item.box->has_preferred_aspect_ratio()) {
|
||||
item.flex_base_size = adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(child_box, item.flex_base_size, computed_cross_min_size(child_box), computed_cross_max_size(child_box));
|
||||
}
|
||||
|
||||
@ -762,7 +762,7 @@ CSSPixels FlexFormattingContext::content_size_suggestion(FlexItem const& item) c
|
||||
{
|
||||
auto suggestion = calculate_min_content_main_size(item);
|
||||
|
||||
if (item.box->has_natural_aspect_ratio()) {
|
||||
if (item.box->has_preferred_aspect_ratio()) {
|
||||
suggestion = adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(item.box, suggestion, computed_cross_min_size(item.box), computed_cross_max_size(item.box));
|
||||
}
|
||||
|
||||
@ -775,8 +775,8 @@ Optional<CSSPixels> FlexFormattingContext::transferred_size_suggestion(FlexItem
|
||||
// If the item has a preferred aspect ratio and its preferred cross size is definite,
|
||||
// then the transferred size suggestion is that size
|
||||
// (clamped by its minimum and maximum cross sizes if they are definite), converted through the aspect ratio.
|
||||
if (item.box->has_natural_aspect_ratio() && has_definite_cross_size(item.box)) {
|
||||
auto aspect_ratio = item.box->natural_aspect_ratio().value();
|
||||
if (item.box->has_preferred_aspect_ratio() && has_definite_cross_size(item.box)) {
|
||||
auto aspect_ratio = item.box->preferred_aspect_ratio().value();
|
||||
return adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(
|
||||
item.box,
|
||||
calculate_main_size_from_cross_size_and_aspect_ratio(inner_cross_size(item.box), aspect_ratio),
|
||||
|
@ -256,7 +256,7 @@ FormattingContext::ShrinkToFitResult FormattingContext::calculate_shrink_to_fit_
|
||||
};
|
||||
}
|
||||
|
||||
CSSPixelSize FormattingContext::solve_replaced_size_constraint(CSSPixels input_width, CSSPixels input_height, ReplacedBox const& box) const
|
||||
CSSPixelSize FormattingContext::solve_replaced_size_constraint(CSSPixels input_width, CSSPixels input_height, Box const& box) const
|
||||
{
|
||||
// 10.4 Minimum and maximum widths: 'min-width' and 'max-width'
|
||||
|
||||
@ -365,7 +365,7 @@ CSSPixels FormattingContext::compute_auto_height_for_block_formatting_context_ro
|
||||
}
|
||||
|
||||
// 10.3.2 Inline, replaced elements, https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-width
|
||||
CSSPixels FormattingContext::tentative_width_for_replaced_element(ReplacedBox const& box, CSS::Size const& computed_width, AvailableSpace const& available_space) const
|
||||
CSSPixels FormattingContext::tentative_width_for_replaced_element(Box const& box, CSS::Size const& computed_width, AvailableSpace const& available_space) const
|
||||
{
|
||||
// Treat percentages of indefinite containing block widths as 0 (the initial width).
|
||||
if (computed_width.is_percentage() && !m_state.get(*box.containing_block()).has_definite_width())
|
||||
@ -387,16 +387,16 @@ CSSPixels FormattingContext::tentative_width_for_replaced_element(ReplacedBox co
|
||||
// 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value of 'width' is:
|
||||
//
|
||||
// (used height) * (intrinsic ratio)
|
||||
if ((computed_height.is_auto() && computed_width.is_auto() && !box.has_natural_width() && box.has_natural_height() && box.has_natural_aspect_ratio())
|
||||
|| (computed_width.is_auto() && !computed_height.is_auto() && box.has_natural_aspect_ratio())) {
|
||||
return compute_height_for_replaced_element(box, available_space) * static_cast<double>(box.natural_aspect_ratio().value());
|
||||
if ((computed_height.is_auto() && computed_width.is_auto() && !box.has_natural_width() && box.has_natural_height() && box.has_preferred_aspect_ratio())
|
||||
|| (computed_width.is_auto() && !computed_height.is_auto() && box.has_preferred_aspect_ratio())) {
|
||||
return compute_height_for_replaced_element(box, available_space) * static_cast<double>(box.preferred_aspect_ratio().value());
|
||||
}
|
||||
|
||||
// If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width,
|
||||
// then the used value of 'width' is undefined in CSS 2.2. However, it is suggested that, if the containing block's width does not itself
|
||||
// depend on the replaced element's width, then the used value of 'width' is calculated from the constraint equation used for block-level,
|
||||
// non-replaced elements in normal flow.
|
||||
if (computed_height.is_auto() && computed_width.is_auto() && !box.has_natural_width() && !box.has_natural_height() && box.has_natural_aspect_ratio()) {
|
||||
if (computed_height.is_auto() && computed_width.is_auto() && !box.has_natural_width() && !box.has_natural_height() && box.has_preferred_aspect_ratio()) {
|
||||
return calculate_stretch_fit_width(box, available_space.width);
|
||||
}
|
||||
|
||||
@ -414,21 +414,21 @@ CSSPixels FormattingContext::tentative_width_for_replaced_element(ReplacedBox co
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
if (is<ReplacedBox>(box))
|
||||
compute_width_for_absolutely_positioned_replaced_element(verify_cast<ReplacedBox>(box), available_space);
|
||||
if (box_is_sized_as_replaced_element(box))
|
||||
compute_width_for_absolutely_positioned_replaced_element(box, available_space);
|
||||
else
|
||||
compute_width_for_absolutely_positioned_non_replaced_element(box, available_space);
|
||||
}
|
||||
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout before_or_after_inside_layout)
|
||||
{
|
||||
if (is<ReplacedBox>(box))
|
||||
compute_height_for_absolutely_positioned_replaced_element(static_cast<ReplacedBox const&>(box), available_space, before_or_after_inside_layout);
|
||||
if (box_is_sized_as_replaced_element(box))
|
||||
compute_height_for_absolutely_positioned_replaced_element(box, available_space, before_or_after_inside_layout);
|
||||
else
|
||||
compute_height_for_absolutely_positioned_non_replaced_element(box, available_space, before_or_after_inside_layout);
|
||||
}
|
||||
|
||||
CSSPixels FormattingContext::compute_width_for_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space) const
|
||||
CSSPixels FormattingContext::compute_width_for_replaced_element(Box const& box, AvailableSpace const& available_space) const
|
||||
{
|
||||
// 10.3.4 Block-level, replaced elements in normal flow...
|
||||
// 10.3.2 Inline, replaced elements
|
||||
@ -443,7 +443,7 @@ CSSPixels FormattingContext::compute_width_for_replaced_element(ReplacedBox cons
|
||||
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
|
||||
auto used_width = tentative_width_for_replaced_element(box, computed_width, available_space);
|
||||
|
||||
if (computed_width.is_auto() && computed_height.is_auto() && box.has_natural_aspect_ratio()) {
|
||||
if (computed_width.is_auto() && computed_height.is_auto() && box.has_preferred_aspect_ratio()) {
|
||||
CSSPixels w = used_width;
|
||||
CSSPixels h = tentative_height_for_replaced_element(box, computed_height, available_space);
|
||||
used_width = solve_replaced_size_constraint(w, h, box).width();
|
||||
@ -473,7 +473,7 @@ CSSPixels FormattingContext::compute_width_for_replaced_element(ReplacedBox cons
|
||||
|
||||
// 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
|
||||
// https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-height
|
||||
CSSPixels FormattingContext::tentative_height_for_replaced_element(ReplacedBox const& box, CSS::Size const& computed_height, AvailableSpace const& available_space) const
|
||||
CSSPixels FormattingContext::tentative_height_for_replaced_element(Box const& box, CSS::Size const& computed_height, AvailableSpace const& available_space) const
|
||||
{
|
||||
// If 'height' and 'width' both have computed values of 'auto' and the element also has
|
||||
// an intrinsic height, then that intrinsic height is the used value of 'height'.
|
||||
@ -483,8 +483,8 @@ CSSPixels FormattingContext::tentative_height_for_replaced_element(ReplacedBox c
|
||||
// Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is:
|
||||
//
|
||||
// (used width) / (intrinsic ratio)
|
||||
if (computed_height.is_auto() && box.has_natural_aspect_ratio())
|
||||
return m_state.get(box).content_width() / static_cast<double>(box.natural_aspect_ratio().value());
|
||||
if (computed_height.is_auto() && box.has_preferred_aspect_ratio())
|
||||
return m_state.get(box).content_width() / static_cast<double>(box.preferred_aspect_ratio().value());
|
||||
|
||||
// Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'.
|
||||
if (computed_height.is_auto() && box.has_natural_height())
|
||||
@ -500,7 +500,7 @@ CSSPixels FormattingContext::tentative_height_for_replaced_element(ReplacedBox c
|
||||
return calculate_inner_height(box, available_space.height, computed_height).to_px(box);
|
||||
}
|
||||
|
||||
CSSPixels FormattingContext::compute_height_for_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space) const
|
||||
CSSPixels FormattingContext::compute_height_for_replaced_element(Box const& box, AvailableSpace const& available_space) const
|
||||
{
|
||||
// 10.6.2 Inline replaced elements
|
||||
// 10.6.4 Block-level replaced elements in normal flow
|
||||
@ -518,7 +518,7 @@ CSSPixels FormattingContext::compute_height_for_replaced_element(ReplacedBox con
|
||||
// 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_natural_aspect_ratio()) {
|
||||
if (computed_width.is_auto() && computed_height.is_auto() && box.has_preferred_aspect_ratio()) {
|
||||
CSSPixels w = tentative_width_for_replaced_element(box, computed_width, available_space);
|
||||
CSSPixels h = used_height;
|
||||
used_height = solve_replaced_size_constraint(w, h, box).height();
|
||||
@ -689,12 +689,14 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele
|
||||
box_state.padding_right = padding_right;
|
||||
}
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space)
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(Box const& box, AvailableSpace const& available_space)
|
||||
{
|
||||
// 10.3.8 Absolutely positioned, replaced elements
|
||||
// The used value of 'width' is determined as for inline replaced elements.
|
||||
// FIXME: This const_cast is gross.
|
||||
const_cast<ReplacedBox&>(box).prepare_for_replaced_layout();
|
||||
if (is<ReplacedBox>(box)) {
|
||||
// FIXME: This const_cast is gross.
|
||||
static_cast<ReplacedBox&>(const_cast<Box&>(box)).prepare_for_replaced_layout();
|
||||
}
|
||||
m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(box, available_space));
|
||||
}
|
||||
|
||||
@ -1061,7 +1063,7 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box, Ava
|
||||
independent_formatting_context->parent_context_did_dimension_child_root_box();
|
||||
}
|
||||
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout)
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(Box const& box, AvailableSpace const& available_space, BeforeOrAfterInsideLayout)
|
||||
{
|
||||
// 10.6.5 Absolutely positioned, replaced elements
|
||||
// The used value of 'height' is determined as for inline replaced elements.
|
||||
@ -1283,8 +1285,8 @@ CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box
|
||||
|
||||
CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box, AvailableSize const& available_width) const
|
||||
{
|
||||
if (box.has_natural_aspect_ratio() && available_width.is_definite())
|
||||
return available_width.to_px() / static_cast<double>(*box.natural_aspect_ratio());
|
||||
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_natural_height())
|
||||
return *box.natural_height();
|
||||
@ -1623,4 +1625,13 @@ CSSPixelRect FormattingContext::margin_box_rect_in_ancestor_coordinate_space(Box
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool box_is_sized_as_replaced_element(Box const& box)
|
||||
{
|
||||
// When a box has a preferred aspect ratio, its automatic sizes are calculated the same as for a
|
||||
// replaced element with a natural aspect ratio and no natural size in that axis, see e.g. CSS2 §10
|
||||
// and CSS Flexible Box Model Level 1 §9.2.
|
||||
// https://www.w3.org/TR/css-sizing-4/#aspect-ratio-automatic
|
||||
return is<ReplacedBox>(box) || box.has_preferred_aspect_ratio();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ public:
|
||||
|
||||
static bool creates_block_formatting_context(Box const&);
|
||||
|
||||
CSSPixels compute_width_for_replaced_element(ReplacedBox const&, AvailableSpace const&) const;
|
||||
CSSPixels compute_height_for_replaced_element(ReplacedBox const&, AvailableSpace const&) const;
|
||||
CSSPixels compute_width_for_replaced_element(Box const&, AvailableSpace const&) const;
|
||||
CSSPixels compute_height_for_replaced_element(Box const&, AvailableSpace const&) const;
|
||||
|
||||
OwnPtr<FormattingContext> create_independent_formatting_context_if_needed(LayoutState&, Box const& child_box);
|
||||
|
||||
@ -112,18 +112,18 @@ protected:
|
||||
CSSPixels preferred_minimum_width { 0 };
|
||||
};
|
||||
|
||||
CSSPixels tentative_width_for_replaced_element(ReplacedBox const&, CSS::Size const& computed_width, AvailableSpace const&) const;
|
||||
CSSPixels tentative_height_for_replaced_element(ReplacedBox const&, CSS::Size const& computed_height, AvailableSpace const&) const;
|
||||
CSSPixels tentative_width_for_replaced_element(Box const&, CSS::Size const& computed_width, AvailableSpace const&) const;
|
||||
CSSPixels tentative_height_for_replaced_element(Box const&, CSS::Size const& computed_height, AvailableSpace const&) const;
|
||||
CSSPixels compute_auto_height_for_block_formatting_context_root(Box const&) const;
|
||||
|
||||
[[nodiscard]] CSSPixelSize solve_replaced_size_constraint(CSSPixels input_width, CSSPixels input_height, ReplacedBox const&) const;
|
||||
[[nodiscard]] CSSPixelSize solve_replaced_size_constraint(CSSPixels input_width, CSSPixels input_height, Box const&) const;
|
||||
|
||||
ShrinkToFitResult calculate_shrink_to_fit_widths(Box const&);
|
||||
|
||||
void layout_absolutely_positioned_element(Box const&, AvailableSpace const&);
|
||||
void compute_width_for_absolutely_positioned_element(Box const&, AvailableSpace const&);
|
||||
void compute_width_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&);
|
||||
void compute_width_for_absolutely_positioned_replaced_element(ReplacedBox const&, AvailableSpace const&);
|
||||
void compute_width_for_absolutely_positioned_replaced_element(Box const&, AvailableSpace const&);
|
||||
|
||||
enum class BeforeOrAfterInsideLayout {
|
||||
Before,
|
||||
@ -131,7 +131,7 @@ protected:
|
||||
};
|
||||
void compute_height_for_absolutely_positioned_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout);
|
||||
void compute_height_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout);
|
||||
void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const&, AvailableSpace const&, BeforeOrAfterInsideLayout);
|
||||
void compute_height_for_absolutely_positioned_replaced_element(Box const&, AvailableSpace const&, BeforeOrAfterInsideLayout);
|
||||
|
||||
Type m_type {};
|
||||
|
||||
@ -141,4 +141,6 @@ protected:
|
||||
LayoutState& m_state;
|
||||
};
|
||||
|
||||
bool box_is_sized_as_replaced_element(Box const&);
|
||||
|
||||
}
|
||||
|
@ -110,14 +110,12 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l
|
||||
box_state.border_bottom = computed_values.border_bottom().width;
|
||||
box_state.margin_bottom = computed_values.margin().bottom().to_px(box, width_of_containing_block);
|
||||
|
||||
if (is<ReplacedBox>(box)) {
|
||||
auto& replaced = verify_cast<ReplacedBox>(box);
|
||||
|
||||
box_state.set_content_width(compute_width_for_replaced_element(replaced, *m_available_space));
|
||||
box_state.set_content_height(compute_height_for_replaced_element(replaced, *m_available_space));
|
||||
if (box_is_sized_as_replaced_element(box)) {
|
||||
box_state.set_content_width(compute_width_for_replaced_element(box, *m_available_space));
|
||||
box_state.set_content_height(compute_height_for_replaced_element(box, *m_available_space));
|
||||
|
||||
if (is<SVGSVGBox>(box))
|
||||
(void)layout_inside(replaced, layout_mode, box_state.available_inner_space_or_constraints_from(*m_available_space));
|
||||
(void)layout_inside(box, layout_mode, box_state.available_inner_space_or_constraints_from(*m_available_space));
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user