2020-03-14 12:41:51 +01:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < andreas @ ladybird . org >
2022-10-03 21:08:02 +02:00
* Copyright ( c ) 2022 , networkException < networkexception @ serenityos . org >
2020-03-14 12:41:51 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-03-14 12:41:51 +01:00
*/
2021-01-28 20:31:20 +00:00
# include <AK/Debug.h>
2020-03-14 12:41:51 +01:00
# include <AK/StringBuilder.h>
2021-01-28 20:31:20 +00:00
# include <LibTextCodec/Decoder.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/HTMLScriptElementPrototype.h>
2022-09-30 17:16:16 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2024-12-02 16:01:19 +00:00
# include <LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h>
2020-03-14 12:41:51 +01:00
# include <LibWeb/DOM/Document.h>
2020-10-18 13:44:20 +02:00
# include <LibWeb/DOM/Event.h>
2021-01-28 20:31:20 +00:00
# include <LibWeb/DOM/ShadowRoot.h>
2020-03-14 12:41:51 +01:00
# include <LibWeb/DOM/Text.h>
2020-11-21 19:15:57 +00:00
# include <LibWeb/HTML/EventNames.h>
2020-09-18 09:49:51 +02:00
# include <LibWeb/HTML/HTMLScriptElement.h>
2021-09-09 19:04:33 +02:00
# include <LibWeb/HTML/Scripting/ClassicScript.h>
2022-10-03 21:08:02 +02:00
# include <LibWeb/HTML/Scripting/Fetching.h>
2024-04-13 21:22:05 +01:00
# include <LibWeb/HTML/Scripting/ImportMapParseResult.h>
# include <LibWeb/HTML/Window.h>
2022-10-01 18:25:29 +01:00
# include <LibWeb/Infra/CharacterTypes.h>
2023-02-17 15:21:32 +00:00
# include <LibWeb/Infra/Strings.h>
2022-09-17 17:57:30 +02:00
# include <LibWeb/MimeSniff/MimeType.h>
2025-08-05 21:58:13 +02:00
# include <LibWeb/TrustedTypes/RequireTrustedTypesForDirective.h>
# include <LibWeb/TrustedTypes/TrustedTypePolicy.h>
2020-03-14 12:41:51 +01:00
2020-07-28 18:20:36 +02:00
namespace Web : : HTML {
2020-03-14 12:41:51 +01:00
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( HTMLScriptElement ) ;
2023-11-19 19:47:52 +01:00
2022-02-18 21:00:52 +01:00
HTMLScriptElement : : HTMLScriptElement ( DOM : : Document & document , DOM : : QualifiedName qualified_name )
2021-02-07 11:20:15 +01:00
: HTMLElement ( document , move ( qualified_name ) )
2020-03-14 12:41:51 +01:00
{
}
2022-03-14 13:21:51 -06:00
HTMLScriptElement : : ~ HTMLScriptElement ( ) = default ;
2020-03-14 12:41:51 +01:00
2023-08-07 08:41:28 +02:00
void HTMLScriptElement : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( HTMLScriptElement ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2023-01-10 06:28:20 -05:00
}
2022-08-28 13:42:07 +02:00
void HTMLScriptElement : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2024-11-15 04:01:23 +13:00
if ( auto * script = m_result . get_pointer < GC : : Ref < Script > > ( ) )
2023-11-19 16:18:00 +13:00
visitor . visit ( * script ) ;
visitor . visit ( m_parser_document ) ;
visitor . visit ( m_preparation_time_document ) ;
2022-08-28 13:42:07 +02:00
}
2024-11-14 08:14:16 -05:00
void HTMLScriptElement : : attribute_changed ( FlyString const & name , Optional < String > const & old_value , Optional < String > const & value , Optional < FlyString > const & namespace_ )
2023-05-10 17:08:07 -04:00
{
2024-11-14 08:14:16 -05:00
Base : : attribute_changed ( name , old_value , value , namespace_ ) ;
2023-05-10 17:08:07 -04:00
2023-07-03 17:31:17 +02:00
if ( name = = HTML : : AttributeNames : : crossorigin ) {
2023-11-19 18:10:36 +13:00
m_crossorigin = cors_setting_attribute_from_keyword ( value ) ;
2023-07-03 17:31:17 +02:00
} else if ( name = = HTML : : AttributeNames : : referrerpolicy ) {
2024-03-05 09:35:25 -07:00
m_referrer_policy = ReferrerPolicy : : from_string ( value . value_or ( " " _string ) ) . value_or ( ReferrerPolicy : : ReferrerPolicy : : EmptyString ) ;
2024-07-07 21:57:01 +02:00
} else if ( name = = HTML : : AttributeNames : : src ) {
2024-12-10 19:35:21 +13:00
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:html-element-post-connection-steps-6
// 1. If namespace is not null, then return.
if ( namespace_ . has_value ( ) )
return ;
2025-02-21 13:23:34 +00:00
// AD-HOC: This ensures that prepare_script() is not called when the src attribute is removed.
// See: https://github.com/whatwg/html/pull/10188/files#r1685905457 for more information.
if ( ! value . has_value ( ) )
return ;
2024-12-10 19:35:21 +13:00
// 2. If localName is src, then run the script HTML element post-connection steps, given element.
post_connection ( ) ;
2024-08-04 19:06:07 +01:00
} else if ( name = = HTML : : AttributeNames : : async ) {
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:script-force-async
// When an async attribute is added to a script element el, the user agent must set el's force async to false.
m_force_async = false ;
2023-07-03 17:31:17 +02:00
}
2023-05-10 17:08:07 -04:00
}
2022-03-19 16:11:36 +01:00
void HTMLScriptElement : : begin_delaying_document_load_event ( DOM : : Document & document )
2022-03-19 14:22:46 +01:00
{
2021-09-26 02:03:56 +02:00
// https://html.spec.whatwg.org/multipage/scripting.html#concept-script-script
// The user agent must delay the load event of the element's node document until the script is ready.
m_document_load_event_delayer . emplace ( document ) ;
2020-05-24 22:00:46 +02:00
}
2021-09-09 18:43:28 +02:00
// https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
2020-05-26 15:55:18 +02:00
void HTMLScriptElement : : execute_script ( )
{
2024-01-14 15:59:38 +13:00
// https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-html
// Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be true for document.
if ( ! m_document - > ready_to_run_scripts ( ) )
2024-11-15 04:01:23 +13:00
main_thread_event_loop ( ) . spin_until ( GC : : create_function ( heap ( ) , [ & ] { return m_document - > ready_to_run_scripts ( ) ; } ) ) ;
2024-01-14 15:59:38 +13:00
2022-11-20 17:12:17 +01:00
// 1. Let document be el's node document.
2024-11-15 04:01:23 +13:00
GC : : Ref < DOM : : Document > document = this - > document ( ) ;
2022-10-03 21:08:02 +02:00
2022-11-20 17:12:17 +01:00
// 2. If el's preparation-time document is not equal to document, then return.
if ( m_preparation_time_document . ptr ( ) ! = document . ptr ( ) ) {
2021-01-28 20:31:20 +00:00
dbgln ( " HTMLScriptElement: Refusing to run script because the preparation time document is not the same as the node document. " ) ;
return ;
}
2025-02-04 19:09:14 +01:00
// 3. Unblock rendering on el.
unblock_rendering ( ) ;
2022-11-20 17:12:17 +01:00
// 3. If el's result is null, then fire an event named error at el, and return.
if ( m_result . has < ResultState : : Null > ( ) ) {
dbgln ( " HTMLScriptElement: Refusing to run script because the element's result is null. " ) ;
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2021-01-28 20:31:20 +00:00
return ;
}
2022-11-20 17:12:17 +01:00
// 5. If el's from an external file is true, or el's type is "module", then increment document's ignore-destructive-writes counter.
2021-01-28 20:31:20 +00:00
bool incremented_destructive_writes_counter = false ;
if ( m_from_an_external_file | | m_script_type = = ScriptType : : Module ) {
2022-11-20 17:12:17 +01:00
document - > increment_ignore_destructive_writes_counter ( ) ;
2021-01-28 20:31:20 +00:00
incremented_destructive_writes_counter = true ;
}
2022-11-20 17:12:17 +01:00
// 5. Switch on el's type:
// -> "classic"
2021-01-28 20:31:20 +00:00
if ( m_script_type = = ScriptType : : Classic ) {
2021-09-09 18:43:28 +02:00
// 1. Let oldCurrentScript be the value to which document's currentScript object was most recently set.
2022-11-20 17:12:17 +01:00
auto old_current_script = document - > current_script ( ) ;
// 2. If el's root is not a shadow root, then set document's currentScript attribute to el. Otherwise, set it to null.
2021-01-28 20:31:20 +00:00
if ( ! is < DOM : : ShadowRoot > ( root ( ) ) )
2022-11-20 17:12:17 +01:00
document - > set_current_script ( { } , this ) ;
2021-01-28 20:31:20 +00:00
else
2022-11-20 17:12:17 +01:00
document - > set_current_script ( { } , nullptr ) ;
2021-01-28 20:31:20 +00:00
if ( m_from_an_external_file )
2024-01-13 20:12:18 +13:00
dbgln_if ( HTML_SCRIPT_DEBUG , " HTMLScriptElement: Running script {} " , attribute ( HTML : : AttributeNames : : src ) . value_or ( String { } ) ) ;
2021-01-28 20:31:20 +00:00
else
2021-02-07 15:33:24 +03:30
dbgln_if ( HTML_SCRIPT_DEBUG , " HTMLScriptElement: Running inline script " ) ;
2021-01-28 20:31:20 +00:00
2022-11-20 17:12:17 +01:00
// 3. Run the classic script given by el's result.
2025-01-21 09:12:05 -05:00
( void ) as < ClassicScript > ( * m_result . get < GC : : Ref < Script > > ( ) ) . run ( ) ;
2021-01-28 20:31:20 +00:00
2021-12-15 03:59:36 +00:00
// 4. Set document's currentScript attribute to oldCurrentScript.
2022-11-20 17:12:17 +01:00
document - > set_current_script ( { } , old_current_script ) ;
}
// -> "module"
else if ( m_script_type = = ScriptType : : Module ) {
2021-09-09 18:43:28 +02:00
// 1. Assert: document's currentScript attribute is null.
2022-11-20 17:12:17 +01:00
VERIFY ( document - > current_script ( ) = = nullptr ) ;
2021-09-09 18:43:28 +02:00
2022-11-20 17:12:17 +01:00
// 2. Run the module script given by el's result.
2025-01-21 09:12:05 -05:00
( void ) as < JavaScriptModuleScript > ( * m_result . get < GC : : Ref < Script > > ( ) ) . run ( ) ;
2024-04-13 21:22:05 +01:00
}
// -> "importmap"
else if ( m_script_type = = ScriptType : : ImportMap ) {
// 1. Register an import map given el's relevant global object and el's result.
2025-01-21 09:12:05 -05:00
m_result . get < GC : : Ref < ImportMapParseResult > > ( ) - > register_import_map ( as < Window > ( relevant_global_object ( * this ) ) ) ;
2021-01-28 20:31:20 +00:00
}
2022-11-20 17:12:17 +01:00
// 7. Decrement the ignore-destructive-writes counter of document, if it was incremented in the earlier step.
2021-01-28 20:31:20 +00:00
if ( incremented_destructive_writes_counter )
2022-11-20 17:12:17 +01:00
document - > decrement_ignore_destructive_writes_counter ( ) ;
2021-01-28 20:31:20 +00:00
2022-11-20 17:12:17 +01:00
// 8. If el's from an external file is true, then fire an event named load at el.
2021-01-28 20:31:20 +00:00
if ( m_from_an_external_file )
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : load ) ) ;
2021-01-28 20:31:20 +00:00
}
// https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script
2024-10-25 16:27:26 +13:00
// https://whatpr.org/html/9893/scripting.html#prepare-a-script
2021-03-15 19:41:15 +01:00
void HTMLScriptElement : : prepare_script ( )
2020-05-24 22:00:46 +02:00
{
2022-11-20 17:12:17 +01:00
// 1. If el's already started is true, then return.
2021-01-28 20:31:20 +00:00
if ( m_already_started ) {
dbgln ( " HTMLScriptElement: Refusing to run script because it has already started. " ) ;
2020-05-24 22:00:46 +02:00
return ;
2021-01-28 20:31:20 +00:00
}
2022-11-20 17:12:17 +01:00
// 2. Let parser document be el's parser document.
2024-11-15 04:01:23 +13:00
GC : : Ptr < DOM : : Document > parser_document = m_parser_document ;
2021-09-08 19:50:53 +02:00
2022-11-20 17:12:17 +01:00
// 3. Set el's parser document to null.
2020-05-24 22:00:46 +02:00
m_parser_document = nullptr ;
2022-11-20 17:12:17 +01:00
// 4. If parser document is non-null and el does not have an async attribute, then set el's force async to true.
2020-06-03 20:51:28 +02:00
if ( parser_document & & ! has_attribute ( HTML : : AttributeNames : : async ) ) {
2022-11-20 14:22:14 +01:00
m_force_async = true ;
2020-05-24 22:00:46 +02:00
}
2022-11-20 17:12:17 +01:00
// 5. Let source text be el's child text content.
2020-05-24 22:00:46 +02:00
auto source_text = child_text_content ( ) ;
2025-07-28 09:46:06 -04:00
auto source_text_utf8 = source_text . to_utf8_but_should_be_ported_to_utf16 ( ) ;
2021-09-08 19:50:53 +02:00
2022-11-20 17:12:17 +01:00
// 6. If el has no src attribute, and source text is the empty string, then return.
if ( ! has_attribute ( HTML : : AttributeNames : : src ) & & source_text . is_empty ( ) ) {
2020-05-24 22:00:46 +02:00
return ;
2022-11-20 17:12:17 +01:00
}
2020-05-24 22:00:46 +02:00
2022-11-20 17:12:17 +01:00
// 7. If el is not connected, then return.
2021-01-28 20:31:20 +00:00
if ( ! is_connected ( ) ) {
dbgln ( " HTMLScriptElement: Refusing to run script because the element is not connected. " ) ;
2020-05-24 22:00:46 +02:00
return ;
2021-01-28 20:31:20 +00:00
}
2020-05-24 22:00:46 +02:00
2022-11-20 17:12:17 +01:00
// 8. If any of the following are true:
// - el has a type attribute whose value is the empty string;
// - el has no type attribute but it has a language attribute and that attribute's value is the empty string; or
// - el has neither a type attribute nor a language attribute
2024-01-13 20:12:18 +13:00
String script_block_type ;
auto maybe_type_attribute = attribute ( HTML : : AttributeNames : : type ) ;
auto maybe_language_attribute = attribute ( HTML : : AttributeNames : : language ) ;
if ( ( maybe_type_attribute . has_value ( ) & & maybe_type_attribute - > is_empty ( ) )
| | ( ! maybe_type_attribute . has_value ( ) & & maybe_language_attribute . has_value ( ) & & maybe_language_attribute - > is_empty ( ) )
| | ( ! maybe_type_attribute . has_value ( ) & & ! maybe_language_attribute . has_value ( ) ) ) {
2022-11-20 17:12:17 +01:00
// then let the script block's type string for this script element be "text/javascript".
2024-01-13 20:12:18 +13:00
script_block_type = " text/javascript " _string ;
2022-11-20 17:12:17 +01:00
}
// Otherwise, if el has a type attribute,
2024-01-13 20:12:18 +13:00
else if ( maybe_type_attribute . has_value ( ) ) {
2022-11-20 17:12:17 +01:00
// then let the script block's type string be the value of that attribute with leading and trailing ASCII whitespace stripped.
2024-01-13 20:12:18 +13:00
script_block_type = MUST ( maybe_type_attribute - > trim ( Infra : : ASCII_WHITESPACE ) ) ;
2022-11-20 17:12:17 +01:00
}
// Otherwise, el has a non-empty language attribute;
2024-01-13 20:12:18 +13:00
else if ( maybe_language_attribute . has_value ( ) & & ! maybe_language_attribute - > is_empty ( ) ) {
2022-11-20 17:12:17 +01:00
// let the script block's type string be the concatenation of "text/" and the value of el's language attribute.
2024-01-13 20:12:18 +13:00
script_block_type = MUST ( String : : formatted ( " text/{} " , maybe_language_attribute . value ( ) ) ) ;
2021-01-28 20:31:20 +00:00
}
2022-11-20 17:12:17 +01:00
// 9. If the script block's type string is a JavaScript MIME type essence match,
2024-10-29 23:59:32 +01:00
if ( MimeSniff : : is_javascript_mime_type_essence_match ( script_block_type ) ) {
2022-11-20 17:12:17 +01:00
// then set el's type to "classic".
2021-01-28 20:31:20 +00:00
m_script_type = ScriptType : : Classic ;
2022-11-20 17:12:17 +01:00
}
// 10. Otherwise, if the script block's type string is an ASCII case-insensitive match for the string "module",
2025-05-18 15:04:56 +12:00
else if ( script_block_type . equals_ignoring_ascii_case ( " module " sv ) ) {
2022-11-20 17:12:17 +01:00
// then set el's type to "module".
2021-01-28 20:31:20 +00:00
m_script_type = ScriptType : : Module ;
2022-11-20 17:12:17 +01:00
}
// 11. Otherwise, if the script block's type string is an ASCII case-insensitive match for the string "importmap",
2025-05-18 15:04:56 +12:00
else if ( script_block_type . equals_ignoring_ascii_case ( " importmap " sv ) ) {
2022-11-20 17:12:17 +01:00
// then set el's type to "importmap".
m_script_type = ScriptType : : ImportMap ;
}
// 12. Otherwise, return. (No script is executed, and el's type is left as null.)
else {
VERIFY ( m_script_type = = ScriptType : : Null ) ;
2021-01-28 20:31:20 +00:00
return ;
}
2020-05-24 22:00:46 +02:00
2022-11-20 17:12:17 +01:00
// 13. If parser document is non-null, then set el's parser document back to parser document and set el's force async to false.
2020-05-24 22:00:46 +02:00
if ( parser_document ) {
2022-08-28 13:42:07 +02:00
m_parser_document = parser_document ;
2022-11-20 14:22:14 +01:00
m_force_async = false ;
2020-05-24 22:00:46 +02:00
}
2022-11-20 17:12:17 +01:00
// 14. Set el's already started to true.
2020-05-24 22:00:46 +02:00
m_already_started = true ;
2021-09-08 19:50:53 +02:00
2022-11-20 17:12:17 +01:00
// 15. Set el's preparation-time document to its node document.
2022-08-28 13:42:07 +02:00
m_preparation_time_document = & document ( ) ;
2020-05-24 22:00:46 +02:00
2022-11-20 17:12:17 +01:00
// 16. If parser document is non-null, and parser document is not equal to el's preparation-time document, then return.
if ( parser_document ! = nullptr & & parser_document ! = m_preparation_time_document ) {
2021-01-28 20:31:20 +00:00
dbgln ( " HTMLScriptElement: Refusing to run script because the parser document is not the same as the preparation time document. " ) ;
2020-05-24 22:00:46 +02:00
return ;
}
2022-11-20 17:12:17 +01:00
// 17. If scripting is disabled for el, then return.
2021-07-05 03:59:47 +01:00
if ( is_scripting_disabled ( ) ) {
dbgln ( " HTMLScriptElement: Refusing to run script because scripting is disabled. " ) ;
return ;
}
2021-01-28 20:31:20 +00:00
2022-11-20 17:12:17 +01:00
// 18. If el has a nomodule content attribute and its type is "classic", then return.
2021-01-28 20:31:20 +00:00
if ( m_script_type = = ScriptType : : Classic & & has_attribute ( HTML : : AttributeNames : : nomodule ) ) {
dbgln ( " HTMLScriptElement: Refusing to run classic script because it has the nomodule attribute. " ) ;
return ;
}
2024-12-02 16:01:19 +00:00
// 19. If el does not have a src content attribute, and the Should element's inline behavior be blocked by Content Security Policy?
// algorithm returns "Blocked" when given el, "script", and source text, then return. [CSP]
if ( ! has_attribute ( AttributeNames : : src )
2025-07-28 09:46:06 -04:00
& & ContentSecurityPolicy : : should_elements_inline_type_behavior_be_blocked_by_content_security_policy ( realm ( ) , * this , ContentSecurityPolicy : : Directives : : Directive : : InlineType : : Script , source_text_utf8 ) = = ContentSecurityPolicy : : Directives : : Directive : : Result : : Blocked ) {
2024-12-02 16:01:19 +00:00
dbgln ( " HTMLScriptElement: Refusing to run inline script because it violates the Content Security Policy. " ) ;
return ;
}
2021-01-28 20:31:20 +00:00
2022-11-20 17:12:17 +01:00
// 20. If el has an event attribute and a for attribute, and el's type is "classic", then:
2021-01-28 20:31:20 +00:00
if ( m_script_type = = ScriptType : : Classic & & has_attribute ( HTML : : AttributeNames : : event ) & & has_attribute ( HTML : : AttributeNames : : for_ ) ) {
2022-11-20 17:12:17 +01:00
// 1. Let for be the value of el's' for attribute.
2024-01-16 19:04:45 +01:00
auto for_ = get_attribute_value ( HTML : : AttributeNames : : for_ ) ;
2021-09-08 19:52:09 +02:00
2022-11-20 17:12:17 +01:00
// 2. Let event be the value of el's event attribute.
2024-01-16 19:04:45 +01:00
auto event = get_attribute_value ( HTML : : AttributeNames : : event ) ;
2021-01-28 20:31:20 +00:00
2021-09-08 19:52:09 +02:00
// 3. Strip leading and trailing ASCII whitespace from event and for.
2024-01-16 19:04:45 +01:00
for_ = MUST ( for_ . trim ( Infra : : ASCII_WHITESPACE ) ) ;
event = MUST ( event . trim ( Infra : : ASCII_WHITESPACE ) ) ;
2021-09-08 19:50:53 +02:00
2022-11-20 17:12:17 +01:00
// 4. If for is not an ASCII case-insensitive match for the string "window", then return.
2025-05-18 15:04:56 +12:00
if ( ! for_ . equals_ignoring_ascii_case ( " window " sv ) ) {
2021-01-28 20:31:20 +00:00
dbgln ( " HTMLScriptElement: Refusing to run classic script because the provided 'for' attribute is not equal to 'window' " ) ;
return ;
}
2022-11-20 17:12:17 +01:00
// 5. If event is not an ASCII case-insensitive match for either the string "onload" or the string "onload()", then return.
2025-05-18 15:04:56 +12:00
if ( ! event . equals_ignoring_ascii_case ( " onload " sv )
& & ! event . equals_ignoring_ascii_case ( " onload() " sv ) ) {
2021-01-28 20:31:20 +00:00
dbgln ( " HTMLScriptElement: Refusing to run classic script because the provided 'event' attribute is not equal to 'onload' or 'onload()' " ) ;
return ;
}
}
2023-05-10 17:08:07 -04:00
// 21. If el has a charset attribute, then let encoding be the result of getting an encoding from the value of the charset attribute.
// If el does not have a charset attribute, or if getting an encoding failed, then let encoding be el's node document's the encoding.
Optional < String > encoding ;
2021-09-08 19:50:53 +02:00
2023-05-10 17:08:07 -04:00
if ( has_attribute ( HTML : : AttributeNames : : charset ) ) {
2024-01-16 19:04:45 +01:00
auto charset = TextCodec : : get_standardized_encoding ( get_attribute_value ( HTML : : AttributeNames : : charset ) ) ;
2023-05-10 17:08:07 -04:00
if ( charset . has_value ( ) )
encoding = String : : from_utf8 ( * charset ) . release_value_but_fixme_should_propagate_errors ( ) ;
}
2021-09-08 19:50:53 +02:00
2023-05-10 17:08:07 -04:00
if ( ! encoding . has_value ( ) ) {
2023-09-15 21:46:58 +12:00
encoding = document ( ) . encoding_or_default ( ) ;
2023-05-10 17:08:07 -04:00
}
2021-09-08 19:50:53 +02:00
2023-05-10 17:08:07 -04:00
VERIFY ( encoding . has_value ( ) ) ;
2021-09-08 19:50:53 +02:00
2023-05-10 17:08:07 -04:00
// 22. Let classic script CORS setting be the current state of el's crossorigin content attribute.
auto classic_script_cors_setting = m_crossorigin ;
2021-09-08 19:50:53 +02:00
2023-05-10 17:08:07 -04:00
// 23. Let module script credentials mode be the CORS settings attribute credentials mode for el's crossorigin content attribute.
auto module_script_credential_mode = cors_settings_attribute_credentials_mode ( m_crossorigin ) ;
2024-12-02 16:01:19 +00:00
// 24. Let cryptographic nonce be el's [[CryptographicNonce]] internal slot's value.
auto cryptographic_nonce = m_cryptographic_nonce ;
2021-09-08 19:50:53 +02:00
2023-05-10 17:08:07 -04:00
// 25. If el has an integrity attribute, then let integrity metadata be that attribute's value.
// Otherwise, let integrity metadata be the empty string.
String integrity_metadata ;
2024-01-16 19:04:45 +01:00
if ( auto maybe_integrity = attribute ( HTML : : AttributeNames : : integrity ) ; maybe_integrity . has_value ( ) ) {
integrity_metadata = * maybe_integrity ;
2023-05-10 17:08:07 -04:00
}
2021-09-08 19:50:53 +02:00
2023-05-10 17:08:07 -04:00
// 26. Let referrer policy be the current state of el's referrerpolicy content attribute.
auto referrer_policy = m_referrer_policy ;
2024-05-30 21:10:12 +01:00
// 27. Let fetch priority be the current state of el's fetchpriority content attribute.
auto fetch_priority = Fetch : : Infrastructure : : request_priority_from_string ( get_attribute_value ( HTML : : AttributeNames : : fetchpriority ) ) . value_or ( Fetch : : Infrastructure : : Request : : Priority : : Auto ) ;
// 28. Let parser metadata be "parser-inserted" if el is parser-inserted, and "not-parser-inserted" otherwise.
2023-05-10 17:08:07 -04:00
auto parser_metadata = is_parser_inserted ( )
? Fetch : : Infrastructure : : Request : : ParserMetadata : : ParserInserted
: Fetch : : Infrastructure : : Request : : ParserMetadata : : NotParserInserted ;
2024-05-30 21:10:12 +01:00
// 29. Let options be a script fetch options whose cryptographic nonce is cryptographic nonce,
2023-05-10 17:08:07 -04:00
// integrity metadata is integrity metadata, parser metadata is parser metadata,
2024-05-30 21:10:12 +01:00
// credentials mode is module script credentials mode, referrer policy is referrer policy,
// and fetch priority is fetch priority.
2023-05-10 17:08:07 -04:00
ScriptFetchOptions options {
2024-12-02 16:01:19 +00:00
. cryptographic_nonce = move ( cryptographic_nonce ) ,
2023-05-10 17:08:07 -04:00
. integrity_metadata = move ( integrity_metadata ) ,
. parser_metadata = parser_metadata ,
. credentials_mode = module_script_credential_mode ,
. referrer_policy = move ( referrer_policy ) ,
2024-05-30 21:10:12 +01:00
. fetch_priority = move ( fetch_priority ) ,
2023-05-10 17:08:07 -04:00
} ;
2021-09-08 19:50:53 +02:00
2024-05-30 21:10:12 +01:00
// 30. Let settings object be el's node document's relevant settings object.
2022-11-20 17:12:17 +01:00
auto & settings_object = document ( ) . relevant_settings_object ( ) ;
2020-05-24 22:00:46 +02:00
2024-05-30 21:10:12 +01:00
// 31. If el has a src content attribute, then:
2020-06-03 20:51:28 +02:00
if ( has_attribute ( HTML : : AttributeNames : : src ) ) {
2022-11-20 17:12:17 +01:00
// 1. If el's type is "importmap",
if ( m_script_type = = ScriptType : : ImportMap ) {
// then queue an element task on the DOM manipulation task source given el to fire an event named error at el, and return.
queue_an_element_task ( HTML : : Task : : Source : : DOMManipulation , [ this ] {
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2022-11-20 17:12:17 +01:00
} ) ;
return ;
}
// 2. Let src be the value of el's src attribute.
2024-01-16 19:04:45 +01:00
auto src = get_attribute_value ( HTML : : AttributeNames : : src ) ;
2022-11-20 17:12:17 +01:00
// 3. If src is the empty string, then queue an element task on the DOM manipulation task source given el to fire an event named error at el, and return.
2020-05-24 22:00:46 +02:00
if ( src . is_empty ( ) ) {
2021-01-28 20:31:20 +00:00
dbgln ( " HTMLScriptElement: Refusing to run script because the src attribute is empty. " ) ;
2022-10-05 17:01:16 +01:00
queue_an_element_task ( HTML : : Task : : Source : : DOMManipulation , [ this ] {
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2021-09-09 01:06:38 +02:00
} ) ;
2021-01-28 20:31:20 +00:00
return ;
2020-05-24 22:00:46 +02:00
}
2022-11-20 17:12:17 +01:00
// 4. Set el's from an external file to true.
2020-05-24 22:00:46 +02:00
m_from_an_external_file = true ;
2025-06-24 15:35:11 +01:00
// 5. Let url be the result of encoding-parsing a URL given src, relative to el's node document.
auto url = document ( ) . encoding_parse_url ( src ) ;
2022-11-20 17:12:17 +01:00
2025-06-24 15:35:11 +01:00
// 6. If url is failure, then queue an element task on the DOM manipulation task source given el to fire an event named error at el, and return.
2025-01-23 19:40:57 +13:00
if ( ! url . has_value ( ) ) {
2021-01-28 20:31:20 +00:00
dbgln ( " HTMLScriptElement: Refusing to run script because the src URL '{}' is invalid. " , url ) ;
2022-10-05 17:01:16 +01:00
queue_an_element_task ( HTML : : Task : : Source : : DOMManipulation , [ this ] {
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2021-09-09 01:06:38 +02:00
} ) ;
2021-01-28 20:31:20 +00:00
return ;
2020-05-24 22:00:46 +02:00
}
2025-02-04 19:09:14 +01:00
// 7. If el is potentially render-blocking, then block rendering on el.
if ( is_potentially_render_blocking ( ) ) {
block_rendering ( ) ;
}
2021-04-14 10:36:34 -04:00
2022-11-20 17:12:17 +01:00
// 8. Set el's delaying the load event to true.
begin_delaying_document_load_event ( * m_preparation_time_document ) ;
// FIXME: 9. If el is currently render-blocking, then set options's render-blocking to true.
// 10. Let onComplete given result be the following steps:
2023-10-29 01:46:02 +02:00
OnFetchScriptComplete on_complete = create_on_fetch_script_complete ( heap ( ) , [ this ] ( auto result ) {
2023-05-10 17:08:07 -04:00
// 1. Mark as ready el given result.
if ( result )
mark_as_ready ( Result { * result } ) ;
else
mark_as_ready ( ResultState : : Null { } ) ;
2023-10-29 01:46:02 +02:00
} ) ;
2022-11-20 17:12:17 +01:00
// 11. Switch on el's type:
// -> "classic"
if ( m_script_type = = ScriptType : : Classic ) {
// Fetch a classic script given url, settings object, options, classic script CORS setting, encoding, and onComplete.
2025-01-23 19:40:57 +13:00
fetch_classic_script ( * this , * url , settings_object , move ( options ) , classic_script_cors_setting , encoding . release_value ( ) , on_complete ) . release_value_but_fixme_should_propagate_errors ( ) ;
2022-11-20 17:12:17 +01:00
}
// -> "module"
else if ( m_script_type = = ScriptType : : Module ) {
2024-06-01 11:25:58 +01:00
// If el does not have an integrity attribute, then set options's integrity metadata to the result of resolving a module integrity metadata with url and settings object.
if ( ! has_attribute ( HTML : : AttributeNames : : integrity ) )
2025-01-23 19:40:57 +13:00
options . integrity_metadata = resolve_a_module_integrity_metadata ( * url , settings_object ) ;
2024-06-01 11:25:58 +01:00
2022-10-03 21:08:02 +02:00
// Fetch an external module script graph given url, settings object, options, and onComplete.
2025-01-23 19:40:57 +13:00
fetch_external_module_script_graph ( realm ( ) , * url , settings_object , options , on_complete ) ;
2021-01-28 20:31:20 +00:00
}
2022-11-20 17:12:17 +01:00
}
2021-09-08 19:50:53 +02:00
2024-05-30 21:10:12 +01:00
// 32. If el does not have a src content attribute:
2022-11-20 17:12:17 +01:00
if ( ! has_attribute ( HTML : : AttributeNames : : src ) ) {
// Let base URL be el's node document's document base URL.
auto base_url = document ( ) . base_url ( ) ;
2021-09-08 19:50:53 +02:00
2022-11-20 17:12:17 +01:00
// 2. Switch on el's type:
// -> "classic"
2021-01-28 20:31:20 +00:00
if ( m_script_type = = ScriptType : : Classic ) {
2024-10-25 16:27:26 +13:00
// 1. Let script be the result of creating a classic script using source text, settings object's realm, base URL, and options.
2022-10-03 21:08:02 +02:00
// FIXME: Pass options.
2025-07-28 09:46:06 -04:00
auto script = ClassicScript : : create ( m_document - > url ( ) . to_byte_string ( ) , source_text_utf8 , settings_object . realm ( ) , base_url , m_source_line_number ) ;
2021-09-08 19:50:53 +02:00
2022-11-20 17:12:17 +01:00
// 2. Mark as ready el given script.
mark_as_ready ( Result ( move ( script ) ) ) ;
}
// -> "module"
else if ( m_script_type = = ScriptType : : Module ) {
// 1. Set el's delaying the load event to true.
begin_delaying_document_load_event ( * m_preparation_time_document ) ;
2022-10-03 21:08:02 +02:00
2023-10-29 01:46:02 +02:00
auto steps = create_on_fetch_script_complete ( heap ( ) , [ this ] ( auto result ) {
2022-10-03 21:08:02 +02:00
// 1. Mark as ready el given result.
2022-11-20 17:12:17 +01:00
if ( ! result )
mark_as_ready ( ResultState : : Null { } ) ;
else
mark_as_ready ( Result ( * result ) ) ;
2022-10-03 21:08:02 +02:00
} ) ;
2023-10-29 01:46:02 +02:00
// 2. Fetch an inline module script graph, given source text, base URL, settings object, options, and with the following steps given result:
// FIXME: Pass options
2023-12-16 17:49:34 +03:30
fetch_inline_module_script_graph ( realm ( ) , m_document - > url ( ) . to_byte_string ( ) , source_text . to_byte_string ( ) , base_url , document ( ) . relevant_settings_object ( ) , steps ) ;
2021-01-28 20:31:20 +00:00
}
2022-11-20 17:12:17 +01:00
// -> "importmap"
else if ( m_script_type = = ScriptType : : ImportMap ) {
2024-12-03 20:31:14 +13:00
// 1. Let result be the result of creating an import map parse result given source text and base URL.
2024-04-13 21:22:05 +01:00
auto result = ImportMapParseResult : : create ( realm ( ) , source_text . to_byte_string ( ) , base_url ) ;
2024-12-03 20:31:14 +13:00
// 2. Mark as ready el given result.
2024-04-13 21:22:05 +01:00
mark_as_ready ( Result ( move ( result ) ) ) ;
2022-11-20 17:12:17 +01:00
}
2020-05-24 22:00:46 +02:00
}
2024-05-30 21:10:12 +01:00
// 33. If el's type is "classic" and el has a src attribute, or el's type is "module":
2022-11-20 20:56:38 +01:00
if ( ( m_script_type = = ScriptType : : Classic & & has_attribute ( HTML : : AttributeNames : : src ) ) | | m_script_type = = ScriptType : : Module ) {
2022-11-20 17:12:17 +01:00
// 1. Assert: el's result is "uninitialized".
2022-11-20 20:56:38 +01:00
// FIXME: I believe this step to be a spec bug, and it should be removed: https://github.com/whatwg/html/issues/8534
2022-11-20 17:12:17 +01:00
// 2. If el has an async attribute or el's force async is true:
if ( has_attribute ( HTML : : AttributeNames : : async ) | | m_force_async ) {
// 1. Let scripts be el's preparation-time document's set of scripts that will execute as soon as possible.
// 2. Append el to scripts.
m_preparation_time_document - > scripts_to_execute_as_soon_as_possible ( ) . append ( * this ) ;
// 3. Set el's steps to run when the result is ready to the following:
m_steps_to_run_when_the_result_is_ready = [ this ] {
// 1. Execute the script element el.
execute_script ( ) ;
// 2. Remove el from scripts.
m_preparation_time_document - > scripts_to_execute_as_soon_as_possible ( ) . remove_first_matching ( [ this ] ( auto & entry ) {
return entry . ptr ( ) = = this ;
} ) ;
} ;
}
2021-09-08 19:50:53 +02:00
2022-11-20 17:12:17 +01:00
// 3. Otherwise, if el is not parser-inserted:
else if ( ! is_parser_inserted ( ) ) {
// 1. Let scripts be el's preparation-time document's list of scripts that will execute in order as soon as possible.
// 2. Append el to scripts.
m_preparation_time_document - > scripts_to_execute_in_order_as_soon_as_possible ( ) . append ( * this ) ;
// 3. Set el's steps to run when the result is ready to the following:
m_steps_to_run_when_the_result_is_ready = [ this ] {
auto & scripts = m_preparation_time_document - > scripts_to_execute_in_order_as_soon_as_possible ( ) ;
// 1. If scripts[0] is not el, then abort these steps.
if ( scripts [ 0 ] ! = this )
return ;
// 2. While scripts is not empty, and scripts[0]'s result is not "uninitialized":
while ( ! scripts . is_empty ( ) & & ! scripts [ 0 ] - > m_result . has < ResultState : : Uninitialized > ( ) ) {
// 1. Execute the script element scripts[0].
scripts [ 0 ] - > execute_script ( ) ;
// 2. Remove scripts[0].
scripts . take_first ( ) ;
}
} ;
}
2020-05-24 22:00:46 +02:00
2022-11-20 17:12:17 +01:00
// 4. Otherwise, if el has a defer attribute or el's type is "module":
else if ( has_attribute ( HTML : : AttributeNames : : defer ) | | m_script_type = = ScriptType : : Module ) {
// 1. Append el to its parser document's list of scripts that will execute when the document has finished parsing.
2023-10-28 23:38:31 +02:00
m_parser_document - > add_script_to_execute_when_parsing_has_finished ( { } , * this ) ;
2020-05-24 22:00:46 +02:00
2022-11-20 17:12:17 +01:00
// 2. Set el's steps to run when the result is ready to the following:
m_steps_to_run_when_the_result_is_ready = [ this ] {
// set el's ready to be parser-executed to true. (The parser will handle executing the script.)
m_ready_to_be_parser_executed = true ;
} ;
}
2021-09-20 17:06:10 +02:00
2022-11-20 17:12:17 +01:00
// 5. Otherwise:
else {
// 1. Set el's parser document's pending parsing-blocking script to el.
2024-07-25 14:08:45 +02:00
m_parser_document - > set_pending_parsing_blocking_script ( this ) ;
2022-11-20 17:12:17 +01:00
// FIXME: 2. Block rendering on el.
2020-05-24 22:00:46 +02:00
2022-11-20 17:12:17 +01:00
// 3. Set el's steps to run when the result is ready to the following:
m_steps_to_run_when_the_result_is_ready = [ this ] {
// set el's ready to be parser-executed to true. (The parser will handle executing the script.)
m_ready_to_be_parser_executed = true ;
} ;
}
}
2021-01-28 20:31:20 +00:00
2024-05-30 21:10:12 +01:00
// 34. Otherwise:
2020-05-24 22:00:46 +02:00
else {
2022-11-20 17:12:17 +01:00
// 1. Assert: el's result is not "uninitialized".
VERIFY ( ! m_result . has < ResultState : : Uninitialized > ( ) ) ;
// 2. If all of the following are true:
// - el's type is "classic";
// - el is parser-inserted;
// - el's parser document has a style sheet that is blocking scripts; and
// FIXME: - either the parser that created el is an XML parser, or it's an HTML parser whose script nesting level is not greater than one,
// then:
if ( m_script_type = = ScriptType : : Classic
& & is_parser_inserted ( )
& & m_parser_document - > has_a_style_sheet_that_is_blocking_scripts ( ) ) {
// 1. Set el's parser document's pending parsing-blocking script to el.
2024-07-25 14:08:45 +02:00
m_parser_document - > set_pending_parsing_blocking_script ( this ) ;
2022-11-20 17:12:17 +01:00
// 2. Set el's ready to be parser-executed to true. (The parser will handle executing the script.)
m_ready_to_be_parser_executed = true ;
}
// 3. Otherwise,
else {
// immediately execute the script element el, even if other scripts are already executing.
execute_script ( ) ;
}
2020-05-24 22:00:46 +02:00
}
}
2024-12-10 19:35:21 +13:00
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:html-element-post-connection-steps-4
2025-01-27 01:16:33 +13:00
void HTMLScriptElement : : children_changed ( ChildrenChangedMetadata const * metadata )
2021-03-15 19:41:15 +01:00
{
2025-01-27 01:16:33 +13:00
Base : : children_changed ( metadata ) ;
2025-01-26 13:49:39 +13:00
2024-12-10 19:35:21 +13:00
// 1. Run the script HTML element post-connection steps, given the script element.
post_connection ( ) ;
}
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:prepare-the-script-element-5
void HTMLScriptElement : : post_connection ( )
{
// 1. If insertedNode is not connected, then return.
if ( ! is_connected ( ) )
return ;
// 2. If insertedNode is parser-inserted, then return.
if ( is_parser_inserted ( ) )
return ;
// 3. Prepare the script element given insertedNode.
prepare_script ( ) ;
2021-03-15 19:41:15 +01:00
}
2022-11-20 17:12:17 +01:00
// https://html.spec.whatwg.org/multipage/scripting.html#mark-as-ready
void HTMLScriptElement : : mark_as_ready ( Result result )
{
// 1. Set el's result to result.
m_result = move ( result ) ;
// 2. If el's steps to run when the result is ready are not null, then run them.
if ( m_steps_to_run_when_the_result_is_ready )
m_steps_to_run_when_the_result_is_ready ( ) ;
// 3. Set el's steps to run when the result is ready to null.
m_steps_to_run_when_the_result_is_ready = nullptr ;
// 4. Set el's delaying the load event to false.
m_document_load_event_delayer . clear ( ) ;
}
2023-03-10 14:56:44 +01:00
void HTMLScriptElement : : unmark_as_already_started ( Badge < DOM : : Range > )
{
m_already_started = false ;
}
void HTMLScriptElement : : unmark_as_parser_inserted ( Badge < DOM : : Range > )
{
m_parser_document = nullptr ;
}
2025-08-05 21:58:13 +02:00
// https://www.w3.org/TR/trusted-types/#the-text-idl-attribute
WebIDL : : ExceptionOr < void > HTMLScriptElement : : set_text ( TrustedTypes : : TrustedScriptOrString text )
{
// 1. Let value be the result of calling Get Trusted Type compliant string with
// TrustedScript, this’ s relevant global object, the given value, HTMLScriptElement text, and script.
auto const value = TRY ( get_trusted_type_compliant_string (
TrustedTypes : : TrustedTypeName : : TrustedScript ,
HTML : : relevant_global_object ( * this ) ,
text ,
TrustedTypes : : InjectionSink : : HTMLScriptElementtext ,
TrustedTypes : : Script . to_string ( ) ) ) ;
// 2. Set this’ s script text value to the given value.
m_script_text = value ;
// 3. String replace all with the given value within this.
string_replace_all ( value ) ;
return { } ;
}
2025-08-06 02:28:52 +02:00
// https://www.w3.org/TR/trusted-types/#the-src-idl-attribute
WebIDL : : ExceptionOr < void > HTMLScriptElement : : set_src ( TrustedTypes : : TrustedScriptURLOrString text )
{
// 1. Let value be the result of calling Get Trusted Type compliant string with
// TrustedScriptURL, this’ s relevant global object, the given value, HTMLScriptElement src, and script.
auto const value = TRY ( TrustedTypes : : get_trusted_type_compliant_string (
TrustedTypes : : TrustedTypeName : : TrustedScriptURL ,
HTML : : relevant_global_object ( * this ) ,
text ,
TrustedTypes : : InjectionSink : : HTMLScriptElementsrc ,
TrustedTypes : : Script . to_string ( ) ) ) ;
// 2. Set this’ s src content attribute to value.
TRY ( set_attribute ( AttributeNames : : src , value ) ) ;
return { } ;
}
2024-04-14 18:54:28 +02:00
// https://html.spec.whatwg.org/multipage/scripting.html#dom-script-async
bool HTMLScriptElement : : async ( ) const
{
// 1. If this's force async is true, then return true.
if ( m_force_async )
return true ;
// 2. If this's async content attribute is present, then return true.
if ( has_attribute ( HTML : : AttributeNames : : async ) )
return true ;
// 3. Return false.
return false ;
}
void HTMLScriptElement : : set_async ( bool async )
{
// 1. Set this's force async to false.
m_force_async = false ;
// 2. If the given value is true, then set this's async content attribute to the empty string.
if ( async ) {
MUST ( set_attribute ( HTML : : AttributeNames : : async , " " _string ) ) ;
}
// 3. Otherwise, remove this's async content attribute.
else {
remove_attribute ( HTML : : AttributeNames : : async ) ;
}
}
2025-01-05 15:46:57 +00:00
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:concept-node-clone-ext
2025-01-11 17:37:08 +00:00
WebIDL : : ExceptionOr < void > HTMLScriptElement : : cloned ( Node & copy , bool subtree ) const
2025-01-05 15:46:57 +00:00
{
TRY ( Base : : cloned ( copy , subtree ) ) ;
// The cloning steps for script elements given node, copy, and subtree are to set copy's already started to node's already started.
2025-01-21 09:12:05 -05:00
auto & script_copy = as < HTMLScriptElement > ( copy ) ;
2025-01-05 15:46:57 +00:00
script_copy . m_already_started = m_already_started ;
return { } ;
}
2025-02-04 19:09:14 +01:00
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#implicitly-potentially-render-blocking
bool HTMLScriptElement : : is_implicitly_potentially_render_blocking ( ) const
{
// A script element el is implicitly potentially render-blocking if el's type is "classic", el is parser-inserted, and el does not have an async or defer attribute.
return m_script_type = = ScriptType : : Classic & & is_parser_inserted ( ) & & ! has_attribute ( AttributeNames : : async ) & & ! has_attribute ( AttributeNames : : defer ) ;
}
2020-03-14 12:41:51 +01:00
}