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>
2020-11-22 15:53:01 +01:00
# include <LibWeb/Layout/Box.h>
2021-09-08 11:27:46 +02:00
# include <LibWeb/Layout/InitialContainingBlock.h>
2022-02-14 16:39:45 +01:00
# include <LibWeb/Layout/ReplacedBox.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>
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 )
{
2022-03-10 15:38:34 +01:00
if ( auto const * paintable = layout_node . paintable ( ) )
paintable - > paint ( context , phase ) ;
2022-03-10 14:02:25 +01:00
}
2022-03-10 02:13:28 +01:00
StackingContext : : StackingContext ( Layout : : Box & box , StackingContext * parent )
2020-06-15 17:29:35 +02:00
: m_box ( box )
2022-07-19 15:18:20 +01: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 )
{
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 ) {
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 ) ;
if ( a_z_index = = b_z_index )
return a - > m_box . is_before ( b - > m_box ) ;
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
}
void StackingContext : : paint_descendants ( PaintContext & context , Layout : : Node & box , StackingContextPaintPhase phase ) const
{
if ( auto * paintable = box . paintable ( ) )
2022-09-13 20:45:38 +02:00
paintable - > before_children_paint ( context , to_paint_phase ( phase ) , Paintable : : ShouldClipOverflow : : Yes ) ;
2021-08-01 16:24:59 +02:00
2021-05-07 19:03:25 +03:00
box . for_each_child ( [ & ] ( auto & child ) {
2022-03-16 19:02:37 +01:00
// If `child` establishes its own stacking context, skip over it.
if ( is < Layout : : Box > ( child ) & & child . paintable ( ) & & static_cast < Layout : : Box const & > ( child ) . paint_box ( ) - > stacking_context ( ) )
2021-07-23 13:16:04 +03:00
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-02-14 16:39:45 +01:00
if ( ! child_is_inline_or_replaced & & ! child . is_floating ( ) & & ! child . is_positioned ( ) ) {
2022-03-10 14:02:25 +01:00
paint_node ( child , context , PaintPhase : : Background ) ;
paint_node ( child , context , PaintPhase : : Border ) ;
2021-05-07 19:03:25 +03:00
paint_descendants ( context , child , phase ) ;
}
break ;
case StackingContextPaintPhase : : Floats :
if ( ! child . is_positioned ( ) ) {
if ( child . is_floating ( ) ) {
2022-03-10 14:02:25 +01:00
paint_node ( child , context , PaintPhase : : Background ) ;
paint_node ( child , context , PaintPhase : : Border ) ;
2021-05-07 19:03:25 +03:00
paint_descendants ( context , child , StackingContextPaintPhase : : BackgroundAndBorders ) ;
}
paint_descendants ( context , child , phase ) ;
}
break ;
2022-02-14 16:39:45 +01:00
case StackingContextPaintPhase : : BackgroundAndBordersForInlineLevelAndReplaced :
if ( ! child . is_positioned ( ) ) {
if ( child_is_inline_or_replaced ) {
2022-03-10 14:02:25 +01:00
paint_node ( child , context , PaintPhase : : Background ) ;
paint_node ( child , context , PaintPhase : : Border ) ;
2022-02-14 16:39:45 +01:00
paint_descendants ( context , child , StackingContextPaintPhase : : BackgroundAndBorders ) ;
}
paint_descendants ( context , child , phase ) ;
}
break ;
2021-05-07 19:03:25 +03:00
case StackingContextPaintPhase : : Foreground :
if ( ! child . is_positioned ( ) ) {
2022-03-10 14:02:25 +01:00
paint_node ( child , context , PaintPhase : : Foreground ) ;
2021-05-07 19:03:25 +03:00
paint_descendants ( context , child , phase ) ;
}
break ;
case StackingContextPaintPhase : : FocusAndOverlay :
if ( context . has_focus ( ) ) {
2022-03-10 14:02:25 +01:00
paint_node ( child , context , PaintPhase : : FocusOutline ) ;
2021-05-07 19:03:25 +03:00
}
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
2022-07-19 11:02:56 +01:00
if ( auto * paintable = box . paintable ( ) )
2022-09-13 20:45:38 +02:00
paintable - > after_children_paint ( context , to_paint_phase ( phase ) , Paintable : : ShouldClipOverflow : : Yes ) ;
2021-05-07 19:03:25 +03:00
}
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
auto paint_child = [ & ] ( auto * child ) {
auto parent = child - > m_box . parent ( ) ;
2022-09-24 14:30:00 +01:00
auto should_clip_overflow = child - > m_box . is_absolutely_positioned ( ) ? Paintable : : ShouldClipOverflow : : No : Paintable : : ShouldClipOverflow : : Yes ;
2022-07-19 11:06:21 +01:00
auto * paintable = parent ? parent - > paintable ( ) : nullptr ;
if ( paintable )
2022-09-13 20:45:38 +02:00
paintable - > before_children_paint ( context , PaintPhase : : Foreground , should_clip_overflow ) ;
2022-07-19 11:06:21 +01:00
child - > paint ( context ) ;
if ( paintable )
2022-09-13 20:45:38 +02:00
paintable - > after_children_paint ( context , PaintPhase : : Foreground , should_clip_overflow ) ;
2022-07-19 11:06:21 +01:00
} ;
2021-05-07 19:03:25 +03:00
// Draw positioned descendants with negative z-indices (step 3)
for ( auto * child : m_children ) {
if ( child - > m_box . computed_values ( ) . z_index ( ) . has_value ( ) & & child - > m_box . computed_values ( ) . z_index ( ) . value ( ) < 0 )
2022-07-19 11:06:21 +01:00
paint_child ( 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 ) ;
// Draw other positioned descendants (steps 8, 9)
2020-06-15 17:29:35 +02:00
for ( auto * child : m_children ) {
2021-05-07 19:03:25 +03:00
if ( child - > m_box . computed_values ( ) . z_index ( ) . has_value ( ) & & child - > m_box . computed_values ( ) . z_index ( ) . value ( ) < 0 )
continue ;
2022-07-19 11:06:21 +01:00
paint_child ( child ) ;
2020-06-15 17:29:35 +02:00
}
2021-05-07 19:03:25 +03:00
2022-03-10 14:02:25 +01:00
paint_node ( m_box , context , PaintPhase : : FocusOutline ) ;
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 ( ) ;
2022-07-14 17:45:23 +01:00
auto value = [ this , transformation ] ( size_t index , Optional < CSS : : Length const & > reference_length = { } ) - > float {
2022-03-21 23:18:38 +01:00
return transformation . values [ index ] . visit (
2022-07-14 17:45:23 +01:00
[ this , reference_length ] ( CSS : : LengthPercentage const & value ) {
return value . resolved ( m_box , reference_length . value ( ) ) . to_px ( m_box ) ;
} ,
[ ] ( CSS : : Angle const & value ) {
return value . to_degrees ( ) * static_cast < float > ( M_DEG2RAD ) ;
2022-03-18 01:29:20 +01:00
} ,
2022-03-21 23:18:38 +01:00
[ ] ( float value ) {
return value ;
2022-03-18 01:29:20 +01:00
} ) ;
2022-03-21 23:18:38 +01:00
} ;
2022-04-08 15:56:18 +02:00
auto reference_box = paintable ( ) . 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 ;
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 ;
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 ,
0 , value ( 0 ) , 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 ;
2022-03-18 01:29:20 +01:00
default :
dbgln_if ( LIBWEB_CSS_DEBUG , " FIXME: Unhandled transformation function {} " , CSS : : TransformationStyleValue : : create ( transformation . function , { } ) - > to_string ( ) ) ;
}
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 ( ) ) ;
if ( m_box . is_fixed_position ( ) ) {
context . painter ( ) . translate ( context . scroll_offset ( ) ) ;
}
2021-07-23 13:19:16 +03: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 ( ) ;
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 ( ) ;
auto source_rect = paintable ( ) . absolute_paint_rect ( ) . translated ( - transform_origin ) ;
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-09-26 00:01:31 +01:00
Gfx : : FloatPoint 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.
destination_clipped_fixup = Gfx : : FloatPoint { destination_rect . location ( ) - actual_destination_rect . location ( ) } ;
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 ) ;
2022-09-26 00:01:31 +01:00
painter . translate ( ( - paintable ( ) . absolute_paint_rect ( ) . location ( ) + destination_clipped_fixup ) . to_rounded < 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
2022-09-26 00:01:31 +01:00
if ( destination_rect . size ( ) = = bitmap - > size ( ) )
context . painter ( ) . blit ( destination_rect . location ( ) , * bitmap , bitmap - > rect ( ) , opacity ) ;
2022-07-20 22:02:56 +02:00
else
2022-09-25 15:24:21 +01:00
context . painter ( ) . draw_scaled_bitmap ( destination_rect , * bitmap , bitmap - > rect ( ) , opacity , Gfx : : Painter : : ScalingMode : : BilinearBlend ) ;
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
{
auto style_value = m_box . computed_values ( ) . transform_origin ( ) ;
// FIXME: respect transform-box property
2022-04-08 15:56:18 +02:00
auto reference_box = paintable ( ) . absolute_border_box_rect ( ) ;
2022-03-21 19:38:00 +01:00
auto x = reference_box . left ( ) + style_value . x . resolved ( m_box , CSS : : Length : : make_px ( reference_box . width ( ) ) ) . to_px ( m_box ) ;
auto y = reference_box . top ( ) + style_value . y . resolved ( m_box , CSS : : Length : : make_px ( reference_box . height ( ) ) ) . to_px ( m_box ) ;
return { x , y } ;
}
2022-03-21 11:11:05 +01:00
Optional < HitTestResult > StackingContext : : hit_test ( Gfx : : FloatPoint const & position , HitTestType type ) const
2020-07-01 19:02:28 +02:00
{
2022-07-03 18:34:51 -03:00
if ( ! m_box . is_visible ( ) )
return { } ;
if ( m_box . computed_values ( ) . z_index ( ) . value_or ( 0 ) < 0 )
return { } ;
2022-03-21 19:38:00 +01:00
auto transform_origin = this - > transform_origin ( ) ;
2022-07-19 15:18:20 +01:00
auto transformed_position = affine_transform_matrix ( ) . inverse ( ) . value_or ( { } ) . map ( position - transform_origin ) + transform_origin ;
2022-03-18 01:32:50 +01:00
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
if ( paintable ( ) . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paintable ( ) . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
if ( ! paintable ( ) . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
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).
for ( ssize_t i = m_children . size ( ) - 1 ; i > = 0 ; - - i ) {
auto const & child = * m_children [ i ] ;
2022-03-18 01:32:50 +01:00
auto result = child . hit_test ( transformed_position , type ) ;
2022-03-21 11:11:05 +01:00
if ( result . has_value ( ) )
2022-03-04 15:02:16 +01:00
return result ;
}
2022-03-21 11:11:05 +01:00
Optional < HitTestResult > result ;
2022-03-04 15:02:16 +01:00
// 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
2022-04-08 15:56:18 +02:00
paintable ( ) . for_each_in_subtree_of_type < PaintableBox > ( [ & ] ( auto & paint_box ) {
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
if ( paint_box . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paint_box . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
if ( ! paint_box . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
return TraversalDecision : : SkipChildrenAndContinue ;
}
2022-04-08 15:56:18 +02:00
auto & layout_box = paint_box . layout_box ( ) ;
if ( layout_box . is_positioned ( ) & & ! paint_box . stacking_context ( ) ) {
if ( auto candidate = paint_box . hit_test ( transformed_position , type ) ; candidate . has_value ( ) )
2022-03-21 12:12:50 +01:00
result = move ( candidate ) ;
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-03-21 11:11:05 +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.
2022-03-10 02:13:28 +01:00
if ( m_box . children_are_inline ( ) & & is < Layout : : BlockContainer > ( m_box ) ) {
2022-04-08 15:56:18 +02:00
auto result = paintable ( ) . hit_test ( transformed_position , type ) ;
2022-03-21 11:11:05 +01:00
if ( result . has_value ( ) )
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.
2022-04-08 15:56:18 +02:00
paintable ( ) . for_each_in_subtree_of_type < PaintableBox > ( [ & ] ( auto const & paint_box ) {
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
if ( paint_box . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paint_box . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
if ( ! paint_box . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
return TraversalDecision : : SkipChildrenAndContinue ;
}
2022-04-08 15:56:18 +02:00
auto & layout_box = paint_box . layout_box ( ) ;
if ( layout_box . is_floating ( ) ) {
if ( auto candidate = paint_box . hit_test ( transformed_position , type ) ; candidate . has_value ( ) )
2022-03-21 12:12:50 +01:00
result = move ( candidate ) ;
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-03-21 11:11:05 +01:00
if ( result . has_value ( ) )
return result ;
2022-03-04 15:02:16 +01:00
// 3. the in-flow, non-inline-level, non-positioned descendants.
if ( ! m_box . children_are_inline ( ) ) {
2022-04-08 15:56:18 +02:00
paintable ( ) . for_each_in_subtree_of_type < PaintableBox > ( [ & ] ( auto const & paint_box ) {
2022-07-03 18:34:51 -03:00
// FIXME: Support more overflow variations.
if ( paint_box . computed_values ( ) . overflow_x ( ) = = CSS : : Overflow : : Hidden & & paint_box . computed_values ( ) . overflow_y ( ) = = CSS : : Overflow : : Hidden ) {
if ( ! paint_box . absolute_border_box_rect ( ) . contains ( transformed_position . x ( ) , transformed_position . y ( ) ) )
return TraversalDecision : : SkipChildrenAndContinue ;
}
2022-04-08 15:56:18 +02:00
auto & layout_box = paint_box . layout_box ( ) ;
if ( ! layout_box . is_absolutely_positioned ( ) & & ! layout_box . is_floating ( ) ) {
if ( auto candidate = paint_box . hit_test ( transformed_position , type ) ; candidate . has_value ( ) )
2022-03-21 12:12:50 +01:00
result = move ( candidate ) ;
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-03-21 11:11:05 +01:00
if ( result . has_value ( ) )
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).
for ( ssize_t i = m_children . size ( ) - 1 ; i > = 0 ; - - i ) {
auto const & child = * m_children [ i ] ;
2022-03-18 01:32:50 +01:00
auto result = child . hit_test ( transformed_position , type ) ;
2022-03-21 11:11:05 +01:00
if ( result . has_value ( ) )
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.
2022-04-08 15:56:18 +02:00
if ( paintable ( ) . absolute_border_box_rect ( ) . contains ( transformed_position ) ) {
2022-03-11 00:03:28 +01:00
return HitTestResult {
2022-04-08 15:56:18 +02:00
. paintable = paintable ( ) ,
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 ( ' ' ) ;
2022-04-08 15:56:18 +02:00
builder . appendff ( " SC for {} {} [children: {}] (z-index: " , m_box . debug_description ( ) , paintable ( ) . absolute_rect ( ) , m_children . size ( ) ) ;
2022-03-04 15:03:48 +01:00
if ( m_box . computed_values ( ) . z_index ( ) . has_value ( ) )
builder . appendff ( " {} " , m_box . computed_values ( ) . z_index ( ) . value ( ) ) ;
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 ) ;
}
}