summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibWeb/DOM/Document.cpp7
-rw-r--r--Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp25
-rw-r--r--Userland/Libraries/LibWeb/Layout/AvailableSpace.h41
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp265
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h42
-rw-r--r--Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp374
-rw-r--r--Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h33
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.cpp202
-rw-r--r--Userland/Libraries/LibWeb/Layout/FormattingContext.h45
-rw-r--r--Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp5
-rw-r--r--Userland/Libraries/LibWeb/Layout/GridFormattingContext.h2
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp76
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h11
-rw-r--r--Userland/Libraries/LibWeb/Layout/LayoutState.cpp102
-rw-r--r--Userland/Libraries/LibWeb/Layout/LayoutState.h17
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp8
-rw-r--r--Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h2
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp24
-rw-r--r--Userland/Libraries/LibWeb/Layout/TableFormattingContext.h6
19 files changed, 643 insertions, 644 deletions
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
index df64ff4877..a023d90572 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -804,16 +804,15 @@ void Document::update_layout()
auto& icb = static_cast<Layout::InitialContainingBlock&>(*m_layout_root);
auto& icb_state = layout_state.get_mutable(icb);
- icb_state.set_has_definite_width(true);
- icb_state.set_has_definite_height(true);
icb_state.set_content_width(viewport_rect.width());
icb_state.set_content_height(viewport_rect.height());
root_formatting_context.run(
*m_layout_root,
Layout::LayoutMode::Normal,
- Layout::AvailableSpace::make_definite(viewport_rect.width()),
- Layout::AvailableSpace::make_definite(viewport_rect.height()));
+ Layout::AvailableSpace(
+ Layout::AvailableSize::make_definite(viewport_rect.width()),
+ Layout::AvailableSize::make_definite(viewport_rect.height())));
}
layout_state.commit();
diff --git a/Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp b/Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp
index 026fe90311..605da0780c 100644
--- a/Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp
+++ b/Userland/Libraries/LibWeb/Layout/AvailableSpace.cpp
@@ -9,27 +9,27 @@
namespace Web::Layout {
-AvailableSpace AvailableSpace::make_definite(float value)
+AvailableSize AvailableSize::make_definite(float value)
{
- return AvailableSpace { Type::Definite, value };
+ return AvailableSize { Type::Definite, value };
}
-AvailableSpace AvailableSpace::make_indefinite()
+AvailableSize AvailableSize::make_indefinite()
{
- return AvailableSpace { Type::Indefinite, INFINITY };
+ return AvailableSize { Type::Indefinite, INFINITY };
}
-AvailableSpace AvailableSpace::make_min_content()
+AvailableSize AvailableSize::make_min_content()
{
- return AvailableSpace { Type::MinContent, 0 };
+ return AvailableSize { Type::MinContent, 0 };
}
-AvailableSpace AvailableSpace::make_max_content()
+AvailableSize AvailableSize::make_max_content()
{
- return AvailableSpace { Type::MaxContent, INFINITY };
+ return AvailableSize { Type::MaxContent, INFINITY };
}
-String AvailableSpace::to_string() const
+String AvailableSize::to_string() const
{
switch (m_type) {
case Type::Definite:
@@ -44,7 +44,12 @@ String AvailableSpace::to_string() const
VERIFY_NOT_REACHED();
}
-AvailableSpace::AvailableSpace(Type type, float value)
+String AvailableSpace::to_string() const
+{
+ return String::formatted("{} x {}", width, height);
+}
+
+AvailableSize::AvailableSize(Type type, float value)
: m_type(type)
, m_value(value)
{
diff --git a/Userland/Libraries/LibWeb/Layout/AvailableSpace.h b/Userland/Libraries/LibWeb/Layout/AvailableSpace.h
index 7f319ab9cb..7d2b1e7fb8 100644
--- a/Userland/Libraries/LibWeb/Layout/AvailableSpace.h
+++ b/Userland/Libraries/LibWeb/Layout/AvailableSpace.h
@@ -12,7 +12,7 @@
namespace Web::Layout {
-class AvailableSpace {
+class AvailableSize {
public:
enum class Type {
Definite,
@@ -21,10 +21,10 @@ public:
MaxContent,
};
- static AvailableSpace make_definite(float);
- static AvailableSpace make_indefinite();
- static AvailableSpace make_min_content();
- static AvailableSpace make_max_content();
+ static AvailableSize make_definite(float);
+ static AvailableSize make_indefinite();
+ static AvailableSize make_min_content();
+ static AvailableSize make_max_content();
bool is_definite() const { return m_type == Type::Definite; }
bool is_indefinite() const { return m_type == Type::Indefinite; }
@@ -32,29 +32,52 @@ public:
bool is_max_content() const { return m_type == Type::MaxContent; }
bool is_intrinsic_sizing_constraint() const { return is_min_content() || is_max_content(); }
- float definite_value() const
+ float to_px() const
{
- VERIFY(is_definite());
return m_value;
}
- float to_px() const
+ float to_px_or_zero() const
{
+ if (!is_definite())
+ return 0.0f;
return m_value;
}
String to_string() const;
private:
- AvailableSpace(Type type, float);
+ AvailableSize(Type type, float);
Type m_type {};
float m_value {};
};
+class AvailableSpace {
+public:
+ AvailableSpace(AvailableSize w, AvailableSize h)
+ : width(move(w))
+ , height(move(h))
+ {
+ }
+
+ AvailableSize width;
+ AvailableSize height;
+
+ String to_string() const;
+};
+
}
template<>
+struct AK::Formatter<Web::Layout::AvailableSize> : Formatter<StringView> {
+ ErrorOr<void> format(FormatBuilder& builder, Web::Layout::AvailableSize const& available_size)
+ {
+ return Formatter<StringView>::format(builder, available_size.to_string());
+ }
+};
+
+template<>
struct AK::Formatter<Web::Layout::AvailableSpace> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::Layout::AvailableSpace const& available_space)
{
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
index 5c540403ab..0416ffeac3 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
@@ -6,6 +6,7 @@
#include <LibWeb/CSS/Length.h>
#include <LibWeb/DOM/Node.h>
+#include <LibWeb/Dump.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/Layout/BlockContainer.h>
#include <LibWeb/Layout/BlockFormattingContext.h>
@@ -41,20 +42,20 @@ bool BlockFormattingContext::is_initial() const
float BlockFormattingContext::automatic_content_height() const
{
- return compute_auto_height_for_block_formatting_context_root(m_state, root());
+ return compute_auto_height_for_block_formatting_context_root(root());
}
-void BlockFormattingContext::run(Box const&, LayoutMode layout_mode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
+void BlockFormattingContext::run(Box const&, LayoutMode layout_mode, AvailableSpace const& available_space)
{
if (is_initial()) {
- layout_initial_containing_block(layout_mode);
+ layout_initial_containing_block(layout_mode, available_space);
return;
}
if (root().children_are_inline())
- layout_inline_children(root(), layout_mode);
+ layout_inline_children(root(), layout_mode, available_space);
else
- layout_block_level_children(root(), layout_mode);
+ layout_block_level_children(root(), layout_mode, available_space);
}
void BlockFormattingContext::parent_context_did_dimension_child_root_box()
@@ -64,25 +65,29 @@ void BlockFormattingContext::parent_context_did_dimension_child_root_box()
// Left-side floats: offset_from_edge is from left edge (0) to left content edge of floating_box.
for (auto& floating_box : m_left_floats.all_boxes) {
auto& box_state = m_state.get_mutable(floating_box->box);
- box_state.offset.set_x(floating_box->offset_from_edge);
+ box_state.set_content_x(floating_box->offset_from_edge);
}
// Right-side floats: offset_from_edge is from right edge (float_containing_block_width) to the left content edge of floating_box.
for (auto& floating_box : m_right_floats.all_boxes) {
auto float_containing_block_width = containing_block_width_for(floating_box->box);
auto& box_state = m_state.get_mutable(floating_box->box);
- box_state.offset.set_x(float_containing_block_width - floating_box->offset_from_edge);
+ box_state.set_content_x(float_containing_block_width - floating_box->offset_from_edge);
}
// We can also layout absolutely positioned boxes within this BFC.
- for (auto& box : m_absolutely_positioned_boxes)
- layout_absolutely_positioned_element(box);
+ for (auto& box : m_absolutely_positioned_boxes) {
+ auto& cb_state = m_state.get(*box.containing_block());
+ auto available_width = AvailableSize::make_definite(cb_state.content_width() + cb_state.padding_left + cb_state.padding_right);
+ auto available_height = AvailableSize::make_definite(cb_state.content_height() + cb_state.padding_top + cb_state.padding_bottom);
+ layout_absolutely_positioned_element(box, AvailableSpace(available_width, available_height));
+ }
}
-void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mode)
+void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const& available_space, LayoutMode)
{
if (box.is_absolutely_positioned()) {
- compute_width_for_absolutely_positioned_element(box);
+ compute_width_for_absolutely_positioned_element(box, available_space);
return;
}
@@ -91,30 +96,34 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
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);
+ compute_width_for_block_level_replaced_element_in_normal_flow(replaced, available_space);
// NOTE: We don't return here.
}
if (box.is_floating()) {
- compute_width_for_floating_box(box, layout_mode);
+ compute_width_for_floating_box(box, available_space);
return;
}
auto const& computed_values = box.computed_values();
- float width_of_containing_block = containing_block_width_for(box);
- auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
+
+ if (should_treat_width_as_auto(box, available_space) && available_space.width.is_intrinsic_sizing_constraint())
+ return;
+
+ float width_of_containing_block = available_space.width.to_px();
+ auto width_of_containing_block_as_length_for_resolve = available_space.width.is_definite() ? CSS::Length::make_px(width_of_containing_block) : CSS::Length::make_px(0);
auto zero_value = CSS::Length::make_px(0);
auto margin_left = CSS::Length::make_auto();
auto margin_right = CSS::Length::make_auto();
- auto padding_left = computed_values.padding().left().resolved(box, width_of_containing_block_as_length).resolved(box);
- auto padding_right = computed_values.padding().right().resolved(box, width_of_containing_block_as_length).resolved(box);
+ auto padding_left = computed_values.padding().left().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
+ auto padding_right = computed_values.padding().right().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
auto try_compute_width = [&](auto const& a_width) {
CSS::Length width = a_width;
- margin_left = computed_values.margin().left().resolved(box, width_of_containing_block_as_length).resolved(box);
- margin_right = computed_values.margin().right().resolved(box, width_of_containing_block_as_length).resolved(box);
+ margin_left = computed_values.margin().left().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
+ margin_right = computed_values.margin().right().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
float total_px = computed_values.border_left().width + computed_values.border_right().width;
for (auto& value : { margin_left, padding_left, width, padding_right, margin_right }) {
total_px += value.to_px(box);
@@ -132,6 +141,8 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
// 10.3.3 cont'd.
auto underflow_px = width_of_containing_block - total_px;
+ if (!isfinite(underflow_px))
+ underflow_px = 0;
if (width.is_auto()) {
if (margin_left.is_auto())
@@ -139,19 +150,7 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
if (margin_right.is_auto())
margin_right = zero_value;
- if (width_of_containing_block == INFINITY) {
- // If width of containing block is infinity
- // then we might as well behave like we don't have a containing block
- // and remove it from the calculation. In that case, our width
- // will end up being the sum of margin_*, padding_*, border_*
-
- float sum_of_all = computed_values.border_left().width + computed_values.border_right().width;
- for (const auto& value : { margin_left, padding_left, width, padding_right, margin_right }) {
- sum_of_all += value.to_px(box);
- }
-
- width = CSS::Length(sum_of_all, CSS::Length::Type::Px);
- } else {
+ if (available_space.width.is_definite()) {
if (underflow_px >= 0) {
width = CSS::Length(underflow_px, CSS::Length::Type::Px);
} else {
@@ -178,9 +177,9 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
};
auto input_width = [&] {
- if (should_treat_width_as_auto(box))
+ if (should_treat_width_as_auto(box, available_space))
return CSS::Length::make_auto();
- return computed_values.width().resolved(box, width_of_containing_block_as_length).resolved(box);
+ return computed_values.width().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
}();
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
@@ -189,7 +188,7 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
// 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'.
if (!computed_values.max_width().is_none()) {
- auto max_width = computed_values.max_width().resolved(box, width_of_containing_block_as_length).resolved(box);
+ auto max_width = computed_values.max_width().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
if (used_width.to_px(box) > max_width.to_px(box)) {
used_width = try_compute_width(max_width);
}
@@ -198,7 +197,7 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
// 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
// but this time using the value of 'min-width' as the computed value for 'width'.
if (!computed_values.min_width().is_auto()) {
- auto min_width = computed_values.min_width().resolved(box, width_of_containing_block_as_length).resolved(box);
+ auto min_width = computed_values.min_width().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
if (used_width.to_px(box) < min_width.to_px(box)) {
used_width = try_compute_width(min_width);
}
@@ -206,7 +205,7 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
auto& box_state = m_state.get_mutable(box);
- if (!is<ReplacedBox>(box))
+ if (!is<ReplacedBox>(box) && !used_width.is_auto())
box_state.set_content_width(used_width.to_px(box));
box_state.margin_left = margin_left.to_px(box);
@@ -217,19 +216,21 @@ void BlockFormattingContext::compute_width(Box const& box, LayoutMode layout_mod
box_state.padding_right = padding_right.to_px(box);
}
-void BlockFormattingContext::compute_width_for_floating_box(Box const& box, LayoutMode)
+void BlockFormattingContext::compute_width_for_floating_box(Box const& box, AvailableSpace const& available_space)
{
// 10.3.5 Floating, non-replaced elements
auto& computed_values = box.computed_values();
- float width_of_containing_block = containing_block_width_for(box);
- auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto zero_value = CSS::Length::make_px(0);
+ float width_of_containing_block = available_space.width.to_px();
+ auto width_of_containing_block_as_length_for_resolve = CSS::Length::make_px(width_of_containing_block);
+ if (!available_space.width.is_definite())
+ width_of_containing_block_as_length_for_resolve = CSS::Length::make_px(0);
- auto margin_left = computed_values.margin().left().resolved(box, width_of_containing_block_as_length).resolved(box);
- auto margin_right = computed_values.margin().right().resolved(box, width_of_containing_block_as_length).resolved(box);
- auto const padding_left = computed_values.padding().left().resolved(box, width_of_containing_block_as_length).resolved(box);
- auto const padding_right = computed_values.padding().right().resolved(box, width_of_containing_block_as_length).resolved(box);
+ auto margin_left = computed_values.margin().left().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
+ auto margin_right = computed_values.margin().right().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
+ auto const padding_left = computed_values.padding().left().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
+ auto const padding_right = computed_values.padding().right().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
// If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'.
if (margin_left.is_auto())
@@ -258,9 +259,9 @@ void BlockFormattingContext::compute_width_for_floating_box(Box const& box, Layo
};
auto input_width = [&] {
- if (should_treat_width_as_auto(box))
+ if (should_treat_width_as_auto(box, available_space))
return CSS::Length::make_auto();
- return computed_values.width().resolved(box, width_of_containing_block_as_length).resolved(box);
+ return computed_values.width().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
}();
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
@@ -269,7 +270,7 @@ void BlockFormattingContext::compute_width_for_floating_box(Box const& box, Layo
// 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'.
if (!computed_values.max_width().is_none()) {
- auto max_width = computed_values.max_width().resolved(box, width_of_containing_block_as_length).resolved(box);
+ auto max_width = computed_values.max_width().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
if (width.to_px(box) > max_width.to_px(box))
width = compute_width(max_width);
}
@@ -277,7 +278,7 @@ void BlockFormattingContext::compute_width_for_floating_box(Box const& box, Layo
// 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
// but this time using the value of 'min-width' as the computed value for 'width'.
if (!computed_values.min_width().is_auto()) {
- auto min_width = computed_values.min_width().resolved(box, width_of_containing_block_as_length).resolved(box);
+ auto min_width = computed_values.min_width().resolved(box, width_of_containing_block_as_length_for_resolve).resolved(box);
if (width.to_px(box) < min_width.to_px(box))
width = compute_width(min_width);
}
@@ -292,25 +293,25 @@ void BlockFormattingContext::compute_width_for_floating_box(Box const& box, Layo
box_state.padding_right = padding_right.to_px(box);
}
-void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const& box)
+void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const& box, AvailableSpace const& available_space)
{
- m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(m_state, box));
+ m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(m_state, box, available_space));
}
-void BlockFormattingContext::compute_height(Box const& box, LayoutState& state)
+void BlockFormattingContext::compute_height(Box const& box, AvailableSpace const& available_space)
{
- resolve_vertical_box_model_metrics(box, *box.containing_block(), state);
+ resolve_vertical_box_model_metrics(box, available_space, m_state);
auto const& computed_values = box.computed_values();
- auto containing_block_height = CSS::Length::make_px(containing_block_height_for(box, state));
+ auto containing_block_height = CSS::Length::make_px(available_space.height.to_px());
// Then work out what the height is, based on box type and CSS properties.
float height = 0;
if (is<ReplacedBox>(box)) {
- height = compute_height_for_replaced_element(state, verify_cast<ReplacedBox>(box));
+ height = compute_height_for_replaced_element(m_state, verify_cast<ReplacedBox>(box), available_space);
} else {
- if (should_treat_height_as_auto(box, state)) {
- height = compute_auto_height_for_block_level_element(state, box);
+ if (should_treat_height_as_auto(box, available_space)) {
+ height = compute_auto_height_for_block_level_element(box);
} else {
height = computed_values.height().resolved(box, containing_block_height).to_px(box);
}
@@ -321,51 +322,33 @@ void BlockFormattingContext::compute_height(Box const& box, LayoutState& state)
if (!max_height.is_auto())
height = min(height, max_height.to_px(box));
}
- auto specified_min_height = computed_values.min_height().resolved(box, containing_block_height).resolved(box);
- if (!specified_min_height.is_auto())
- height = max(height, specified_min_height.to_px(box));
+ if (!computed_values.min_height().is_auto()) {
+ auto min_height = computed_values.min_height().resolved(box, containing_block_height).resolved(box);
+ height = max(height, min_height.to_px(box));
+ }
- state.get_mutable(box).set_content_height(height);
+ m_state.get_mutable(box).set_content_height(height);
}
-void BlockFormattingContext::layout_inline_children(BlockContainer const& block_container, LayoutMode layout_mode)
+void BlockFormattingContext::layout_inline_children(BlockContainer const& block_container, LayoutMode layout_mode, AvailableSpace const& available_space)
{
VERIFY(block_container.children_are_inline());
auto& block_container_state = m_state.get_mutable(block_container);
- if (layout_mode == LayoutMode::IntrinsicSizing) {
- if (should_treat_width_as_auto(block_container) || block_container_state.width_constraint != SizeConstraint::None)
- block_container_state.set_content_width(containing_block_width_for(block_container));
- if (should_treat_height_as_auto(block_container) || block_container_state.height_constraint != SizeConstraint::None)
- block_container_state.set_content_height(containing_block_height_for(block_container));
- }
-
InlineFormattingContext context(m_state, block_container, *this);
context.run(
block_container,
layout_mode,
- AvailableSpace::make_definite(containing_block_width_for(block_container)),
- AvailableSpace::make_definite(containing_block_height_for(block_container)));
+ available_space);
- float max_line_width = 0;
- float content_height = 0;
-
- for (auto& line_box : block_container_state.line_boxes) {
- max_line_width = max(max_line_width, line_box.width());
- content_height += line_box.height();
- }
-
- if (layout_mode == LayoutMode::IntrinsicSizing) {
- if (should_treat_width_as_auto(block_container) || block_container_state.width_constraint != SizeConstraint::None)
- block_container_state.set_content_width(max_line_width);
- }
-
- // FIXME: This is weird. Figure out a way to make callers responsible for setting the content height.
- block_container_state.set_content_height(content_height);
+ if (!block_container_state.has_definite_width())
+ block_container_state.set_content_width(context.automatic_content_width());
+ if (!block_container_state.has_definite_height())
+ block_container_state.set_content_height(context.automatic_content_height());
}
-void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContainer const& block_container, LayoutMode layout_mode, float& bottom_of_lowest_margin_box)
+void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContainer const& block_container, LayoutMode layout_mode, float& bottom_of_lowest_margin_box, AvailableSpace const& available_space)
{
auto& box_state = m_state.get_mutable(box);
@@ -379,38 +362,39 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
return;
if (box.is_floating()) {
- layout_floating_box(box, block_container, layout_mode);
+ layout_floating_box(box, block_container, layout_mode, available_space);
bottom_of_lowest_margin_box = max(bottom_of_lowest_margin_box, box_state.offset.y() + box_state.content_height() + box_state.margin_box_bottom());
return;
}
- compute_width(box, layout_mode);
+ compute_width(box, available_space, layout_mode);
+
if (is<ReplacedBox>(box) || is<BlockContainer>(box))
- place_block_level_element_in_normal_flow_vertically(box, block_container);
+ place_block_level_element_in_normal_flow_vertically(box, available_space);
if (box_state.has_definite_height()) {
- compute_height(box, m_state);
+ compute_height(box, available_space);
}
OwnPtr<FormattingContext> independent_formatting_context;
if (box.can_have_children()) {
if (box.children_are_inline()) {
- layout_inline_children(verify_cast<BlockContainer>(box), layout_mode);
+ layout_inline_children(verify_cast<BlockContainer>(box), layout_mode, box_state.available_inner_space_or_constraints_from(available_space));
} else {
independent_formatting_context = create_independent_formatting_context_if_needed(m_state, box);
if (independent_formatting_context)
- independent_formatting_context->run(box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
+ independent_formatting_context->run(box, layout_mode, box_state.available_inner_space_or_constraints_from(available_space));
else
- layout_block_level_children(verify_cast<BlockContainer>(box), layout_mode);
+ layout_block_level_children(verify_cast<BlockContainer>(box), layout_mode, box_state.available_inner_space_or_constraints_from(available_space));
}
}
- compute_height(box, m_state);
+ compute_height(box, available_space);
compute_inset(box);
if (is<ReplacedBox>(box) || is<BlockContainer>(box))
- place_block_level_element_in_normal_flow_horizontally(box, block_container);
+ place_block_level_element_in_normal_flow_horizontally(box, available_space);
if (is<ListItemBox>(box)) {
layout_list_item_marker(static_cast<ListItemBox const&>(box));
@@ -422,54 +406,46 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
independent_formatting_context->parent_context_did_dimension_child_root_box();
}
-void BlockFormattingContext::layout_block_level_children(BlockContainer const& block_container, LayoutMode layout_mode)
+void BlockFormattingContext::layout_block_level_children(BlockContainer const& block_container, LayoutMode layout_mode, AvailableSpace const& available_space)
{
VERIFY(!block_container.children_are_inline());
float bottom_of_lowest_margin_box = 0;
- if (layout_mode == LayoutMode::IntrinsicSizing) {
- auto& block_container_state = m_state.get_mutable(block_container);
- if (should_treat_width_as_auto(block_container) || block_container_state.width_constraint != SizeConstraint::None)
- block_container_state.set_content_width(containing_block_width_for(block_container));
- if (should_treat_height_as_auto(block_container) || block_container_state.height_constraint != SizeConstraint::None)
- block_container_state.set_content_height(containing_block_height_for(block_container));
- }
-
block_container.for_each_child_of_type<Box>([&](Box& box) {
- layout_block_level_box(box, block_container, layout_mode, bottom_of_lowest_margin_box);
+ layout_block_level_box(box, block_container, layout_mode, bottom_of_lowest_margin_box, available_space);
return IterationDecision::Continue;
});
if (layout_mode == LayoutMode::IntrinsicSizing) {
auto& block_container_state = m_state.get_mutable(block_container);
- if (should_treat_width_as_auto(block_container) || block_container_state.width_constraint != SizeConstraint::None)
+ if (!block_container_state.has_definite_width())
block_container_state.set_content_width(greatest_child_width(block_container));
- if (should_treat_height_as_auto(block_container) || block_container_state.height_constraint != SizeConstraint::None)
+ if (!block_container_state.has_definite_height())
block_container_state.set_content_height(bottom_of_lowest_margin_box);
}
}
-void BlockFormattingContext::resolve_vertical_box_model_metrics(Box const& box, BlockContainer const& containing_block, LayoutState& state)
+void BlockFormattingContext::resolve_vertical_box_model_metrics(Box const& box, AvailableSpace const& available_space, LayoutState& state)
{
auto& box_state = state.get_mutable(box);
auto const& computed_values = box.computed_values();
- auto width_of_containing_block = CSS::Length::make_px(containing_block_width_for(box, state));
+ auto width_of_containing_block = CSS::Length::make_px(available_space.width.to_px_or_zero());
- box_state.margin_top = computed_values.margin().top().resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
- box_state.margin_bottom = computed_values.margin().bottom().resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
+ box_state.margin_top = computed_values.margin().top().resolved(box, width_of_containing_block).to_px(box);
+ box_state.margin_bottom = computed_values.margin().bottom().resolved(box, width_of_containing_block).to_px(box);
box_state.border_top = computed_values.border_top().width;
box_state.border_bottom = computed_values.border_bottom().width;
- box_state.padding_top = computed_values.padding().top().resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
- box_state.padding_bottom = computed_values.padding().bottom().resolved(box, width_of_containing_block).resolved(containing_block).to_px(box);
+ box_state.padding_top = computed_values.padding().top().resolved(box, width_of_containing_block).to_px(box);
+ box_state.padding_bottom = computed_values.padding().bottom().resolved(box, width_of_containing_block).to_px(box);
}
-void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically(Box const& child_box, BlockContainer const& containing_block)
+void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically(Box const& child_box, AvailableSpace const& available_space)
{
auto& box_state = m_state.get_mutable(child_box);
auto const& computed_values = child_box.computed_values();
- resolve_vertical_box_model_metrics(child_box, containing_block, m_state);
+ resolve_vertical_box_model_metrics(child_box, available_space, m_state);
auto y = FormattingContext::compute_box_y_position_with_respect_to_siblings(child_box, box_state);
@@ -504,15 +480,15 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
if ((computed_values.clear() == CSS::Clear::Right || computed_values.clear() == CSS::Clear::Both) && !child_box.is_flex_item())
clear_floating_boxes(m_right_floats);
- box_state.offset = Gfx::FloatPoint { box_state.offset.x(), y };
+ box_state.set_content_offset(Gfx::FloatPoint { box_state.offset.x(), y });
}
-void BlockFormattingContext::place_block_level_element_in_normal_flow_horizontally(Box const& child_box, BlockContainer const& containing_block)
+void BlockFormattingContext::place_block_level_element_in_normal_flow_horizontally(Box const& child_box, AvailableSpace const& available_space)
{
auto& box_state = m_state.get_mutable(child_box);
float x = 0;
- float available_width_within_containing_block = containing_block_width_for(child_box);
+ float available_width_within_containing_block = available_space.width.to_px();
if ((!m_left_floats.current_boxes.is_empty() || !m_right_floats.current_boxes.is_empty())
&& creates_block_formatting_context(child_box)) {
@@ -520,13 +496,13 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_horizontal
x += m_left_floats.current_width;
}
- if (containing_block.computed_values().text_align() == CSS::TextAlign::LibwebCenter) {
+ if (child_box.containing_block()->computed_values().text_align() == CSS::TextAlign::LibwebCenter) {
x += (available_width_within_containing_block / 2) - box_state.content_width() / 2;
} else {
x += box_state.margin_box_left();
}
- box_state.offset = Gfx::FloatPoint { x, box_state.offset.y() };
+ box_state.set_content_offset(Gfx::FloatPoint { x, box_state.offset.y() });
}
static void measure_scrollable_overflow(LayoutState const& state, Box const& box, float& bottom_edge, float& right_edge)
@@ -547,7 +523,7 @@ static void measure_scrollable_overflow(LayoutState const& state, Box const& box
});
}
-void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_mode)
+void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_mode, AvailableSpace const& available_space)
{
auto viewport_rect = root().browsing_context().viewport_rect();
@@ -555,9 +531,9 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m
auto& icb_state = m_state.get_mutable(icb);
if (root().children_are_inline())
- layout_inline_children(root(), layout_mode);
+ layout_inline_children(root(), layout_mode, available_space);
else
- layout_block_level_children(root(), layout_mode);
+ layout_block_level_children(root(), layout_mode, available_space);
float bottom_edge = 0;
float right_edge = 0;
@@ -572,25 +548,26 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m
}
}
-void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer const& containing_block, LayoutMode layout_mode, LineBuilder* line_builder)
+void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer const&, LayoutMode layout_mode, AvailableSpace const& available_space, LineBuilder* line_builder)
{
VERIFY(box.is_floating());
auto& box_state = m_state.get_mutable(box);
- float width_of_containing_block = containing_block_width_for(box);
+ float width_of_containing_block = available_space.width.to_px();
- compute_width(box, layout_mode);
- (void)layout_inside(box, layout_mode);
- compute_height(box, m_state);
+ compute_width(box, available_space, layout_mode);
+ if (auto independent_formatting_context = layout_inside(box, layout_mode, box_state.available_inner_space_or_constraints_from(available_space)))
+ independent_formatting_context->parent_context_did_dimension_child_root_box();
+ compute_height(box, available_space);
// First we place the box normally (to get the right y coordinate.)
// If we have a LineBuilder, we're in the middle of inline layout, otherwise this is block layout.
if (line_builder) {
auto y = line_builder->y_for_float_to_be_inserted_here(box);
- box_state.offset.set_y(y + box_state.margin_box_top());
+ box_state.set_content_y(y + box_state.margin_box_top());
} else {
- place_block_level_element_in_normal_flow_vertically(box, containing_block);
- place_block_level_element_in_normal_flow_horizontally(box, containing_block);
+ place_block_level_element_in_normal_flow_vertically(box, available_space);
+ place_block_level_element_in_normal_flow_horizontally(box, available_space);
}
// Then we float it to the left or right.
@@ -678,7 +655,7 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
// NOTE: We don't set the X position here, that happens later, once we know the root block width.
// See parent_context_did_dimension_child_root_box() for that logic.
- box_state.offset.set_y(y);
+ box_state.set_content_y(y);
// If the new box was inserted below the bottom of the opposite side,
// we reset the other side back to its edge.
@@ -726,10 +703,8 @@ void BlockFormattingContext::layout_list_item_marker(ListItemBox const& list_ite
marker_state.set_content_height(max(image_height, marker.font().glyph_height() + 1));
- marker_state.offset = {
- -(marker_state.content_width() + default_marker_width),
- max(0.f, (marker.line_height() - marker_state.content_height()) / 2.f)
- };
+ marker_state.set_content_offset({ -(marker_state.content_width() + default_marker_width),
+ max(0.f, (marker.line_height() - marker_state.content_height()) / 2.f) });
if (marker_state.content_height() > list_item_state.content_height())
list_item_state.set_content_height(marker_state.content_height());
@@ -801,16 +776,26 @@ float BlockFormattingContext::greatest_child_width(Box const& box)
return max_width;
}
-bool BlockFormattingContext::should_treat_width_as_auto(Box const& box, LayoutState const& state)
+bool BlockFormattingContext::should_treat_width_as_auto(Box const& box, AvailableSpace const& available_space)
{
return box.computed_values().width().is_auto()
- || (box.computed_values().width().contains_percentage() && !state.get(*box.containing_block()).has_definite_width());
+ || (box.computed_values().width().contains_percentage() && !available_space.width.is_definite());
}
-bool BlockFormattingContext::should_treat_height_as_auto(Box const& box, LayoutState const& state)
+bool BlockFormattingContext::should_treat_height_as_auto(Box const& box, AvailableSpace const& available_space)
{
return box.computed_values().height().is_auto()
- || (box.computed_values().height().contains_percentage() && !state.get(*box.containing_block()).has_definite_height());
+ || (box.computed_values().height().contains_percentage() && !available_space.height.is_definite());
+}
+
+void BlockFormattingContext::determine_width_of_child(Box const& box, AvailableSpace const& available_space)
+{
+ compute_width(box, available_space);
+}
+
+void BlockFormattingContext::determine_height_of_child(Box const& box, AvailableSpace const& available_space)
+{
+ compute_height(box, available_space);
}
}
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
index 03b1e5ef63..55f35c7156 100644
--- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
@@ -21,7 +21,7 @@ public:
explicit BlockFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent);
~BlockFormattingContext();
- virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) override;
virtual float automatic_content_height() const override;
bool is_initial() const;
@@ -29,14 +29,14 @@ public:
auto const& left_side_floats() const { return m_left_floats; }
auto const& right_side_floats() const { return m_right_floats; }
- void compute_width(Box const&, LayoutMode = LayoutMode::Normal);
+ void compute_width(Box const&, AvailableSpace const&, LayoutMode = LayoutMode::Normal);
// https://www.w3.org/TR/css-display/#block-formatting-context-root
BlockContainer const& root() const { return static_cast<BlockContainer const&>(context_box()); }
virtual void parent_context_did_dimension_child_root_box() override;
- static void compute_height(Box const&, LayoutState&);
+ void compute_height(Box const&, AvailableSpace const&);
void add_absolutely_positioned_box(Box const& box) { m_absolutely_positioned_boxes.append(box); }
@@ -44,38 +44,32 @@ public:
virtual float greatest_child_width(Box const&) override;
- void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode, LineBuilder* = nullptr);
+ void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode, AvailableSpace const&, LineBuilder* = nullptr);
- void layout_block_level_box(Box const&, BlockContainer const&, LayoutMode, float& bottom_of_lowest_margin_box);
+ void layout_block_level_box(Box const&, BlockContainer const&, LayoutMode, float& bottom_of_lowest_margin_box, AvailableSpace const&);
- static bool should_treat_width_as_auto(Box const&, LayoutState const&);
- static bool should_treat_height_as_auto(Box const&, LayoutState const&);
+ static bool should_treat_width_as_auto(Box const&, AvailableSpace const&);
+ static bool should_treat_height_as_auto(Box const&, AvailableSpace const&);
- bool should_treat_width_as_auto(Box const& box) const
- {
- return should_treat_width_as_auto(box, m_state);
- }
-
- bool should_treat_height_as_auto(Box const& box) const
- {
- return should_treat_height_as_auto(box, m_state);
- }
+ virtual bool can_determine_size_of_child() const override { return true; }
+ virtual void determine_width_of_child(Box const&, AvailableSpace const&) override;
+ virtual void determine_height_of_child(Box const&, AvailableSpace const&) override;
private:
virtual bool is_block_formatting_context() const final { return true; }
- void compute_width_for_floating_box(Box const&, LayoutMode);
+ void compute_width_for_floating_box(Box const&, AvailableSpace const&);
- void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const&);
+ void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox const&, AvailableSpace const&);
- void layout_initial_containing_block(LayoutMode);
+ void layout_initial_containing_block(LayoutMode, AvailableSpace const&);
- void layout_block_level_children(BlockContainer const&, LayoutMode);
- void layout_inline_children(BlockContainer const&, LayoutMode);
+ void layout_block_level_children(BlockContainer const&, LayoutMode, AvailableSpace const&);
+ void layout_inline_children(BlockContainer const&, LayoutMode, AvailableSpace const&);
- static void resolve_vertical_box_model_metrics(Box const& box, BlockContainer const& containing_block, LayoutState&);
- void place_block_level_element_in_normal_flow_horizontally(Box const& child_box, BlockContainer const&);
- void place_block_level_element_in_normal_flow_vertically(Box const& child_box, BlockContainer const&);
+ static void resolve_vertical_box_model_metrics(Box const& box, AvailableSpace const&, LayoutState&);
+ void place_block_level_element_in_normal_flow_horizontally(Box const& child_box, AvailableSpace const&);
+ void place_block_level_element_in_normal_flow_vertically(Box const& child_box, AvailableSpace const&);
void layout_list_item_marker(ListItemBox const&);
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
index e8e81f9e38..eb13e6966f 100644
--- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp
@@ -67,10 +67,29 @@ float FlexFormattingContext::automatic_content_height() const
return m_state.get(flex_container()).content_height();
}
-void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
+void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace const& available_content_space)
{
VERIFY(&run_box == &flex_container());
+ // NOTE: The available space provided by the parent context is basically our *content box*.
+ // FFC is currently written in a way that expects that to include padding and border as well,
+ // so we pad out the available space here to accommodate that.
+ // FIXME: Refactor the necessary parts of FFC so we don't need this hack!
+
+ auto available_width = available_content_space.width;
+ if (available_width.is_definite())
+ available_width = AvailableSize::make_definite(available_width.to_px() + m_flex_container_state.border_box_left() + m_flex_container_state.border_box_right());
+ auto available_height = available_content_space.height;
+ if (available_height.is_definite())
+ available_height = AvailableSize::make_definite(available_height.to_px() + m_flex_container_state.border_box_top() + m_flex_container_state.border_box_bottom());
+
+ m_available_space_for_flex_container = AxisAgnosticAvailableSpace {
+ .main = is_row_layout() ? available_width : available_height,
+ .cross = !is_row_layout() ? available_width : available_height,
+ .width = available_width,
+ .height = available_height,
+ };
+
// This implements https://www.w3.org/TR/css-flexbox-1/#layout-algorithm
// 1. Generate anonymous flex items
@@ -91,34 +110,14 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[ma
auto item_preferred_outer_cross_size = css_clamp(flex_container_inner_cross_size, item_min_cross_size, item_max_cross_size);
auto item_inner_cross_size = item_preferred_outer_cross_size - item.margins.cross_before - item.margins.cross_after - item.padding.cross_before - item.padding.cross_after - item.borders.cross_before - item.borders.cross_after;
set_cross_size(item.box, item_inner_cross_size);
- set_has_definite_cross_size(item.box, true);
- item.has_assigned_definite_cross_size = true;
}
}
}
// 2. Determine the available main and cross space for the flex items
- float main_max_size = NumericLimits<float>::max();
- float main_min_size = 0;
- float cross_max_size = NumericLimits<float>::max();
- float cross_min_size = 0;
- bool main_is_constrained = false;
- bool cross_is_constrained = false;
- determine_available_main_and_cross_space(main_is_constrained, cross_is_constrained, main_min_size, main_max_size, cross_min_size, cross_max_size);
-
- if (m_flex_container_state.width_constraint == SizeConstraint::MaxContent || m_flex_container_state.height_constraint == SizeConstraint::MaxContent) {
- if (is_row_layout())
- m_available_space->main = INFINITY;
- else
- m_available_space->cross = INFINITY;
- }
-
- if (m_flex_container_state.width_constraint == SizeConstraint::MinContent || m_flex_container_state.height_constraint == SizeConstraint::MinContent) {
- if (is_row_layout())
- m_available_space->main = 0;
- else
- m_available_space->cross = 0;
- }
+ float cross_min_size = has_cross_min_size(flex_container()) ? specified_cross_min_size(flex_container()) : 0;
+ float cross_max_size = has_cross_max_size(flex_container()) ? specified_cross_max_size(flex_container()) : INFINITY;
+ determine_available_space_for_items(AvailableSpace(available_width, available_height));
// 3. Determine the flex base size and hypothetical main size of each item
for (auto& flex_item : m_flex_items) {
@@ -129,9 +128,9 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[ma
determine_flex_base_size_and_hypothetical_main_size(flex_item);
}
- if (m_flex_container_state.width_constraint != SizeConstraint::None || m_flex_container_state.height_constraint != SizeConstraint::None) {
+ if (available_width.is_intrinsic_sizing_constraint() || available_height.is_intrinsic_sizing_constraint()) {
// We're computing intrinsic size for the flex container.
- determine_intrinsic_size_of_flex_container(layout_mode);
+ determine_intrinsic_size_of_flex_container();
// Our caller is only interested in the content-width and content-height results,
// which have now been set on m_flex_container_state, so there's no need to continue
@@ -140,7 +139,7 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[ma
}
// 4. Determine the main size of the flex container
- determine_main_size_of_flex_container(main_is_constrained, main_min_size, main_max_size);
+ determine_main_size_of_flex_container();
// 5. Collect flex items into flex lines:
// After this step no additional items are to be added to flex_lines or any of its items!
@@ -187,8 +186,6 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[ma
if (flex_container_computed_cross_size.is_auto()) {
for (auto& item : m_flex_items) {
set_cross_size(item.box, item.cross_size);
- set_has_definite_cross_size(item.box, true);
- item.has_assigned_definite_cross_size = true;
}
}
}
@@ -211,7 +208,8 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[ma
// AD-HOC: Layout the inside of all flex items.
copy_dimensions_from_flex_items_to_boxes();
for (auto& flex_item : m_flex_items) {
- if (auto independent_formatting_context = layout_inside(flex_item.box, LayoutMode::Normal))
+ auto& box_state = m_state.get(flex_item.box);
+ if (auto independent_formatting_context = layout_inside(flex_item.box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(AvailableSpace(m_available_space_for_flex_container->width, m_available_space_for_flex_container->height))))
independent_formatting_context->parent_context_did_dimension_child_root_box();
}
@@ -225,8 +223,12 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode layout_mode, [[ma
void FlexFormattingContext::parent_context_did_dimension_child_root_box()
{
flex_container().for_each_child_of_type<Box>([&](Layout::Box& box) {
- if (box.is_absolutely_positioned())
- layout_absolutely_positioned_element(box);
+ if (box.is_absolutely_positioned()) {
+ auto& cb_state = m_state.get(*box.containing_block());
+ auto available_width = AvailableSize::make_definite(cb_state.content_width() + cb_state.padding_left + cb_state.padding_right);
+ auto available_height = AvailableSize::make_definite(cb_state.content_height() + cb_state.padding_top + cb_state.padding_bottom);
+ layout_absolutely_positioned_element(box, AvailableSpace(available_width, available_height));
+ }
});
}
@@ -363,15 +365,11 @@ float FlexFormattingContext::specified_cross_size(Box const& box) const
float FlexFormattingContext::resolved_definite_cross_size(FlexItem const& item) const
{
- if (item.has_assigned_definite_cross_size)
- return specified_cross_size(item.box);
return !is_row_layout() ? m_state.resolved_definite_width(item.box) : m_state.resolved_definite_height(item.box);
}
float FlexFormattingContext::resolved_definite_main_size(FlexItem const& item) const
{
- if (item.has_assigned_definite_main_size)
- return specified_main_size(item.box);
return is_row_layout() ? m_state.resolved_definite_width(item.box) : m_state.resolved_definite_height(item.box);
}
@@ -393,13 +391,6 @@ bool FlexFormattingContext::has_definite_cross_size(Box const& box) const
return is_row_layout() ? used_values.has_definite_height() : used_values.has_definite_width();
}
-float FlexFormattingContext::specified_main_size_of_child_box(Box const& child_box) const
-{
- auto main_size_of_parent = specified_main_size(flex_container());
- auto& value = is_row_layout() ? child_box.computed_values().width() : child_box.computed_values().height();
- return value.resolved(child_box, CSS::Length::make_px(main_size_of_parent)).to_px(child_box);
-}
-
float FlexFormattingContext::specified_main_min_size(Box const& box) const
{
return is_row_layout()
@@ -440,12 +431,6 @@ float FlexFormattingContext::specified_cross_max_size(Box const& box) const
: get_pixel_width(box, box.computed_values().max_width());
}
-float FlexFormattingContext::calculated_main_size(Box const& box) const
-{
- auto const& box_state = m_state.get(box);
- return is_row_layout() ? box_state.content_width() : box_state.content_height();
-}
-
bool FlexFormattingContext::is_cross_auto(Box const& box) const
{
auto& cross_length = is_row_layout() ? box.computed_values().height() : box.computed_values().width();
@@ -468,24 +453,6 @@ void FlexFormattingContext::set_cross_size(Box const& box, float size)
m_state.get_mutable(box).set_content_width(size);
}
-void FlexFormattingContext::set_has_definite_main_size(Box const& box, bool definite)
-{
- auto& used_values = m_state.get_mutable(box);
- if (is_row_layout())
- used_values.set_has_definite_width(definite);
- else
- used_values.set_has_definite_height(definite);
-}
-
-void FlexFormattingContext::set_has_definite_cross_size(Box const& box, bool definite)
-{
- auto& used_values = m_state.get_mutable(box);
- if (!is_row_layout())
- used_values.set_has_definite_width(definite);
- else
- used_values.set_has_definite_height(definite);
-}
-
void FlexFormattingContext::set_offset(Box const& box, float main_offset, float cross_offset)
{
if (is_row_layout())
@@ -512,93 +479,73 @@ void FlexFormattingContext::set_main_axis_second_margin(FlexItem& item, float ma
m_state.get_mutable(item.box).margin_bottom = margin;
}
-float FlexFormattingContext::sum_of_margin_padding_border_in_main_axis(Box const& box) const
+// https://drafts.csswg.org/css-flexbox-1/#algo-available
+void FlexFormattingContext::determine_available_space_for_items(AvailableSpace const& available_space)
{
- auto const& box_state = m_state.get(box);
+ // For each dimension, if that dimension of the flex container’s content box is a definite size, use that;
+ // if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint;
+ // otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value.
+ // This might result in an infinite value.
- if (is_row_layout()) {
- return box_state.margin_left + box_state.margin_right
- + box_state.padding_left + box_state.padding_right
- + box_state.border_left + box_state.border_right;
+ Optional<AvailableSize> available_width_for_items;
+ if (m_flex_container_state.has_definite_width()) {
+ available_width_for_items = AvailableSize::make_definite(m_state.resolved_definite_width(flex_container()));
} else {
- return box_state.margin_top + box_state.margin_bottom
- + box_state.padding_top + box_state.padding_bottom
- + box_state.border_top + box_state.border_bottom;
- }
-}
-
-// https://www.w3.org/TR/css-flexbox-1/#algo-available
-void FlexFormattingContext::determine_available_main_and_cross_space(bool& main_is_constrained, bool& cross_is_constrained, float& main_min_size, float& main_max_size, float& cross_min_size, float& cross_max_size)
-{
- auto containing_block_effective_main_size = [&](Box const& box) -> Optional<float> {
- auto& containing_block = *box.containing_block();
- if (has_definite_main_size(containing_block))
- return is_row_layout() ? m_state.resolved_definite_width(box) : m_state.resolved_definite_height(box);
- return {};
- };
-
- Optional<float> main_available_space;
- main_is_constrained = false;
-
- // For each dimension,
- // if that dimension of the flex container’s content box is a definite size, use that;
- // if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint;
- // otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. (This might result in an infinite value.)
-
- if (has_definite_main_size(flex_container())) {
- main_is_constrained = true;
- main_available_space = specified_main_size(flex_container());
- } else {
- if (has_main_max_size(flex_container())) {
- bool main_max_size_behaves_like_auto = false;
- if (computed_main_max_size(flex_container()).contains_percentage())
- main_max_size_behaves_like_auto = !has_definite_main_size(*flex_container().containing_block());
-
- if (!main_max_size_behaves_like_auto) {
- main_max_size = specified_main_max_size(flex_container());
- main_available_space = main_max_size;
- main_is_constrained = true;
+ if (available_space.width.is_intrinsic_sizing_constraint()) {
+ available_width_for_items = available_space.width;
+ } else {
+ if (available_space.width.is_definite()) {
+ auto remaining = available_space.width.to_px()
+ - m_flex_container_state.margin_left
+ - m_flex_container_state.margin_right
+ - m_flex_container_state.border_left
+ - m_flex_container_state.padding_right
+ - m_flex_container_state.padding_left
+ - m_flex_container_state.padding_right;
+ available_width_for_items = AvailableSize::make_definite(remaining);
+ } else {
+ available_width_for_items = AvailableSize::make_indefinite();
}
}
- if (has_main_min_size(flex_container())) {
- main_min_size = specified_main_min_size(flex_container());
- main_is_constrained = true;
- }
-
- if (!main_is_constrained) {
- auto available_main_size = containing_block_effective_main_size(flex_container());
- main_available_space = available_main_size.value_or(NumericLimits<float>::max()) - sum_of_margin_padding_border_in_main_axis(flex_container());
- }
}
- Optional<float> cross_available_space;
- cross_is_constrained = false;
-
- if (has_definite_cross_size(flex_container())) {
- cross_available_space = specified_cross_size(flex_container());
+ Optional<AvailableSize> available_height_for_items;
+ if (m_flex_container_state.has_definite_height()) {
+ available_height_for_items = AvailableSize::make_definite(m_state.resolved_definite_height(flex_container()));
} else {
- if (has_cross_max_size(flex_container())) {
-
- bool cross_max_size_behaves_like_auto = false;
- if (computed_cross_max_size(flex_container()).contains_percentage())
- cross_max_size_behaves_like_auto = !has_definite_cross_size(*flex_container().containing_block());
-
- if (!cross_max_size_behaves_like_auto) {
- cross_max_size = specified_cross_max_size(flex_container());
- cross_is_constrained = true;
+ if (available_space.height.is_intrinsic_sizing_constraint()) {
+ available_height_for_items = available_space.height;
+ } else {
+ if (available_space.height.is_definite()) {
+ auto remaining = available_space.height.to_px()
+ - m_flex_container_state.margin_top
+ - m_flex_container_state.margin_bottom
+ - m_flex_container_state.border_top
+ - m_flex_container_state.padding_bottom
+ - m_flex_container_state.padding_top
+ - m_flex_container_state.padding_bottom;
+ available_height_for_items = AvailableSize::make_definite(remaining);
+ } else {
+ available_height_for_items = AvailableSize::make_indefinite();
}
}
- if (has_cross_min_size(flex_container())) {
- cross_min_size = specified_cross_min_size(flex_container());
- cross_is_constrained = true;
- }
-
- // FIXME: Is this right? Probably not.
- if (!cross_is_constrained)
- cross_available_space = cross_max_size;
}
- m_available_space = AvailableSpaceForItems { .main = main_available_space, .cross = cross_available_space };
+ if (is_row_layout()) {
+ m_available_space_for_items = AxisAgnosticAvailableSpace {
+ .main = *available_width_for_items,
+ .cross = *available_height_for_items,
+ .width = *available_width_for_items,
+ .height = *available_height_for_items,
+ };
+ } else {
+ m_available_space_for_items = AxisAgnosticAvailableSpace {
+ .main = *available_height_for_items,
+ .cross = *available_width_for_items,
+ .width = *available_width_for_items,
+ .height = *available_height_for_items,
+ };
+ }
}
float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item)
@@ -635,7 +582,7 @@ float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item
VERIFY(independent_formatting_context);
box_state.set_content_width(fit_content_cross_size);
- independent_formatting_context->run(item.box, LayoutMode::Normal, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
+ independent_formatting_context->run(item.box, LayoutMode::Normal, AvailableSpace(m_available_space_for_items->width, m_available_space_for_items->height));
return independent_formatting_context->automatic_content_height();
}
@@ -719,9 +666,8 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
// and the flex container is being sized under a min-content or max-content constraint
// (e.g. when performing automatic table layout [CSS21]), size the item under that constraint.
// The flex base size is the item’s resulting main size.
- auto flex_container_main_size_constraint = is_row_layout() ? m_flex_container_state.width_constraint : m_flex_container_state.height_constraint;
- if (flex_item.used_flex_basis.type == CSS::FlexBasis::Content && flex_container_main_size_constraint != SizeConstraint::None) {
- if (flex_container_main_size_constraint == SizeConstraint::MinContent)
+ if (flex_item.used_flex_basis.type == CSS::FlexBasis::Content && m_available_space_for_items->main.is_intrinsic_sizing_constraint()) {
+ if (m_available_space_for_items->main.is_min_content())
return calculate_min_content_main_size(flex_item);
return calculate_max_content_main_size(flex_item);
}
@@ -825,15 +771,39 @@ float FlexFormattingContext::content_based_minimum_size(FlexItem const& item) co
return unclamped_size;
}
-// https://www.w3.org/TR/css-flexbox-1/#algo-main-container
-void FlexFormattingContext::determine_main_size_of_flex_container(bool const main_is_constrained, float const main_min_size, float const main_max_size)
+// https://drafts.csswg.org/css-flexbox-1/#algo-main-container
+void FlexFormattingContext::determine_main_size_of_flex_container()
{
- // FIXME: This needs to be reworked.
- if (!main_is_constrained || !m_available_space->main.has_value()) {
- auto result = is_row_layout() ? calculate_max_content_width(flex_container()) : calculate_max_content_height(flex_container());
- m_available_space->main = css_clamp(result, main_min_size, main_max_size);
+ // Determine the main size of the flex container using the rules of the formatting context in which it participates.
+ // NOTE: The automatic block size of a block-level flex container is its max-content size.
+
+ // FIXME: Once all parent contexts now how to size a given child, we can remove
+ // `can_determine_size_of_child()`.
+ if (parent()->can_determine_size_of_child()) {
+ AvailableSpace available_space(m_available_space_for_flex_container->width, m_available_space_for_flex_container->height);
+ if (is_row_layout()) {
+ parent()->determine_width_of_child(flex_container(), available_space);
+ } else {
+ parent()->determine_height_of_child(flex_container(), available_space);
+ }
+ return;
+ }
+
+ // HACK: The hack below doesn't know how to size absolutely positioned flex containers at all.
+ // We just leave it alone for now and let the parent context deal with it.
+ if (flex_container().is_absolutely_positioned())
+ return;
+
+ if (is_row_layout()) {
+ if (!flex_container().is_out_of_flow(*parent()) && m_state.get(*flex_container().containing_block()).has_definite_width()) {
+ set_main_size(flex_container(), calculate_stretch_fit_width(flex_container(), m_available_space_for_flex_container->main));
+ } else {
+ set_main_size(flex_container(), calculate_max_content_width(flex_container()));
+ }
+ } else {
+ if (!has_definite_main_size(flex_container()))
+ set_main_size(flex_container(), calculate_max_content_height(flex_container()));
}
- set_main_size(flex_container(), m_available_space->main.value_or(NumericLimits<float>::max()));
}
// https://www.w3.org/TR/css-flexbox-1/#algo-line-break
@@ -864,7 +834,7 @@ void FlexFormattingContext::collect_flex_items_into_flex_lines()
float line_main_size = 0;
for (auto& flex_item : m_flex_items) {
auto outer_hypothetical_main_size = flex_item.hypothetical_main_size + flex_item.margins.main_before + flex_item.margins.main_after + flex_item.borders.main_before + flex_item.borders.main_after + flex_item.padding.main_before + flex_item.padding.main_after;
- if ((line_main_size + outer_hypothetical_main_size) > m_available_space->main.value_or(NumericLimits<float>::max())) {
+ if (!line.items.is_empty() && (line_main_size + outer_hypothetical_main_size) > specified_main_size(flex_container())) {
m_flex_lines.append(move(line));
line = {};
line_main_size = 0;
@@ -892,7 +862,7 @@ void FlexFormattingContext::resolve_flexible_lengths()
for (auto& flex_item : flex_line.items) {
sum_of_hypothetical_main_sizes += (flex_item->hypothetical_main_size + flex_item->margins.main_before + flex_item->margins.main_after + flex_item->borders.main_before + flex_item->borders.main_after + flex_item->padding.main_before + flex_item->padding.main_after);
}
- if (sum_of_hypothetical_main_sizes < m_available_space->main.value_or(NumericLimits<float>::max()))
+ if (sum_of_hypothetical_main_sizes < specified_main_size(flex_container()))
used_flex_factor = FlexFactor::FlexGrowFactor;
else
used_flex_factor = FlexFactor::FlexShrinkFactor;
@@ -1042,14 +1012,6 @@ void FlexFormattingContext::resolve_flexible_lengths()
for (auto& flex_item : flex_line.items) {
flex_item->main_size = flex_item->target_main_size;
set_main_size(flex_item->box, flex_item->main_size);
-
- // https://drafts.csswg.org/css-flexbox-1/#definite-sizes
- // 1. If the flex container has a definite main size, then the post-flexing main sizes of its flex items are treated as definite.
- // 2. If a flex-item’s flex basis is definite, then its post-flexing main size is also definite.
- if (has_definite_main_size(flex_container()) || flex_item->used_flex_basis_is_definite) {
- set_has_definite_main_size(flex_item->box, true);
- flex_item->has_assigned_definite_main_size = true;
- }
}
flex_line.remaining_free_space = calculate_free_space();
@@ -1087,10 +1049,8 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
auto& containing_block_state = throwaway_state.get_mutable(flex_container());
if (is_row_layout()) {
containing_block_state.set_content_width(item.main_size);
- containing_block_state.set_has_definite_width(true);
} else {
containing_block_state.set_content_height(item.main_size);
- containing_block_state.set_has_definite_height(true);
}
auto& box_state = throwaway_state.get_mutable(item.box);
@@ -1100,7 +1060,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
// NOTE: Flex items should always create an independent formatting context!
VERIFY(independent_formatting_context);
- independent_formatting_context->run(item.box, LayoutMode::Normal, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
+ independent_formatting_context->run(item.box, LayoutMode::Normal, AvailableSpace(m_available_space_for_items->width, m_available_space_for_items->height));
auto automatic_cross_size = is_row_layout() ? independent_formatting_context->automatic_content_height()
: box_state.content_width();
@@ -1124,12 +1084,6 @@ void FlexFormattingContext::calculate_cross_size_of_each_flex_line(float const c
// and its hypothetical outer cross-start edge, and the largest of the distances between each item’s baseline
// and its hypothetical outer cross-end edge, and sum these two values.
- // FIXME: This isn't spec but makes sense here
- if (has_definite_cross_size(flex_container()) && flex_container().computed_values().align_items() == CSS::AlignItems::Stretch) {
- flex_line.cross_size = specified_cross_size(flex_container()) / m_flex_lines.size();
- continue;
- }
-
// 2. Among all the items not collected by the previous step, find the largest outer hypothetical cross size.
float largest_hypothetical_cross_size = 0;
for (auto& flex_item : flex_line.items) {
@@ -1227,10 +1181,10 @@ void FlexFormattingContext::distribute_any_remaining_free_space()
break;
case CSS::JustifyContent::FlexEnd:
flex_region_render_cursor = FlexRegionRenderCursor::Right;
- initial_offset = m_available_space->main.value_or(NumericLimits<float>::max());
+ initial_offset = specified_main_size(flex_container());
break;
case CSS::JustifyContent::Center:
- initial_offset = (m_available_space->main.value_or(NumericLimits<float>::max()) - used_main_space) / 2.0f;
+ initial_offset = (specified_main_size(flex_container()) - used_main_space) / 2.0f;
break;
case CSS::JustifyContent::SpaceBetween:
space_between_items = flex_line.remaining_free_space / (number_of_items - 1);
@@ -1435,12 +1389,11 @@ void FlexFormattingContext::copy_dimensions_from_flex_items_to_boxes()
}
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-sizes
-void FlexFormattingContext::determine_intrinsic_size_of_flex_container(LayoutMode layout_mode)
+void FlexFormattingContext::determine_intrinsic_size_of_flex_container()
{
- VERIFY(layout_mode != LayoutMode::Normal);
+ float main_size = calculate_intrinsic_main_size_of_flex_container();
+ float cross_size = calculate_intrinsic_cross_size_of_flex_container();
- float main_size = calculate_intrinsic_main_size_of_flex_container(layout_mode);
- float cross_size = calculate_intrinsic_cross_size_of_flex_container(layout_mode);
if (is_row_layout()) {
m_flex_container_state.set_content_width(main_size);
m_flex_container_state.set_content_height(cross_size);
@@ -1451,14 +1404,12 @@ void FlexFormattingContext::determine_intrinsic_size_of_flex_container(LayoutMod
}
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-main-sizes
-float FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container(LayoutMode layout_mode)
+float FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container()
{
- VERIFY(layout_mode != LayoutMode::Normal);
-
// The min-content main size of a single-line flex container is calculated identically to the max-content main size,
// except that the flex items’ min-content contributions are used instead of their max-content contributions.
// However, for a multi-line container, it is simply the largest min-content contribution of all the non-collapsed flex items in the flex container.
- if (!is_single_line() && flex_container_main_constraint() == SizeConstraint::MinContent) {
+ if (!is_single_line() && m_available_space_for_items->main.is_min_content()) {
float largest_contribution = 0;
for (auto const& flex_item : m_flex_items) {
// FIXME: Skip collapsed flex items.
@@ -1479,10 +1430,10 @@ float FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container(Lay
// This is the item’s desired flex fraction.
for (auto& flex_item : m_flex_items) {
- float contribution;
- if (m_flex_container_state.width_constraint == SizeConstraint::MinContent || m_flex_container_state.height_constraint == SizeConstraint::MinContent)
+ float contribution = 0;
+ if (m_available_space_for_items->main.is_min_content())
contribution = calculate_main_min_content_contribution(flex_item);
- else
+ else if (m_available_space_for_items->main.is_max_content())
contribution = calculate_main_max_content_contribution(flex_item);
float outer_flex_base_size = flex_item.flex_base_size + flex_item.margins.main_before + flex_item.margins.main_after + flex_item.borders.main_before + flex_item.borders.main_after + flex_item.padding.main_before + flex_item.padding.main_after;
@@ -1539,7 +1490,7 @@ float FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container(Lay
flex_line.chosen_flex_fraction = chosen_flex_fraction;
}
- auto determine_main_size = [&](bool resolve_percentage_min_max_sizes) -> float {
+ auto determine_main_size = [&]() -> float {
float largest_sum = 0;
for (auto& flex_line : m_flex_lines) {
// 4. Add each item’s flex base size to the product of its flex grow factor (scaled flex shrink factor, if shrinking)
@@ -1556,8 +1507,8 @@ float FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container(Lay
auto const& computed_min_size = this->computed_main_min_size(flex_item->box);
auto const& computed_max_size = this->computed_main_max_size(flex_item->box);
- auto clamp_min = (!computed_min_size.is_auto() && (resolve_percentage_min_max_sizes || !computed_min_size.contains_percentage())) ? specified_main_min_size(flex_item->box) : automatic_minimum_size(*flex_item);
- auto clamp_max = (!computed_max_size.is_none() && (resolve_percentage_min_max_sizes || !computed_max_size.contains_percentage())) ? specified_main_max_size(flex_item->box) : NumericLimits<float>::max();
+ auto clamp_min = (!computed_min_size.is_auto() && !computed_min_size.contains_percentage()) ? specified_main_min_size(flex_item->box) : automatic_minimum_size(*flex_item);
+ auto clamp_max = (!computed_max_size.is_none() && !computed_max_size.contains_percentage()) ? specified_main_max_size(flex_item->box) : NumericLimits<float>::max();
result = css_clamp(result, clamp_min, clamp_max);
@@ -1573,27 +1524,24 @@ float FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container(Lay
return largest_sum;
};
- auto first_pass_main_size = determine_main_size(false);
- set_main_size(flex_container(), first_pass_main_size);
- auto second_pass_main_size = determine_main_size(true);
- return second_pass_main_size;
+ auto main_size = determine_main_size();
+ set_main_size(flex_container(), main_size);
+ return main_size;
}
// https://drafts.csswg.org/css-flexbox-1/#intrinsic-cross-sizes
-float FlexFormattingContext::calculate_intrinsic_cross_size_of_flex_container(LayoutMode layout_mode)
+float FlexFormattingContext::calculate_intrinsic_cross_size_of_flex_container()
{
- VERIFY(layout_mode != LayoutMode::Normal);
-
// The min-content/max-content cross size of a single-line flex container
// is the largest min-content contribution/max-content contribution (respectively) of its flex items.
if (is_single_line()) {
auto calculate_largest_contribution = [&](bool resolve_percentage_min_max_sizes) {
float largest_contribution = 0;
for (auto& flex_item : m_flex_items) {
- float contribution;
- if (m_flex_container_state.width_constraint == SizeConstraint::MinContent || m_flex_container_state.height_constraint == SizeConstraint::MinContent)
+ float contribution = 0;
+ if (m_available_space_for_items->cross.is_min_content())
contribution = calculate_cross_min_content_contribution(flex_item, resolve_percentage_min_max_sizes);
- else if (m_flex_container_state.width_constraint == SizeConstraint::MaxContent || m_flex_container_state.height_constraint == SizeConstraint::MaxContent)
+ else if (m_available_space_for_items->cross.is_max_content())
contribution = calculate_cross_max_content_contribution(flex_item, resolve_percentage_min_max_sizes);
largest_contribution = max(largest_contribution, contribution);
}
@@ -1606,11 +1554,19 @@ float FlexFormattingContext::calculate_intrinsic_cross_size_of_flex_container(La
return second_pass_largest_contribution;
}
- // For a multi-line flex container, the min-content/max-content cross size is the sum of the flex line cross sizes
- // resulting from sizing the flex container under a cross-axis min-content constraint/max-content constraint (respectively).
+ // FIXME: For a multi-line flex container, the min-content/max-content cross size is the sum of the flex line cross sizes
+ // resulting from sizing the flex container under a cross-axis min-content constraint/max-content constraint (respectively).
// FIXME: However, if the flex container is flex-flow: column wrap;, then it’s sized by first finding the largest
// min-content/max-content cross-size contribution among the flex items (respectively), then using that size
// as the available space in the cross axis for each of the flex items during layout.
+
+ // HACK: We run steps 7, 9 and 11 from the main algorithm. This gives us *some* cross size information to work with.
+ for (auto& flex_item : m_flex_items) {
+ determine_hypothetical_cross_size_of_item(flex_item, false);
+ }
+ calculate_cross_size_of_each_flex_line(0, INFINITY);
+ determine_used_cross_size_of_each_flex_item();
+
float sum_of_flex_line_cross_sizes = 0;
for (auto& flex_line : m_flex_lines) {
sum_of_flex_line_cross_sizes += flex_line.cross_size;
@@ -1709,14 +1665,14 @@ float FlexFormattingContext::calculate_min_content_main_size(FlexItem const& ite
float FlexFormattingContext::calculate_fit_content_main_size(FlexItem const& item) const
{
- return is_row_layout() ? calculate_fit_content_width(item.box, m_state.get(item.box).width_constraint, m_available_space->main)
- : calculate_fit_content_height(item.box, m_state.get(item.box).height_constraint, m_available_space->main);
+ return is_row_layout() ? calculate_fit_content_width(item.box, m_available_space_for_items->main)
+ : calculate_fit_content_height(item.box, m_available_space_for_items->main);
}
float FlexFormattingContext::calculate_fit_content_cross_size(FlexItem const& item) const
{
- return !is_row_layout() ? calculate_fit_content_width(item.box, m_state.get(item.box).width_constraint, m_available_space->cross)
- : calculate_fit_content_height(item.box, m_state.get(item.box).height_constraint, m_available_space->cross);
+ return !is_row_layout() ? calculate_fit_content_width(item.box, m_available_space_for_items->cross)
+ : calculate_fit_content_height(item.box, m_available_space_for_items->cross);
}
float FlexFormattingContext::calculate_max_content_main_size(FlexItem const& item) const
@@ -1734,16 +1690,6 @@ float FlexFormattingContext::calculate_max_content_cross_size(FlexItem const& it
return is_row_layout() ? calculate_max_content_height(item.box) : calculate_max_content_width(item.box);
}
-SizeConstraint FlexFormattingContext::flex_container_main_constraint() const
-{
- return is_row_layout() ? m_flex_container_state.width_constraint : m_flex_container_state.height_constraint;
-}
-
-SizeConstraint FlexFormattingContext::flex_container_cross_constraint() const
-{
- return is_row_layout() ? m_flex_container_state.height_constraint : m_flex_container_state.width_constraint;
-}
-
// https://drafts.csswg.org/css-flexbox-1/#stretched
bool FlexFormattingContext::flex_item_is_stretched(FlexItem const& item) const
{
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
index ea810da2b9..d3031e23d8 100644
--- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
@@ -18,15 +18,12 @@ public:
virtual bool inhibits_floating() const override { return true; }
- virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) override;
virtual float automatic_content_height() const override;
Box const& flex_container() const { return context_box(); }
private:
- SizeConstraint flex_container_main_constraint() const;
- SizeConstraint flex_container_cross_constraint() const;
-
void dump_items() const;
struct DirectionAgnosticMargins {
@@ -63,8 +60,6 @@ private:
DirectionAgnosticMargins padding {};
bool is_min_violation { false };
bool is_max_violation { false };
- bool has_assigned_definite_main_size { false };
- bool has_assigned_definite_cross_size { false };
float add_main_margin_box_sizes(float content_size) const
{
@@ -94,14 +89,11 @@ private:
bool has_cross_min_size(Box const&) const;
float specified_main_max_size(Box const&) const;
float specified_cross_max_size(Box const&) const;
- float calculated_main_size(Box const&) const;
bool is_cross_auto(Box const&) const;
- float specified_main_size_of_child_box(Box const& child_box) const;
float specified_main_min_size(Box const&) const;
float specified_cross_min_size(Box const&) const;
bool has_main_max_size(Box const&) const;
bool has_cross_max_size(Box const&) const;
- float sum_of_margin_padding_border_in_main_axis(Box const&) const;
float automatic_minimum_size(FlexItem const&) const;
float content_based_minimum_size(FlexItem const&) const;
Optional<float> specified_size_suggestion(FlexItem const&) const;
@@ -121,8 +113,6 @@ private:
void set_main_size(Box const&, float size);
void set_cross_size(Box const&, float size);
- void set_has_definite_main_size(Box const&, bool);
- void set_has_definite_cross_size(Box const&, bool);
void set_offset(Box const&, float main_offset, float cross_offset);
void set_main_axis_first_margin(FlexItem&, float margin);
void set_main_axis_second_margin(FlexItem&, float margin);
@@ -131,12 +121,12 @@ private:
void generate_anonymous_flex_items();
- void determine_available_main_and_cross_space(bool& main_is_constrained, bool& cross_is_constrained, float& main_min_size, float& main_max_size, float& cross_min_size, float& cross_max_size);
+ void determine_available_space_for_items(AvailableSpace const&);
float calculate_indefinite_main_size(FlexItem const&);
void determine_flex_base_size_and_hypothetical_main_size(FlexItem&);
- void determine_main_size_of_flex_container(bool main_is_constrained, float main_min_size, float main_max_size);
+ void determine_main_size_of_flex_container();
void collect_flex_items_into_flex_lines();
@@ -165,9 +155,9 @@ private:
bool is_direction_reverse() const { return m_flex_direction == CSS::FlexDirection::ColumnReverse || m_flex_direction == CSS::FlexDirection::RowReverse; }
void populate_specified_margins(FlexItem&, CSS::FlexDirection) const;
- void determine_intrinsic_size_of_flex_container(LayoutMode);
- [[nodiscard]] float calculate_intrinsic_main_size_of_flex_container(LayoutMode);
- [[nodiscard]] float calculate_intrinsic_cross_size_of_flex_container(LayoutMode);
+ void determine_intrinsic_size_of_flex_container();
+ [[nodiscard]] float calculate_intrinsic_main_size_of_flex_container();
+ [[nodiscard]] float calculate_intrinsic_cross_size_of_flex_container();
[[nodiscard]] float calculate_cross_min_content_contribution(FlexItem const&, bool resolve_percentage_min_max_sizes) const;
[[nodiscard]] float calculate_cross_max_content_contribution(FlexItem const&, bool resolve_percentage_min_max_sizes) const;
@@ -192,11 +182,14 @@ private:
Vector<FlexItem> m_flex_items;
CSS::FlexDirection m_flex_direction {};
- struct AvailableSpaceForItems {
- Optional<float> main;
- Optional<float> cross;
+ struct AxisAgnosticAvailableSpace {
+ AvailableSize main;
+ AvailableSize cross;
+ AvailableSize width;
+ AvailableSize height;
};
- Optional<AvailableSpaceForItems> m_available_space;
+ Optional<AxisAgnosticAvailableSpace> m_available_space_for_items;
+ Optional<AxisAgnosticAvailableSpace> m_available_space_for_flex_container;
};
}
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
index 1d255d5e71..3db024b717 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
@@ -33,24 +33,18 @@ void FormattingContext::run_intrinsic_sizing(Box const& box)
{
auto& box_state = m_state.get_mutable(box);
- if (box_state.has_definite_width())
- box_state.set_content_width(box.computed_values().width().resolved(box, CSS::Length::make_px(containing_block_width_for(box))).to_px(box));
-
- if (box_state.has_definite_height())
- box_state.set_content_height(box.computed_values().height().resolved(box, CSS::Length::make_px(containing_block_height_for(box))).to_px(box));
-
- auto to_available_space = [&](SizeConstraint constraint) {
+ auto to_available_size = [&](SizeConstraint constraint) {
if (constraint == SizeConstraint::MinContent)
- return AvailableSpace::make_min_content();
+ return AvailableSize::make_min_content();
if (constraint == SizeConstraint::MaxContent)
- return AvailableSpace::make_max_content();
- return AvailableSpace::make_indefinite();
+ return AvailableSize::make_max_content();
+ return AvailableSize::make_indefinite();
};
- auto available_width = to_available_space(box_state.width_constraint);
- auto available_height = to_available_space(box_state.height_constraint);
+ auto available_width = to_available_size(box_state.width_constraint);
+ auto available_height = to_available_size(box_state.height_constraint);
- run(box, LayoutMode::IntrinsicSizing, available_width, available_height);
+ run(box, LayoutMode::IntrinsicSizing, AvailableSpace(available_width, available_height));
}
bool FormattingContext::creates_block_formatting_context(Box const& box)
@@ -117,7 +111,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
{
}
virtual float automatic_content_height() const override { return 0; };
- virtual void run(Box const&, LayoutMode, AvailableSpace const&, AvailableSpace const&) override { }
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) override { }
};
return make<ReplacedFormattingContext>(state, child_box);
}
@@ -159,7 +153,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
{
}
virtual float automatic_content_height() const override { return 0; };
- virtual void run(Box const&, LayoutMode, AvailableSpace const&, AvailableSpace const&) override { }
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) override { }
};
return make<DummyFormattingContext>(state, child_box);
}
@@ -168,7 +162,7 @@ OwnPtr<FormattingContext> FormattingContext::create_independent_formatting_conte
return {};
}
-OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box, LayoutMode layout_mode)
+OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box, LayoutMode layout_mode, AvailableSpace const& available_space)
{
{
// OPTIMIZATION: If we're doing intrinsic sizing and `child_box` has definite size in both axes,
@@ -189,9 +183,9 @@ OwnPtr<FormattingContext> FormattingContext::layout_inside(Box const& child_box,
auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, child_box);
if (independent_formatting_context)
- independent_formatting_context->run(child_box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
+ independent_formatting_context->run(child_box, layout_mode, available_space);
else
- run(child_box, layout_mode, AvailableSpace::make_indefinite(), AvailableSpace::make_indefinite());
+ run(child_box, layout_mode, available_space);
return independent_formatting_context;
}
@@ -262,16 +256,19 @@ static Gfx::FloatSize solve_replaced_size_constraint(LayoutState const& state, f
return { w, h };
}
-float FormattingContext::compute_auto_height_for_block_level_element(LayoutState const& state, Box const& box)
+float FormattingContext::compute_auto_height_for_block_level_element(Box const& box) const
{
if (creates_block_formatting_context(box))
- return compute_auto_height_for_block_formatting_context_root(state, verify_cast<BlockContainer>(box));
+ return compute_auto_height_for_block_formatting_context_root(verify_cast<BlockContainer>(box));
- auto const& box_state = state.get(box);
+ auto const& box_state = m_state.get(box);
auto display = box.computed_values().display();
- if (display.is_flex_inside())
- return box_state.content_height();
+ 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);
+ }
// https://www.w3.org/TR/CSS22/visudet.html#normal-block
// 10.6.3 Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible'
@@ -295,7 +292,7 @@ float FormattingContext::compute_auto_height_for_block_level_element(LayoutState
if (child_box->is_list_item_marker_box())
continue;
- auto const& child_box_state = state.get(*child_box);
+ auto const& child_box_state = m_state.get(*child_box);
// Ignore anonymous block containers with no lines. These don't count as in-flow block boxes.
if (child_box->is_anonymous() && child_box->is_block_container() && child_box_state.line_boxes.is_empty())
@@ -311,7 +308,7 @@ float FormattingContext::compute_auto_height_for_block_level_element(LayoutState
}
// https://www.w3.org/TR/CSS22/visudet.html#root-height
-float FormattingContext::compute_auto_height_for_block_formatting_context_root(LayoutState const& state, BlockContainer const& root)
+float FormattingContext::compute_auto_height_for_block_formatting_context_root(BlockContainer const& root) const
{
// 10.6.7 'Auto' heights for block formatting context roots
Optional<float> top;
@@ -320,7 +317,7 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
if (root.children_are_inline()) {
// If it only has inline-level children, the height is the distance between
// the top content edge and the bottom of the bottommost line box.
- auto const& line_boxes = state.get(root).line_boxes;
+ auto const& line_boxes = m_state.get(root).line_boxes;
top = 0;
if (!line_boxes.is_empty())
bottom = line_boxes.last().bottom();
@@ -339,7 +336,7 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
if ((root.computed_values().overflow_y() == CSS::Overflow::Visible) && child_box.is_floating())
return IterationDecision::Continue;
- auto const& child_box_state = state.get(child_box);
+ auto const& child_box_state = m_state.get(child_box);
float child_box_top = child_box_state.offset.y() - child_box_state.margin_box_top();
float child_box_bottom = child_box_state.offset.y() + child_box_state.content_height() + child_box_state.margin_box_bottom();
@@ -357,10 +354,10 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
// In addition, if the element has any floating descendants
// whose bottom margin edge is below the element's bottom content edge,
// then the height is increased to include those edges.
- for (auto* floating_box : state.get(root).floating_descendants()) {
+ for (auto* floating_box : m_state.get(root).floating_descendants()) {
// NOTE: Floating box coordinates are relative to their own containing block,
// which may or may not be the BFC root.
- auto margin_box = margin_box_rect_in_ancestor_coordinate_space(*floating_box, root, state);
+ auto margin_box = margin_box_rect_in_ancestor_coordinate_space(*floating_box, root, m_state);
float floating_box_bottom_margin_edge = margin_box.bottom() + 1;
if (!bottom.has_value() || floating_box_bottom_margin_edge > bottom.value())
bottom = floating_box_bottom_margin_edge;
@@ -370,7 +367,7 @@ float FormattingContext::compute_auto_height_for_block_formatting_context_root(L
}
// 10.3.2 Inline, replaced elements, https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-width
-float FormattingContext::tentative_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_width)
+float FormattingContext::tentative_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_width, AvailableSpace const& available_space)
{
// Treat percentages of indefinite containing block widths as 0 (the initial width).
if (computed_width.is_percentage() && !state.get(*box.containing_block()).has_definite_width())
@@ -379,7 +376,7 @@ float FormattingContext::tentative_width_for_replaced_element(LayoutState const&
auto height_of_containing_block = CSS::Length::make_px(containing_block_height_for(box, state));
auto const& computed_height = box.computed_values().height();
- float used_width = computed_width.resolved(box, CSS::Length::make_px(containing_block_width_for(box, state))).to_px(box);
+ float used_width = computed_width.resolved(box, CSS::Length::make_px(available_space.width.to_px())).to_px(box);
// If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width,
// then that intrinsic width is the used value of 'width'.
@@ -394,7 +391,7 @@ float FormattingContext::tentative_width_for_replaced_element(LayoutState const&
// (used height) * (intrinsic ratio)
if ((computed_height.is_auto() && computed_width.is_auto() && !box.has_intrinsic_width() && box.has_intrinsic_height() && box.has_intrinsic_aspect_ratio())
|| (computed_width.is_auto() && !computed_height.is_auto() && box.has_intrinsic_aspect_ratio())) {
- return compute_height_for_replaced_element(state, box) * box.intrinsic_aspect_ratio().value();
+ return compute_height_for_replaced_element(state, box, available_space) * box.intrinsic_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,
@@ -414,29 +411,29 @@ float FormattingContext::tentative_width_for_replaced_element(LayoutState const&
return used_width;
}
-void FormattingContext::compute_width_for_absolutely_positioned_element(Box const& box)
+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));
+ compute_width_for_absolutely_positioned_replaced_element(verify_cast<ReplacedBox>(box), available_space);
else
- compute_width_for_absolutely_positioned_non_replaced_element(box);
+ compute_width_for_absolutely_positioned_non_replaced_element(box, available_space);
}
-void FormattingContext::compute_height_for_absolutely_positioned_element(Box const& box)
+void FormattingContext::compute_height_for_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
{
if (is<ReplacedBox>(box))
- compute_height_for_absolutely_positioned_replaced_element(verify_cast<ReplacedBox>(box));
+ compute_height_for_absolutely_positioned_replaced_element(static_cast<ReplacedBox const&>(box), available_space);
else
- compute_height_for_absolutely_positioned_non_replaced_element(box);
+ compute_height_for_absolutely_positioned_non_replaced_element(box, available_space);
}
-float FormattingContext::compute_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box)
+float FormattingContext::compute_width_for_replaced_element(LayoutState const& state, ReplacedBox const& box, AvailableSpace const& available_space)
{
// 10.3.4 Block-level, replaced elements in normal flow...
// 10.3.2 Inline, replaced elements
auto zero_value = CSS::Length::make_px(0);
- auto width_of_containing_block_as_length = CSS::Length::make_px(containing_block_width_for(box, state));
+ auto width_of_containing_block_as_length = CSS::Length::make_px(available_space.width.to_px());
auto margin_left = box.computed_values().margin().left().resolved(box, width_of_containing_block_as_length).resolved(box);
auto margin_right = box.computed_values().margin().right().resolved(box, width_of_containing_block_as_length).resolved(box);
@@ -450,14 +447,14 @@ float FormattingContext::compute_width_for_replaced_element(LayoutState const& s
auto computed_width = box.computed_values().width();
// 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);
+ auto used_width = tentative_width_for_replaced_element(state, box, computed_width, available_space);
// 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();
if (!computed_max_width.is_none()) {
if (used_width > computed_max_width.resolved(box, width_of_containing_block_as_length).to_px(box)) {
- used_width = tentative_width_for_replaced_element(state, box, computed_max_width);
+ used_width = tentative_width_for_replaced_element(state, box, computed_max_width, available_space);
}
}
@@ -466,7 +463,7 @@ float FormattingContext::compute_width_for_replaced_element(LayoutState const& s
auto computed_min_width = box.computed_values().min_width();
if (!computed_min_width.is_auto()) {
if (used_width < computed_min_width.resolved(box, width_of_containing_block_as_length).to_px(box)) {
- used_width = tentative_width_for_replaced_element(state, box, computed_min_width);
+ used_width = tentative_width_for_replaced_element(state, box, computed_min_width, available_space);
}
}
@@ -475,7 +472,7 @@ float FormattingContext::compute_width_for_replaced_element(LayoutState const& s
// 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
-float FormattingContext::tentative_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_height)
+float FormattingContext::tentative_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box, CSS::Size const& computed_height, AvailableSpace const& available_space)
{
// Treat percentages of indefinite containing block heights as 0 (the initial height).
if (computed_height.is_percentage() && !state.get(*box.containing_block()).has_definite_height())
@@ -492,7 +489,7 @@ float FormattingContext::tentative_height_for_replaced_element(LayoutState const
//
// (used width) / (intrinsic ratio)
if (computed_height.is_auto() && box.has_intrinsic_aspect_ratio())
- return compute_width_for_replaced_element(state, box) / box.intrinsic_aspect_ratio().value();
+ return compute_width_for_replaced_element(state, box, available_space) / box.intrinsic_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_intrinsic_height())
@@ -504,23 +501,23 @@ float FormattingContext::tentative_height_for_replaced_element(LayoutState const
if (computed_height.is_auto())
return 150;
- return computed_height.resolved(box, CSS::Length::make_px(containing_block_height_for(box, state))).to_px(box);
+ return computed_height.resolved(box, CSS::Length::make_px(available_space.height.to_px())).to_px(box);
}
-float FormattingContext::compute_height_for_replaced_element(LayoutState const& state, ReplacedBox const& box)
+float 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
- auto width_of_containing_block_as_length = CSS::Length::make_px(containing_block_width_for(box, state));
- auto height_of_containing_block_as_length = CSS::Length::make_px(containing_block_height_for(box, state));
+ auto width_of_containing_block_as_length = CSS::Length::make_px(available_space.width.to_px());
+ auto height_of_containing_block_as_length = CSS::Length::make_px(available_space.height.to_px());
auto computed_width = box.computed_values().width();
auto computed_height = box.computed_values().height();
- float used_height = tentative_height_for_replaced_element(state, box, computed_height);
+ float used_height = tentative_height_for_replaced_element(state, box, computed_height, available_space);
if (computed_width.is_auto() && computed_height.is_auto() && box.has_intrinsic_aspect_ratio()) {
- float w = tentative_width_for_replaced_element(state, box, computed_width);
+ float w = tentative_width_for_replaced_element(state, box, computed_width, available_space);
float h = used_height;
used_height = solve_replaced_size_constraint(state, w, h, box).height();
}
@@ -528,9 +525,9 @@ float FormattingContext::compute_height_for_replaced_element(LayoutState const&
return used_height;
}
-void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box const& box)
+void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box const& box, AvailableSpace const& available_space)
{
- auto width_of_containing_block = containing_block_width_for(box);
+ auto width_of_containing_block = available_space.width.to_px();
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto& computed_values = box.computed_values();
auto zero_value = CSS::Length::make_px(0);
@@ -670,17 +667,17 @@ 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)
+void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox 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();
- m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(m_state, box));
+ m_state.get_mutable(box).set_content_width(compute_width_for_replaced_element(m_state, box, available_space));
}
// https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-height
-void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box const& box)
+void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box const& box, AvailableSpace const& available_space)
{
// 10.6.4 Absolutely positioned, non-replaced elements
@@ -688,7 +685,7 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
auto& computed_values = box.computed_values();
auto width_of_containing_block = containing_block_width_for(box);
- auto height_of_containing_block = containing_block_height_for(box);
+ auto height_of_containing_block = available_space.height.to_px();
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto height_of_containing_block_as_length = CSS::Length::make_px(height_of_containing_block);
@@ -714,11 +711,11 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
box_state.padding_bottom = computed_values.padding().bottom().resolved(box, width_of_containing_block_as_length).to_px(box);
if (computed_height.is_auto() && computed_top.is_auto() && computed_bottom.is_auto()) {
- tentative_height = CSS::Length(compute_auto_height_for_block_level_element(m_state, box), CSS::Length::Type::Px);
+ tentative_height = CSS::Length(compute_auto_height_for_block_level_element(box), CSS::Length::Type::Px);
}
else if (computed_height.is_auto() && !computed_top.is_auto() && computed_bottom.is_auto()) {
- tentative_height = CSS::Length(compute_auto_height_for_block_level_element(m_state, box), CSS::Length::Type::Px);
+ tentative_height = CSS::Length(compute_auto_height_for_block_level_element(box), CSS::Length::Type::Px);
box_state.inset_bottom = height_of_containing_block - tentative_height.to_px(box) - used_top - box_state.margin_top - box_state.padding_top - box_state.border_top - box_state.margin_bottom - box_state.padding_bottom - box_state.border_bottom;
}
@@ -735,29 +732,22 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el
box_state.set_content_height(used_height);
}
-void FormattingContext::layout_absolutely_positioned_element(Box const& box)
+void FormattingContext::layout_absolutely_positioned_element(Box const& box, AvailableSpace const& available_space)
{
- // https://drafts.csswg.org/css-sizing-3/#definite
- // Additionally, the size of the containing block of an absolutely positioned element is always definite with respect to that element.
auto& containing_block_state = m_state.get_mutable(*box.containing_block());
- auto containing_block_had_definite_width = containing_block_state.has_definite_width();
- containing_block_state.set_has_definite_width(true);
- auto containing_block_definite_width_guard = ScopeGuard([&] {
- containing_block_state.set_has_definite_width(containing_block_had_definite_width);
- });
+ auto& box_state = m_state.get_mutable(box);
- auto width_of_containing_block = containing_block_width_for(box);
- auto height_of_containing_block = containing_block_height_for(box);
+ auto width_of_containing_block = available_space.width.to_px();
+ auto height_of_containing_block = available_space.height.to_px();
auto width_of_containing_block_as_length = CSS::Length::make_px(width_of_containing_block);
auto height_of_containing_block_as_length = CSS::Length::make_px(height_of_containing_block);
auto specified_width = box.computed_values().width().resolved(box, width_of_containing_block_as_length).resolved(box);
- compute_width_for_absolutely_positioned_element(box);
- auto independent_formatting_context = layout_inside(box, LayoutMode::Normal);
- compute_height_for_absolutely_positioned_element(box);
+ compute_width_for_absolutely_positioned_element(box, available_space);
+ auto independent_formatting_context = layout_inside(box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(available_space));
+ compute_height_for_absolutely_positioned_element(box, available_space);
- auto& box_state = m_state.get_mutable(box);
box_state.margin_left = box.computed_values().margin().left().resolved(box, width_of_containing_block_as_length).to_px(box);
box_state.margin_top = box.computed_values().margin().top().resolved(box, height_of_containing_block_as_length).to_px(box);
box_state.margin_right = box.computed_values().margin().right().resolved(box, width_of_containing_block_as_length).to_px(box);
@@ -773,7 +763,7 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box)
box_state.inset_right = box.computed_values().inset().right().resolved(box, width_of_containing_block_as_length).to_px(box);
box_state.inset_bottom = box.computed_values().inset().bottom().resolved(box, height_of_containing_block_as_length).to_px(box);
- if (box.computed_values().inset().left().is_auto() && specified_width.is_auto() && box.computed_values().inset().right().is_auto()) {
+ if (box.computed_values().inset().left().is_auto() && box.computed_values().width().is_auto() && box.computed_values().inset().right().is_auto()) {
if (box.computed_values().margin().left().is_auto())
box_state.margin_left = 0;
if (box.computed_values().margin().right().is_auto())
@@ -826,17 +816,17 @@ void FormattingContext::layout_absolutely_positioned_element(Box const& box)
// NOTE: Absolutely positioned boxes are relative to the *padding edge* of the containing block.
used_offset.translate_by(-containing_block_state.padding_left, -containing_block_state.padding_top);
- box_state.offset = used_offset;
+ box_state.set_content_offset(used_offset);
if (independent_formatting_context)
independent_formatting_context->parent_context_did_dimension_child_root_box();
}
-void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const& box)
+void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const& box, AvailableSpace const& available_space)
{
// 10.6.5 Absolutely positioned, replaced elements
// The used value of 'height' is determined as for inline replaced elements.
- m_state.get_mutable(box).set_content_height(compute_height_for_replaced_element(m_state, box));
+ m_state.get_mutable(box).set_content_height(compute_height_for_replaced_element(m_state, box, available_space));
}
// https://www.w3.org/TR/css-position-3/#relpos-insets
@@ -879,49 +869,49 @@ void FormattingContext::compute_inset(Box const& box)
resolve_two_opposing_insets(computed_values.inset().top(), computed_values.inset().bottom(), box_state.inset_top, box_state.inset_bottom, containing_block_height_for(box));
}
-float FormattingContext::calculate_fit_content_size(float min_content_size, float max_content_size, SizeConstraint constraint, Optional<float> available_space) const
+float FormattingContext::calculate_fit_content_size(float min_content_size, float max_content_size, AvailableSize const& available_size) const
{
// If the available space in a given axis is definite, 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.has_value()) {
+ if (available_size.is_definite()) {
// FIXME: Compute the real stretch-fit size.
- auto stretch_fit_size = *available_space;
+ auto stretch_fit_size = available_size.to_px();
auto s = max(min_content_size, min(max_content_size, stretch_fit_size));
return s;
}
// When sizing under a min-content constraint, equal to the min-content size.
- if (constraint == SizeConstraint::MinContent)
+ if (available_size.is_min_content())
return min_content_size;
// Otherwise, equal to the max-content size in that axis.
return max_content_size;
}
-float FormattingContext::calculate_fit_content_width(Layout::Box const& box, SizeConstraint constraint, Optional<float> available_space) const
+float FormattingContext::calculate_fit_content_width(Layout::Box const& box, AvailableSize const& available_size) const
{
// When sizing under a min-content constraint, equal to the min-content size.
// NOTE: We check this first, to avoid needlessly calculating the max-content size.
- if (constraint == SizeConstraint::MinContent)
+ if (available_size.is_min_content())
return calculate_min_content_width(box);
- if (constraint == SizeConstraint::MaxContent)
+ if (available_size.is_max_content())
return calculate_max_content_width(box);
- return calculate_fit_content_size(calculate_min_content_width(box), calculate_max_content_width(box), constraint, available_space);
+ return calculate_fit_content_size(calculate_min_content_width(box), calculate_max_content_width(box), available_size);
}
-float FormattingContext::calculate_fit_content_height(Layout::Box const& box, SizeConstraint constraint, Optional<float> available_space) const
+float FormattingContext::calculate_fit_content_height(Layout::Box const& box, AvailableSize const& available_size) const
{
// When sizing under a min-content constraint, equal to the min-content size.
// NOTE: We check this first, to avoid needlessly calculating the max-content size.
- if (constraint == SizeConstraint::MinContent)
+ if (available_size.is_min_content())
return calculate_min_content_height(box);
- if (constraint == SizeConstraint::MaxContent)
+ if (available_size.is_max_content())
return calculate_max_content_height(box);
- return calculate_fit_content_size(calculate_min_content_height(box), calculate_max_content_height(box), constraint, available_space);
+ return calculate_fit_content_size(calculate_min_content_height(box), calculate_max_content_height(box), available_size);
}
float FormattingContext::calculate_min_content_width(Layout::Box const& box) const
@@ -936,14 +926,6 @@ float FormattingContext::calculate_min_content_width(Layout::Box const& box) con
return *cache.min_content_width;
LayoutState throwaway_state(&m_state);
- auto const& containing_block = *box.containing_block();
- auto& containing_block_state = throwaway_state.get_mutable(containing_block);
- containing_block_state.set_content_width(0);
-
- if (!containing_block_state.has_definite_height())
- containing_block_state.set_content_height(INFINITY);
- else if (containing_block.computed_values().height().is_auto())
- containing_block_state.set_content_height(containing_block_height_for(containing_block));
auto& box_state = throwaway_state.get_mutable(box);
box_state.width_constraint = SizeConstraint::MinContent;
@@ -978,14 +960,6 @@ float FormattingContext::calculate_max_content_width(Layout::Box const& box) con
return *cache.max_content_width;
LayoutState throwaway_state(&m_state);
- auto const& containing_block = *box.containing_block();
- auto& containing_block_state = throwaway_state.get_mutable(containing_block);
- containing_block_state.set_content_width(INFINITY);
-
- if (!containing_block_state.has_definite_height())
- containing_block_state.set_content_height(INFINITY);
- else if (containing_block.computed_values().height().is_auto())
- containing_block_state.set_content_height(containing_block_height_for(containing_block));
auto& box_state = throwaway_state.get_mutable(box);
box_state.width_constraint = SizeConstraint::MaxContent;
@@ -1020,14 +994,6 @@ float FormattingContext::calculate_min_content_height(Layout::Box const& box) co
return *cache.min_content_height;
LayoutState throwaway_state(&m_state);
- auto const& containing_block = *box.containing_block();
- auto& containing_block_state = throwaway_state.get_mutable(containing_block);
- containing_block_state.set_content_height(0);
-
- if (!containing_block_state.has_definite_width())
- containing_block_state.set_content_width(INFINITY);
- else if (containing_block.computed_values().width().is_auto())
- containing_block_state.set_content_width(containing_block_width_for(containing_block));
auto& box_state = throwaway_state.get_mutable(box);
box_state.height_constraint = SizeConstraint::MinContent;
@@ -1058,14 +1024,6 @@ float FormattingContext::calculate_max_content_height(Layout::Box const& box) co
return *cache.max_content_height;
LayoutState throwaway_state(&m_state);
- auto const& containing_block = *box.containing_block();
- auto& containing_block_state = throwaway_state.get_mutable(containing_block);
- containing_block_state.set_content_height(INFINITY);
-
- if (!containing_block_state.has_definite_width())
- containing_block_state.set_content_width(INFINITY);
- else if (containing_block.computed_values().width().is_auto())
- containing_block_state.set_content_width(containing_block_width_for(containing_block));
auto& box_state = throwaway_state.get_mutable(box);
box_state.height_constraint = SizeConstraint::MaxContent;
@@ -1181,7 +1139,7 @@ float FormattingContext::compute_box_y_position_with_respect_to_siblings(Box con
}
// https://drafts.csswg.org/css-sizing-3/#stretch-fit-size
-float FormattingContext::calculate_stretch_fit_width(Box const& box, AvailableSpace const& available_width) const
+float FormattingContext::calculate_stretch_fit_width(Box const& box, AvailableSize const& available_width) const
{
// The size a box would take if its outer size filled the available space in the given axis;
// in other words, the stretch fit into the available space, if that is definite.
diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
index 1800163763..bb0d8e519a 100644
--- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h
@@ -25,7 +25,10 @@ public:
SVG,
};
- virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) = 0;
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) = 0;
+
+ // This function returns the automatic content height of the context's root box.
+ virtual float automatic_content_width() const { return 0; }
// This function returns the automatic content height of the context's root box.
virtual float automatic_content_height() const = 0;
@@ -42,8 +45,8 @@ public:
static bool creates_block_formatting_context(Box const&);
- static float compute_width_for_replaced_element(LayoutState const&, ReplacedBox const&);
- static float compute_height_for_replaced_element(LayoutState const&, ReplacedBox const&);
+ static float compute_width_for_replaced_element(LayoutState const&, ReplacedBox const&, AvailableSpace const&);
+ static float compute_height_for_replaced_element(LayoutState const&, ReplacedBox const&, AvailableSpace const&);
OwnPtr<FormattingContext> create_independent_formatting_context_if_needed(LayoutState&, Box const& child_box);
@@ -54,8 +57,8 @@ public:
float calculate_min_content_height(Layout::Box const&) const;
float calculate_max_content_height(Layout::Box const&) const;
- float calculate_fit_content_height(Layout::Box const&, SizeConstraint, Optional<float> available_height) const;
- float calculate_fit_content_width(Layout::Box const&, SizeConstraint, Optional<float> available_width) const;
+ float calculate_fit_content_height(Layout::Box const&, AvailableSize const&) const;
+ float calculate_fit_content_width(Layout::Box const&, AvailableSize const&) const;
virtual float greatest_child_width(Box const&);
@@ -69,14 +72,18 @@ public:
float compute_box_y_position_with_respect_to_siblings(Box const&, LayoutState::UsedValues const&);
- float calculate_stretch_fit_width(Box const&, AvailableSpace const& available_width) const;
+ float calculate_stretch_fit_width(Box const&, AvailableSize const&) const;
+
+ virtual bool can_determine_size_of_child() const { return false; }
+ virtual void determine_width_of_child(Box const&, AvailableSpace const&) { }
+ virtual void determine_height_of_child(Box const&, AvailableSpace const&) { }
protected:
FormattingContext(Type, LayoutState&, Box const&, FormattingContext* parent = nullptr);
- float calculate_fit_content_size(float min_content_size, float max_content_size, SizeConstraint, Optional<float> available_space) const;
+ float calculate_fit_content_size(float min_content_size, float max_content_size, AvailableSize const&) const;
- OwnPtr<FormattingContext> layout_inside(Box const&, LayoutMode);
+ OwnPtr<FormattingContext> layout_inside(Box const&, LayoutMode, AvailableSpace const&);
void compute_inset(Box const& box);
struct SpaceUsedByFloats {
@@ -89,20 +96,20 @@ protected:
float preferred_minimum_width { 0 };
};
- static float tentative_width_for_replaced_element(LayoutState const&, ReplacedBox const&, CSS::Size const& computed_width);
- static float tentative_height_for_replaced_element(LayoutState const&, ReplacedBox const&, CSS::Size const& computed_height);
- static float compute_auto_height_for_block_formatting_context_root(LayoutState const&, BlockContainer const&);
- static float compute_auto_height_for_block_level_element(LayoutState const&, Box const&);
+ static float tentative_width_for_replaced_element(LayoutState const&, ReplacedBox const&, CSS::Size const& computed_width, AvailableSpace const&);
+ static float tentative_height_for_replaced_element(LayoutState const&, ReplacedBox const&, CSS::Size const& computed_height, AvailableSpace const&);
+ float compute_auto_height_for_block_formatting_context_root(BlockContainer const&) const;
+ float compute_auto_height_for_block_level_element(Box const&) const;
ShrinkToFitResult calculate_shrink_to_fit_widths(Box const&);
- void layout_absolutely_positioned_element(Box const&);
- void compute_width_for_absolutely_positioned_element(Box const&);
- void compute_width_for_absolutely_positioned_non_replaced_element(Box const&);
- void compute_width_for_absolutely_positioned_replaced_element(ReplacedBox const&);
- void compute_height_for_absolutely_positioned_element(Box const&);
- void compute_height_for_absolutely_positioned_non_replaced_element(Box const&);
- void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox 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_height_for_absolutely_positioned_element(Box const&, AvailableSpace const&);
+ void compute_height_for_absolutely_positioned_non_replaced_element(Box const&, AvailableSpace const&);
+ void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox const&, AvailableSpace const&);
Type m_type {};
diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
index 1ac3ee8941..8bf6a686eb 100644
--- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp
@@ -17,7 +17,7 @@ GridFormattingContext::GridFormattingContext(LayoutState& state, BlockContainer
GridFormattingContext::~GridFormattingContext() = default;
-void GridFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
+void GridFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const& available_space)
{
auto should_skip_is_anonymous_text_run = [&](Box& child_box) -> bool {
if (child_box.is_anonymous() && !child_box.first_child_of_type<BlockContainer>()) {
@@ -374,7 +374,8 @@ void GridFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Ava
auto& child_box_state = m_state.get_mutable(positioned_box.box);
if (child_box_state.content_height() > positioned_box.computed_height)
positioned_box.computed_height = child_box_state.content_height();
- (void)layout_inside(positioned_box.box, LayoutMode::Normal);
+ if (auto independent_formatting_context = layout_inside(positioned_box.box, LayoutMode::Normal, available_space))
+ independent_formatting_context->parent_context_did_dimension_child_root_box();
if (child_box_state.content_height() > positioned_box.computed_height)
positioned_box.computed_height = child_box_state.content_height();
}
diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h
index 09a5450ed3..0e9f48cc14 100644
--- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h
@@ -17,7 +17,7 @@ public:
explicit GridFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent);
~GridFormattingContext();
- virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
+ virtual void run(Box const&, LayoutMode, AvailableSpace const& available_space) override;
virtual float automatic_content_height() const override;
private:
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
index 351288f67e..e174d2fdbe 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
@@ -24,17 +24,6 @@ InlineFormattingContext::InlineFormattingContext(LayoutState& state, BlockContai
: FormattingContext(Type::Inline, state, containing_block, &parent)
, m_containing_block_state(state.get(containing_block))
{
- switch (m_containing_block_state.width_constraint) {
- case SizeConstraint::MinContent:
- m_effective_containing_block_width = 0;
- break;
- case SizeConstraint::MaxContent:
- m_effective_containing_block_width = INFINITY;
- break;
- default:
- m_effective_containing_block_width = m_containing_block_state.content_width();
- break;
- }
}
InlineFormattingContext::~InlineFormattingContext() = default;
@@ -55,43 +44,54 @@ float InlineFormattingContext::leftmost_x_offset_at(float y) const
auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space(containing_block(), parent().root(), m_state);
float y_in_root = box_in_root_rect.y() + y;
auto space = parent().space_used_by_floats(y_in_root);
- float containing_block_x = m_containing_block_state.offset.x();
- return max(space.left, containing_block_x) - containing_block_x;
+ return space.left;
}
float InlineFormattingContext::available_space_for_line(float y) const
{
- if (m_effective_containing_block_width == 0)
- return 0;
- if (!isfinite(m_effective_containing_block_width))
- return INFINITY;
-
// NOTE: Floats are relative to the BFC root box, not necessarily the containing block of this IFC.
auto& root_block = parent().root();
auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space(containing_block(), root_block, m_state);
float y_in_root = box_in_root_rect.y() + y;
auto space = parent().space_used_by_floats(y_in_root);
- space.left = max(space.left, m_containing_block_state.offset.x()) - m_containing_block_state.offset.x();
- space.right = min(m_containing_block_state.content_width() - space.right, m_containing_block_state.offset.x() + m_effective_containing_block_width);
+ space.left = space.left;
+ space.right = min(m_available_space->width.to_px() - space.right, m_available_space->width.to_px());
return space.right - space.left;
}
+float InlineFormattingContext::automatic_content_width() const
+{
+ return m_automatic_content_width;
+}
+
float InlineFormattingContext::automatic_content_height() const
{
- return compute_auto_height_for_block_formatting_context_root(m_state, containing_block());
+ return m_automatic_content_height;
}
-void InlineFormattingContext::run(Box const&, LayoutMode layout_mode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
+void InlineFormattingContext::run(Box const&, LayoutMode layout_mode, AvailableSpace const& available_space)
{
VERIFY(containing_block().children_are_inline());
+ m_available_space = available_space;
generate_line_boxes(layout_mode);
+
+ float max_line_width = 0;
+ float content_height = 0;
+
+ for (auto& line_box : m_containing_block_state.line_boxes) {
+ max_line_width = max(max_line_width, line_box.width());
+ content_height += line_box.height();
+ }
+
+ m_automatic_content_width = max_line_width;
+ m_automatic_content_height = content_height;
}
void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode layout_mode)
{
- auto width_of_containing_block = CSS::Length::make_px(m_effective_containing_block_width);
+ auto width_of_containing_block = CSS::Length::make_px(m_available_space->width.to_px());
auto& box_state = m_state.get_mutable(box);
auto const& computed_values = box.computed_values();
@@ -115,10 +115,10 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l
auto& replaced = verify_cast<ReplacedBox>(box);
if (is<SVGSVGBox>(box))
- (void)layout_inside(replaced, layout_mode);
+ (void)layout_inside(replaced, layout_mode, *m_available_space);
- box_state.set_content_width(compute_width_for_replaced_element(m_state, replaced));
- box_state.set_content_height(compute_height_for_replaced_element(m_state, replaced));
+ box_state.set_content_width(compute_width_for_replaced_element(m_state, replaced, *m_available_space));
+ box_state.set_content_height(compute_height_for_replaced_element(m_state, replaced, *m_available_space));
return;
}
@@ -129,7 +129,7 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l
if (width_value.is_auto()) {
auto result = calculate_shrink_to_fit_widths(inline_block);
- auto available_width = m_containing_block_state.content_width()
+ auto available_width = m_available_space->width.to_px()
- box_state.margin_left
- box_state.border_left
- box_state.padding_left
@@ -140,15 +140,19 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l
auto width = min(max(result.preferred_minimum_width, available_width), result.preferred_width);
box_state.set_content_width(width);
} else {
- auto container_width = CSS::Length::make_px(m_effective_containing_block_width);
- box_state.set_content_width(width_value.resolved(box, container_width).to_px(inline_block));
+ if (width_value.contains_percentage() && !m_available_space->width.is_definite()) {
+ // NOTE: We can't resolve percentages yet. We'll have to wait until after inner layout.
+ } else {
+ auto container_width = CSS::Length::make_px(m_available_space->width.to_px());
+ box_state.set_content_width(width_value.resolved(box, container_width).to_px(inline_block));
+ }
}
- auto independent_formatting_context = layout_inside(inline_block, layout_mode);
+ auto independent_formatting_context = layout_inside(inline_block, layout_mode, box_state.available_inner_space_or_constraints_from(*m_available_space));
auto& height_value = inline_block.computed_values().height();
if (height_value.is_auto()) {
// FIXME: (10.6.6) If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
- BlockFormattingContext::compute_height(inline_block, m_state);
+ parent().compute_height(inline_block, AvailableSpace(AvailableSize::make_indefinite(), AvailableSize::make_indefinite()));
} else {
auto container_height = CSS::Length::make_px(m_containing_block_state.content_height());
box_state.set_content_height(height_value.resolved(box, container_height).to_px(inline_block));
@@ -177,11 +181,11 @@ void InlineFormattingContext::apply_justification_to_fragments(CSS::TextJustify
break;
}
- float excess_horizontal_space = m_effective_containing_block_width - line_box.width();
+ float excess_horizontal_space = m_available_space->width.to_px() - line_box.width();
// Only justify the text if the excess horizontal space is less than or
// equal to 10%, or if we are not looking at the last line box.
- if (is_last_line && excess_horizontal_space / m_effective_containing_block_width > text_justification_threshold)
+ if (is_last_line && excess_horizontal_space / m_available_space->width.to_px() > text_justification_threshold)
return;
float excess_horizontal_space_including_whitespace = excess_horizontal_space;
@@ -250,7 +254,7 @@ void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode)
case InlineLevelIterator::Item::Type::FloatingElement:
if (is<Box>(*item.node))
- parent().layout_floating_box(static_cast<Layout::Box const&>(*item.node), containing_block(), layout_mode, &line_builder);
+ parent().layout_floating_box(static_cast<Layout::Box const&>(*item.node), containing_block(), layout_mode, *m_available_space, &line_builder);
break;
case InlineLevelIterator::Item::Type::Text: {
@@ -318,9 +322,9 @@ bool InlineFormattingContext::can_fit_new_line_at_y(float y) const
auto space_bottom = parent().space_used_by_floats(y_in_root + containing_block().line_height() - 1);
[[maybe_unused]] auto top_left_edge = space_top.left;
- [[maybe_unused]] auto top_right_edge = m_effective_containing_block_width - space_top.right;
+ [[maybe_unused]] auto top_right_edge = m_available_space->width.to_px() - space_top.right;
[[maybe_unused]] auto bottom_left_edge = space_bottom.left;
- [[maybe_unused]] auto bottom_right_edge = m_effective_containing_block_width - space_bottom.right;
+ [[maybe_unused]] auto bottom_right_edge = m_available_space->width.to_px() - space_bottom.right;
if (top_left_edge > bottom_right_edge)
return false;
diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
index 8bb801f658..03de64809a 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
@@ -23,8 +23,9 @@ public:
BlockContainer const& containing_block() const { return static_cast<BlockContainer const&>(context_box()); }
- virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) override;
virtual float automatic_content_height() const override;
+ virtual float automatic_content_width() const override;
void dimension_box_on_line(Box const&, LayoutMode);
@@ -33,14 +34,16 @@ public:
bool any_floats_intrude_at_y(float y) const;
bool can_fit_new_line_at_y(float y) const;
- float effective_containing_block_width() const { return m_effective_containing_block_width; }
-
private:
void generate_line_boxes(LayoutMode);
void apply_justification_to_fragments(CSS::TextJustify, LineBox&, bool is_last_line);
LayoutState::UsedValues const& m_containing_block_state;
- float m_effective_containing_block_width { 0 };
+
+ Optional<AvailableSpace> m_available_space;
+
+ float m_automatic_content_width { 0 };
+ float m_automatic_content_height { 0 };
};
}
diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp
index 940059baf7..ed110d252b 100644
--- a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp
+++ b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <LibWeb/Layout/AvailableSpace.h>
#include <LibWeb/Layout/BlockContainer.h>
#include <LibWeb/Layout/LayoutState.h>
#include <LibWeb/Layout/TextNode.h>
@@ -195,9 +196,21 @@ void LayoutState::UsedValues::set_node(NodeWithStyleAndBoxModelMetrics& node, Us
if (size.is_auto()) {
// NOTE: The width of a non-flex-item block is considered definite if it's auto and the containing block has definite width.
- if (width && node.parent() && !node.parent()->computed_values().display().is_flex_inside()) {
+ if (width
+ && node.computed_values().display().is_block_outside()
+ && node.parent()
+ && !node.parent()->is_floating()
+ && (node.parent()->computed_values().display().is_flow_root_inside()
+ || node.parent()->computed_values().display().is_flow_inside())) {
if (containing_block_has_definite_size) {
- resolved_definite_size = width ? containing_block_used_values->content_width() : containing_block_used_values->content_height();
+ float available_width = containing_block_used_values->content_width();
+ resolved_definite_size = available_width
+ - margin_left
+ - margin_right
+ - padding_left
+ - padding_right
+ - border_left
+ - border_right;
return true;
}
return false;
@@ -205,12 +218,25 @@ void LayoutState::UsedValues::set_node(NodeWithStyleAndBoxModelMetrics& node, Us
return false;
}
- if (size.is_length()) {
- if (size.length().is_calculated())
+ if (size.is_length() && size.length().is_calculated()) {
+ if (width && size.length().calculated_style_value()->contains_percentage() && containing_block_has_definite_size) {
+ auto& calc_value = *size.length().calculated_style_value();
+ auto containing_block_width_as_length = CSS::Length::make_px(containing_block_used_values->content_width());
+ resolved_definite_size = calc_value.resolve_length_percentage(node, containing_block_width_as_length).value_or(CSS::Length::make_auto()).to_px(node);
+ return false;
+ }
+ if (size.length().calculated_style_value()->contains_percentage())
return false;
resolved_definite_size = size.length().to_px(node);
return true;
}
+
+ if (size.is_length()) {
+ VERIFY(!size.is_auto()); // This should have been covered by the Size::is_auto() branch above.
+ VERIFY(!size.length().is_calculated()); // Covered above.
+ resolved_definite_size = size.length().to_px(node);
+ 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();
@@ -230,33 +256,73 @@ void LayoutState::UsedValues::set_node(NodeWithStyleAndBoxModelMetrics& node, Us
void LayoutState::UsedValues::set_content_width(float width)
{
m_content_width = width;
+ m_has_definite_width = true;
}
void LayoutState::UsedValues::set_content_height(float height)
{
m_content_height = height;
+ m_has_definite_height = true;
}
float LayoutState::resolved_definite_width(Box const& box) const
{
- auto const& computed_value = box.computed_values().width();
- if (computed_value.is_auto())
- return get(*box.containing_block()).content_width();
- if (computed_value.is_length())
- return get(box).content_width();
- auto containing_block_size = get(*box.containing_block()).content_width();
- return computed_value.resolved(box, CSS::Length::make_px(containing_block_size)).to_px(box);
+ return get(box).content_width();
}
float LayoutState::resolved_definite_height(Box const& box) const
{
- auto const& computed_value = box.computed_values().height();
- if (computed_value.is_auto())
- return get(*box.containing_block()).content_height();
- if (computed_value.is_length())
- return get(box).content_height();
- auto containing_block_size = get(*box.containing_block()).content_height();
- return computed_value.resolved(box, CSS::Length::make_px(containing_block_size)).to_px(box);
+ return get(box).content_height();
+}
+
+AvailableSize LayoutState::UsedValues::available_width_inside() const
+{
+ if (width_constraint == SizeConstraint::MinContent)
+ return AvailableSize::make_min_content();
+ if (width_constraint == SizeConstraint::MaxContent)
+ return AvailableSize::make_max_content();
+ if (has_definite_width())
+ return AvailableSize::make_definite(m_content_width);
+ return AvailableSize::make_indefinite();
+}
+
+AvailableSize LayoutState::UsedValues::available_height_inside() const
+{
+ if (height_constraint == SizeConstraint::MinContent)
+ return AvailableSize::make_min_content();
+ if (height_constraint == SizeConstraint::MaxContent)
+ return AvailableSize::make_max_content();
+ if (has_definite_height())
+ return AvailableSize::make_definite(m_content_height);
+ return AvailableSize::make_indefinite();
+}
+
+AvailableSpace LayoutState::UsedValues::available_inner_space_or_constraints_from(AvailableSpace const& outer_space) const
+{
+ auto inner_width = available_width_inside();
+ auto inner_height = available_height_inside();
+
+ if (inner_width.is_indefinite() && outer_space.width.is_intrinsic_sizing_constraint())
+ inner_width = outer_space.width;
+ if (inner_height.is_indefinite() && outer_space.height.is_intrinsic_sizing_constraint())
+ inner_height = outer_space.height;
+ return AvailableSpace(inner_width, inner_height);
+}
+
+void LayoutState::UsedValues::set_content_offset(Gfx::FloatPoint offset)
+{
+ set_content_x(offset.x());
+ set_content_y(offset.y());
+}
+
+void LayoutState::UsedValues::set_content_x(float x)
+{
+ offset.set_x(x);
+}
+
+void LayoutState::UsedValues::set_content_y(float y)
+{
+ offset.set_y(y);
}
}
diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.h b/Userland/Libraries/LibWeb/Layout/LayoutState.h
index 091e79dee2..b82bb775ae 100644
--- a/Userland/Libraries/LibWeb/Layout/LayoutState.h
+++ b/Userland/Libraries/LibWeb/Layout/LayoutState.h
@@ -20,6 +20,9 @@ enum class SizeConstraint {
MaxContent,
};
+class AvailableSize;
+class AvailableSpace;
+
struct LayoutState {
LayoutState()
: m_root(*this)
@@ -52,8 +55,15 @@ struct LayoutState {
bool has_definite_width() const { return m_has_definite_width && width_constraint == SizeConstraint::None; }
bool has_definite_height() const { return m_has_definite_height && height_constraint == SizeConstraint::None; }
- void set_has_definite_width(bool value) { m_has_definite_width = value; }
- void set_has_definite_height(bool value) { m_has_definite_height = value; }
+
+ // Returns the available space for content inside this layout box.
+ // If the space in an axis is indefinite, and the outer space is an intrinsic sizing constraint,
+ // the constraint is used in that axis instead.
+ AvailableSpace available_inner_space_or_constraints_from(AvailableSpace const& outer_space) const;
+
+ void set_content_offset(Gfx::FloatPoint);
+ void set_content_x(float);
+ void set_content_y(float);
Gfx::FloatPoint offset;
@@ -113,6 +123,9 @@ struct LayoutState {
auto const& floating_descendants() const { return m_floating_descendants; }
private:
+ AvailableSize available_width_inside() const;
+ AvailableSize available_height_inside() const;
+
Layout::NodeWithStyleAndBoxModelMetrics* m_node { nullptr };
float m_content_width { 0 };
diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp
index e7b0f5f154..aacfa2be84 100644
--- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp
@@ -25,7 +25,7 @@ float SVGFormattingContext::automatic_content_height() const
return 0;
}
-void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
+void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] AvailableSpace const& available_space)
{
auto& svg_svg_element = verify_cast<SVG::SVGSVGElement>(*box.dom_node());
@@ -38,7 +38,7 @@ void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Avai
auto& dom_node = const_cast<SVGGeometryBox&>(geometry_box).dom_node();
if (svg_svg_element.has_attribute(HTML::AttributeNames::width) && svg_svg_element.has_attribute(HTML::AttributeNames::height)) {
- geometry_box_state.offset = { 0, 0 };
+ geometry_box_state.set_content_offset({ 0, 0 });
auto& layout_node = *svg_svg_element.layout_node();
// FIXME: Allow for relative lengths here
@@ -67,7 +67,7 @@ void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Avai
if (maybe_view_box.has_value()) {
auto view_box = maybe_view_box.value();
Gfx::FloatPoint viewbox_offset = { view_box.min_x, view_box.min_y };
- geometry_box_state.offset = path_bounding_box.top_left() + viewbox_offset;
+ geometry_box_state.set_content_offset(path_bounding_box.top_left() + viewbox_offset);
geometry_box_state.set_content_width(view_box.width);
geometry_box_state.set_content_height(view_box.height);
@@ -75,7 +75,7 @@ void SVGFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Avai
return IterationDecision::Continue;
}
- geometry_box_state.offset = path_bounding_box.top_left();
+ geometry_box_state.set_content_offset(path_bounding_box.top_left());
geometry_box_state.set_content_width(path_bounding_box.width());
geometry_box_state.set_content_height(path_bounding_box.height());
}
diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h
index 6c6d011f1b..0d403d99a4 100644
--- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.h
@@ -16,7 +16,7 @@ public:
explicit SVGFormattingContext(LayoutState&, Box const&, FormattingContext* parent);
~SVGFormattingContext();
- virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) override;
virtual float automatic_content_height() const override;
};
diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp
index f391be5efb..5e2a95dd1b 100644
--- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp
+++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp
@@ -23,11 +23,11 @@ TableFormattingContext::TableFormattingContext(LayoutState& state, BlockContaine
TableFormattingContext::~TableFormattingContext() = default;
-void TableFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] AvailableSpace const& available_width, [[maybe_unused]] AvailableSpace const& available_height)
+void TableFormattingContext::run(Box const& box, LayoutMode, AvailableSpace const& available_space)
{
auto& box_state = m_state.get_mutable(box);
- compute_width(box);
+ compute_width(box, available_space);
auto table_width = CSS::Length::make_px(box_state.content_width());
auto table_width_is_auto = box.computed_values().width().is_auto();
@@ -36,12 +36,12 @@ void TableFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Av
Vector<ColumnWidth> column_widths;
box.for_each_child_of_type<TableRowGroupBox>([&](auto& row_group_box) {
- compute_width(row_group_box);
+ compute_width(row_group_box, available_space);
auto column_count = row_group_box.column_count();
column_widths.resize(max(column_count, column_widths.size()));
row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) {
- calculate_column_widths(row, table_width, column_widths);
+ calculate_column_widths(row, table_width, column_widths, available_space);
});
});
box.for_each_child_of_type<TableRowGroupBox>([&](auto& row_group_box) {
@@ -89,7 +89,7 @@ void TableFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Av
row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) {
auto& row_state = m_state.get_mutable(row);
row_state.offset = { 0, content_height };
- layout_row(row, column_widths);
+ layout_row(row, column_widths, available_space);
content_width = max(content_width, row_state.content_width());
content_height += row_state.content_height();
});
@@ -110,7 +110,7 @@ void TableFormattingContext::run(Box const& box, LayoutMode, [[maybe_unused]] Av
m_automatic_content_height = total_content_height;
}
-void TableFormattingContext::calculate_column_widths(Box const& row, CSS::Length const& table_width, Vector<ColumnWidth>& column_widths)
+void TableFormattingContext::calculate_column_widths(Box const& row, CSS::Length const& table_width, Vector<ColumnWidth>& column_widths, AvailableSpace const& available_space)
{
m_state.get_mutable(row);
size_t column_index = 0;
@@ -123,10 +123,11 @@ void TableFormattingContext::calculate_column_widths(Box const& row, CSS::Length
auto width = calculate_max_content_width(cell);
cell_state.set_content_width(width);
} else {
- compute_width(cell, LayoutMode::Normal);
+ compute_width(cell, AvailableSpace(AvailableSize::make_indefinite(), AvailableSize::make_indefinite()), LayoutMode::Normal);
}
- (void)layout_inside(cell, LayoutMode::Normal);
+ if (auto independent_formatting_context = layout_inside(cell, LayoutMode::Normal, cell_state.available_inner_space_or_constraints_from(available_space)))
+ independent_formatting_context->parent_context_did_dimension_child_root_box();
if (cell.colspan() == 1) {
auto min_width = calculate_min_content_width(cell);
@@ -167,7 +168,7 @@ void TableFormattingContext::calculate_column_widths(Box const& row, CSS::Length
});
}
-void TableFormattingContext::layout_row(Box const& row, Vector<ColumnWidth>& column_widths)
+void TableFormattingContext::layout_row(Box const& row, Vector<ColumnWidth>& column_widths, AvailableSpace const& available_space)
{
auto& row_state = m_state.get_mutable(row);
size_t column_index = 0;
@@ -184,11 +185,12 @@ void TableFormattingContext::layout_row(Box const& row, Vector<ColumnWidth>& col
span_width += column_widths[column_index++].used;
cell_state.set_content_width(span_width - cell_state.border_box_left() - cell_state.border_box_right());
- BlockFormattingContext::compute_height(cell, m_state);
+ BlockFormattingContext::compute_height(cell, AvailableSpace(AvailableSize::make_indefinite(), AvailableSize::make_indefinite()));
cell_state.offset = row_state.offset.translated(cell_state.border_box_left() + content_width, cell_state.border_box_top());
// Layout the cell contents a second time, now that we know its final width.
- (void)layout_inside(cell, LayoutMode::Normal);
+ if (auto independent_formatting_context = layout_inside(cell, LayoutMode::Normal, cell_state.available_inner_space_or_constraints_from(available_space)))
+ independent_formatting_context->parent_context_did_dimension_child_root_box();
content_width += span_width;
tallest_cell_height = max(tallest_cell_height, cell_state.border_box_height());
diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
index afef496e96..9f4d56d5ba 100644
--- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
+++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
@@ -23,12 +23,12 @@ public:
explicit TableFormattingContext(LayoutState&, BlockContainer const&, FormattingContext* parent);
~TableFormattingContext();
- virtual void run(Box const&, LayoutMode, AvailableSpace const& available_width, AvailableSpace const& available_height) override;
+ virtual void run(Box const&, LayoutMode, AvailableSpace const&) override;
virtual float automatic_content_height() const override;
private:
- void calculate_column_widths(Box const& row, CSS::Length const& table_width, Vector<ColumnWidth>& column_widths);
- void layout_row(Box const& row, Vector<ColumnWidth>& column_widths);
+ void calculate_column_widths(Box const& row, CSS::Length const& table_width, Vector<ColumnWidth>& column_widths, AvailableSpace const&);
+ void layout_row(Box const& row, Vector<ColumnWidth>& column_widths, AvailableSpace const&);
float m_automatic_content_height { 0 };
};