2020-01-18 09:38:21 +01:00
/*
2025-02-26 18:16:36 +01:00
* Copyright ( c ) 2018 - 2025 , Andreas Kling < andreas @ ladybird . org >
2025-01-15 14:58:23 +00:00
* Copyright ( c ) 2021 - 2025 , Sam Atkins < sam @ ladybird . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2024-11-03 13:20:04 +01:00
# include <AK/NonnullRawPtr.h>
2021-05-30 20:22:25 +02:00
# include <AK/TypeCasts.h>
2020-02-06 15:04:03 +01:00
# include <LibCore/DirIterator.h>
2024-12-20 16:35:12 +01:00
# include <LibGC/CellAllocator.h>
2022-07-31 18:47:09 +02:00
# include <LibWeb/CSS/Clip.h>
2024-12-20 11:32:17 +01:00
# include <LibWeb/CSS/ComputedProperties.h>
2025-01-02 12:59:09 +11:00
# include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
2023-03-23 21:17:43 +00:00
# include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
2024-07-24 15:47:11 +01:00
# include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
2024-07-18 20:29:02 +01:00
# include <LibWeb/CSS/StyleValues/CounterStyleValue.h>
2025-02-21 17:56:24 +01:00
# include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
2023-04-26 21:05:38 +02:00
# include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
2025-02-26 18:16:36 +01:00
# include <LibWeb/CSS/StyleValues/FitContentStyleValue.h>
2025-09-03 18:57:32 +12:00
# include <LibWeb/CSS/StyleValues/FontStyleStyleValue.h>
2023-08-17 20:25:18 +02:00
# include <LibWeb/CSS/StyleValues/GridAutoFlowStyleValue.h>
2023-03-24 14:25:00 +00:00
# include <LibWeb/CSS/StyleValues/GridTemplateAreaStyleValue.h>
2023-03-24 14:53:08 +00:00
# include <LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h>
2023-04-29 17:59:07 +02:00
# include <LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h>
2023-06-01 17:01:09 +01:00
# include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
2025-08-08 10:28:41 +01:00
# include <LibWeb/CSS/StyleValues/KeywordStyleValue.h>
2023-05-27 12:28:25 +01:00
# include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
2023-09-07 15:29:54 +01:00
# include <LibWeb/CSS/StyleValues/MathDepthStyleValue.h>
2023-06-01 16:16:15 +01:00
# include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
2024-10-01 09:20:06 +01:00
# include <LibWeb/CSS/StyleValues/OpenTypeTaggedStyleValue.h>
2023-03-24 17:28:43 +00:00
# include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
2023-10-16 13:52:51 +02:00
# include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
2023-03-25 00:02:50 +00:00
# include <LibWeb/CSS/StyleValues/RectStyleValue.h>
2025-05-26 22:36:12 +01:00
# include <LibWeb/CSS/StyleValues/ScrollbarColorStyleValue.h>
2023-03-24 17:45:25 +00:00
# include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
2023-03-24 17:48:42 +00:00
# include <LibWeb/CSS/StyleValues/StringStyleValue.h>
2023-03-25 00:12:21 +00:00
# include <LibWeb/CSS/StyleValues/StyleValueList.h>
2025-11-18 10:53:25 +00:00
# include <LibWeb/CSS/StyleValues/TextIndentStyleValue.h>
2025-09-14 16:04:26 +12:00
# include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
2025-10-26 18:44:31 +13:00
# include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
2023-03-24 17:59:33 +00:00
# include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
2021-10-06 20:02:41 +02:00
# include <LibWeb/Layout/BlockContainer.h>
2021-08-12 16:41:09 +01:00
# include <LibWeb/Layout/Node.h>
2022-09-17 21:25:50 +02:00
# include <LibWeb/Platform/FontPlugin.h>
2019-09-21 15:32:17 +03:00
2020-07-26 20:01:35 +02:00
namespace Web : : CSS {
2020-03-07 10:27:02 +01:00
2024-12-20 16:35:12 +01:00
GC_DEFINE_ALLOCATOR ( ComputedProperties ) ;
ComputedProperties : : ComputedProperties ( ) = default ;
ComputedProperties : : ~ ComputedProperties ( ) = default ;
void ComputedProperties : : visit_edges ( Visitor & visitor )
2024-08-02 13:59:19 +02:00
{
2024-12-20 16:35:12 +01:00
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_transition_property_source ) ;
2024-08-02 13:59:19 +02:00
}
2025-02-05 12:57:41 +00:00
bool ComputedProperties : : is_property_important ( PropertyID property_id ) const
2024-03-16 07:44:48 +01:00
{
2025-08-25 16:04:32 +12:00
VERIFY ( property_id > = first_longhand_property_id & & property_id < = last_longhand_property_id ) ;
size_t n = to_underlying ( property_id ) - to_underlying ( first_longhand_property_id ) ;
2024-12-20 16:35:12 +01:00
return m_property_important [ n / 8 ] & ( 1 < < ( n % 8 ) ) ;
2024-08-02 13:59:19 +02:00
}
2025-02-05 12:57:41 +00:00
void ComputedProperties : : set_property_important ( PropertyID property_id , Important important )
2024-08-02 13:59:19 +02:00
{
2025-08-25 16:04:32 +12:00
VERIFY ( property_id > = first_longhand_property_id & & property_id < = last_longhand_property_id ) ;
size_t n = to_underlying ( property_id ) - to_underlying ( first_longhand_property_id ) ;
2024-08-02 13:59:19 +02:00
if ( important = = Important : : Yes )
2024-12-20 16:35:12 +01:00
m_property_important [ n / 8 ] | = ( 1 < < ( n % 8 ) ) ;
2024-08-02 13:59:19 +02:00
else
2024-12-20 16:35:12 +01:00
m_property_important [ n / 8 ] & = ~ ( 1 < < ( n % 8 ) ) ;
2024-03-16 07:44:48 +01:00
}
2025-02-05 12:57:41 +00:00
bool ComputedProperties : : is_property_inherited ( PropertyID property_id ) const
2024-03-16 07:44:48 +01:00
{
2025-08-25 16:04:32 +12:00
VERIFY ( property_id > = first_longhand_property_id & & property_id < = last_longhand_property_id ) ;
size_t n = to_underlying ( property_id ) - to_underlying ( first_longhand_property_id ) ;
2024-12-20 16:35:12 +01:00
return m_property_inherited [ n / 8 ] & ( 1 < < ( n % 8 ) ) ;
2024-08-02 13:59:19 +02:00
}
2025-08-20 23:30:31 +12:00
bool ComputedProperties : : is_animated_property_inherited ( PropertyID property_id ) const
{
2025-08-25 16:04:32 +12:00
VERIFY ( property_id > = first_longhand_property_id & & property_id < = last_longhand_property_id ) ;
size_t n = to_underlying ( property_id ) - to_underlying ( first_longhand_property_id ) ;
2025-08-20 23:30:31 +12:00
return m_animated_property_inherited [ n / 8 ] & ( 1 < < ( n % 8 ) ) ;
}
2025-02-05 12:57:41 +00:00
void ComputedProperties : : set_property_inherited ( PropertyID property_id , Inherited inherited )
2024-08-02 13:59:19 +02:00
{
2025-08-25 16:04:32 +12:00
VERIFY ( property_id > = first_longhand_property_id & & property_id < = last_longhand_property_id ) ;
size_t n = to_underlying ( property_id ) - to_underlying ( first_longhand_property_id ) ;
2024-08-02 13:59:19 +02:00
if ( inherited = = Inherited : : Yes )
2024-12-20 16:35:12 +01:00
m_property_inherited [ n / 8 ] | = ( 1 < < ( n % 8 ) ) ;
2024-08-02 13:59:19 +02:00
else
2024-12-20 16:35:12 +01:00
m_property_inherited [ n / 8 ] & = ~ ( 1 < < ( n % 8 ) ) ;
2024-03-16 07:44:48 +01:00
}
2025-08-20 23:30:31 +12:00
void ComputedProperties : : set_animated_property_inherited ( PropertyID property_id , Inherited inherited )
{
2025-08-25 16:04:32 +12:00
VERIFY ( property_id > = first_longhand_property_id & & property_id < = last_longhand_property_id ) ;
size_t n = to_underlying ( property_id ) - to_underlying ( first_longhand_property_id ) ;
2025-08-20 23:30:31 +12:00
if ( inherited = = Inherited : : Yes )
m_animated_property_inherited [ n / 8 ] | = ( 1 < < ( n % 8 ) ) ;
else
m_animated_property_inherited [ n / 8 ] & = ~ ( 1 < < ( n % 8 ) ) ;
}
2025-08-08 10:11:51 +01:00
void ComputedProperties : : set_property ( PropertyID id , NonnullRefPtr < StyleValue const > value , Inherited inherited , Important important )
2024-03-16 07:44:48 +01:00
{
2025-08-25 16:04:32 +12:00
VERIFY ( id > = first_longhand_property_id & & id < = last_longhand_property_id ) ;
2025-10-27 01:18:52 +13:00
set_property_without_modifying_flags ( id , move ( value ) ) ;
2024-08-02 13:59:19 +02:00
set_property_important ( id , important ) ;
set_property_inherited ( id , inherited ) ;
2024-03-16 07:44:48 +01:00
}
2025-10-27 01:18:52 +13:00
void ComputedProperties : : set_property_without_modifying_flags ( PropertyID id , NonnullRefPtr < StyleValue const > value )
{
VERIFY ( id > = first_longhand_property_id & & id < = last_longhand_property_id ) ;
m_property_values [ to_underlying ( id ) - to_underlying ( first_longhand_property_id ) ] = move ( value ) ;
}
2025-02-05 12:57:41 +00:00
void ComputedProperties : : revert_property ( PropertyID id , ComputedProperties const & style_for_revert )
2024-08-04 10:08:28 +02:00
{
2025-08-25 16:04:32 +12:00
VERIFY ( id > = first_longhand_property_id & & id < = last_longhand_property_id ) ;
m_property_values [ to_underlying ( id ) - to_underlying ( first_longhand_property_id ) ] = style_for_revert . m_property_values [ to_underlying ( id ) - to_underlying ( first_longhand_property_id ) ] ;
2024-08-04 10:08:28 +02:00
set_property_important ( id , style_for_revert . is_property_important ( id ) ? Important : : Yes : Important : : No ) ;
set_property_inherited ( id , style_for_revert . is_property_inherited ( id ) ? Inherited : : Yes : Inherited : : No ) ;
}
2025-09-09 00:35:59 +02:00
Display ComputedProperties : : display_before_box_type_transformation ( ) const
{
return m_display_before_box_type_transformation ;
}
void ComputedProperties : : set_display_before_box_type_transformation ( Display value )
{
m_display_before_box_type_transformation = value ;
}
2025-08-20 23:30:31 +12:00
void ComputedProperties : : set_animated_property ( PropertyID id , NonnullRefPtr < StyleValue const > value , Inherited inherited )
2024-03-16 07:44:48 +01:00
{
2024-12-20 16:35:12 +01:00
m_animated_property_values . set ( id , move ( value ) ) ;
2025-08-20 23:30:31 +12:00
set_animated_property_inherited ( id , inherited ) ;
}
void ComputedProperties : : remove_animated_property ( PropertyID id )
{
m_animated_property_values . remove ( id ) ;
2024-03-16 07:44:48 +01:00
}
2025-11-19 00:04:20 +13:00
void ComputedProperties : : reset_non_inherited_animated_properties ( Badge < Animations : : KeyframeEffect > )
2024-03-16 07:44:48 +01:00
{
2025-11-19 00:04:20 +13:00
for ( auto property_id : m_animated_property_values . keys ( ) ) {
if ( ! is_animated_property_inherited ( property_id ) )
m_animated_property_values . remove ( property_id ) ;
}
2019-09-21 15:32:17 +03:00
}
2025-08-08 10:11:51 +01:00
StyleValue const & ComputedProperties : : property ( PropertyID property_id , WithAnimationsApplied return_animated_value ) const
2019-09-21 15:32:17 +03:00
{
2025-08-25 16:04:32 +12:00
VERIFY ( property_id > = first_longhand_property_id & & property_id < = last_longhand_property_id ) ;
2025-10-27 02:06:01 +13:00
// Important properties override animated properties
if ( ! is_property_important ( property_id ) & & return_animated_value = = WithAnimationsApplied : : Yes ) {
2024-12-20 16:35:12 +01:00
if ( auto animated_value = m_animated_property_values . get ( property_id ) ; animated_value . has_value ( ) )
2024-11-03 13:20:04 +01:00
return * animated_value . value ( ) ;
2024-09-19 10:46:32 +01:00
}
2024-03-16 07:44:48 +01:00
2022-04-14 11:52:35 +01:00
// By the time we call this method, all properties have values assigned.
2025-08-25 16:04:32 +12:00
return * m_property_values [ to_underlying ( property_id ) - to_underlying ( first_longhand_property_id ) ] ;
2019-09-21 15:32:17 +03:00
}
2025-02-05 12:57:41 +00:00
Variant < LengthPercentage , NormalGap > ComputedProperties : : gap_value ( PropertyID id ) const
2024-11-09 17:38:09 +01:00
{
auto const & value = property ( id ) ;
if ( value . is_keyword ( ) ) {
2025-02-05 12:57:41 +00:00
VERIFY ( value . as_keyword ( ) . keyword ( ) = = Keyword : : Normal ) ;
2024-11-09 17:38:09 +01:00
return NormalGap { } ;
}
2025-10-02 16:18:48 +13:00
return LengthPercentage : : from_style_value ( value ) ;
2024-11-09 17:38:09 +01:00
}
2025-02-05 12:57:41 +00:00
Size ComputedProperties : : size_value ( PropertyID id ) const
2022-09-25 15:47:40 +02:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( id ) ;
if ( value . is_keyword ( ) ) {
switch ( value . to_keyword ( ) ) {
2024-08-14 14:06:03 +01:00
case Keyword : : Auto :
2025-02-05 12:57:41 +00:00
return Size : : make_auto ( ) ;
2024-08-14 14:06:03 +01:00
case Keyword : : MinContent :
2025-02-05 12:57:41 +00:00
return Size : : make_min_content ( ) ;
2024-08-14 14:06:03 +01:00
case Keyword : : MaxContent :
2025-02-05 12:57:41 +00:00
return Size : : make_max_content ( ) ;
2024-08-14 14:06:03 +01:00
case Keyword : : None :
2025-02-05 12:57:41 +00:00
return Size : : make_none ( ) ;
2022-09-25 15:47:40 +02:00
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2025-02-26 18:16:36 +01:00
if ( value . is_fit_content ( ) ) {
auto & fit_content = value . as_fit_content ( ) ;
2025-08-28 12:14:15 +01:00
if ( auto length_percentage = fit_content . length_percentage ( ) ; length_percentage . has_value ( ) )
return Size : : make_fit_content ( length_percentage . release_value ( ) ) ;
return Size : : make_fit_content ( ) ;
2025-02-26 18:16:36 +01:00
}
2022-09-25 15:47:40 +02:00
2024-12-11 15:05:56 +00:00
if ( value . is_calculated ( ) )
2025-02-05 12:57:41 +00:00
return Size : : make_calculated ( value . as_calculated ( ) ) ;
2022-09-25 15:47:40 +02:00
2024-11-03 13:20:04 +01:00
if ( value . is_percentage ( ) )
2025-02-05 12:57:41 +00:00
return Size : : make_percentage ( value . as_percentage ( ) . percentage ( ) ) ;
2022-09-25 15:47:40 +02:00
2025-09-01 14:03:25 +01:00
if ( value . is_length ( ) )
return Size : : make_length ( value . as_length ( ) . length ( ) ) ;
2022-09-25 15:47:40 +02:00
2025-07-30 14:14:53 +02:00
// FIXME: Support `anchor-size(..)`
if ( value . is_anchor_size ( ) )
return Size : : make_none ( ) ;
2025-05-16 19:20:24 +01:00
dbgln ( " FIXME: Unsupported size value: `{}`, treating as `auto` " , value . to_string ( SerializationMode : : Normal ) ) ;
2025-02-05 12:57:41 +00:00
return Size : : make_auto ( ) ;
2022-09-25 15:47:40 +02:00
}
2025-08-22 03:41:19 +12:00
Optional < LengthPercentage > ComputedProperties : : length_percentage ( PropertyID id , Layout : : NodeWithStyle const & layout_node , ClampNegativeLengths disallow_negative_lengths ) const
2022-01-19 16:19:43 +00:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( id ) ;
2022-01-19 16:19:43 +00:00
2024-12-11 15:05:56 +00:00
if ( value . is_calculated ( ) )
2024-12-11 15:16:34 +00:00
return LengthPercentage { value . as_calculated ( ) } ;
2022-01-19 16:19:43 +00:00
2025-08-22 03:41:19 +12:00
if ( value . is_percentage ( ) ) {
auto percentage = value . as_percentage ( ) . percentage ( ) ;
2022-01-19 16:19:43 +00:00
2025-08-22 03:41:19 +12:00
// FIXME: This value can be negative as interpolation does not yet clamp values to allowed ranges - remove this
// once we do that.
if ( disallow_negative_lengths = = ClampNegativeLengths : : Yes & & percentage . as_fraction ( ) < 0 )
return { } ;
return percentage ;
}
if ( value . is_length ( ) ) {
auto length = value . as_length ( ) . length ( ) ;
// FIXME: This value can be negative as interpolation does not yet clamp values to allowed ranges - remove this
// once we do that.
if ( disallow_negative_lengths = = ClampNegativeLengths : : Yes & & length . to_px ( layout_node ) < 0 )
return { } ;
return length ;
}
2022-01-19 16:19:43 +00:00
2022-02-18 15:10:11 +00:00
return { } ;
2019-09-21 15:32:17 +03:00
}
2019-09-25 11:55:04 +03:00
2025-08-28 02:07:57 +12:00
Length ComputedProperties : : length ( PropertyID property_id ) const
{
return property ( property_id ) . as_length ( ) . length ( ) ;
}
2025-09-01 12:51:52 +01:00
LengthBox ComputedProperties : : length_box ( PropertyID left_id , PropertyID top_id , PropertyID right_id , PropertyID bottom_id , Layout : : NodeWithStyle const & layout_node , ClampNegativeLengths disallow_negative_lengths , LengthPercentageOrAuto const & default_value ) const
2020-05-11 23:04:59 +02:00
{
2025-09-01 12:51:52 +01:00
auto length_box_side = [ & ] ( PropertyID id ) - > LengthPercentageOrAuto {
2025-08-27 15:42:33 +01:00
auto const & value = property ( id ) ;
if ( value . is_calculated ( ) )
return LengthPercentage { value . as_calculated ( ) } ;
if ( value . is_percentage ( ) ) {
auto percentage = value . as_percentage ( ) . percentage ( ) ;
// FIXME: This value can be negative as interpolation does not yet clamp values to allowed ranges - remove this
// once we do that.
if ( disallow_negative_lengths = = ClampNegativeLengths : : Yes & & percentage . as_fraction ( ) < 0 )
return default_value ;
return percentage ;
}
if ( value . is_length ( ) ) {
auto length = value . as_length ( ) . length ( ) ;
// FIXME: This value can be negative as interpolation does not yet clamp values to allowed ranges - remove this
// once we do that.
if ( disallow_negative_lengths = = ClampNegativeLengths : : Yes & & length . to_px ( layout_node ) < 0 )
return default_value ;
return value . as_length ( ) . length ( ) ;
}
if ( value . has_auto ( ) )
2025-09-01 12:51:52 +01:00
return LengthPercentageOrAuto : : make_auto ( ) ;
2025-08-27 15:42:33 +01:00
return default_value ;
} ;
return LengthBox {
length_box_side ( top_id ) ,
length_box_side ( right_id ) ,
length_box_side ( bottom_id ) ,
length_box_side ( left_id )
} ;
2020-05-11 23:04:59 +02:00
}
2025-07-19 13:28:14 +12:00
Color ComputedProperties : : color_or_fallback ( PropertyID id , ColorResolutionContext color_resolution_context , Color fallback ) const
2019-09-28 22:18:19 +02:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( id ) ;
if ( ! value . has_color ( ) )
2019-09-28 22:18:19 +02:00
return fallback ;
2025-07-19 13:28:14 +12:00
return value . to_color ( color_resolution_context ) . value ( ) ;
2019-09-28 22:18:19 +02:00
}
2019-10-06 11:23:58 +02:00
2025-10-18 18:03:58 +13:00
// https://drafts.csswg.org/css-values-4/#linked-properties
HashMap < PropertyID , StyleValueVector > ComputedProperties : : assemble_coordinated_value_list ( PropertyID base_property_id , Vector < PropertyID > const & property_ids ) const
{
// A coordinating list property group creates a coordinated value list, which has, for each entry, a value from each
// property in the group; these are used together to define a single effect, such as a background image layer or an
// animation. The coordinated value list is assembled as follows:
// - The length of the coordinated value list is determined by the number of items specified in one particular
// coordinating list property, the coordinating list base property. (In the case of backgrounds, this is the
// background-image property.)
// - The Nth value of the coordinated value list is constructed by collecting the Nth use value of each coordinating
// list property
// - If a coordinating list property has too many values specified, excess values at the end of its list are not
// used.
// - If a coordinating list property has too few values specified, its value list is repeated to add more used
// values.
// - The computed values of the coordinating list properties are not affected by such truncation or repetition.
// FIXME: This is only required until we update parse_comma_separated_list to always return a StyleValueList
auto const get_property_value_as_list = [ & ] ( PropertyID property_id ) {
auto const & value = property ( property_id ) ;
return value . is_value_list ( ) ? value . as_value_list ( ) . values ( ) : StyleValueVector { value } ;
} ;
HashMap < PropertyID , StyleValueVector > coordinated_value_list ;
for ( size_t i = 0 ; i < get_property_value_as_list ( base_property_id ) . size ( ) ; i + + ) {
for ( auto property_id : property_ids ) {
auto const & list = get_property_value_as_list ( property_id ) ;
coordinated_value_list . ensure ( property_id ) . append ( list [ i % list . size ( ) ] ) ;
}
}
return coordinated_value_list ;
}
2025-08-15 23:53:15 +01:00
ColorInterpolation ComputedProperties : : color_interpolation ( ) const
{
auto const & value = property ( PropertyID : : ColorInterpolation ) ;
return keyword_to_color_interpolation ( value . to_keyword ( ) ) . value_or ( CSS : : ColorInterpolation : : Auto ) ;
}
2025-01-02 12:59:09 +11:00
// https://drafts.csswg.org/css-color-adjust-1/#determine-the-used-color-scheme
2025-02-05 12:57:41 +00:00
PreferredColorScheme ComputedProperties : : color_scheme ( PreferredColorScheme preferred_scheme , Optional < Vector < String > const & > document_supported_schemes ) const
2025-01-02 12:59:09 +11:00
{
// To determine the used color scheme of an element:
2025-02-05 12:57:41 +00:00
auto const & scheme_value = property ( PropertyID : : ColorScheme ) . as_color_scheme ( ) ;
2025-01-02 12:59:09 +11:00
// 1. If the user’ s preferred color scheme, as indicated by the prefers-color-scheme media feature,
// is present among the listed color schemes, and is supported by the user agent,
// that’ s the element’ s used color scheme.
2025-02-05 12:57:41 +00:00
if ( preferred_scheme ! = PreferredColorScheme : : Auto & & scheme_value . schemes ( ) . contains_slow ( preferred_color_scheme_to_string ( preferred_scheme ) ) )
2025-01-02 12:59:09 +11:00
return preferred_scheme ;
// 2. Otherwise, if the user has indicated an overriding preference for their chosen color scheme,
// and the only keyword is not present in color-scheme for the element,
// the user agent must override the color scheme with the user’ s preferred color scheme.
// See § 2.3 Overriding the Color Scheme.
// FIXME: We don't currently support setting an "overriding preference" for color schemes.
// 3. Otherwise, if the user agent supports at least one of the listed color schemes,
// the used color scheme is the first supported color scheme in the list.
2025-02-05 12:57:41 +00:00
auto first_supported = scheme_value . schemes ( ) . first_matching ( [ ] ( auto scheme ) { return preferred_color_scheme_from_string ( scheme ) ! = PreferredColorScheme : : Auto ; } ) ;
2025-01-02 12:59:09 +11:00
if ( first_supported . has_value ( ) )
return preferred_color_scheme_from_string ( first_supported . value ( ) ) ;
// 4. Otherwise, the used color scheme is the browser default. (Same as normal.)
2025-01-04 09:59:57 +11:00
// `normal` indicates that the element supports the page’ s supported color schemes, if they are set
if ( document_supported_schemes . has_value ( ) ) {
2025-02-05 12:57:41 +00:00
if ( preferred_scheme ! = PreferredColorScheme : : Auto & & document_supported_schemes - > contains_slow ( preferred_color_scheme_to_string ( preferred_scheme ) ) )
2025-01-04 09:59:57 +11:00
return preferred_scheme ;
2025-02-05 12:57:41 +00:00
auto document_first_supported = document_supported_schemes - > first_matching ( [ ] ( auto scheme ) { return preferred_color_scheme_from_string ( scheme ) ! = PreferredColorScheme : : Auto ; } ) ;
2025-01-04 09:59:57 +11:00
if ( document_first_supported . has_value ( ) )
return preferred_color_scheme_from_string ( document_first_supported . value ( ) ) ;
}
2025-02-05 12:57:41 +00:00
return PreferredColorScheme : : Light ;
2025-01-02 12:59:09 +11:00
}
2025-01-02 03:56:05 +03:00
NonnullRefPtr < Gfx : : Font const > ComputedProperties : : font_fallback ( bool monospace , bool bold , float point_size )
2021-04-22 20:47:47 +02:00
{
if ( monospace & & bold )
2022-09-17 21:25:50 +02:00
return Platform : : FontPlugin : : the ( ) . default_fixed_width_font ( ) . bold_variant ( ) ;
2021-04-22 20:47:47 +02:00
if ( monospace )
2022-09-17 21:25:50 +02:00
return Platform : : FontPlugin : : the ( ) . default_fixed_width_font ( ) ;
2021-04-22 20:47:47 +02:00
if ( bold )
2025-01-02 03:56:05 +03:00
return Platform : : FontPlugin : : the ( ) . default_font ( point_size ) - > bold_variant ( ) ;
2021-04-22 20:47:47 +02:00
2025-01-02 03:56:05 +03:00
return * Platform : : FontPlugin : : the ( ) . default_font ( point_size ) ;
2021-04-22 20:47:47 +02:00
}
2025-09-23 20:53:07 +12:00
CSSPixels ComputedProperties : : line_height ( ) const
2023-03-17 23:08:45 +01:00
{
2025-09-23 20:53:07 +12:00
// https://drafts.csswg.org/css-inline-3/#line-height-property
2025-02-05 12:57:41 +00:00
auto const & line_height = property ( PropertyID : : LineHeight ) ;
2023-03-17 23:08:45 +01:00
2025-09-23 20:53:07 +12:00
// normal
// Determine the preferred line height automatically based on font metrics.
2024-11-03 13:20:04 +01:00
if ( line_height . is_keyword ( ) & & line_height . to_keyword ( ) = = Keyword : : Normal )
2025-09-23 20:53:07 +12:00
return CSSPixels { round_to < i32 > ( font_size ( ) * normal_line_height_scale ) } ;
2023-03-17 23:08:45 +01:00
2025-09-23 20:53:07 +12:00
// <length [0,∞]>
// The specified length is used as the preferred line height. Negative values are illegal.
2025-09-01 14:03:25 +01:00
if ( line_height . is_length ( ) )
2025-09-23 20:53:07 +12:00
return line_height . as_length ( ) . length ( ) . absolute_length_to_px ( ) ;
2023-03-17 23:08:45 +01:00
2025-09-23 20:53:07 +12:00
// <number [0,∞]>
// The preferred line height is this number multiplied by the element’ s computed font-size.
2024-11-03 13:20:04 +01:00
if ( line_height . is_number ( ) )
2025-09-23 20:53:07 +12:00
return CSSPixels { font_size ( ) * line_height . as_number ( ) . number ( ) } ;
2023-03-17 23:08:45 +01:00
2025-09-23 20:53:07 +12:00
VERIFY_NOT_REACHED ( ) ;
2023-03-17 23:08:45 +01:00
}
2024-12-20 11:32:17 +01:00
Optional < int > ComputedProperties : : z_index ( ) const
2020-06-15 17:29:35 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ZIndex ) ;
2024-11-03 13:20:04 +01:00
if ( value . has_auto ( ) )
2022-03-04 15:01:01 +01:00
return { } ;
2025-05-20 12:06:56 +02:00
// Clamp z-index to the range of a signed 32-bit integer for consistency with other engines.
2025-08-08 00:13:15 +12:00
if ( value . is_integer ( ) ) {
auto number = value . as_integer ( ) . integer ( ) ;
2025-05-20 12:06:56 +02:00
if ( number > = NumericLimits < int > : : max ( ) )
2023-04-26 07:19:07 +02:00
return NumericLimits < int > : : max ( ) ;
2025-05-20 12:06:56 +02:00
if ( number < = NumericLimits < int > : : min ( ) )
2023-04-26 07:19:07 +02:00
return NumericLimits < int > : : min ( ) ;
2025-05-20 12:06:56 +02:00
2025-08-08 00:13:15 +12:00
return value . as_integer ( ) . integer ( ) ;
2025-05-20 12:06:56 +02:00
}
2025-08-08 00:13:15 +12:00
2025-05-20 12:06:56 +02:00
if ( value . is_calculated ( ) ) {
2025-09-24 14:16:48 +01:00
auto maybe_double = value . as_calculated ( ) . resolve_number ( { } ) ;
2025-05-20 12:06:56 +02:00
if ( maybe_double . has_value ( ) ) {
2025-08-08 00:13:15 +12:00
if ( * maybe_double > = NumericLimits < int > : : max ( ) )
return NumericLimits < int > : : max ( ) ;
if ( * maybe_double < = NumericLimits < int > : : min ( ) )
return NumericLimits < int > : : min ( ) ;
2025-08-01 15:07:07 +12:00
// Round up on half
2025-08-08 00:13:15 +12:00
return floor ( maybe_double . value ( ) + 0.5f ) ;
2025-05-20 12:06:56 +02:00
}
2023-04-26 07:19:07 +02:00
}
2021-09-10 20:37:09 +01:00
return { } ;
2020-06-15 17:29:35 +02:00
}
2024-12-20 11:32:17 +01:00
float ComputedProperties : : opacity ( ) const
2023-05-19 20:35:39 +01:00
{
2025-09-08 00:05:45 +12:00
return property ( PropertyID : : Opacity ) . as_number ( ) . number ( ) ;
2023-05-19 20:35:39 +01:00
}
2024-12-20 11:32:17 +01:00
float ComputedProperties : : fill_opacity ( ) const
2023-05-19 20:35:39 +01:00
{
2025-09-08 00:10:21 +12:00
return property ( PropertyID : : FillOpacity ) . as_number ( ) . number ( ) ;
2023-05-19 20:35:39 +01:00
}
2025-02-05 12:55:02 +00:00
StrokeLinecap ComputedProperties : : stroke_linecap ( ) const
2024-10-10 10:15:49 -04:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : StrokeLinecap ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_stroke_linecap ( value . to_keyword ( ) ) . release_value ( ) ;
2024-10-10 10:15:49 -04:00
}
2025-02-05 12:55:02 +00:00
StrokeLinejoin ComputedProperties : : stroke_linejoin ( ) const
2024-10-28 20:51:16 -04:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : StrokeLinejoin ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_stroke_linejoin ( value . to_keyword ( ) ) . release_value ( ) ;
2024-10-28 20:51:16 -04:00
}
2025-11-09 16:28:32 +13:00
double ComputedProperties : : stroke_miterlimit ( ) const
2024-10-28 20:51:16 -04:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : StrokeMiterlimit ) ;
2024-10-28 20:51:16 -04:00
2024-12-11 15:05:56 +00:00
if ( value . is_calculated ( ) ) {
2025-11-09 16:28:32 +13:00
return value . as_calculated ( ) . resolve_number ( { } ) . value ( ) ;
2024-10-28 20:51:16 -04:00
}
2025-11-09 16:28:32 +13:00
return value . as_number ( ) . number ( ) ;
2024-10-28 20:51:16 -04:00
}
2024-12-20 11:32:17 +01:00
float ComputedProperties : : stroke_opacity ( ) const
2023-05-19 20:35:39 +01:00
{
2025-09-08 00:14:40 +12:00
return property ( PropertyID : : StrokeOpacity ) . as_number ( ) . number ( ) ;
2023-05-19 20:35:39 +01:00
}
2024-12-20 11:32:17 +01:00
float ComputedProperties : : stop_opacity ( ) const
2023-05-19 20:35:39 +01:00
{
2025-09-08 00:13:13 +12:00
return property ( PropertyID : : StopOpacity ) . as_number ( ) . number ( ) ;
2023-05-19 20:35:39 +01:00
}
2025-02-05 12:55:02 +00:00
FillRule ComputedProperties : : fill_rule ( ) const
2023-06-11 16:43:46 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FillRule ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_fill_rule ( value . to_keyword ( ) ) . release_value ( ) ;
2023-06-11 16:43:46 +01:00
}
2025-02-05 12:55:02 +00:00
ClipRule ComputedProperties : : clip_rule ( ) const
2024-05-12 20:19:43 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ClipRule ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_fill_rule ( value . to_keyword ( ) ) . release_value ( ) ;
2024-05-12 20:19:43 +01:00
}
2025-07-06 21:48:55 +02:00
float ComputedProperties : : flood_opacity ( ) const
{
2025-09-08 00:16:12 +12:00
return property ( PropertyID : : FloodOpacity ) . as_number ( ) . number ( ) ;
2025-07-06 21:48:55 +02:00
}
2025-02-05 12:55:02 +00:00
FlexDirection ComputedProperties : : flex_direction ( ) const
2021-01-18 17:41:57 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FlexDirection ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_flex_direction ( value . to_keyword ( ) ) . release_value ( ) ;
2021-01-18 17:41:57 +01:00
}
2025-02-05 12:55:02 +00:00
FlexWrap ComputedProperties : : flex_wrap ( ) const
2021-05-30 12:11:32 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FlexWrap ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_flex_wrap ( value . to_keyword ( ) ) . release_value ( ) ;
2021-05-30 12:11:32 +02:00
}
2025-02-05 12:55:02 +00:00
FlexBasis ComputedProperties : : flex_basis ( ) const
2021-05-30 14:23:43 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FlexBasis ) ;
2021-05-30 14:23:43 +02:00
2025-02-05 12:57:41 +00:00
if ( value . is_keyword ( ) & & value . to_keyword ( ) = = Keyword : : Content )
return FlexBasisContent { } ;
2021-05-30 14:23:43 +02:00
2025-02-05 12:57:41 +00:00
return size_value ( PropertyID : : FlexBasis ) ;
2021-05-30 14:23:43 +02:00
}
2024-12-20 11:32:17 +01:00
float ComputedProperties : : flex_grow ( ) const
2021-05-30 20:22:25 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FlexGrow ) ;
2024-11-03 13:20:04 +01:00
if ( ! value . is_number ( ) )
2021-10-19 15:22:08 +02:00
return 0 ;
2024-11-03 13:20:04 +01:00
return value . as_number ( ) . number ( ) ;
2021-05-30 20:22:25 +02:00
}
2024-12-20 11:32:17 +01:00
float ComputedProperties : : flex_shrink ( ) const
2021-05-30 20:22:25 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FlexShrink ) ;
2024-11-03 13:20:04 +01:00
if ( ! value . is_number ( ) )
2021-10-19 15:22:08 +02:00
return 1 ;
2024-11-03 13:20:04 +01:00
return value . as_number ( ) . number ( ) ;
2021-05-30 20:22:25 +02:00
}
2021-10-19 15:22:08 +02:00
2024-12-20 11:32:17 +01:00
int ComputedProperties : : order ( ) const
2022-03-31 22:11:38 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Order ) ;
2024-11-03 13:20:04 +01:00
if ( ! value . is_integer ( ) )
2022-03-31 22:11:38 +02:00
return 0 ;
2024-11-03 13:20:04 +01:00
return value . as_integer ( ) . integer ( ) ;
2022-03-31 22:11:38 +02:00
}
2025-02-05 12:55:02 +00:00
ImageRendering ComputedProperties : : image_rendering ( ) const
2022-02-18 12:21:27 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ImageRendering ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_image_rendering ( value . to_keyword ( ) ) . release_value ( ) ;
2022-02-18 12:21:27 +01:00
}
2025-02-05 12:57:41 +00:00
Length ComputedProperties : : border_spacing_horizontal ( Layout : : Node const & layout_node ) const
2023-06-16 02:35:03 +00:00
{
2025-03-21 01:24:34 +00:00
auto resolve_value = [ & ] ( auto const & style_value ) - > Optional < Length > {
if ( style_value . is_length ( ) )
return style_value . as_length ( ) . length ( ) ;
if ( style_value . is_calculated ( ) )
2025-09-24 14:33:18 +01:00
return style_value . as_calculated ( ) . resolve_length ( { . length_resolution_context = Length : : ResolutionContext : : for_layout_node ( layout_node ) } ) . value_or ( Length : : make_px ( 0 ) ) ;
2025-03-21 01:24:34 +00:00
return { } ;
} ;
auto const & style_value = property ( PropertyID : : BorderSpacing ) ;
auto resolved_value = resolve_value ( style_value ) ;
if ( ! resolved_value . has_value ( ) ) {
auto const & list = style_value . as_value_list ( ) ;
VERIFY ( list . size ( ) > 0 ) ;
resolved_value = resolve_value ( * list . value_at ( 0 , false ) ) ;
}
VERIFY ( resolved_value . has_value ( ) ) ;
return * resolved_value ;
2023-06-16 02:35:03 +00:00
}
2025-02-05 12:57:41 +00:00
Length ComputedProperties : : border_spacing_vertical ( Layout : : Node const & layout_node ) const
2023-06-16 02:35:03 +00:00
{
2025-03-21 01:24:34 +00:00
auto resolve_value = [ & ] ( auto const & style_value ) - > Optional < Length > {
if ( style_value . is_length ( ) )
return style_value . as_length ( ) . length ( ) ;
if ( style_value . is_calculated ( ) )
2025-09-24 14:33:18 +01:00
return style_value . as_calculated ( ) . resolve_length ( { . length_resolution_context = Length : : ResolutionContext : : for_layout_node ( layout_node ) } ) . value_or ( Length : : make_px ( 0 ) ) ;
2025-03-21 01:24:34 +00:00
return { } ;
} ;
auto const & style_value = property ( PropertyID : : BorderSpacing ) ;
auto resolved_value = resolve_value ( style_value ) ;
if ( ! resolved_value . has_value ( ) ) {
auto const & list = style_value . as_value_list ( ) ;
VERIFY ( list . size ( ) > 1 ) ;
resolved_value = resolve_value ( * list . value_at ( 1 , false ) ) ;
}
VERIFY ( resolved_value . has_value ( ) ) ;
return * resolved_value ;
2023-06-16 02:35:03 +00:00
}
2025-02-05 12:55:02 +00:00
CaptionSide ComputedProperties : : caption_side ( ) const
2023-06-07 02:10:55 +00:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : CaptionSide ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_caption_side ( value . to_keyword ( ) ) . release_value ( ) ;
2023-06-07 02:10:55 +00:00
}
2025-02-05 12:57:41 +00:00
Clip ComputedProperties : : clip ( ) const
2022-07-31 18:47:09 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Clip ) ;
2024-11-03 13:20:04 +01:00
if ( ! value . is_rect ( ) )
2025-02-05 12:57:41 +00:00
return Clip : : make_auto ( ) ;
return Clip ( value . as_rect ( ) . rect ( ) ) ;
2022-07-31 18:47:09 +02:00
}
2025-02-05 12:55:02 +00:00
JustifyContent ComputedProperties : : justify_content ( ) const
2021-07-16 18:38:26 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : JustifyContent ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_justify_content ( value . to_keyword ( ) ) . release_value ( ) ;
2021-07-16 18:38:26 +02:00
}
2021-05-30 20:22:25 +02:00
2025-02-05 12:55:02 +00:00
JustifyItems ComputedProperties : : justify_items ( ) const
2023-07-14 20:49:22 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : JustifyItems ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_justify_items ( value . to_keyword ( ) ) . release_value ( ) ;
2023-07-14 20:49:22 +02:00
}
2025-02-05 12:55:02 +00:00
JustifySelf ComputedProperties : : justify_self ( ) const
2023-07-14 14:41:22 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : JustifySelf ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_justify_self ( value . to_keyword ( ) ) . release_value ( ) ;
2023-07-14 14:41:22 +02:00
}
2025-08-08 10:11:51 +01:00
Vector < Transformation > ComputedProperties : : transformations_for_style_value ( StyleValue const & value )
2021-09-18 17:20:00 +02:00
{
2025-02-05 12:57:41 +00:00
if ( value . is_keyword ( ) & & value . to_keyword ( ) = = Keyword : : None )
2021-09-18 17:20:00 +02:00
return { } ;
2024-01-06 18:05:21 +01:00
if ( ! value . is_value_list ( ) )
2021-09-18 17:20:00 +02:00
return { } ;
2024-01-06 18:05:21 +01:00
auto & list = value . as_value_list ( ) ;
2021-09-18 17:20:00 +02:00
2025-01-15 16:03:06 +00:00
Vector < Transformation > transformations ;
2021-09-18 17:20:00 +02:00
for ( auto & it : list . values ( ) ) {
2023-03-06 14:33:11 +01:00
if ( ! it - > is_transformation ( ) )
2021-09-18 17:20:00 +02:00
return { } ;
2025-01-15 16:03:06 +00:00
transformations . append ( it - > as_transformation ( ) . to_transformation ( ) ) ;
2021-09-18 17:20:00 +02:00
}
return transformations ;
}
2025-02-05 12:57:41 +00:00
Vector < Transformation > ComputedProperties : : transformations ( ) const
2024-01-06 18:05:21 +01:00
{
2025-02-05 12:57:41 +00:00
return transformations_for_style_value ( property ( PropertyID : : Transform ) ) ;
2024-01-06 18:05:21 +01:00
}
2025-01-15 17:21:22 +00:00
Optional < Transformation > ComputedProperties : : rotate ( ) const
2024-10-16 08:50:35 +02:00
{
2025-01-15 17:21:22 +00:00
auto const & value = property ( PropertyID : : Rotate ) ;
if ( ! value . is_transformation ( ) )
2024-10-16 08:50:35 +02:00
return { } ;
2025-01-15 17:21:22 +00:00
return value . as_transformation ( ) . to_transformation ( ) ;
2024-10-16 08:50:35 +02:00
}
2025-01-15 16:48:44 +00:00
Optional < Transformation > ComputedProperties : : translate ( ) const
2024-11-22 16:42:20 +01:00
{
2025-01-15 16:48:44 +00:00
auto const & value = property ( PropertyID : : Translate ) ;
if ( ! value . is_transformation ( ) )
2024-11-22 16:42:20 +01:00
return { } ;
2025-01-15 16:48:44 +00:00
return value . as_transformation ( ) . to_transformation ( ) ;
2024-11-22 16:42:20 +01:00
}
2025-01-15 14:58:23 +00:00
Optional < Transformation > ComputedProperties : : scale ( ) const
2024-11-22 18:07:16 +01:00
{
2025-01-15 14:58:23 +00:00
auto const & value = property ( PropertyID : : Scale ) ;
if ( ! value . is_transformation ( ) )
2024-11-22 18:07:16 +01:00
return { } ;
2025-01-15 14:58:23 +00:00
return value . as_transformation ( ) . to_transformation ( ) ;
2024-11-22 18:07:16 +01:00
}
2025-02-05 12:55:02 +00:00
TransformBox ComputedProperties : : transform_box ( ) const
2024-01-25 17:02:37 +00:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TransformBox ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_transform_box ( value . to_keyword ( ) ) . release_value ( ) ;
2024-01-25 17:02:37 +00:00
}
2025-11-10 15:10:38 +01:00
Optional < CSSPixels > ComputedProperties : : perspective ( ) const
{
auto const & value = property ( PropertyID : : Perspective ) ;
if ( value . is_keyword ( ) & & value . to_keyword ( ) = = Keyword : : None )
return { } ;
if ( value . is_length ( ) )
return value . as_length ( ) . length ( ) . absolute_length_to_px ( ) ;
if ( value . is_calculated ( ) )
return value . as_calculated ( ) . resolve_length ( { . length_resolution_context = { } } ) - > absolute_length_to_px ( ) ;
VERIFY_NOT_REACHED ( ) ;
}
2025-02-05 12:57:41 +00:00
TransformOrigin ComputedProperties : : transform_origin ( ) const
2022-03-21 19:38:00 +01:00
{
2025-10-02 16:18:48 +13:00
auto length_percentage_with_keywords_resolved = [ ] ( StyleValue const & value ) - > LengthPercentage {
2025-06-14 00:47:59 +01:00
if ( value . is_keyword ( ) ) {
auto keyword = value . to_keyword ( ) ;
if ( keyword = = Keyword : : Left | | keyword = = Keyword : : Top )
return Percentage ( 0 ) ;
if ( keyword = = Keyword : : Center )
return Percentage ( 50 ) ;
if ( keyword = = Keyword : : Right | | keyword = = Keyword : : Bottom )
return Percentage ( 100 ) ;
VERIFY_NOT_REACHED ( ) ;
}
2025-10-02 16:18:48 +13:00
return LengthPercentage : : from_style_value ( value ) ;
2025-06-14 00:47:59 +01:00
} ;
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TransformOrigin ) ;
2025-06-14 00:52:42 +01:00
if ( ! value . is_value_list ( ) | | value . as_value_list ( ) . size ( ) ! = 3 )
2022-03-21 19:38:00 +01:00
return { } ;
2024-11-03 13:20:04 +01:00
auto const & list = value . as_value_list ( ) ;
2025-06-14 00:47:59 +01:00
auto x_value = length_percentage_with_keywords_resolved ( list . values ( ) [ 0 ] ) ;
auto y_value = length_percentage_with_keywords_resolved ( list . values ( ) [ 1 ] ) ;
2025-10-02 16:18:48 +13:00
auto z_value = LengthPercentage : : from_style_value ( list . values ( ) [ 2 ] ) ;
return { x_value , y_value , z_value } ;
2022-03-21 19:38:00 +01:00
}
2025-11-11 17:08:51 +01:00
TransformStyle ComputedProperties : : transform_style ( ) const
{
auto const & value = property ( PropertyID : : TransformStyle ) ;
return keyword_to_transform_style ( value . to_keyword ( ) ) . release_value ( ) ;
}
2024-12-20 11:32:17 +01:00
Optional < Color > ComputedProperties : : accent_color ( Layout : : NodeWithStyle const & node ) const
2023-03-18 20:49:00 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : AccentColor ) ;
2024-11-03 13:20:04 +01:00
if ( value . has_color ( ) )
2025-07-19 11:31:07 +12:00
return value . to_color ( ColorResolutionContext : : for_layout_node_with_style ( node ) ) ;
2023-03-18 20:49:00 +01:00
return { } ;
}
2025-02-05 12:55:02 +00:00
AlignContent ComputedProperties : : align_content ( ) const
2022-10-14 13:50:06 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : AlignContent ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_align_content ( value . to_keyword ( ) ) . release_value ( ) ;
2022-10-14 13:50:06 +02:00
}
2025-02-05 12:55:02 +00:00
AlignItems ComputedProperties : : align_items ( ) const
2021-09-15 18:27:20 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : AlignItems ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_align_items ( value . to_keyword ( ) ) . release_value ( ) ;
2021-09-15 18:27:20 +02:00
}
2025-02-05 12:55:02 +00:00
AlignSelf ComputedProperties : : align_self ( ) const
2022-07-11 23:52:36 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : AlignSelf ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_align_self ( value . to_keyword ( ) ) . release_value ( ) ;
2022-07-11 23:52:36 +02:00
}
2025-02-05 12:55:02 +00:00
Appearance ComputedProperties : : appearance ( ) const
2022-07-22 16:05:11 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Appearance ) ;
2025-02-05 12:55:02 +00:00
auto appearance = keyword_to_appearance ( value . to_keyword ( ) ) . release_value ( ) ;
switch ( appearance ) {
// Note: All these compatibility values can be treated as 'auto'
2025-02-05 12:57:41 +00:00
case Appearance : : Textfield :
case Appearance : : MenulistButton :
case Appearance : : Searchfield :
case Appearance : : Textarea :
case Appearance : : PushButton :
case Appearance : : SliderHorizontal :
case Appearance : : Checkbox :
case Appearance : : Radio :
case Appearance : : SquareButton :
case Appearance : : Menulist :
case Appearance : : Listbox :
case Appearance : : Meter :
case Appearance : : ProgressBar :
case Appearance : : Button :
appearance = Appearance : : Auto ;
2025-02-05 12:55:02 +00:00
break ;
default :
break ;
2022-07-22 16:05:11 +01:00
}
return appearance ;
}
2025-02-05 12:57:41 +00:00
Filter ComputedProperties : : backdrop_filter ( ) const
2022-09-15 08:31:19 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : BackdropFilter ) ;
2024-11-03 13:20:04 +01:00
if ( value . is_filter_value_list ( ) )
return Filter ( value . as_filter_value_list ( ) ) ;
2024-10-25 11:00:22 +02:00
return Filter : : make_none ( ) ;
}
2025-02-05 12:57:41 +00:00
Filter ComputedProperties : : filter ( ) const
2024-10-25 11:00:22 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Filter ) ;
2024-11-03 13:20:04 +01:00
if ( value . is_filter_value_list ( ) )
return Filter ( value . as_filter_value_list ( ) ) ;
2024-10-25 11:00:22 +02:00
return Filter : : make_none ( ) ;
2022-09-15 08:31:19 +01:00
}
2025-02-05 12:55:02 +00:00
Positioning ComputedProperties : : position ( ) const
2020-03-23 17:29:15 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Position ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_positioning ( value . to_keyword ( ) ) . release_value ( ) ;
2020-03-23 17:29:15 +01:00
}
2024-12-20 11:32:17 +01:00
bool ComputedProperties : : operator = = ( ComputedProperties const & other ) const
2019-10-14 18:32:02 +02:00
{
2024-12-20 16:35:12 +01:00
for ( size_t i = 0 ; i < m_property_values . size ( ) ; + + i ) {
auto const & my_style = m_property_values [ i ] ;
auto const & other_style = other . m_property_values [ i ] ;
2024-08-02 13:59:19 +02:00
if ( ! my_style ) {
if ( other_style )
2022-02-18 20:21:49 +01:00
return false ;
continue ;
}
2024-08-02 13:59:19 +02:00
if ( ! other_style )
2019-10-14 18:32:02 +02:00
return false ;
2024-08-02 13:59:19 +02:00
auto const & my_value = * my_style ;
auto const & other_value = * other_style ;
2019-10-14 18:32:02 +02:00
if ( my_value . type ( ) ! = other_value . type ( ) )
return false ;
2020-12-14 15:56:01 +01:00
if ( my_value ! = other_value )
2019-10-14 18:32:02 +02:00
return false ;
}
return true ;
}
2020-03-07 10:27:02 +01:00
2025-02-05 12:55:02 +00:00
TextAnchor ComputedProperties : : text_anchor ( ) const
2023-07-19 19:12:00 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TextAnchor ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_text_anchor ( value . to_keyword ( ) ) . release_value ( ) ;
2023-07-19 19:12:00 +01:00
}
2025-02-05 12:55:02 +00:00
TextAlign ComputedProperties : : text_align ( ) const
2020-06-13 10:54:58 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TextAlign ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_text_align ( value . to_keyword ( ) ) . release_value ( ) ;
2022-03-12 19:31:32 +00:00
}
2025-02-05 12:55:02 +00:00
TextJustify ComputedProperties : : text_justify ( ) const
2022-03-12 19:31:32 +00:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TextJustify ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_text_justify ( value . to_keyword ( ) ) . release_value ( ) ;
2020-06-13 10:54:58 +02:00
}
2025-02-05 12:55:02 +00:00
TextOverflow ComputedProperties : : text_overflow ( ) const
2024-07-24 20:58:17 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TextOverflow ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_text_overflow ( value . to_keyword ( ) ) . release_value ( ) ;
2024-07-24 20:58:17 +02:00
}
2025-06-27 07:03:05 +01:00
TextRendering ComputedProperties : : text_rendering ( ) const
{
auto const & value = property ( PropertyID : : TextRendering ) ;
return keyword_to_text_rendering ( value . to_keyword ( ) ) . release_value ( ) ;
}
2025-09-12 12:21:16 +12:00
CSSPixels ComputedProperties : : text_underline_offset ( ) const
{
auto const & computed_text_underline_offset = property ( PropertyID : : TextUnderlineOffset ) ;
// auto
if ( computed_text_underline_offset . to_keyword ( ) = = Keyword : : Auto )
return InitialValues : : text_underline_offset ( ) ;
// <length>
if ( computed_text_underline_offset . is_length ( ) )
return computed_text_underline_offset . as_length ( ) . length ( ) . absolute_length_to_px ( ) ;
// <percentage>
if ( computed_text_underline_offset . is_percentage ( ) )
return font_size ( ) . scaled ( computed_text_underline_offset . as_percentage ( ) . percentage ( ) . as_fraction ( ) ) ;
// NOTE: We also support calc()'d <length-percentage>
if ( computed_text_underline_offset . is_calculated ( ) )
// NOTE: We don't need to pass a length resolution context here as lengths have already been absolutized in
// StyleComputer::compute_text_underline_offset
return computed_text_underline_offset . as_calculated ( ) . resolve_length ( { . percentage_basis = Length : : make_px ( font_size ( ) ) , . length_resolution_context = { } } ) - > absolute_length_to_px ( ) ;
VERIFY_NOT_REACHED ( ) ;
}
2025-09-14 16:04:26 +12:00
TextUnderlinePosition ComputedProperties : : text_underline_position ( ) const
{
auto const & computed_text_underline_position = property ( PropertyID : : TextUnderlinePosition ) . as_text_underline_position ( ) ;
return {
. horizontal = computed_text_underline_position . horizontal ( ) ,
. vertical = computed_text_underline_position . vertical ( )
} ;
}
2025-02-05 12:55:02 +00:00
PointerEvents ComputedProperties : : pointer_events ( ) const
2021-10-05 19:47:13 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : PointerEvents ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_pointer_events ( value . to_keyword ( ) ) . release_value ( ) ;
2021-10-05 19:47:13 +01:00
}
2025-11-09 16:28:32 +13:00
Variant < Length , double > ComputedProperties : : tab_size ( ) const
2024-10-01 13:07:06 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TabSize ) ;
2024-12-11 15:05:56 +00:00
if ( value . is_calculated ( ) ) {
auto const & math_value = value . as_calculated ( ) ;
2024-10-01 13:07:06 +01:00
if ( math_value . resolves_to_length ( ) ) {
2025-11-09 16:28:32 +13:00
return math_value . resolve_length ( { } ) . value ( ) ;
2024-10-01 13:07:06 +01:00
}
if ( math_value . resolves_to_number ( ) ) {
2025-11-09 16:28:32 +13:00
return math_value . resolve_number ( { } ) . value ( ) ;
2024-10-01 13:07:06 +01:00
}
}
2024-11-03 13:20:04 +01:00
if ( value . is_length ( ) )
2025-11-09 16:28:32 +13:00
return value . as_length ( ) . length ( ) ;
2024-10-01 13:07:06 +01:00
2025-11-09 16:28:32 +13:00
return value . as_number ( ) . number ( ) ;
2024-10-01 13:07:06 +01:00
}
2025-02-05 12:55:02 +00:00
WordBreak ComputedProperties : : word_break ( ) const
2024-10-25 16:47:05 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : WordBreak ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_word_break ( value . to_keyword ( ) ) . release_value ( ) ;
2024-10-25 16:47:05 +01:00
}
2025-09-09 16:59:30 +01:00
CSSPixels ComputedProperties : : word_spacing ( ) const
2024-10-18 21:00:28 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : WordSpacing ) ;
2025-08-02 00:07:48 +12:00
if ( value . is_keyword ( ) & & value . to_keyword ( ) = = Keyword : : Normal )
2025-09-09 16:59:30 +01:00
return 0 ;
2024-10-18 21:00:28 +01:00
2024-11-03 13:20:04 +01:00
if ( value . is_length ( ) )
2025-09-09 16:59:30 +01:00
return value . as_length ( ) . length ( ) . absolute_length_to_px ( ) ;
2024-10-18 21:00:28 +01:00
2025-08-02 00:07:48 +12:00
if ( value . is_percentage ( ) )
2025-09-09 16:59:30 +01:00
return font_size ( ) . scale_by ( value . as_percentage ( ) . percentage ( ) . as_fraction ( ) ) ;
2025-08-02 00:07:48 +12:00
if ( value . is_calculated ( ) )
2025-09-09 16:59:30 +01:00
return value . as_calculated ( ) . resolve_length ( { . percentage_basis = Length : : make_px ( font_size ( ) ) , . length_resolution_context = { } } ) - > absolute_length_to_px ( ) ;
2025-08-02 00:07:48 +12:00
VERIFY_NOT_REACHED ( ) ;
2024-10-18 21:00:28 +01:00
}
2025-05-16 18:32:31 +12:00
WhiteSpaceCollapse ComputedProperties : : white_space_collapse ( ) const
{
auto const & value = property ( PropertyID : : WhiteSpaceCollapse ) ;
return keyword_to_white_space_collapse ( value . to_keyword ( ) ) . release_value ( ) ;
}
2025-05-18 02:21:42 +12:00
WhiteSpaceTrimData ComputedProperties : : white_space_trim ( ) const
{
auto const & value = property ( PropertyID : : WhiteSpaceTrim ) ;
if ( value . is_keyword ( ) & & value . to_keyword ( ) = = Keyword : : None )
return WhiteSpaceTrimData { } ;
if ( value . is_value_list ( ) ) {
auto white_space_trim_data = WhiteSpaceTrimData { } ;
for ( auto const & value : value . as_value_list ( ) . values ( ) ) {
switch ( value - > as_keyword ( ) . keyword ( ) ) {
case Keyword : : DiscardBefore :
white_space_trim_data . discard_before = true ;
break ;
case Keyword : : DiscardAfter :
white_space_trim_data . discard_after = true ;
break ;
case Keyword : : DiscardInner :
white_space_trim_data . discard_inner = true ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
return white_space_trim_data ;
}
VERIFY_NOT_REACHED ( ) ;
}
2025-09-09 09:56:53 +01:00
CSSPixels ComputedProperties : : letter_spacing ( ) const
2024-10-22 14:21:20 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : LetterSpacing ) ;
2025-09-09 09:56:53 +01:00
if ( value . is_keyword ( ) & & value . to_keyword ( ) = = Keyword : : Normal )
return 0 ;
2024-10-22 14:21:20 +01:00
2024-11-03 13:20:04 +01:00
if ( value . is_length ( ) )
2025-09-09 09:56:53 +01:00
return value . as_length ( ) . length ( ) . absolute_length_to_px ( ) ;
2024-10-22 14:21:20 +01:00
2025-09-09 09:56:53 +01:00
if ( value . is_percentage ( ) )
return font_size ( ) . scale_by ( value . as_percentage ( ) . percentage ( ) . as_fraction ( ) ) ;
if ( value . is_calculated ( ) )
return value . as_calculated ( ) . resolve_length ( { . percentage_basis = Length : : make_px ( font_size ( ) ) , . length_resolution_context = { } } ) - > absolute_length_to_px ( ) ;
VERIFY_NOT_REACHED ( ) ;
2024-10-22 14:21:20 +01:00
}
2025-02-05 12:57:41 +00:00
LineStyle ComputedProperties : : line_style ( PropertyID property_id ) const
2020-12-04 16:11:55 +01:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( property_id ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_line_style ( value . to_keyword ( ) ) . release_value ( ) ;
2023-08-02 17:24:14 +01:00
}
2025-02-05 12:55:02 +00:00
OutlineStyle ComputedProperties : : outline_style ( ) const
2023-08-02 17:24:14 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : OutlineStyle ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_outline_style ( value . to_keyword ( ) ) . release_value ( ) ;
2020-12-04 16:11:55 +01:00
}
2025-02-05 12:55:02 +00:00
Float ComputedProperties : : float_ ( ) const
2020-06-26 15:08:42 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Float ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_float ( value . to_keyword ( ) ) . release_value ( ) ;
2020-06-26 15:08:42 +02:00
}
2025-03-09 13:59:33 +00:00
Color ComputedProperties : : caret_color ( Layout : : NodeWithStyle const & node ) const
{
auto const & value = property ( PropertyID : : CaretColor ) ;
if ( value . is_keyword ( ) & & value . to_keyword ( ) = = Keyword : : Auto )
return node . computed_values ( ) . color ( ) ;
if ( value . has_color ( ) )
2025-07-19 11:31:07 +12:00
return value . to_color ( ColorResolutionContext : : for_layout_node_with_style ( node ) ) . value ( ) ;
2025-03-09 13:59:33 +00:00
return InitialValues : : caret_color ( ) ;
}
2025-02-05 12:55:02 +00:00
Clear ComputedProperties : : clear ( ) const
2020-12-06 01:45:51 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Clear ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_clear ( value . to_keyword ( ) ) . release_value ( ) ;
2020-12-06 01:45:51 +01:00
}
2025-02-05 12:55:02 +00:00
ColumnSpan ComputedProperties : : column_span ( ) const
2024-08-20 20:23:55 -04:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ColumnSpan ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_column_span ( value . to_keyword ( ) ) . release_value ( ) ;
2024-08-20 20:23:55 -04:00
}
2025-06-17 16:33:23 +01:00
ComputedProperties : : ContentDataAndQuoteNestingLevel ComputedProperties : : content ( DOM : : AbstractElement & element_reference , u32 initial_quote_nesting_level ) const
2022-02-24 16:52:58 +00:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Content ) ;
2023-09-12 14:22:46 +01:00
auto quotes_data = quotes ( ) ;
2023-09-18 15:41:17 +01:00
auto quote_nesting_level = initial_quote_nesting_level ;
2023-09-12 14:22:46 +01:00
auto get_quote_string = [ & ] ( bool open , auto depth ) {
switch ( quotes_data . type ) {
case QuotesData : : Type : : None :
2024-07-17 12:42:12 +01:00
return FlyString { } ;
2023-09-12 14:22:46 +01:00
case QuotesData : : Type : : Auto :
// FIXME: "A typographically appropriate used value for quotes is automatically chosen by the UA
// based on the content language of the element and/or its parent."
if ( open )
2024-07-17 12:42:12 +01:00
return depth = = 0 ? " “ " _fly_string : " ‘ " _fly_string ;
return depth = = 0 ? " ” " _fly_string : " ’ " _fly_string ;
2023-09-12 14:22:46 +01:00
case QuotesData : : Type : : Specified :
2023-09-18 15:28:40 +01:00
// If the depth is greater than the number of pairs, the last pair is repeated.
auto & level = quotes_data . strings [ min ( depth , quotes_data . strings . size ( ) - 1 ) ] ;
2023-09-12 14:22:46 +01:00
return open ? level [ 0 ] : level [ 1 ] ;
}
VERIFY_NOT_REACHED ( ) ;
} ;
2024-11-03 13:20:04 +01:00
if ( value . is_content ( ) ) {
auto & content_style_value = value . as_content ( ) ;
2022-02-24 16:52:58 +00:00
2025-02-05 12:57:41 +00:00
ContentData content_data ;
2022-02-24 16:52:58 +00:00
for ( auto const & item : content_style_value . content ( ) . values ( ) ) {
2023-03-06 14:33:11 +01:00
if ( item - > is_string ( ) ) {
2025-07-27 15:55:16 +02:00
content_data . data . append ( item - > as_string ( ) . string_value ( ) . to_string ( ) ) ;
2024-08-14 11:46:56 +01:00
} else if ( item - > is_keyword ( ) ) {
2024-08-14 14:06:03 +01:00
switch ( item - > to_keyword ( ) ) {
case Keyword : : OpenQuote :
2025-07-27 15:55:16 +02:00
content_data . data . append ( get_quote_string ( true , quote_nesting_level + + ) . to_string ( ) ) ;
2023-09-12 14:22:46 +01:00
break ;
2024-08-14 14:06:03 +01:00
case Keyword : : CloseQuote :
2023-09-18 15:41:17 +01:00
// A 'close-quote' or 'no-close-quote' that would make the depth negative is in error and is ignored
// (at rendering time): the depth stays at 0 and no quote mark is rendered (although the rest of the
// 'content' property's value is still inserted).
// - https://www.w3.org/TR/CSS21/generate.html#quotes-insert
// (This is missing from the CONTENT-3 spec.)
if ( quote_nesting_level > 0 )
2025-07-27 15:55:16 +02:00
content_data . data . append ( get_quote_string ( false , - - quote_nesting_level ) . to_string ( ) ) ;
2023-09-12 14:22:46 +01:00
break ;
2024-08-14 14:06:03 +01:00
case Keyword : : NoOpenQuote :
2023-09-18 15:41:17 +01:00
quote_nesting_level + + ;
2023-09-12 14:22:46 +01:00
break ;
2024-08-14 14:06:03 +01:00
case Keyword : : NoCloseQuote :
2023-09-18 15:41:17 +01:00
// NOTE: See CloseQuote
if ( quote_nesting_level > 0 )
quote_nesting_level - - ;
2023-09-12 14:22:46 +01:00
break ;
default :
2025-05-16 19:20:24 +01:00
dbgln ( " `{}` is not supported in `content` (yet?) " , item - > to_string ( SerializationMode : : Normal ) ) ;
2023-09-12 14:22:46 +01:00
break ;
}
2024-07-18 20:29:02 +01:00
} else if ( item - > is_counter ( ) ) {
2025-07-27 15:55:16 +02:00
content_data . data . append ( item - > as_counter ( ) . resolve ( element_reference ) ) ;
} else if ( item - > is_image ( ) ) {
content_data . data . append ( NonnullRefPtr { const_cast < ImageStyleValue & > ( item - > as_image ( ) ) } ) ;
2022-02-24 16:52:58 +00:00
} else {
2024-07-18 20:29:02 +01:00
// TODO: Implement images, and other things.
2025-05-16 19:20:24 +01:00
dbgln ( " `{}` is not supported in `content` (yet?) " , item - > to_string ( SerializationMode : : Normal ) ) ;
2022-02-24 16:52:58 +00:00
}
}
2025-07-27 15:55:16 +02:00
content_data . type = ContentData : : Type : : List ;
2022-02-24 16:52:58 +00:00
if ( content_style_value . has_alt_text ( ) ) {
StringBuilder alt_text_builder ;
for ( auto const & item : content_style_value . alt_text ( ) - > values ( ) ) {
2023-03-06 14:33:11 +01:00
if ( item - > is_string ( ) ) {
2023-09-12 11:33:11 +01:00
alt_text_builder . append ( item - > as_string ( ) . string_value ( ) ) ;
2024-07-18 20:29:02 +01:00
} else if ( item - > is_counter ( ) ) {
2025-06-17 16:33:23 +01:00
alt_text_builder . append ( item - > as_counter ( ) . resolve ( element_reference ) ) ;
2022-02-24 16:52:58 +00:00
} else {
2025-05-16 19:20:24 +01:00
dbgln ( " `{}` is not supported in `content` alt-text (yet?) " , item - > to_string ( SerializationMode : : Normal ) ) ;
2022-02-24 16:52:58 +00:00
}
}
2023-09-12 14:22:46 +01:00
content_data . alt_text = MUST ( alt_text_builder . to_string ( ) ) ;
2022-02-24 16:52:58 +00:00
}
2023-09-18 15:41:17 +01:00
return { content_data , quote_nesting_level } ;
2022-02-24 16:52:58 +00:00
}
2024-11-03 13:20:04 +01:00
switch ( value . to_keyword ( ) ) {
2024-08-14 14:06:03 +01:00
case Keyword : : None :
2025-07-27 15:55:16 +02:00
return { { ContentData : : Type : : None , { } } , quote_nesting_level } ;
2024-08-14 14:06:03 +01:00
case Keyword : : Normal :
2025-07-27 15:55:16 +02:00
return { { ContentData : : Type : : Normal , { } } , quote_nesting_level } ;
2022-02-24 16:52:58 +00:00
default :
break ;
}
2023-09-18 15:41:17 +01:00
return { { } , quote_nesting_level } ;
2022-02-24 16:52:58 +00:00
}
2025-02-05 12:55:02 +00:00
ContentVisibility ComputedProperties : : content_visibility ( ) const
2024-06-23 14:52:56 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ContentVisibility ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_content_visibility ( value . to_keyword ( ) ) . release_value ( ) ;
2024-06-23 14:52:56 +02:00
}
2025-02-20 12:17:29 +00:00
Vector < CursorData > ComputedProperties : : cursor ( ) const
2021-02-21 17:41:00 +00:00
{
2025-02-20 12:17:29 +00:00
// Return the first available cursor.
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Cursor ) ;
2025-02-20 12:17:29 +00:00
Vector < CursorData > cursors ;
if ( value . is_value_list ( ) ) {
for ( auto const & item : value . as_value_list ( ) . values ( ) ) {
if ( item - > is_cursor ( ) ) {
cursors . append ( { item - > as_cursor ( ) } ) ;
continue ;
}
2025-07-09 10:39:22 +01:00
if ( auto keyword = keyword_to_cursor_predefined ( item - > to_keyword ( ) ) ; keyword . has_value ( ) )
2025-02-20 12:17:29 +00:00
cursors . append ( keyword . release_value ( ) ) ;
}
} else if ( value . is_keyword ( ) ) {
2025-07-09 10:39:22 +01:00
if ( auto keyword = keyword_to_cursor_predefined ( value . to_keyword ( ) ) ; keyword . has_value ( ) )
2025-02-20 12:17:29 +00:00
cursors . append ( keyword . release_value ( ) ) ;
}
if ( cursors . is_empty ( ) )
2025-07-09 10:39:22 +01:00
cursors . append ( CursorPredefined : : Auto ) ;
2025-02-20 12:17:29 +00:00
return cursors ;
2021-02-21 17:41:00 +00:00
}
2025-02-05 12:55:02 +00:00
Visibility ComputedProperties : : visibility ( ) const
2022-03-21 15:42:57 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Visibility ) ;
2024-11-03 13:20:04 +01:00
if ( ! value . is_keyword ( ) )
2022-03-21 15:42:57 +01:00
return { } ;
2025-02-05 12:55:02 +00:00
return keyword_to_visibility ( value . to_keyword ( ) ) . release_value ( ) ;
2022-03-21 15:42:57 +01:00
}
2024-12-20 11:32:17 +01:00
Display ComputedProperties : : display ( ) const
2020-06-24 16:22:16 +02:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( PropertyID : : Display ) ;
if ( value . is_display ( ) ) {
return value . as_display ( ) . display ( ) ;
2020-12-14 22:22:35 +01:00
}
2023-04-26 21:05:38 +02:00
return Display : : from_short ( Display : : Short : : Inline ) ;
2020-06-24 16:22:16 +02:00
}
2025-02-05 12:57:41 +00:00
Vector < TextDecorationLine > ComputedProperties : : text_decoration_line ( ) const
2020-12-15 13:36:27 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TextDecorationLine ) ;
2022-04-14 16:22:35 +01:00
2025-11-13 17:25:00 +13:00
if ( value . to_keyword ( ) = = Keyword : : None )
return { } ;
2024-11-03 13:20:04 +01:00
if ( value . is_value_list ( ) ) {
2025-02-05 12:57:41 +00:00
Vector < TextDecorationLine > lines ;
2024-11-03 13:20:04 +01:00
auto & values = value . as_value_list ( ) . values ( ) ;
2022-04-14 16:22:35 +01:00
for ( auto const & item : values ) {
2024-08-14 14:06:03 +01:00
lines . append ( keyword_to_text_decoration_line ( item - > to_keyword ( ) ) . value ( ) ) ;
2022-04-14 16:22:35 +01:00
}
return lines ;
}
2025-11-13 17:25:00 +13:00
VERIFY_NOT_REACHED ( ) ;
2020-12-15 13:36:27 +01:00
}
2025-02-05 12:55:02 +00:00
TextDecorationStyle ComputedProperties : : text_decoration_style ( ) const
2022-01-20 20:27:55 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TextDecorationStyle ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_text_decoration_style ( value . to_keyword ( ) ) . release_value ( ) ;
2022-01-20 20:27:55 +01:00
}
2025-08-27 15:12:17 +01:00
TextDecorationThickness ComputedProperties : : text_decoration_thickness ( ) const
{
auto const & value = property ( PropertyID : : TextDecorationThickness ) ;
if ( value . is_keyword ( ) ) {
switch ( value . to_keyword ( ) ) {
case Keyword : : Auto :
return { TextDecorationThickness : : Auto { } } ;
case Keyword : : FromFont :
return { TextDecorationThickness : : FromFont { } } ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2025-10-02 16:18:48 +13:00
return TextDecorationThickness { LengthPercentage : : from_style_value ( value ) } ;
2025-08-27 15:12:17 +01:00
}
2025-02-05 12:55:02 +00:00
TextTransform ComputedProperties : : text_transform ( ) const
2020-12-15 14:15:49 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TextTransform ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_text_transform ( value . to_keyword ( ) ) . release_value ( ) ;
2020-12-15 14:15:49 +01:00
}
2025-02-05 12:55:02 +00:00
ListStyleType ComputedProperties : : list_style_type ( ) const
2020-12-15 16:50:39 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ListStyleType ) ;
2025-02-10 12:48:40 +00:00
if ( value . is_string ( ) )
return value . as_string ( ) . string_value ( ) . to_string ( ) ;
return keyword_to_counter_style_name_keyword ( value . to_keyword ( ) ) . release_value ( ) ;
2020-12-15 16:50:39 +01:00
}
2025-02-05 12:55:02 +00:00
ListStylePosition ComputedProperties : : list_style_position ( ) const
2023-06-02 23:05:15 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ListStylePosition ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_list_style_position ( value . to_keyword ( ) ) . release_value ( ) ;
2023-06-02 23:05:15 +02:00
}
2025-02-05 12:55:02 +00:00
Overflow ComputedProperties : : overflow_x ( ) const
2021-02-22 15:20:31 +01:00
{
2025-02-05 12:57:41 +00:00
return overflow ( PropertyID : : OverflowX ) ;
2021-02-22 15:20:31 +01:00
}
2025-02-05 12:55:02 +00:00
Overflow ComputedProperties : : overflow_y ( ) const
2021-02-22 15:20:31 +01:00
{
2025-02-05 12:57:41 +00:00
return overflow ( PropertyID : : OverflowY ) ;
2021-02-22 15:20:31 +01:00
}
2025-02-05 12:57:41 +00:00
Overflow ComputedProperties : : overflow ( PropertyID property_id ) const
2021-02-22 15:20:31 +01:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( property_id ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_overflow ( value . to_keyword ( ) ) . release_value ( ) ;
2021-02-22 15:20:31 +01:00
}
2024-12-20 11:32:17 +01:00
Vector < ShadowData > ComputedProperties : : shadow ( PropertyID property_id , Layout : : Node const & layout_node ) const
2021-07-23 21:22:31 +02:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( property_id ) ;
2021-07-23 21:22:31 +02:00
2025-08-08 10:11:51 +01:00
auto resolve_to_length = [ & layout_node ] ( NonnullRefPtr < StyleValue const > const & value ) - > Optional < Length > {
2023-05-31 16:07:06 -04:00
if ( value - > is_length ( ) )
return value - > as_length ( ) . length ( ) ;
2024-12-11 15:05:56 +00:00
if ( value - > is_calculated ( ) )
2025-09-16 10:50:37 +12:00
return value - > as_calculated ( ) . resolve_length ( { . length_resolution_context = Length : : ResolutionContext : : for_layout_node ( layout_node ) } ) ;
2023-05-31 16:07:06 -04:00
return { } ;
} ;
2024-08-15 11:06:18 +01:00
auto make_shadow_data = [ resolve_to_length , & layout_node ] ( ShadowStyleValue const & value ) - > Optional < ShadowData > {
2023-05-31 16:07:06 -04:00
auto maybe_offset_x = resolve_to_length ( value . offset_x ( ) ) ;
if ( ! maybe_offset_x . has_value ( ) )
return { } ;
auto maybe_offset_y = resolve_to_length ( value . offset_y ( ) ) ;
if ( ! maybe_offset_y . has_value ( ) )
return { } ;
auto maybe_blur_radius = resolve_to_length ( value . blur_radius ( ) ) ;
if ( ! maybe_blur_radius . has_value ( ) )
return { } ;
auto maybe_spread_distance = resolve_to_length ( value . spread_distance ( ) ) ;
if ( ! maybe_spread_distance . has_value ( ) )
return { } ;
return ShadowData {
maybe_offset_x . release_value ( ) ,
maybe_offset_y . release_value ( ) ,
maybe_blur_radius . release_value ( ) ,
maybe_spread_distance . release_value ( ) ,
2025-07-19 11:31:07 +12:00
value . color ( ) - > to_color ( ColorResolutionContext : : for_layout_node_with_style ( as < Layout : : NodeWithStyle > ( layout_node ) ) ) . value ( ) ,
2023-05-31 16:07:06 -04:00
value . placement ( )
} ;
2022-02-08 14:48:37 +00:00
} ;
2024-11-03 13:20:04 +01:00
if ( value . is_value_list ( ) ) {
auto const & value_list = value . as_value_list ( ) ;
2022-02-08 14:48:37 +00:00
2022-03-23 21:16:36 +00:00
Vector < ShadowData > shadow_data ;
shadow_data . ensure_capacity ( value_list . size ( ) ) ;
2023-05-31 16:07:06 -04:00
for ( auto const & layer_value : value_list . values ( ) ) {
auto maybe_shadow_data = make_shadow_data ( layer_value - > as_shadow ( ) ) ;
if ( ! maybe_shadow_data . has_value ( ) )
return { } ;
shadow_data . append ( maybe_shadow_data . release_value ( ) ) ;
}
2022-02-08 14:48:37 +00:00
2022-03-23 21:16:36 +00:00
return shadow_data ;
2022-02-08 14:48:37 +00:00
}
2024-11-03 13:20:04 +01:00
if ( value . is_shadow ( ) ) {
auto maybe_shadow_data = make_shadow_data ( value . as_shadow ( ) ) ;
2023-05-31 16:07:06 -04:00
if ( ! maybe_shadow_data . has_value ( ) )
return { } ;
return { maybe_shadow_data . release_value ( ) } ;
2022-02-08 14:48:37 +00:00
}
return { } ;
2021-07-23 21:22:31 +02:00
}
2021-10-05 16:55:02 +01:00
2024-12-20 11:32:17 +01:00
Vector < ShadowData > ComputedProperties : : box_shadow ( Layout : : Node const & layout_node ) const
2022-03-23 21:16:36 +00:00
{
2023-05-31 16:07:06 -04:00
return shadow ( PropertyID : : BoxShadow , layout_node ) ;
2022-03-23 21:16:36 +00:00
}
2024-12-20 11:32:17 +01:00
Vector < ShadowData > ComputedProperties : : text_shadow ( Layout : : Node const & layout_node ) const
2022-03-23 21:16:36 +00:00
{
2023-05-31 16:07:06 -04:00
return shadow ( PropertyID : : TextShadow , layout_node ) ;
2022-03-23 21:16:36 +00:00
}
2025-11-18 10:53:25 +00:00
TextIndentData ComputedProperties : : text_indent ( ) const
{
auto const & value = property ( PropertyID : : TextIndent ) . as_text_indent ( ) ;
return TextIndentData {
. length_percentage = LengthPercentage : : from_style_value ( value . length_percentage ( ) ) ,
. each_line = value . each_line ( ) ,
. hanging = value . hanging ( ) ,
} ;
}
2025-05-18 00:49:25 +12:00
TextWrapMode ComputedProperties : : text_wrap_mode ( ) const
{
auto const & value = property ( PropertyID : : TextWrapMode ) ;
return keyword_to_text_wrap_mode ( value . to_keyword ( ) ) . release_value ( ) ;
}
2025-02-05 12:55:02 +00:00
BoxSizing ComputedProperties : : box_sizing ( ) const
2021-10-05 16:55:02 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : BoxSizing ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_box_sizing ( value . to_keyword ( ) ) . release_value ( ) ;
2021-10-05 16:55:02 +01:00
}
2022-02-26 01:34:07 +01:00
2025-02-05 12:57:41 +00:00
Variant < VerticalAlign , LengthPercentage > ComputedProperties : : vertical_align ( ) const
2022-02-26 01:34:07 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : VerticalAlign ) ;
2022-02-26 01:34:07 +01:00
2024-11-03 13:20:04 +01:00
if ( value . is_keyword ( ) )
return keyword_to_vertical_align ( value . to_keyword ( ) ) . release_value ( ) ;
2022-02-26 01:34:07 +01:00
2025-10-02 16:18:48 +13:00
return LengthPercentage : : from_style_value ( value ) ;
2022-02-26 01:34:07 +01:00
}
2025-06-22 18:59:42 +01:00
FontKerning ComputedProperties : : font_kerning ( ) const
{
auto const & value = property ( PropertyID : : FontKerning ) ;
return keyword_to_font_kerning ( value . to_keyword ( ) ) . release_value ( ) ;
}
2024-12-20 11:32:17 +01:00
Optional < FlyString > ComputedProperties : : font_language_override ( ) const
2024-09-27 17:11:05 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontLanguageOverride ) ;
2024-11-03 13:20:04 +01:00
if ( value . is_string ( ) )
return value . as_string ( ) . string_value ( ) ;
2024-09-27 17:11:05 +01:00
return { } ;
}
2025-11-09 21:00:22 +13:00
Gfx : : ShapeFeatures ComputedProperties : : font_features ( ) const
{
HashMap < StringView , u8 > merged_features ;
auto font_variant_features = [ & ] ( ) {
HashMap < StringView , u8 > features ;
// 6.4 https://drafts.csswg.org/css-fonts/#font-variant-ligatures-prop
auto ligature_or_null = font_variant_ligatures ( ) ;
auto disable_all_ligatures = [ & ] ( ) {
features . set ( " liga " sv , 0 ) ;
features . set ( " clig " sv , 0 ) ;
features . set ( " dlig " sv , 0 ) ;
features . set ( " hlig " sv , 0 ) ;
features . set ( " calt " sv , 0 ) ;
} ;
if ( ligature_or_null . has_value ( ) ) {
auto ligature = ligature_or_null . release_value ( ) ;
if ( ligature . none ) {
// Specifies that all types of ligatures and contextual forms covered by this property are explicitly disabled.
disable_all_ligatures ( ) ;
} else {
switch ( ligature . common ) {
case Gfx : : FontVariantLigatures : : Common : : Common :
// Enables display of common ligatures (OpenType features: liga, clig).
features . set ( " liga " sv , 1 ) ;
features . set ( " clig " sv , 1 ) ;
break ;
case Gfx : : FontVariantLigatures : : Common : : NoCommon :
// Disables display of common ligatures (OpenType features: liga, clig).
features . set ( " liga " sv , 0 ) ;
features . set ( " clig " sv , 0 ) ;
break ;
case Gfx : : FontVariantLigatures : : Common : : Unset :
break ;
}
switch ( ligature . discretionary ) {
case Gfx : : FontVariantLigatures : : Discretionary : : Discretionary :
// Enables display of discretionary ligatures (OpenType feature: dlig).
features . set ( " dlig " sv , 1 ) ;
break ;
case Gfx : : FontVariantLigatures : : Discretionary : : NoDiscretionary :
// Disables display of discretionary ligatures (OpenType feature: dlig).
features . set ( " dlig " sv , 0 ) ;
break ;
case Gfx : : FontVariantLigatures : : Discretionary : : Unset :
break ;
}
switch ( ligature . historical ) {
case Gfx : : FontVariantLigatures : : Historical : : Historical :
// Enables display of historical ligatures (OpenType feature: hlig).
features . set ( " hlig " sv , 1 ) ;
break ;
case Gfx : : FontVariantLigatures : : Historical : : NoHistorical :
// Disables display of historical ligatures (OpenType feature: hlig).
features . set ( " hlig " sv , 0 ) ;
break ;
case Gfx : : FontVariantLigatures : : Historical : : Unset :
break ;
}
switch ( ligature . contextual ) {
case Gfx : : FontVariantLigatures : : Contextual : : Contextual :
// Enables display of contextual ligatures (OpenType feature: calt).
features . set ( " calt " sv , 1 ) ;
break ;
case Gfx : : FontVariantLigatures : : Contextual : : NoContextual :
// Disables display of contextual ligatures (OpenType feature: calt).
features . set ( " calt " sv , 0 ) ;
break ;
case Gfx : : FontVariantLigatures : : Contextual : : Unset :
break ;
}
}
} else if ( text_rendering ( ) = = CSS : : TextRendering : : Optimizespeed ) {
// AD-HOC: Disable ligatures if font-variant-ligatures is set to normal and text rendering is set to optimize speed.
disable_all_ligatures ( ) ;
} else {
// A value of normal specifies that common default features are enabled, as described in detail in the next section.
features . set ( " liga " sv , 1 ) ;
features . set ( " clig " sv , 1 ) ;
}
// 6.5 https://drafts.csswg.org/css-fonts/#font-variant-position-prop
switch ( font_variant_position ( ) ) {
case CSS : : FontVariantPosition : : Normal :
// None of the features listed below are enabled.
break ;
case CSS : : FontVariantPosition : : Sub :
// Enables display of subscripts (OpenType feature: subs).
features . set ( " subs " sv , 1 ) ;
break ;
case CSS : : FontVariantPosition : : Super :
// Enables display of superscripts (OpenType feature: sups).
features . set ( " sups " sv , 1 ) ;
break ;
default :
break ;
}
// 6.6 https://drafts.csswg.org/css-fonts/#font-variant-caps-prop
switch ( font_variant_caps ( ) ) {
case CSS : : FontVariantCaps : : Normal :
// None of the features listed below are enabled.
break ;
case CSS : : FontVariantCaps : : SmallCaps :
// Enables display of small capitals (OpenType feature: smcp). Small-caps glyphs typically use the form of uppercase letters but are reduced to the size of lowercase letters.
features . set ( " smcp " sv , 1 ) ;
break ;
case CSS : : FontVariantCaps : : AllSmallCaps :
// Enables display of small capitals for both upper and lowercase letters (OpenType features: c2sc, smcp).
features . set ( " c2sc " sv , 1 ) ;
features . set ( " smcp " sv , 1 ) ;
break ;
case CSS : : FontVariantCaps : : PetiteCaps :
// Enables display of petite capitals (OpenType feature: pcap).
features . set ( " pcap " sv , 1 ) ;
break ;
case CSS : : FontVariantCaps : : AllPetiteCaps :
// Enables display of petite capitals for both upper and lowercase letters (OpenType features: c2pc, pcap).
features . set ( " c2pc " sv , 1 ) ;
features . set ( " pcap " sv , 1 ) ;
break ;
case CSS : : FontVariantCaps : : Unicase :
// Enables display of mixture of small capitals for uppercase letters with normal lowercase letters (OpenType feature: unic).
features . set ( " unic " sv , 1 ) ;
break ;
case CSS : : FontVariantCaps : : TitlingCaps :
// Enables display of titling capitals (OpenType feature: titl).
features . set ( " titl " sv , 1 ) ;
break ;
default :
break ;
}
// 6.7 https://drafts.csswg.org/css-fonts/#font-variant-numeric-prop
auto numeric_or_null = font_variant_numeric ( ) ;
if ( numeric_or_null . has_value ( ) ) {
auto numeric = numeric_or_null . release_value ( ) ;
if ( numeric . figure = = Gfx : : FontVariantNumeric : : Figure : : Oldstyle ) {
// Enables display of old-style numerals (OpenType feature: onum).
features . set ( " onum " sv , 1 ) ;
} else if ( numeric . figure = = Gfx : : FontVariantNumeric : : Figure : : Lining ) {
// Enables display of lining numerals (OpenType feature: lnum).
features . set ( " lnum " sv , 1 ) ;
}
if ( numeric . spacing = = Gfx : : FontVariantNumeric : : Spacing : : Proportional ) {
// Enables display of proportional numerals (OpenType feature: pnum).
features . set ( " pnum " sv , 1 ) ;
} else if ( numeric . spacing = = Gfx : : FontVariantNumeric : : Spacing : : Tabular ) {
// Enables display of tabular numerals (OpenType feature: tnum).
features . set ( " tnum " sv , 1 ) ;
}
if ( numeric . fraction = = Gfx : : FontVariantNumeric : : Fraction : : Diagonal ) {
// Enables display of diagonal fractions (OpenType feature: frac).
features . set ( " frac " sv , 1 ) ;
} else if ( numeric . fraction = = Gfx : : FontVariantNumeric : : Fraction : : Stacked ) {
// Enables display of stacked fractions (OpenType feature: afrc).
features . set ( " afrc " sv , 1 ) ;
features . set ( " afrc " sv , 1 ) ;
}
if ( numeric . ordinal ) {
// Enables display of letter forms used with ordinal numbers (OpenType feature: ordn).
features . set ( " ordn " sv , 1 ) ;
}
if ( numeric . slashed_zero ) {
// Enables display of slashed zeros (OpenType feature: zero).
features . set ( " zero " sv , 1 ) ;
}
}
// 6.10 https://drafts.csswg.org/css-fonts/#font-variant-east-asian-prop
auto east_asian_or_null = font_variant_east_asian ( ) ;
if ( east_asian_or_null . has_value ( ) ) {
auto east_asian = east_asian_or_null . release_value ( ) ;
switch ( east_asian . variant ) {
case Gfx : : FontVariantEastAsian : : Variant : : Jis78 :
// Enables display of JIS78 forms (OpenType feature: jp78).
features . set ( " jp78 " sv , 1 ) ;
break ;
case Gfx : : FontVariantEastAsian : : Variant : : Jis83 :
// Enables display of JIS83 forms (OpenType feature: jp83).
features . set ( " jp83 " sv , 1 ) ;
break ;
case Gfx : : FontVariantEastAsian : : Variant : : Jis90 :
// Enables display of JIS90 forms (OpenType feature: jp90).
features . set ( " jp90 " sv , 1 ) ;
break ;
case Gfx : : FontVariantEastAsian : : Variant : : Jis04 :
// Enables display of JIS04 forms (OpenType feature: jp04).
features . set ( " jp04 " sv , 1 ) ;
break ;
case Gfx : : FontVariantEastAsian : : Variant : : Simplified :
// Enables display of simplified forms (OpenType feature: smpl).
features . set ( " smpl " sv , 1 ) ;
break ;
case Gfx : : FontVariantEastAsian : : Variant : : Traditional :
// Enables display of traditional forms (OpenType feature: trad).
features . set ( " trad " sv , 1 ) ;
break ;
default :
break ;
}
switch ( east_asian . width ) {
case Gfx : : FontVariantEastAsian : : Width : : FullWidth :
// Enables display of full-width forms (OpenType feature: fwid).
features . set ( " fwid " sv , 1 ) ;
break ;
case Gfx : : FontVariantEastAsian : : Width : : Proportional :
// Enables display of proportional-width forms (OpenType feature: pwid).
features . set ( " pwid " sv , 1 ) ;
break ;
default :
break ;
}
if ( east_asian . ruby ) {
// Enables display of ruby forms (OpenType feature: ruby).
features . set ( " ruby " sv , 1 ) ;
}
}
// FIXME: vkrn should be enabled for vertical text.
switch ( font_kerning ( ) ) {
case CSS : : FontKerning : : Auto :
// AD-HOC: Disable kerning if font-kerning is set to normal and text rendering is set to optimize speed.
features . set ( " kern " sv , text_rendering ( ) ! = CSS : : TextRendering : : Optimizespeed ? 1 : 0 ) ;
break ;
case CSS : : FontKerning : : Normal :
features . set ( " kern " sv , 1 ) ;
break ;
case CSS : : FontKerning : : None :
features . set ( " kern " sv , 0 ) ;
break ;
default :
break ;
}
return features ;
} ;
// https://www.w3.org/TR/css-fonts-3/#feature-precedence
2025-11-13 14:05:19 +01:00
// FIXME: 1. Font features enabled by default, including features required for a given script.
2025-11-09 21:00:22 +13:00
2025-11-13 14:05:19 +01:00
// FIXME: 2. If the font is defined via an @font-face rule, the font features implied by the font-feature-settings descriptor in the @font-face rule.
2025-11-09 21:00:22 +13:00
// 3. Font features implied by the value of the ‘ font-variant’ property, the related ‘ font-variant’ subproperties and any other CSS property that uses OpenType features (e.g. the ‘ font-kerning’ property).
merged_features . update ( font_variant_features ( ) ) ;
2025-11-13 14:05:19 +01:00
// FIXME: 4. Feature settings determined by properties other than ‘ font-variant’ or ‘ font-feature-settings’ . For example, setting a non-default value for the ‘ letter-spacing’ property disables common ligatures.
2025-11-09 21:00:22 +13:00
// 5. Font features implied by the value of ‘ font-feature-settings’ property.
merged_features . update ( font_feature_settings ( ) ) ;
Gfx : : ShapeFeatures shape_features ;
shape_features . ensure_capacity ( merged_features . size ( ) ) ;
for ( auto & it : merged_features ) {
shape_features . unchecked_append ( { { it . key [ 0 ] , it . key [ 1 ] , it . key [ 2 ] , it . key [ 3 ] } , static_cast < u32 > ( it . value ) } ) ;
}
return shape_features ;
}
2024-12-20 11:32:17 +01:00
Optional < Gfx : : FontVariantAlternates > ComputedProperties : : font_variant_alternates ( ) const
2024-12-05 01:19:03 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariantAlternates ) ;
2025-02-10 14:32:58 +00:00
switch ( keyword_to_font_variant_alternates ( value . to_keyword ( ) ) . value ( ) ) {
case FontVariantAlternates : : Normal :
return { } ;
case FontVariantAlternates : : HistoricalForms :
return Gfx : : FontVariantAlternates { . historical_forms = true } ;
}
VERIFY_NOT_REACHED ( ) ;
2024-12-05 01:19:03 +01:00
}
2025-02-05 12:55:02 +00:00
FontVariantCaps ComputedProperties : : font_variant_caps ( ) const
2024-12-05 01:19:03 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariantCaps ) ;
2025-02-10 14:32:58 +00:00
return keyword_to_font_variant_caps ( value . to_keyword ( ) ) . release_value ( ) ;
2024-12-05 01:19:03 +01:00
}
2024-12-20 11:32:17 +01:00
Optional < Gfx : : FontVariantEastAsian > ComputedProperties : : font_variant_east_asian ( ) const
2024-12-05 01:19:03 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariantEastAsian ) ;
2025-02-10 14:32:58 +00:00
Gfx : : FontVariantEastAsian east_asian { } ;
bool normal = false ;
auto apply_keyword = [ & east_asian , & normal ] ( Keyword keyword ) {
switch ( keyword ) {
case Keyword : : Normal :
normal = true ;
break ;
case Keyword : : Jis78 :
east_asian . variant = Gfx : : FontVariantEastAsian : : Variant : : Jis78 ;
break ;
case Keyword : : Jis83 :
east_asian . variant = Gfx : : FontVariantEastAsian : : Variant : : Jis83 ;
break ;
case Keyword : : Jis90 :
east_asian . variant = Gfx : : FontVariantEastAsian : : Variant : : Jis90 ;
break ;
case Keyword : : Jis04 :
east_asian . variant = Gfx : : FontVariantEastAsian : : Variant : : Jis04 ;
break ;
case Keyword : : Simplified :
east_asian . variant = Gfx : : FontVariantEastAsian : : Variant : : Simplified ;
break ;
case Keyword : : Traditional :
east_asian . variant = Gfx : : FontVariantEastAsian : : Variant : : Traditional ;
break ;
case Keyword : : FullWidth :
east_asian . width = Gfx : : FontVariantEastAsian : : Width : : FullWidth ;
break ;
case Keyword : : ProportionalWidth :
east_asian . width = Gfx : : FontVariantEastAsian : : Width : : Proportional ;
break ;
case Keyword : : Ruby :
east_asian . ruby = true ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
} ;
if ( value . is_keyword ( ) ) {
apply_keyword ( value . to_keyword ( ) ) ;
} else if ( value . is_value_list ( ) ) {
for ( auto & child_value : value . as_value_list ( ) . values ( ) ) {
apply_keyword ( child_value - > to_keyword ( ) ) ;
}
}
if ( normal )
return { } ;
return east_asian ;
2024-12-05 01:19:03 +01:00
}
2025-02-05 12:55:02 +00:00
FontVariantEmoji ComputedProperties : : font_variant_emoji ( ) const
2024-12-05 01:19:03 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariantEmoji ) ;
2025-02-10 14:32:58 +00:00
return keyword_to_font_variant_emoji ( value . to_keyword ( ) ) . release_value ( ) ;
2024-12-05 01:19:03 +01:00
}
2024-12-20 11:32:17 +01:00
Optional < Gfx : : FontVariantLigatures > ComputedProperties : : font_variant_ligatures ( ) const
2024-12-05 01:19:03 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariantLigatures ) ;
2025-02-10 14:32:58 +00:00
Gfx : : FontVariantLigatures ligatures { } ;
bool normal = false ;
auto apply_keyword = [ & ligatures , & normal ] ( Keyword keyword ) {
switch ( keyword ) {
case Keyword : : Normal :
normal = true ;
break ;
case Keyword : : None :
ligatures . none = true ;
break ;
case Keyword : : CommonLigatures :
ligatures . common = Gfx : : FontVariantLigatures : : Common : : Common ;
break ;
case Keyword : : NoCommonLigatures :
ligatures . common = Gfx : : FontVariantLigatures : : Common : : NoCommon ;
break ;
case Keyword : : DiscretionaryLigatures :
ligatures . discretionary = Gfx : : FontVariantLigatures : : Discretionary : : Discretionary ;
break ;
case Keyword : : NoDiscretionaryLigatures :
ligatures . discretionary = Gfx : : FontVariantLigatures : : Discretionary : : NoDiscretionary ;
break ;
case Keyword : : HistoricalLigatures :
ligatures . historical = Gfx : : FontVariantLigatures : : Historical : : Historical ;
break ;
case Keyword : : NoHistoricalLigatures :
ligatures . historical = Gfx : : FontVariantLigatures : : Historical : : NoHistorical ;
break ;
case Keyword : : Contextual :
ligatures . contextual = Gfx : : FontVariantLigatures : : Contextual : : Contextual ;
break ;
case Keyword : : NoContextual :
ligatures . contextual = Gfx : : FontVariantLigatures : : Contextual : : NoContextual ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
} ;
if ( value . is_keyword ( ) ) {
apply_keyword ( value . to_keyword ( ) ) ;
} else if ( value . is_value_list ( ) ) {
for ( auto & child_value : value . as_value_list ( ) . values ( ) ) {
apply_keyword ( child_value - > to_keyword ( ) ) ;
}
}
if ( normal )
return { } ;
return ligatures ;
2024-12-05 01:19:03 +01:00
}
2024-12-20 11:32:17 +01:00
Optional < Gfx : : FontVariantNumeric > ComputedProperties : : font_variant_numeric ( ) const
2024-12-05 01:19:03 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariantNumeric ) ;
2025-02-10 14:32:58 +00:00
Gfx : : FontVariantNumeric numeric { } ;
bool normal = false ;
auto apply_keyword = [ & numeric , & normal ] ( Keyword keyword ) {
switch ( keyword ) {
case Keyword : : Normal :
normal = true ;
break ;
case Keyword : : Ordinal :
numeric . ordinal = true ;
break ;
case Keyword : : SlashedZero :
numeric . slashed_zero = true ;
break ;
case Keyword : : OldstyleNums :
numeric . figure = Gfx : : FontVariantNumeric : : Figure : : Oldstyle ;
break ;
case Keyword : : LiningNums :
numeric . figure = Gfx : : FontVariantNumeric : : Figure : : Lining ;
break ;
case Keyword : : ProportionalNums :
numeric . spacing = Gfx : : FontVariantNumeric : : Spacing : : Proportional ;
break ;
case Keyword : : TabularNums :
numeric . spacing = Gfx : : FontVariantNumeric : : Spacing : : Tabular ;
break ;
case Keyword : : DiagonalFractions :
numeric . fraction = Gfx : : FontVariantNumeric : : Fraction : : Diagonal ;
break ;
case Keyword : : StackedFractions :
numeric . fraction = Gfx : : FontVariantNumeric : : Fraction : : Stacked ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
} ;
if ( value . is_keyword ( ) ) {
apply_keyword ( value . to_keyword ( ) ) ;
} else if ( value . is_value_list ( ) ) {
for ( auto & child_value : value . as_value_list ( ) . values ( ) ) {
apply_keyword ( child_value - > to_keyword ( ) ) ;
}
}
if ( normal )
return { } ;
return numeric ;
2024-12-05 01:19:03 +01:00
}
2025-02-05 12:55:02 +00:00
FontVariantPosition ComputedProperties : : font_variant_position ( ) const
2024-12-05 01:19:03 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariantPosition ) ;
2025-02-10 14:32:58 +00:00
return keyword_to_font_variant_position ( value . to_keyword ( ) ) . release_value ( ) ;
2024-12-05 01:19:03 +01:00
}
2025-11-09 16:28:32 +13:00
HashMap < StringView , u8 > ComputedProperties : : font_feature_settings ( ) const
2024-10-01 09:37:43 +01:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( PropertyID : : FontFeatureSettings ) ;
2024-10-01 09:37:43 +01:00
2024-11-03 13:20:04 +01:00
if ( value . is_keyword ( ) )
2024-10-01 09:37:43 +01:00
return { } ; // normal
2024-11-03 13:20:04 +01:00
if ( value . is_value_list ( ) ) {
auto const & feature_tags = value . as_value_list ( ) . values ( ) ;
2025-11-09 16:28:32 +13:00
HashMap < StringView , u8 > result ;
2024-10-01 09:37:43 +01:00
result . ensure_capacity ( feature_tags . size ( ) ) ;
for ( auto const & tag_value : feature_tags ) {
auto const & feature_tag = tag_value - > as_open_type_tagged ( ) ;
if ( feature_tag . value ( ) - > is_integer ( ) ) {
2025-08-08 10:55:30 +01:00
result . set ( feature_tag . tag ( ) , feature_tag . value ( ) - > as_integer ( ) . integer ( ) ) ;
2024-10-01 09:37:43 +01:00
} else {
2024-12-11 15:05:56 +00:00
VERIFY ( feature_tag . value ( ) - > is_calculated ( ) ) ;
2025-11-09 16:28:32 +13:00
result . set ( feature_tag . tag ( ) , feature_tag . value ( ) - > as_calculated ( ) . resolve_integer ( { } ) . value ( ) ) ;
2024-10-01 09:37:43 +01:00
}
}
return result ;
}
return { } ;
}
2024-12-20 11:32:17 +01:00
Optional < HashMap < FlyString , NumberOrCalculated > > ComputedProperties : : font_variation_settings ( ) const
2024-10-01 09:20:06 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : FontVariationSettings ) ;
2024-10-01 09:20:06 +01:00
2024-11-03 13:20:04 +01:00
if ( value . is_keyword ( ) )
2024-10-01 09:20:06 +01:00
return { } ; // normal
2024-11-03 13:20:04 +01:00
if ( value . is_value_list ( ) ) {
auto const & axis_tags = value . as_value_list ( ) . values ( ) ;
2024-10-01 09:20:06 +01:00
HashMap < FlyString , NumberOrCalculated > result ;
result . ensure_capacity ( axis_tags . size ( ) ) ;
for ( auto const & tag_value : axis_tags ) {
auto const & axis_tag = tag_value - > as_open_type_tagged ( ) ;
if ( axis_tag . value ( ) - > is_number ( ) ) {
2025-08-08 10:55:30 +01:00
result . set ( axis_tag . tag ( ) , axis_tag . value ( ) - > as_number ( ) . number ( ) ) ;
2024-10-01 09:20:06 +01:00
} else {
2024-12-11 15:05:56 +00:00
VERIFY ( axis_tag . value ( ) - > is_calculated ( ) ) ;
result . set ( axis_tag . tag ( ) , NumberOrCalculated { axis_tag . value ( ) - > as_calculated ( ) } ) ;
2024-10-01 09:20:06 +01:00
}
}
return result ;
}
return { } ;
}
2025-02-05 12:57:41 +00:00
GridTrackSizeList ComputedProperties : : grid_auto_columns ( ) const
2023-05-21 18:08:41 +03:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridAutoColumns ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_size_list ( ) . grid_track_size_list ( ) ;
2023-05-21 18:08:41 +03:00
}
2025-02-05 12:57:41 +00:00
GridTrackSizeList ComputedProperties : : grid_auto_rows ( ) const
2023-05-21 18:08:41 +03:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridAutoRows ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_size_list ( ) . grid_track_size_list ( ) ;
2023-05-21 18:08:41 +03:00
}
2025-02-05 12:57:41 +00:00
GridTrackSizeList ComputedProperties : : grid_template_columns ( ) const
2022-08-23 19:49:07 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridTemplateColumns ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_size_list ( ) . grid_track_size_list ( ) ;
2022-08-23 19:49:07 +02:00
}
2025-02-05 12:57:41 +00:00
GridTrackSizeList ComputedProperties : : grid_template_rows ( ) const
2022-08-23 19:49:07 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridTemplateRows ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_size_list ( ) . grid_track_size_list ( ) ;
2022-08-23 19:49:07 +02:00
}
2025-02-05 12:57:41 +00:00
GridAutoFlow ComputedProperties : : grid_auto_flow ( ) const
2023-08-17 20:25:18 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridAutoFlow ) ;
2024-11-03 13:20:04 +01:00
if ( ! value . is_grid_auto_flow ( ) )
2025-02-05 12:57:41 +00:00
return GridAutoFlow { } ;
2024-11-03 13:20:04 +01:00
auto & grid_auto_flow_value = value . as_grid_auto_flow ( ) ;
2025-02-05 12:57:41 +00:00
return GridAutoFlow { . row = grid_auto_flow_value . is_row ( ) , . dense = grid_auto_flow_value . is_dense ( ) } ;
2023-08-17 20:25:18 +02:00
}
2025-02-05 12:57:41 +00:00
GridTrackPlacement ComputedProperties : : grid_column_end ( ) const
2022-08-23 19:58:00 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridColumnEnd ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_placement ( ) . grid_track_placement ( ) ;
2022-08-23 19:58:00 +02:00
}
2025-02-05 12:57:41 +00:00
GridTrackPlacement ComputedProperties : : grid_column_start ( ) const
2022-08-23 19:58:00 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridColumnStart ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_placement ( ) . grid_track_placement ( ) ;
2022-08-23 19:58:00 +02:00
}
2025-02-05 12:57:41 +00:00
GridTrackPlacement ComputedProperties : : grid_row_end ( ) const
2022-08-23 19:58:00 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridRowEnd ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_placement ( ) . grid_track_placement ( ) ;
2022-08-23 19:58:00 +02:00
}
2025-02-05 12:57:41 +00:00
GridTrackPlacement ComputedProperties : : grid_row_start ( ) const
2022-08-23 19:58:00 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridRowStart ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_track_placement ( ) . grid_track_placement ( ) ;
2022-08-23 19:58:00 +02:00
}
2025-02-05 12:55:02 +00:00
BorderCollapse ComputedProperties : : border_collapse ( ) const
2023-01-02 23:01:29 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : BorderCollapse ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_border_collapse ( value . to_keyword ( ) ) . release_value ( ) ;
2025-06-18 12:28:18 +01:00
}
EmptyCells ComputedProperties : : empty_cells ( ) const
{
auto const & value = property ( PropertyID : : EmptyCells ) ;
return keyword_to_empty_cells ( value . to_keyword ( ) ) . release_value ( ) ;
2023-01-02 23:01:29 +01:00
}
2024-12-20 11:32:17 +01:00
Vector < Vector < String > > ComputedProperties : : grid_template_areas ( ) const
2023-01-16 18:17:05 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : GridTemplateAreas ) ;
2024-11-03 13:20:04 +01:00
return value . as_grid_template_area ( ) . grid_template_area ( ) ;
2023-01-16 18:17:05 +01:00
}
2025-02-05 12:55:02 +00:00
ObjectFit ComputedProperties : : object_fit ( ) const
2023-08-01 20:06:50 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ObjectFit ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_object_fit ( value . to_keyword ( ) ) . release_value ( ) ;
2023-08-01 20:06:50 +02:00
}
2025-02-05 12:57:41 +00:00
ObjectPosition ComputedProperties : : object_position ( ) const
2023-10-16 13:52:51 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ObjectPosition ) ;
2024-11-03 13:20:04 +01:00
auto const & position = value . as_position ( ) ;
2025-02-05 12:57:41 +00:00
ObjectPosition object_position ;
2024-02-26 11:33:54 +01:00
auto const & edge_x = position . edge_x ( ) ;
auto const & edge_y = position . edge_y ( ) ;
if ( edge_x - > is_edge ( ) ) {
auto const & edge = edge_x - > as_edge ( ) ;
2024-11-29 22:44:14 +11:00
object_position . edge_x = edge . edge ( ) . value_or ( PositionEdge : : Left ) ;
2024-02-26 11:33:54 +01:00
object_position . offset_x = edge . offset ( ) ;
}
if ( edge_y - > is_edge ( ) ) {
auto const & edge = edge_y - > as_edge ( ) ;
2024-11-29 22:44:14 +11:00
object_position . edge_y = edge . edge ( ) . value_or ( PositionEdge : : Top ) ;
2024-02-26 11:33:54 +01:00
object_position . offset_y = edge . offset ( ) ;
}
return object_position ;
2023-10-16 13:52:51 +02:00
}
2025-02-05 12:55:02 +00:00
TableLayout ComputedProperties : : table_layout ( ) const
2023-08-07 01:32:52 +00:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : TableLayout ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_table_layout ( value . to_keyword ( ) ) . release_value ( ) ;
2023-08-07 01:32:52 +00:00
}
2025-02-05 12:55:02 +00:00
Direction ComputedProperties : : direction ( ) const
2024-08-10 23:13:26 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Direction ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_direction ( value . to_keyword ( ) ) . release_value ( ) ;
2024-08-10 23:13:26 +01:00
}
2025-02-05 12:55:02 +00:00
UnicodeBidi ComputedProperties : : unicode_bidi ( ) const
2024-10-03 21:09:29 +02:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : UnicodeBidi ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_unicode_bidi ( value . to_keyword ( ) ) . release_value ( ) ;
2024-10-03 21:09:29 +02:00
}
2025-02-05 12:55:02 +00:00
WritingMode ComputedProperties : : writing_mode ( ) const
2024-10-27 22:12:54 +00:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : WritingMode ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_writing_mode ( value . to_keyword ( ) ) . release_value ( ) ;
2024-10-27 22:12:54 +00:00
}
2025-02-05 12:55:02 +00:00
UserSelect ComputedProperties : : user_select ( ) const
2025-01-08 01:51:29 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : UserSelect ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_user_select ( value . to_keyword ( ) ) . release_value ( ) ;
2025-01-08 01:51:29 +01:00
}
2025-02-05 12:55:02 +00:00
Isolation ComputedProperties : : isolation ( ) const
2025-01-11 01:34:47 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Isolation ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_isolation ( value . to_keyword ( ) ) . release_value ( ) ;
2025-01-11 01:34:47 +01:00
}
2025-03-16 18:44:49 -07:00
TouchActionData ComputedProperties : : touch_action ( ) const
{
auto const & touch_action = property ( PropertyID : : TouchAction ) ;
if ( touch_action . is_keyword ( ) ) {
switch ( touch_action . to_keyword ( ) ) {
case Keyword : : Auto :
return TouchActionData { } ;
case Keyword : : None :
return TouchActionData : : none ( ) ;
case Keyword : : Manipulation :
return TouchActionData { . allow_other = false } ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
if ( touch_action . is_value_list ( ) ) {
TouchActionData touch_action_data = TouchActionData : : none ( ) ;
for ( auto const & value : touch_action . as_value_list ( ) . values ( ) ) {
switch ( value - > as_keyword ( ) . keyword ( ) ) {
case Keyword : : PanX :
touch_action_data . allow_right = true ;
touch_action_data . allow_left = true ;
break ;
case Keyword : : PanLeft :
touch_action_data . allow_left = true ;
break ;
case Keyword : : PanRight :
touch_action_data . allow_right = true ;
break ;
case Keyword : : PanY :
touch_action_data . allow_up = true ;
touch_action_data . allow_down = true ;
break ;
case Keyword : : PanUp :
touch_action_data . allow_up = true ;
break ;
case Keyword : : PanDown :
touch_action_data . allow_down = true ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
return touch_action_data ;
}
return TouchActionData { } ;
}
2025-02-05 12:57:41 +00:00
Containment ComputedProperties : : contain ( ) const
2025-01-18 20:39:26 +01:00
{
2025-02-05 12:57:41 +00:00
Containment containment = { } ;
auto const & value = property ( PropertyID : : Contain ) ;
2025-01-18 20:39:26 +01:00
switch ( value . to_keyword ( ) ) {
case Keyword : : None :
// This value indicates that the property has no effect. The element renders as normal, with no containment effects applied.
return { } ;
case Keyword : : Strict :
// This value computes to 'size layout paint style', and thus turns on all forms of containment for the element.
containment . size_containment = true ;
containment . layout_containment = true ;
containment . paint_containment = true ;
containment . style_containment = true ;
break ;
case Keyword : : Content :
// This value computes to 'layout paint style', and thus turns on all forms of containment except size containment for the element.
containment . layout_containment = true ;
containment . paint_containment = true ;
containment . style_containment = true ;
break ;
case Keyword : : Size :
containment . size_containment = true ;
break ;
case Keyword : : InlineSize :
containment . inline_size_containment = true ;
break ;
case Keyword : : Layout :
containment . layout_containment = true ;
break ;
case Keyword : : Style :
containment . style_containment = true ;
break ;
case Keyword : : Paint :
containment . paint_containment = true ;
break ;
default :
if ( value . is_value_list ( ) ) {
auto & values = value . as_value_list ( ) . values ( ) ;
for ( auto const & item : values ) {
switch ( item - > to_keyword ( ) ) {
case Keyword : : Size :
containment . size_containment = true ;
break ;
case Keyword : : InlineSize :
containment . inline_size_containment = true ;
break ;
case Keyword : : Layout :
containment . layout_containment = true ;
break ;
case Keyword : : Style :
containment . style_containment = true ;
break ;
case Keyword : : Paint :
containment . paint_containment = true ;
break ;
default :
2025-05-16 19:20:24 +01:00
dbgln ( " `{}` is not supported in `contain` (yet?) " , item - > to_string ( SerializationMode : : Normal ) ) ;
2025-01-18 20:39:26 +01:00
break ;
}
}
}
}
return containment ;
}
2025-09-30 14:48:55 +01:00
ContainerType ComputedProperties : : container_type ( ) const
{
ContainerType container_type { } ;
2025-11-05 00:20:47 +01:00
auto const & value = property ( PropertyID : : ContainerType ) ;
2025-09-30 14:48:55 +01:00
if ( value . to_keyword ( ) = = Keyword : : Normal )
return container_type ;
if ( value . is_value_list ( ) ) {
auto & values = value . as_value_list ( ) . values ( ) ;
for ( auto const & item : values ) {
switch ( item - > to_keyword ( ) ) {
case Keyword : : Size :
container_type . is_size_container = true ;
break ;
case Keyword : : InlineSize :
container_type . is_inline_size_container = true ;
break ;
case Keyword : : ScrollState :
container_type . is_scroll_state_container = true ;
break ;
default :
dbgln ( " `{}` is not supported in `container-type` (yet?) " , item - > to_string ( SerializationMode : : Normal ) ) ;
break ;
}
}
}
return container_type ;
}
2025-02-05 12:55:02 +00:00
MixBlendMode ComputedProperties : : mix_blend_mode ( ) const
2025-01-22 09:50:49 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : MixBlendMode ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_mix_blend_mode ( value . to_keyword ( ) ) . release_value ( ) ;
2025-01-22 09:50:49 +01:00
}
2025-02-21 17:56:24 +01:00
Optional < FlyString > ComputedProperties : : view_transition_name ( ) const
{
auto const & value = property ( PropertyID : : ViewTransitionName ) ;
2025-02-25 11:47:36 +00:00
if ( value . is_custom_ident ( ) )
return value . as_custom_ident ( ) . custom_ident ( ) ;
2025-02-21 17:56:24 +01:00
return { } ;
}
2025-10-26 18:44:31 +13:00
Vector < ComputedProperties : : AnimationProperties > ComputedProperties : : animations ( ) const
{
// CSS Animations are defined by binding keyframes to an element using the animation-* properties. These list-valued
// properties, which are all longhands of the animation shorthand, form a coordinating list property group with
// animation-name as the coordinating list base property and each item in the coordinated value list defining the
// properties of a single animation effect.
auto const & coordinated_properties = assemble_coordinated_value_list (
PropertyID : : AnimationName ,
{ PropertyID : : AnimationDuration ,
PropertyID : : AnimationTimingFunction ,
PropertyID : : AnimationIterationCount ,
PropertyID : : AnimationDirection ,
PropertyID : : AnimationPlayState ,
PropertyID : : AnimationDelay ,
PropertyID : : AnimationFillMode ,
PropertyID : : AnimationComposition ,
PropertyID : : AnimationName } ) ;
Vector < AnimationProperties > animations ;
for ( size_t i = 0 ; i < coordinated_properties . get ( PropertyID : : AnimationName ) - > size ( ) ; i + + ) {
// https://drafts.csswg.org/css-animations-1/#propdef-animation-name
// none: No keyframes are specified at all, so there will be no animation. Any other animations properties
// specified for this animation have no effect.
if ( coordinated_properties . get ( PropertyID : : AnimationName ) . value ( ) [ i ] - > to_keyword ( ) = = Keyword : : None )
continue ;
auto animation_name_style_value = coordinated_properties . get ( PropertyID : : AnimationName ) . value ( ) [ i ] ;
auto animation_duration_style_value = coordinated_properties . get ( PropertyID : : AnimationDuration ) . value ( ) [ i ] ;
auto animation_timing_function_style_value = coordinated_properties . get ( PropertyID : : AnimationTimingFunction ) . value ( ) [ i ] ;
auto animation_iteration_count_style_value = coordinated_properties . get ( PropertyID : : AnimationIterationCount ) . value ( ) [ i ] ;
auto animation_direction_style_value = coordinated_properties . get ( PropertyID : : AnimationDirection ) . value ( ) [ i ] ;
auto animation_play_state_style_value = coordinated_properties . get ( PropertyID : : AnimationPlayState ) . value ( ) [ i ] ;
auto animation_delay_style_value = coordinated_properties . get ( PropertyID : : AnimationDelay ) . value ( ) [ i ] ;
auto animation_fill_mode_style_value = coordinated_properties . get ( PropertyID : : AnimationFillMode ) . value ( ) [ i ] ;
auto animation_composition_style_value = coordinated_properties . get ( PropertyID : : AnimationComposition ) . value ( ) [ i ] ;
auto duration = [ & ] {
if ( animation_duration_style_value - > is_time ( ) )
return animation_duration_style_value - > as_time ( ) . time ( ) . to_milliseconds ( ) ;
if ( animation_duration_style_value - > is_calculated ( ) )
return animation_duration_style_value - > as_calculated ( ) . resolve_time ( { } ) . value ( ) . to_milliseconds ( ) ;
VERIFY_NOT_REACHED ( ) ;
} ( ) ;
auto timing_function = EasingFunction : : from_style_value ( animation_timing_function_style_value ) ;
auto iteration_count = [ & ] {
if ( animation_iteration_count_style_value - > to_keyword ( ) = = Keyword : : Infinite )
return AK : : Infinity < double > ;
if ( animation_iteration_count_style_value - > is_number ( ) )
return animation_iteration_count_style_value - > as_number ( ) . number ( ) ;
if ( animation_iteration_count_style_value - > is_calculated ( ) )
return animation_iteration_count_style_value - > as_calculated ( ) . resolve_number ( { } ) . value ( ) ;
VERIFY_NOT_REACHED ( ) ;
} ( ) ;
auto direction = keyword_to_animation_direction ( animation_direction_style_value - > to_keyword ( ) ) . value ( ) ;
auto play_state = keyword_to_animation_play_state ( animation_play_state_style_value - > to_keyword ( ) ) . value ( ) ;
auto delay = [ & ] {
if ( animation_delay_style_value - > is_time ( ) )
return animation_delay_style_value - > as_time ( ) . time ( ) . to_milliseconds ( ) ;
if ( animation_delay_style_value - > is_calculated ( ) )
return animation_delay_style_value - > as_calculated ( ) . resolve_time ( { } ) . value ( ) . to_milliseconds ( ) ;
VERIFY_NOT_REACHED ( ) ;
} ( ) ;
auto fill_mode = keyword_to_animation_fill_mode ( animation_fill_mode_style_value - > to_keyword ( ) ) . value ( ) ;
auto composition = keyword_to_animation_composition ( animation_composition_style_value - > to_keyword ( ) ) . value ( ) ;
auto name = [ & ] {
if ( animation_name_style_value - > is_custom_ident ( ) )
return animation_name_style_value - > as_custom_ident ( ) . custom_ident ( ) ;
if ( animation_name_style_value - > is_string ( ) )
return animation_name_style_value - > as_string ( ) . string_value ( ) ;
VERIFY_NOT_REACHED ( ) ;
} ( ) ;
animations . append ( AnimationProperties {
. duration = duration ,
. timing_function = timing_function ,
. iteration_count = iteration_count ,
. direction = direction ,
. play_state = play_state ,
. delay = delay ,
. fill_mode = fill_mode ,
. composition = composition ,
. name = name ,
} ) ;
}
return animations ;
}
2025-02-05 12:55:02 +00:00
MaskType ComputedProperties : : mask_type ( ) const
2023-10-08 11:06:34 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : MaskType ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_mask_type ( value . to_keyword ( ) ) . release_value ( ) ;
2023-10-08 11:06:34 +01:00
}
2024-12-20 11:32:17 +01:00
void ComputedProperties : : set_math_depth ( int math_depth )
2023-09-07 15:29:54 +01:00
{
2024-12-20 16:35:12 +01:00
m_math_depth = math_depth ;
2023-09-07 15:29:54 +01:00
// Make our children inherit our computed value, not our specified value.
set_property ( PropertyID : : MathDepth , MathDepthStyleValue : : create_integer ( IntegerStyleValue : : create ( math_depth ) ) ) ;
}
2024-12-20 11:32:17 +01:00
QuotesData ComputedProperties : : quotes ( ) const
2023-09-12 11:34:26 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : Quotes ) ;
2024-11-03 13:20:04 +01:00
if ( value . is_keyword ( ) ) {
switch ( value . to_keyword ( ) ) {
2024-08-14 14:06:03 +01:00
case Keyword : : Auto :
2023-09-12 11:34:26 +01:00
return QuotesData { . type = QuotesData : : Type : : Auto } ;
2024-08-14 14:06:03 +01:00
case Keyword : : None :
2023-09-12 11:34:26 +01:00
return QuotesData { . type = QuotesData : : Type : : None } ;
default :
break ;
}
}
2024-11-03 13:20:04 +01:00
if ( value . is_value_list ( ) ) {
auto & value_list = value . as_value_list ( ) ;
2023-09-12 11:34:26 +01:00
QuotesData quotes_data { . type = QuotesData : : Type : : Specified } ;
VERIFY ( value_list . size ( ) % 2 = = 0 ) ;
for ( auto i = 0u ; i < value_list . size ( ) ; i + = 2 ) {
quotes_data . strings . empend (
value_list . value_at ( i , false ) - > as_string ( ) . string_value ( ) ,
value_list . value_at ( i + 1 , false ) - > as_string ( ) . string_value ( ) ) ;
}
return quotes_data ;
}
return InitialValues : : quotes ( ) ;
}
2024-12-20 11:32:17 +01:00
Vector < CounterData > ComputedProperties : : counter_data ( PropertyID property_id ) const
2024-07-24 15:47:11 +01:00
{
2024-11-03 13:20:04 +01:00
auto const & value = property ( property_id ) ;
2024-07-24 15:47:11 +01:00
2024-11-03 13:20:04 +01:00
if ( value . is_counter_definitions ( ) ) {
auto & counter_definitions = value . as_counter_definitions ( ) . counter_definitions ( ) ;
2024-07-24 15:47:11 +01:00
Vector < CounterData > result ;
for ( auto & counter : counter_definitions ) {
CounterData data {
. name = counter . name ,
. is_reversed = counter . is_reversed ,
. value = { } ,
} ;
if ( counter . value ) {
if ( counter . value - > is_integer ( ) ) {
data . value = AK : : clamp_to < i32 > ( counter . value - > as_integer ( ) . integer ( ) ) ;
2024-12-11 15:05:56 +00:00
} else if ( counter . value - > is_calculated ( ) ) {
2025-09-24 14:06:10 +01:00
auto maybe_int = counter . value - > as_calculated ( ) . resolve_integer ( { } ) ;
2024-07-24 15:47:11 +01:00
if ( maybe_int . has_value ( ) )
data . value = AK : : clamp_to < i32 > ( * maybe_int ) ;
} else {
2025-05-16 19:20:24 +01:00
dbgln ( " Unimplemented type for {} integer value: '{}' " , string_from_property_id ( property_id ) , counter . value - > to_string ( SerializationMode : : Normal ) ) ;
2024-07-24 15:47:11 +01:00
}
}
result . append ( move ( data ) ) ;
}
return result ;
}
2024-11-03 13:20:04 +01:00
if ( value . to_keyword ( ) = = Keyword : : None )
2024-07-24 15:47:11 +01:00
return { } ;
2025-05-16 19:20:24 +01:00
dbgln ( " Unhandled type for {} value: '{}' " , string_from_property_id ( property_id ) , value . to_string ( SerializationMode : : Normal ) ) ;
2024-07-24 15:47:11 +01:00
return { } ;
}
2025-05-26 22:36:12 +01:00
ScrollbarColorData ComputedProperties : : scrollbar_color ( Layout : : NodeWithStyle const & layout_node ) const
{
auto const & value = property ( PropertyID : : ScrollbarColor ) ;
if ( value . is_keyword ( ) & & value . as_keyword ( ) . keyword ( ) = = Keyword : : Auto )
return InitialValues : : scrollbar_color ( ) ;
if ( value . is_scrollbar_color ( ) ) {
auto & scrollbar_color_value = value . as_scrollbar_color ( ) ;
2025-07-19 11:31:07 +12:00
auto thumb_color = scrollbar_color_value . thumb_color ( ) - > to_color ( ColorResolutionContext : : for_layout_node_with_style ( layout_node ) ) . value ( ) ;
auto track_color = scrollbar_color_value . track_color ( ) - > to_color ( ColorResolutionContext : : for_layout_node_with_style ( layout_node ) ) . value ( ) ;
2025-05-26 22:36:12 +01:00
return { thumb_color , track_color } ;
}
return { } ;
}
2025-02-05 12:55:02 +00:00
ScrollbarWidth ComputedProperties : : scrollbar_width ( ) const
2024-02-27 09:37:18 +01:00
{
2025-02-05 12:57:41 +00:00
auto const & value = property ( PropertyID : : ScrollbarWidth ) ;
2025-02-05 12:55:02 +00:00
return keyword_to_scrollbar_width ( value . to_keyword ( ) ) . release_value ( ) ;
2024-02-27 09:37:18 +01:00
}
2025-08-16 17:48:30 +01:00
ShapeRendering ComputedProperties : : shape_rendering ( ) const
{
auto const & value = property ( PropertyID : : ShapeRendering ) ;
return keyword_to_shape_rendering ( value . to_keyword ( ) ) . release_value ( ) ;
}
2025-08-24 15:00:53 +01:00
PaintOrderList ComputedProperties : : paint_order ( ) const
{
auto const & value = property ( PropertyID : : PaintOrder ) ;
if ( value . is_keyword ( ) ) {
auto keyword = value . as_keyword ( ) . keyword ( ) ;
if ( keyword = = Keyword : : Normal )
return InitialValues : : paint_order ( ) ;
auto paint_order_keyword = keyword_to_paint_order ( keyword ) ;
VERIFY ( paint_order_keyword . has_value ( ) ) ;
switch ( * paint_order_keyword ) {
case PaintOrder : : Fill :
return InitialValues : : paint_order ( ) ;
case PaintOrder : : Stroke :
return PaintOrderList { PaintOrder : : Stroke , PaintOrder : : Fill , PaintOrder : : Markers } ;
case PaintOrder : : Markers :
return PaintOrderList { PaintOrder : : Markers , PaintOrder : : Fill , PaintOrder : : Stroke } ;
}
}
VERIFY ( value . is_value_list ( ) ) ;
auto const & value_list = value . as_value_list ( ) ;
// The list must contain 2 values at this point, since the third value is omitted during parsing due to the
// shortest-serialization principle.
VERIFY ( value_list . size ( ) = = 2 ) ;
PaintOrderList paint_order_list { } ;
// We use the sum of the keyword values to infer what the missing keyword is. Since each keyword can only appear in
// the list once, the sum of their values will always be 3.
auto sum = 0 ;
for ( auto i = 0 ; i < 2 ; i + + ) {
auto keyword = value_list . value_at ( i , false ) - > as_keyword ( ) . keyword ( ) ;
auto paint_order_keyword = keyword_to_paint_order ( keyword ) ;
VERIFY ( paint_order_keyword . has_value ( ) ) ;
sum + = to_underlying ( * paint_order_keyword ) ;
paint_order_list [ i ] = * paint_order_keyword ;
}
VERIFY ( sum < = 3 ) ;
paint_order_list [ 2 ] = static_cast < PaintOrder > ( 3 - sum ) ;
return paint_order_list ;
}
2025-08-16 08:39:18 +01:00
WillChange ComputedProperties : : will_change ( ) const
{
auto const & value = property ( PropertyID : : WillChange ) ;
if ( value . to_keyword ( ) = = Keyword : : Auto )
return WillChange : : make_auto ( ) ;
auto to_will_change_entry = [ ] ( StyleValue const & value ) - > Optional < WillChange : : WillChangeEntry > {
if ( value . is_keyword ( ) ) {
switch ( value . as_keyword ( ) . keyword ( ) ) {
case Keyword : : Contents :
return WillChange : : Type : : Contents ;
case Keyword : : ScrollPosition :
return WillChange : : Type : : ScrollPosition ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
VERIFY ( value . is_custom_ident ( ) ) ;
auto custom_ident = value . as_custom_ident ( ) . custom_ident ( ) ;
auto property_id = property_id_from_string ( custom_ident ) ;
if ( ! property_id . has_value ( ) )
return { } ;
return property_id . release_value ( ) ;
} ;
if ( value . is_value_list ( ) ) {
auto const & value_list = value . as_value_list ( ) ;
Vector < WillChange : : WillChangeEntry > will_change_entries ;
for ( auto const & style_value : value_list . values ( ) ) {
if ( auto entry = to_will_change_entry ( * style_value ) ; entry . has_value ( ) )
will_change_entries . append ( * entry ) ;
}
return WillChange ( move ( will_change_entries ) ) ;
}
auto will_change_entry = to_will_change_entry ( value ) ;
if ( will_change_entry . has_value ( ) )
return WillChange ( { * will_change_entry } ) ;
return WillChange : : make_auto ( ) ;
}
2025-08-25 13:20:20 +12:00
CSSPixels ComputedProperties : : font_size ( ) const
{
2025-08-31 17:10:31 +12:00
return property ( PropertyID : : FontSize ) . as_length ( ) . length ( ) . absolute_length_to_px ( ) ;
2025-08-25 13:20:20 +12:00
}
2025-09-02 01:14:06 +12:00
double ComputedProperties : : font_weight ( ) const
{
return property ( PropertyID : : FontWeight ) . as_number ( ) . number ( ) ;
}
2025-09-02 20:43:10 +12:00
Percentage ComputedProperties : : font_width ( ) const
{
return property ( PropertyID : : FontWidth ) . as_percentage ( ) . percentage ( ) ;
}
2025-09-03 18:57:32 +12:00
int ComputedProperties : : font_slope ( ) const
{
return property ( PropertyID : : FontStyle ) . as_font_style ( ) . to_font_slope ( ) ;
}
2020-03-07 10:27:02 +01:00
}