diff --git a/Userland/Libraries/LibWeb/Layout/Box.cpp b/Userland/Libraries/LibWeb/Layout/Box.cpp index 523bad3dae..d3cd09e1fc 100644 --- a/Userland/Libraries/LibWeb/Layout/Box.cpp +++ b/Userland/Libraries/LibWeb/Layout/Box.cpp @@ -42,8 +42,6 @@ void Box::paint(PaintContext& context, PaintPhase phase) Painting::paint_border(context, Painting::BorderEdge::Bottom, bordered_rect, computed_values()); } - Layout::NodeWithStyleAndBoxModelMetrics::paint(context, phase); - if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) { auto content_rect = absolute_rect(); @@ -179,23 +177,6 @@ StackingContext* Box::enclosing_stacking_context() VERIFY_NOT_REACHED(); } -bool Box::establishes_stacking_context() const -{ - if (!has_style()) - return false; - if (dom_node() == document().root()) - return true; - auto position = computed_values().position(); - auto z_index = computed_values().z_index(); - if (position == CSS::Position::Absolute || position == CSS::Position::Relative) { - if (z_index.has_value()) - return true; - } - if (position == CSS::Position::Fixed || position == CSS::Position::Sticky) - return true; - return false; -} - LineBox& Box::ensure_last_line_box() { if (m_line_boxes.is_empty()) diff --git a/Userland/Libraries/LibWeb/Layout/Box.h b/Userland/Libraries/LibWeb/Layout/Box.h index a86f34d57f..08a6620241 100644 --- a/Userland/Libraries/LibWeb/Layout/Box.h +++ b/Userland/Libraries/LibWeb/Layout/Box.h @@ -104,7 +104,6 @@ public: void set_containing_line_box_fragment(LineBoxFragment&); - bool establishes_stacking_context() const; 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); } diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp index ea86059d97..0b71a20cfa 100644 --- a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp @@ -56,18 +56,7 @@ void InitialContainingBlockBox::paint_document_background(PaintContext& context) void InitialContainingBlockBox::paint_all_phases(PaintContext& context) { paint_document_background(context); - - paint(context, PaintPhase::Background); - paint(context, PaintPhase::Border); - paint(context, PaintPhase::Foreground); - if (context.has_focus()) - paint(context, PaintPhase::FocusOutline); - paint(context, PaintPhase::Overlay); -} - -void InitialContainingBlockBox::paint(PaintContext& context, PaintPhase phase) -{ - stacking_context()->paint(context, phase); + stacking_context()->paint(context); } HitTestResult InitialContainingBlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h index 7334f9bcde..1466b4bbc0 100644 --- a/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h +++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h @@ -19,7 +19,6 @@ public: const DOM::Document& dom_node() const { return static_cast(*Node::dom_node()); } void paint_all_phases(PaintContext&); - virtual void paint(PaintContext&, PaintPhase) override; void paint_document_background(PaintContext&); diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index c51945b225..1c5e2a0dab 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -67,6 +67,18 @@ const BlockBox* Node::containing_block() const return nearest_block_ancestor(); } +bool Node::establishes_stacking_context() const +{ + if (!has_style()) + return false; + if (dom_node() == document().root()) + return true; + auto position = computed_values().position(); + if (position == CSS::Position::Absolute || position == CSS::Position::Relative || position == CSS::Position::Fixed || position == CSS::Position::Sticky) + return true; + return false; +} + void Node::paint(PaintContext& context, PaintPhase phase) { if (!is_visible()) diff --git a/Userland/Libraries/LibWeb/Layout/Node.h b/Userland/Libraries/LibWeb/Layout/Node.h index a68e2a6a55..770339732c 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.h +++ b/Userland/Libraries/LibWeb/Layout/Node.h @@ -112,6 +112,8 @@ public: const BlockBox* containing_block() const; BlockBox* containing_block() { return const_cast(const_cast(this)->containing_block()); } + bool establishes_stacking_context() const; + bool can_contain_boxes_with_position_absolute() const; const Gfx::Font& font() const; diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index 1d3565065d..bbd9e8ae76 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -22,24 +22,80 @@ StackingContext::StackingContext(Box& box, StackingContext* parent) m_parent->m_children.append(this); // FIXME: Don't sort on every append.. + // FIXME: Apparently this also breaks tree order inside layers quick_sort(m_parent->m_children, [](auto& a, auto& b) { return a->m_box.computed_values().z_index().value_or(0) < b->m_box.computed_values().z_index().value_or(0); }); } } -void StackingContext::paint(PaintContext& context, PaintPhase phase) +void StackingContext::paint_descendants(PaintContext& context, Node& box, StackingContextPaintPhase phase) { - if (!is(m_box)) { - m_box.paint(context, phase); - } else { - // NOTE: InitialContainingBlockBox::paint() merely calls StackingContext::paint() - // so we call its base class instead. - downcast(m_box).BlockBox::paint(context, phase); - } + box.for_each_child([&](auto& child) { + switch (phase) { + case StackingContextPaintPhase::BackgroundAndBorders: + if (!child.is_floating() && !child.is_positioned()) { + child.paint(context, PaintPhase::Background); + child.paint(context, PaintPhase::Border); + paint_descendants(context, child, phase); + } + break; + case StackingContextPaintPhase::Floats: + if (!child.is_positioned()) { + if (child.is_floating()) { + child.paint(context, PaintPhase::Background); + child.paint(context, PaintPhase::Border); + paint_descendants(context, child, StackingContextPaintPhase::BackgroundAndBorders); + } + paint_descendants(context, child, phase); + } + break; + case StackingContextPaintPhase::Foreground: + if (!child.is_positioned()) { + child.paint(context, PaintPhase::Foreground); + child.before_children_paint(context, PaintPhase::Foreground); + paint_descendants(context, child, phase); + child.after_children_paint(context, PaintPhase::Foreground); + } + break; + case StackingContextPaintPhase::FocusAndOverlay: + if (context.has_focus()) { + child.paint(context, PaintPhase::FocusOutline); + } + child.paint(context, PaintPhase::Overlay); + paint_descendants(context, child, phase); + break; + } + }); +} + +void StackingContext::paint(PaintContext& context) +{ + // For a more elaborate description of the algorithm, see CSS 2.1 Appendix E + // Draw the background and borders for the context root (steps 1, 2) + m_box.paint(context, PaintPhase::Background); + m_box.paint(context, PaintPhase::Border); + // Draw positioned descendants with negative z-indices (step 3) for (auto* child : m_children) { - child->paint(context, phase); + if (child->m_box.computed_values().z_index().has_value() && child->m_box.computed_values().z_index().value() < 0) + child->paint(context); } + // Draw the background and borders for block-level children (step 4) + paint_descendants(context, m_box, StackingContextPaintPhase::BackgroundAndBorders); + // Draw the non-positioned floats (step 5) + paint_descendants(context, m_box, StackingContextPaintPhase::Floats); + // Draw inline content, replaced content, etc. (steps 6, 7) + paint_descendants(context, m_box, StackingContextPaintPhase::Foreground); + // Draw other positioned descendants (steps 8, 9) + for (auto* child : m_children) { + if (child->m_box.computed_values().z_index().has_value() && child->m_box.computed_values().z_index().value() < 0) + continue; + child->paint(context); + } + + m_box.paint(context, PaintPhase::FocusOutline); + m_box.paint(context, PaintPhase::Overlay); + paint_descendants(context, m_box, StackingContextPaintPhase::FocusAndOverlay); } HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position, HitTestType type) const diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h index 7dccd290d4..687e887b49 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.h +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h @@ -18,7 +18,15 @@ public: StackingContext* parent() { return m_parent; } const StackingContext* parent() const { return m_parent; } - void paint(PaintContext&, PaintPhase); + enum class StackingContextPaintPhase { + BackgroundAndBorders, + Floats, + Foreground, + FocusAndOverlay, + }; + + void paint_descendants(PaintContext&, Node&, StackingContextPaintPhase); + void paint(PaintContext&); HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const; void dump(int indent = 0) const;