2020-01-18 09:38:21 +01:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
2022-03-10 11:55:06 +00:00
* Copyright ( c ) 2021 - 2022 , 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"
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-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 ) ;
2022-04-01 17:41:43 +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-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-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 } ;
generator . append ( R " ~~~(
# 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 ,
) ~ ~ ~ " );
2022-12-04 18:02:33 +00:00
Vector < DeprecatedString > shorthand_property_ids ;
Vector < DeprecatedString > 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 ) {
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . append ( R " ~~~(
@ name : titlecase @ ,
) ~ ~ ~ " );
}
for ( auto & name : longhand_property_ids ) {
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . append ( R " ~~~(
@ name : titlecase @ ,
) ~ ~ ~ " );
}
2021-09-11 17:26:38 +01:00
2022-04-01 17:41:43 +01:00
generator . set ( " first_property_id " , title_casify ( first_property_id ) ) ;
generator . set ( " last_property_id " , title_casify ( last_property_id ) ) ;
generator . set ( " first_shorthand_property_id " , title_casify ( shorthand_property_ids . first ( ) ) ) ;
generator . set ( " last_shorthand_property_id " , title_casify ( shorthand_property_ids . last ( ) ) ) ;
generator . set ( " first_longhand_property_id " , title_casify ( longhand_property_ids . first ( ) ) ) ;
generator . set ( " last_longhand_property_id " , title_casify ( longhand_property_ids . last ( ) ) ) ;
generator . append ( R " ~~~(
} ;
PropertyID property_id_from_camel_case_string ( StringView ) ;
PropertyID property_id_from_string ( StringView ) ;
2022-07-11 17:32:29 +00:00
StringView string_from_property_id ( PropertyID ) ;
2022-04-01 17:41:43 +01:00
bool is_inherited_property ( PropertyID ) ;
2023-02-28 16:54:25 +00:00
NonnullRefPtr < StyleValue > property_initial_value ( JS : : Realm & , PropertyID ) ;
2022-04-01 17:41:43 +01:00
bool property_accepts_value ( PropertyID , StyleValue & ) ;
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 < >
struct Traits < Web : : CSS : : PropertyID > : public GenericTraits < Web : : CSS : : PropertyID > {
static unsigned hash ( Web : : CSS : : PropertyID property_id ) { return int_hash ( ( unsigned ) property_id ) ; }
} ;
} // namespace AK
) ~ ~ ~ " );
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-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 } ;
generator . append ( R " ~~~(
# 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-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
2021-09-29 20:29:44 +02:00
PropertyID property_id_from_camel_case_string ( StringView string )
{
) ~ ~ ~ " );
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 " ~~~(
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 @ ;
) ~ ~ ~ " );
} ) ;
generator . append ( R " ~~~(
return PropertyID : : Invalid ;
}
2021-11-11 00:55:02 +01:00
PropertyID property_id_from_string ( StringView string )
2020-10-23 18:37:35 +02:00
{
) ~ ~ ~ " );
2019-11-18 11:12:58 +01:00
2021-09-11 17:26:38 +01: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
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name " , name ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
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 @ ;
) ~ ~ ~ " );
2019-11-18 11:12:58 +01:00
} ) ;
2020-10-23 18:37:35 +02:00
generator . append ( R " ~~~(
return PropertyID : : Invalid ;
}
2019-11-18 11:12:58 +01:00
2022-07-11 17:32:29 +00:00
StringView string_from_property_id ( PropertyID property_id ) {
2020-10-23 18:37:35 +02:00
switch ( property_id ) {
) ~ ~ ~ " );
2019-11-18 11:12:58 +01:00
2021-09-11 17:26:38 +01: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
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name " , name ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ :
2022-07-11 17:32:29 +00:00
return " @name@ " sv ;
2021-08-16 16:07:13 +01:00
) ~ ~ ~ " );
2019-11-18 11:12:58 +01:00
} ) ;
2020-10-23 18:37:35 +02:00
generator . append ( R " ~~~(
default :
2022-07-11 17:32:29 +00:00
return " (invalid CSS::PropertyID) " sv ;
2020-10-23 18:37:35 +02:00
}
}
2021-08-16 16:07:13 +01:00
bool is_inherited_property ( PropertyID property_id )
{
switch ( property_id ) {
) ~ ~ ~ " );
2021-09-11 17:26:38 +01: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 ) {
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ :
return true ;
) ~ ~ ~ " );
}
} ) ;
generator . append ( R " ~~~(
default :
return false ;
}
}
2022-03-16 17:46:24 +01:00
bool property_affects_layout ( PropertyID property_id )
{
switch ( property_id ) {
) ~ ~ ~ " );
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
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 ) {
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 ;
}
}
2022-03-21 10:58:51 +01:00
bool property_affects_stacking_context ( PropertyID property_id )
{
switch ( property_id ) {
) ~ ~ ~ " );
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
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 ) {
2022-03-21 10:58:51 +01:00
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 ;
}
}
2023-02-28 16:54:25 +00: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 ;
static bool initialized = false ;
if ( ! initialized ) {
initialized = true ;
2023-02-28 16:54:25 +00:00
Parser : : ParsingContext parsing_context ( context_realm ) ;
2021-08-16 17:42:09 +01: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
// NOTE: Parsing a shorthand property requires that its longhands are already available here.
// So, we do this in two passes: First longhands, then shorthands.
// Probably we should build a dependency graph and then handle them in order, but this
// works for now! :^)
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 ( ) ;
}
2022-12-21 14:37:27 +00:00
auto initial_value = object . get_deprecated_string ( " initial " sv ) ;
VERIFY ( initial_value . has_value ( ) ) ;
auto & initial_value_string = initial_value . value ( ) ;
2021-08-16 17:42:09 +01:00
2021-11-10 13:48:41 +00:00
auto member_generator = generator . fork ( ) ;
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
member_generator . set ( " initial_value_string " , initial_value_string ) ;
member_generator . append ( R " ~~~(
{
2022-07-11 17:32:29 +00: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 ( ) ) ;
2022-03-13 17:15:58 +01:00
initial_values [ to_underlying ( PropertyID : : @ name : titlecase @ ) ] = parsed_value . release_nonnull ( ) ;
2021-08-16 17:42:09 +01:00
}
2021-11-10 13:48:41 +00: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
} ;
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 ) )
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
return ;
output_initial_value_code ( name , value . as_object ( ) ) ;
} ) ;
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 ) )
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
return ;
output_initial_value_code ( name , value . as_object ( ) ) ;
2021-08-16 17:42:09 +01:00
} ) ;
generator . append ( R " ~~~(
}
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
2022-03-13 17:15:58 +01:00
return * initial_values [ to_underlying ( property_id ) ] ;
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 ) {
) ~ ~ ~ " );
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 ( " 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 ( ) ) {
auto property_generator = generator . fork ( ) ;
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
property_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ : {
switch ( quirk ) {
) ~ ~ ~ " );
for ( auto & quirk : quirks . values ( ) ) {
VERIFY ( quirk . is_string ( ) ) ;
auto quirk_generator = property_generator . fork ( ) ;
quirk_generator . set ( " quirk:titlecase " , title_casify ( quirk . as_string ( ) ) ) ;
quirk_generator . append ( R " ~~~(
case Quirk : : @ quirk : titlecase @ :
return true ;
) ~ ~ ~ " );
}
property_generator . append ( R " ~~~(
default :
return false ;
}
}
) ~ ~ ~ " );
}
}
} ) ;
generator . append ( R " ~~~(
default :
return false ;
}
}
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
bool property_accepts_value ( PropertyID property_id , StyleValue & style_value )
{
2021-12-03 20:17:17 +00:00
if ( style_value . is_builtin ( ) )
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
return true ;
switch ( property_id ) {
) ~ ~ ~ " );
properties . for_each_member ( [ & ] ( auto & name , auto & value ) {
VERIFY ( value . is_object ( ) ) ;
auto & object = value . as_object ( ) ;
2022-07-11 17:32:29 +00:00
bool has_valid_types = object . has ( " valid-types " sv ) ;
auto has_valid_identifiers = object . has ( " valid-identifiers " sv ) ;
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
if ( has_valid_types | | has_valid_identifiers ) {
auto property_generator = generator . fork ( ) ;
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
property_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ : {
) ~ ~ ~ " );
2022-03-26 14:51:00 +00:00
auto output_numeric_value_check = [ ] ( SourceGenerator & generator , StringView type_check_function , StringView value_getter , Span < StringView > resolved_type_names , StringView min_value , StringView max_value ) {
auto test_generator = generator . fork ( ) ;
test_generator . set ( " type_check_function " , type_check_function ) ;
test_generator . set ( " value_getter " , value_getter ) ;
test_generator . append ( R " ~~~(
if ( ( style_value . @ type_check_function @ ( ) ) ~ ~ ~ " );
if ( ! min_value . is_empty ( ) & & min_value ! = " -∞ " ) {
test_generator . set ( " minvalue " , min_value ) ;
test_generator . append ( " && (style_value.@value_getter@ >= @minvalue@) " ) ;
}
if ( ! max_value . is_empty ( ) & & max_value ! = " ∞ " ) {
test_generator . set ( " maxvalue " , max_value ) ;
test_generator . append ( " && (style_value.@value_getter@ <= @maxvalue@) " ) ;
}
test_generator . append ( " ) " ) ;
if ( ! resolved_type_names . is_empty ( ) ) {
test_generator . append ( R " ~~~(
| | ( style_value . is_calculated ( ) & & ( ) ~ ~ ~ " );
bool first = true ;
for ( auto & type_name : resolved_type_names ) {
test_generator . set ( " resolved_type_name " , type_name ) ;
if ( ! first )
test_generator . append ( " || " ) ;
test_generator . append ( " style_value.as_calculated().resolved_type() == CalculatedStyleValue::ResolvedType::@resolved_type_name@ " ) ;
first = false ;
}
test_generator . append ( " )) " ) ;
}
test_generator . append ( R " ~~~() {
return true ;
}
) ~ ~ ~ " );
} ;
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
if ( has_valid_types ) {
2022-12-21 14:37:27 +00:00
auto valid_types_value = object . get_array ( " valid-types " sv ) ;
VERIFY ( valid_types_value . has_value ( ) ) ;
auto & valid_types = valid_types_value . value ( ) ;
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
if ( ! valid_types . is_empty ( ) ) {
for ( auto & type : valid_types . values ( ) ) {
VERIFY ( type . is_string ( ) ) ;
2021-09-23 12:45:08 +01:00
auto type_parts = type . as_string ( ) . split_view ( ' ' ) ;
auto type_name = type_parts . first ( ) ;
auto type_args = type_parts . size ( ) > 1 ? type_parts [ 1 ] : " " sv ;
2022-03-26 14:51:00 +00:00
StringView min_value ;
StringView max_value ;
if ( ! type_args . is_empty ( ) ) {
VERIFY ( type_args . starts_with ( ' [ ' ) & & type_args . ends_with ( ' ] ' ) ) ;
auto comma_index = type_args . find ( ' , ' ) . value ( ) ;
min_value = type_args . substring_view ( 1 , comma_index - 1 ) ;
max_value = type_args . substring_view ( comma_index + 1 , type_args . length ( ) - comma_index - 2 ) ;
}
2022-02-22 13:49:05 +00:00
if ( type_name = = " angle " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " is_angle " sv , " as_angle().angle().to_degrees() " sv , Array { " Angle " sv } , min_value , max_value ) ;
2022-02-22 13:49:05 +00:00
} else if ( type_name = = " color " ) {
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
property_generator . append ( R " ~~~(
2021-09-23 19:54:19 +01:00
if ( style_value . has_color ( ) )
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
return true ;
2022-09-15 08:31:17 +01:00
) ~ ~ ~ " );
} else if ( type_name = = " filter-value-list " ) {
property_generator . append ( R " ~~~(
if ( style_value . is_filter_value_list ( ) )
return true ;
2022-02-22 13:49:05 +00:00
) ~ ~ ~ " );
} else if ( type_name = = " frequency " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " is_frequency " sv , " as_frequency().frequency().to_hertz() " sv , Array { " Frequency " sv } , min_value , max_value ) ;
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
} else if ( type_name = = " image " ) {
property_generator . append ( R " ~~~(
2022-09-15 08:31:06 +01:00
if ( style_value . is_abstract_image ( ) )
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
return true ;
) ~ ~ ~ " );
2022-03-26 14:51:00 +00:00
} else if ( type_name = = " integer " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " has_integer " sv , " to_integer() " sv , Array { " Integer " sv } , min_value , max_value ) ;
2021-10-19 16:07:24 +01:00
} else if ( type_name = = " length " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " has_length " sv , " to_length().raw_value() " sv , Array { " Length " sv } , min_value , max_value ) ;
2022-03-26 14:51:00 +00:00
} else if ( type_name = = " number " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " has_number " sv , " to_number() " sv , Array { " Integer " sv , " Number " sv } , min_value , max_value ) ;
2022-02-22 13:49:05 +00:00
} else if ( type_name = = " percentage " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " is_percentage " sv , " as_percentage().percentage().value() " sv , Array { " Percentage " sv } , min_value , max_value ) ;
2022-07-31 18:46:35 +02:00
} else if ( type_name = = " rect " ) {
property_generator . append ( R " ~~~(
if ( style_value . has_rect ( ) )
return true ;
) ~ ~ ~ " );
2022-02-22 13:49:05 +00:00
} else if ( type_name = = " resolution " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " is_resolution " sv , " as_resolution().resolution().to_dots_per_pixel() " sv , Array < StringView , 0 > { } , min_value , max_value ) ;
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
} else if ( type_name = = " string " ) {
property_generator . append ( R " ~~~(
if ( style_value . is_string ( ) )
return true ;
2022-02-22 13:49:05 +00:00
) ~ ~ ~ " );
} else if ( type_name = = " time " ) {
2022-07-11 17:32:29 +00:00
output_numeric_value_check ( property_generator , " is_time " sv , " as_time().time().to_seconds() " sv , Array { " Time " sv } , min_value , max_value ) ;
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
} else if ( type_name = = " url " ) {
// FIXME: Handle urls!
} else {
2022-04-14 11:23:23 +01:00
// Assume that any other type names are defined in Enums.json.
// If they're not, the output won't compile, but that's fine since it's invalid.
property_generator . set ( " type_name:snakecase " , snake_casify ( type_name ) ) ;
property_generator . append ( R " ~~~(
if ( auto converted_identifier = value_id_to_ @ type_name : snakecase @ ( style_value . to_identifier ( ) ) ; converted_identifier . has_value ( ) )
return true ;
) ~ ~ ~ " );
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
}
}
}
}
if ( has_valid_identifiers ) {
2022-12-21 14:37:27 +00:00
auto valid_identifiers_value = object . get_array ( " valid-identifiers " sv ) ;
VERIFY ( valid_identifiers_value . has_value ( ) ) ;
auto & valid_identifiers = valid_identifiers_value . value ( ) ;
LibWeb: Generate property_accepts_value() function :^)
Previously, we have not been validating the values for CSS declarations
inside the Parser. This causes issues, since we should be discarding
invalid style declarations, so that previous ones are used instead. For
example, in this code:
```css
.foo {
width: 2em;
width: orange;
}
```
... the `width: orange` declaration overwrites the `width: 2em` one,
even though it is invalid. According to the spec, `width: orange` should
be rejected at parse time, and discarded, leaving `width: 2em` as the
resulting value.
Many properties (mostly shorthands) are parsed specially, and so they
are already rejected if they are invalid. But for simple properties, we
currently accept any value. With `property_accepts_value()`, we can
check if the value is valid in `parse_css_value()`, and reject it if it
is not.
2021-09-22 20:11:15 +01:00
if ( ! valid_identifiers . is_empty ( ) ) {
property_generator . append ( R " ~~~(
switch ( style_value . to_identifier ( ) ) {
) ~ ~ ~ " );
for ( auto & identifier : valid_identifiers . values ( ) ) {
VERIFY ( identifier . is_string ( ) ) ;
auto identifier_generator = generator . fork ( ) ;
identifier_generator . set ( " identifier:titlecase " , title_casify ( identifier . as_string ( ) ) ) ;
identifier_generator . append ( R " ~~~(
case ValueID : : @ identifier : titlecase @ :
) ~ ~ ~ " );
}
property_generator . append ( R " ~~~(
return true ;
default :
break ;
}
) ~ ~ ~ " );
}
}
generator . append ( R " ~~~(
return false ;
}
) ~ ~ ~ " );
}
} ) ;
generator . append ( R " ~~~(
default :
return true ;
}
}
2021-09-22 12:24:33 +01:00
size_t property_maximum_value_count ( PropertyID property_id )
{
switch ( property_id ) {
) ~ ~ ~ " );
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 ( " max-values " sv ) ) {
2022-12-21 14:37:27 +00:00
auto max_values = value . as_object ( ) . get ( " max-values " sv ) ;
VERIFY ( max_values . has_value ( ) & & max_values - > is_number ( ) & & ! max_values - > is_double ( ) ) ;
2021-09-22 12:24:33 +01:00
auto property_generator = generator . fork ( ) ;
property_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2022-12-21 14:37:27 +00:00
property_generator . set ( " max_values " , max_values - > to_deprecated_string ( ) ) ;
2021-09-22 12:24:33 +01:00
property_generator . append ( R " ~~~(
case PropertyID : : @ name : titlecase @ :
return @ max_values @ ;
) ~ ~ ~ " );
}
} ) ;
generator . append ( R " ~~~(
default :
return 1 ;
}
}
2020-10-23 18:37:35 +02:00
} // namespace Web::CSS
2021-04-05 10:12:37 -04: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
}