LibWeb: Introduce simple scrollable overflow, size ICB to viewport

Per spec, the initial containing block (ICB) should have the size of the
viewport. We have only done this for the width until now, since we had
no way to express scrollable overflow.

This patch adds Layout::Box::m_overflow_data, an optional struct that
can hold on to information about a box's overflow. Then we have BFC
set the ICB up with some scrollable overflow instead of sizing it to fit
its content vertically.

This fixes a number of broken layouts where correctness depends on
having the appropriate ICB height.
This commit is contained in:
Andreas Kling 2021-10-14 22:23:20 +02:00
parent c94873806c
commit 27d4ac316f
Notes: sideshowbarker 2024-07-18 02:22:27 +09:00
3 changed files with 40 additions and 7 deletions

View File

@ -571,20 +571,24 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m
icb.build_stacking_context_tree();
icb.set_width(viewport_rect.width());
layout_block_level_children(root(), layout_mode);
icb.set_height(viewport_rect.height());
VERIFY(!icb.children_are_inline());
layout_block_level_children(root(), layout_mode);
// FIXME: The ICB should have the height of the viewport.
// Instead of auto-sizing the ICB, we should spill into overflow.
// Compute scrollable overflow.
float lowest_bottom = 0;
icb.for_each_child_of_type<Box>([&](auto& child) {
lowest_bottom = max(lowest_bottom, child.absolute_rect().bottom());
});
// FIXME: This is a hack and should be managed by an overflow mechanism.
icb.set_height(max(static_cast<float>(viewport_rect.height()), lowest_bottom));
if (lowest_bottom >= viewport_rect.height()) {
auto& overflow_data = icb.ensure_overflow_data();
overflow_data.scrollable_overflow_rect = viewport_rect.to_type<float>();
overflow_data.scrollable_overflow_rect.set_height(lowest_bottom);
} else {
icb.clear_overflow_data();
}
}
static Gfx::FloatRect rect_in_coordinate_space(const Box& box, const Box& context_box)

View File

@ -16,6 +16,11 @@ namespace Web::Layout {
class Box : public NodeWithStyleAndBoxModelMetrics {
public:
struct OverflowData {
Gfx::FloatRect scrollable_overflow_rect;
Gfx::FloatPoint scroll_offset;
};
const Gfx::FloatRect absolute_rect() const;
Gfx::FloatPoint effective_offset() const;
@ -129,6 +134,24 @@ public:
bool has_intrinsic_height() const { return intrinsic_height().has_value(); }
bool has_intrinsic_aspect_ratio() const { return intrinsic_aspect_ratio().has_value(); }
bool has_overflow() const { return m_overflow_data; }
Optional<Gfx::FloatRect> scrollable_overflow_rect() const
{
if (!m_overflow_data)
return {};
return m_overflow_data->scrollable_overflow_rect;
}
OverflowData& ensure_overflow_data()
{
if (!m_overflow_data)
m_overflow_data = make<OverflowData>();
return *m_overflow_data;
}
void clear_overflow_data() { m_overflow_data = nullptr; }
protected:
Box(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
: NodeWithStyleAndBoxModelMetrics(document, node, move(style))
@ -152,6 +175,8 @@ private:
WeakPtr<LineBoxFragment> m_containing_line_box_fragment;
OwnPtr<StackingContext> m_stacking_context;
OwnPtr<OverflowData> m_overflow_data;
};
template<>

View File

@ -102,7 +102,11 @@ void PageHost::page_did_layout()
{
auto* layout_root = this->layout_root();
VERIFY(layout_root);
auto content_size = enclosing_int_rect(layout_root->absolute_rect()).size();
Gfx::IntSize content_size;
if (layout_root->has_overflow())
content_size = enclosing_int_rect(layout_root->scrollable_overflow_rect().value()).size();
else
content_size = enclosing_int_rect(layout_root->absolute_rect()).size();
m_client.async_did_layout(content_size);
}