2020-01-18 09:38:21 +01:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
2023-05-10 17:28:15 +01: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-03-08 14:36:18 +00:00
# include "GeneratorUtil.h"
2023-05-31 15:36:21 +01:00
# include <AK/CharacterTypes.h>
2023-05-10 17:28:15 +01:00
# include <AK/GenericShorthands.h>
2020-10-23 18:37:35 +02:00
# include <AK/SourceGenerator.h>
2019-11-18 11:12:58 +01:00
# include <AK/StringBuilder.h>
2022-04-01 17:41:43 +01:00
# include <LibCore/ArgsParser.h>
2022-03-10 11:55:06 +00:00
# include <LibMain/Main.h>
2021-09-29 20:29:44 +02:00
2023-08-22 09:09:07 +02:00
void replace_logical_aliases ( JsonObject & properties ) ;
2023-02-09 03:02:46 +01:00
ErrorOr < void > generate_header_file ( JsonObject & properties , Core : : File & file ) ;
ErrorOr < void > generate_implementation_file ( JsonObject & properties , Core : : File & file ) ;
2023-08-22 09:09:07 +02:00
void generate_bounds_checking_function ( JsonObject & properties , SourceGenerator & parent_generator , StringView css_type_name , StringView type_name , Optional < StringView > default_unit_name = { } , Optional < StringView > value_getter = { } ) ;
2023-10-31 19:33:58 -07:00
bool is_animatable_property ( JsonObject & properties , StringView property_name ) ;
2022-04-01 17:41:43 +01:00
2023-05-10 17:28:15 +01:00
static bool type_name_is_enum ( StringView type_name )
{
2023-07-06 16:54:15 +03:30
return ! AK : : first_is_one_of ( type_name ,
2024-05-26 00:20:52 +01:00
" angle " sv , " background-position " sv , " basic-shape " sv , " color " sv , " custom-ident " sv , " easing-function " sv , " flex " sv , " frequency " sv , " image " sv ,
2023-11-20 13:11:46 +00:00
" integer " sv , " length " sv , " number " sv , " paint " sv , " percentage " sv , " position " sv , " ratio " sv , " rect " sv ,
2023-07-06 16:54:15 +03:30
" resolution " sv , " string " sv , " time " sv , " url " sv ) ;
2023-05-10 17:28:15 +01:00
}
2022-03-10 11:55:06 +00:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2019-11-18 11:12:58 +01:00
{
2022-04-01 17:41:43 +01:00
StringView generated_header_path ;
StringView generated_implementation_path ;
StringView properties_json_path ;
Core : : ArgsParser args_parser ;
args_parser . add_option ( generated_header_path , " Path to the PropertyID header file to generate " , " generated-header-path " , ' h ' , " generated-header-path " ) ;
args_parser . add_option ( generated_implementation_path , " Path to the PropertyID implementation file to generate " , " generated-implementation-path " , ' c ' , " generated-implementation-path " ) ;
args_parser . add_option ( properties_json_path , " Path to the JSON file to read from " , " json-path " , ' j ' , " json-path " ) ;
args_parser . parse ( arguments ) ;
2019-11-18 11:12:58 +01:00
2022-04-01 17:41:43 +01:00
auto json = TRY ( read_entire_file_as_json ( properties_json_path ) ) ;
2021-11-15 01:46:51 +01:00
VERIFY ( json . is_object ( ) ) ;
2022-04-01 17:41:43 +01:00
auto properties = json . as_object ( ) ;
2023-09-08 20:20:17 +01:00
// Check we're in alphabetical order
2023-12-16 17:49:34 +03:30
ByteString most_recent_name = " " ;
2023-09-08 20:20:17 +01:00
properties . for_each_member ( [ & ] ( auto & name , auto & ) {
if ( name < most_recent_name ) {
warnln ( " `{}` is in the wrong position in `{}`. Please keep this list alphabetical! " , name , properties_json_path ) ;
VERIFY_NOT_REACHED ( ) ;
}
most_recent_name = name ;
} ) ;
2023-08-22 09:09:07 +02:00
replace_logical_aliases ( properties ) ;
2023-05-25 23:57:09 -07:00
2023-02-09 03:02:46 +01:00
auto generated_header_file = TRY ( Core : : File : : open ( generated_header_path , Core : : File : : OpenMode : : Write ) ) ;
auto generated_implementation_file = TRY ( Core : : File : : open ( generated_implementation_path , Core : : File : : OpenMode : : Write ) ) ;
2022-04-01 17:41:43 +01:00
TRY ( generate_header_file ( properties , * generated_header_file ) ) ;
TRY ( generate_implementation_file ( properties , * generated_implementation_file ) ) ;
return 0 ;
}
2023-08-22 09:09:07 +02:00
void replace_logical_aliases ( JsonObject & properties )
2023-05-25 23:57:09 -07:00
{
2023-12-16 17:49:34 +03:30
AK : : HashMap < ByteString , ByteString > logical_aliases ;
2023-05-25 23:57:09 -07:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
VERIFY ( value . is_object ( ) ) ;
2024-04-24 06:53:44 -04:00
auto const & value_as_object = value . as_object ( ) ;
auto const logical_alias_for = value_as_object . get_array ( " logical-alias-for " sv ) ;
2023-05-25 23:57:09 -07:00
if ( logical_alias_for . has_value ( ) ) {
2023-06-06 10:35:16 +02:00
auto const & aliased_properties = logical_alias_for . value ( ) ;
for ( auto const & aliased_property : aliased_properties . values ( ) ) {
2024-01-06 15:49:17 -05:00
logical_aliases . set ( name , aliased_property . as_string ( ) ) ;
2023-06-06 10:35:16 +02:00
}
2023-05-25 23:57:09 -07:00
}
} ) ;
for ( auto & [ name , alias ] : logical_aliases ) {
auto const maybe_alias_object = properties . get_object ( alias ) ;
if ( ! maybe_alias_object . has_value ( ) ) {
dbgln ( " No property '{}' found for logical alias '{}' " , alias , name ) ;
VERIFY_NOT_REACHED ( ) ;
}
JsonObject alias_object = maybe_alias_object . value ( ) ;
// Copy over anything the logical property overrides
properties . get_object ( name ) . value ( ) . for_each_member ( [ & ] ( auto & key , auto & value ) {
alias_object . set ( key , value ) ;
} ) ;
properties . set ( name , alias_object ) ;
}
}
2023-02-09 03:02:46 +01:00
ErrorOr < void > generate_header_file ( JsonObject & properties , Core : : File & file )
2022-04-01 17:41:43 +01:00
{
StringBuilder builder ;
SourceGenerator generator { builder } ;
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2022-04-01 17:41:43 +01:00
# pragma once
# include <AK/NonnullRefPtr.h>
# include <AK/StringView.h>
# include <AK/Traits.h>
2023-02-28 16:54:25 +00:00
# include <LibJS/Forward.h>
2022-04-01 17:41:43 +01:00
# include <LibWeb/Forward.h>
namespace Web : : CSS {
enum class PropertyID {
Invalid ,
Custom ,
2023-07-29 10:53:24 +02:00
All ,
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-04-01 17:41:43 +01:00
2023-12-16 17:49:34 +03:30
Vector < ByteString > shorthand_property_ids ;
Vector < ByteString > longhand_property_ids ;
2022-04-01 17:41:43 +01:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
VERIFY ( value . is_object ( ) ) ;
2022-07-11 17:32:29 +00:00
if ( value . as_object ( ) . has ( " longhands " sv ) )
2022-04-01 17:41:43 +01:00
shorthand_property_ids . append ( name ) ;
else
longhand_property_ids . append ( name ) ;
} ) ;
auto first_property_id = shorthand_property_ids . first ( ) ;
auto last_property_id = longhand_property_ids . last ( ) ;
2019-11-18 11:12:58 +01:00
2022-04-01 17:41:43 +01:00
for ( auto & name : shorthand_property_ids ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2022-04-01 17:41:43 +01:00
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2022-04-01 17:41:43 +01:00
@ name : titlecase @ ,
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-04-01 17:41:43 +01:00
}
for ( auto & name : longhand_property_ids ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2022-04-01 17:41:43 +01:00
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2022-04-01 17:41:43 +01:00
@ name : titlecase @ ,
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-04-01 17:41:43 +01:00
}
2021-09-11 17:26:38 +01:00
2023-08-21 16:59:41 +02:00
generator . set ( " first_property_id " , title_casify ( first_property_id ) ) ;
generator . set ( " last_property_id " , title_casify ( last_property_id ) ) ;
2022-04-01 17:41:43 +01:00
2023-08-21 16:59:41 +02:00
generator . set ( " first_shorthand_property_id " , title_casify ( shorthand_property_ids . first ( ) ) ) ;
generator . set ( " last_shorthand_property_id " , title_casify ( shorthand_property_ids . last ( ) ) ) ;
2022-04-01 17:41:43 +01:00
2023-08-21 16:59:41 +02:00
generator . set ( " first_longhand_property_id " , title_casify ( longhand_property_ids . first ( ) ) ) ;
generator . set ( " last_longhand_property_id " , title_casify ( longhand_property_ids . last ( ) ) ) ;
2022-04-01 17:41:43 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2022-04-01 17:41:43 +01:00
} ;
2023-10-31 19:33:58 -07:00
enum class AnimationType {
Discrete ,
ByComputedValue ,
RepeatableList ,
Custom ,
None ,
} ;
AnimationType animation_type_from_longhand_property ( PropertyID ) ;
bool is_animatable_property ( PropertyID ) ;
2023-05-10 13:01:30 +01:00
Optional < PropertyID > property_id_from_camel_case_string ( StringView ) ;
Optional < PropertyID > property_id_from_string ( StringView ) ;
2024-03-15 20:32:45 +01:00
[[nodiscard]] FlyString const & string_from_property_id ( PropertyID ) ;
2024-06-02 10:08:41 -07:00
[[nodiscard]] FlyString const & camel_case_string_from_property_id ( PropertyID ) ;
2022-04-01 17:41:43 +01:00
bool is_inherited_property ( PropertyID ) ;
2023-08-19 15:01:21 +01:00
NonnullRefPtr < StyleValue > property_initial_value ( JS : : Realm & , PropertyID ) ;
2022-04-01 17:41:43 +01:00
2023-05-10 17:28:15 +01:00
enum class ValueType {
Angle ,
2023-11-20 15:33:21 +00:00
BackgroundPosition ,
2024-05-26 00:20:52 +01:00
BasicShape ,
2023-05-10 17:28:15 +01:00
Color ,
2023-05-25 12:43:52 +01:00
CustomIdent ,
2023-07-06 16:54:15 +03:30
EasingFunction ,
2023-05-10 17:28:15 +01:00
FilterValueList ,
2023-09-28 15:18:14 +01:00
Flex ,
2023-05-10 17:28:15 +01:00
Frequency ,
Image ,
Integer ,
Length ,
Number ,
Paint ,
Percentage ,
Position ,
2023-06-06 15:42:43 +01:00
Ratio ,
2023-05-10 17:28:15 +01:00
Rect ,
Resolution ,
String ,
Time ,
Url ,
} ;
bool property_accepts_type ( PropertyID , ValueType ) ;
bool property_accepts_identifier ( PropertyID , ValueID ) ;
2023-06-22 16:22:16 +01:00
Optional < ValueType > property_resolves_percentages_relative_to ( PropertyID ) ;
2023-05-31 15:36:21 +01:00
// These perform range-checking, but are also safe to call with properties that don't accept that type. (They'll just return false.)
bool property_accepts_angle ( PropertyID , Angle const & ) ;
2023-09-28 15:18:14 +01:00
bool property_accepts_flex ( PropertyID , Flex const & ) ;
2023-05-31 15:36:21 +01:00
bool property_accepts_frequency ( PropertyID , Frequency const & ) ;
bool property_accepts_integer ( PropertyID , i64 const & ) ;
bool property_accepts_length ( PropertyID , Length const & ) ;
bool property_accepts_number ( PropertyID , double const & ) ;
bool property_accepts_percentage ( PropertyID , Percentage const & ) ;
bool property_accepts_resolution ( PropertyID , Resolution const & ) ;
bool property_accepts_time ( PropertyID , Time const & ) ;
2023-09-08 17:56:41 +01:00
bool property_is_shorthand ( PropertyID ) ;
2023-05-26 23:16:43 +03:30
Vector < PropertyID > longhands_for_shorthand ( PropertyID ) ;
2022-04-01 17:41:43 +01:00
size_t property_maximum_value_count ( PropertyID ) ;
bool property_affects_layout ( PropertyID ) ;
bool property_affects_stacking_context ( PropertyID ) ;
constexpr PropertyID first_property_id = PropertyID : : @ first_property_id @ ;
constexpr PropertyID last_property_id = PropertyID : : @ last_property_id @ ;
constexpr PropertyID first_shorthand_property_id = PropertyID : : @ first_shorthand_property_id @ ;
constexpr PropertyID last_shorthand_property_id = PropertyID : : @ last_shorthand_property_id @ ;
constexpr PropertyID first_longhand_property_id = PropertyID : : @ first_longhand_property_id @ ;
constexpr PropertyID last_longhand_property_id = PropertyID : : @ last_longhand_property_id @ ;
enum class Quirk {
// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
HashlessHexColor ,
// https://quirks.spec.whatwg.org/#the-unitless-length-quirk
UnitlessLength ,
} ;
bool property_has_quirk ( PropertyID , Quirk ) ;
} // namespace Web::CSS
namespace AK {
template < >
2023-11-08 20:29:12 +01:00
struct Traits < Web : : CSS : : PropertyID > : public DefaultTraits < Web : : CSS : : PropertyID > {
2022-04-01 17:41:43 +01:00
static unsigned hash ( Web : : CSS : : PropertyID property_id ) { return int_hash ( ( unsigned ) property_id ) ; }
} ;
} // namespace AK
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-04-01 17:41:43 +01:00
2023-03-01 16:28:32 +01:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-04-01 17:41:43 +01:00
return { } ;
}
2023-08-22 09:09:07 +02:00
void generate_bounds_checking_function ( JsonObject & properties , SourceGenerator & parent_generator , StringView css_type_name , StringView type_name , Optional < StringView > default_unit_name , Optional < StringView > value_getter )
2023-05-31 15:36:21 +01:00
{
2023-08-21 16:42:48 +02:00
auto generator = parent_generator . fork ( ) ;
2023-08-22 09:09:07 +02:00
generator . set ( " css_type_name " , css_type_name ) ;
generator . set ( " type_name " , type_name ) ;
2023-05-31 15:36:21 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-31 15:36:21 +01:00
bool property_accepts_ @ css_type_name @ ( PropertyID property_id , [[maybe_unused]] @ type_name @ const & value )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-05-31 15:36:21 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , JsonValue const & value ) - > void {
2023-05-31 15:36:21 +01:00
VERIFY ( value . is_object ( ) ) ;
if ( auto maybe_valid_types = value . as_object ( ) . get_array ( " valid-types " sv ) ; maybe_valid_types . has_value ( ) & & ! maybe_valid_types - > is_empty ( ) ) {
for ( auto valid_type : maybe_valid_types - > values ( ) ) {
auto type_and_range = valid_type . as_string ( ) . split_view ( ' ' ) ;
if ( type_and_range . first ( ) ! = css_type_name )
continue ;
2023-08-21 16:42:48 +02:00
auto property_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " property_name:titlecase " , title_casify ( name ) ) ;
2023-05-31 15:36:21 +01:00
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2023-05-31 15:36:21 +01:00
case PropertyID : : @ property_name : titlecase @ :
2023-08-21 16:39:43 +02:00
return ) ~ ~ ~ " );
2023-05-31 15:36:21 +01:00
if ( type_and_range . size ( ) > 1 ) {
auto range = type_and_range [ 1 ] ;
VERIFY ( range . starts_with ( ' [ ' ) & & range . ends_with ( ' ] ' ) & & range . contains ( ' , ' ) ) ;
auto comma_index = range . find ( ' , ' ) . value ( ) ;
StringView min_value_string = range . substring_view ( 1 , comma_index - 1 ) ;
StringView max_value_string = range . substring_view ( comma_index + 1 , range . length ( ) - comma_index - 2 ) ;
// If the min/max value is infinite, we can just skip that side of the check.
if ( min_value_string = = " -∞ " )
min_value_string = { } ;
if ( max_value_string = = " ∞ " )
max_value_string = { } ;
2023-06-02 17:14:27 +01:00
if ( min_value_string . is_empty ( ) & & max_value_string . is_empty ( ) ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " true; " ) ;
2023-06-02 17:14:27 +01:00
break ;
}
2023-08-22 09:09:07 +02:00
auto output_check = [ & ] ( auto & value_string , StringView comparator ) {
2023-05-31 15:36:21 +01:00
if ( value_getter . has_value ( ) ) {
2023-08-22 09:09:07 +02:00
property_generator . set ( " value_number " , value_string ) ;
property_generator . set ( " value_getter " , value_getter . value ( ) ) ;
property_generator . set ( " comparator " , comparator ) ;
2023-08-21 16:39:43 +02:00
property_generator . append ( " @value_getter@ @comparator@ @value_number@ " ) ;
2023-08-22 09:09:07 +02:00
return ;
2023-05-31 15:36:21 +01:00
}
GenericLexer lexer { value_string } ;
auto value_number = lexer . consume_until ( is_ascii_alpha ) ;
auto value_unit = lexer . consume_while ( is_ascii_alpha ) ;
if ( value_unit . is_empty ( ) )
value_unit = default_unit_name . value ( ) ;
VERIFY ( lexer . is_eof ( ) ) ;
2023-08-22 09:09:07 +02:00
property_generator . set ( " value_number " , value_number ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " value_unit " , title_casify ( value_unit ) ) ;
2023-08-22 09:09:07 +02:00
property_generator . set ( " comparator " , comparator ) ;
2023-08-21 16:39:43 +02:00
property_generator . append ( " value @comparator@ @type_name@(@value_number@, @type_name@::Type::@value_unit@) " ) ;
2023-05-31 15:36:21 +01:00
} ;
if ( ! min_value_string . is_empty ( ) )
2023-08-22 09:09:07 +02:00
output_check ( min_value_string , " >= " sv ) ;
2023-05-31 15:36:21 +01:00
if ( ! min_value_string . is_empty ( ) & & ! max_value_string . is_empty ( ) )
2023-08-21 16:39:43 +02:00
property_generator . append ( " && " ) ;
2023-05-31 15:36:21 +01:00
if ( ! max_value_string . is_empty ( ) )
2023-08-22 09:09:07 +02:00
output_check ( max_value_string , " <= " sv ) ;
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " ; " ) ;
2023-05-31 15:36:21 +01:00
} else {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " true; " ) ;
2023-05-31 15:36:21 +01:00
}
break ;
}
}
2023-08-22 09:09:07 +02:00
} ) ;
2023-05-31 15:36:21 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-31 15:36:21 +01:00
default :
return false ;
}
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-05-31 15:36:21 +01:00
}
2023-02-09 03:02:46 +01:00
ErrorOr < void > generate_implementation_file ( JsonObject & properties , Core : : File & file )
2022-04-01 17:41:43 +01:00
{
2020-10-23 18:37:35 +02:00
StringBuilder builder ;
SourceGenerator generator { builder } ;
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2020-10-23 18:37:35 +02:00
# include <AK/Assertions.h>
2022-04-14 11:23:23 +01:00
# include <LibWeb/CSS/Enums.h>
2021-08-16 17:42:09 +01:00
# include <LibWeb/CSS/Parser/Parser.h>
2020-10-23 18:37:35 +02:00
# include <LibWeb/CSS/PropertyID.h>
2021-08-16 17:42:09 +01:00
# include <LibWeb/CSS/StyleValue.h>
2023-03-24 17:28:43 +00:00
# include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
2023-05-26 23:16:43 +03:30
# include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
2023-02-17 16:43:28 +00:00
# include <LibWeb/Infra/Strings.h>
2020-10-23 18:37:35 +02:00
namespace Web : : CSS {
2019-11-18 11:12:58 +01:00
2023-05-10 13:01:30 +01:00
Optional < PropertyID > property_id_from_camel_case_string ( StringView string )
2021-09-29 20:29:44 +02:00
{
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-09-29 20:29:44 +02:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2021-09-29 20:29:44 +02:00
VERIFY ( value . is_object ( ) ) ;
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-22 09:09:07 +02:00
member_generator . set ( " name " , name ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . set ( " name:camelcase " , camel_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2023-03-10 08:48:54 +01:00
if ( string . equals_ignoring_ascii_case ( " @name:camelcase@ " sv ) )
2021-09-29 20:29:44 +02:00
return PropertyID : : @ name : titlecase @ ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
} ) ;
2021-09-29 20:29:44 +02:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-10 13:01:30 +01:00
return { } ;
2021-09-29 20:29:44 +02:00
}
2023-05-10 13:01:30 +01:00
Optional < PropertyID > property_id_from_string ( StringView string )
2020-10-23 18:37:35 +02:00
{
2023-07-29 10:53:24 +02:00
if ( Infra : : is_ascii_case_insensitive_match ( string , " all " sv ) )
return PropertyID : : All ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2019-11-18 11:12:58 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2021-02-23 20:42:32 +01:00
VERIFY ( value . is_object ( ) ) ;
2020-10-23 18:37:35 +02:00
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-22 09:09:07 +02:00
member_generator . set ( " name " , name ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2023-02-17 16:43:28 +00:00
if ( Infra : : is_ascii_case_insensitive_match ( string , " @name@ " sv ) )
2020-10-23 18:37:35 +02:00
return PropertyID : : @ name : titlecase @ ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
} ) ;
2019-11-18 11:12:58 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-10 13:01:30 +01:00
return { } ;
2020-10-23 18:37:35 +02:00
}
2019-11-18 11:12:58 +01:00
2024-03-15 20:32:45 +01:00
FlyString const & string_from_property_id ( PropertyID property_id ) {
2020-10-23 18:37:35 +02:00
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2019-11-18 11:12:58 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2021-02-23 20:42:32 +01:00
VERIFY ( value . is_object ( ) ) ;
2020-10-23 18:37:35 +02:00
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-22 09:09:07 +02:00
member_generator . set ( " name " , name ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2024-03-15 20:32:45 +01:00
case PropertyID : : @ name : titlecase @ : {
static FlyString name = " @name@ " _fly_string ;
return name ;
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
} ) ;
2019-11-18 11:12:58 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2024-03-15 20:32:45 +01:00
default : {
static FlyString invalid_property_id_string = " (invalid CSS::PropertyID) " _fly_string ;
return invalid_property_id_string ;
}
2020-10-23 18:37:35 +02:00
}
}
2024-06-02 10:08:41 -07:00
FlyString const & camel_case_string_from_property_id ( PropertyID property_id ) {
switch ( property_id ) {
) ~ ~ ~ " );
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
VERIFY ( value . is_object ( ) ) ;
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name " , name ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . set ( " name:camelcase " , camel_casify ( name ) ) ;
member_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ : {
static FlyString name = " @name:camelcase@ " _fly_string ;
return name ;
}
) ~ ~ ~ " );
} ) ;
generator . append ( R " ~~~(
default : {
static FlyString invalid_property_id_string = " (invalid CSS::PropertyID) " _fly_string ;
return invalid_property_id_string ;
}
}
}
2023-10-31 19:33:58 -07:00
AnimationType animation_type_from_longhand_property ( PropertyID property_id )
{
switch ( property_id ) {
) ~ ~ ~ " );
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
VERIFY ( value . is_object ( ) ) ;
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
// Shorthand properties should have already been expanded before calling into this function
if ( value . as_object ( ) . has ( " longhands " sv ) ) {
if ( value . as_object ( ) . has ( " animation-type " sv ) ) {
dbgln ( " Property '{}' with longhands cannot specify 'animation-type' " , name ) ;
VERIFY_NOT_REACHED ( ) ;
}
member_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ :
VERIFY_NOT_REACHED ( ) ;
) ~ ~ ~ " );
return ;
}
if ( ! value . as_object ( ) . has ( " animation-type " sv ) ) {
dbgln ( " No animation-type specified for property '{}' " , name ) ;
VERIFY_NOT_REACHED ( ) ;
}
auto animation_type = value . as_object ( ) . get_byte_string ( " animation-type " sv ) . value ( ) ;
member_generator . set ( " value " , title_casify ( animation_type ) ) ;
member_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ :
return AnimationType : : @ value @ ;
) ~ ~ ~ " );
} ) ;
generator . append ( R " ~~~(
default :
return AnimationType : : None ;
}
}
bool is_animatable_property ( PropertyID property_id )
{
switch ( property_id ) {
) ~ ~ ~ " );
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
VERIFY ( value . is_object ( ) ) ;
if ( is_animatable_property ( properties , name ) ) {
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ :
) ~ ~ ~ " );
}
} ) ;
generator . append ( R " ~~~(
return true ;
default :
return false ;
}
}
2021-08-16 16:07:13 +01:00
bool is_inherited_property ( PropertyID property_id )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-08-16 16:07:13 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2021-08-16 16:07:13 +01:00
VERIFY ( value . is_object ( ) ) ;
bool inherited = false ;
2022-07-11 17:32:29 +00:00
if ( value . as_object ( ) . has ( " inherited " sv ) ) {
2022-12-21 14:37:27 +00:00
auto inherited_value = value . as_object ( ) . get_bool ( " inherited " sv ) ;
VERIFY ( inherited_value . has_value ( ) ) ;
inherited = inherited_value . value ( ) ;
2021-08-16 16:07:13 +01:00
}
if ( inherited ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2021-08-16 16:07:13 +01:00
case PropertyID : : @ name : titlecase @ :
return true ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-08-16 16:07:13 +01:00
}
2023-08-22 09:09:07 +02:00
} ) ;
2021-08-16 16:07:13 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2021-08-16 16:07:13 +01:00
default :
return false ;
}
}
2022-03-16 17:46:24 +01:00
bool property_affects_layout ( PropertyID property_id )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-03-16 17:46:24 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2022-03-16 17:46:24 +01:00
VERIFY ( value . is_object ( ) ) ;
bool affects_layout = true ;
2022-07-11 17:32:29 +00:00
if ( value . as_object ( ) . has ( " affects-layout " sv ) )
2022-12-21 14:37:27 +00:00
affects_layout = value . as_object ( ) . get_bool ( " affects-layout " sv ) . value_or ( false ) ;
2022-03-16 17:46:24 +01:00
if ( affects_layout ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2022-03-16 17:46:24 +01:00
case PropertyID : : @ name : titlecase @ :
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-03-16 17:46:24 +01:00
}
2023-08-22 09:09:07 +02:00
} ) ;
2022-03-16 17:46:24 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2022-03-16 17:46:24 +01:00
return true ;
default :
return false ;
}
}
2022-03-21 10:58:51 +01:00
bool property_affects_stacking_context ( PropertyID property_id )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-03-21 10:58:51 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2022-03-21 10:58:51 +01:00
VERIFY ( value . is_object ( ) ) ;
2022-03-25 00:59:42 +01:00
bool affects_stacking_context = false ;
2022-07-11 17:32:29 +00:00
if ( value . as_object ( ) . has ( " affects-stacking-context " sv ) )
2022-12-21 14:37:27 +00:00
affects_stacking_context = value . as_object ( ) . get_bool ( " affects-stacking-context " sv ) . value_or ( false ) ;
2022-03-21 10:58:51 +01:00
2022-03-25 00:59:42 +01:00
if ( affects_stacking_context ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2022-03-21 10:58:51 +01:00
case PropertyID : : @ name : titlecase @ :
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2022-03-21 10:58:51 +01:00
}
2023-08-22 09:09:07 +02:00
} ) ;
2022-03-21 10:58:51 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2022-03-21 10:58:51 +01:00
return true ;
default :
return false ;
}
}
2023-08-19 15:01:21 +01:00
NonnullRefPtr < StyleValue > property_initial_value ( JS : : Realm & context_realm , PropertyID property_id )
2021-08-16 17:42:09 +01:00
{
2022-03-13 17:15:58 +01:00
static Array < RefPtr < StyleValue > , to_underlying ( last_property_id ) + 1 > initial_values ;
2023-04-02 22:46:22 +01:00
if ( auto initial_value = initial_values [ to_underlying ( property_id ) ] )
return initial_value . release_nonnull ( ) ;
// Lazily parse initial values as needed.
// This ensures the shorthands will always be able to get the initial values of their longhands.
// This also now allows a longhand have its own longhand (like background-position-x).
2021-08-16 17:42:09 +01:00
2023-04-02 22:46:22 +01:00
Parser : : ParsingContext parsing_context ( context_realm ) ;
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
LibWeb: Generate shorthand initial values after their longhands
When parsing shorthand values, we'd like to use
`property_initial_value()` to get their longhand property values,
instead of hard-coding them as we currently do. That involves
recursively calling that function while the `initial_values` map is
being initialized, which causes problems because the shorthands appear
alphabetically before their longhand components, so the longhands aren't
initialized yet!
The solution here is to perform 2 passes when generating the code,
outputting properties without "longhands" first, and the rest after.
This could potentially cause issues when shorthands have multiple
levels, in particular `border` -> `border-color` -> `border-left-color`.
But, we do not currently define a default value for `border`, and
`border-color` takes only a single value, so it's fine for now. :^)
2021-09-17 16:53:43 +01:00
2023-08-22 09:09:07 +02:00
auto output_initial_value_code = [ & ] ( auto & name , auto & object ) {
2022-07-11 17:32:29 +00:00
if ( ! object . has ( " initial " sv ) ) {
2021-11-10 13:48:41 +00:00
dbgln ( " No initial value specified for property '{}' " , name ) ;
VERIFY_NOT_REACHED ( ) ;
}
2023-12-16 17:49:34 +03:30
auto initial_value = object . get_byte_string ( " initial " sv ) ;
2022-12-21 14:37:27 +00:00
VERIFY ( initial_value . has_value ( ) ) ;
auto & initial_value_string = initial_value . value ( ) ;
2021-08-16 17:42:09 +01:00
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-22 09:09:07 +02:00
member_generator . set ( " initial_value_string " , initial_value_string ) ;
2023-08-21 16:39:43 +02:00
member_generator . append (
2023-04-02 22:46:22 +01:00
R " ~~~( case PropertyID::@name:titlecase@:
2021-11-10 13:48:41 +00:00
{
2023-08-19 15:01:21 +01:00
auto parsed_value = parse_css_value ( parsing_context , " @initial_value_string@ " sv , PropertyID : : @ name : titlecase @ ) ;
2021-11-10 13:48:41 +00:00
VERIFY ( ! parsed_value . is_null ( ) ) ;
2023-04-02 22:46:22 +01:00
auto initial_value = parsed_value . release_nonnull ( ) ;
initial_values [ to_underlying ( PropertyID : : @ name : titlecase @ ) ] = initial_value ;
return initial_value ;
2021-08-16 17:42:09 +01:00
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
LibWeb: Generate shorthand initial values after their longhands
When parsing shorthand values, we'd like to use
`property_initial_value()` to get their longhand property values,
instead of hard-coding them as we currently do. That involves
recursively calling that function while the `initial_values` map is
being initialized, which causes problems because the shorthands appear
alphabetically before their longhand components, so the longhands aren't
initialized yet!
The solution here is to perform 2 passes when generating the code,
outputting properties without "longhands" first, and the rest after.
This could potentially cause issues when shorthands have multiple
levels, in particular `border` -> `border-color` -> `border-left-color`.
But, we do not currently define a default value for `border`, and
`border-color` takes only a single value, so it's fine for now. :^)
2021-09-17 16:53:43 +01:00
} ;
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
LibWeb: Generate shorthand initial values after their longhands
When parsing shorthand values, we'd like to use
`property_initial_value()` to get their longhand property values,
instead of hard-coding them as we currently do. That involves
recursively calling that function while the `initial_values` map is
being initialized, which causes problems because the shorthands appear
alphabetically before their longhand components, so the longhands aren't
initialized yet!
The solution here is to perform 2 passes when generating the code,
outputting properties without "longhands" first, and the rest after.
This could potentially cause issues when shorthands have multiple
levels, in particular `border` -> `border-color` -> `border-left-color`.
But, we do not currently define a default value for `border`, and
`border-color` takes only a single value, so it's fine for now. :^)
2021-09-17 16:53:43 +01:00
VERIFY ( value . is_object ( ) ) ;
2023-08-22 09:09:07 +02:00
output_initial_value_code ( name , value . as_object ( ) ) ;
} ) ;
LibWeb: Generate shorthand initial values after their longhands
When parsing shorthand values, we'd like to use
`property_initial_value()` to get their longhand property values,
instead of hard-coding them as we currently do. That involves
recursively calling that function while the `initial_values` map is
being initialized, which causes problems because the shorthands appear
alphabetically before their longhand components, so the longhands aren't
initialized yet!
The solution here is to perform 2 passes when generating the code,
outputting properties without "longhands" first, and the rest after.
This could potentially cause issues when shorthands have multiple
levels, in particular `border` -> `border-color` -> `border-left-color`.
But, we do not currently define a default value for `border`, and
`border-color` takes only a single value, so it's fine for now. :^)
2021-09-17 16:53:43 +01:00
2023-08-21 16:39:43 +02:00
generator . append (
2023-04-02 22:46:22 +01:00
R " ~~~( default: VERIFY_NOT_REACHED();
2021-08-16 17:42:09 +01:00
}
2023-04-02 22:46:22 +01:00
VERIFY_NOT_REACHED ( ) ;
2021-08-16 17:42:09 +01:00
}
2021-09-11 17:26:38 +01:00
bool property_has_quirk ( PropertyID property_id , Quirk quirk )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-09-11 17:26:38 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2021-09-11 17:26:38 +01:00
VERIFY ( value . is_object ( ) ) ;
2022-07-11 17:32:29 +00:00
if ( value . as_object ( ) . has ( " quirks " sv ) ) {
2022-12-21 14:37:27 +00:00
auto quirks_value = value . as_object ( ) . get_array ( " quirks " sv ) ;
VERIFY ( quirks_value . has_value ( ) ) ;
auto & quirks = quirks_value . value ( ) ;
2021-09-11 17:26:38 +01:00
if ( ! quirks . is_empty ( ) ) {
2023-08-21 16:42:48 +02:00
auto property_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2021-09-11 17:26:38 +01:00
case PropertyID : : @ name : titlecase @ : {
switch ( quirk ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-09-11 17:26:38 +01:00
for ( auto & quirk : quirks . values ( ) ) {
VERIFY ( quirk . is_string ( ) ) ;
2023-08-21 16:42:48 +02:00
auto quirk_generator = property_generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
quirk_generator . set ( " quirk:titlecase " , title_casify ( quirk . as_string ( ) ) ) ;
2023-08-21 16:39:43 +02:00
quirk_generator . append ( R " ~~~(
2021-09-11 17:26:38 +01:00
case Quirk : : @ quirk : titlecase @ :
return true ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-09-11 17:26:38 +01:00
}
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2021-09-11 17:26:38 +01:00
default :
return false ;
}
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-09-11 17:26:38 +01:00
}
}
2023-08-22 09:09:07 +02:00
} ) ;
2021-09-11 17:26:38 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2021-09-11 17:26:38 +01:00
default :
return false ;
}
}
2023-05-10 17:28:15 +01:00
bool property_accepts_type ( PropertyID property_id , ValueType value_type )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2023-05-10 17:28:15 +01:00
VERIFY ( value . is_object ( ) ) ;
auto & object = value . as_object ( ) ;
if ( auto maybe_valid_types = object . get_array ( " valid-types " sv ) ; maybe_valid_types . has_value ( ) & & ! maybe_valid_types - > is_empty ( ) ) {
auto & valid_types = maybe_valid_types . value ( ) ;
2023-08-21 16:42:48 +02:00
auto property_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2023-05-10 17:28:15 +01:00
case PropertyID : : @ name : titlecase @ : {
switch ( value_type ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-05-10 17:28:15 +01:00
bool did_output_accepted_type = false ;
for ( auto & type : valid_types . values ( ) ) {
VERIFY ( type . is_string ( ) ) ;
auto type_name = type . as_string ( ) . split_view ( ' ' ) . first ( ) ;
if ( type_name_is_enum ( type_name ) )
continue ;
if ( type_name = = " angle " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Angle: " ) ;
2023-11-20 15:33:21 +00:00
} else if ( type_name = = " background-position " ) {
property_generator . appendln ( " case ValueType::BackgroundPosition: " ) ;
2024-05-26 00:20:52 +01:00
} else if ( type_name = = " basic-shape " ) {
property_generator . appendln ( " case ValueType::BasicShape: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " color " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Color: " ) ;
2023-05-25 12:43:52 +01:00
} else if ( type_name = = " custom-ident " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::CustomIdent: " ) ;
2023-07-06 16:54:15 +03:30
} else if ( type_name = = " easing-function " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::EasingFunction: " ) ;
2023-09-28 15:18:14 +01:00
} else if ( type_name = = " flex " ) {
property_generator . appendln ( " case ValueType::Flex: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " frequency " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Frequency: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " image " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Image: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " integer " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Integer: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " length " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Length: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " number " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Number: " ) ;
2023-06-15 16:37:36 +01:00
} else if ( type_name = = " paint " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Paint: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " percentage " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Percentage: " ) ;
2023-11-20 13:11:46 +00:00
} else if ( type_name = = " position " ) {
property_generator . appendln ( " case ValueType::Position: " ) ;
2023-06-06 15:42:43 +01:00
} else if ( type_name = = " ratio " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Ratio: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " rect " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Rect: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " resolution " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Resolution: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " string " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::String: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " time " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Time: " ) ;
2023-05-10 17:28:15 +01:00
} else if ( type_name = = " url " ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case ValueType::Url: " ) ;
2023-05-10 17:28:15 +01:00
} else {
VERIFY_NOT_REACHED ( ) ;
}
did_output_accepted_type = true ;
}
if ( did_output_accepted_type )
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " return true; " ) ;
2023-05-10 17:28:15 +01:00
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2023-05-10 17:28:15 +01:00
default :
return false ;
}
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-05-10 17:28:15 +01:00
}
2023-08-22 09:09:07 +02:00
} ) ;
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-10 17:28:15 +01:00
default :
return false ;
}
}
bool property_accepts_identifier ( PropertyID property_id , ValueID identifier )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2023-05-10 17:28:15 +01:00
VERIFY ( value . is_object ( ) ) ;
auto & object = value . as_object ( ) ;
2023-08-21 16:42:48 +02:00
auto property_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " case PropertyID::@name:titlecase@: { " ) ;
2023-05-10 17:28:15 +01:00
if ( auto maybe_valid_identifiers = object . get_array ( " valid-identifiers " sv ) ; maybe_valid_identifiers . has_value ( ) & & ! maybe_valid_identifiers - > is_empty ( ) ) {
2023-08-21 16:06:29 +02:00
property_generator . appendln ( " switch (identifier) { " ) ;
2023-05-10 17:28:15 +01:00
auto & valid_identifiers = maybe_valid_identifiers . value ( ) ;
for ( auto & identifier : valid_identifiers . values ( ) ) {
2023-08-21 16:42:48 +02:00
auto identifier_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
identifier_generator . set ( " identifier:titlecase " , title_casify ( identifier . as_string ( ) ) ) ;
2023-08-21 16:06:29 +02:00
identifier_generator . appendln ( " case ValueID::@identifier:titlecase@: " ) ;
2023-05-10 17:28:15 +01:00
}
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2023-05-10 17:28:15 +01:00
return true ;
default :
break ;
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-05-10 17:28:15 +01:00
}
if ( auto maybe_valid_types = object . get_array ( " valid-types " sv ) ; maybe_valid_types . has_value ( ) & & ! maybe_valid_types - > is_empty ( ) ) {
auto & valid_types = maybe_valid_types . value ( ) ;
for ( auto & valid_type : valid_types . values ( ) ) {
auto type_name = valid_type . as_string ( ) . split_view ( ' ' ) . first ( ) ;
if ( ! type_name_is_enum ( type_name ) )
continue ;
2023-08-21 16:42:48 +02:00
auto type_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
type_generator . set ( " type_name:snakecase " , snake_casify ( type_name ) ) ;
2023-08-21 16:39:43 +02:00
type_generator . append ( R " ~~~(
2023-05-10 17:28:15 +01:00
if ( value_id_to_ @ type_name : snakecase @ ( identifier ) . has_value ( ) )
return true ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-05-10 17:28:15 +01:00
}
}
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2023-05-10 17:28:15 +01:00
return false ;
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
} ) ;
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-10 17:28:15 +01:00
default :
return false ;
}
}
2023-06-22 16:22:16 +01:00
Optional < ValueType > property_resolves_percentages_relative_to ( PropertyID property_id )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-06-22 16:22:16 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2023-06-22 16:22:16 +01:00
VERIFY ( value . is_object ( ) ) ;
2023-12-16 17:49:34 +03:30
if ( auto resolved_type = value . as_object ( ) . get_byte_string ( " percentages-resolve-to " sv ) ; resolved_type . has_value ( ) ) {
2023-08-21 16:42:48 +02:00
auto property_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
property_generator . set ( " resolved_type:titlecase " , title_casify ( resolved_type . value ( ) ) ) ;
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2023-06-22 16:22:16 +01:00
case PropertyID : : @ name : titlecase @ :
return ValueType : : @ resolved_type : titlecase @ ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-06-22 16:22:16 +01:00
}
2023-08-22 09:09:07 +02:00
} ) ;
2023-06-22 16:22:16 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-06-22 16:22:16 +01:00
default :
return { } ;
}
}
2021-09-22 12:24:33 +01:00
size_t property_maximum_value_count ( PropertyID property_id )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-09-22 12:24:33 +01:00
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2021-09-22 12:24:33 +01:00
VERIFY ( value . is_object ( ) ) ;
2022-07-11 17:32:29 +00:00
if ( value . as_object ( ) . has ( " max-values " sv ) ) {
2023-11-14 01:15:54 -05:00
JsonValue max_values = value . as_object ( ) . get ( " max-values " sv ) . release_value ( ) ;
VERIFY ( max_values . is_integer < size_t > ( ) ) ;
2023-08-21 16:42:48 +02:00
auto property_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-11-14 01:15:54 -05:00
property_generator . set ( " max_values " , MUST ( String : : formatted ( " {} " , max_values . as_integer < size_t > ( ) ) ) ) ;
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2021-09-22 12:24:33 +01:00
case PropertyID : : @ name : titlecase @ :
return @ max_values @ ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-09-22 12:24:33 +01:00
}
2023-08-22 09:09:07 +02:00
} ) ;
2021-09-22 12:24:33 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2021-09-22 12:24:33 +01:00
default :
return 1 ;
}
2023-08-21 16:39:43 +02:00
} ) ~ ~ ~ " );
2021-09-22 12:24:33 +01:00
2023-08-22 09:09:07 +02:00
generate_bounds_checking_function ( properties , generator , " angle " sv , " Angle " sv , " Deg " sv ) ;
2023-09-28 15:18:14 +01:00
generate_bounds_checking_function ( properties , generator , " flex " sv , " Flex " sv , " Fr " sv ) ;
2023-08-22 09:09:07 +02:00
generate_bounds_checking_function ( properties , generator , " frequency " sv , " Frequency " sv , " Hertz " sv ) ;
generate_bounds_checking_function ( properties , generator , " integer " sv , " i64 " sv , { } , " value " sv ) ;
generate_bounds_checking_function ( properties , generator , " length " sv , " Length " sv , { } , " value.raw_value() " sv ) ;
generate_bounds_checking_function ( properties , generator , " number " sv , " double " sv , { } , " value " sv ) ;
generate_bounds_checking_function ( properties , generator , " percentage " sv , " Percentage " sv , { } , " value.value() " sv ) ;
generate_bounds_checking_function ( properties , generator , " resolution " sv , " Resolution " sv , " Dpi " sv ) ;
generate_bounds_checking_function ( properties , generator , " time " sv , " Time " sv , " S " sv ) ;
2023-05-31 15:36:21 +01:00
2023-09-08 17:56:41 +01:00
generator . append ( R " ~~~(
bool property_is_shorthand ( PropertyID property_id )
{
switch ( property_id ) {
) ~ ~ ~ " );
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
if ( value . as_object ( ) . has ( " longhands " sv ) ) {
auto property_generator = generator . fork ( ) ;
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
property_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ :
) ~ ~ ~ " );
}
} ) ;
generator . append ( R " ~~~(
return true ;
default :
return false ;
}
}
) ~ ~ ~ " );
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-26 23:16:43 +03:30
Vector < PropertyID > longhands_for_shorthand ( PropertyID property_id )
{
switch ( property_id ) {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
2023-05-26 23:16:43 +03:30
if ( value . as_object ( ) . has ( " longhands " sv ) ) {
auto longhands = value . as_object ( ) . get ( " longhands " sv ) ;
VERIFY ( longhands . has_value ( ) & & longhands - > is_array ( ) ) ;
auto longhand_values = longhands - > as_array ( ) ;
2023-08-21 16:42:48 +02:00
auto property_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-05-26 23:16:43 +03:30
StringBuilder builder ;
bool first = true ;
2023-08-22 09:09:07 +02:00
longhand_values . for_each ( [ & ] ( auto & longhand ) {
2023-05-26 23:16:43 +03:30
if ( first )
first = false ;
else
2023-08-21 16:39:43 +02:00
builder . append ( " , " sv ) ;
2024-01-06 15:49:17 -05:00
builder . appendff ( " PropertyID::{} " , title_casify ( longhand . as_string ( ) ) ) ;
2023-05-26 23:16:43 +03:30
return IterationDecision : : Continue ;
2023-08-22 09:09:07 +02:00
} ) ;
2023-12-16 17:49:34 +03:30
property_generator . set ( " longhands " , builder . to_byte_string ( ) ) ;
2023-08-21 16:39:43 +02:00
property_generator . append ( R " ~~~(
2023-05-26 23:16:43 +03:30
case PropertyID : : @ name : titlecase @ :
return { @ longhands @ } ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-05-26 23:16:43 +03:30
}
2023-08-22 09:09:07 +02:00
} ) ;
2023-05-26 23:16:43 +03:30
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-26 23:16:43 +03:30
default :
return { } ;
}
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2021-04-05 10:12:37 -04:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-05-26 23:16:43 +03:30
} // namespace Web::CSS
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2020-10-23 18:37:35 +02:00
2023-03-01 16:28:32 +01:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-04-01 17:41:43 +01:00
return { } ;
2019-11-18 11:12:58 +01:00
}
2023-10-31 19:33:58 -07:00
bool is_animatable_property ( JsonObject & properties , StringView property_name )
{
auto property = properties . get_object ( property_name ) ;
VERIFY ( property . has_value ( ) ) ;
if ( auto animation_type = property . value ( ) . get_byte_string ( " animation-type " sv ) ; animation_type . has_value ( ) ) {
return animation_type ! = " none " ;
}
if ( ! property . value ( ) . has ( " longhands " sv ) ) {
dbgln ( " Property '{}' must specify either 'animation-type' or 'longhands' " sv , property_name ) ;
VERIFY_NOT_REACHED ( ) ;
}
auto longhands = property . value ( ) . get_array ( " longhands " sv ) ;
VERIFY ( longhands . has_value ( ) ) ;
for ( auto const & subproperty_name : longhands - > values ( ) ) {
VERIFY ( subproperty_name . is_string ( ) ) ;
if ( is_animatable_property ( properties , subproperty_name . as_string ( ) ) )
return true ;
}
return false ;
}