diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index c605b266122..6a039688dfa 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -284,6 +284,7 @@ set(SOURCES Page/Page.cpp Painting/BackgroundPainting.cpp Painting/BorderPainting.cpp + Painting/Box.cpp Painting/PaintContext.cpp Painting/ShadowPainting.cpp Painting/StackingContext.cpp diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 7d40221d176..9ffa0a28ebe 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -35,6 +35,7 @@ #include #include #include +#include namespace Web::DOM { @@ -436,7 +437,7 @@ NonnullRefPtr Element::get_bounding_client_rect() const auto viewport_offset = document().browsing_context()->viewport_scroll_offset(); auto& box = static_cast(*layout_node()); - return Geometry::DOMRect::create(box.absolute_rect().translated(-viewport_offset.x(), -viewport_offset.y())); + return Geometry::DOMRect::create(box.m_paint_box->absolute_rect().translated(-viewport_offset.x(), -viewport_offset.y())); } // https://drafts.csswg.org/cssom-view/#dom-element-getclientrects @@ -465,34 +466,30 @@ NonnullRefPtr Element::get_client_rects() const int Element::client_top() const { - if (!layout_node() || !layout_node()->is_box()) - return 0; - auto& box = static_cast(*layout_node()); - return box.absolute_rect().top(); + if (auto* paint_box = this->paint_box()) + return paint_box->absolute_rect().top(); + return 0; } int Element::client_left() const { - if (!layout_node() || !layout_node()->is_box()) - return 0; - auto& box = static_cast(*layout_node()); - return box.absolute_rect().left(); + if (auto* paint_box = this->paint_box()) + return paint_box->absolute_rect().left(); + return 0; } int Element::client_width() const { - if (!layout_node() || !layout_node()->is_box()) - return 0; - auto& box = static_cast(*layout_node()); - return box.absolute_rect().width(); + if (auto* paint_box = this->paint_box()) + return paint_box->absolute_rect().width(); + return 0; } int Element::client_height() const { - if (!layout_node() || !layout_node()->is_box()) - return 0; - auto& box = static_cast(*layout_node()); - return box.absolute_rect().height(); + if (auto* paint_box = this->paint_box()) + return paint_box->absolute_rect().height(); + return 0; } void Element::children_changed() diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp index 1b52e3199e2..874aa48dc66 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.cpp +++ b/Userland/Libraries/LibWeb/DOM/Node.cpp @@ -1010,4 +1010,13 @@ size_t Node::length() const return child_count(); } +Painting::Box const* Node::paint_box() const +{ + if (!layout_node()) + return nullptr; + if (!layout_node()->is_box()) + return nullptr; + return static_cast(*layout_node()).m_paint_box; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Node.h b/Userland/Libraries/LibWeb/DOM/Node.h index 59992fa52f4..695b75ff895 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.h +++ b/Userland/Libraries/LibWeb/DOM/Node.h @@ -158,6 +158,8 @@ public: const Layout::Node* layout_node() const { return m_layout_node; } Layout::Node* layout_node() { return m_layout_node; } + Painting::Box const* paint_box() const; + void set_layout_node(Badge, Layout::Node*) const; virtual bool is_child_allowed(const Node&) const { return true; } diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp index 984adfe6044..ca4c25499b2 100644 --- a/Userland/Libraries/LibWeb/Dump.cpp +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * Copyright (c) 2021, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -26,6 +26,7 @@ #include #include #include +#include #include namespace Web { @@ -166,10 +167,10 @@ void dump_tree(StringBuilder& builder, Layout::Node const& layout_node, bool sho builder.appendff("@{:p} ", &layout_node); builder.appendff("at ({},{}) content-size {}x{}", - box.absolute_x(), - box.absolute_y(), - box.content_width(), - box.content_height()); + box.m_paint_box->absolute_x(), + box.m_paint_box->absolute_y(), + box.m_paint_box->content_width(), + box.m_paint_box->content_height()); if (box.is_positioned()) builder.appendff(" {}positioned{}", positioned_color_on, color_off); @@ -204,7 +205,7 @@ void dump_tree(StringBuilder& builder, Layout::Node const& layout_node, bool sho box.box_model().margin.left, box.box_model().border.left, box.box_model().padding.left, - box.content_width(), + box.m_paint_box->content_width(), box.box_model().padding.right, box.box_model().border.right, box.box_model().margin.right); @@ -214,7 +215,7 @@ void dump_tree(StringBuilder& builder, Layout::Node const& layout_node, bool sho box.box_model().margin.top, box.box_model().border.top, box.box_model().padding.top, - box.content_height(), + box.m_paint_box->content_height(), box.box_model().padding.bottom, box.box_model().border.bottom, box.box_model().margin.bottom); @@ -225,8 +226,8 @@ void dump_tree(StringBuilder& builder, Layout::Node const& layout_node, bool sho if (is(layout_node) && static_cast(layout_node).children_are_inline()) { auto& block = static_cast(layout_node); - for (size_t line_box_index = 0; line_box_index < block.line_boxes().size(); ++line_box_index) { - auto& line_box = block.line_boxes()[line_box_index]; + for (size_t line_box_index = 0; line_box_index < block.m_paint_box->line_boxes().size(); ++line_box_index) { + auto& line_box = block.m_paint_box->line_boxes()[line_box_index]; for (size_t i = 0; i < indent; ++i) builder.append(" "); builder.appendff(" {}line {}{} width: {}, bottom: {}, baseline: {}\n", diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 45f75c15b92..e8b60157a1f 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -263,6 +263,10 @@ namespace Web::NavigationTiming { class PerformanceTiming; } +namespace Web::Painting { +class Box; +} + namespace Web::RequestIdleCallback { class IdleDeadline; } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp index b0b52aff66d..30b9f505ed4 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -150,17 +151,17 @@ int HTMLElement::offset_left() const // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetwidth int HTMLElement::offset_width() const { - if (!layout_node() || !layout_node()->is_box()) - return 0; - return static_cast(*layout_node()).border_box_width(); + if (auto* paint_box = this->paint_box()) + return paint_box->border_box_width(); + return 0; } // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetheight int HTMLElement::offset_height() const { - if (!layout_node() || !layout_node()->is_box()) - return 0; - return static_cast(*layout_node()).border_box_height(); + if (auto* paint_box = this->paint_box()) + return paint_box->border_box_height(); + return 0; } bool HTMLElement::cannot_navigate() const diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp index b2e3c4d94ba..3dba508a0d7 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -13,6 +13,7 @@ #include #include #include +#include namespace Web::HTML { @@ -84,8 +85,8 @@ unsigned HTMLImageElement::width() const const_cast(document()).update_layout(); // Return the rendered width of the image, in CSS pixels, if the image is being rendered. - if (layout_node() && is(*layout_node())) - return static_cast(*layout_node()).content_width(); + if (auto* paint_box = this->paint_box()) + return paint_box->content_width(); // ...or else the density-corrected intrinsic width and height of the image, in CSS pixels, // if the image has intrinsic dimensions and is available but not being rendered. @@ -107,8 +108,8 @@ unsigned HTMLImageElement::height() const const_cast(document()).update_layout(); // Return the rendered height of the image, in CSS pixels, if the image is being rendered. - if (layout_node() && is(*layout_node())) - return static_cast(*layout_node()).content_height(); + if (auto* paint_box = this->paint_box()) + return paint_box->content_height(); // ...or else the density-corrected intrinsic height and height of the image, in CSS pixels, // if the image has intrinsic dimensions and is available but not being rendered. diff --git a/Userland/Libraries/LibWeb/InProcessWebView.cpp b/Userland/Libraries/LibWeb/InProcessWebView.cpp index b93d783ee85..33751c025ca 100644 --- a/Userland/Libraries/LibWeb/InProcessWebView.cpp +++ b/Userland/Libraries/LibWeb/InProcessWebView.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,8 @@ void InProcessWebView::set_preferred_color_scheme(CSS::PreferredColorScheme colo void InProcessWebView::page_did_layout() { VERIFY(layout_root()); - set_content_size(layout_root()->content_size().to_type()); + VERIFY(layout_root()->m_paint_box); + set_content_size(layout_root()->m_paint_box->content_size().to_type()); } void InProcessWebView::page_did_change_title(const String& title) @@ -177,13 +179,13 @@ void InProcessWebView::layout_and_sync_size() bool had_horizontal_scrollbar = horizontal_scrollbar().is_visible(); page().top_level_browsing_context().set_size(available_size()); - set_content_size(layout_root()->content_size().to_type()); + set_content_size(layout_root()->m_paint_box->content_size().to_type()); // NOTE: If layout caused us to gain or lose scrollbars, we have to lay out again // since the scrollbars now take up some of the available space. if (had_vertical_scrollbar != vertical_scrollbar().is_visible() || had_horizontal_scrollbar != horizontal_scrollbar().is_visible()) { page().top_level_browsing_context().set_size(available_size()); - set_content_size(layout_root()->content_size().to_type()); + set_content_size(layout_root()->m_paint_box->content_size().to_type()); } page().top_level_browsing_context().set_viewport_scroll_offset({ horizontal_scrollbar().value(), vertical_scrollbar().value() }); diff --git a/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp b/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp index 81948ada176..517dac46064 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -11,6 +11,7 @@ #include #include #include +#include namespace Web::Layout { @@ -46,11 +47,11 @@ void BlockContainer::paint(PaintContext& context, PaintPhase phase) if (should_clip_overflow()) { context.painter().save(); // FIXME: Handle overflow-x and overflow-y being different values. - context.painter().add_clip_rect(enclosing_int_rect(absolute_padding_box_rect())); + context.painter().add_clip_rect(enclosing_int_rect(m_paint_box->absolute_padding_box_rect())); context.painter().translate(-m_scroll_offset.to_type()); } - for (auto& line_box : m_line_boxes) { + for (auto& line_box : m_paint_box->m_line_boxes) { for (auto& fragment : line_box.fragments()) { if (context.should_show_line_box_borders()) context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Green); @@ -64,7 +65,7 @@ void BlockContainer::paint(PaintContext& context, PaintPhase phase) // FIXME: Merge this loop with the above somehow.. if (phase == PaintPhase::FocusOutline) { - for (auto& line_box : m_line_boxes) { + for (auto& line_box : m_paint_box->m_line_boxes) { for (auto& fragment : line_box.fragments()) { auto* node = fragment.layout_node().dom_node(); if (!node) @@ -85,7 +86,7 @@ HitTestResult BlockContainer::hit_test(const Gfx::IntPoint& position, HitTestTyp return Box::hit_test(position, type); HitTestResult last_good_candidate; - for (auto& line_box : m_line_boxes) { + for (auto& line_box : m_paint_box->m_line_boxes) { for (auto& fragment : line_box.fragments()) { if (is(fragment.layout_node()) && verify_cast(fragment.layout_node()).stacking_context()) continue; @@ -101,7 +102,7 @@ HitTestResult BlockContainer::hit_test(const Gfx::IntPoint& position, HitTestTyp if (type == HitTestType::TextCursor && last_good_candidate.layout_node) return last_good_candidate; - return { absolute_border_box_rect().contains(position.x(), position.y()) ? this : nullptr }; + return { m_paint_box->absolute_border_box_rect().contains(position.x(), position.y()) ? this : nullptr }; } bool BlockContainer::is_scrollable() const diff --git a/Userland/Libraries/LibWeb/Layout/BlockContainer.h b/Userland/Libraries/LibWeb/Layout/BlockContainer.h index d05874b81e2..2eda20df56f 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockContainer.h +++ b/Userland/Libraries/LibWeb/Layout/BlockContainer.h @@ -27,22 +27,10 @@ public: BlockContainer* next_sibling() { return verify_cast(Node::next_sibling()); } const BlockContainer* next_sibling() const { return verify_cast(Node::next_sibling()); } - template - void for_each_fragment(Callback); - template - void for_each_fragment(Callback) const; - bool is_scrollable() const; const Gfx::FloatPoint& scroll_offset() const { return m_scroll_offset; } void set_scroll_offset(const Gfx::FloatPoint&); - const Vector& line_boxes() const { return m_line_boxes; } - - void set_line_boxes(Vector&& line_boxes) { m_line_boxes = move(line_boxes); } - -protected: - Vector m_line_boxes; - private: virtual bool is_block_container() const final { return true; } virtual bool wants_mouse_events() const override { return false; } @@ -56,26 +44,4 @@ private: template<> inline bool Node::fast_is() const { return is_block_container(); } -template -void BlockContainer::for_each_fragment(Callback callback) -{ - for (auto& line_box : line_boxes()) { - for (auto& fragment : line_box.fragments()) { - if (callback(fragment) == IterationDecision::Break) - return; - } - } -} - -template -void BlockContainer::for_each_fragment(Callback callback) const -{ - for (auto& line_box : line_boxes()) { - for (auto& fragment : line_box.fragments()) { - if (callback(fragment) == IterationDecision::Break) - return; - } - } -} - } diff --git a/Userland/Libraries/LibWeb/Layout/Box.cpp b/Userland/Libraries/LibWeb/Layout/Box.cpp index 73d0d3b940f..34d0fb46028 100644 --- a/Userland/Libraries/LibWeb/Layout/Box.cpp +++ b/Userland/Libraries/LibWeb/Layout/Box.cpp @@ -15,10 +15,25 @@ #include #include #include +#include #include namespace Web::Layout { +Box::Box(DOM::Document& document, DOM::Node* node, NonnullRefPtr style) + : NodeWithStyleAndBoxModelMetrics(document, node, move(style)) +{ +} + +Box::Box(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values) + : NodeWithStyleAndBoxModelMetrics(document, node, move(computed_values)) +{ +} + +Box::~Box() +{ +} + void Box::paint(PaintContext& context, PaintPhase phase) { if (!is_visible()) @@ -34,17 +49,17 @@ void Box::paint(PaintContext& context, PaintPhase phase) } if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) { - auto content_rect = absolute_rect(); + auto content_rect = m_paint_box->absolute_rect(); auto margin_box = box_model().margin_box(); Gfx::FloatRect margin_rect; - margin_rect.set_x(absolute_x() - margin_box.left); - margin_rect.set_width(content_width() + margin_box.left + margin_box.right); - margin_rect.set_y(absolute_y() - margin_box.top); - margin_rect.set_height(content_height() + margin_box.top + margin_box.bottom); + margin_rect.set_x(m_paint_box->absolute_x() - margin_box.left); + margin_rect.set_width(m_paint_box->content_width() + margin_box.left + margin_box.right); + margin_rect.set_y(m_paint_box->absolute_y() - margin_box.top); + margin_rect.set_height(m_paint_box->content_height() + margin_box.top + margin_box.bottom); - auto border_rect = absolute_border_box_rect(); - auto padding_rect = absolute_padding_box_rect(); + auto border_rect = m_paint_box->absolute_border_box_rect(); + auto padding_rect = m_paint_box->absolute_padding_box_rect(); auto paint_inspector_rect = [&](Gfx::FloatRect const& rect, Color color) { context.painter().fill_rect(enclosing_int_rect(rect), Color(color).with_alpha(100)); @@ -74,7 +89,7 @@ void Box::paint(PaintContext& context, PaintPhase phase) } if (phase == PaintPhase::FocusOutline && dom_node() && dom_node()->is_element() && verify_cast(*dom_node()).is_focused()) { - context.painter().draw_rect(enclosing_int_rect(absolute_rect()), context.palette().focus_outline()); + context.painter().draw_rect(enclosing_int_rect(m_paint_box->absolute_rect()), context.palette().focus_outline()); } } @@ -86,7 +101,7 @@ void Box::paint_border(PaintContext& context) .bottom = computed_values().border_bottom(), .left = computed_values().border_left(), }; - Painting::paint_all_borders(context, absolute_border_box_rect(), normalized_border_radius_data(), borders_data); + Painting::paint_all_borders(context, m_paint_box->absolute_border_box_rect(), normalized_border_radius_data(), borders_data); } void Box::paint_background(PaintContext& context) @@ -110,13 +125,13 @@ void Box::paint_background(PaintContext& context) background_color = document().background_color(context.palette()); } } else { - background_rect = enclosing_int_rect(absolute_padding_box_rect()); + background_rect = enclosing_int_rect(m_paint_box->absolute_padding_box_rect()); } // HACK: If the Box has a border, use the bordered_rect to paint the background. // This way if we have a border-radius there will be no gap between the filling and actual border. if (computed_values().border_top().width || computed_values().border_right().width || computed_values().border_bottom().width || computed_values().border_left().width) - background_rect = enclosing_int_rect(absolute_border_box_rect()); + background_rect = enclosing_int_rect(m_paint_box->absolute_border_box_rect()); Painting::paint_background(context, *this, background_rect, background_color, background_layers, normalized_border_radius_data()); } @@ -138,12 +153,12 @@ void Box::paint_box_shadow(PaintContext& context) static_cast(layer.spread_distance.to_px(*this)), layer.placement == CSS::BoxShadowPlacement::Outer ? Painting::BoxShadowPlacement::Outer : Painting::BoxShadowPlacement::Inner); } - Painting::paint_box_shadow(context, enclosing_int_rect(absolute_border_box_rect()), resolved_box_shadow_data); + Painting::paint_box_shadow(context, enclosing_int_rect(m_paint_box->absolute_border_box_rect()), resolved_box_shadow_data); } Painting::BorderRadiusData Box::normalized_border_radius_data() { - return Painting::normalized_border_radius_data(*this, absolute_border_box_rect(), + return Painting::normalized_border_radius_data(*this, m_paint_box->absolute_border_box_rect(), computed_values().border_top_left_radius(), computed_values().border_top_right_radius(), computed_values().border_bottom_right_radius(), @@ -178,7 +193,7 @@ HitTestResult Box::hit_test(const Gfx::IntPoint& position, HitTestType type) con // FIXME: It would be nice if we could confidently skip over hit testing // parts of the layout tree, but currently we can't just check // m_rect.contains() since inline text rects can't be trusted.. - HitTestResult result { absolute_border_box_rect().contains(position.x(), position.y()) ? this : nullptr }; + HitTestResult result { m_paint_box->absolute_border_box_rect().contains(position.x(), position.y()) ? this : nullptr }; for_each_child_in_paint_order([&](auto& child) { auto child_result = child.hit_test(position, type); if (child_result.layout_node) @@ -190,7 +205,7 @@ HitTestResult Box::hit_test(const Gfx::IntPoint& position, HitTestType type) con void Box::set_needs_display() { if (!is_inline()) { - browsing_context().set_needs_display(enclosing_int_rect(absolute_rect())); + browsing_context().set_needs_display(enclosing_int_rect(m_paint_box->absolute_rect())); return; } @@ -202,45 +217,6 @@ bool Box::is_body() const return dom_node() && dom_node() == document().body(); } -void Box::set_offset(const Gfx::FloatPoint& offset) -{ - if (m_offset == offset) - return; - m_offset = offset; - did_set_rect(); -} - -void Box::set_content_size(Gfx::FloatSize const& size) -{ - if (m_content_size == size) - return; - m_content_size = size; - did_set_rect(); -} - -Gfx::FloatPoint Box::effective_offset() const -{ - if (m_containing_line_box_fragment.has_value()) { - auto const& fragment = containing_block()->line_boxes()[m_containing_line_box_fragment->line_box_index].fragments()[m_containing_line_box_fragment->fragment_index]; - return fragment.offset(); - } - return m_offset; -} - -const Gfx::FloatRect Box::absolute_rect() const -{ - Gfx::FloatRect rect { effective_offset(), content_size() }; - for (auto* block = containing_block(); block; block = block->containing_block()) { - rect.translate_by(block->effective_offset()); - } - return rect; -} - -void Box::set_containing_line_box_fragment(Optional fragment_coordinate) -{ - m_containing_line_box_fragment = fragment_coordinate; -} - StackingContext* Box::enclosing_stacking_context() { for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { @@ -262,7 +238,7 @@ void Box::before_children_paint(PaintContext& context, PaintPhase phase) // FIXME: Support more overflow variations. if (computed_values().overflow_x() == CSS::Overflow::Hidden && computed_values().overflow_y() == CSS::Overflow::Hidden) { context.painter().save(); - context.painter().add_clip_rect(enclosing_int_rect(absolute_border_box_rect())); + context.painter().add_clip_rect(enclosing_int_rect(m_paint_box->absolute_border_box_rect())); } } diff --git a/Userland/Libraries/LibWeb/Layout/Box.h b/Userland/Libraries/LibWeb/Layout/Box.h index 8148c8f72a7..3c6e11a900a 100644 --- a/Userland/Libraries/LibWeb/Layout/Box.h +++ b/Userland/Libraries/LibWeb/Layout/Box.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -21,64 +21,7 @@ struct LineBoxFragmentCoordinate { 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; - - void set_offset(const Gfx::FloatPoint& offset); - void set_offset(float x, float y) { set_offset({ x, y }); } - - Gfx::FloatSize const& content_size() const { return m_content_size; } - void set_content_size(Gfx::FloatSize const&); - void set_content_size(float width, float height) { set_content_size({ width, height }); } - - void set_content_width(float width) { set_content_size(width, content_height()); } - void set_content_height(float height) { set_content_size(content_width(), height); } - float content_width() const { return m_content_size.width(); } - float content_height() const { return m_content_size.height(); } - - Gfx::FloatRect absolute_padding_box_rect() const - { - auto absolute_rect = this->absolute_rect(); - Gfx::FloatRect rect; - rect.set_x(absolute_rect.x() - box_model().padding.left); - rect.set_width(content_width() + box_model().padding.left + box_model().padding.right); - rect.set_y(absolute_rect.y() - box_model().padding.top); - rect.set_height(content_height() + box_model().padding.top + box_model().padding.bottom); - return rect; - } - - Gfx::FloatRect absolute_border_box_rect() const - { - auto padded_rect = this->absolute_padding_box_rect(); - Gfx::FloatRect rect; - rect.set_x(padded_rect.x() - box_model().border.left); - rect.set_width(padded_rect.width() + box_model().border.left + box_model().border.right); - rect.set_y(padded_rect.y() - box_model().border.top); - rect.set_height(padded_rect.height() + box_model().border.top + box_model().border.bottom); - return rect; - } - - float border_box_width() const - { - auto border_box = box_model().border_box(); - return content_width() + border_box.left + border_box.right; - } - - float border_box_height() const - { - auto border_box = box_model().border_box(); - return content_height() + border_box.top + border_box.bottom; - } - - float absolute_x() const { return absolute_rect().x(); } - float absolute_y() const { return absolute_rect().y(); } - Gfx::FloatPoint absolute_position() const { return absolute_rect().location(); } + OwnPtr m_paint_box; bool is_out_of_flow(FormattingContext const&) const; @@ -87,8 +30,6 @@ public: bool is_body() const; - void set_containing_line_box_fragment(Optional); - StackingContext* stacking_context() { return m_stacking_context; } const StackingContext* stacking_context() const { return m_stacking_context; } void set_stacking_context(NonnullOwnPtr context) { m_stacking_context = move(context); } @@ -109,45 +50,21 @@ 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.has_value(); } - - Optional scrollable_overflow_rect() const - { - if (!m_overflow_data.has_value()) - return {}; - return m_overflow_data->scrollable_overflow_rect; - } - - void set_overflow_data(Optional data) { m_overflow_data = move(data); } - virtual void before_children_paint(PaintContext&, PaintPhase) override; virtual void after_children_paint(PaintContext&, PaintPhase) override; -protected: - Box(DOM::Document& document, DOM::Node* node, NonnullRefPtr style) - : NodeWithStyleAndBoxModelMetrics(document, node, move(style)) - { - } - - Box(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values) - : NodeWithStyleAndBoxModelMetrics(document, node, move(computed_values)) - { - } + virtual ~Box() override; virtual void did_set_rect() { } +protected: + Box(DOM::Document&, DOM::Node*, NonnullRefPtr); + Box(DOM::Document&, DOM::Node*, CSS::ComputedValues); + private: virtual bool is_box() const final { return true; } - Gfx::FloatPoint m_offset; - Gfx::FloatSize m_content_size; - - // Some boxes hang off of line box fragments. (inline-block, inline-table, replaced, etc) - Optional m_containing_line_box_fragment; - OwnPtr m_stacking_context; - - Optional m_overflow_data; }; template<> diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp index 53dd25794e6..ba2bbd35444 100644 --- a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Web::Layout { @@ -38,7 +39,7 @@ void ButtonBox::paint(PaintContext& context, PaintPhase phase) LabelableNode::paint(context, phase); if (phase == PaintPhase::Foreground) { - auto text_rect = enclosing_int_rect(absolute_rect()); + auto text_rect = enclosing_int_rect(m_paint_box->absolute_rect()); if (m_being_pressed) text_rect.translate_by(1, 1); context.painter().draw_text(text_rect, dom_node().value(), font(), Gfx::TextAlignment::Center, computed_values().color()); @@ -66,7 +67,7 @@ void ButtonBox::handle_mouseup(Badge, const Gfx::IntPoint& positio NonnullRefPtr protected_this = *this; NonnullRefPtr protected_browsing_context = browsing_context(); - bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_node_or_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); if (!is_inside_node_or_label) is_inside_node_or_label = Label::is_inside_associated_label(*this, position); @@ -84,7 +85,7 @@ void ButtonBox::handle_mousemove(Badge, const Gfx::IntPoint& posit if (!m_tracking_mouse || !dom_node().enabled()) return; - bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_node_or_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); if (!is_inside_node_or_label) is_inside_node_or_label = Label::is_inside_associated_label(*this, position); diff --git a/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp b/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp index 7721c9dc1a7..c79fde12b73 100644 --- a/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/CanvasBox.cpp @@ -1,11 +1,12 @@ /* - * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include +#include namespace Web::Layout { @@ -33,11 +34,11 @@ void CanvasBox::paint(PaintContext& context, PaintPhase phase) if (phase == PaintPhase::Foreground) { // FIXME: This should be done at a different level. Also rect() does not include padding etc! - if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) + if (!context.viewport_rect().intersects(enclosing_int_rect(m_paint_box->absolute_rect()))) return; if (dom_node().bitmap()) - context.painter().draw_scaled_bitmap(rounded_int_rect(absolute_rect()), *dom_node().bitmap(), dom_node().bitmap()->rect(), 1.0f, to_gfx_scaling_mode(computed_values().image_rendering())); + context.painter().draw_scaled_bitmap(rounded_int_rect(m_paint_box->absolute_rect()), *dom_node().bitmap(), dom_node().bitmap()->rect(), 1.0f, to_gfx_scaling_mode(computed_values().image_rendering())); } } diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp index 8d1a3da5894..517c37fa7b6 100644 --- a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Web::Layout { @@ -34,7 +35,7 @@ void CheckBox::paint(PaintContext& context, PaintPhase phase) LabelableNode::paint(context, phase); if (phase == PaintPhase::Foreground) { - Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), dom_node().enabled(), dom_node().checked(), m_being_pressed); + Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(m_paint_box->absolute_rect()), context.palette(), dom_node().enabled(), dom_node().checked(), m_being_pressed); } } @@ -58,7 +59,7 @@ void CheckBox::handle_mouseup(Badge, const Gfx::IntPoint& position // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. NonnullRefPtr protect = *this; - bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_node_or_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); if (!is_inside_node_or_label) is_inside_node_or_label = Label::is_inside_associated_label(*this, position); @@ -77,7 +78,7 @@ void CheckBox::handle_mousemove(Badge, const Gfx::IntPoint& positi if (!m_tracking_mouse || !dom_node().enabled()) return; - bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_node_or_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); if (!is_inside_node_or_label) is_inside_node_or_label = Label::is_inside_associated_label(*this, position); diff --git a/Userland/Libraries/LibWeb/Layout/FormattingState.cpp b/Userland/Libraries/LibWeb/Layout/FormattingState.cpp index ebfda928e96..3060a3ca346 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingState.cpp +++ b/Userland/Libraries/LibWeb/Layout/FormattingState.cpp @@ -38,19 +38,15 @@ void FormattingState::commit() node.box_model().border = { node_state.border_top, node_state.border_right, node_state.border_bottom, node_state.border_left }; node.box_model().margin = { node_state.margin_top, node_state.margin_right, node_state.margin_bottom, node_state.margin_left }; - // For boxes, transfer relative offset, size, and overflow data. + // For boxes, transfer all the state needed for painting. if (is(node)) { auto& box = static_cast(node); - box.set_offset(node_state.offset); - box.set_content_size(node_state.content_width, node_state.content_height); - box.set_overflow_data(move(node_state.overflow_data)); - box.set_containing_line_box_fragment(node_state.containing_line_box_fragment); - } - - // For block containers, transfer line boxes. - if (is(node)) { - auto& block_container = static_cast(node); - block_container.set_line_boxes(move(node_state.line_boxes)); + box.m_paint_box = Painting::Box::create(box); + box.m_paint_box->set_offset(node_state.offset); + box.m_paint_box->set_content_size(node_state.content_width, node_state.content_height); + box.m_paint_box->set_overflow_data(move(node_state.overflow_data)); + box.m_paint_box->set_containing_line_box_fragment(node_state.containing_line_box_fragment); + box.m_paint_box->set_line_boxes(move(node_state.line_boxes)); } } } diff --git a/Userland/Libraries/LibWeb/Layout/FormattingState.h b/Userland/Libraries/LibWeb/Layout/FormattingState.h index 9a68830efee..97c38a290ca 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingState.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingState.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace Web::Layout { @@ -54,12 +55,12 @@ struct FormattingState { float border_box_width() const { return border_box_left() + content_width + border_box_right(); } float border_box_height() const { return border_box_top() + content_height + border_box_bottom(); } - Optional overflow_data; + Optional overflow_data; - Layout::Box::OverflowData& ensure_overflow_data() + Painting::Box::OverflowData& ensure_overflow_data() { if (!overflow_data.has_value()) - overflow_data = Layout::Box::OverflowData {}; + overflow_data = Painting::Box::OverflowData {}; return *overflow_data; } diff --git a/Userland/Libraries/LibWeb/Layout/FrameBox.cpp b/Userland/Libraries/LibWeb/Layout/FrameBox.cpp index 21c4bf2ff07..ee5c3569f36 100644 --- a/Userland/Libraries/LibWeb/Layout/FrameBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/FrameBox.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace Web::Layout { @@ -46,8 +47,8 @@ void FrameBox::paint(PaintContext& context, PaintPhase phase) context.painter().save(); auto old_viewport_rect = context.viewport_rect(); - context.painter().add_clip_rect(enclosing_int_rect(absolute_rect())); - context.painter().translate(absolute_x(), absolute_y()); + context.painter().add_clip_rect(enclosing_int_rect(m_paint_box->absolute_rect())); + context.painter().translate(m_paint_box->absolute_x(), m_paint_box->absolute_y()); context.set_viewport_rect({ {}, dom_node().nested_browsing_context()->size() }); const_cast(hosted_layout_tree)->paint_all_phases(context); @@ -57,7 +58,7 @@ void FrameBox::paint(PaintContext& context, PaintPhase phase) if constexpr (HIGHLIGHT_FOCUSED_FRAME_DEBUG) { if (dom_node().nested_browsing_context()->is_focused_context()) { - context.painter().draw_rect(absolute_rect().to_type(), Color::Cyan); + context.painter().draw_rect(m_paint_box->absolute_rect().to_type(), Color::Cyan); } } } @@ -68,7 +69,7 @@ void FrameBox::did_set_rect() ReplacedBox::did_set_rect(); VERIFY(dom_node().nested_browsing_context()); - dom_node().nested_browsing_context()->set_size(content_size().to_type()); + dom_node().nested_browsing_context()->set_size(m_paint_box->content_size().to_type()); } } diff --git a/Userland/Libraries/LibWeb/Layout/ImageBox.cpp b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp index cdfea154543..d682cc7c3a7 100644 --- a/Userland/Libraries/LibWeb/Layout/ImageBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/ImageBox.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Web::Layout { @@ -67,8 +68,7 @@ void ImageBox::prepare_for_replaced_layout() } if (!has_intrinsic_width() && !has_intrinsic_height()) { - set_content_width(16); - set_content_height(16); + // FIXME: Do something. } } @@ -78,7 +78,7 @@ void ImageBox::paint(PaintContext& context, PaintPhase phase) return; // FIXME: This should be done at a different level. Also rect() does not include padding etc! - if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) + if (!context.viewport_rect().intersects(enclosing_int_rect(m_paint_box->absolute_rect()))) return; ReplacedBox::paint(context, phase); @@ -87,13 +87,13 @@ void ImageBox::paint(PaintContext& context, PaintPhase phase) if (renders_as_alt_text()) { auto& image_element = verify_cast(dom_node()); context.painter().set_font(Gfx::FontDatabase::default_font()); - Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2); + Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(m_paint_box->absolute_rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2); auto alt = image_element.alt(); if (alt.is_empty()) alt = image_element.src(); - context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, computed_values().color(), Gfx::TextElision::Right); + context.painter().draw_text(enclosing_int_rect(m_paint_box->absolute_rect()), alt, Gfx::TextAlignment::Center, computed_values().color(), Gfx::TextElision::Right); } else if (auto bitmap = m_image_loader.bitmap(m_image_loader.current_frame_index())) { - context.painter().draw_scaled_bitmap(rounded_int_rect(absolute_rect()), *bitmap, bitmap->rect(), 1.0f, to_gfx_scaling_mode(computed_values().image_rendering())); + context.painter().draw_scaled_bitmap(rounded_int_rect(m_paint_box->absolute_rect()), *bitmap, bitmap->rect(), 1.0f, to_gfx_scaling_mode(computed_values().image_rendering())); } } } @@ -107,7 +107,7 @@ bool ImageBox::renders_as_alt_text() const void ImageBox::browsing_context_did_set_viewport_rect(Gfx::IntRect const& viewport_rect) { - m_image_loader.set_visible_in_viewport(viewport_rect.to_type().intersects(absolute_rect())); + m_image_loader.set_visible_in_viewport(viewport_rect.to_type().intersects(m_paint_box->absolute_rect())); } } diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp b/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp index 98065f37ecf..f58258532ce 100644 --- a/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp +++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace Web::Layout { @@ -41,7 +42,7 @@ void InitialContainingBlock::build_stacking_context_tree() void InitialContainingBlock::paint_all_phases(PaintContext& context) { - context.painter().fill_rect(enclosing_int_rect(absolute_rect()), context.palette().base()); + context.painter().fill_rect(enclosing_int_rect(m_paint_box->absolute_rect()), context.palette().base()); context.painter().translate(-context.viewport_rect().location()); stacking_context()->paint(context); } diff --git a/Userland/Libraries/LibWeb/Layout/InlineNode.cpp b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp index 68048e7cb8c..16ddd6206a0 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineNode.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp @@ -36,7 +36,7 @@ void InlineNode::paint(PaintContext& context, PaintPhase phase) auto top_right_border_radius = computed_values().border_top_right_radius(); auto bottom_right_border_radius = computed_values().border_bottom_right_radius(); auto bottom_left_border_radius = computed_values().border_bottom_left_radius(); - auto containing_block_position_in_absolute_coordinates = containing_block()->absolute_position(); + auto containing_block_position_in_absolute_coordinates = containing_block()->m_paint_box->absolute_position(); for_each_fragment([&](auto const& fragment, bool is_first_fragment, bool is_last_fragment) { Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() }; @@ -87,7 +87,7 @@ void InlineNode::paint(PaintContext& context, PaintPhase phase) .left = computed_values().border_left(), }; - auto containing_block_position_in_absolute_coordinates = containing_block()->absolute_position(); + auto containing_block_position_in_absolute_coordinates = containing_block()->m_paint_box->absolute_position(); for_each_fragment([&](auto const& fragment, bool is_first_fragment, bool is_last_fragment) { Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() }; @@ -130,7 +130,7 @@ void InlineNode::for_each_fragment(Callback callback) { // FIXME: This will be slow if the containing block has a lot of fragments! Vector fragments; - containing_block()->for_each_fragment([&](auto& fragment) { + containing_block()->m_paint_box->for_each_fragment([&](auto& fragment) { if (is_inclusive_ancestor_of(fragment.layout_node())) fragments.append(fragment); return IterationDecision::Continue; diff --git a/Userland/Libraries/LibWeb/Layout/Label.cpp b/Userland/Libraries/LibWeb/Layout/Label.cpp index 10d7a419730..b58c1c8f8f0 100644 --- a/Userland/Libraries/LibWeb/Layout/Label.cpp +++ b/Userland/Libraries/LibWeb/Layout/Label.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Web::Layout { @@ -46,8 +47,8 @@ void Label::handle_mouseup_on_label(Badge, const Gfx::IntPoint& positi NonnullRefPtr protect = *this; if (auto* control = labeled_control(); control) { - bool is_inside_control = enclosing_int_rect(control->absolute_rect()).contains(position); - bool is_inside_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_control = enclosing_int_rect(control->m_paint_box->absolute_rect()).contains(position); + bool is_inside_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); if (is_inside_control || is_inside_label) control->handle_associated_label_mouseup({}); @@ -62,8 +63,8 @@ void Label::handle_mousemove_on_label(Badge, const Gfx::IntPoint& posi return; if (auto* control = labeled_control(); control) { - bool is_inside_control = enclosing_int_rect(control->absolute_rect()).contains(position); - bool is_inside_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_control = enclosing_int_rect(control->m_paint_box->absolute_rect()).contains(position); + bool is_inside_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); control->handle_associated_label_mousemove({}, is_inside_control || is_inside_label); } @@ -72,7 +73,7 @@ void Label::handle_mousemove_on_label(Badge, const Gfx::IntPoint& posi bool Label::is_inside_associated_label(LabelableNode& control, const Gfx::IntPoint& position) { if (auto* label = label_for_control_node(control); label) - return enclosing_int_rect(label->absolute_rect()).contains(position); + return enclosing_int_rect(label->m_paint_box->absolute_rect()).contains(position); return false; } diff --git a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp index 2f160c8ef8c..87b485f9f4c 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp +++ b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp @@ -48,7 +48,7 @@ StringView LineBoxFragment::text() const const Gfx::FloatRect LineBoxFragment::absolute_rect() const { Gfx::FloatRect rect { {}, size() }; - rect.set_location(m_layout_node.containing_block()->absolute_position()); + rect.set_location(m_layout_node.containing_block()->m_paint_box->absolute_position()); rect.translate_by(offset()); return rect; } diff --git a/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp index 714cb2affad..06f67ec0cae 100644 --- a/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace Web::Layout { @@ -59,7 +60,7 @@ void ListItemMarkerBox::paint(PaintContext& context, PaintPhase phase) if (phase != PaintPhase::Foreground) return; - auto enclosing = enclosing_int_rect(absolute_rect()); + auto enclosing = enclosing_int_rect(m_paint_box->absolute_rect()); if (auto const* list_style_image = list_style_image_bitmap()) { context.painter().blit(enclosing.location(), *list_style_image, list_style_image->rect()); diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index 768658fa57f..e27ee86c920 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -112,7 +112,7 @@ InitialContainingBlock& Node::root() void Node::set_needs_display() { if (auto* block = containing_block()) { - block->for_each_fragment([&](auto& fragment) { + block->m_paint_box->for_each_fragment([&](auto& fragment) { if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { browsing_context().set_needs_display(enclosing_int_rect(fragment.absolute_rect())); } @@ -124,11 +124,11 @@ void Node::set_needs_display() Gfx::FloatPoint Node::box_type_agnostic_position() const { if (is(*this)) - return verify_cast(*this).absolute_position(); + return verify_cast(*this).m_paint_box->absolute_position(); VERIFY(is_inline()); Gfx::FloatPoint position; if (auto* block = containing_block()) { - block->for_each_fragment([&](auto& fragment) { + block->m_paint_box->for_each_fragment([&](auto& fragment) { if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { position = fragment.absolute_rect().location(); return IterationDecision::Break; diff --git a/Userland/Libraries/LibWeb/Layout/Progress.cpp b/Userland/Libraries/LibWeb/Layout/Progress.cpp index fffa16a5175..873dd519618 100644 --- a/Userland/Libraries/LibWeb/Layout/Progress.cpp +++ b/Userland/Libraries/LibWeb/Layout/Progress.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Web::Layout { @@ -27,7 +28,7 @@ void Progress::paint(PaintContext& context, PaintPhase phase) if (phase == PaintPhase::Foreground) { // FIXME: This does not support floating point value() and max() - Gfx::StylePainter::paint_progressbar(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), 0, dom_node().max(), dom_node().value(), ""); + Gfx::StylePainter::paint_progressbar(context.painter(), enclosing_int_rect(m_paint_box->absolute_rect()), context.palette(), 0, dom_node().max(), dom_node().value(), ""); } } diff --git a/Userland/Libraries/LibWeb/Layout/RadioButton.cpp b/Userland/Libraries/LibWeb/Layout/RadioButton.cpp index 5bf42da367d..d41775cc27c 100644 --- a/Userland/Libraries/LibWeb/Layout/RadioButton.cpp +++ b/Userland/Libraries/LibWeb/Layout/RadioButton.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Web::Layout { @@ -33,7 +34,7 @@ void RadioButton::paint(PaintContext& context, PaintPhase phase) LabelableNode::paint(context, phase); if (phase == PaintPhase::Foreground) { - Gfx::StylePainter::paint_radio_button(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), dom_node().checked(), m_being_pressed); + Gfx::StylePainter::paint_radio_button(context.painter(), enclosing_int_rect(m_paint_box->absolute_rect()), context.palette(), dom_node().checked(), m_being_pressed); } } @@ -57,7 +58,7 @@ void RadioButton::handle_mouseup(Badge, const Gfx::IntPoint& posit // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. NonnullRefPtr protect = *this; - bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_node_or_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); if (!is_inside_node_or_label) is_inside_node_or_label = Label::is_inside_associated_label(*this, position); @@ -74,7 +75,7 @@ void RadioButton::handle_mousemove(Badge, const Gfx::IntPoint& pos if (!m_tracking_mouse || !dom_node().enabled()) return; - bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + bool is_inside_node_or_label = enclosing_int_rect(m_paint_box->absolute_rect()).contains(position); if (!is_inside_node_or_label) is_inside_node_or_label = Label::is_inside_associated_label(*this, position); diff --git a/Userland/Libraries/LibWeb/Layout/SVGGeometryBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGGeometryBox.cpp index 6f20b7867f8..f5e2f285933 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGGeometryBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGGeometryBox.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -39,7 +40,7 @@ void SVGGeometryBox::paint(PaintContext& context, PaintPhase phase) SVG::SVGSVGElement* svg_element = geometry_element.first_ancestor_of_type(); auto maybe_view_box = svg_element->view_box(); - context.painter().add_clip_rect((Gfx::Rect)absolute_rect()); + context.painter().add_clip_rect(enclosing_int_rect(m_paint_box->absolute_rect())); Gfx::Path path = geometry_element.get_path(); @@ -121,10 +122,10 @@ float SVGGeometryBox::viewbox_scaling() const auto view_box = svg_box->view_box().value(); bool has_specified_width = svg_box->has_attribute(HTML::AttributeNames::width); - auto specified_width = content_width(); + auto specified_width = m_paint_box->content_width(); bool has_specified_height = svg_box->has_attribute(HTML::AttributeNames::height); - auto specified_height = content_height(); + auto specified_height = m_paint_box->content_height(); auto scale_width = has_specified_width ? specified_width / view_box.width : 1; auto scale_height = has_specified_height ? specified_height / view_box.height : 1; diff --git a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp index 5c941bfed14..6a6b3f8561b 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp @@ -5,6 +5,7 @@ */ #include +#include namespace Web::Layout { @@ -19,7 +20,7 @@ void SVGSVGBox::before_children_paint(PaintContext& context, PaintPhase phase) return; if (!context.has_svg_context()) - context.set_svg_context(SVGContext(absolute_rect())); + context.set_svg_context(SVGContext(m_paint_box->absolute_rect())); SVGGraphicsBox::before_children_paint(context, phase); } diff --git a/Userland/Libraries/LibWeb/Painting/Box.cpp b/Userland/Libraries/LibWeb/Painting/Box.cpp new file mode 100644 index 00000000000..888e2a8ce2a --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/Box.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::Painting { + +void Box::set_offset(const Gfx::FloatPoint& offset) +{ + if (m_offset == offset) + return; + m_offset = offset; + // FIXME: This const_cast is gross. + const_cast(m_layout_box).did_set_rect(); +} + +void Box::set_content_size(Gfx::FloatSize const& size) +{ + if (m_content_size == size) + return; + m_content_size = size; + // FIXME: This const_cast is gross. + const_cast(m_layout_box).did_set_rect(); +} + +Gfx::FloatPoint Box::effective_offset() const +{ + if (m_containing_line_box_fragment.has_value()) { + auto const& fragment = m_layout_box.containing_block()->m_paint_box->line_boxes()[m_containing_line_box_fragment->line_box_index].fragments()[m_containing_line_box_fragment->fragment_index]; + return fragment.offset(); + } + return m_offset; +} + +Gfx::FloatRect Box::absolute_rect() const +{ + Gfx::FloatRect rect { effective_offset(), content_size() }; + for (auto* block = m_layout_box.containing_block(); block; block = block->containing_block()) + rect.translate_by(block->m_paint_box->effective_offset()); + return rect; +} + +void Box::set_containing_line_box_fragment(Optional fragment_coordinate) +{ + m_containing_line_box_fragment = fragment_coordinate; +} + +} diff --git a/Userland/Libraries/LibWeb/Painting/Box.h b/Userland/Libraries/LibWeb/Painting/Box.h new file mode 100644 index 00000000000..f5e35be8d10 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/Box.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::Painting { + +class Box { +public: + static NonnullOwnPtr create(Layout::Box const& layout_box) + { + return adopt_own(*new Box(layout_box)); + } + + explicit Box(Layout::Box const& layout_box) + : m_layout_box(layout_box) + { + } + + Layout::Box const& m_layout_box; + + auto& box_model() { return m_layout_box.box_model(); } + auto const& box_model() const { return m_layout_box.box_model(); } + + struct OverflowData { + Gfx::FloatRect scrollable_overflow_rect; + Gfx::FloatPoint scroll_offset; + }; + Optional m_overflow_data; + + Gfx::FloatPoint m_offset; + Gfx::FloatSize m_content_size; + + Vector const& line_boxes() const { return m_line_boxes; } + void set_line_boxes(Vector&& line_boxes) { m_line_boxes = move(line_boxes); } + + Vector m_line_boxes; + + // Some boxes hang off of line box fragments. (inline-block, inline-table, replaced, etc) + Optional m_containing_line_box_fragment; + + Gfx::FloatRect absolute_rect() const; + Gfx::FloatPoint effective_offset() const; + + void set_offset(Gfx::FloatPoint const&); + void set_offset(float x, float y) { set_offset({ x, y }); } + + Gfx::FloatSize const& content_size() const { return m_content_size; } + void set_content_size(Gfx::FloatSize const&); + void set_content_size(float width, float height) { set_content_size({ width, height }); } + + void set_content_width(float width) { set_content_size(width, content_height()); } + void set_content_height(float height) { set_content_size(content_width(), height); } + float content_width() const { return m_content_size.width(); } + float content_height() const { return m_content_size.height(); } + + Gfx::FloatRect absolute_padding_box_rect() const + { + auto absolute_rect = this->absolute_rect(); + Gfx::FloatRect rect; + rect.set_x(absolute_rect.x() - box_model().padding.left); + rect.set_width(content_width() + box_model().padding.left + box_model().padding.right); + rect.set_y(absolute_rect.y() - box_model().padding.top); + rect.set_height(content_height() + box_model().padding.top + box_model().padding.bottom); + return rect; + } + + Gfx::FloatRect absolute_border_box_rect() const + { + auto padded_rect = this->absolute_padding_box_rect(); + Gfx::FloatRect rect; + rect.set_x(padded_rect.x() - box_model().border.left); + rect.set_width(padded_rect.width() + box_model().border.left + box_model().border.right); + rect.set_y(padded_rect.y() - box_model().border.top); + rect.set_height(padded_rect.height() + box_model().border.top + box_model().border.bottom); + return rect; + } + + float border_box_width() const + { + auto border_box = box_model().border_box(); + return content_width() + border_box.left + border_box.right; + } + + float border_box_height() const + { + auto border_box = box_model().border_box(); + return content_height() + border_box.top + border_box.bottom; + } + + float absolute_x() const { return absolute_rect().x(); } + float absolute_y() const { return absolute_rect().y(); } + Gfx::FloatPoint absolute_position() const { return absolute_rect().location(); } + + bool has_overflow() const { return m_overflow_data.has_value(); } + + Optional scrollable_overflow_rect() const + { + if (!m_overflow_data.has_value()) + return {}; + return m_overflow_data->scrollable_overflow_rect; + } + + void set_overflow_data(Optional data) { m_overflow_data = move(data); } + void set_containing_line_box_fragment(Optional); + + template + void for_each_fragment(Callback callback) const + { + for (auto& line_box : line_boxes()) { + for (auto& fragment : line_box.fragments()) { + if (callback(fragment) == IterationDecision::Break) + return; + } + } + } +}; + +} diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index d4a824614de..20a6ca82be8 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Web::Layout { @@ -140,7 +141,7 @@ void StackingContext::paint(PaintContext& context) Gfx::Painter painter(bitmap); PaintContext paint_context(painter, context.palette(), context.scroll_offset()); paint_internal(paint_context); - context.painter().blit(Gfx::IntPoint(m_box.absolute_position()), bitmap, Gfx::IntRect(m_box.absolute_rect()), opacity); + context.painter().blit(Gfx::IntPoint(m_box.m_paint_box->absolute_position()), bitmap, Gfx::IntRect(m_box.m_paint_box->absolute_rect()), opacity); } else { paint_internal(context); } @@ -216,7 +217,7 @@ HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position, HitTestTy } // 1. the background and borders of the element forming the stacking context. - if (m_box.absolute_border_box_rect().contains(position.to_type())) { + if (m_box.m_paint_box->absolute_border_box_rect().contains(position.to_type())) { return HitTestResult { .layout_node = m_box, }; @@ -230,7 +231,7 @@ void StackingContext::dump(int indent) const StringBuilder builder; for (int i = 0; i < indent; ++i) builder.append(' '); - builder.appendff("SC for {} {} [children: {}] (z-index: ", m_box.debug_description(), m_box.absolute_rect(), m_children.size()); + builder.appendff("SC for {} {} [children: {}] (z-index: ", m_box.debug_description(), m_box.m_paint_box->absolute_rect(), m_children.size()); if (m_box.computed_values().z_index().has_value()) builder.appendff("{}", m_box.computed_values().z_index().value()); else diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index 7b9c9cb96f9..ad666415004 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, Andreas Kling + * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2021, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -322,8 +323,13 @@ Messages::WebContentServer::InspectDomNodeResponse ConnectionFromClient::inspect MUST(serializer.add("border_right", box_model.border.right)); MUST(serializer.add("border_bottom", box_model.border.bottom)); MUST(serializer.add("border_left", box_model.border.left)); - MUST(serializer.add("content_width", box->content_width())); - MUST(serializer.add("content_height", box->content_height())); + if (auto* paint_box = box->paint_box()) { + MUST(serializer.add("content_width", paint_box->content_width())); + MUST(serializer.add("content_height", paint_box->content_height())); + } else { + MUST(serializer.add("content_width", 0)); + MUST(serializer.add("content_height", 0)); + } MUST(serializer.finish()); return builder.to_string(); diff --git a/Userland/Services/WebContent/PageHost.cpp b/Userland/Services/WebContent/PageHost.cpp index 39b2e1cba5e..a80294ed4a6 100644 --- a/Userland/Services/WebContent/PageHost.cpp +++ b/Userland/Services/WebContent/PageHost.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace WebContent { @@ -121,10 +122,10 @@ void PageHost::page_did_layout() auto* layout_root = this->layout_root(); VERIFY(layout_root); Gfx::IntSize content_size; - if (layout_root->has_overflow()) - content_size = enclosing_int_rect(layout_root->scrollable_overflow_rect().value()).size(); + if (layout_root->m_paint_box->has_overflow()) + content_size = enclosing_int_rect(layout_root->m_paint_box->scrollable_overflow_rect().value()).size(); else - content_size = enclosing_int_rect(layout_root->absolute_rect()).size(); + content_size = enclosing_int_rect(layout_root->m_paint_box->absolute_rect()).size(); m_client.async_did_layout(content_size); }