2020-06-15 17:29:35 +02:00
|
|
|
/*
|
2022-02-14 16:39:45 +01:00
|
|
|
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
|
2022-07-19 15:18:20 +01:00
|
|
|
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
2020-06-15 17:29:35 +02:00
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-06-15 17:29:35 +02:00
|
|
|
*/
|
|
|
|
|
2022-03-18 01:29:20 +01:00
|
|
|
#include <AK/Debug.h>
|
2020-06-15 17:29:35 +02:00
|
|
|
#include <AK/QuickSort.h>
|
2021-01-01 16:42:44 +01:00
|
|
|
#include <AK/StringBuilder.h>
|
2022-03-18 01:29:20 +01:00
|
|
|
#include <LibGfx/AffineTransform.h>
|
|
|
|
#include <LibGfx/Matrix4x4.h>
|
|
|
|
#include <LibGfx/Rect.h>
|
2023-06-08 23:57:53 +03:00
|
|
|
#include <LibWeb/CSS/ComputedValues.h>
|
2023-03-24 17:59:33 +00:00
|
|
|
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
|
2020-11-22 15:53:01 +01:00
|
|
|
#include <LibWeb/Layout/Box.h>
|
2022-02-14 16:39:45 +01:00
|
|
|
#include <LibWeb/Layout/ReplacedBox.h>
|
2023-02-25 11:04:29 +01:00
|
|
|
#include <LibWeb/Layout/Viewport.h>
|
2022-03-10 23:13:37 +01:00
|
|
|
#include <LibWeb/Painting/PaintableBox.h>
|
2023-09-10 14:10:55 +01:00
|
|
|
#include <LibWeb/Painting/SVGPaintable.h>
|
2020-06-18 22:01:05 +02:00
|
|
|
#include <LibWeb/Painting/StackingContext.h>
|
2023-07-01 03:06:21 +00:00
|
|
|
#include <LibWeb/Painting/TableBordersPainting.h>
|
2023-09-10 14:10:55 +01:00
|
|
|
#include <LibWeb/SVG/SVGMaskElement.h>
|
2020-06-15 17:29:35 +02:00
|
|
|
|
2022-03-10 02:13:28 +01:00
|
|
|
namespace Web::Painting {
|
2020-06-15 17:29:35 +02:00
|
|
|
|
2023-08-19 15:20:45 +02:00
|
|
|
static void paint_node(Paintable const& paintable, PaintContext& context, PaintPhase phase)
|
2022-03-10 14:02:25 +01:00
|
|
|
{
|
2023-08-31 17:08:45 -05:00
|
|
|
paintable.before_paint(context, phase);
|
2023-08-19 15:20:45 +02:00
|
|
|
paintable.paint(context, phase);
|
2023-08-31 17:08:45 -05:00
|
|
|
paintable.after_paint(context, phase);
|
2022-03-10 14:02:25 +01:00
|
|
|
}
|
|
|
|
|
2024-01-03 02:40:31 +01:00
|
|
|
StackingContext::StackingContext(Paintable& paintable, StackingContext* parent, size_t index_in_tree_order)
|
|
|
|
: m_paintable(paintable)
|
2020-06-15 17:29:35 +02:00
|
|
|
, m_parent(parent)
|
2023-06-02 12:01:14 +02:00
|
|
|
, m_index_in_tree_order(index_in_tree_order)
|
2020-06-15 17:29:35 +02:00
|
|
|
{
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY(m_parent != this);
|
2022-03-13 16:19:54 +01:00
|
|
|
if (m_parent)
|
2020-06-15 17:29:35 +02:00
|
|
|
m_parent->m_children.append(this);
|
2022-03-13 16:19:54 +01:00
|
|
|
}
|
2020-06-15 17:29:35 +02:00
|
|
|
|
2022-03-13 16:19:54 +01:00
|
|
|
void StackingContext::sort()
|
|
|
|
{
|
|
|
|
quick_sort(m_children, [](auto& a, auto& b) {
|
2024-01-03 02:40:31 +01:00
|
|
|
auto a_z_index = a->paintable().computed_values().z_index().value_or(0);
|
|
|
|
auto b_z_index = b->paintable().computed_values().z_index().value_or(0);
|
2022-03-13 16:19:54 +01:00
|
|
|
if (a_z_index == b_z_index)
|
2023-06-02 12:01:14 +02:00
|
|
|
return a->m_index_in_tree_order < b->m_index_in_tree_order;
|
2022-03-13 16:19:54 +01:00
|
|
|
return a_z_index < b_z_index;
|
|
|
|
});
|
|
|
|
|
|
|
|
for (auto* child : m_children)
|
|
|
|
child->sort();
|
2020-06-15 17:29:35 +02:00
|
|
|
}
|
|
|
|
|
2022-07-19 11:02:56 +01:00
|
|
|
static PaintPhase to_paint_phase(StackingContext::StackingContextPaintPhase phase)
|
2020-06-15 17:29:35 +02:00
|
|
|
{
|
2022-09-09 14:53:53 -07:00
|
|
|
// There are not a fully correct mapping since some stacking context phases are combined.
|
2022-07-19 11:02:56 +01:00
|
|
|
switch (phase) {
|
|
|
|
case StackingContext::StackingContextPaintPhase::Floats:
|
|
|
|
case StackingContext::StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced:
|
|
|
|
case StackingContext::StackingContextPaintPhase::BackgroundAndBorders:
|
|
|
|
return PaintPhase::Background;
|
|
|
|
case StackingContext::StackingContextPaintPhase::Foreground:
|
|
|
|
return PaintPhase::Foreground;
|
|
|
|
case StackingContext::StackingContextPaintPhase::FocusAndOverlay:
|
|
|
|
return PaintPhase::Overlay;
|
|
|
|
default:
|
|
|
|
VERIFY_NOT_REACHED();
|
2022-03-10 14:02:25 +01:00
|
|
|
}
|
2022-07-19 11:02:56 +01:00
|
|
|
}
|
|
|
|
|
2023-09-10 13:41:00 +01:00
|
|
|
void StackingContext::paint_node_as_stacking_context(Paintable const& paintable, PaintContext& context)
|
2023-08-20 19:42:54 +02:00
|
|
|
{
|
|
|
|
paint_node(paintable, context, PaintPhase::Background);
|
|
|
|
paint_node(paintable, context, PaintPhase::Border);
|
|
|
|
paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBorders);
|
|
|
|
paint_descendants(context, paintable, StackingContextPaintPhase::Floats);
|
|
|
|
paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
|
|
|
|
paint_node(paintable, context, PaintPhase::Foreground);
|
|
|
|
paint_descendants(context, paintable, StackingContextPaintPhase::Foreground);
|
|
|
|
paint_node(paintable, context, PaintPhase::Outline);
|
|
|
|
paint_node(paintable, context, PaintPhase::Overlay);
|
|
|
|
paint_descendants(context, paintable, StackingContextPaintPhase::FocusAndOverlay);
|
|
|
|
}
|
|
|
|
|
2023-09-10 13:41:00 +01:00
|
|
|
void StackingContext::paint_descendants(PaintContext& context, Paintable const& paintable, StackingContextPaintPhase phase)
|
2022-07-19 11:02:56 +01:00
|
|
|
{
|
2023-08-19 15:20:45 +02:00
|
|
|
paintable.before_children_paint(context, to_paint_phase(phase));
|
2021-08-01 16:24:59 +02:00
|
|
|
|
2023-09-10 13:41:00 +01:00
|
|
|
paintable.for_each_child([&context, phase](auto& child) {
|
2024-01-03 02:40:31 +01:00
|
|
|
auto* stacking_context = child.stacking_context();
|
2024-01-02 20:05:35 +01:00
|
|
|
auto const& z_index = child.computed_values().z_index();
|
2022-10-23 23:31:08 +02:00
|
|
|
|
2023-08-20 19:42:54 +02:00
|
|
|
// NOTE: Grid specification https://www.w3.org/TR/css-grid-2/#z-order says that grid items should be treated
|
|
|
|
// the same way as CSS2 defines for inline-blocks:
|
|
|
|
// "For each one of these, treat the element as if it created a new stacking context, but any positioned
|
|
|
|
// descendants and descendants which actually create a new stacking context should be considered part of
|
|
|
|
// the parent stacking context, not this new one."
|
2024-01-02 20:05:35 +01:00
|
|
|
auto should_be_treated_as_stacking_context = child.layout_node().is_grid_item() && !z_index.has_value();
|
2023-08-20 19:42:54 +02:00
|
|
|
if (should_be_treated_as_stacking_context) {
|
|
|
|
// FIXME: This may not be fully correct with respect to the paint phases.
|
|
|
|
if (phase == StackingContextPaintPhase::Foreground)
|
|
|
|
paint_node_as_stacking_context(child, context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-01-02 20:05:35 +01:00
|
|
|
if (stacking_context && z_index.has_value())
|
|
|
|
return;
|
|
|
|
if (child.is_positioned() && !z_index.has_value())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (stacking_context) {
|
|
|
|
// FIXME: This may not be fully correct with respect to the paint phases.
|
|
|
|
if (phase == StackingContextPaintPhase::Foreground) {
|
|
|
|
paint_child(context, *stacking_context);
|
|
|
|
}
|
|
|
|
// Note: Don't further recurse into descendants as paint_child() will do that.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-10 02:13:28 +01:00
|
|
|
bool child_is_inline_or_replaced = child.is_inline() || is<Layout::ReplacedBox>(child);
|
2021-05-07 19:03:25 +03:00
|
|
|
switch (phase) {
|
|
|
|
case StackingContextPaintPhase::BackgroundAndBorders:
|
2022-10-23 18:15:14 +02:00
|
|
|
if (!child_is_inline_or_replaced && !child.is_floating()) {
|
2022-03-10 14:02:25 +01:00
|
|
|
paint_node(child, context, PaintPhase::Background);
|
2023-08-03 03:48:37 +00:00
|
|
|
bool is_table_with_collapsed_borders = child.display().is_table_inside() && child.computed_values().border_collapse() == CSS::BorderCollapse::Collapse;
|
|
|
|
if (!child.display().is_table_cell() && !is_table_with_collapsed_borders)
|
2023-06-27 02:13:29 +00:00
|
|
|
paint_node(child, context, PaintPhase::Border);
|
2021-05-07 19:03:25 +03:00
|
|
|
paint_descendants(context, child, phase);
|
2023-08-03 03:48:37 +00:00
|
|
|
if (child.display().is_table_inside() || child.computed_values().border_collapse() == CSS::BorderCollapse::Collapse) {
|
2023-08-19 15:20:45 +02:00
|
|
|
paint_table_borders(context, verify_cast<PaintableBox>(child));
|
2023-08-03 03:48:37 +00:00
|
|
|
}
|
2021-05-07 19:03:25 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case StackingContextPaintPhase::Floats:
|
2022-10-23 18:15:14 +02:00
|
|
|
if (child.is_floating()) {
|
|
|
|
paint_node(child, context, PaintPhase::Background);
|
|
|
|
paint_node(child, context, PaintPhase::Border);
|
|
|
|
paint_descendants(context, child, StackingContextPaintPhase::BackgroundAndBorders);
|
2021-05-07 19:03:25 +03:00
|
|
|
}
|
2022-10-23 18:15:14 +02:00
|
|
|
paint_descendants(context, child, phase);
|
2021-05-07 19:03:25 +03:00
|
|
|
break;
|
2022-02-14 16:39:45 +01:00
|
|
|
case StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced:
|
2022-10-23 18:15:14 +02:00
|
|
|
if (child_is_inline_or_replaced) {
|
|
|
|
paint_node(child, context, PaintPhase::Background);
|
|
|
|
paint_node(child, context, PaintPhase::Border);
|
2023-08-14 09:24:48 +00:00
|
|
|
if (child.display().is_table_inside() && child.computed_values().border_collapse() == CSS::BorderCollapse::Separate)
|
2023-08-19 15:20:45 +02:00
|
|
|
paint_table_borders(context, verify_cast<PaintableBox>(child));
|
2022-10-23 18:15:14 +02:00
|
|
|
paint_descendants(context, child, StackingContextPaintPhase::BackgroundAndBorders);
|
2022-02-14 16:39:45 +01:00
|
|
|
}
|
2022-10-23 18:15:14 +02:00
|
|
|
paint_descendants(context, child, phase);
|
2022-02-14 16:39:45 +01:00
|
|
|
break;
|
2021-05-07 19:03:25 +03:00
|
|
|
case StackingContextPaintPhase::Foreground:
|
2022-10-23 18:15:14 +02:00
|
|
|
paint_node(child, context, PaintPhase::Foreground);
|
|
|
|
paint_descendants(context, child, phase);
|
2021-05-07 19:03:25 +03:00
|
|
|
break;
|
|
|
|
case StackingContextPaintPhase::FocusAndOverlay:
|
2023-08-02 17:24:14 +01:00
|
|
|
paint_node(child, context, PaintPhase::Outline);
|
2022-03-10 14:02:25 +01:00
|
|
|
paint_node(child, context, PaintPhase::Overlay);
|
2021-05-07 19:03:25 +03:00
|
|
|
paint_descendants(context, child, phase);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
2021-08-01 16:24:59 +02:00
|
|
|
|
2023-08-19 15:20:45 +02:00
|
|
|
paintable.after_children_paint(context, to_paint_phase(phase));
|
2021-05-07 19:03:25 +03:00
|
|
|
}
|
|
|
|
|
2023-09-10 13:41:00 +01:00
|
|
|
void StackingContext::paint_child(PaintContext& context, StackingContext const& child)
|
2023-08-13 18:56:56 +01:00
|
|
|
{
|
2024-01-03 02:40:31 +01:00
|
|
|
auto parent_paintable = child.paintable().parent();
|
2023-08-13 18:56:56 +01:00
|
|
|
if (parent_paintable)
|
|
|
|
parent_paintable->before_children_paint(context, PaintPhase::Foreground);
|
|
|
|
|
|
|
|
child.paint(context);
|
|
|
|
|
|
|
|
if (parent_paintable)
|
|
|
|
parent_paintable->after_children_paint(context, PaintPhase::Foreground);
|
|
|
|
}
|
|
|
|
|
2022-03-10 15:44:43 +01:00
|
|
|
void StackingContext::paint_internal(PaintContext& context) const
|
2021-05-07 19:03:25 +03:00
|
|
|
{
|
|
|
|
// 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)
|
2024-01-03 02:40:31 +01:00
|
|
|
paint_node(paintable(), context, PaintPhase::Background);
|
|
|
|
paint_node(paintable(), context, PaintPhase::Border);
|
2022-07-19 11:06:21 +01:00
|
|
|
|
2023-08-13 18:56:56 +01:00
|
|
|
// Stacking contexts formed by positioned descendants with negative z-indices (excluding 0) in z-index order
|
|
|
|
// (most negative first) then tree order. (step 3)
|
2024-01-02 20:05:35 +01:00
|
|
|
// NOTE: This doesn't check if a descendant is positioned as modern CSS allows for alternative methods to establish stacking contexts.
|
2021-05-07 19:03:25 +03:00
|
|
|
for (auto* child : m_children) {
|
2024-01-03 02:40:31 +01:00
|
|
|
if (child->paintable().computed_values().z_index().has_value() && child->paintable().computed_values().z_index().value() < 0)
|
2023-08-13 18:56:56 +01:00
|
|
|
paint_child(context, *child);
|
2020-06-15 17:29:35 +02:00
|
|
|
}
|
2022-07-19 11:06:21 +01:00
|
|
|
|
2021-05-07 19:03:25 +03:00
|
|
|
// Draw the background and borders for block-level children (step 4)
|
2024-01-03 02:40:31 +01:00
|
|
|
paint_descendants(context, paintable(), StackingContextPaintPhase::BackgroundAndBorders);
|
2021-05-07 19:03:25 +03:00
|
|
|
// Draw the non-positioned floats (step 5)
|
2024-01-03 02:40:31 +01:00
|
|
|
paint_descendants(context, paintable(), StackingContextPaintPhase::Floats);
|
2021-05-07 19:03:25 +03:00
|
|
|
// Draw inline content, replaced content, etc. (steps 6, 7)
|
2024-01-03 02:40:31 +01:00
|
|
|
paint_descendants(context, paintable(), StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
|
|
|
|
paint_node(paintable(), context, PaintPhase::Foreground);
|
|
|
|
paint_descendants(context, paintable(), StackingContextPaintPhase::Foreground);
|
2022-10-23 18:18:58 +02:00
|
|
|
|
|
|
|
// Draw positioned descendants with z-index `0` or `auto` in tree order. (step 8)
|
|
|
|
// FIXME: There's more to this step that we have yet to understand and implement.
|
2024-01-03 02:40:31 +01:00
|
|
|
paintable().for_each_in_subtree([&context](Paintable const& paintable) {
|
2023-07-14 10:03:14 +02:00
|
|
|
auto const& z_index = paintable.computed_values().z_index();
|
|
|
|
|
2023-08-19 15:20:45 +02:00
|
|
|
if (!paintable.is_positioned() || (z_index.has_value() && z_index.value() != 0)) {
|
2024-01-03 02:40:31 +01:00
|
|
|
return paintable.stacking_context()
|
2023-08-13 18:56:56 +01:00
|
|
|
? TraversalDecision::SkipChildrenAndContinue
|
|
|
|
: TraversalDecision::Continue;
|
2022-10-23 18:18:58 +02:00
|
|
|
}
|
2023-08-13 18:56:56 +01:00
|
|
|
|
|
|
|
// At this point, `paintable_box` is a positioned descendant with z-index: auto.
|
2022-10-23 18:18:58 +02:00
|
|
|
// FIXME: This is basically duplicating logic found elsewhere in this same function. Find a way to make this more elegant.
|
2023-08-23 21:30:26 +01:00
|
|
|
auto exit_decision = TraversalDecision::Continue;
|
2023-08-19 15:20:45 +02:00
|
|
|
auto* parent_paintable = paintable.parent();
|
2023-01-25 04:50:14 +03:00
|
|
|
if (parent_paintable)
|
|
|
|
parent_paintable->before_children_paint(context, PaintPhase::Foreground);
|
2024-01-03 02:40:31 +01:00
|
|
|
if (auto* child = paintable.stacking_context()) {
|
2023-08-13 18:56:56 +01:00
|
|
|
paint_child(context, *child);
|
2023-08-23 21:30:26 +01:00
|
|
|
exit_decision = TraversalDecision::SkipChildrenAndContinue;
|
2023-08-13 18:56:56 +01:00
|
|
|
} else {
|
2023-08-20 19:42:54 +02:00
|
|
|
paint_node_as_stacking_context(paintable, context);
|
2023-08-13 18:56:56 +01:00
|
|
|
}
|
2023-01-25 04:50:14 +03:00
|
|
|
if (parent_paintable)
|
|
|
|
parent_paintable->after_children_paint(context, PaintPhase::Foreground);
|
2023-12-11 16:57:47 +01:00
|
|
|
|
2023-08-23 21:30:26 +01:00
|
|
|
return exit_decision;
|
2022-10-23 18:18:58 +02:00
|
|
|
});
|
|
|
|
|
2023-08-13 18:56:56 +01:00
|
|
|
// Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order
|
|
|
|
// (smallest first) then tree order. (Step 9)
|
2024-01-02 20:05:35 +01:00
|
|
|
// NOTE: This doesn't check if a descendant is positioned as modern CSS allows for alternative methods to establish stacking contexts.
|
2020-06-15 17:29:35 +02:00
|
|
|
for (auto* child : m_children) {
|
2024-01-03 02:40:31 +01:00
|
|
|
if (child->paintable().computed_values().z_index().has_value() && child->paintable().computed_values().z_index().value() >= 1)
|
2023-08-13 18:56:56 +01:00
|
|
|
paint_child(context, *child);
|
2020-06-15 17:29:35 +02:00
|
|
|
}
|
2021-05-07 19:03:25 +03:00
|
|
|
|
2024-01-03 02:40:31 +01:00
|
|
|
paint_node(paintable(), context, PaintPhase::Outline);
|
2023-12-06 11:49:45 -05:00
|
|
|
|
|
|
|
if (context.should_paint_overlay()) {
|
2024-01-03 02:40:31 +01:00
|
|
|
paint_node(paintable(), context, PaintPhase::Overlay);
|
|
|
|
paint_descendants(context, paintable(), StackingContextPaintPhase::FocusAndOverlay);
|
2023-12-06 11:49:45 -05:00
|
|
|
}
|
2020-06-15 17:29:35 +02:00
|
|
|
}
|
|
|
|
|
2022-03-18 01:29:20 +01:00
|
|
|
// FIXME: This extracts the affine 2D part of the full transformation matrix.
|
|
|
|
// Use the whole matrix when we get better transformation support in LibGfx or use LibGL for drawing the bitmap
|
2022-07-19 15:18:20 +01:00
|
|
|
Gfx::AffineTransform StackingContext::affine_transform_matrix() const
|
2022-03-18 01:29:20 +01:00
|
|
|
{
|
2024-01-16 19:54:05 +01:00
|
|
|
if (paintable().is_paintable_box())
|
|
|
|
return Gfx::extract_2d_affine_transform(paintable_box().transform());
|
|
|
|
return Gfx::AffineTransform {};
|
2022-03-18 01:29:20 +01:00
|
|
|
}
|
|
|
|
|
LibWeb: Don't encode painting limitations in RecordingPainter API
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
2023-11-18 14:23:59 +00:00
|
|
|
static Gfx::FloatMatrix4x4 matrix_with_scaled_translation(Gfx::FloatMatrix4x4 matrix, float scale)
|
2021-07-23 13:19:16 +03:00
|
|
|
{
|
LibWeb: Don't encode painting limitations in RecordingPainter API
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
2023-11-18 14:23:59 +00:00
|
|
|
auto* m = matrix.elements();
|
|
|
|
m[0][3] *= scale;
|
|
|
|
m[1][3] *= scale;
|
|
|
|
m[2][3] *= scale;
|
|
|
|
return matrix;
|
|
|
|
}
|
2021-10-14 23:49:15 +02:00
|
|
|
|
LibWeb: Don't encode painting limitations in RecordingPainter API
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
2023-11-18 14:23:59 +00:00
|
|
|
void StackingContext::paint(PaintContext& context) const
|
|
|
|
{
|
2024-01-03 02:40:31 +01:00
|
|
|
auto opacity = paintable().computed_values().opacity();
|
2021-10-19 15:27:40 +02:00
|
|
|
if (opacity == 0.0f)
|
2021-07-23 13:19:16 +03:00
|
|
|
return;
|
|
|
|
|
2023-11-27 18:49:50 +01:00
|
|
|
RecordingPainterStateSaver saver(context.recording_painter());
|
2023-10-15 04:27:48 +02:00
|
|
|
|
LibWeb: Don't encode painting limitations in RecordingPainter API
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
2023-11-18 14:23:59 +00:00
|
|
|
auto to_device_pixels_scale = float(context.device_pixels_per_css_pixel());
|
2024-01-03 02:40:31 +01:00
|
|
|
Gfx::IntRect source_paintable_rect;
|
|
|
|
if (paintable().is_paintable_box()) {
|
|
|
|
source_paintable_rect = context.enclosing_device_rect(paintable_box().absolute_paint_rect()).to_type<int>();
|
|
|
|
} else if (paintable().is_inline()) {
|
|
|
|
source_paintable_rect = context.enclosing_device_rect(inline_paintable().bounding_rect()).to_type<int>();
|
|
|
|
} else {
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
|
|
|
|
2024-01-16 19:54:05 +01:00
|
|
|
auto transform_matrix = Gfx::FloatMatrix4x4::identity();
|
|
|
|
Gfx::FloatPoint transform_origin;
|
|
|
|
if (paintable().is_paintable_box()) {
|
|
|
|
transform_matrix = paintable_box().transform();
|
|
|
|
transform_origin = paintable_box().transform_origin().to_type<float>();
|
|
|
|
}
|
|
|
|
|
2023-10-15 04:27:48 +02:00
|
|
|
RecordingPainter::PushStackingContextParams push_stacking_context_params {
|
|
|
|
.opacity = opacity,
|
2024-01-03 02:40:31 +01:00
|
|
|
.is_fixed_position = paintable().is_fixed_position(),
|
|
|
|
.source_paintable_rect = source_paintable_rect,
|
|
|
|
.image_rendering = paintable().computed_values().image_rendering(),
|
LibWeb: Don't encode painting limitations in RecordingPainter API
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
2023-11-18 14:23:59 +00:00
|
|
|
.transform = {
|
2024-01-16 19:54:05 +01:00
|
|
|
.origin = transform_origin.scaled(to_device_pixels_scale),
|
|
|
|
.matrix = matrix_with_scaled_translation(transform_matrix, to_device_pixels_scale),
|
LibWeb: Don't encode painting limitations in RecordingPainter API
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
2023-11-18 14:23:59 +00:00
|
|
|
},
|
2023-10-15 04:27:48 +02:00
|
|
|
};
|
2022-09-25 15:24:21 +01:00
|
|
|
|
2024-01-03 02:40:31 +01:00
|
|
|
if (paintable().is_paintable_box()) {
|
|
|
|
if (auto masking_area = paintable_box().get_masking_area(); masking_area.has_value()) {
|
|
|
|
if (masking_area->is_empty())
|
|
|
|
return;
|
|
|
|
auto mask_bitmap = paintable_box().calculate_mask(context, *masking_area);
|
|
|
|
if (mask_bitmap) {
|
|
|
|
auto source_paintable_rect = context.enclosing_device_rect(*masking_area).to_type<int>();
|
|
|
|
push_stacking_context_params.source_paintable_rect = source_paintable_rect;
|
|
|
|
push_stacking_context_params.mask = StackingContextMask {
|
|
|
|
.mask_bitmap = mask_bitmap.release_nonnull(),
|
|
|
|
.mask_kind = *paintable_box().get_mask_type()
|
|
|
|
};
|
|
|
|
}
|
2023-12-16 18:54:50 +01:00
|
|
|
}
|
2023-10-15 04:27:48 +02:00
|
|
|
}
|
|
|
|
|
2024-02-01 12:31:31 +01:00
|
|
|
context.recording_painter().save();
|
|
|
|
if (paintable().is_paintable_box() && paintable_box().scroll_frame_id().has_value())
|
|
|
|
context.recording_painter().set_scroll_frame_id(*paintable_box().scroll_frame_id());
|
2023-11-27 18:49:50 +01:00
|
|
|
context.recording_painter().push_stacking_context(push_stacking_context_params);
|
LibWeb: Don't encode painting limitations in RecordingPainter API
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
2023-11-18 14:23:59 +00:00
|
|
|
paint_internal(context);
|
2023-11-27 18:49:50 +01:00
|
|
|
context.recording_painter().pop_stacking_context();
|
2024-02-01 12:31:31 +01:00
|
|
|
context.recording_painter().restore();
|
2021-07-23 13:19:16 +03:00
|
|
|
}
|
|
|
|
|
2024-01-03 02:40:31 +01:00
|
|
|
template<typename Callback>
|
|
|
|
static TraversalDecision for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(Paintable const& paintable, Callback callback)
|
2022-11-03 19:08:07 +01:00
|
|
|
{
|
2024-01-03 02:40:31 +01:00
|
|
|
if (paintable.stacking_context()) {
|
2023-04-09 11:21:00 +01:00
|
|
|
// Note: Include the stacking context (so we can hit test it), but don't recurse into it.
|
2024-01-03 02:40:31 +01:00
|
|
|
if (auto decision = callback(paintable); decision != TraversalDecision::Continue)
|
2023-04-09 11:21:00 +01:00
|
|
|
return decision;
|
2022-11-03 19:08:07 +01:00
|
|
|
return TraversalDecision::SkipChildrenAndContinue;
|
2023-04-09 11:21:00 +01:00
|
|
|
}
|
2022-11-03 19:08:07 +01:00
|
|
|
for (auto* child = paintable.last_child(); child; child = child->previous_sibling()) {
|
2024-01-03 02:40:31 +01:00
|
|
|
if (for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(*child, callback) == TraversalDecision::Break)
|
2022-11-03 19:08:07 +01:00
|
|
|
return TraversalDecision::Break;
|
|
|
|
}
|
2024-01-03 02:40:31 +01:00
|
|
|
if (auto decision = callback(paintable); decision != TraversalDecision::Continue)
|
|
|
|
return decision;
|
2022-11-03 19:08:07 +01:00
|
|
|
return TraversalDecision::Continue;
|
|
|
|
}
|
|
|
|
|
2024-01-03 02:40:31 +01:00
|
|
|
template<typename Callback>
|
|
|
|
static TraversalDecision for_each_in_subtree_within_same_stacking_context_in_reverse(Paintable const& paintable, Callback callback)
|
2022-11-03 19:08:07 +01:00
|
|
|
{
|
|
|
|
for (auto* child = paintable.last_child(); child; child = child->previous_sibling()) {
|
2024-01-03 02:40:31 +01:00
|
|
|
if (for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(*child, callback) == TraversalDecision::Break)
|
2022-11-03 19:08:07 +01:00
|
|
|
return TraversalDecision::Break;
|
|
|
|
}
|
|
|
|
return TraversalDecision::Continue;
|
|
|
|
}
|
|
|
|
|
2022-11-02 17:35:53 +00:00
|
|
|
Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTestType type) const
|
2020-07-01 19:02:28 +02:00
|
|
|
{
|
2024-01-03 02:40:31 +01:00
|
|
|
if (!paintable().is_visible())
|
2022-07-03 18:34:51 -03:00
|
|
|
return {};
|
|
|
|
|
2024-01-16 19:54:05 +01:00
|
|
|
CSSPixelPoint transform_origin { 0, 0 };
|
|
|
|
if (paintable().is_paintable_box())
|
|
|
|
transform_origin = paintable_box().transform_origin();
|
2022-11-02 17:35:53 +00:00
|
|
|
// NOTE: This CSSPixels -> Float -> CSSPixels conversion is because we can't AffineTransform::map() a CSSPixelPoint.
|
|
|
|
Gfx::FloatPoint offset_position {
|
2023-06-12 21:37:35 +03:00
|
|
|
(position.x() - transform_origin.x()).to_float(),
|
|
|
|
(position.y() - transform_origin.y()).to_float()
|
2022-11-02 17:35:53 +00:00
|
|
|
};
|
|
|
|
auto transformed_position = affine_transform_matrix().inverse().value_or({}).map(offset_position).to_type<CSSPixels>() + transform_origin;
|
2022-03-18 01:32:50 +01:00
|
|
|
|
2024-01-03 02:40:31 +01:00
|
|
|
if (paintable().is_fixed_position()) {
|
|
|
|
auto scroll_offset = paintable().document().navigable()->viewport_scroll_offset();
|
2023-07-15 16:32:13 +02:00
|
|
|
transformed_position.translate_by(-scroll_offset);
|
|
|
|
}
|
|
|
|
|
2022-03-04 15:02:16 +01:00
|
|
|
// NOTE: Hit testing basically happens in reverse painting order.
|
|
|
|
// https://www.w3.org/TR/CSS22/visuren.html#z-index
|
|
|
|
|
|
|
|
// 7. the child stacking contexts with positive stack levels (least positive first).
|
2022-10-07 11:51:56 +02:00
|
|
|
// NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed.
|
2022-03-04 15:02:16 +01:00
|
|
|
for (ssize_t i = m_children.size() - 1; i >= 0; --i) {
|
|
|
|
auto const& child = *m_children[i];
|
2024-01-03 02:40:31 +01:00
|
|
|
if (child.paintable().computed_values().z_index().value_or(0) <= 0)
|
2022-10-07 11:51:56 +02:00
|
|
|
break;
|
2022-03-18 01:32:50 +01:00
|
|
|
auto result = child.hit_test(transformed_position, type);
|
2022-10-20 01:43:31 +03:00
|
|
|
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
2022-03-04 15:02:16 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
|
2023-04-09 11:21:00 +01:00
|
|
|
Optional<HitTestResult> result;
|
2024-01-03 02:40:31 +01:00
|
|
|
for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
|
|
|
|
if (!paintable.is_paintable_box())
|
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
|
|
|
auto const& paintable_box = verify_cast<PaintableBox>(paintable);
|
|
|
|
|
2023-04-20 16:02:16 +01:00
|
|
|
auto const& z_index = paintable_box.computed_values().z_index();
|
2023-08-19 15:20:45 +02:00
|
|
|
if (z_index.value_or(0) == 0 && paintable_box.is_positioned() && !paintable_box.stacking_context()) {
|
2023-04-20 16:02:16 +01:00
|
|
|
auto candidate = paintable_box.hit_test(transformed_position, type);
|
2023-04-09 11:21:00 +01:00
|
|
|
if (candidate.has_value() && candidate->paintable->visible_for_hit_testing()) {
|
|
|
|
result = move(candidate);
|
|
|
|
return TraversalDecision::Break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-20 16:02:16 +01:00
|
|
|
if (paintable_box.stacking_context()) {
|
2023-04-09 11:21:00 +01:00
|
|
|
if (z_index.value_or(0) == 0) {
|
2023-04-20 16:02:16 +01:00
|
|
|
auto candidate = paintable_box.stacking_context()->hit_test(transformed_position, type);
|
2022-11-03 19:08:07 +01:00
|
|
|
if (candidate.has_value() && candidate->paintable->visible_for_hit_testing()) {
|
|
|
|
result = move(candidate);
|
|
|
|
return TraversalDecision::Break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-03 18:34:51 -03:00
|
|
|
return TraversalDecision::Continue;
|
2022-03-04 15:02:16 +01:00
|
|
|
});
|
2023-04-09 11:21:00 +01:00
|
|
|
if (result.has_value())
|
2022-03-04 15:02:16 +01:00
|
|
|
return result;
|
|
|
|
|
|
|
|
// 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
|
2024-01-03 02:40:31 +01:00
|
|
|
if (paintable().layout_node().children_are_inline() && is<Layout::BlockContainer>(paintable().layout_node())) {
|
2023-04-20 16:00:21 +01:00
|
|
|
auto result = paintable_box().hit_test(transformed_position, type);
|
2022-10-20 01:43:31 +03:00
|
|
|
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
2022-03-04 15:02:16 +01:00
|
|
|
return result;
|
2020-07-01 19:02:28 +02:00
|
|
|
}
|
|
|
|
|
2022-03-04 15:02:16 +01:00
|
|
|
// 4. the non-positioned floats.
|
2024-01-03 02:40:31 +01:00
|
|
|
for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
|
|
|
|
if (!paintable.is_paintable_box())
|
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
|
|
|
auto const& paintable_box = verify_cast<PaintableBox>(paintable);
|
2023-08-19 15:20:45 +02:00
|
|
|
if (paintable_box.is_floating()) {
|
2023-04-20 16:02:16 +01:00
|
|
|
if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
|
2022-03-21 12:12:50 +01:00
|
|
|
result = move(candidate);
|
2022-11-03 19:08:07 +01:00
|
|
|
return TraversalDecision::Break;
|
|
|
|
}
|
2022-03-04 15:02:16 +01:00
|
|
|
}
|
2022-07-03 18:34:51 -03:00
|
|
|
return TraversalDecision::Continue;
|
2022-03-04 15:02:16 +01:00
|
|
|
});
|
2022-10-20 01:43:31 +03:00
|
|
|
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
2022-03-21 11:11:05 +01:00
|
|
|
return result;
|
2022-03-04 15:02:16 +01:00
|
|
|
|
|
|
|
// 3. the in-flow, non-inline-level, non-positioned descendants.
|
2024-01-03 02:40:31 +01:00
|
|
|
if (!paintable().layout_node().children_are_inline()) {
|
|
|
|
for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
|
|
|
|
if (!paintable.is_paintable_box())
|
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
|
|
|
auto const& paintable_box = verify_cast<PaintableBox>(paintable);
|
2023-08-19 15:20:45 +02:00
|
|
|
if (!paintable_box.is_absolutely_positioned() && !paintable_box.is_floating()) {
|
2023-04-20 16:02:16 +01:00
|
|
|
if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
|
2022-03-21 12:12:50 +01:00
|
|
|
result = move(candidate);
|
2022-11-03 19:08:07 +01:00
|
|
|
return TraversalDecision::Break;
|
|
|
|
}
|
2022-03-04 15:02:16 +01:00
|
|
|
}
|
2022-07-03 18:34:51 -03:00
|
|
|
return TraversalDecision::Continue;
|
2022-03-04 15:02:16 +01:00
|
|
|
});
|
2022-10-20 01:43:31 +03:00
|
|
|
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
2022-03-04 15:02:16 +01:00
|
|
|
return result;
|
|
|
|
}
|
2021-03-31 10:26:11 -04:00
|
|
|
|
2022-03-04 15:02:16 +01:00
|
|
|
// 2. the child stacking contexts with negative stack levels (most negative first).
|
2022-10-07 11:51:56 +02:00
|
|
|
// NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed.
|
2022-03-04 15:02:16 +01:00
|
|
|
for (ssize_t i = m_children.size() - 1; i >= 0; --i) {
|
|
|
|
auto const& child = *m_children[i];
|
2024-01-03 02:40:31 +01:00
|
|
|
if (child.paintable().computed_values().z_index().value_or(0) >= 0)
|
2022-10-07 11:51:56 +02:00
|
|
|
break;
|
2022-03-18 01:32:50 +01:00
|
|
|
auto result = child.hit_test(transformed_position, type);
|
2022-10-20 01:43:31 +03:00
|
|
|
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
2022-03-04 15:02:16 +01:00
|
|
|
return result;
|
|
|
|
}
|
2021-03-31 10:26:11 -04:00
|
|
|
|
2022-03-04 15:02:16 +01:00
|
|
|
// 1. the background and borders of the element forming the stacking context.
|
2024-01-03 02:40:31 +01:00
|
|
|
if (paintable().is_paintable_box()) {
|
|
|
|
if (paintable_box().absolute_border_box_rect().contains(transformed_position.x(), transformed_position.y())) {
|
|
|
|
return HitTestResult {
|
|
|
|
.paintable = const_cast<PaintableBox&>(paintable_box()),
|
|
|
|
};
|
|
|
|
}
|
2020-07-01 19:02:28 +02:00
|
|
|
}
|
2022-03-04 15:02:16 +01:00
|
|
|
|
|
|
|
return {};
|
2020-07-01 19:02:28 +02:00
|
|
|
}
|
|
|
|
|
2020-06-15 17:29:35 +02:00
|
|
|
void StackingContext::dump(int indent) const
|
|
|
|
{
|
2021-01-01 16:42:44 +01:00
|
|
|
StringBuilder builder;
|
2020-06-15 17:29:35 +02:00
|
|
|
for (int i = 0; i < indent; ++i)
|
2021-01-01 16:42:44 +01:00
|
|
|
builder.append(' ');
|
2024-01-03 02:40:31 +01:00
|
|
|
CSSPixelRect rect;
|
|
|
|
if (paintable().is_paintable_box()) {
|
|
|
|
rect = paintable_box().absolute_rect();
|
|
|
|
} else if (paintable().is_inline_paintable()) {
|
|
|
|
rect = inline_paintable().bounding_rect();
|
|
|
|
} else {
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
|
|
|
builder.appendff("SC for {} {} [children: {}] (z-index: ", paintable().layout_node().debug_description(), rect, m_children.size());
|
|
|
|
|
|
|
|
if (paintable().computed_values().z_index().has_value())
|
|
|
|
builder.appendff("{}", paintable().computed_values().z_index().value());
|
2022-03-04 15:03:48 +01:00
|
|
|
else
|
2022-07-11 17:32:29 +00:00
|
|
|
builder.append("auto"sv);
|
2022-03-04 15:03:48 +01:00
|
|
|
builder.append(')');
|
2022-03-18 01:29:20 +01:00
|
|
|
|
2022-07-19 15:18:20 +01:00
|
|
|
auto affine_transform = affine_transform_matrix();
|
2022-03-18 01:29:20 +01:00
|
|
|
if (!affine_transform.is_identity()) {
|
|
|
|
builder.appendff(", transform: {}", affine_transform);
|
|
|
|
}
|
2021-01-01 16:42:44 +01:00
|
|
|
dbgln("{}", builder.string_view());
|
2020-06-15 17:29:35 +02:00
|
|
|
for (auto& child : m_children)
|
|
|
|
child->dump(indent + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|