2020-01-18 09:38:21 +01:00
/*
2023-02-20 18:56:08 +01:00
* Copyright ( c ) 2018 - 2023 , Andreas Kling < kling @ serenityos . org >
2021-02-21 13:45:26 +02:00
* Copyright ( c ) 2021 , the SerenityOS developers .
2023-02-17 14:06:55 +00:00
* Copyright ( c ) 2021 - 2023 , Sam Atkins < atkinssj @ serenityos . 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
*/
2022-02-10 17:49:50 +01:00
# include <AK/Debug.h>
2022-12-06 03:08:20 -03:00
# include <AK/Error.h>
2022-11-30 16:17:57 +01:00
# include <AK/HashMap.h>
2020-06-10 16:41:30 +02:00
# include <AK/QuickSort.h>
2021-12-03 20:00:31 +00:00
# include <AK/TemporaryChange.h>
2022-04-09 09:28:38 +02:00
# include <LibGfx/Font/Font.h>
# include <LibGfx/Font/FontDatabase.h>
# include <LibGfx/Font/FontStyleMapping.h>
2022-12-19 12:26:27 +01:00
# include <LibGfx/Font/OpenType/Font.h>
2022-04-09 10:14:58 +02:00
# include <LibGfx/Font/ScaledFont.h>
2022-04-09 11:18:46 +02:00
# include <LibGfx/Font/VectorFont.h>
2022-04-09 21:34:58 +02:00
# include <LibGfx/Font/WOFF/Font.h>
2022-03-29 02:14:20 +02:00
# include <LibWeb/CSS/CSSFontFaceRule.h>
2023-03-12 16:04:12 +01:00
# include <LibWeb/CSS/CSSImportRule.h>
2021-03-07 15:00:02 +01:00
# include <LibWeb/CSS/CSSStyleRule.h>
2021-07-14 19:57:02 +01:00
# include <LibWeb/CSS/Parser/Parser.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/CSS/SelectorEngine.h>
2021-09-24 13:49:57 +02:00
# include <LibWeb/CSS/StyleComputer.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/CSS/StyleSheet.h>
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/Element.h>
2021-09-23 13:13:51 +02:00
# include <LibWeb/FontCache.h>
2022-03-16 12:30:06 +01:00
# include <LibWeb/HTML/HTMLHtmlElement.h>
2022-03-29 02:14:20 +02:00
# include <LibWeb/Loader/ResourceLoader.h>
2022-09-08 11:55:12 +02:00
# include <LibWeb/Platform/FontPlugin.h>
2019-06-27 20:40:21 +02:00
# include <stdio.h>
2019-06-27 17:47:59 +02:00
2020-07-26 20:01:35 +02:00
namespace Web : : CSS {
2020-03-07 10:27:02 +01:00
2021-09-24 13:49:57 +02:00
StyleComputer : : StyleComputer ( DOM : : Document & document )
2019-06-27 17:47:59 +02:00
: m_document ( document )
{
}
2022-03-29 02:14:20 +02:00
StyleComputer : : ~ StyleComputer ( ) = default ;
class StyleComputer : : FontLoader : public ResourceClient {
public :
2023-02-17 14:06:55 +00:00
explicit FontLoader ( StyleComputer & style_computer , FlyString family_name , AK : : URL url )
2022-03-29 02:14:20 +02:00
: m_style_computer ( style_computer )
, m_family_name ( move ( family_name ) )
{
LoadRequest request ;
request . set_url ( move ( url ) ) ;
set_resource ( ResourceLoader : : the ( ) . load_resource ( Resource : : Type : : Generic , request ) ) ;
}
virtual ~ FontLoader ( ) override { }
virtual void resource_did_load ( ) override
{
2022-04-09 21:34:58 +02:00
auto result = try_load_font ( ) ;
2022-03-29 02:14:20 +02:00
if ( result . is_error ( ) )
return ;
2022-04-09 11:18:46 +02:00
m_vector_font = result . release_value ( ) ;
2022-03-29 02:14:20 +02:00
m_style_computer . did_load_font ( m_family_name ) ;
}
virtual void resource_did_fail ( ) override
{
}
RefPtr < Gfx : : Font > font_with_point_size ( float point_size ) const
{
2022-04-09 11:18:46 +02:00
if ( ! m_vector_font )
2022-03-29 02:14:20 +02:00
return nullptr ;
2022-04-10 12:32:16 +02:00
if ( auto it = m_cached_fonts . find ( point_size ) ; it ! = m_cached_fonts . end ( ) )
return it - > value ;
// FIXME: It might be nicer to have a global cap on the number of fonts we cache
// instead of doing it at the per-font level like this.
constexpr size_t max_cached_font_size_count = 64 ;
if ( m_cached_fonts . size ( ) > max_cached_font_size_count )
m_cached_fonts . remove ( m_cached_fonts . begin ( ) ) ;
auto font = adopt_ref ( * new Gfx : : ScaledFont ( * m_vector_font , point_size , point_size ) ) ;
m_cached_fonts . set ( point_size , font ) ;
return font ;
2022-03-29 02:14:20 +02:00
}
private :
2022-04-09 21:34:58 +02:00
ErrorOr < NonnullRefPtr < Gfx : : VectorFont > > try_load_font ( )
{
// FIXME: This could maybe use the format() provided in @font-face as well, since often the mime type is just application/octet-stream and we have to try every format
auto mime_type = resource ( ) - > mime_type ( ) ;
if ( mime_type = = " font/ttf " sv | | mime_type = = " application/x-font-ttf " sv )
2022-12-19 12:26:27 +01:00
return TRY ( OpenType : : Font : : try_load_from_externally_owned_memory ( resource ( ) - > encoded_data ( ) ) ) ;
2022-04-09 21:34:58 +02:00
if ( mime_type = = " font/woff " sv )
return TRY ( WOFF : : Font : : try_load_from_externally_owned_memory ( resource ( ) - > encoded_data ( ) ) ) ;
2022-12-19 12:26:27 +01:00
auto ttf = OpenType : : Font : : try_load_from_externally_owned_memory ( resource ( ) - > encoded_data ( ) ) ;
2022-04-09 21:34:58 +02:00
if ( ! ttf . is_error ( ) )
return ttf . release_value ( ) ;
auto woff = WOFF : : Font : : try_load_from_externally_owned_memory ( resource ( ) - > encoded_data ( ) ) ;
if ( ! woff . is_error ( ) )
return woff . release_value ( ) ;
return ttf . release_error ( ) ;
}
2022-03-29 02:14:20 +02:00
StyleComputer & m_style_computer ;
2023-02-17 14:06:55 +00:00
FlyString m_family_name ;
2022-04-09 11:18:46 +02:00
RefPtr < Gfx : : VectorFont > m_vector_font ;
2022-04-10 12:32:16 +02:00
HashMap < float , NonnullRefPtr < Gfx : : ScaledFont > > mutable m_cached_fonts ;
2022-03-29 02:14:20 +02:00
} ;
2023-02-28 16:54:25 +00:00
static CSSStyleSheet & default_stylesheet ( DOM : : Document const & document )
2019-10-05 09:01:12 +02:00
{
2022-08-07 13:14:54 +02:00
static JS : : Handle < CSSStyleSheet > sheet ;
if ( ! sheet . cell ( ) ) {
2023-02-17 13:00:40 +00:00
extern StringView default_stylesheet_source ;
2023-02-28 16:54:25 +00:00
sheet = JS : : make_handle ( parse_css_stylesheet ( CSS : : Parser : : ParsingContext ( document ) , default_stylesheet_source ) ) ;
2019-10-05 09:01:12 +02:00
}
return * sheet ;
}
2023-02-28 16:54:25 +00:00
static CSSStyleSheet & quirks_mode_stylesheet ( DOM : : Document const & document )
2020-09-24 10:33:33 +02:00
{
2022-08-07 13:14:54 +02:00
static JS : : Handle < CSSStyleSheet > sheet ;
if ( ! sheet . cell ( ) ) {
2023-02-17 13:00:40 +00:00
extern StringView quirks_mode_stylesheet_source ;
2023-02-28 16:54:25 +00:00
sheet = JS : : make_handle ( parse_css_stylesheet ( CSS : : Parser : : ParsingContext ( document ) , quirks_mode_stylesheet_source ) ) ;
2020-09-24 10:33:33 +02:00
}
return * sheet ;
}
2023-03-12 16:04:12 +01:00
static void collect_style_sheets ( CSSStyleSheet const & sheet , Vector < JS : : NonnullGCPtr < CSSStyleSheet const > > & sheets )
{
sheets . append ( sheet ) ;
for ( auto const & rule : sheet . rules ( ) ) {
if ( rule . type ( ) = = CSSRule : : Type : : Import ) {
auto const & import_rule = static_cast < CSSImportRule const & > ( rule ) ;
if ( auto const * imported_sheet = import_rule . loaded_style_sheet ( ) ) {
collect_style_sheets ( * imported_sheet , sheets ) ;
}
}
}
}
2019-10-05 09:01:12 +02:00
template < typename Callback >
2021-09-24 13:49:57 +02:00
void StyleComputer : : for_each_stylesheet ( CascadeOrigin cascade_origin , Callback callback ) const
2019-10-05 09:01:12 +02:00
{
2022-02-11 12:08:27 +01:00
if ( cascade_origin = = CascadeOrigin : : UserAgent ) {
2023-02-28 16:54:25 +00:00
callback ( default_stylesheet ( document ( ) ) ) ;
2021-09-21 11:38:18 +02:00
if ( document ( ) . in_quirks_mode ( ) )
2023-02-28 16:54:25 +00:00
callback ( quirks_mode_stylesheet ( document ( ) ) ) ;
2021-09-21 11:38:18 +02:00
}
2022-02-11 12:08:27 +01:00
if ( cascade_origin = = CascadeOrigin : : Author ) {
2023-03-12 16:04:12 +01:00
Vector < JS : : NonnullGCPtr < CSSStyleSheet const > > sheets ;
for ( auto const & sheet : document ( ) . style_sheets ( ) . sheets ( ) )
collect_style_sheets ( sheet , sheets ) ;
for ( auto const & sheet : sheets )
2022-09-09 11:08:56 +02:00
callback ( * sheet ) ;
2019-10-05 09:01:12 +02:00
}
}
2023-03-07 20:13:13 +01:00
StyleComputer : : RuleCache const & StyleComputer : : rule_cache_for_cascade_origin ( CascadeOrigin cascade_origin ) const
{
switch ( cascade_origin ) {
case CascadeOrigin : : Author :
return * m_author_rule_cache ;
case CascadeOrigin : : UserAgent :
return * m_user_agent_rule_cache ;
default :
TODO ( ) ;
}
}
2022-02-24 15:54:12 +00:00
Vector < MatchingRule > StyleComputer : : collect_matching_rules ( DOM : : Element const & element , CascadeOrigin cascade_origin , Optional < CSS : : Selector : : PseudoElement > pseudo_element ) const
2019-06-27 20:40:21 +02:00
{
2023-03-07 20:13:13 +01:00
auto const & rule_cache = rule_cache_for_cascade_origin ( cascade_origin ) ;
Vector < MatchingRule > rules_to_run ;
2023-03-09 19:27:23 +01:00
auto add_rules_to_run = [ & ] ( Vector < MatchingRule > const & rules ) {
if ( pseudo_element . has_value ( ) ) {
for ( auto & rule : rules ) {
if ( rule . contains_pseudo_element )
rules_to_run . append ( rule ) ;
}
} else {
rules_to_run . extend ( rules ) ;
2022-02-10 17:49:50 +01:00
}
2023-03-09 19:27:23 +01:00
} ;
for ( auto const & class_name : element . class_names ( ) ) {
2023-03-09 18:07:49 +01:00
if ( auto it = rule_cache . rules_by_class . find ( class_name ) ; it ! = rule_cache . rules_by_class . end ( ) )
2023-03-09 19:27:23 +01:00
add_rules_to_run ( it - > value ) ;
}
if ( auto id = element . get_attribute ( HTML : : AttributeNames : : id ) ; ! id . is_null ( ) ) {
2023-03-09 18:07:49 +01:00
if ( auto it = rule_cache . rules_by_id . find ( FlyString : : from_deprecated_fly_string ( id ) . release_value_but_fixme_should_propagate_errors ( ) ) ; it ! = rule_cache . rules_by_id . end ( ) )
2023-03-09 19:27:23 +01:00
add_rules_to_run ( it - > value ) ;
2022-02-10 17:49:50 +01:00
}
2023-03-09 18:07:49 +01:00
if ( auto it = rule_cache . rules_by_tag_name . find ( FlyString : : from_deprecated_fly_string ( element . local_name ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ) ; it ! = rule_cache . rules_by_tag_name . end ( ) )
2023-03-09 19:27:23 +01:00
add_rules_to_run ( it - > value ) ;
add_rules_to_run ( rule_cache . other_rules ) ;
2019-10-05 09:01:12 +02:00
2022-02-10 17:49:50 +01:00
Vector < MatchingRule > matching_rules ;
2023-03-07 20:13:13 +01:00
matching_rules . ensure_capacity ( rules_to_run . size ( ) ) ;
for ( auto const & rule_to_run : rules_to_run ) {
auto const & selector = rule_to_run . rule - > selectors ( ) [ rule_to_run . selector_index ] ;
if ( SelectorEngine : : matches ( selector , element , pseudo_element ) )
matching_rules . append ( rule_to_run ) ;
}
2019-06-27 20:40:21 +02:00
return matching_rules ;
}
2022-02-09 20:11:16 +01:00
static void sort_matching_rules ( Vector < MatchingRule > & matching_rules )
2020-12-14 21:31:10 +00:00
{
quick_sort ( matching_rules , [ & ] ( MatchingRule & a , MatchingRule & b ) {
2022-02-09 20:11:16 +01:00
auto const & a_selector = a . rule - > selectors ( ) [ a . selector_index ] ;
auto const & b_selector = b . rule - > selectors ( ) [ b . selector_index ] ;
2023-03-06 14:17:01 +01:00
auto a_specificity = a_selector - > specificity ( ) ;
auto b_specificity = b_selector - > specificity ( ) ;
if ( a_selector - > specificity ( ) = = b_selector - > specificity ( ) ) {
2020-12-14 21:31:10 +00:00
if ( a . style_sheet_index = = b . style_sheet_index )
return a . rule_index < b . rule_index ;
return a . style_sheet_index < b . style_sheet_index ;
}
return a_specificity < b_specificity ;
} ) ;
}
2020-06-10 16:14:31 +02:00
enum class Edge {
Top ,
Right ,
Bottom ,
Left ,
All ,
} ;
static bool contains ( Edge a , Edge b )
{
return a = = b | | b = = Edge : : All ;
}
2021-11-04 17:10:12 +00:00
static void set_property_expanding_shorthands ( StyleProperties & style , CSS : : PropertyID property_id , StyleValue const & value , DOM : : Document & document )
2019-11-18 11:49:19 +01:00
{
2021-08-06 14:34:59 +01:00
auto assign_edge_values = [ & style ] ( PropertyID top_property , PropertyID right_property , PropertyID bottom_property , PropertyID left_property , auto const & values ) {
if ( values . size ( ) = = 4 ) {
style . set_property ( top_property , values [ 0 ] ) ;
style . set_property ( right_property , values [ 1 ] ) ;
style . set_property ( bottom_property , values [ 2 ] ) ;
style . set_property ( left_property , values [ 3 ] ) ;
} else if ( values . size ( ) = = 3 ) {
style . set_property ( top_property , values [ 0 ] ) ;
style . set_property ( right_property , values [ 1 ] ) ;
style . set_property ( bottom_property , values [ 2 ] ) ;
style . set_property ( left_property , values [ 1 ] ) ;
} else if ( values . size ( ) = = 2 ) {
style . set_property ( top_property , values [ 0 ] ) ;
style . set_property ( right_property , values [ 1 ] ) ;
style . set_property ( bottom_property , values [ 0 ] ) ;
style . set_property ( left_property , values [ 1 ] ) ;
} else if ( values . size ( ) = = 1 ) {
style . set_property ( top_property , values [ 0 ] ) ;
style . set_property ( right_property , values [ 0 ] ) ;
style . set_property ( bottom_property , values [ 0 ] ) ;
style . set_property ( left_property , values [ 0 ] ) ;
}
} ;
2020-12-15 13:36:27 +01:00
if ( property_id = = CSS : : PropertyID : : TextDecoration ) {
2021-08-04 12:34:14 +01:00
if ( value . is_text_decoration ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & text_decoration = value . as_text_decoration ( ) ;
2021-08-04 12:34:14 +01:00
style . set_property ( CSS : : PropertyID : : TextDecorationLine , text_decoration . line ( ) ) ;
2022-03-06 02:09:00 +01:00
style . set_property ( CSS : : PropertyID : : TextDecorationThickness , text_decoration . thickness ( ) ) ;
2021-08-04 12:34:14 +01:00
style . set_property ( CSS : : PropertyID : : TextDecorationStyle , text_decoration . style ( ) ) ;
style . set_property ( CSS : : PropertyID : : TextDecorationColor , text_decoration . color ( ) ) ;
2021-07-22 14:56:44 +01:00
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : TextDecorationLine , value ) ;
2022-03-06 02:09:00 +01:00
style . set_property ( CSS : : PropertyID : : TextDecorationThickness , value ) ;
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : TextDecorationStyle , value ) ;
style . set_property ( CSS : : PropertyID : : TextDecorationColor , value ) ;
2020-12-15 13:36:27 +01:00
return ;
}
2021-02-22 15:20:31 +01:00
if ( property_id = = CSS : : PropertyID : : Overflow ) {
2021-08-09 14:54:40 +01:00
if ( value . is_overflow ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & overflow = value . as_overflow ( ) ;
2021-08-09 14:54:40 +01:00
style . set_property ( CSS : : PropertyID : : OverflowX , overflow . overflow_x ( ) ) ;
style . set_property ( CSS : : PropertyID : : OverflowY , overflow . overflow_y ( ) ) ;
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : OverflowX , value ) ;
style . set_property ( CSS : : PropertyID : : OverflowY , value ) ;
2021-02-22 15:20:31 +01:00
return ;
}
2020-03-20 20:29:39 +01:00
if ( property_id = = CSS : : PropertyID : : Border ) {
2020-06-10 17:47:04 +02:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BorderTop , value , document ) ;
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BorderRight , value , document ) ;
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BorderBottom , value , document ) ;
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BorderLeft , value , document ) ;
2021-08-05 21:11:38 +01:00
// FIXME: Also reset border-image, in line with the spec: https://www.w3.org/TR/css-backgrounds-3/#border-shorthands
2020-12-19 19:30:49 +01:00
return ;
2020-06-10 16:14:31 +02:00
}
2021-05-14 22:31:52 +02:00
if ( property_id = = CSS : : PropertyID : : BorderRadius ) {
2022-04-18 17:10:50 +01:00
if ( value . is_border_radius_shorthand ( ) ) {
auto const & shorthand = value . as_border_radius_shorthand ( ) ;
style . set_property ( CSS : : PropertyID : : BorderTopLeftRadius , shorthand . top_left ( ) ) ;
style . set_property ( CSS : : PropertyID : : BorderTopRightRadius , shorthand . top_right ( ) ) ;
style . set_property ( CSS : : PropertyID : : BorderBottomRightRadius , shorthand . bottom_right ( ) ) ;
style . set_property ( CSS : : PropertyID : : BorderBottomLeftRadius , shorthand . bottom_left ( ) ) ;
2021-08-06 16:55:08 +01:00
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : BorderTopLeftRadius , value ) ;
style . set_property ( CSS : : PropertyID : : BorderTopRightRadius , value ) ;
style . set_property ( CSS : : PropertyID : : BorderBottomRightRadius , value ) ;
style . set_property ( CSS : : PropertyID : : BorderBottomLeftRadius , value ) ;
2021-05-14 22:31:52 +02:00
return ;
}
2020-06-10 16:14:31 +02:00
if ( property_id = = CSS : : PropertyID : : BorderTop
| | property_id = = CSS : : PropertyID : : BorderRight
| | property_id = = CSS : : PropertyID : : BorderBottom
| | property_id = = CSS : : PropertyID : : BorderLeft ) {
Edge edge = Edge : : All ;
switch ( property_id ) {
case CSS : : PropertyID : : BorderTop :
edge = Edge : : Top ;
break ;
case CSS : : PropertyID : : BorderRight :
edge = Edge : : Right ;
break ;
case CSS : : PropertyID : : BorderBottom :
edge = Edge : : Bottom ;
break ;
case CSS : : PropertyID : : BorderLeft :
edge = Edge : : Left ;
break ;
default :
break ;
}
2021-08-05 21:11:38 +01:00
if ( value . is_border ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & border = value . as_border ( ) ;
2021-08-05 21:11:38 +01:00
if ( contains ( Edge : : Top , edge ) ) {
style . set_property ( PropertyID : : BorderTopWidth , border . border_width ( ) ) ;
style . set_property ( PropertyID : : BorderTopStyle , border . border_style ( ) ) ;
style . set_property ( PropertyID : : BorderTopColor , border . border_color ( ) ) ;
2021-07-14 19:57:02 +01:00
}
2021-08-05 21:11:38 +01:00
if ( contains ( Edge : : Right , edge ) ) {
style . set_property ( PropertyID : : BorderRightWidth , border . border_width ( ) ) ;
style . set_property ( PropertyID : : BorderRightStyle , border . border_style ( ) ) ;
style . set_property ( PropertyID : : BorderRightColor , border . border_color ( ) ) ;
}
if ( contains ( Edge : : Bottom , edge ) ) {
style . set_property ( PropertyID : : BorderBottomWidth , border . border_width ( ) ) ;
style . set_property ( PropertyID : : BorderBottomStyle , border . border_style ( ) ) ;
style . set_property ( PropertyID : : BorderBottomColor , border . border_color ( ) ) ;
}
if ( contains ( Edge : : Left , edge ) ) {
style . set_property ( PropertyID : : BorderLeftWidth , border . border_width ( ) ) ;
style . set_property ( PropertyID : : BorderLeftStyle , border . border_style ( ) ) ;
style . set_property ( PropertyID : : BorderLeftColor , border . border_color ( ) ) ;
2020-03-20 20:29:39 +01:00
}
return ;
}
return ;
}
2019-11-27 20:51:15 +01:00
if ( property_id = = CSS : : PropertyID : : BorderStyle ) {
2021-08-06 14:34:59 +01:00
if ( value . is_value_list ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & values_list = value . as_value_list ( ) ;
2021-08-06 14:34:59 +01:00
assign_edge_values ( PropertyID : : BorderTopStyle , PropertyID : : BorderRightStyle , PropertyID : : BorderBottomStyle , PropertyID : : BorderLeftStyle , values_list . values ( ) ) ;
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : BorderTopStyle , value ) ;
style . set_property ( CSS : : PropertyID : : BorderRightStyle , value ) ;
style . set_property ( CSS : : PropertyID : : BorderBottomStyle , value ) ;
style . set_property ( CSS : : PropertyID : : BorderLeftStyle , value ) ;
2019-11-27 20:51:15 +01:00
return ;
}
if ( property_id = = CSS : : PropertyID : : BorderWidth ) {
2021-08-06 14:34:59 +01:00
if ( value . is_value_list ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & values_list = value . as_value_list ( ) ;
2021-08-06 14:34:59 +01:00
assign_edge_values ( PropertyID : : BorderTopWidth , PropertyID : : BorderRightWidth , PropertyID : : BorderBottomWidth , PropertyID : : BorderLeftWidth , values_list . values ( ) ) ;
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : BorderTopWidth , value ) ;
style . set_property ( CSS : : PropertyID : : BorderRightWidth , value ) ;
style . set_property ( CSS : : PropertyID : : BorderBottomWidth , value ) ;
style . set_property ( CSS : : PropertyID : : BorderLeftWidth , value ) ;
2019-11-27 20:51:15 +01:00
return ;
}
if ( property_id = = CSS : : PropertyID : : BorderColor ) {
2021-08-06 14:34:59 +01:00
if ( value . is_value_list ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & values_list = value . as_value_list ( ) ;
2021-08-06 14:34:59 +01:00
assign_edge_values ( PropertyID : : BorderTopColor , PropertyID : : BorderRightColor , PropertyID : : BorderBottomColor , PropertyID : : BorderLeftColor , values_list . values ( ) ) ;
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : BorderTopColor , value ) ;
style . set_property ( CSS : : PropertyID : : BorderRightColor , value ) ;
style . set_property ( CSS : : PropertyID : : BorderBottomColor , value ) ;
style . set_property ( CSS : : PropertyID : : BorderLeftColor , value ) ;
2019-11-27 20:51:15 +01:00
return ;
}
2020-05-30 11:58:05 +02:00
if ( property_id = = CSS : : PropertyID : : Background ) {
2021-11-11 11:52:33 +00:00
if ( value . is_background ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & background = value . as_background ( ) ;
2021-08-03 14:56:08 +01:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundColor , background . color ( ) , document ) ;
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundImage , background . image ( ) , document ) ;
2021-11-03 16:30:55 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundPosition , background . position ( ) , document ) ;
2021-11-05 16:15:02 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundSize , background . size ( ) , document ) ;
2021-11-04 15:56:10 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundRepeat , background . repeat ( ) , document ) ;
2021-11-03 17:46:45 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundAttachment , background . attachment ( ) , document ) ;
2021-11-03 20:28:34 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundOrigin , background . origin ( ) , document ) ;
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundClip , background . clip ( ) , document ) ;
2021-07-21 17:47:50 +01:00
return ;
}
2021-07-14 19:57:02 +01:00
2021-09-23 15:23:04 +01:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundColor , value , document ) ;
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundImage , value , document ) ;
2021-11-03 16:30:55 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundPosition , value , document ) ;
2021-11-05 16:15:02 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundSize , value , document ) ;
2021-11-04 15:56:10 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundRepeat , value , document ) ;
2021-11-03 17:46:45 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundAttachment , value , document ) ;
2021-11-03 20:28:34 +00:00
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundOrigin , value , document ) ;
set_property_expanding_shorthands ( style , CSS : : PropertyID : : BackgroundClip , value , document ) ;
2021-11-03 17:46:45 +00:00
return ;
}
2019-11-18 11:49:19 +01:00
if ( property_id = = CSS : : PropertyID : : Margin ) {
2021-08-06 14:34:59 +01:00
if ( value . is_value_list ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & values_list = value . as_value_list ( ) ;
2021-08-06 14:34:59 +01:00
assign_edge_values ( PropertyID : : MarginTop , PropertyID : : MarginRight , PropertyID : : MarginBottom , PropertyID : : MarginLeft , values_list . values ( ) ) ;
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : MarginTop , value ) ;
style . set_property ( CSS : : PropertyID : : MarginRight , value ) ;
style . set_property ( CSS : : PropertyID : : MarginBottom , value ) ;
style . set_property ( CSS : : PropertyID : : MarginLeft , value ) ;
2019-11-18 11:49:19 +01:00
return ;
}
2019-11-18 12:25:22 +01:00
if ( property_id = = CSS : : PropertyID : : Padding ) {
2021-08-06 14:34:59 +01:00
if ( value . is_value_list ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & values_list = value . as_value_list ( ) ;
2021-08-06 14:34:59 +01:00
assign_edge_values ( PropertyID : : PaddingTop , PropertyID : : PaddingRight , PropertyID : : PaddingBottom , PropertyID : : PaddingLeft , values_list . values ( ) ) ;
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : PaddingTop , value ) ;
style . set_property ( CSS : : PropertyID : : PaddingRight , value ) ;
style . set_property ( CSS : : PropertyID : : PaddingBottom , value ) ;
style . set_property ( CSS : : PropertyID : : PaddingLeft , value ) ;
2019-11-18 12:25:22 +01:00
return ;
}
2020-05-30 12:04:15 +02:00
if ( property_id = = CSS : : PropertyID : : ListStyle ) {
2021-08-03 15:56:51 +01:00
if ( value . is_list_style ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & list_style = value . as_list_style ( ) ;
2021-08-03 15:56:51 +01:00
style . set_property ( CSS : : PropertyID : : ListStylePosition , list_style . position ( ) ) ;
style . set_property ( CSS : : PropertyID : : ListStyleImage , list_style . image ( ) ) ;
style . set_property ( CSS : : PropertyID : : ListStyleType , list_style . style_type ( ) ) ;
2021-07-14 19:57:02 +01:00
return ;
2020-05-30 12:04:15 +02:00
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : ListStylePosition , value ) ;
style . set_property ( CSS : : PropertyID : : ListStyleImage , value ) ;
style . set_property ( CSS : : PropertyID : : ListStyleType , value ) ;
2020-05-30 12:04:15 +02:00
return ;
}
2021-04-13 20:41:57 +03:00
if ( property_id = = CSS : : PropertyID : : Font ) {
2021-08-03 11:37:24 +01:00
if ( value . is_font ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & font_shorthand = value . as_font ( ) ;
2021-08-03 11:37:24 +01:00
style . set_property ( CSS : : PropertyID : : FontSize , font_shorthand . font_size ( ) ) ;
2021-08-14 21:05:15 +01:00
style . set_property ( CSS : : PropertyID : : FontFamily , font_shorthand . font_families ( ) ) ;
2023-02-03 14:27:09 +03:00
style . set_property ( CSS : : PropertyID : : FontStretch , font_shorthand . font_stretch ( ) ) ;
2021-08-03 11:37:24 +01:00
style . set_property ( CSS : : PropertyID : : FontStyle , font_shorthand . font_style ( ) ) ;
style . set_property ( CSS : : PropertyID : : FontWeight , font_shorthand . font_weight ( ) ) ;
style . set_property ( CSS : : PropertyID : : LineHeight , font_shorthand . line_height ( ) ) ;
2023-02-03 14:27:09 +03:00
// FIXME: Implement font-variant
2021-07-21 15:39:40 +01:00
return ;
}
2021-09-23 15:23:04 +01:00
2023-02-03 14:27:09 +03:00
style . set_property ( CSS : : PropertyID : : FontStretch , value ) ;
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : FontSize , value ) ;
style . set_property ( CSS : : PropertyID : : FontFamily , value ) ;
style . set_property ( CSS : : PropertyID : : FontStyle , value ) ;
style . set_property ( CSS : : PropertyID : : FontWeight , value ) ;
style . set_property ( CSS : : PropertyID : : LineHeight , value ) ;
2023-02-03 14:27:09 +03:00
// FIXME: Implement font-variant
2021-07-21 15:39:40 +01:00
return ;
}
2021-07-20 12:05:43 +01:00
if ( property_id = = CSS : : PropertyID : : Flex ) {
2021-08-04 17:48:08 +01:00
if ( value . is_flex ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & flex = value . as_flex ( ) ;
2021-08-04 17:48:08 +01:00
style . set_property ( CSS : : PropertyID : : FlexGrow , flex . grow ( ) ) ;
style . set_property ( CSS : : PropertyID : : FlexShrink , flex . shrink ( ) ) ;
style . set_property ( CSS : : PropertyID : : FlexBasis , flex . basis ( ) ) ;
2021-07-20 12:05:43 +01:00
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : FlexGrow , value ) ;
style . set_property ( CSS : : PropertyID : : FlexShrink , value ) ;
style . set_property ( CSS : : PropertyID : : FlexBasis , value ) ;
2021-07-20 12:05:43 +01:00
return ;
}
2021-05-30 12:25:10 +02:00
if ( property_id = = CSS : : PropertyID : : FlexFlow ) {
2021-08-05 17:19:29 +01:00
if ( value . is_flex_flow ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & flex_flow = value . as_flex_flow ( ) ;
2021-08-05 17:19:29 +01:00
style . set_property ( CSS : : PropertyID : : FlexDirection , flex_flow . flex_direction ( ) ) ;
style . set_property ( CSS : : PropertyID : : FlexWrap , flex_flow . flex_wrap ( ) ) ;
return ;
}
2021-09-23 15:23:04 +01:00
style . set_property ( CSS : : PropertyID : : FlexDirection , value ) ;
style . set_property ( CSS : : PropertyID : : FlexWrap , value ) ;
2021-07-14 19:57:02 +01:00
return ;
}
2023-01-16 19:02:39 +01:00
if ( property_id = = CSS : : PropertyID : : GridArea ) {
if ( value . is_grid_area_shorthand ( ) ) {
auto const & shorthand = value . as_grid_area_shorthand ( ) ;
style . set_property ( CSS : : PropertyID : : GridRowStart , shorthand . row_start ( ) ) ;
style . set_property ( CSS : : PropertyID : : GridColumnStart , shorthand . column_start ( ) ) ;
style . set_property ( CSS : : PropertyID : : GridRowEnd , shorthand . row_end ( ) ) ;
style . set_property ( CSS : : PropertyID : : GridColumnEnd , shorthand . column_end ( ) ) ;
return ;
}
style . set_property ( CSS : : PropertyID : : GridRowStart , value ) ;
style . set_property ( CSS : : PropertyID : : GridColumnStart , value ) ;
style . set_property ( CSS : : PropertyID : : GridRowEnd , value ) ;
style . set_property ( CSS : : PropertyID : : GridColumnEnd , value ) ;
return ;
}
2022-08-23 20:00:16 +02:00
if ( property_id = = CSS : : PropertyID : : GridColumn ) {
if ( value . is_grid_track_placement_shorthand ( ) ) {
auto const & shorthand = value . as_grid_track_placement_shorthand ( ) ;
style . set_property ( CSS : : PropertyID : : GridColumnStart , shorthand . start ( ) ) ;
style . set_property ( CSS : : PropertyID : : GridColumnEnd , shorthand . end ( ) ) ;
return ;
}
style . set_property ( CSS : : PropertyID : : GridColumnStart , value ) ;
style . set_property ( CSS : : PropertyID : : GridColumnEnd , value ) ;
return ;
}
if ( property_id = = CSS : : PropertyID : : GridRow ) {
if ( value . is_grid_track_placement_shorthand ( ) ) {
auto const & shorthand = value . as_grid_track_placement_shorthand ( ) ;
style . set_property ( CSS : : PropertyID : : GridRowStart , shorthand . start ( ) ) ;
style . set_property ( CSS : : PropertyID : : GridRowEnd , shorthand . end ( ) ) ;
return ;
}
style . set_property ( CSS : : PropertyID : : GridRowStart , value ) ;
style . set_property ( CSS : : PropertyID : : GridRowEnd , value ) ;
return ;
}
2022-11-06 12:42:22 +01:00
if ( property_id = = CSS : : PropertyID : : Gap | | property_id = = CSS : : PropertyID : : GridGap ) {
if ( value . is_value_list ( ) ) {
auto const & values_list = value . as_value_list ( ) ;
style . set_property ( CSS : : PropertyID : : RowGap , values_list . values ( ) [ 0 ] ) ;
style . set_property ( CSS : : PropertyID : : ColumnGap , values_list . values ( ) [ 1 ] ) ;
return ;
}
style . set_property ( CSS : : PropertyID : : RowGap , value ) ;
style . set_property ( CSS : : PropertyID : : ColumnGap , value ) ;
return ;
}
if ( property_id = = CSS : : PropertyID : : RowGap | | property_id = = CSS : : PropertyID : : GridRowGap ) {
style . set_property ( CSS : : PropertyID : : RowGap , value ) ;
return ;
}
if ( property_id = = CSS : : PropertyID : : ColumnGap | | property_id = = CSS : : PropertyID : : GridColumnGap ) {
style . set_property ( CSS : : PropertyID : : ColumnGap , value ) ;
return ;
}
2019-11-18 11:49:19 +01:00
style . set_property ( property_id , value ) ;
}
2023-02-20 18:56:08 +01:00
static RefPtr < StyleValue const > get_custom_property ( DOM : : Element const & element , FlyString const & custom_property_name )
2022-03-13 17:07:14 +01:00
{
for ( auto const * current_element = & element ; current_element ; current_element = current_element - > parent_element ( ) ) {
2023-02-17 14:19:16 +00:00
if ( auto it = current_element - > custom_properties ( ) . find ( custom_property_name . to_string ( ) . to_deprecated_string ( ) ) ; it ! = current_element - > custom_properties ( ) . end ( ) )
2022-03-13 17:07:14 +01:00
return it - > value . value ;
}
return nullptr ;
}
2023-02-17 14:19:16 +00:00
bool StyleComputer : : expand_variables ( DOM : : Element & element , StringView property_name , HashMap < FlyString , NonnullRefPtr < PropertyDependencyNode > > & dependencies , Parser : : TokenStream < Parser : : ComponentValue > & source , Vector < Parser : : ComponentValue > & dest ) const
2021-12-03 12:32:12 +00:00
{
2021-12-03 13:10:21 +00:00
// Arbitrary large value chosen to avoid the billion-laughs attack.
// https://www.w3.org/TR/css-variables-1/#long-variables
const size_t MAX_VALUE_COUNT = 16384 ;
2022-09-27 16:46:22 +01:00
if ( source . remaining_token_count ( ) + dest . size ( ) > MAX_VALUE_COUNT ) {
2021-12-03 13:10:21 +00:00
dbgln ( " Stopped expanding CSS variables: maximum length reached. " ) ;
return false ;
}
2021-12-03 12:32:12 +00:00
2023-02-17 14:19:16 +00:00
auto get_dependency_node = [ & ] ( FlyString name ) - > NonnullRefPtr < PropertyDependencyNode > {
2022-02-09 20:11:16 +01:00
if ( auto existing = dependencies . get ( name ) ; existing . has_value ( ) )
2021-12-03 20:00:31 +00:00
return * existing . value ( ) ;
2023-02-17 14:19:16 +00:00
auto new_node = PropertyDependencyNode : : create ( name . to_string ( ) ) ;
2022-02-09 20:11:16 +01:00
dependencies . set ( name , new_node ) ;
return new_node ;
2021-12-03 20:00:31 +00:00
} ;
2022-09-27 16:46:22 +01:00
while ( source . has_next_token ( ) ) {
auto const & value = source . next_token ( ) ;
2022-11-02 20:19:03 +01:00
if ( ! value . is_function ( ) ) {
dest . empend ( value ) ;
continue ;
}
2023-03-10 08:48:54 +01:00
if ( ! value . function ( ) . name ( ) . equals_ignoring_ascii_case ( " var " sv ) ) {
2022-11-02 20:19:03 +01:00
auto const & source_function = value . function ( ) ;
Vector < Parser : : ComponentValue > function_values ;
Parser : : TokenStream source_function_contents { source_function . values ( ) } ;
if ( ! expand_variables ( element , property_name , dependencies , source_function_contents , function_values ) )
return false ;
2023-02-14 18:55:59 +00:00
NonnullRefPtr < Parser : : Function > function = Parser : : Function : : create ( FlyString : : from_utf8 ( source_function . name ( ) ) . release_value_but_fixme_should_propagate_errors ( ) , move ( function_values ) ) ;
2022-11-02 20:19:03 +01:00
dest . empend ( function ) ;
continue ;
}
2021-12-03 12:32:12 +00:00
2022-11-02 20:19:03 +01:00
Parser : : TokenStream var_contents { value . function ( ) . values ( ) } ;
var_contents . skip_whitespace ( ) ;
if ( ! var_contents . has_next_token ( ) )
return false ;
auto const & custom_property_name_token = var_contents . next_token ( ) ;
if ( ! custom_property_name_token . is ( Parser : : Token : : Type : : Ident ) )
return false ;
auto custom_property_name = custom_property_name_token . token ( ) . ident ( ) ;
if ( ! custom_property_name . starts_with ( " -- " sv ) )
return false ;
// Detect dependency cycles. https://www.w3.org/TR/css-variables-1/#cycles
// We do not do this by the spec, since we are not keeping a graph of var dependencies around,
// but rebuilding it every time.
if ( custom_property_name = = property_name )
return false ;
2023-02-17 14:19:16 +00:00
auto parent = get_dependency_node ( FlyString : : from_utf8 ( property_name ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
auto child = get_dependency_node ( FlyString : : from_utf8 ( custom_property_name ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-11-02 20:19:03 +01:00
parent - > add_child ( child ) ;
if ( parent - > has_cycles ( ) )
return false ;
2023-02-17 14:19:16 +00:00
if ( auto custom_property_value = get_custom_property ( element , FlyString : : from_utf8 ( custom_property_name ) . release_value_but_fixme_should_propagate_errors ( ) ) ) {
2022-11-02 20:19:03 +01:00
VERIFY ( custom_property_value - > is_unresolved ( ) ) ;
Parser : : TokenStream custom_property_tokens { custom_property_value - > as_unresolved ( ) . values ( ) } ;
if ( ! expand_variables ( element , custom_property_name , dependencies , custom_property_tokens , dest ) )
return false ;
continue ;
}
2021-12-03 12:32:12 +00:00
2022-11-02 20:19:03 +01:00
// Use the provided fallback value, if any.
var_contents . skip_whitespace ( ) ;
if ( var_contents . has_next_token ( ) ) {
auto const & comma_token = var_contents . next_token ( ) ;
if ( ! comma_token . is ( Parser : : Token : : Type : : Comma ) )
return false ;
var_contents . skip_whitespace ( ) ;
if ( ! expand_variables ( element , property_name , dependencies , var_contents , dest ) )
return false ;
}
}
return true ;
}
2021-12-03 20:00:31 +00:00
2022-11-02 20:19:03 +01:00
bool StyleComputer : : expand_unresolved_values ( DOM : : Element & element , StringView property_name , Parser : : TokenStream < Parser : : ComponentValue > & source , Vector < Parser : : ComponentValue > & dest ) const
{
// FIXME: Do this better!
// We build a copy of the tree of ComponentValues, with all var()s and attr()s replaced with their contents.
// This is a very naive solution, and we could do better if the CSS Parser could accept tokens one at a time.
2021-12-03 12:32:12 +00:00
2022-11-02 20:19:03 +01:00
while ( source . has_next_token ( ) ) {
auto const & value = source . next_token ( ) ;
if ( value . is_function ( ) ) {
2023-03-10 08:48:54 +01:00
if ( value . function ( ) . name ( ) . equals_ignoring_ascii_case ( " attr " sv ) ) {
2022-03-29 23:33:50 +02:00
// https://drafts.csswg.org/css-values-5/#attr-substitution
2022-09-27 16:46:22 +01:00
Parser : : TokenStream attr_contents { value . function ( ) . values ( ) } ;
attr_contents . skip_whitespace ( ) ;
if ( ! attr_contents . has_next_token ( ) )
2022-03-29 23:33:50 +02:00
return false ;
2022-09-27 16:46:22 +01:00
auto const & attr_name_token = attr_contents . next_token ( ) ;
2022-04-12 15:10:08 +01:00
if ( ! attr_name_token . is ( Parser : : Token : : Type : : Ident ) )
2022-03-29 23:33:50 +02:00
return false ;
auto attr_name = attr_name_token . token ( ) . ident ( ) ;
auto attr_value = element . get_attribute ( attr_name ) ;
// 1. If the attr() function has a substitution value, replace the attr() function by the substitution value.
if ( ! attr_value . is_null ( ) ) {
// FIXME: attr() should also accept an optional type argument, not just strings.
2023-03-09 18:07:49 +01:00
dest . empend ( Parser : : Token : : of_string ( FlyString : : from_deprecated_fly_string ( attr_value ) . release_value_but_fixme_should_propagate_errors ( ) ) ) ;
2022-03-29 23:33:50 +02:00
continue ;
}
// 2. Otherwise, if the attr() function has a fallback value as its last argument, replace the attr() function by the fallback value.
// If there are any var() or attr() references in the fallback, substitute them as well.
2022-09-27 16:46:22 +01:00
attr_contents . skip_whitespace ( ) ;
if ( attr_contents . has_next_token ( ) ) {
auto const & comma_token = attr_contents . next_token ( ) ;
if ( ! comma_token . is ( Parser : : Token : : Type : : Comma ) )
return false ;
attr_contents . skip_whitespace ( ) ;
2022-11-02 20:19:03 +01:00
if ( ! expand_unresolved_values ( element , property_name , attr_contents , dest ) )
2022-03-29 23:33:50 +02:00
return false ;
continue ;
}
// 3. Otherwise, the property containing the attr() function is invalid at computed-value time.
return false ;
2021-12-03 12:32:12 +00:00
}
2023-03-10 08:48:54 +01:00
if ( value . function ( ) . name ( ) . equals_ignoring_ascii_case ( " calc " sv ) ) {
2022-11-02 20:26:11 +01:00
auto const & calc_function = value . function ( ) ;
if ( auto calc_value = CSS : : Parser : : Parser : : parse_calculated_value ( { } , Parser : : ParsingContext { document ( ) } , calc_function . values ( ) ) ) {
switch ( calc_value - > resolved_type ( ) ) {
case CalculatedStyleValue : : ResolvedType : : Integer : {
auto resolved_value = calc_value - > resolve_integer ( ) ;
dest . empend ( Parser : : Token : : create_number ( resolved_value . value ( ) ) ) ;
continue ;
}
case CalculatedStyleValue : : ResolvedType : : Percentage : {
auto resolved_value = calc_value - > resolve_percentage ( ) ;
dest . empend ( Parser : : Token : : create_percentage ( resolved_value . value ( ) . value ( ) ) ) ;
continue ;
}
default :
dbgln ( " FIXME: Unimplement calc() expansion in StyleComputer " ) ;
break ;
}
}
}
2022-02-09 20:11:16 +01:00
auto const & source_function = value . function ( ) ;
2022-04-12 12:13:10 +01:00
Vector < Parser : : ComponentValue > function_values ;
2022-09-27 16:46:22 +01:00
Parser : : TokenStream source_function_contents { source_function . values ( ) } ;
2022-11-02 20:19:03 +01:00
if ( ! expand_unresolved_values ( element , property_name , source_function_contents , function_values ) )
2021-12-03 12:32:12 +00:00
return false ;
2023-02-14 18:55:59 +00:00
// FIXME: This would be much nicer if we could access the source_function's FlyString value directly.
NonnullRefPtr < Parser : : Function > function = Parser : : Function : : create ( FlyString : : from_utf8 ( source_function . name ( ) ) . release_value_but_fixme_should_propagate_errors ( ) , move ( function_values ) ) ;
2021-12-03 12:32:12 +00:00
dest . empend ( function ) ;
continue ;
}
if ( value . is_block ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & source_block = value . block ( ) ;
2022-09-27 16:46:22 +01:00
Parser : : TokenStream source_block_values { source_block . values ( ) } ;
2022-04-12 12:13:10 +01:00
Vector < Parser : : ComponentValue > block_values ;
2022-11-02 20:19:03 +01:00
if ( ! expand_unresolved_values ( element , property_name , source_block_values , block_values ) )
2021-12-03 12:32:12 +00:00
return false ;
2022-04-12 16:05:44 +01:00
NonnullRefPtr < Parser : : Block > block = Parser : : Block : : create ( source_block . token ( ) , move ( block_values ) ) ;
2022-04-12 14:08:26 +01:00
dest . empend ( move ( block ) ) ;
2021-12-03 12:32:12 +00:00
continue ;
}
dest . empend ( value . token ( ) ) ;
}
return true ;
}
2022-03-13 17:07:14 +01:00
RefPtr < StyleValue > StyleComputer : : resolve_unresolved_style_value ( DOM : : Element & element , PropertyID property_id , UnresolvedStyleValue const & unresolved ) const
2021-12-03 12:32:12 +00:00
{
2022-03-29 23:33:50 +02:00
// Unresolved always contains a var() or attr(), unless it is a custom property's value, in which case we shouldn't be trying
2021-12-03 12:32:12 +00:00
// to produce a different StyleValue from it.
2022-03-29 23:33:50 +02:00
VERIFY ( unresolved . contains_var_or_attr ( ) ) ;
2021-12-03 12:32:12 +00:00
2022-11-02 20:19:03 +01:00
Parser : : TokenStream unresolved_values_without_variables_expanded { unresolved . values ( ) } ;
Vector < Parser : : ComponentValue > values_with_variables_expanded ;
2023-02-17 14:19:16 +00:00
HashMap < FlyString , NonnullRefPtr < PropertyDependencyNode > > dependencies ;
2022-11-02 20:19:03 +01:00
if ( ! expand_variables ( element , string_from_property_id ( property_id ) , dependencies , unresolved_values_without_variables_expanded , values_with_variables_expanded ) )
return { } ;
Parser : : TokenStream unresolved_values_with_variables_expanded { values_with_variables_expanded } ;
Vector < Parser : : ComponentValue > expanded_values ;
if ( ! expand_unresolved_values ( element , string_from_property_id ( property_id ) , unresolved_values_with_variables_expanded , expanded_values ) )
2021-12-03 12:32:12 +00:00
return { } ;
2022-04-12 12:00:07 +01:00
if ( auto parsed_value = Parser : : Parser : : parse_css_value ( { } , Parser : : ParsingContext { document ( ) } , property_id , expanded_values ) )
2021-12-03 12:32:12 +00:00
return parsed_value . release_nonnull ( ) ;
return { } ;
}
2022-03-13 17:07:14 +01:00
void StyleComputer : : cascade_declarations ( StyleProperties & style , DOM : : Element & element , Vector < MatchingRule > const & matching_rules , CascadeOrigin cascade_origin , Important important ) const
2021-09-21 11:38:18 +02:00
{
2022-02-09 20:11:16 +01:00
for ( auto const & match : matching_rules ) {
for ( auto const & property : verify_cast < PropertyOwningCSSStyleDeclaration > ( match . rule - > declaration ( ) ) . properties ( ) ) {
2021-09-21 11:38:18 +02:00
if ( important ! = property . important )
continue ;
2021-05-24 23:02:58 +02:00
auto property_value = property . value ;
2021-12-03 12:32:12 +00:00
if ( property . value - > is_unresolved ( ) ) {
2022-03-13 17:07:14 +01:00
if ( auto resolved = resolve_unresolved_style_value ( element , property . property_id , property . value - > as_unresolved ( ) ) )
2021-12-03 12:32:12 +00:00
property_value = resolved . release_nonnull ( ) ;
}
2022-12-31 11:11:41 +01:00
if ( ! property_value - > is_unresolved ( ) )
set_property_expanding_shorthands ( style , property . property_id , property_value , m_document ) ;
2021-09-21 11:38:18 +02:00
}
}
if ( cascade_origin = = CascadeOrigin : : Author ) {
2022-02-09 20:11:16 +01:00
if ( auto const * inline_style = verify_cast < ElementInlineCSSStyleDeclaration > ( element . inline_style ( ) ) ) {
for ( auto const & property : inline_style - > properties ( ) ) {
2021-09-21 11:38:18 +02:00
if ( important ! = property . important )
2021-09-20 20:11:33 +02:00
continue ;
2022-03-26 16:06:31 +01:00
auto property_value = property . value ;
if ( property . value - > is_unresolved ( ) ) {
if ( auto resolved = resolve_unresolved_style_value ( element , property . property_id , property . value - > as_unresolved ( ) ) )
property_value = resolved . release_nonnull ( ) ;
}
2022-12-31 11:11:41 +01:00
if ( ! property_value - > is_unresolved ( ) )
set_property_expanding_shorthands ( style , property . property_id , property_value , m_document ) ;
2021-09-20 20:11:33 +02:00
}
2019-06-28 21:17:34 +02:00
}
}
2021-09-21 11:38:18 +02:00
}
2022-12-06 03:08:20 -03:00
static ErrorOr < void > cascade_custom_properties ( DOM : : Element & element , Vector < MatchingRule > const & matching_rules )
2022-02-10 17:04:31 +01:00
{
2022-03-03 13:40:06 +01:00
size_t needed_capacity = 0 ;
for ( auto const & matching_rule : matching_rules )
needed_capacity + = verify_cast < PropertyOwningCSSStyleDeclaration > ( matching_rule . rule - > declaration ( ) ) . custom_properties ( ) . size ( ) ;
2022-03-26 16:10:30 +01:00
if ( auto const * inline_style = verify_cast < PropertyOwningCSSStyleDeclaration > ( element . inline_style ( ) ) )
needed_capacity + = inline_style - > custom_properties ( ) . size ( ) ;
2022-03-03 13:40:06 +01:00
2023-01-08 19:23:00 -05:00
HashMap < DeprecatedFlyString , StyleProperty > custom_properties ;
2022-12-06 03:08:20 -03:00
TRY ( custom_properties . try_ensure_capacity ( needed_capacity ) ) ;
2022-02-10 17:04:31 +01:00
for ( auto const & matching_rule : matching_rules ) {
2022-03-03 13:40:06 +01:00
for ( auto const & it : verify_cast < PropertyOwningCSSStyleDeclaration > ( matching_rule . rule - > declaration ( ) ) . custom_properties ( ) )
2022-03-26 16:10:30 +01:00
custom_properties . set ( it . key , it . value ) ;
}
if ( auto const * inline_style = verify_cast < PropertyOwningCSSStyleDeclaration > ( element . inline_style ( ) ) ) {
for ( auto const & it : inline_style - > custom_properties ( ) )
2022-03-03 13:40:06 +01:00
custom_properties . set ( it . key , it . value ) ;
2022-02-10 17:04:31 +01:00
}
2022-03-03 13:40:06 +01:00
element . set_custom_properties ( move ( custom_properties ) ) ;
2022-12-06 03:08:20 -03:00
return { } ;
2022-02-10 17:04:31 +01:00
}
2021-10-15 19:46:52 +01:00
// https://www.w3.org/TR/css-cascade/#cascading
2022-12-06 03:08:20 -03:00
ErrorOr < void > StyleComputer : : compute_cascaded_values ( StyleProperties & style , DOM : : Element & element , Optional < CSS : : Selector : : PseudoElement > pseudo_element ) const
2021-09-21 11:38:18 +02:00
{
// First, we collect all the CSS rules whose selectors match `element`:
MatchingRuleSet matching_rule_set ;
2022-02-24 15:54:12 +00:00
matching_rule_set . user_agent_rules = collect_matching_rules ( element , CascadeOrigin : : UserAgent , pseudo_element ) ;
2021-09-21 11:38:18 +02:00
sort_matching_rules ( matching_rule_set . user_agent_rules ) ;
2022-02-24 15:54:12 +00:00
matching_rule_set . author_rules = collect_matching_rules ( element , CascadeOrigin : : Author , pseudo_element ) ;
2021-09-21 11:38:18 +02:00
sort_matching_rules ( matching_rule_set . author_rules ) ;
2022-02-10 17:04:31 +01:00
// Then we resolve all the CSS custom properties ("variables") for this element:
2022-03-13 17:07:14 +01:00
// FIXME: Look into how custom properties should interact with pseudo elements and support that properly.
if ( ! pseudo_element . has_value ( ) )
2022-12-06 03:08:20 -03:00
TRY ( cascade_custom_properties ( element , matching_rule_set . author_rules ) ) ;
2022-02-10 17:04:31 +01:00
2021-09-21 11:38:18 +02:00
// Then we apply the declarations from the matched rules in cascade order:
// Normal user agent declarations
2022-03-13 17:07:14 +01:00
cascade_declarations ( style , element , matching_rule_set . user_agent_rules , CascadeOrigin : : UserAgent , Important : : No ) ;
2021-09-21 11:38:18 +02:00
// FIXME: Normal user declarations
// Author presentational hints (NOTE: The spec doesn't say exactly how to prioritize these.)
element . apply_presentational_hints ( style ) ;
2022-03-12 01:25:43 +01:00
// Normal author declarations
2022-03-13 17:07:14 +01:00
cascade_declarations ( style , element , matching_rule_set . author_rules , CascadeOrigin : : Author , Important : : No ) ;
2022-03-12 01:25:43 +01:00
2021-09-21 11:38:18 +02:00
// FIXME: Animation declarations [css-animations-1]
// Important author declarations
2022-03-13 17:07:14 +01:00
cascade_declarations ( style , element , matching_rule_set . author_rules , CascadeOrigin : : Author , Important : : Yes ) ;
2021-09-21 11:38:18 +02:00
// FIXME: Important user declarations
// Important user agent declarations
2022-03-13 17:07:14 +01:00
cascade_declarations ( style , element , matching_rule_set . user_agent_rules , CascadeOrigin : : UserAgent , Important : : Yes ) ;
2021-09-21 11:38:18 +02:00
// FIXME: Transition declarations [css-transitions-1]
2022-12-06 03:08:20 -03:00
return { } ;
2021-09-21 11:38:18 +02:00
}
2022-11-05 17:08:16 +01:00
static DOM : : Element const * element_to_inherit_style_from ( DOM : : Element const * element , Optional < CSS : : Selector : : PseudoElement > pseudo_element )
2021-09-23 13:13:51 +02:00
{
2022-02-24 15:54:12 +00:00
// Pseudo-elements treat their originating element as their parent.
DOM : : Element const * parent_element = nullptr ;
if ( pseudo_element . has_value ( ) ) {
parent_element = element ;
} else if ( element ) {
2022-11-05 17:08:16 +01:00
parent_element = element - > parent_or_shadow_host_element ( ) ;
2022-02-24 15:54:12 +00:00
}
return parent_element ;
}
2023-02-28 16:54:25 +00:00
static NonnullRefPtr < StyleValue const > get_inherit_value ( JS : : Realm & initial_value_context_realm , CSS : : PropertyID property_id , DOM : : Element const * element , Optional < CSS : : Selector : : PseudoElement > pseudo_element )
2022-02-24 15:54:12 +00:00
{
2022-11-05 17:08:16 +01:00
auto * parent_element = element_to_inherit_style_from ( element , pseudo_element ) ;
2022-02-24 15:54:12 +00:00
2022-03-15 19:41:35 +01:00
if ( ! parent_element | | ! parent_element - > computed_css_values ( ) )
2023-02-28 16:54:25 +00:00
return property_initial_value ( initial_value_context_realm , property_id ) ;
2022-04-14 11:52:35 +01:00
return parent_element - > computed_css_values ( ) - > property ( property_id ) ;
2021-09-23 13:13:51 +02:00
} ;
2022-02-24 15:54:12 +00:00
void StyleComputer : : compute_defaulted_property_value ( StyleProperties & style , DOM : : Element const * element , CSS : : PropertyID property_id , Optional < CSS : : Selector : : PseudoElement > pseudo_element ) const
2021-09-21 11:38:18 +02:00
{
// FIXME: If we don't know the correct initial value for a property, we fall back to InitialStyleValue.
2022-02-18 20:21:49 +01:00
auto & value_slot = style . m_property_values [ to_underlying ( property_id ) ] ;
if ( ! value_slot ) {
2021-09-23 13:13:51 +02:00
if ( is_inherited_property ( property_id ) )
2023-02-28 16:54:25 +00:00
style . m_property_values [ to_underlying ( property_id ) ] = get_inherit_value ( document ( ) . realm ( ) , property_id , element , pseudo_element ) ;
2021-09-23 13:13:51 +02:00
else
2023-02-28 16:54:25 +00:00
style . m_property_values [ to_underlying ( property_id ) ] = property_initial_value ( document ( ) . realm ( ) , property_id ) ;
2021-09-23 13:13:51 +02:00
return ;
}
2021-09-21 11:38:18 +02:00
2022-02-18 20:21:49 +01:00
if ( value_slot - > is_initial ( ) ) {
2023-02-28 16:54:25 +00:00
value_slot = property_initial_value ( document ( ) . realm ( ) , property_id ) ;
2021-09-23 13:13:51 +02:00
return ;
}
2021-09-21 11:38:18 +02:00
2022-02-18 20:21:49 +01:00
if ( value_slot - > is_inherit ( ) ) {
2023-02-28 16:54:25 +00:00
value_slot = get_inherit_value ( document ( ) . realm ( ) , property_id , element , pseudo_element ) ;
2021-09-23 13:13:51 +02:00
return ;
}
2022-03-12 17:00:29 +01:00
// https://www.w3.org/TR/css-cascade-4/#inherit-initial
// If the cascaded value of a property is the unset keyword,
if ( value_slot - > is_unset ( ) ) {
if ( is_inherited_property ( property_id ) ) {
// then if it is an inherited property, this is treated as inherit,
2023-02-28 16:54:25 +00:00
value_slot = get_inherit_value ( document ( ) . realm ( ) , property_id , element , pseudo_element ) ;
2022-03-12 17:00:29 +01:00
} else {
// and if it is not, this is treated as initial.
2023-02-28 16:54:25 +00:00
value_slot = property_initial_value ( document ( ) . realm ( ) , property_id ) ;
2022-03-12 17:00:29 +01:00
}
}
2021-09-23 13:13:51 +02:00
}
2021-10-15 19:46:52 +01:00
// https://www.w3.org/TR/css-cascade/#defaulting
2022-02-24 15:54:12 +00:00
void StyleComputer : : compute_defaulted_values ( StyleProperties & style , DOM : : Element const * element , Optional < CSS : : Selector : : PseudoElement > pseudo_element ) const
2021-09-23 13:13:51 +02:00
{
2021-09-21 11:38:18 +02:00
// Walk the list of all known CSS properties and:
// - Add them to `style` if they are missing.
// - Resolve `inherit` and `initial` as needed.
2021-09-21 15:51:51 +02:00
for ( auto i = to_underlying ( CSS : : first_longhand_property_id ) ; i < = to_underlying ( CSS : : last_longhand_property_id ) ; + + i ) {
2021-09-21 11:38:18 +02:00
auto property_id = ( CSS : : PropertyID ) i ;
2022-02-24 15:54:12 +00:00
compute_defaulted_property_value ( style , element , property_id , pseudo_element ) ;
2021-09-23 13:13:51 +02:00
}
}
2021-09-21 11:38:18 +02:00
2022-11-08 17:29:52 +00:00
CSSPixels StyleComputer : : root_element_font_size ( ) const
2022-03-16 12:30:06 +01:00
{
2022-09-08 12:41:42 +02:00
constexpr float default_root_element_font_size = 16 ;
2022-03-16 12:30:06 +01:00
2022-03-19 18:08:52 +01:00
auto const * root_element = m_document . first_child_of_type < HTML : : HTMLHtmlElement > ( ) ;
2022-03-16 12:30:06 +01:00
if ( ! root_element )
return default_root_element_font_size ;
auto const * computed_root_style = root_element - > computed_css_values ( ) ;
if ( ! computed_root_style )
return default_root_element_font_size ;
2022-04-14 11:52:35 +01:00
auto root_value = computed_root_style - > property ( CSS : : PropertyID : : FontSize ) ;
2022-03-16 12:30:06 +01:00
2022-04-14 11:52:35 +01:00
return root_value - > to_length ( ) . to_px ( viewport_rect ( ) , computed_root_style - > computed_font ( ) . pixel_metrics ( ) , default_root_element_font_size , default_root_element_font_size ) ;
2022-03-16 12:30:06 +01:00
}
2022-02-24 15:54:12 +00:00
void StyleComputer : : compute_font ( StyleProperties & style , DOM : : Element const * element , Optional < CSS : : Selector : : PseudoElement > pseudo_element ) const
2021-09-23 13:13:51 +02:00
{
// To compute the font, first ensure that we've defaulted the relevant CSS font properties.
// FIXME: This should be more sophisticated.
2022-02-24 15:54:12 +00:00
compute_defaulted_property_value ( style , element , CSS : : PropertyID : : FontFamily , pseudo_element ) ;
compute_defaulted_property_value ( style , element , CSS : : PropertyID : : FontSize , pseudo_element ) ;
2023-02-03 14:27:09 +03:00
compute_defaulted_property_value ( style , element , CSS : : PropertyID : : FontStretch , pseudo_element ) ;
2022-02-24 15:54:12 +00:00
compute_defaulted_property_value ( style , element , CSS : : PropertyID : : FontStyle , pseudo_element ) ;
compute_defaulted_property_value ( style , element , CSS : : PropertyID : : FontWeight , pseudo_element ) ;
2022-11-05 17:08:16 +01:00
auto * parent_element = element_to_inherit_style_from ( element , pseudo_element ) ;
2021-09-23 13:13:51 +02:00
2022-04-14 11:52:35 +01:00
auto font_size = style . property ( CSS : : PropertyID : : FontSize ) ;
auto font_style = style . property ( CSS : : PropertyID : : FontStyle ) ;
auto font_weight = style . property ( CSS : : PropertyID : : FontWeight ) ;
2023-02-04 23:08:48 +03:00
auto font_stretch = style . property ( CSS : : PropertyID : : FontStretch ) ;
2021-09-23 13:13:51 +02:00
2023-02-04 23:00:34 +03:00
int width = Gfx : : FontWidth : : Normal ;
2023-02-04 23:08:48 +03:00
if ( font_stretch - > is_identifier ( ) ) {
switch ( static_cast < IdentifierStyleValue const & > ( * font_stretch ) . id ( ) ) {
case CSS : : ValueID : : UltraCondensed :
width = Gfx : : FontWidth : : UltraCondensed ;
break ;
case CSS : : ValueID : : ExtraCondensed :
width = Gfx : : FontWidth : : ExtraCondensed ;
break ;
case CSS : : ValueID : : Condensed :
width = Gfx : : FontWidth : : Condensed ;
break ;
case CSS : : ValueID : : SemiCondensed :
width = Gfx : : FontWidth : : SemiCondensed ;
break ;
case CSS : : ValueID : : Normal :
width = Gfx : : FontWidth : : Normal ;
break ;
case CSS : : ValueID : : SemiExpanded :
width = Gfx : : FontWidth : : SemiExpanded ;
break ;
case CSS : : ValueID : : Expanded :
width = Gfx : : FontWidth : : Expanded ;
break ;
case CSS : : ValueID : : ExtraExpanded :
width = Gfx : : FontWidth : : ExtraExpanded ;
break ;
case CSS : : ValueID : : UltraExpanded :
width = Gfx : : FontWidth : : UltraExpanded ;
break ;
default :
break ;
}
} else if ( font_stretch - > is_percentage ( ) ) {
float percentage = font_stretch - > as_percentage ( ) . percentage ( ) . value ( ) ;
if ( percentage < = 50 ) {
width = Gfx : : FontWidth : : UltraCondensed ;
} else if ( percentage < = 62.5f ) {
width = Gfx : : FontWidth : : ExtraCondensed ;
} else if ( percentage < = 75.0f ) {
width = Gfx : : FontWidth : : Condensed ;
} else if ( percentage < = 87.5f ) {
width = Gfx : : FontWidth : : SemiCondensed ;
} else if ( percentage < = 100.0f ) {
width = Gfx : : FontWidth : : Normal ;
} else if ( percentage < = 112.5f ) {
width = Gfx : : FontWidth : : SemiExpanded ;
} else if ( percentage < = 125.0f ) {
width = Gfx : : FontWidth : : Expanded ;
} else if ( percentage < = 150.0f ) {
width = Gfx : : FontWidth : : ExtraExpanded ;
} else {
width = Gfx : : FontWidth : : UltraExpanded ;
}
}
2023-02-04 23:00:34 +03:00
2021-09-23 13:13:51 +02:00
int weight = Gfx : : FontWeight : : Regular ;
if ( font_weight - > is_identifier ( ) ) {
switch ( static_cast < IdentifierStyleValue const & > ( * font_weight ) . id ( ) ) {
case CSS : : ValueID : : Normal :
weight = Gfx : : FontWeight : : Regular ;
break ;
case CSS : : ValueID : : Bold :
weight = Gfx : : FontWeight : : Bold ;
break ;
case CSS : : ValueID : : Lighter :
// FIXME: This should be relative to the parent.
weight = Gfx : : FontWeight : : Regular ;
break ;
case CSS : : ValueID : : Bolder :
// FIXME: This should be relative to the parent.
weight = Gfx : : FontWeight : : Bold ;
break ;
default :
break ;
2021-09-21 11:38:18 +02:00
}
2021-10-19 16:43:56 +01:00
} else if ( font_weight - > has_integer ( ) ) {
int font_weight_integer = font_weight - > to_integer ( ) ;
2021-09-23 13:13:51 +02:00
if ( font_weight_integer < = Gfx : : FontWeight : : Regular )
weight = Gfx : : FontWeight : : Regular ;
else if ( font_weight_integer < = Gfx : : FontWeight : : Bold )
weight = Gfx : : FontWeight : : Bold ;
else
weight = Gfx : : FontWeight : : Black ;
2022-01-27 15:54:38 +00:00
} else if ( font_weight - > is_calculated ( ) ) {
2023-02-20 18:56:08 +01:00
auto maybe_weight = const_cast < CalculatedStyleValue & > ( font_weight - > as_calculated ( ) ) . resolve_integer ( ) ;
2022-01-27 15:54:38 +00:00
if ( maybe_weight . has_value ( ) )
weight = maybe_weight . value ( ) ;
2021-09-23 13:13:51 +02:00
}
2021-09-21 11:38:18 +02:00
2021-09-23 13:13:51 +02:00
bool bold = weight > Gfx : : FontWeight : : Regular ;
2022-11-30 16:17:57 +01:00
// FIXME: Should be based on "user's default font size"
2022-09-08 12:41:42 +02:00
float font_size_in_px = 16 ;
2021-09-23 13:13:51 +02:00
if ( font_size - > is_identifier ( ) ) {
2022-11-30 16:17:57 +01:00
// https://w3c.github.io/csswg-drafts/css-fonts/#absolute-size-mapping
AK : : HashMap < Web : : CSS : : ValueID , float > absolute_size_mapping = {
{ CSS : : ValueID : : XxSmall , 0.6 } ,
{ CSS : : ValueID : : XSmall , 0.75 } ,
{ CSS : : ValueID : : Small , 8.0 / 9.0 } ,
{ CSS : : ValueID : : Medium , 1.0 } ,
{ CSS : : ValueID : : Large , 1.2 } ,
{ CSS : : ValueID : : XLarge , 1.5 } ,
{ CSS : : ValueID : : XxLarge , 2.0 } ,
{ CSS : : ValueID : : XxxLarge , 3.0 } ,
2022-11-30 16:56:33 +01:00
{ CSS : : ValueID : : Smaller , 0.8 } ,
{ CSS : : ValueID : : Larger , 1.25 } ,
2022-11-30 16:17:57 +01:00
} ;
2022-11-30 16:56:33 +01:00
2022-11-30 16:17:57 +01:00
auto const identifier = static_cast < IdentifierStyleValue const & > ( * font_size ) . id ( ) ;
2022-11-30 16:56:33 +01:00
// https://w3c.github.io/csswg-drafts/css-fonts/#valdef-font-size-relative-size
// TODO: If the parent element has a keyword font size in the absolute size keyword mapping table,
// larger may compute the font size to the next entry in the table,
// and smaller may compute the font size to the previous entry in the table.
2022-11-30 16:17:57 +01:00
if ( identifier = = CSS : : ValueID : : Smaller | | identifier = = CSS : : ValueID : : Larger ) {
2022-11-30 16:56:33 +01:00
if ( parent_element & & parent_element - > computed_css_values ( ) ) {
font_size_in_px = parent_element - > computed_css_values ( ) - > computed_font ( ) . pixel_metrics ( ) . size ;
}
2021-09-23 13:13:51 +02:00
}
2022-11-30 16:56:33 +01:00
auto const multiplier = absolute_size_mapping . get ( identifier ) . value_or ( 1.0 ) ;
font_size_in_px * = multiplier ;
2021-09-23 13:13:51 +02:00
} else {
2022-11-08 17:29:52 +00:00
auto root_font_size = root_element_font_size ( ) ;
2021-09-23 13:13:51 +02:00
2022-03-28 12:03:44 +02:00
Gfx : : FontPixelMetrics font_metrics ;
2022-03-15 19:41:35 +01:00
if ( parent_element & & parent_element - > computed_css_values ( ) )
2022-03-28 12:03:44 +02:00
font_metrics = parent_element - > computed_css_values ( ) - > computed_font ( ) . pixel_metrics ( ) ;
2021-09-23 13:13:51 +02:00
else
2022-09-17 21:25:50 +02:00
font_metrics = Platform : : FontPlugin : : the ( ) . default_font ( ) . pixel_metrics ( ) ;
2021-09-23 13:13:51 +02:00
2022-11-08 17:29:52 +00:00
auto parent_font_size = [ & ] ( ) - > CSSPixels {
2022-03-16 12:30:06 +01:00
if ( ! parent_element | | ! parent_element - > computed_css_values ( ) )
2022-03-23 14:10:35 +01:00
return font_size_in_px ;
2022-04-14 11:52:35 +01:00
auto value = parent_element - > computed_css_values ( ) - > property ( CSS : : PropertyID : : FontSize ) ;
2022-03-16 12:30:06 +01:00
if ( value - > is_length ( ) ) {
auto length = static_cast < LengthStyleValue const & > ( * value ) . to_length ( ) ;
if ( length . is_absolute ( ) | | length . is_relative ( ) )
2022-03-23 14:10:35 +01:00
return length . to_px ( viewport_rect ( ) , font_metrics , font_size_in_px , root_font_size ) ;
2022-03-16 12:30:06 +01:00
}
2022-03-23 14:10:35 +01:00
return font_size_in_px ;
2022-03-16 12:30:06 +01:00
} ;
2021-09-23 13:13:51 +02:00
Optional < Length > maybe_length ;
2022-01-18 17:01:15 +00:00
if ( font_size - > is_percentage ( ) ) {
// Percentages refer to parent element's font size
2022-03-16 12:30:06 +01:00
maybe_length = Length : : make_px ( font_size - > as_percentage ( ) . percentage ( ) . as_fraction ( ) * parent_font_size ( ) ) ;
2022-01-18 17:01:15 +00:00
} else if ( font_size - > is_length ( ) ) {
maybe_length = font_size - > to_length ( ) ;
2021-09-23 13:13:51 +02:00
} else if ( font_size - > is_calculated ( ) ) {
2023-02-20 18:56:08 +01:00
maybe_length = Length : : make_calculated ( const_cast < CalculatedStyleValue & > ( font_size - > as_calculated ( ) ) ) ;
2021-09-23 13:13:51 +02:00
}
if ( maybe_length . has_value ( ) ) {
2021-09-24 15:47:42 +02:00
// FIXME: Support font-size: calc(...)
2022-01-27 15:54:38 +00:00
// Theoretically we can do this now, but to resolve it we need a layout_node which we might not have. :^(
2021-09-24 15:47:42 +02:00
if ( ! maybe_length - > is_calculated ( ) ) {
2022-11-08 17:29:52 +00:00
auto px = maybe_length . value ( ) . to_px ( viewport_rect ( ) , font_metrics , parent_font_size ( ) , root_font_size ) . value ( ) ;
2021-09-24 15:47:42 +02:00
if ( px ! = 0 )
2022-03-23 14:10:35 +01:00
font_size_in_px = px ;
2021-09-24 15:47:42 +02:00
}
2021-09-21 11:38:18 +02:00
}
2021-09-23 13:13:51 +02:00
}
2022-07-11 17:32:29 +00:00
int slope = Gfx : : name_to_slope ( " Normal " sv ) ;
2022-02-19 21:21:20 +01:00
// FIXME: Implement oblique <angle>
if ( font_style - > is_identifier ( ) ) {
switch ( static_cast < IdentifierStyleValue const & > ( * font_style ) . id ( ) ) {
case CSS : : ValueID : : Italic :
2022-07-11 17:32:29 +00:00
slope = Gfx : : name_to_slope ( " Italic " sv ) ;
2022-02-19 21:21:20 +01:00
break ;
case CSS : : ValueID : : Oblique :
2022-07-11 17:32:29 +00:00
slope = Gfx : : name_to_slope ( " Oblique " sv ) ;
2022-02-19 21:21:20 +01:00
break ;
case CSS : : ValueID : : Normal :
default :
break ;
}
}
2021-09-23 13:13:51 +02:00
// FIXME: Implement the full font-matching algorithm: https://www.w3.org/TR/css-fonts-4/#font-matching-algorithm
2021-09-21 11:38:18 +02:00
2021-09-23 13:13:51 +02:00
// Note: This is modified by the find_font() lambda
FontSelector font_selector ;
bool monospace = false ;
2023-02-20 18:56:08 +01:00
auto find_font = [ & ] ( String const & family ) - > RefPtr < Gfx : : Font const > {
2022-03-26 23:55:11 +01:00
float font_size_in_pt = font_size_in_px * 0.75f ;
2023-02-04 23:00:34 +03:00
font_selector = { family , font_size_in_pt , weight , width , slope } ;
2021-09-23 13:13:51 +02:00
2022-03-29 02:14:20 +02:00
if ( auto it = m_loaded_fonts . find ( family ) ; it ! = m_loaded_fonts . end ( ) ) {
auto & loader = * it - > value ;
if ( auto found_font = loader . font_with_point_size ( font_size_in_pt ) )
return found_font ;
}
2021-09-23 13:13:51 +02:00
if ( auto found_font = FontCache : : the ( ) . get ( font_selector ) )
return found_font ;
2023-02-17 14:06:55 +00:00
if ( auto found_font = Gfx : : FontDatabase : : the ( ) . get ( family . to_deprecated_string ( ) , font_size_in_pt , weight , width , slope , Gfx : : Font : : AllowInexactSizeMatch : : Yes ) )
2021-09-23 13:13:51 +02:00
return found_font ;
return { } ;
} ;
2023-02-20 18:56:08 +01:00
auto find_generic_font = [ & ] ( ValueID font_id ) - > RefPtr < Gfx : : Font const > {
2022-09-08 11:55:12 +02:00
Platform : : GenericFont generic_font { } ;
2021-09-23 13:13:51 +02:00
switch ( font_id ) {
case ValueID : : Monospace :
case ValueID : : UiMonospace :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : Monospace ;
2021-09-23 13:13:51 +02:00
monospace = true ;
2022-09-08 11:55:12 +02:00
break ;
2021-09-23 13:13:51 +02:00
case ValueID : : Serif :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : Serif ;
break ;
2022-03-05 17:58:07 +01:00
case ValueID : : Fantasy :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : Fantasy ;
break ;
2021-09-23 13:13:51 +02:00
case ValueID : : SansSerif :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : SansSerif ;
break ;
2021-09-23 13:13:51 +02:00
case ValueID : : Cursive :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : Cursive ;
break ;
2021-09-23 13:13:51 +02:00
case ValueID : : UiSerif :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : UiSerif ;
break ;
2021-09-23 13:13:51 +02:00
case ValueID : : UiSansSerif :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : UiSansSerif ;
break ;
2021-09-23 13:13:51 +02:00
case ValueID : : UiRounded :
2022-09-08 11:55:12 +02:00
generic_font = Platform : : GenericFont : : UiRounded ;
break ;
2021-09-23 13:13:51 +02:00
default :
return { } ;
}
2023-02-17 14:06:55 +00:00
return find_font ( String : : from_utf8 ( Platform : : FontPlugin : : the ( ) . generic_font_name ( generic_font ) ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2021-09-23 13:13:51 +02:00
} ;
2023-02-20 18:56:08 +01:00
RefPtr < Gfx : : Font const > found_font ;
2021-09-23 13:13:51 +02:00
2022-04-14 11:52:35 +01:00
auto family_value = style . property ( PropertyID : : FontFamily ) ;
2021-09-23 13:13:51 +02:00
if ( family_value - > is_value_list ( ) ) {
2022-02-09 20:11:16 +01:00
auto const & family_list = static_cast < StyleValueList const & > ( * family_value ) . values ( ) ;
for ( auto const & family : family_list ) {
2023-03-06 14:33:11 +01:00
if ( family - > is_identifier ( ) ) {
found_font = find_generic_font ( family - > to_identifier ( ) ) ;
} else if ( family - > is_string ( ) ) {
found_font = find_font ( family - > to_string ( ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2021-09-23 13:13:51 +02:00
}
if ( found_font )
break ;
}
} else if ( family_value - > is_identifier ( ) ) {
found_font = find_generic_font ( family_value - > to_identifier ( ) ) ;
} else if ( family_value - > is_string ( ) ) {
2023-02-17 14:06:55 +00:00
found_font = find_font ( family_value - > to_string ( ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2021-09-23 13:13:51 +02:00
}
if ( ! found_font ) {
found_font = StyleProperties : : font_fallback ( monospace , bold ) ;
}
FontCache : : the ( ) . set ( font_selector , * found_font ) ;
2022-03-23 14:10:35 +01:00
style . set_property ( CSS : : PropertyID : : FontSize , LengthStyleValue : : create ( CSS : : Length : : make_px ( font_size_in_px ) ) ) ;
2022-02-21 16:24:12 +01:00
style . set_property ( CSS : : PropertyID : : FontWeight , NumericStyleValue : : create_integer ( weight ) ) ;
2021-09-23 13:13:51 +02:00
style . set_computed_font ( found_font . release_nonnull ( ) ) ;
}
2022-03-11 12:53:32 +00:00
Gfx : : Font const & StyleComputer : : initial_font ( ) const
{
// FIXME: This is not correct.
return StyleProperties : : font_fallback ( false , false ) ;
}
2022-02-24 15:54:12 +00:00
void StyleComputer : : absolutize_values ( StyleProperties & style , DOM : : Element const * , Optional < CSS : : Selector : : PseudoElement > ) const
2021-09-23 13:13:51 +02:00
{
2022-03-28 12:03:44 +02:00
auto font_metrics = style . computed_font ( ) . pixel_metrics ( ) ;
2022-11-08 17:29:52 +00:00
auto root_font_size = root_element_font_size ( ) ;
auto font_size = style . property ( CSS : : PropertyID : : FontSize ) - > to_length ( ) . to_px ( viewport_rect ( ) , font_metrics , root_font_size , root_font_size ) ;
2021-09-23 13:13:51 +02:00
2022-02-26 01:35:25 +01:00
for ( size_t i = 0 ; i < style . m_property_values . size ( ) ; + + i ) {
auto & value_slot = style . m_property_values [ i ] ;
2022-02-18 20:21:49 +01:00
if ( ! value_slot )
continue ;
2022-11-08 17:29:52 +00:00
value_slot = value_slot - > absolutized ( viewport_rect ( ) , font_metrics , font_size . value ( ) , root_font_size . value ( ) ) ;
2019-09-30 20:25:33 +02:00
}
2021-09-21 11:38:18 +02:00
}
2019-09-30 20:25:33 +02:00
2022-02-28 11:27:57 +01:00
enum class BoxTypeTransformation {
None ,
Blockify ,
Inlinify ,
} ;
2022-02-28 11:34:15 +01:00
static BoxTypeTransformation required_box_type_transformation ( StyleProperties const & style , DOM : : Element const & element , Optional < CSS : : Selector : : PseudoElement > const & )
2022-02-28 11:27:57 +01:00
{
// Absolute positioning or floating an element blockifies the box’ s display type. [CSS2]
if ( style . position ( ) = = CSS : : Position : : Absolute | | style . position ( ) = = CSS : : Position : : Fixed | | style . float_ ( ) ! = CSS : : Float : : None )
return BoxTypeTransformation : : Blockify ;
// FIXME: Containment in a ruby container inlinifies the box’ s display type, as described in [CSS-RUBY-1].
2022-02-28 11:34:15 +01:00
// A parent with a grid or flex display value blockifies the box’ s display type. [CSS-GRID-1] [CSS-FLEXBOX-1]
2022-03-15 19:41:35 +01:00
if ( element . parent_element ( ) & & element . parent_element ( ) - > computed_css_values ( ) ) {
auto const & parent_display = element . parent_element ( ) - > computed_css_values ( ) - > display ( ) ;
2022-02-28 11:34:15 +01:00
if ( parent_display . is_grid_inside ( ) | | parent_display . is_flex_inside ( ) )
return BoxTypeTransformation : : Blockify ;
}
2022-02-28 11:27:57 +01:00
return BoxTypeTransformation : : None ;
}
2022-01-24 14:41:48 +01:00
// https://drafts.csswg.org/css-display/#transformations
2022-02-28 11:27:57 +01:00
void StyleComputer : : transform_box_type_if_needed ( StyleProperties & style , DOM : : Element const & element , Optional < CSS : : Selector : : PseudoElement > pseudo_element ) const
2022-01-24 14:41:48 +01:00
{
// 2.7. Automatic Box Type Transformations
// Some layout effects require blockification or inlinification of the box type,
// which sets the box’ s computed outer display type to block or inline (respectively).
// (This has no effect on display types that generate no box at all, such as none or contents.)
// FIXME: If a block box (block flow) is inlinified, its inner display type is set to flow-root so that it remains a block container.
//
// FIXME: If an inline box (inline flow) is inlinified, it recursively inlinifies all of its in-flow children,
// so that no block-level descendants break up the inline formatting context in which it participates.
//
// FIXME: For legacy reasons, if an inline block box (inline flow-root) is blockified, it becomes a block box (losing its flow-root nature).
// For consistency, a run-in flow-root box also blockifies to a block box.
//
// FIXME: If a layout-internal box is blockified, its inner display type converts to flow so that it becomes a block container.
// Inlinification has no effect on layout-internal boxes. (However, placement in such an inline context will typically cause them
// to be wrapped in an appropriately-typed anonymous inline-level box.)
auto display = style . display ( ) ;
2022-02-28 11:27:57 +01:00
if ( display . is_none ( ) | | display . is_contents ( ) )
return ;
2022-01-24 14:41:48 +01:00
2022-02-28 11:27:57 +01:00
switch ( required_box_type_transformation ( style , element , pseudo_element ) ) {
case BoxTypeTransformation : : None :
break ;
case BoxTypeTransformation : : Blockify :
2022-07-19 15:31:01 +02:00
if ( ! display . is_block_outside ( ) ) {
// FIXME: We only want to change the outer display type here, but we don't have a nice API
// to do that specifically. For now, we simply check for "inline-flex" and convert
// that to "flex".
if ( display . is_flex_inside ( ) )
style . set_property ( CSS : : PropertyID : : Display , IdentifierStyleValue : : create ( CSS : : ValueID : : Flex ) ) ;
else
style . set_property ( CSS : : PropertyID : : Display , IdentifierStyleValue : : create ( CSS : : ValueID : : Block ) ) ;
}
2022-02-28 11:27:57 +01:00
break ;
case BoxTypeTransformation : : Inlinify :
2022-02-28 11:35:37 +01:00
if ( ! display . is_inline_outside ( ) )
style . set_property ( CSS : : PropertyID : : Display , IdentifierStyleValue : : create ( CSS : : ValueID : : Inline ) ) ;
2022-02-28 11:27:57 +01:00
break ;
}
2022-01-24 14:41:48 +01:00
}
2021-09-24 13:49:57 +02:00
NonnullRefPtr < StyleProperties > StyleComputer : : create_document_style ( ) const
2021-09-23 19:48:41 +02:00
{
auto style = StyleProperties : : create ( ) ;
2022-02-24 15:54:12 +00:00
compute_font ( style , nullptr , { } ) ;
compute_defaulted_values ( style , nullptr , { } ) ;
absolutize_values ( style , nullptr , { } ) ;
2022-03-19 18:08:52 +01:00
style - > set_property ( CSS : : PropertyID : : Width , CSS : : LengthStyleValue : : create ( CSS : : Length : : make_px ( viewport_rect ( ) . width ( ) ) ) ) ;
style - > set_property ( CSS : : PropertyID : : Height , CSS : : LengthStyleValue : : create ( CSS : : Length : : make_px ( viewport_rect ( ) . height ( ) ) ) ) ;
2022-10-06 12:19:33 +02:00
style - > set_property ( CSS : : PropertyID : : Display , CSS : : IdentifierStyleValue : : create ( CSS : : ValueID : : Block ) ) ;
2021-09-23 19:48:41 +02:00
return style ;
}
2022-12-06 03:08:20 -03:00
ErrorOr < NonnullRefPtr < StyleProperties > > StyleComputer : : compute_style ( DOM : : Element & element , Optional < CSS : : Selector : : PseudoElement > pseudo_element ) const
2021-09-21 11:38:18 +02:00
{
2022-02-10 17:49:50 +01:00
build_rule_cache_if_needed ( ) ;
2021-09-21 11:38:18 +02:00
auto style = StyleProperties : : create ( ) ;
2021-09-23 13:13:51 +02:00
// 1. Perform the cascade. This produces the "specified style"
2022-12-06 03:08:20 -03:00
TRY ( compute_cascaded_values ( style , element , pseudo_element ) ) ;
2021-09-23 13:13:51 +02:00
// 2. Compute the font, since that may be needed for font-relative CSS units
2022-02-24 15:54:12 +00:00
compute_font ( style , & element , pseudo_element ) ;
2021-09-23 13:13:51 +02:00
// 3. Absolutize values, turning font/viewport relative lengths into absolute lengths
2022-02-24 15:54:12 +00:00
absolutize_values ( style , & element , pseudo_element ) ;
2021-09-23 13:13:51 +02:00
// 4. Default the values, applying inheritance and 'initial' as needed
2022-02-24 15:54:12 +00:00
compute_defaulted_values ( style , & element , pseudo_element ) ;
2021-09-23 13:13:51 +02:00
2022-01-24 14:41:48 +01:00
// 5. Run automatic box type transformations
2022-02-24 15:54:12 +00:00
transform_box_type_if_needed ( style , element , pseudo_element ) ;
2022-01-24 14:41:48 +01:00
2019-10-07 09:23:53 +02:00
return style ;
2019-06-27 17:47:59 +02:00
}
2021-12-03 20:00:31 +00:00
2023-02-17 14:19:16 +00:00
PropertyDependencyNode : : PropertyDependencyNode ( String name )
2021-12-03 20:00:31 +00:00
: m_name ( move ( name ) )
{
}
void PropertyDependencyNode : : add_child ( NonnullRefPtr < PropertyDependencyNode > new_child )
{
for ( auto const & child : m_children ) {
2023-03-06 14:17:01 +01:00
if ( child - > m_name = = new_child - > m_name )
2021-12-03 20:00:31 +00:00
return ;
}
// We detect self-reference already.
VERIFY ( new_child - > m_name ! = m_name ) ;
m_children . append ( move ( new_child ) ) ;
}
bool PropertyDependencyNode : : has_cycles ( )
{
if ( m_marked )
return true ;
TemporaryChange change { m_marked , true } ;
for ( auto & child : m_children ) {
2023-03-06 14:17:01 +01:00
if ( child - > has_cycles ( ) )
2021-12-03 20:00:31 +00:00
return true ;
}
return false ;
}
2022-02-10 17:49:50 +01:00
void StyleComputer : : build_rule_cache_if_needed ( ) const
{
2023-03-07 20:13:13 +01:00
if ( m_author_rule_cache & & m_user_agent_rule_cache )
2022-02-10 17:49:50 +01:00
return ;
const_cast < StyleComputer & > ( * this ) . build_rule_cache ( ) ;
}
2023-03-07 20:13:13 +01:00
NonnullOwnPtr < StyleComputer : : RuleCache > StyleComputer : : make_rule_cache_for_cascade_origin ( CascadeOrigin cascade_origin )
2022-02-10 17:49:50 +01:00
{
2023-03-07 20:13:13 +01:00
auto rule_cache = make < RuleCache > ( ) ;
2022-02-10 17:49:50 +01:00
size_t num_class_rules = 0 ;
2022-02-10 18:50:58 +01:00
size_t num_id_rules = 0 ;
2022-02-10 18:59:19 +01:00
size_t num_tag_name_rules = 0 ;
2022-02-25 17:50:06 +00:00
size_t num_pseudo_element_rules = 0 ;
2022-02-10 17:49:50 +01:00
Vector < MatchingRule > matching_rules ;
size_t style_sheet_index = 0 ;
2023-03-07 20:13:13 +01:00
for_each_stylesheet ( cascade_origin , [ & ] ( auto & sheet ) {
2022-02-10 17:49:50 +01:00
size_t rule_index = 0 ;
2022-08-07 13:14:54 +02:00
sheet . for_each_effective_style_rule ( [ & ] ( auto const & rule ) {
2022-02-10 17:49:50 +01:00
size_t selector_index = 0 ;
for ( CSS : : Selector const & selector : rule . selectors ( ) ) {
2023-03-09 19:27:23 +01:00
MatchingRule matching_rule {
& rule ,
style_sheet_index ,
rule_index ,
selector_index ,
selector . specificity ( ) ,
} ;
2022-02-10 17:49:50 +01:00
for ( auto const & simple_selector : selector . compound_selectors ( ) . last ( ) . simple_selectors ) {
2022-02-25 17:50:06 +00:00
if ( simple_selector . type = = CSS : : Selector : : SimpleSelector : : Type : : PseudoElement ) {
2023-03-09 19:27:23 +01:00
matching_rule . contains_pseudo_element = true ;
2022-02-25 17:50:06 +00:00
+ + num_pseudo_element_rules ;
2022-02-10 18:50:58 +01:00
break ;
}
2022-02-25 17:50:06 +00:00
}
2023-03-09 19:27:23 +01:00
bool added_to_bucket = false ;
for ( auto const & simple_selector : selector . compound_selectors ( ) . last ( ) . simple_selectors ) {
if ( simple_selector . type = = CSS : : Selector : : SimpleSelector : : Type : : Id ) {
rule_cache - > rules_by_id . ensure ( simple_selector . name ( ) ) . append ( move ( matching_rule ) ) ;
+ + num_id_rules ;
added_to_bucket = true ;
break ;
}
if ( simple_selector . type = = CSS : : Selector : : SimpleSelector : : Type : : Class ) {
rule_cache - > rules_by_class . ensure ( simple_selector . name ( ) ) . append ( move ( matching_rule ) ) ;
+ + num_class_rules ;
added_to_bucket = true ;
break ;
}
if ( simple_selector . type = = CSS : : Selector : : SimpleSelector : : Type : : TagName ) {
rule_cache - > rules_by_tag_name . ensure ( simple_selector . name ( ) ) . append ( move ( matching_rule ) ) ;
+ + num_tag_name_rules ;
added_to_bucket = true ;
break ;
2022-02-10 18:59:19 +01:00
}
2022-02-10 17:49:50 +01:00
}
if ( ! added_to_bucket )
2023-03-07 20:13:13 +01:00
rule_cache - > other_rules . append ( move ( matching_rule ) ) ;
2022-02-10 17:49:50 +01:00
+ + selector_index ;
}
+ + rule_index ;
} ) ;
+ + style_sheet_index ;
} ) ;
if constexpr ( LIBWEB_CSS_DEBUG ) {
dbgln ( " Built rule cache! " ) ;
2022-02-25 17:50:06 +00:00
dbgln ( " ID: {} " , num_id_rules ) ;
dbgln ( " Class: {} " , num_class_rules ) ;
dbgln ( " TagName: {} " , num_tag_name_rules ) ;
dbgln ( " PseudoElement: {} " , num_pseudo_element_rules ) ;
2023-03-07 20:13:13 +01:00
dbgln ( " Other: {} " , rule_cache - > other_rules . size ( ) ) ;
dbgln ( " Total: {} " , num_class_rules + num_id_rules + num_tag_name_rules + rule_cache - > other_rules . size ( ) ) ;
2022-02-10 17:49:50 +01:00
}
2023-03-07 20:13:13 +01:00
return rule_cache ;
}
void StyleComputer : : build_rule_cache ( )
{
m_author_rule_cache = make_rule_cache_for_cascade_origin ( CascadeOrigin : : Author ) ;
m_user_agent_rule_cache = make_rule_cache_for_cascade_origin ( CascadeOrigin : : UserAgent ) ;
2022-02-10 17:49:50 +01:00
}
void StyleComputer : : invalidate_rule_cache ( )
{
2023-03-07 20:13:13 +01:00
m_author_rule_cache = nullptr ;
// NOTE: It might not be necessary to throw away the UA rule cache.
// If we are sure that it's safe, we could keep it as an optimization.
m_user_agent_rule_cache = nullptr ;
2022-02-10 17:49:50 +01:00
}
2022-11-09 12:32:20 +00:00
CSSPixelRect StyleComputer : : viewport_rect ( ) const
2022-03-19 18:08:52 +01:00
{
if ( auto const * browsing_context = document ( ) . browsing_context ( ) )
2022-11-09 12:32:20 +00:00
return browsing_context - > viewport_rect ( ) ;
2022-03-19 18:08:52 +01:00
return { } ;
}
2023-02-17 14:06:55 +00:00
void StyleComputer : : did_load_font ( [[maybe_unused]] FlyString const & family_name )
2022-03-29 02:14:20 +02:00
{
2022-09-14 21:24:31 +02:00
document ( ) . invalidate_layout ( ) ;
2022-03-29 02:14:20 +02:00
}
2022-04-08 21:27:35 +02:00
void StyleComputer : : load_fonts_from_sheet ( CSSStyleSheet const & sheet )
2022-03-29 02:14:20 +02:00
{
2022-04-08 21:27:35 +02:00
for ( auto const & rule : static_cast < CSSStyleSheet const & > ( sheet ) . rules ( ) ) {
if ( ! is < CSSFontFaceRule > ( rule ) )
continue ;
auto const & font_face = static_cast < CSSFontFaceRule const & > ( rule ) . font_face ( ) ;
if ( font_face . sources ( ) . is_empty ( ) )
continue ;
2023-02-17 14:06:55 +00:00
if ( m_loaded_fonts . contains ( font_face . font_family ( ) ) )
2022-04-08 21:27:35 +02:00
continue ;
2022-03-29 02:14:20 +02:00
2022-09-14 21:46:34 +02:00
// NOTE: This is rather ad-hoc, we just look for the first valid
2022-12-19 12:26:27 +01:00
// source URL that's either a WOFF or OpenType file and try loading that.
2022-09-14 21:46:34 +02:00
// FIXME: Find out exactly which resources we need to load and how.
Optional < AK : : URL > candidate_url ;
for ( auto & source : font_face . sources ( ) ) {
if ( ! source . url . is_valid ( ) )
continue ;
2022-09-29 01:30:58 +02:00
if ( source . url . scheme ( ) ! = " data " ) {
2022-09-15 12:43:27 +02:00
auto path = source . url . path ( ) ;
if ( ! path . ends_with ( " .woff " sv , AK : : CaseSensitivity : : CaseInsensitive )
& & ! path . ends_with ( " .ttf " sv , AK : : CaseSensitivity : : CaseInsensitive ) ) {
continue ;
}
2022-09-14 21:46:34 +02:00
}
candidate_url = source . url ;
break ;
}
if ( ! candidate_url . has_value ( ) )
continue ;
2022-04-08 21:27:35 +02:00
LoadRequest request ;
2022-12-06 01:12:49 +00:00
auto url = m_document . parse_url ( candidate_url . value ( ) . to_deprecated_string ( ) ) ;
2023-02-17 14:06:55 +00:00
auto loader = make < FontLoader > ( const_cast < StyleComputer & > ( * this ) , font_face . font_family ( ) , move ( url ) ) ;
const_cast < StyleComputer & > ( * this ) . m_loaded_fonts . set ( font_face . font_family ( ) . to_string ( ) , move ( loader ) ) ;
2022-04-08 21:27:35 +02:00
}
2022-03-29 02:14:20 +02:00
}
2022-04-08 21:27:35 +02:00
2020-03-07 10:27:02 +01:00
}