From 27d4ac316fb6cf494ce01193ff741caffa358ea1 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 14 Oct 2021 22:23:20 +0200 Subject: [PATCH] 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. --- .../LibWeb/Layout/BlockFormattingContext.cpp | 16 +++++++----- Userland/Libraries/LibWeb/Layout/Box.h | 25 +++++++++++++++++++ Userland/Services/WebContent/PageHost.cpp | 6 ++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 6727d9444b..e492ef57b1 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -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([&](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(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(); + 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) diff --git a/Userland/Libraries/LibWeb/Layout/Box.h b/Userland/Libraries/LibWeb/Layout/Box.h index 1841af67c1..914157c42d 100644 --- a/Userland/Libraries/LibWeb/Layout/Box.h +++ b/Userland/Libraries/LibWeb/Layout/Box.h @@ -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 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(); + return *m_overflow_data; + } + + void clear_overflow_data() { m_overflow_data = nullptr; } + protected: Box(DOM::Document& document, DOM::Node* node, NonnullRefPtr style) : NodeWithStyleAndBoxModelMetrics(document, node, move(style)) @@ -152,6 +175,8 @@ private: WeakPtr m_containing_line_box_fragment; OwnPtr m_stacking_context; + + OwnPtr m_overflow_data; }; template<> diff --git a/Userland/Services/WebContent/PageHost.cpp b/Userland/Services/WebContent/PageHost.cpp index 0414a88044..31cccb5ac1 100644 --- a/Userland/Services/WebContent/PageHost.cpp +++ b/Userland/Services/WebContent/PageHost.cpp @@ -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); }