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>
2022-07-14 17:45:23 +01:00
# include <AK/ExtraMathConstants.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>
2021-07-23 13:19:16 +03:00
# include <LibGfx/Painter.h>
2022-03-18 01:29:20 +01:00
# 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>
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>
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
2022-03-10 14:02:25 +01:00
static void paint_node ( Layout : : Node const & layout_node , PaintContext & context , PaintPhase phase )
{
2023-08-08 15:02:35 +02:00
if ( auto const * paintable = layout_node . paintable ( ) )
paintable - > paint ( context , phase ) ;
2022-03-10 14:02:25 +01:00
}
2023-06-02 12:01:14 +02:00
StackingContext : : StackingContext ( Layout : : Box & box , StackingContext * parent , size_t index_in_tree_order )
2020-06-15 17:29:35 +02:00
: m_box ( box )
2023-02-26 16:09:02 -07:00
, m_transform ( combine_transformations ( m_box - > computed_values ( ) . transformations ( ) ) )
2022-09-24 20:36:06 +02:00
, m_transform_origin ( compute_transform_origin ( ) )
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 ) {
2023-02-26 16:09:02 -07:00
auto a_z_index = a - > m_box - > computed_values ( ) . z_index ( ) . value_or ( 0 ) ;
auto b_z_index = b - > m_box - > 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
}
2022-10-23 17:59:32 +02:00
void StackingContext : : paint_descendants ( PaintContext & context , Layout : : Node const & box , StackingContextPaintPhase phase ) const
2022-07-19 11:02:56 +01:00
{
2023-01-25 04:50:14 +03:00
if ( auto * paintable = box . paintable ( ) ) {
2022-11-12 00:07:43 +03:00
paintable - > before_children_paint ( context , to_paint_phase ( phase ) ) ;
2023-01-25 04:50:14 +03:00
paintable - > apply_clip_overflow_rect ( context , to_paint_phase ( phase ) ) ;
}
2021-08-01 16:24:59 +02:00
2023-08-13 18:56:56 +01:00
auto child_stacking_context = [ & ] ( auto & child ) - > StackingContext const * {
if ( auto * paintable = child . paintable ( ) ) {
if ( auto * stacking_context = paintable - > stacking_context_rooted_here ( ) )
return stacking_context ;
}
return nullptr ;
} ;
2021-05-07 19:03:25 +03:00
box . for_each_child ( [ & ] ( auto & child ) {
2023-08-13 18:56:56 +01:00
auto * stacking_context = child_stacking_context ( child ) ;
2022-10-23 23:31:08 +02:00
if ( child . is_positioned ( ) ) {
2023-08-13 18:56:56 +01:00
// If `child` is positioned with a z-index of `0` or `auto`, skip over it.
2022-10-23 23:31:08 +02:00
auto const & z_index = child . computed_values ( ) . z_index ( ) ;
if ( ! z_index . has_value ( ) | | z_index . value ( ) = = 0 )
return ;
2023-08-13 18:56:56 +01:00
// Skip positioned children with stacking contexts, these are handled in paint_internal().
if ( stacking_context )
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 recuse into descendants as paint_child() will do that.
return ;
2022-10-23 23:31:08 +02:00
}
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 ) {
paint_table_borders ( context , child ) ;
}
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 )
paint_table_borders ( context , 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-01-25 04:50:14 +03:00
if ( auto * paintable = box . paintable ( ) ) {
paintable - > clear_clip_overflow_rect ( context , to_paint_phase ( phase ) ) ;
2022-11-12 00:07:43 +03:00
paintable - > after_children_paint ( context , to_paint_phase ( phase ) ) ;
2023-01-25 04:50:14 +03:00
}
2021-05-07 19:03:25 +03:00
}
2023-08-13 18:56:56 +01:00
void StackingContext : : paint_child ( PaintContext & context , StackingContext const & child ) const
{
auto parent = child . m_box - > parent ( ) ;
auto * parent_paintable = parent ? parent - > paintable ( ) : nullptr ;
if ( parent_paintable )
parent_paintable - > before_children_paint ( context , PaintPhase : : Foreground ) ;
auto containing_block = child . m_box - > containing_block ( ) ;
auto * containing_block_paintable = containing_block ? containing_block - > paintable ( ) : nullptr ;
if ( containing_block_paintable )
containing_block_paintable - > apply_clip_overflow_rect ( context , PaintPhase : : Foreground ) ;
child . paint ( context ) ;
if ( parent_paintable )
parent_paintable - > after_children_paint ( context , PaintPhase : : Foreground ) ;
if ( containing_block_paintable )
containing_block_paintable - > clear_clip_overflow_rect ( 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)
2022-03-10 14:02:25 +01:00
paint_node ( m_box , context , PaintPhase : : Background ) ;
paint_node ( m_box , 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)
2021-05-07 19:03:25 +03:00
for ( auto * child : m_children ) {
2023-08-13 18:56:56 +01:00
if ( ! child - > paintable_box ( ) . layout_box ( ) . is_positioned ( ) )
continue ;
if ( child - > paintable_box ( ) . computed_values ( ) . z_index ( ) . has_value ( ) & & child - > m_box - > computed_values ( ) . z_index ( ) . value ( ) < 0 )
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)
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)
2022-02-14 16:39:45 +01:00
paint_descendants ( context , m_box , StackingContextPaintPhase : : BackgroundAndBordersForInlineLevelAndReplaced ) ;
2022-03-10 14:02:25 +01:00
paint_node ( m_box , context , PaintPhase : : Foreground ) ;
2021-05-07 19:03:25 +03:00
paint_descendants ( context , m_box , 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.
2023-07-14 10:03:14 +02:00
m_box - > paintable_box ( ) - > for_each_in_subtree ( [ & ] ( Paintable const & paintable ) {
auto const & layout_node = paintable . layout_node ( ) ;
auto const & z_index = paintable . computed_values ( ) . z_index ( ) ;
2023-08-13 18:56:56 +01:00
if ( ! layout_node . is_positioned ( ) | | ( z_index . has_value ( ) & & z_index . value ( ) ! = 0 ) ) {
return paintable . stacking_context_rooted_here ( )
? 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-07-14 10:03:14 +02:00
auto parent = layout_node . parent ( ) ;
2023-01-25 04:50:14 +03:00
auto * parent_paintable = parent ? parent - > paintable ( ) : nullptr ;
if ( parent_paintable )
parent_paintable - > before_children_paint ( context , PaintPhase : : Foreground ) ;
2023-07-14 10:03:14 +02:00
auto containing_block = layout_node . containing_block ( ) ;
2023-01-25 04:50:14 +03:00
auto * containing_block_paintable = containing_block ? containing_block - > paintable ( ) : nullptr ;
if ( containing_block_paintable )
containing_block_paintable - > apply_clip_overflow_rect ( context , PaintPhase : : Foreground ) ;
2023-08-13 18:56:56 +01:00
if ( auto * child = paintable . stacking_context_rooted_here ( ) ) {
paint_child ( context , * child ) ;
return TraversalDecision : : SkipChildrenAndContinue ;
} else {
paint_node ( layout_node , context , PaintPhase : : Background ) ;
paint_node ( layout_node , context , PaintPhase : : Border ) ;
paint_descendants ( context , layout_node , StackingContextPaintPhase : : BackgroundAndBorders ) ;
paint_descendants ( context , layout_node , StackingContextPaintPhase : : Floats ) ;
paint_descendants ( context , layout_node , StackingContextPaintPhase : : BackgroundAndBordersForInlineLevelAndReplaced ) ;
paint_node ( layout_node , context , PaintPhase : : Foreground ) ;
paint_descendants ( context , layout_node , StackingContextPaintPhase : : Foreground ) ;
paint_node ( layout_node , context , PaintPhase : : Outline ) ;
paint_node ( layout_node , context , PaintPhase : : Overlay ) ;
paint_descendants ( context , layout_node , StackingContextPaintPhase : : FocusAndOverlay ) ;
}
2023-01-25 04:50:14 +03:00
if ( parent_paintable )
parent_paintable - > after_children_paint ( context , PaintPhase : : Foreground ) ;
if ( containing_block_paintable )
containing_block_paintable - > clear_clip_overflow_rect ( context , PaintPhase : : Foreground ) ;
2022-10-23 18:18:58 +02:00
return TraversalDecision : : Continue ;
} ) ;
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)
2020-06-15 17:29:35 +02:00
for ( auto * child : m_children ) {
2023-08-13 18:56:56 +01:00
if ( ! child - > paintable_box ( ) . layout_box ( ) . is_positioned ( ) )
continue ;
if ( child - > paintable_box ( ) . computed_values ( ) . z_index ( ) . has_value ( ) & & child - > m_box - > computed_values ( ) . z_index ( ) . value ( ) > = 1 )
paint_child ( context , * child ) ;
2020-06-15 17:29:35 +02:00
}
2021-05-07 19:03:25 +03:00
2023-08-02 13:58:34 +01:00
paint_node ( m_box , context , PaintPhase : : Outline ) ;
2022-03-10 14:02:25 +01:00
paint_node ( m_box , context , PaintPhase : : Overlay ) ;
2021-05-07 19:03:25 +03:00
paint_descendants ( context , m_box , StackingContextPaintPhase : : FocusAndOverlay ) ;
2020-06-15 17:29:35 +02:00
}
2022-03-18 01:29:20 +01:00
Gfx : : FloatMatrix4x4 StackingContext : : get_transformation_matrix ( CSS : : Transformation const & transformation ) const
{
2022-03-21 23:18:38 +01:00
auto count = transformation . values . size ( ) ;
2023-07-15 10:23:43 +02:00
auto value = [ this , transformation ] ( size_t index , CSS : : Length const & reference_length = CSS : : Length : : make_px ( 0 ) ) - > float {
2022-03-21 23:18:38 +01:00
return transformation . values [ index ] . visit (
2023-05-27 21:10:21 +02:00
[ this , reference_length ] ( CSS : : LengthPercentage const & value ) - > double {
2023-07-15 10:23:43 +02:00
return value . resolved ( m_box , reference_length ) . to_px ( m_box ) . to_float ( ) ;
2022-07-14 17:45:23 +01:00
} ,
2023-03-30 15:33:37 +01:00
[ this ] ( CSS : : AngleOrCalculated const & value ) {
2023-05-27 21:10:21 +02:00
return value . resolved ( m_box ) . to_degrees ( ) * M_DEG2RAD ;
2022-03-18 01:29:20 +01:00
} ,
2023-05-27 21:10:21 +02:00
[ ] ( double value ) {
2022-03-21 23:18:38 +01:00
return value ;
2022-03-18 01:29:20 +01:00
} ) ;
2022-03-21 23:18:38 +01:00
} ;
2023-04-20 16:00:21 +01:00
auto reference_box = paintable_box ( ) . absolute_rect ( ) ;
2022-03-21 23:18:38 +01:00
auto width = CSS : : Length : : make_px ( reference_box . width ( ) ) ;
auto height = CSS : : Length : : make_px ( reference_box . height ( ) ) ;
2022-03-18 01:29:20 +01:00
switch ( transformation . function ) {
case CSS : : TransformFunction : : Matrix :
2022-03-21 23:18:38 +01:00
if ( count = = 6 )
2022-07-14 17:45:23 +01:00
return Gfx : : FloatMatrix4x4 ( value ( 0 ) , value ( 2 ) , 0 , value ( 4 ) ,
value ( 1 ) , value ( 3 ) , 0 , value ( 5 ) ,
2022-03-18 01:29:20 +01:00
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
2022-10-01 01:57:54 +01:00
case CSS : : TransformFunction : : Matrix3d :
if ( count = = 16 )
return Gfx : : FloatMatrix4x4 ( value ( 0 ) , value ( 4 ) , value ( 8 ) , value ( 12 ) ,
value ( 1 ) , value ( 5 ) , value ( 9 ) , value ( 13 ) ,
value ( 2 ) , value ( 6 ) , value ( 10 ) , value ( 14 ) ,
value ( 3 ) , value ( 7 ) , value ( 11 ) , value ( 15 ) ) ;
break ;
2022-03-18 01:29:20 +01:00
case CSS : : TransformFunction : : Translate :
2022-03-21 23:18:38 +01:00
if ( count = = 1 )
return Gfx : : FloatMatrix4x4 ( 1 , 0 , 0 , value ( 0 , width ) ,
2022-03-18 01:29:20 +01:00
0 , 1 , 0 , 0 ,
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
2022-03-21 23:18:38 +01:00
if ( count = = 2 )
return Gfx : : FloatMatrix4x4 ( 1 , 0 , 0 , value ( 0 , width ) ,
0 , 1 , 0 , value ( 1 , height ) ,
2022-03-18 01:29:20 +01:00
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
2022-10-28 02:46:27 +03:00
case CSS : : TransformFunction : : Translate3d :
return Gfx : : FloatMatrix4x4 ( 1 , 0 , 0 , value ( 0 , width ) ,
0 , 1 , 0 , value ( 1 , height ) ,
0 , 0 , 1 , value ( 2 ) ,
0 , 0 , 0 , 1 ) ;
break ;
2022-03-18 01:29:20 +01:00
case CSS : : TransformFunction : : TranslateX :
2022-03-21 23:18:38 +01:00
if ( count = = 1 )
return Gfx : : FloatMatrix4x4 ( 1 , 0 , 0 , value ( 0 , width ) ,
2022-03-18 01:29:20 +01:00
0 , 1 , 0 , 0 ,
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
case CSS : : TransformFunction : : TranslateY :
2022-03-21 23:18:38 +01:00
if ( count = = 1 )
2022-03-18 01:29:20 +01:00
return Gfx : : FloatMatrix4x4 ( 1 , 0 , 0 , 0 ,
2022-03-21 23:18:38 +01:00
0 , 1 , 0 , value ( 0 , height ) ,
2022-03-18 01:29:20 +01:00
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
case CSS : : TransformFunction : : Scale :
2022-03-21 23:18:38 +01:00
if ( count = = 1 )
2022-07-14 17:45:23 +01:00
return Gfx : : FloatMatrix4x4 ( value ( 0 ) , 0 , 0 , 0 ,
0 , value ( 0 ) , 0 , 0 ,
2022-03-18 01:29:20 +01:00
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
2022-03-21 23:18:38 +01:00
if ( count = = 2 )
2022-07-14 17:45:23 +01:00
return Gfx : : FloatMatrix4x4 ( value ( 0 ) , 0 , 0 , 0 ,
2022-10-01 01:47:02 +02:00
0 , value ( 1 ) , 0 , 0 ,
2022-03-18 01:29:20 +01:00
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
case CSS : : TransformFunction : : ScaleX :
2022-03-21 23:18:38 +01:00
if ( count = = 1 )
2022-07-14 17:45:23 +01:00
return Gfx : : FloatMatrix4x4 ( value ( 0 ) , 0 , 0 , 0 ,
2022-03-18 01:29:20 +01:00
0 , 1 , 0 , 0 ,
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
case CSS : : TransformFunction : : ScaleY :
2022-03-21 23:18:38 +01:00
if ( count = = 1 )
2022-03-18 01:29:20 +01:00
return Gfx : : FloatMatrix4x4 ( 1 , 0 , 0 , 0 ,
2022-07-14 17:45:23 +01:00
0 , value ( 0 ) , 0 , 0 ,
2022-03-18 01:29:20 +01:00
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
2022-10-01 01:57:13 +01:00
case CSS : : TransformFunction : : RotateX :
if ( count = = 1 )
return Gfx : : rotation_matrix ( { 1.0f , 0.0f , 0.0f } , value ( 0 ) ) ;
break ;
case CSS : : TransformFunction : : RotateY :
if ( count = = 1 )
return Gfx : : rotation_matrix ( { 0.0f , 1.0f , 0.0f } , value ( 0 ) ) ;
break ;
2022-07-21 14:29:47 +01:00
case CSS : : TransformFunction : : Rotate :
2022-10-01 01:57:13 +01:00
case CSS : : TransformFunction : : RotateZ :
2022-07-21 14:29:47 +01:00
if ( count = = 1 )
return Gfx : : rotation_matrix ( { 0.0f , 0.0f , 1.0f } , value ( 0 ) ) ;
break ;
2023-08-09 19:59:54 +01:00
case CSS : : TransformFunction : : Skew :
if ( count = = 1 )
return Gfx : : FloatMatrix4x4 ( 1 , tanf ( value ( 0 ) ) , 0 , 0 ,
0 , 1 , 0 , 0 ,
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
if ( count = = 2 )
return Gfx : : FloatMatrix4x4 ( 1 , tanf ( value ( 0 ) ) , 0 , 0 ,
tanf ( value ( 1 ) ) , 1 , 0 , 0 ,
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
case CSS : : TransformFunction : : SkewX :
if ( count = = 1 )
return Gfx : : FloatMatrix4x4 ( 1 , tanf ( value ( 0 ) ) , 0 , 0 ,
0 , 1 , 0 , 0 ,
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
case CSS : : TransformFunction : : SkewY :
if ( count = = 1 )
return Gfx : : FloatMatrix4x4 ( 1 , 0 , 0 , 0 ,
tanf ( value ( 0 ) ) , 1 , 0 , 0 ,
0 , 0 , 1 , 0 ,
0 , 0 , 0 , 1 ) ;
break ;
2022-03-18 01:29:20 +01:00
default :
2023-05-05 15:02:03 +01:00
dbgln_if ( LIBWEB_CSS_DEBUG , " FIXME: Unhandled transformation function {} " , MUST ( CSS : : TransformationStyleValue : : create ( transformation . function , { } ) ) - > to_string ( ) ) ;
2022-03-18 01:29:20 +01:00
}
return Gfx : : FloatMatrix4x4 : : identity ( ) ;
}
Gfx : : FloatMatrix4x4 StackingContext : : combine_transformations ( Vector < CSS : : Transformation > const & transformations ) const
{
auto matrix = Gfx : : FloatMatrix4x4 : : identity ( ) ;
for ( auto const & transform : transformations )
matrix = matrix * get_transformation_matrix ( transform ) ;
return matrix ;
}
// 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
{
2022-07-19 15:18:20 +01:00
auto * m = m_transform . elements ( ) ;
2022-03-18 01:29:20 +01:00
return Gfx : : AffineTransform ( m [ 0 ] [ 0 ] , m [ 1 ] [ 0 ] , m [ 0 ] [ 1 ] , m [ 1 ] [ 1 ] , m [ 0 ] [ 3 ] , m [ 1 ] [ 3 ] ) ;
}
2022-03-10 15:44:43 +01:00
void StackingContext : : paint ( PaintContext & context ) const
2021-07-23 13:19:16 +03:00
{
2021-10-14 23:49:15 +02:00
Gfx : : PainterStateSaver saver ( context . painter ( ) ) ;
2023-02-26 16:09:02 -07:00
if ( m_box - > is_fixed_position ( ) ) {
2022-10-01 14:04:22 +02:00
context . painter ( ) . translate ( - context . painter ( ) . translation ( ) ) ;
2021-10-14 23:49:15 +02:00
}
2023-02-26 16:09:02 -07:00
auto opacity = m_box - > computed_values ( ) . opacity ( ) ;
2021-10-19 15:27:40 +02:00
if ( opacity = = 0.0f )
2021-07-23 13:19:16 +03:00
return ;
2022-07-19 15:18:20 +01:00
auto affine_transform = affine_transform_matrix ( ) ;
2023-02-18 23:27:53 +03:00
auto translation = context . rounded_device_point ( affine_transform . translation ( ) . to_type < CSSPixels > ( ) ) . to_type < int > ( ) . to_type < float > ( ) ;
affine_transform . set_translation ( translation ) ;
2022-03-18 01:29:20 +01:00
2022-09-29 01:20:13 +02:00
if ( opacity < 1.0f | | ! affine_transform . is_identity_or_translation ( ) ) {
2022-09-25 15:24:21 +01:00
auto transform_origin = this - > transform_origin ( ) ;
2023-04-20 16:00:21 +01:00
auto source_rect = context . enclosing_device_rect ( paintable_box ( ) . absolute_paint_rect ( ) ) . to_type < int > ( ) . to_type < float > ( ) . translated ( - transform_origin ) ;
2022-09-25 15:24:21 +01:00
auto transformed_destination_rect = affine_transform . map ( source_rect ) . translated ( transform_origin ) ;
auto destination_rect = transformed_destination_rect . to_rounded < int > ( ) ;
// FIXME: We should find a way to scale the paintable, rather than paint into a separate bitmap,
// then scale it. This snippet now copies the background at the destination, then scales it down/up
// to the size of the source (which could add some artefacts, though just scaling the bitmap already does that).
// We need to copy the background at the destination because a bunch of our rendering effects now rely on
// being able to sample the painter (see border radii, shadows, filters, etc).
2022-10-31 19:46:55 +00:00
CSSPixelPoint destination_clipped_fixup { } ;
2022-09-25 15:24:21 +01:00
auto try_get_scaled_destination_bitmap = [ & ] ( ) - > ErrorOr < NonnullRefPtr < Gfx : : Bitmap > > {
2022-09-26 00:01:31 +01:00
Gfx : : IntRect actual_destination_rect ;
auto bitmap = TRY ( context . painter ( ) . get_region_bitmap ( destination_rect , Gfx : : BitmapFormat : : BGRA8888 , actual_destination_rect ) ) ;
// get_region_bitmap() may clip to a smaller region if the requested rect goes outside the painter, so we need to account for that.
2022-10-31 19:46:55 +00:00
destination_clipped_fixup = CSSPixelPoint { destination_rect . location ( ) - actual_destination_rect . location ( ) } ;
2022-09-26 00:01:31 +01:00
destination_rect = actual_destination_rect ;
2022-09-25 15:24:21 +01:00
if ( source_rect . size ( ) ! = transformed_destination_rect . size ( ) ) {
2022-09-26 00:01:31 +01:00
auto sx = static_cast < float > ( source_rect . width ( ) ) / transformed_destination_rect . width ( ) ;
auto sy = static_cast < float > ( source_rect . height ( ) ) / transformed_destination_rect . height ( ) ;
bitmap = TRY ( bitmap - > scaled ( sx , sy ) ) ;
destination_clipped_fixup . scale_by ( sx , sy ) ;
2022-09-25 15:24:21 +01:00
}
return bitmap ;
} ;
auto bitmap_or_error = try_get_scaled_destination_bitmap ( ) ;
2021-11-06 19:30:59 +01:00
if ( bitmap_or_error . is_error ( ) )
2021-07-23 13:19:16 +03:00
return ;
2021-11-06 19:30:59 +01:00
auto bitmap = bitmap_or_error . release_value_but_fixme_should_propagate_errors ( ) ;
Gfx : : Painter painter ( bitmap ) ;
2023-04-20 16:00:21 +01:00
painter . translate ( context . rounded_device_point ( - paintable_box ( ) . absolute_paint_rect ( ) . location ( ) + destination_clipped_fixup ) . to_type < int > ( ) ) ;
2022-04-08 19:42:57 +02:00
auto paint_context = context . clone ( painter ) ;
2021-07-23 13:19:16 +03:00
paint_internal ( paint_context ) ;
2022-03-18 01:29:20 +01:00
2023-06-08 23:57:53 +03:00
if ( destination_rect . size ( ) = = bitmap - > size ( ) ) {
2022-09-26 00:01:31 +01:00
context . painter ( ) . blit ( destination_rect . location ( ) , * bitmap , bitmap - > rect ( ) , opacity ) ;
2023-06-08 23:57:53 +03:00
} else {
auto scaling_mode = CSS : : to_gfx_scaling_mode ( m_box - > computed_values ( ) . image_rendering ( ) , bitmap - > rect ( ) , destination_rect ) ;
context . painter ( ) . draw_scaled_bitmap ( destination_rect , * bitmap , bitmap - > rect ( ) , opacity , scaling_mode ) ;
}
2021-07-23 13:19:16 +03:00
} else {
2022-09-29 01:20:13 +02:00
Gfx : : PainterStateSaver saver ( context . painter ( ) ) ;
context . painter ( ) . translate ( affine_transform . translation ( ) . to_rounded < int > ( ) ) ;
2021-07-23 13:19:16 +03:00
paint_internal ( context ) ;
}
}
2022-09-24 20:36:06 +02:00
Gfx : : FloatPoint StackingContext : : compute_transform_origin ( ) const
2022-03-21 19:38:00 +01:00
{
2023-02-26 16:09:02 -07:00
auto style_value = m_box - > computed_values ( ) . transform_origin ( ) ;
2022-03-21 19:38:00 +01:00
// FIXME: respect transform-box property
2023-04-20 16:00:21 +01:00
auto reference_box = paintable_box ( ) . absolute_border_box_rect ( ) ;
2023-05-06 16:34:55 +02:00
auto x = reference_box . left ( ) + style_value . x . to_px ( m_box , reference_box . width ( ) ) ;
auto y = reference_box . top ( ) + style_value . y . to_px ( m_box , reference_box . height ( ) ) ;
2023-06-12 21:37:35 +03:00
return { x . to_float ( ) , y . to_float ( ) } ;
2022-03-21 19:38:00 +01:00
}
2022-11-03 19:08:07 +01:00
template < typename U , typename Callback >
static TraversalDecision for_each_in_inclusive_subtree_of_type_within_same_stacking_context_in_reverse ( Paintable const & paintable , Callback callback )
{
2023-07-14 10:02:19 +02:00
if ( paintable . stacking_context_rooted_here ( ) ) {
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.
if ( auto decision = callback ( static_cast < U const & > ( paintable ) ) ; decision ! = TraversalDecision : : Continue )
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 ( ) ) {
if ( for_each_in_inclusive_subtree_of_type_within_same_stacking_context_in_reverse < U > ( * child , callback ) = = TraversalDecision : : Break )
return TraversalDecision : : Break ;
}
if ( is < U > ( paintable ) ) {
if ( auto decision = callback ( static_cast < U const & > ( paintable ) ) ; decision ! = TraversalDecision : : Continue )
return decision ;
}
return TraversalDecision : : Continue ;
}
template < typename U , typename Callback >
static TraversalDecision for_each_in_subtree_of_type_within_same_stacking_context_in_reverse ( Paintable const & paintable , Callback callback )
{
for ( auto * child = paintable . last_child ( ) ; child ; child = child - > previous_sibling ( ) ) {
if ( for_each_in_inclusive_subtree_of_type_within_same_stacking_context_in_reverse < U > ( * child , callback ) = = TraversalDecision : : Break )
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
{
2023-02-26 16:09:02 -07:00
if ( ! m_box - > is_visible ( ) )
2022-07-03 18:34:51 -03:00
return { } ;
2022-11-02 17:35:53 +00:00
auto transform_origin = this - > transform_origin ( ) . to_type < CSSPixels > ( ) ;
// 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
2023-07-15 16:32:13 +02:00
if ( paintable_box ( ) . layout_box ( ) . is_fixed_position ( ) ) {
auto scroll_offset = paintable_box ( ) . document ( ) . browsing_context ( ) - > viewport_scroll_offset ( ) ;
transformed_position . translate_by ( - scroll_offset ) ;
}
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
2023-04-20 16:00:21 +01:00
if ( paintable_box ( ) . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paintable_box ( ) . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
2023-06-12 21:37:35 +03:00
if ( ! paintable_box ( ) . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
2022-07-03 18:34:51 -03:00
return { } ;
}
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 ] ;
2023-02-26 16:09:02 -07:00
if ( child . m_box - > 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 ;
2023-04-20 16:02:16 +01:00
for_each_in_subtree_of_type_within_same_stacking_context_in_reverse < PaintableBox > ( paintable_box ( ) , [ & ] ( PaintableBox const & paintable_box ) {
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
2023-04-20 16:02:16 +01:00
if ( paintable_box . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paintable_box . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
2023-06-12 21:37:35 +03:00
if ( ! paintable_box . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
2022-07-03 18:34:51 -03:00
return TraversalDecision : : SkipChildrenAndContinue ;
}
2023-04-20 16:02:16 +01:00
auto const & z_index = paintable_box . computed_values ( ) . z_index ( ) ;
auto & layout_box = paintable_box . layout_box ( ) ;
if ( z_index . value_or ( 0 ) = = 0 & & layout_box . is_positioned ( ) & & ! paintable_box . stacking_context ( ) ) {
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.
2023-02-26 16:09:02 -07:00
if ( m_box - > children_are_inline ( ) & & is < Layout : : BlockContainer > ( * m_box ) ) {
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.
2023-04-20 16:02:16 +01:00
for_each_in_subtree_of_type_within_same_stacking_context_in_reverse < PaintableBox > ( paintable_box ( ) , [ & ] ( PaintableBox const & paintable_box ) {
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
2023-04-20 16:02:16 +01:00
if ( paintable_box . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paintable_box . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
2023-06-12 21:37:35 +03:00
if ( ! paintable_box . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
2022-07-03 18:34:51 -03:00
return TraversalDecision : : SkipChildrenAndContinue ;
}
2023-04-20 16:02:16 +01:00
auto & layout_box = paintable_box . layout_box ( ) ;
2022-04-08 15:56:18 +02:00
if ( layout_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.
2023-02-26 16:09:02 -07:00
if ( ! m_box - > children_are_inline ( ) ) {
2023-04-20 16:02:16 +01:00
for_each_in_subtree_of_type_within_same_stacking_context_in_reverse < PaintableBox > ( paintable_box ( ) , [ & ] ( PaintableBox const & paintable_box ) {
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
2023-04-20 16:02:16 +01:00
if ( paintable_box . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paintable_box . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
2023-06-12 21:37:35 +03:00
if ( ! paintable_box . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
2022-07-03 18:34:51 -03:00
return TraversalDecision : : SkipChildrenAndContinue ;
}
2023-04-20 16:02:16 +01:00
auto & layout_box = paintable_box . layout_box ( ) ;
2022-04-08 15:56:18 +02:00
if ( ! layout_box . is_absolutely_positioned ( ) & & ! layout_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 ] ;
2023-02-26 16:09:02 -07:00
if ( child . m_box - > 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.
2023-06-12 21:37:35 +03:00
if ( paintable_box ( ) . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) ) {
2022-03-11 00:03:28 +01:00
return HitTestResult {
2023-04-20 16:00:21 +01:00
. paintable = const_cast < PaintableBox & > ( paintable_box ( ) ) ,
2022-03-04 15:02:16 +01:00
} ;
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 ( ' ' ) ;
2023-04-20 16:00:21 +01:00
builder . appendff ( " SC for {} {} [children: {}] (z-index: " , m_box - > debug_description ( ) , paintable_box ( ) . absolute_rect ( ) , m_children . size ( ) ) ;
2023-02-26 16:09:02 -07:00
if ( m_box - > computed_values ( ) . z_index ( ) . has_value ( ) )
builder . appendff ( " {} " , m_box - > 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 ) ;
}
}