2021-09-09 19:03:01 +03:00
/*
2023-06-17 14:16:35 +03:00
* Copyright ( c ) 2021 - 2023 , Andreas Kling < kling @ serenityos . org >
2021-09-09 19:03:01 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-02-05 18:18:22 +03:00
# include <AK/Debug.h>
2021-09-12 22:56:44 +03:00
# include <LibCore/ElapsedTimer.h>
2023-06-17 14:16:35 +03:00
# include <LibJS/Bytecode/Interpreter.h>
2021-10-14 18:12:53 +03:00
# include <LibWeb/Bindings/ExceptionOrUtils.h>
2021-09-09 19:03:01 +03:00
# include <LibWeb/HTML/Scripting/ClassicScript.h>
2021-10-14 18:12:53 +03:00
# include <LibWeb/HTML/Scripting/Environments.h>
2022-02-07 17:12:41 +03:00
# include <LibWeb/HTML/Scripting/ExceptionReporter.h>
2022-09-25 19:28:46 +03:00
# include <LibWeb/WebIDL/DOMException.h>
2021-09-09 19:03:01 +03:00
namespace Web : : HTML {
2021-09-09 20:03:50 +03:00
// https://html.spec.whatwg.org/multipage/webappapis.html#creating-a-classic-script
2022-12-04 21:02:33 +03:00
JS : : NonnullGCPtr < ClassicScript > ClassicScript : : create ( DeprecatedString filename , StringView source , EnvironmentSettingsObject & environment_settings_object , AK : : URL base_url , size_t source_line_number , MutedErrors muted_errors )
2021-09-09 19:03:01 +03:00
{
2022-09-05 15:32:33 +03:00
auto & vm = environment_settings_object . realm ( ) . vm ( ) ;
2021-09-09 20:03:50 +03:00
// 1. If muted errors was not provided, let it be false. (NOTE: This is taken care of by the default argument.)
// 2. If muted errors is true, then set baseURL to about:blank.
if ( muted_errors = = MutedErrors : : Yes )
2022-07-11 20:32:29 +03:00
base_url = " about:blank " sv ;
2021-09-09 20:03:50 +03:00
2022-03-31 00:42:09 +03:00
// 3. If scripting is disabled for settings, then set source to the empty string.
if ( environment_settings_object . is_scripting_disabled ( ) )
2022-07-11 20:32:29 +03:00
source = " " sv ;
2021-09-09 20:03:50 +03:00
// 4. Let script be a new classic script that this algorithm will subsequently initialize.
2022-09-05 15:32:33 +03:00
auto script = vm . heap ( ) . allocate_without_realm < ClassicScript > ( move ( base_url ) , move ( filename ) , environment_settings_object ) ;
2021-09-09 20:03:50 +03:00
2021-10-14 18:12:53 +03:00
// 5. Set script's settings object to settings. (NOTE: This was already done when constructing.)
2021-09-09 20:03:50 +03:00
// 6. Set script's base URL to baseURL. (NOTE: This was already done when constructing.)
// FIXME: 7. Set script's fetch options to options.
// 8. Set script's muted errors to muted errors.
script - > m_muted_errors = muted_errors ;
2023-05-18 20:05:56 +03:00
// 9. Set script's parse error and error to rethrow to null.
script - > set_parse_error ( JS : : js_null ( ) ) ;
script - > set_error_to_rethrow ( JS : : js_null ( ) ) ;
2021-09-09 20:03:50 +03:00
// 10. Let result be ParseScript(source, settings's Realm, script).
2021-09-14 22:00:41 +03:00
auto parse_timer = Core : : ElapsedTimer : : start_new ( ) ;
2022-09-05 15:32:33 +03:00
auto result = JS : : Script : : parse ( source , environment_settings_object . realm ( ) , script - > filename ( ) , script , source_line_number ) ;
2022-02-05 18:18:22 +03:00
dbgln_if ( HTML_SCRIPT_DEBUG , " ClassicScript: Parsed {} in {}ms " , script - > filename ( ) , parse_timer . elapsed ( ) ) ;
2021-09-09 20:03:50 +03:00
2021-09-14 21:56:57 +03:00
// 11. If result is a list of errors, then:
if ( result . is_error ( ) ) {
2021-10-14 18:12:53 +03:00
auto & parse_error = result . error ( ) . first ( ) ;
2022-12-06 04:12:49 +03:00
dbgln_if ( HTML_SCRIPT_DEBUG , " ClassicScript: Failed to parse: {} " , parse_error . to_deprecated_string ( ) ) ;
2021-10-14 18:12:53 +03:00
2023-05-18 20:05:56 +03:00
// 1. Set script's parse error and its error to rethrow to result[0].
script - > set_parse_error ( JS : : SyntaxError : : create ( environment_settings_object . realm ( ) , parse_error . to_string ( ) . release_value_but_fixme_should_propagate_errors ( ) ) ) ;
script - > set_error_to_rethrow ( script - > parse_error ( ) ) ;
2021-09-14 21:56:57 +03:00
// 2. Return script.
2022-12-14 20:40:33 +03:00
return script ;
2021-09-14 21:56:57 +03:00
}
2021-09-09 20:03:50 +03:00
// 12. Set script's record to result.
2022-09-05 15:32:33 +03:00
script - > m_script_record = * result . release_value ( ) ;
2021-09-09 20:03:50 +03:00
// 13. Return script.
2022-12-14 20:40:33 +03:00
return script ;
2021-09-09 20:03:50 +03:00
}
// https://html.spec.whatwg.org/multipage/webappapis.html#run-a-classic-script
2022-12-09 20:48:25 +03:00
JS : : Completion ClassicScript : : run ( RethrowErrors rethrow_errors , JS : : GCPtr < JS : : Environment > lexical_environment_override )
2021-09-09 20:03:50 +03:00
{
2022-06-27 21:50:40 +03:00
// 1. Let settings be the settings object of script.
auto & settings = settings_object ( ) ;
2021-10-14 18:12:53 +03:00
// 2. Check if we can run script with settings. If this returns "do not run" then return NormalCompletion(empty).
2022-06-27 21:50:40 +03:00
if ( settings . can_run_script ( ) = = RunScriptDecision : : DoNotRun )
2021-10-14 18:12:53 +03:00
return JS : : normal_completion ( { } ) ;
// 3. Prepare to run script given settings.
2022-06-27 21:50:40 +03:00
settings . prepare_to_run_script ( ) ;
2021-10-14 18:12:53 +03:00
// 4. Let evaluationStatus be null.
JS : : Completion evaluation_status ;
// 5. If script's error to rethrow is not null, then set evaluationStatus to Completion { [[Type]]: throw, [[Value]]: script's error to rethrow, [[Target]]: empty }.
2023-05-18 20:05:56 +03:00
if ( ! error_to_rethrow ( ) . is_null ( ) ) {
evaluation_status = JS : : Completion { JS : : Completion : : Type : : Throw , error_to_rethrow ( ) , { } } ;
2021-10-14 18:12:53 +03:00
} else {
auto timer = Core : : ElapsedTimer : : start_new ( ) ;
// 6. Otherwise, set evaluationStatus to ScriptEvaluation(script's record).
2023-08-07 20:59:00 +03:00
evaluation_status = vm ( ) . bytecode_interpreter ( ) . run ( * m_script_record , lexical_environment_override ) ;
2021-10-14 18:12:53 +03:00
// FIXME: If ScriptEvaluation does not complete because the user agent has aborted the running script, leave evaluationStatus as null.
dbgln_if ( HTML_SCRIPT_DEBUG , " ClassicScript: Finished running script {}, Duration: {}ms " , filename ( ) , timer . elapsed ( ) ) ;
2021-09-18 02:38:40 +03:00
}
2021-10-14 18:12:53 +03:00
// 7. If evaluationStatus is an abrupt completion, then:
if ( evaluation_status . is_abrupt ( ) ) {
// 1. If rethrow errors is true and script's muted errors is false, then:
if ( rethrow_errors = = RethrowErrors : : Yes & & m_muted_errors = = MutedErrors : : No ) {
// 1. Clean up after running script with settings.
2022-06-27 21:50:40 +03:00
settings . clean_up_after_running_script ( ) ;
2021-09-09 20:03:50 +03:00
2021-10-14 18:12:53 +03:00
// 2. Rethrow evaluationStatus.[[Value]].
return JS : : throw_completion ( * evaluation_status . value ( ) ) ;
}
2022-01-16 15:16:04 +03:00
2021-10-14 18:12:53 +03:00
// 2. If rethrow errors is true and script's muted errors is true, then:
if ( rethrow_errors = = RethrowErrors : : Yes & & m_muted_errors = = MutedErrors : : Yes ) {
// 1. Clean up after running script with settings.
2022-06-27 21:50:40 +03:00
settings . clean_up_after_running_script ( ) ;
2022-01-08 23:28:27 +03:00
2021-10-14 18:12:53 +03:00
// 2. Throw a "NetworkError" DOMException.
2022-09-26 01:38:21 +03:00
return throw_completion ( WebIDL : : NetworkError : : create ( settings . realm ( ) , " Script error. " ) ) ;
2021-10-14 18:12:53 +03:00
}
2022-01-16 15:16:04 +03:00
2021-10-14 18:12:53 +03:00
// 3. Otherwise, rethrow errors is false. Perform the following steps:
VERIFY ( rethrow_errors = = RethrowErrors : : No ) ;
2022-01-16 15:16:04 +03:00
2021-10-14 18:12:53 +03:00
// 1. Report the exception given by evaluationStatus.[[Value]] for script.
2022-10-08 18:38:32 +03:00
report_exception ( evaluation_status , settings_object ( ) . realm ( ) ) ;
2021-10-14 18:12:53 +03:00
// 2. Clean up after running script with settings.
2022-06-27 21:50:40 +03:00
settings . clean_up_after_running_script ( ) ;
2021-10-14 18:12:53 +03:00
// 3. Return evaluationStatus.
return evaluation_status ;
2022-01-08 23:28:27 +03:00
}
2021-10-14 18:12:53 +03:00
// 8. Clean up after running script with settings.
2022-06-27 21:50:40 +03:00
settings . clean_up_after_running_script ( ) ;
2021-10-14 18:12:53 +03:00
// 9. If evaluationStatus is a normal completion, then return evaluationStatus.
VERIFY ( ! evaluation_status . is_abrupt ( ) ) ;
return evaluation_status ;
// FIXME: 10. If we've reached this point, evaluationStatus was left as null because the script was aborted prematurely during evaluation.
// Return Completion { [[Type]]: throw, [[Value]]: a new "QuotaExceededError" DOMException, [[Target]]: empty }.
2021-09-09 19:03:01 +03:00
}
2022-12-04 21:02:33 +03:00
ClassicScript : : ClassicScript ( AK : : URL base_url , DeprecatedString filename , EnvironmentSettingsObject & environment_settings_object )
2022-06-27 21:50:40 +03:00
: Script ( move ( base_url ) , move ( filename ) , environment_settings_object )
2021-09-09 19:03:01 +03:00
{
}
2022-03-14 22:21:51 +03:00
ClassicScript : : ~ ClassicScript ( ) = default ;
2021-09-09 19:03:01 +03:00
2022-09-05 15:32:33 +03:00
void ClassicScript : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_script_record ) ;
}
2021-09-09 19:03:01 +03:00
}