2022-10-08 16:58:55 -06:00
/*
* Copyright ( c ) 2022 , Andrew Kaster < akaster @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-12-16 17:49:34 +03:30
# include <AK/ByteString.h>
2022-10-08 16:58:55 -06:00
# include <AK/LexicalPath.h>
# include <AK/SourceGenerator.h>
# include <AK/StringBuilder.h>
# include <LibCore/ArgsParser.h>
2023-02-09 03:02:46 +01:00
# include <LibCore/File.h>
2022-10-08 16:58:55 -06:00
# include <LibIDL/IDLParser.h>
# include <LibIDL/Types.h>
# include <LibMain/Main.h>
2024-11-08 16:03:45 +13:00
static ErrorOr < void > add_to_interface_sets ( IDL : : Interface & , Vector < IDL : : Interface & > & intrinsics , Vector < IDL : : Interface & > & window_exposed , Vector < IDL : : Interface & > & dedicated_worker_exposed , Vector < IDL : : Interface & > & shared_worker_exposed , Vector < IDL : : Interface & > & shadow_realm_exposed ) ;
2023-12-16 17:49:34 +03:30
static ByteString s_error_string ;
2022-10-08 16:58:55 -06:00
2023-01-09 17:49:06 -05:00
struct LegacyConstructor {
2023-12-16 17:49:34 +03:30
ByteString name ;
ByteString constructor_class ;
2023-01-09 17:49:06 -05:00
} ;
static void consume_whitespace ( GenericLexer & lexer )
{
bool consumed = true ;
while ( consumed ) {
consumed = lexer . consume_while ( is_ascii_space ) . length ( ) > 0 ;
2023-10-10 12:42:20 +02:00
if ( lexer . consume_specific ( " // " sv ) ) {
2023-01-09 17:49:06 -05:00
lexer . consume_until ( ' \n ' ) ;
lexer . ignore ( ) ;
consumed = true ;
}
}
}
static Optional < LegacyConstructor > const & lookup_legacy_constructor ( IDL : : Interface & interface )
{
static HashMap < StringView , Optional < LegacyConstructor > > s_legacy_constructors ;
if ( auto cache = s_legacy_constructors . get ( interface . name ) ; cache . has_value ( ) )
return cache . value ( ) ;
auto attribute = interface . extended_attributes . get ( " LegacyFactoryFunction " sv ) ;
if ( ! attribute . has_value ( ) ) {
s_legacy_constructors . set ( interface . name , { } ) ;
return s_legacy_constructors . get ( interface . name ) . value ( ) ;
}
GenericLexer function_lexer ( attribute . value ( ) ) ;
consume_whitespace ( function_lexer ) ;
auto name = function_lexer . consume_until ( [ ] ( auto ch ) { return is_ascii_space ( ch ) | | ch = = ' ( ' ; } ) ;
2023-12-16 17:49:34 +03:30
auto constructor_class = ByteString : : formatted ( " {}Constructor " , name ) ;
2023-01-09 17:49:06 -05:00
s_legacy_constructors . set ( interface . name , LegacyConstructor { name , move ( constructor_class ) } ) ;
return s_legacy_constructors . get ( interface . name ) . value ( ) ;
}
static ErrorOr < void > generate_intrinsic_definitions ( StringView output_path , Vector < IDL : : Interface & > & exposed_interfaces )
{
StringBuilder builder ;
SourceGenerator generator ( builder ) ;
generator . append ( R " ~~~(
2024-11-15 04:01:23 +13:00
# include <LibGC/DeferGC.h>
2023-01-09 17:49:06 -05:00
# include <LibJS/Runtime/Object.h>
# include <LibWeb/Bindings/Intrinsics.h> )~~~");
for ( auto & interface : exposed_interfaces ) {
2023-08-21 16:42:48 +02:00
auto gen = generator . fork ( ) ;
2023-03-15 10:02:04 -04:00
gen . set ( " namespace_class " , interface . namespace_class ) ;
2023-01-09 17:49:06 -05:00
gen . set ( " prototype_class " , interface . prototype_class ) ;
gen . set ( " constructor_class " , interface . constructor_class ) ;
2023-03-15 10:02:04 -04:00
if ( interface . is_namespace ) {
gen . append ( R " ~~~(
# include <LibWeb/Bindings/@namespace_class@.h> )~~~");
} else {
gen . append ( R " ~~~(
2023-01-09 17:49:06 -05:00
# include <LibWeb/Bindings/@constructor_class@.h>
# include <LibWeb/Bindings/@prototype_class@.h> )~~~");
2023-03-15 10:02:04 -04:00
if ( auto const & legacy_constructor = lookup_legacy_constructor ( interface ) ; legacy_constructor . has_value ( ) ) {
gen . set ( " legacy_constructor_class " , legacy_constructor - > constructor_class ) ;
gen . append ( R " ~~~(
2023-01-09 17:49:06 -05:00
# include <LibWeb/Bindings/@legacy_constructor_class@.h> )~~~");
2023-03-15 10:02:04 -04:00
}
2023-01-09 17:49:06 -05:00
}
}
generator . append ( R " ~~~(
namespace Web : : Bindings {
) ~ ~ ~ " );
2023-03-15 10:02:04 -04:00
auto add_namespace = [ & ] ( SourceGenerator & gen , StringView name , StringView namespace_class ) {
gen . set ( " interface_name " , name ) ;
gen . set ( " namespace_class " , namespace_class ) ;
gen . append ( R " ~~~(
template < >
void Intrinsics : : create_web_namespace < @ namespace_class @ > ( JS : : Realm & realm )
{
2024-11-14 05:50:17 +13:00
auto namespace_object = realm . create < @ namespace_class @ > ( realm ) ;
2023-11-22 12:55:21 +13:00
m_namespaces . set ( " @interface_name@ " _fly_string , namespace_object ) ;
2023-03-16 12:55:59 -04:00
[[maybe_unused]] static constexpr u8 attr = JS : : Attribute : : Writable | JS : : Attribute : : Configurable ; ) ~ ~ ~ " );
for ( auto & interface : exposed_interfaces ) {
if ( interface . extended_attributes . get ( " LegacyNamespace " sv ) ! = name )
continue ;
gen . set ( " owned_interface_name " , interface . name ) ;
gen . set ( " owned_prototype_class " , interface . prototype_class ) ;
gen . append ( R " ~~~(
2023-11-22 12:55:21 +13:00
namespace_object - > define_intrinsic_accessor ( " @owned_interface_name@ " , attr , [ ] ( auto & realm ) - > JS : : Value { return & Bindings : : ensure_web_constructor < @ owned_prototype_class @ > ( realm , " @interface_name@.@owned_interface_name@ " _fly_string ) ; } ) ; ) ~ ~ ~ " );
2023-03-16 12:55:59 -04:00
}
gen . append ( R " ~~~(
2023-03-15 10:02:04 -04:00
}
) ~ ~ ~ " );
} ;
2023-09-19 13:32:13 -06:00
auto add_interface = [ ] ( SourceGenerator & gen , StringView name , StringView prototype_class , StringView constructor_class , Optional < LegacyConstructor > const & legacy_constructor , StringView named_properties_class ) {
2023-01-09 17:49:06 -05:00
gen . set ( " interface_name " , name ) ;
gen . set ( " prototype_class " , prototype_class ) ;
gen . set ( " constructor_class " , constructor_class ) ;
gen . append ( R " ~~~(
template < >
void Intrinsics : : create_web_prototype_and_constructor < @ prototype_class @ > ( JS : : Realm & realm )
{
auto & vm = realm . vm ( ) ;
2023-09-19 13:32:13 -06:00
) ~ ~ ~ " );
if ( ! named_properties_class . is_empty ( ) ) {
gen . set ( " named_properties_class " , named_properties_class ) ;
gen . append ( R " ~~~(
2024-11-14 05:50:17 +13:00
auto named_properties_object = realm . create < @ named_properties_class @ > ( realm ) ;
2023-11-22 12:55:21 +13:00
m_prototypes . set ( " @named_properties_class@ " _fly_string , named_properties_object ) ;
2023-09-19 13:32:13 -06:00
) ~ ~ ~ " );
}
gen . append ( R " ~~~(
2024-11-14 05:50:17 +13:00
auto prototype = realm . create < @ prototype_class @ > ( realm ) ;
2023-11-22 12:55:21 +13:00
m_prototypes . set ( " @interface_name@ " _fly_string , prototype ) ;
2023-01-09 17:49:06 -05:00
2024-11-14 05:50:17 +13:00
auto constructor = realm . create < @ constructor_class @ > ( realm ) ;
2023-11-22 12:55:21 +13:00
m_constructors . set ( " @interface_name@ " _fly_string , constructor ) ;
2023-01-09 17:49:06 -05:00
prototype - > define_direct_property ( vm . names . constructor , constructor . ptr ( ) , JS : : Attribute : : Writable | JS : : Attribute : : Configurable ) ;
2023-08-08 18:25:57 +02:00
constructor - > define_direct_property ( vm . names . name , JS : : PrimitiveString : : create ( vm , " @interface_name@ " _string ) , JS : : Attribute : : Configurable ) ;
2023-01-09 17:49:06 -05:00
) ~ ~ ~ " );
if ( legacy_constructor . has_value ( ) ) {
gen . set ( " legacy_interface_name " , legacy_constructor - > name ) ;
gen . set ( " legacy_constructor_class " , legacy_constructor - > constructor_class ) ;
gen . append ( R " ~~~(
2024-11-14 05:50:17 +13:00
auto legacy_constructor = realm . create < @ legacy_constructor_class @ > ( realm ) ;
2023-11-22 12:55:21 +13:00
m_constructors . set ( " @legacy_interface_name@ " _fly_string , legacy_constructor ) ;
2023-01-09 17:49:06 -05:00
2023-08-08 18:25:57 +02:00
legacy_constructor - > define_direct_property ( vm . names . name , JS : : PrimitiveString : : create ( vm , " @legacy_interface_name@ " _string ) , JS : : Attribute : : Configurable ) ; ) ~ ~ ~ " );
2023-01-09 17:49:06 -05:00
}
gen . append ( R " ~~~(
}
) ~ ~ ~ " );
} ;
for ( auto & interface : exposed_interfaces ) {
2023-08-21 16:42:48 +02:00
auto gen = generator . fork ( ) ;
2023-03-15 10:02:04 -04:00
2023-09-19 13:32:13 -06:00
String named_properties_class ;
if ( interface . extended_attributes . contains ( " Global " ) & & interface . supports_named_properties ( ) ) {
named_properties_class = MUST ( String : : formatted ( " {}Properties " , interface . name ) ) ;
}
2023-03-15 10:02:04 -04:00
if ( interface . is_namespace )
add_namespace ( gen , interface . name , interface . namespace_class ) ;
else
2023-09-19 13:32:13 -06:00
add_interface ( gen , interface . namespaced_name , interface . prototype_class , interface . constructor_class , lookup_legacy_constructor ( interface ) , named_properties_class ) ;
2023-01-09 17:49:06 -05:00
}
generator . append ( R " ~~~(
}
) ~ ~ ~ " );
auto generated_intrinsics_path = LexicalPath ( output_path ) . append ( " IntrinsicDefinitions.cpp " sv ) . string ( ) ;
2023-02-09 03:02:46 +01:00
auto generated_intrinsics_file = TRY ( Core : : File : : open ( generated_intrinsics_path , Core : : File : : OpenMode : : Write ) ) ;
2023-03-01 16:28:32 +01:00
TRY ( generated_intrinsics_file - > write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2023-01-09 17:49:06 -05:00
return { } ;
}
2022-10-08 16:58:55 -06:00
static ErrorOr < void > generate_exposed_interface_header ( StringView class_name , StringView output_path )
{
StringBuilder builder ;
SourceGenerator generator ( builder ) ;
2023-12-16 17:49:34 +03:30
generator . set ( " global_object_snake_name " , ByteString ( class_name ) . to_snakecase ( ) ) ;
2022-10-08 16:58:55 -06:00
generator . append ( R " ~~~(
# pragma once
# include <LibJS/Forward.h>
namespace Web : : Bindings {
2023-01-10 07:32:24 -05:00
void add_ @ global_object_snake_name @ _exposed_interfaces ( JS : : Object & ) ;
2022-10-08 16:58:55 -06:00
}
) ~ ~ ~ " );
2023-12-16 17:49:34 +03:30
auto generated_header_path = LexicalPath ( output_path ) . append ( ByteString : : formatted ( " {}ExposedInterfaces.h " , class_name ) ) . string ( ) ;
2023-02-09 03:02:46 +01:00
auto generated_header_file = TRY ( Core : : File : : open ( generated_header_path , Core : : File : : OpenMode : : Write ) ) ;
2023-03-01 16:28:32 +01:00
TRY ( generated_header_file - > write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-10-08 16:58:55 -06:00
return { } ;
}
static ErrorOr < void > generate_exposed_interface_implementation ( StringView class_name , StringView output_path , Vector < IDL : : Interface & > & exposed_interfaces )
{
StringBuilder builder ;
SourceGenerator generator ( builder ) ;
generator . set ( " global_object_name " , class_name ) ;
2023-12-16 17:49:34 +03:30
generator . set ( " global_object_snake_name " , ByteString ( class_name ) . to_snakecase ( ) ) ;
2022-10-08 16:58:55 -06:00
generator . append ( R " ~~~(
# include <LibJS/Runtime/Object.h>
# include <LibWeb/Bindings/Intrinsics.h>
# include <LibWeb/Bindings/@global_object_name@ExposedInterfaces.h>
) ~ ~ ~ " );
for ( auto & interface : exposed_interfaces ) {
2023-08-21 16:42:48 +02:00
auto gen = generator . fork ( ) ;
2023-03-15 10:02:04 -04:00
gen . set ( " namespace_class " , interface . namespace_class ) ;
2022-10-08 16:58:55 -06:00
gen . set ( " prototype_class " , interface . prototype_class ) ;
gen . set ( " constructor_class " , interface . constructor_class ) ;
2023-03-15 10:02:04 -04:00
if ( interface . is_namespace ) {
gen . append ( R " ~~~(#include <LibWeb/Bindings/@namespace_class@.h>
) ~ ~ ~ " );
} else {
gen . append ( R " ~~~(#include <LibWeb/Bindings/@constructor_class@.h>
2023-01-09 17:49:06 -05:00
# include <LibWeb/Bindings/@prototype_class@.h>
2022-10-08 16:58:55 -06:00
) ~ ~ ~ " );
2023-01-09 17:49:06 -05:00
2023-03-15 10:02:04 -04:00
if ( auto const & legacy_constructor = lookup_legacy_constructor ( interface ) ; legacy_constructor . has_value ( ) ) {
gen . set ( " legacy_constructor_class " , legacy_constructor - > constructor_class ) ;
gen . append ( R " ~~~(#include <LibWeb/Bindings/@legacy_constructor_class@.h>
2022-10-08 16:58:55 -06:00
) ~ ~ ~ " );
2023-03-15 10:02:04 -04:00
}
2023-01-09 17:49:06 -05:00
}
2022-10-08 16:58:55 -06:00
}
generator . append ( R " ~~~(
namespace Web : : Bindings {
2023-01-10 07:32:24 -05:00
void add_ @ global_object_snake_name @ _exposed_interfaces ( JS : : Object & global )
2022-10-08 16:58:55 -06:00
{
2023-01-10 07:32:24 -05:00
static constexpr u8 attr = JS : : Attribute : : Writable | JS : : Attribute : : Configurable ;
2022-10-08 16:58:55 -06:00
) ~ ~ ~ " );
2024-11-25 13:23:31 +01:00
auto add_interface = [ ] ( SourceGenerator & gen , StringView name , StringView prototype_class , Optional < LegacyConstructor > const & legacy_constructor , Optional < ByteString const & > legacy_alias_name ) {
2022-10-08 16:58:55 -06:00
gen . set ( " interface_name " , name ) ;
gen . set ( " prototype_class " , prototype_class ) ;
2023-01-09 17:49:06 -05:00
gen . append ( R " ~~~(
2023-11-22 12:55:21 +13:00
global . define_intrinsic_accessor ( " @interface_name@ " , attr , [ ] ( auto & realm ) - > JS : : Value { return & ensure_web_constructor < @ prototype_class @ > ( realm , " @interface_name@ " _fly_string ) ; } ) ; ) ~ ~ ~ " );
2023-01-10 12:52:01 -05:00
2024-01-05 13:29:29 +01:00
// https://webidl.spec.whatwg.org/#LegacyWindowAlias
if ( legacy_alias_name . has_value ( ) ) {
if ( legacy_alias_name - > starts_with ( ' ( ' ) ) {
auto legacy_alias_names = legacy_alias_name - > substring_view ( 1 ) . split_view ( ' , ' ) ;
for ( auto legacy_alias_name : legacy_alias_names ) {
gen . set ( " interface_alias_name " , legacy_alias_name . trim_whitespace ( ) ) ;
gen . append ( R " ~~~(
global . define_intrinsic_accessor ( " @interface_alias_name@ " , attr , [ ] ( auto & realm ) - > JS : : Value { return & ensure_web_constructor < @ prototype_class @ > ( realm , " @interface_name@ " _fly_string ) ; } ) ; ) ~ ~ ~ " );
}
} else {
gen . set ( " interface_alias_name " , * legacy_alias_name ) ;
gen . append ( R " ~~~(
global . define_intrinsic_accessor ( " @interface_alias_name@ " , attr , [ ] ( auto & realm ) - > JS : : Value { return & ensure_web_constructor < @ prototype_class @ > ( realm , " @interface_name@ " _fly_string ) ; } ) ; ) ~ ~ ~ " );
}
}
2023-01-10 12:52:01 -05:00
if ( legacy_constructor . has_value ( ) ) {
gen . set ( " legacy_interface_name " , legacy_constructor - > name ) ;
gen . append ( R " ~~~(
2023-11-22 12:55:21 +13:00
global . define_intrinsic_accessor ( " @legacy_interface_name@ " , attr , [ ] ( auto & realm ) - > JS : : Value { return & ensure_web_constructor < @ prototype_class @ > ( realm , " @legacy_interface_name@ " _fly_string ) ; } ) ; ) ~ ~ ~ " );
2023-01-10 12:52:01 -05:00
}
} ;
2022-10-08 16:58:55 -06:00
2023-03-15 10:02:04 -04:00
auto add_namespace = [ ] ( SourceGenerator & gen , StringView name , StringView namespace_class ) {
gen . set ( " interface_name " , name ) ;
gen . set ( " namespace_class " , namespace_class ) ;
gen . append ( R " ~~~(
2023-11-22 12:55:21 +13:00
global . define_intrinsic_accessor ( " @interface_name@ " , attr , [ ] ( auto & realm ) - > JS : : Value { return & ensure_web_namespace < @ namespace_class @ > ( realm , " @interface_name@ " _fly_string ) ; } ) ; ) ~ ~ ~ " );
2023-03-15 10:02:04 -04:00
} ;
2022-10-08 16:58:55 -06:00
for ( auto & interface : exposed_interfaces ) {
2023-08-21 16:42:48 +02:00
auto gen = generator . fork ( ) ;
2023-03-15 10:02:04 -04:00
2024-01-05 13:29:29 +01:00
if ( interface . is_namespace ) {
2023-03-15 10:02:04 -04:00
add_namespace ( gen , interface . name , interface . namespace_class ) ;
2024-01-05 13:29:29 +01:00
} else if ( ! interface . extended_attributes . contains ( " LegacyNamespace " sv ) ) {
if ( class_name = = " Window " ) {
add_interface ( gen , interface . namespaced_name , interface . prototype_class , lookup_legacy_constructor ( interface ) , interface . extended_attributes . get ( " LegacyWindowAlias " sv ) ) ;
} else {
add_interface ( gen , interface . namespaced_name , interface . prototype_class , lookup_legacy_constructor ( interface ) , { } ) ;
}
}
2022-10-08 16:58:55 -06:00
}
generator . append ( R " ~~~(
}
2023-01-09 17:49:06 -05:00
2022-10-08 16:58:55 -06:00
}
) ~ ~ ~ " );
2023-01-09 17:49:06 -05:00
2023-12-16 17:49:34 +03:30
auto generated_implementation_path = LexicalPath ( output_path ) . append ( ByteString : : formatted ( " {}ExposedInterfaces.cpp " , class_name ) ) . string ( ) ;
2023-02-09 03:02:46 +01:00
auto generated_implementation_file = TRY ( Core : : File : : open ( generated_implementation_path , Core : : File : : OpenMode : : Write ) ) ;
2023-03-01 16:28:32 +01:00
TRY ( generated_implementation_file - > write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-10-08 16:58:55 -06:00
return { } ;
}
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
{
Core : : ArgsParser args_parser ;
StringView output_path ;
2024-11-14 16:13:44 +00:00
Vector < ByteString > base_paths ;
2023-12-16 17:49:34 +03:30
Vector < ByteString > paths ;
2022-10-08 16:58:55 -06:00
args_parser . add_option ( output_path , " Path to output generated files into " , " output-path " , ' o ' , " output-path " ) ;
2024-11-14 16:13:44 +00:00
args_parser . add_option ( Core : : ArgsParser : : Option {
. argument_mode = Core : : ArgsParser : : OptionArgumentMode : : Required ,
. help_string = " Path to root of IDL file tree(s) " ,
. long_name = " base-path " ,
. short_name = ' b ' ,
. value_name = " base-path " ,
. accept_value = [ & ] ( StringView s ) {
base_paths . append ( s ) ;
return true ;
} ,
} ) ;
2022-10-08 16:58:55 -06:00
args_parser . add_positional_argument ( paths , " Paths of every IDL file that could be Exposed " , " paths " ) ;
args_parser . parse ( arguments ) ;
VERIFY ( ! paths . is_empty ( ) ) ;
2024-11-14 16:13:44 +00:00
VERIFY ( ! base_paths . is_empty ( ) ) ;
2022-10-08 16:58:55 -06:00
2024-11-15 23:43:04 +01:00
Vector < ByteString > lexical_bases ;
2024-11-14 16:13:44 +00:00
for ( auto const & base_path : base_paths ) {
VERIFY ( ! base_path . is_empty ( ) ) ;
2024-11-15 23:43:04 +01:00
lexical_bases . append ( base_path ) ;
2024-11-14 16:13:44 +00:00
}
2022-10-08 16:58:55 -06:00
// Read in all IDL files, we must own the storage for all of these for the lifetime of the program
2023-12-16 17:49:34 +03:30
Vector < ByteString > file_contents ;
for ( ByteString const & path : paths ) {
2023-02-09 03:02:46 +01:00
auto file_or_error = Core : : File : : open ( path , Core : : File : : OpenMode : : Read ) ;
2022-10-08 16:58:55 -06:00
if ( file_or_error . is_error ( ) ) {
2023-12-16 17:49:34 +03:30
s_error_string = ByteString : : formatted ( " Unable to open file {} " , path ) ;
2023-04-27 21:21:19 +03:30
return Error : : from_string_view ( s_error_string . view ( ) ) ;
2022-10-08 16:58:55 -06:00
}
auto file = file_or_error . release_value ( ) ;
2022-12-11 17:49:00 +01:00
auto string = MUST ( file - > read_until_eof ( ) ) ;
2023-12-16 17:49:34 +03:30
file_contents . append ( ByteString ( ReadonlyBytes ( string ) ) ) ;
2022-10-08 16:58:55 -06:00
}
VERIFY ( paths . size ( ) = = file_contents . size ( ) ) ;
Vector < IDL : : Parser > parsers ;
2023-01-09 17:49:06 -05:00
Vector < IDL : : Interface & > intrinsics ;
2022-10-08 16:58:55 -06:00
Vector < IDL : : Interface & > window_exposed ;
Vector < IDL : : Interface & > dedicated_worker_exposed ;
Vector < IDL : : Interface & > shared_worker_exposed ;
2024-11-08 16:03:45 +13:00
Vector < IDL : : Interface & > shadow_realm_exposed ;
2022-10-08 16:58:55 -06:00
// TODO: service_worker_exposed
for ( size_t i = 0 ; i < paths . size ( ) ; + + i ) {
2024-04-21 12:34:56 +12:00
auto const & path = paths [ i ] ;
2024-11-14 16:13:44 +00:00
IDL : : Parser parser ( path , file_contents [ i ] , lexical_bases ) ;
2024-04-21 12:34:56 +12:00
auto & interface = parser . parse ( ) ;
if ( interface . name . is_empty ( ) ) {
s_error_string = ByteString : : formatted ( " Interface for file {} missing " , path ) ;
return Error : : from_string_view ( s_error_string . view ( ) ) ;
}
2024-11-08 16:03:45 +13:00
TRY ( add_to_interface_sets ( interface , intrinsics , window_exposed , dedicated_worker_exposed , shared_worker_exposed , shadow_realm_exposed ) ) ;
2022-10-08 16:58:55 -06:00
parsers . append ( move ( parser ) ) ;
}
2023-01-09 17:49:06 -05:00
TRY ( generate_intrinsic_definitions ( output_path , intrinsics ) ) ;
2022-10-08 16:58:55 -06:00
TRY ( generate_exposed_interface_header ( " Window " sv , output_path ) ) ;
TRY ( generate_exposed_interface_header ( " DedicatedWorker " sv , output_path ) ) ;
TRY ( generate_exposed_interface_header ( " SharedWorker " sv , output_path ) ) ;
2024-11-08 16:03:45 +13:00
TRY ( generate_exposed_interface_header ( " ShadowRealm " sv , output_path ) ) ;
2022-10-08 16:58:55 -06:00
// TODO: ServiceWorkerExposed.h
TRY ( generate_exposed_interface_implementation ( " Window " sv , output_path , window_exposed ) ) ;
TRY ( generate_exposed_interface_implementation ( " DedicatedWorker " sv , output_path , dedicated_worker_exposed ) ) ;
TRY ( generate_exposed_interface_implementation ( " SharedWorker " sv , output_path , shared_worker_exposed ) ) ;
2024-11-08 16:03:45 +13:00
TRY ( generate_exposed_interface_implementation ( " ShadowRealm " sv , output_path , shadow_realm_exposed ) ) ;
2022-10-08 16:58:55 -06:00
// TODO: ServiceWorkerExposed.cpp
return 0 ;
}
enum ExposedTo {
Nobody = 0x0 ,
DedicatedWorker = 0x1 ,
SharedWorker = 0x2 ,
ServiceWorker = 0x4 ,
AudioWorklet = 0x8 ,
Window = 0x10 ,
2024-11-08 16:03:45 +13:00
ShadowRealm = 0x20 ,
2024-11-30 19:32:50 +13:00
Worklet = 0x40 ,
2024-11-08 16:03:45 +13:00
AllWorkers = DedicatedWorker | SharedWorker | ServiceWorker | AudioWorklet , // FIXME: Is "AudioWorklet" a Worker? We'll assume it is for now (here, and line below)
2024-11-30 19:32:50 +13:00
All = AllWorkers | Window | ShadowRealm | Worklet ,
2022-10-08 16:58:55 -06:00
} ;
AK_ENUM_BITWISE_OPERATORS ( ExposedTo ) ;
static ErrorOr < ExposedTo > parse_exposure_set ( IDL : : Interface & interface )
{
// NOTE: This roughly follows the definitions of https://webidl.spec.whatwg.org/#Exposed
// It does not remotely interpret all the abstract operations therein though.
auto maybe_exposed = interface . extended_attributes . get ( " Exposed " ) ;
if ( ! maybe_exposed . has_value ( ) ) {
2023-12-16 17:49:34 +03:30
s_error_string = ByteString : : formatted ( " Interface {} is missing extended attribute Exposed " , interface . name ) ;
2023-04-27 21:21:19 +03:30
return Error : : from_string_view ( s_error_string . view ( ) ) ;
2022-10-08 16:58:55 -06:00
}
auto exposed = maybe_exposed . value ( ) . trim_whitespace ( ) ;
if ( exposed = = " * " sv )
return ExposedTo : : All ;
2023-07-21 11:59:49 +02:00
if ( exposed = = " Nobody " sv )
return ExposedTo : : Nobody ;
2022-10-08 16:58:55 -06:00
if ( exposed = = " Window " sv )
return ExposedTo : : Window ;
if ( exposed = = " Worker " sv )
return ExposedTo : : AllWorkers ;
2024-07-09 15:54:22 -06:00
if ( exposed = = " DedicatedWorker " sv )
return ExposedTo : : DedicatedWorker ;
if ( exposed = = " SharedWorker " sv )
return ExposedTo : : SharedWorker ;
if ( exposed = = " ServiceWorker " sv )
return ExposedTo : : ServiceWorker ;
2022-10-08 16:58:55 -06:00
if ( exposed = = " AudioWorklet " sv )
return ExposedTo : : AudioWorklet ;
2024-11-30 19:32:50 +13:00
if ( exposed = = " Worklet " sv )
return ExposedTo : : Worklet ;
2024-11-08 16:03:45 +13:00
if ( exposed = = " ShadowRealm " sv )
return ExposedTo : : ShadowRealm ;
2022-10-08 16:58:55 -06:00
if ( exposed [ 0 ] = = ' ( ' ) {
ExposedTo whom = Nobody ;
for ( StringView candidate : exposed . substring_view ( 1 , exposed . length ( ) - 1 ) . split_view ( ' , ' ) ) {
candidate = candidate . trim_whitespace ( ) ;
if ( candidate = = " Window " sv ) {
whom | = ExposedTo : : Window ;
} else if ( candidate = = " Worker " sv ) {
whom | = ExposedTo : : AllWorkers ;
} else if ( candidate = = " DedicatedWorker " sv ) {
whom | = ExposedTo : : DedicatedWorker ;
} else if ( candidate = = " SharedWorker " sv ) {
whom | = ExposedTo : : SharedWorker ;
} else if ( candidate = = " ServiceWorker " sv ) {
whom | = ExposedTo : : ServiceWorker ;
} else if ( candidate = = " AudioWorklet " sv ) {
whom | = ExposedTo : : AudioWorklet ;
2024-11-30 19:32:50 +13:00
} else if ( candidate = = " Worklet " sv ) {
whom | = ExposedTo : : Worklet ;
2024-11-08 16:03:45 +13:00
} else if ( candidate = = " ShadowRealm " sv ) {
whom | = ExposedTo : : ShadowRealm ;
2022-10-08 16:58:55 -06:00
} else {
2023-12-16 17:49:34 +03:30
s_error_string = ByteString : : formatted ( " Unknown Exposed attribute candidate {} in {} in {} " , candidate , exposed , interface . name ) ;
2023-04-27 21:21:19 +03:30
return Error : : from_string_view ( s_error_string . view ( ) ) ;
2022-10-08 16:58:55 -06:00
}
}
if ( whom = = ExposedTo : : Nobody ) {
2023-12-16 17:49:34 +03:30
s_error_string = ByteString : : formatted ( " Unknown Exposed attribute {} in {} " , exposed , interface . name ) ;
2023-04-27 21:21:19 +03:30
return Error : : from_string_view ( s_error_string . view ( ) ) ;
2022-10-08 16:58:55 -06:00
}
return whom ;
}
2023-12-16 17:49:34 +03:30
s_error_string = ByteString : : formatted ( " Unknown Exposed attribute {} in {} " , exposed , interface . name ) ;
2023-04-27 21:21:19 +03:30
return Error : : from_string_view ( s_error_string . view ( ) ) ;
2022-10-08 16:58:55 -06:00
}
2024-11-08 16:03:45 +13:00
ErrorOr < void > add_to_interface_sets ( IDL : : Interface & interface , Vector < IDL : : Interface & > & intrinsics , Vector < IDL : : Interface & > & window_exposed , Vector < IDL : : Interface & > & dedicated_worker_exposed , Vector < IDL : : Interface & > & shared_worker_exposed , Vector < IDL : : Interface & > & shadow_realm_exposed )
2022-10-08 16:58:55 -06:00
{
// TODO: Add service worker exposed and audio worklet exposed
auto whom = TRY ( parse_exposure_set ( interface ) ) ;
2023-07-21 11:59:49 +02:00
intrinsics . append ( interface ) ;
2023-01-09 17:49:06 -05:00
2022-10-08 16:58:55 -06:00
if ( whom & ExposedTo : : Window )
window_exposed . append ( interface ) ;
if ( whom & ExposedTo : : DedicatedWorker )
dedicated_worker_exposed . append ( interface ) ;
if ( whom & ExposedTo : : SharedWorker )
shared_worker_exposed . append ( interface ) ;
2024-11-08 16:03:45 +13:00
if ( whom & ExposedTo : : ShadowRealm )
shadow_realm_exposed . append ( interface ) ;
2022-10-08 16:58:55 -06:00
return { } ;
}