2023-11-08 11:47:41 -07:00
/*
* Copyright ( c ) 2023 , Andrew Kaster < akaster @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-12-20 13:47:01 -07:00
# include <LibIPC/File.h>
2023-11-08 11:47:41 -07:00
# include <LibJS/Runtime/ConsoleObject.h>
2025-05-18 14:10:01 -06:00
# include <LibWeb/Fetch/Enums.h>
2023-11-08 11:47:41 -07:00
# include <LibWeb/Fetch/Fetching/Fetching.h>
# include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
2024-07-09 15:54:22 -06:00
# include <LibWeb/HTML/DedicatedWorkerGlobalScope.h>
2025-05-18 14:10:01 -06:00
# include <LibWeb/HTML/MessageEvent.h>
2024-10-17 08:46:48 -04:00
# include <LibWeb/HTML/MessagePort.h>
2023-11-08 11:47:41 -07:00
# include <LibWeb/HTML/Scripting/ClassicScript.h>
2024-03-05 09:42:10 -07:00
# include <LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.h>
2023-11-08 11:47:41 -07:00
# include <LibWeb/HTML/Scripting/Fetching.h>
2025-05-18 14:10:01 -06:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2023-11-08 11:47:41 -07:00
# include <LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.h>
2025-05-18 14:10:01 -06:00
# include <LibWeb/HTML/SharedWorkerGlobalScope.h>
2023-11-08 11:47:41 -07:00
# include <LibWeb/HTML/WorkerDebugConsoleClient.h>
# include <LibWeb/HTML/WorkerGlobalScope.h>
2024-07-04 11:06:54 -04:00
# include <LibWeb/HighResolutionTime/TimeOrigin.h>
2023-11-08 11:47:41 -07:00
# include <LibWeb/Loader/ResourceLoader.h>
2025-05-18 14:10:01 -06:00
# include <WebWorker/WorkerHost.h>
2023-11-08 11:47:41 -07:00
namespace WebWorker {
2025-05-18 14:10:01 -06:00
WorkerHost : : WorkerHost ( URL : : URL url , Web : : Bindings : : WorkerType type , String name )
2023-12-20 13:47:01 -07:00
: m_url ( move ( url ) )
2024-09-10 20:57:48 -06:00
, m_type ( type )
2024-07-09 15:54:22 -06:00
, m_name ( move ( name ) )
2023-11-08 11:47:41 -07:00
{
}
2025-05-18 14:10:01 -06:00
WorkerHost : : ~ WorkerHost ( ) = default ;
2023-11-08 11:47:41 -07:00
// https://html.spec.whatwg.org/multipage/workers.html#run-a-worker
2025-07-17 09:51:04 -04:00
void WorkerHost : : run ( GC : : Ref < Web : : Page > page , Web : : HTML : : TransferDataEncoder message_port_data , Web : : HTML : : SerializedEnvironmentSettingsObject const & outside_settings_snapshot , Web : : Bindings : : RequestCredentials credentials , bool is_shared )
2023-11-08 11:47:41 -07:00
{
2024-07-04 11:06:54 -04:00
// 3. Let unsafeWorkerCreationTime be the unsafe shared current time.
auto unsafe_worker_creation_time = Web : : HighResolutionTime : : unsafe_shared_current_time ( ) ;
2025-11-27 11:37:53 +00:00
// 5. Let realm execution context be the result of creating a new realm given agent and the following customizations:
2023-11-08 11:47:41 -07:00
auto realm_execution_context = Web : : Bindings : : create_a_new_javascript_realm (
2023-11-22 09:53:14 -07:00
Web : : Bindings : : main_thread_vm ( ) ,
2025-05-18 14:10:01 -06:00
[ page , is_shared ] ( JS : : Realm & realm ) - > JS : : Object * {
2025-11-27 11:37:53 +00:00
// For the global object, if is shared is true, create a new SharedWorkerGlobalScope object.
2023-11-08 11:47:41 -07:00
if ( is_shared )
2025-05-18 14:10:01 -06:00
return Web : : Bindings : : main_thread_vm ( ) . heap ( ) . allocate < Web : : HTML : : SharedWorkerGlobalScope > ( realm , page ) ;
2025-11-27 11:37:53 +00:00
// Otherwise, create a new DedicatedWorkerGlobalScope object.
2024-11-14 06:13:46 +13:00
return Web : : Bindings : : main_thread_vm ( ) . heap ( ) . allocate < Web : : HTML : : DedicatedWorkerGlobalScope > ( realm , page ) ;
2023-11-08 11:47:41 -07:00
} ,
nullptr ) ;
2025-11-27 11:37:53 +00:00
// 6. Let worker global scope be the global object of realm execution context's Realm component.
2023-11-08 11:47:41 -07:00
// NOTE: This is the DedicatedWorkerGlobalScope or SharedWorkerGlobalScope object created in the previous step.
2025-01-21 09:12:05 -05:00
GC : : Ref < Web : : HTML : : WorkerGlobalScope > worker_global_scope = as < Web : : HTML : : WorkerGlobalScope > ( realm_execution_context - > realm - > global_object ( ) ) ;
2023-11-08 11:47:41 -07:00
2025-11-27 11:37:53 +00:00
// 7. Set up a worker environment settings object with realm execution context, outside settings, and
// unsafeWorkerCreationTime, and let inside settings be the result.
2025-03-12 12:16:18 +00:00
auto inside_settings = Web : : HTML : : WorkerEnvironmentSettingsObject : : setup ( page , move ( realm_execution_context ) , outside_settings_snapshot , unsafe_worker_creation_time ) ;
2023-11-08 11:47:41 -07:00
2025-11-27 11:37:53 +00:00
// AD-HOC: Create a console object for the worker.
2025-03-12 12:16:18 +00:00
auto & console_object = * inside_settings - > realm ( ) . intrinsics ( ) . console_object ( ) ;
2024-11-14 06:13:46 +13:00
m_console = console_object . heap ( ) . allocate < Web : : HTML : : WorkerDebugConsoleClient > ( console_object . console ( ) ) ;
2023-11-08 11:47:41 -07:00
VERIFY ( m_console ) ;
console_object . console ( ) . set_client ( * m_console ) ;
2025-11-27 11:37:53 +00:00
// 8. Set worker global scope's name to options["name"].
2025-05-18 14:10:01 -06:00
worker_global_scope - > set_name ( m_name ) ;
2023-11-08 11:47:41 -07:00
2025-11-27 11:37:53 +00:00
// 9. Append owner to worker global scope's owner set.
2023-11-08 11:47:41 -07:00
// FIXME: support for 'owner' set on WorkerGlobalScope
2024-03-05 09:42:10 -07:00
// IMPLEMENTATION DEFINED: We need an object to represent the fetch response's client
2025-03-12 12:16:18 +00:00
auto outside_settings = inside_settings - > realm ( ) . create < Web : : HTML : : EnvironmentSettingsSnapshot > ( inside_settings - > realm ( ) , inside_settings - > realm_execution_context ( ) . copy ( ) , outside_settings_snapshot ) ;
2024-03-05 09:42:10 -07:00
2025-11-27 11:37:53 +00:00
// 10. If is shared is true, then:
2023-11-08 11:47:41 -07:00
if ( is_shared ) {
2025-05-18 14:10:01 -06:00
auto & shared_global_scope = static_cast < Web : : HTML : : SharedWorkerGlobalScope & > ( * worker_global_scope ) ;
2023-11-08 11:47:41 -07:00
// 1. Set worker global scope's constructor origin to outside settings's origin.
2025-05-18 14:10:01 -06:00
shared_global_scope . set_constructor_origin ( outside_settings - > origin ( ) ) ;
2025-11-27 11:37:53 +00:00
// 2. Set worker global scope's constructor URL to url.
2025-05-18 14:10:01 -06:00
shared_global_scope . set_constructor_url ( m_url ) ;
2025-11-27 11:37:53 +00:00
// 3. Set worker global scope's type to options["type"].
2025-05-18 14:10:01 -06:00
shared_global_scope . set_type ( m_type ) ;
2025-11-27 11:37:53 +00:00
// 4. Set worker global scope's credentials to options["credentials"].
2025-05-18 14:10:01 -06:00
shared_global_scope . set_credentials ( Web : : Fetch : : from_bindings_enum ( credentials ) ) ;
2023-11-08 11:47:41 -07:00
}
2025-11-27 11:37:53 +00:00
// 11. Let destination be "sharedworker" if is shared is true, and "worker" otherwise.
2023-11-08 11:47:41 -07:00
auto destination = is_shared ? Web : : Fetch : : Infrastructure : : Request : : Destination : : SharedWorker
: Web : : Fetch : : Infrastructure : : Request : : Destination : : Worker ;
2025-06-24 11:18:14 +01:00
// In both cases, let performFetch be the following perform the fetch hook given request, isTopLevel, and processCustomFetchResponse:
2025-05-18 14:10:01 -06:00
auto perform_fetch_function = [ inside_settings , worker_global_scope , is_shared ] ( GC : : Ref < Web : : Fetch : : Infrastructure : : Request > request , Web : : HTML : : TopLevelModule is_top_level , Web : : Fetch : : Infrastructure : : FetchAlgorithms : : ProcessResponseConsumeBodyFunction process_custom_fetch_response ) - > Web : : WebIDL : : ExceptionOr < void > {
2025-03-12 12:16:18 +00:00
auto & realm = inside_settings - > realm ( ) ;
2023-11-08 11:47:41 -07:00
auto & vm = realm . vm ( ) ;
Web : : Fetch : : Infrastructure : : FetchAlgorithms : : Input fetch_algorithms_input { } ;
2025-11-27 11:37:53 +00:00
// 1. If isTopLevel is false, fetch request with processResponseConsumeBody set to processCustomFetchResponse,
// and abort these steps.
2024-03-05 09:48:10 -07:00
if ( is_top_level = = Web : : HTML : : TopLevelModule : : No ) {
2023-11-08 11:47:41 -07:00
fetch_algorithms_input . process_response_consume_body = move ( process_custom_fetch_response ) ;
2025-09-30 17:15:55 +01:00
Web : : Fetch : : Fetching : : fetch ( realm , request , Web : : Fetch : : Infrastructure : : FetchAlgorithms : : create ( vm , move ( fetch_algorithms_input ) ) ) ;
2023-11-08 11:47:41 -07:00
return { } ;
}
// 2. Set request's reserved client to inside settings.
2025-03-12 12:16:18 +00:00
request - > set_reserved_client ( GC : : Ptr < Web : : HTML : : EnvironmentSettingsObject > ( inside_settings ) ) ;
2023-11-08 11:47:41 -07:00
2025-11-27 11:37:53 +00:00
// NB: We need to store the process custom fetch response function on the heap here, because we're storing it
// in another heap function
2024-11-15 04:01:23 +13:00
auto process_custom_fetch_response_function = GC : : create_function ( vm . heap ( ) , move ( process_custom_fetch_response ) ) ;
2023-11-08 11:47:41 -07:00
2025-11-27 11:37:53 +00:00
// 3. Fetch request with processResponseConsumeBody set to the following steps given response response and
// null, failure, or a byte sequence bodyBytes:
2025-05-18 14:10:01 -06:00
fetch_algorithms_input . process_response_consume_body = [ worker_global_scope , process_custom_fetch_response_function , inside_settings , is_shared ] ( auto response , auto body_bytes ) {
2025-03-12 12:16:18 +00:00
auto & vm = inside_settings - > vm ( ) ;
2024-11-25 17:01:26 +00:00
2023-11-08 11:47:41 -07:00
// 1. Set worker global scope's url to response's url.
worker_global_scope - > set_url ( response - > url ( ) . value_or ( { } ) ) ;
2025-11-27 11:37:53 +00:00
// 2. Initialize worker global scope's policy container given worker global scope, response, and inside
// settings.
2025-03-12 12:16:18 +00:00
worker_global_scope - > initialize_policy_container ( response , inside_settings ) ;
2024-11-25 17:01:26 +00:00
2025-11-27 11:37:53 +00:00
// 3. If the Run CSP initialization for a global object algorithm returns "Blocked" when executed upon
// worker global scope, set response to a network error. [CSP]
2024-11-25 17:01:26 +00:00
if ( worker_global_scope - > run_csp_initialization ( ) = = Web : : ContentSecurityPolicy : : Directives : : Directive : : Result : : Blocked ) {
2025-04-02 20:51:45 +13:00
response = Web : : Fetch : : Infrastructure : : Response : : network_error ( vm , " Blocked by Content Security Policy " _string ) ;
2024-11-25 17:01:26 +00:00
}
2025-05-18 14:10:01 -06:00
// FIXME: Use worker global scope's policy container's embedder policy
2023-11-08 11:47:41 -07:00
// FIXME: 4. If worker global scope's embedder policy's value is compatible with cross-origin isolation and is shared is true,
// then set agent's agent cluster's cross-origin isolation mode to "logical" or "concrete".
// The one chosen is implementation-defined.
// FIXME: 5. If the result of checking a global object's embedder policy with worker global scope, outside settings,
// and response is false, then set response to a network error.
// FIXME: 6. Set worker global scope's cross-origin isolated capability to true if agent's agent cluster's cross-origin
// isolation mode is "concrete".
if ( ! is_shared ) {
2025-11-27 11:37:53 +00:00
// FIXME: 7. If is shared is false and owner's cross-origin isolated capability is false, then set worker
2023-11-08 11:47:41 -07:00
// global scope's cross-origin isolated capability to false.
2025-11-27 11:37:53 +00:00
// FIXME: 8. If is shared is false and response's url's scheme is "data", then set worker global scope's
2023-11-08 11:47:41 -07:00
// cross-origin isolated capability to false.
}
// 9. Run processCustomFetchResponse with response and bodyBytes.
process_custom_fetch_response_function - > function ( ) ( response , body_bytes ) ;
} ;
2025-09-30 17:15:55 +01:00
Web : : Fetch : : Fetching : : fetch ( realm , request , Web : : Fetch : : Infrastructure : : FetchAlgorithms : : create ( vm , move ( fetch_algorithms_input ) ) ) ;
2023-11-08 11:47:41 -07:00
return { } ;
} ;
2025-03-12 12:16:18 +00:00
auto perform_fetch = Web : : HTML : : create_perform_the_fetch_hook ( inside_settings - > heap ( ) , move ( perform_fetch_function ) ) ;
2023-11-08 11:47:41 -07:00
2025-11-27 11:37:53 +00:00
// In both cases, let onComplete given script be the following steps:
2025-05-18 14:10:01 -06:00
auto on_complete_function = [ inside_settings , worker_global_scope , message_port_data = move ( message_port_data ) , url = m_url , is_shared ] ( GC : : Ptr < Web : : HTML : : Script > script ) mutable {
2025-03-12 12:16:18 +00:00
auto & realm = inside_settings - > realm ( ) ;
2025-07-17 09:51:04 -04:00
2023-11-08 11:47:41 -07:00
// 1. If script is null or if script's error to rethrow is non-null, then:
if ( ! script | | ! script - > error_to_rethrow ( ) . is_null ( ) ) {
// FIXME: 1. Queue a global task on the DOM manipulation task source given worker's relevant global object to fire an event named error at worker.
// FIXME: Notify Worker parent through IPC to fire an error event at Worker
2025-03-12 12:07:25 +00:00
// 2. Run the environment discarding steps for inside settings.
inside_settings - > discard_environment ( ) ;
2023-11-08 11:47:41 -07:00
// 3. Abort these steps.
2023-12-04 15:24:18 -07:00
dbgln ( " DedicatedWorkerHost: Unable to fetch script {} because {} " , url , script ? script - > error_to_rethrow ( ) . to_string_without_side_effects ( ) : " script was null " _string ) ;
2023-11-08 11:47:41 -07:00
return ;
}
// FIXME: 2. Associate worker with worker global scope.
// What does this even mean?
2025-11-27 11:37:53 +00:00
// 3. Let inside port be a new MessagePort object in inside settings's realm.
2023-12-20 13:47:01 -07:00
auto inside_port = Web : : HTML : : MessagePort : : create ( realm ) ;
2025-11-27 11:37:53 +00:00
// 4. If is shared is false, then:
if ( ! is_shared ) {
// FIXME: 1. Set inside port's message event target to worker global scope.
// 2. Set worker global scope's inside port to inside port.
worker_global_scope - > set_internal_port ( inside_port ) ;
}
2023-12-20 13:47:01 -07:00
// 5. Entangle outside port and inside port.
2025-07-17 09:51:04 -04:00
Web : : HTML : : TransferDataDecoder decoder { move ( message_port_data ) } ;
MUST ( inside_port - > transfer_receiving_steps ( decoder ) ) ;
2023-11-08 11:47:41 -07:00
// 6. Create a new WorkerLocation object and associate it with worker global scope.
2024-11-14 05:50:17 +13:00
worker_global_scope - > set_location ( realm . create < Web : : HTML : : WorkerLocation > ( * worker_global_scope ) ) ;
2023-11-08 11:47:41 -07:00
// FIXME: 7. Closing orphan workers: Start monitoring the worker such that no sooner than it
// stops being a protected worker, and no later than it stops being a permissible worker,
// worker global scope's closing flag is set to true.
// FIXME: 8. Suspending workers: Start monitoring the worker, such that whenever worker global scope's
// closing flag is false and the worker is a suspendable worker, the user agent suspends
// execution of script in that worker until such time as either the closing flag switches to
// true or the worker stops being a suspendable worker
// 9. Set inside settings's execution ready flag.
2025-03-12 12:16:18 +00:00
inside_settings - > execution_ready = true ;
2023-11-08 11:47:41 -07:00
// 10. If script is a classic script, then run the classic script script.
// Otherwise, it is a module script; run the module script script.
2025-11-27 11:37:53 +00:00
if ( auto * classic_script = as_if < Web : : HTML : : ClassicScript > ( * script ) )
( void ) classic_script - > run ( ) ;
2023-11-08 11:47:41 -07:00
else
2025-01-21 09:12:05 -05:00
( void ) as < Web : : HTML : : JavaScriptModuleScript > ( * script ) . run ( ) ;
2023-11-08 11:47:41 -07:00
// FIXME: 11. Enable outside port's port message queue.
2023-12-20 13:47:01 -07:00
// 12. If is shared is false, enable the port message queue of the worker's implicit port.
if ( ! is_shared ) {
2025-06-07 23:57:17 +02:00
inside_port - > enable ( ) ;
2023-12-20 13:47:01 -07:00
}
2023-11-08 11:47:41 -07:00
2025-06-24 11:18:14 +01:00
// 13. If is shared is true, then queue a global task on the DOM manipulation task source given worker
2023-11-08 11:47:41 -07:00
// global scope to fire an event named connect at worker global scope, using MessageEvent,
// with the data attribute initialized to the empty string, the ports attribute initialized
// to a new frozen array containing inside port, and the source attribute initialized to inside port.
2025-05-18 14:10:01 -06:00
if ( is_shared ) {
Web : : HTML : : queue_global_task ( Web : : HTML : : Task : : Source : : DOMManipulation , * worker_global_scope , GC : : create_function ( realm . heap ( ) , [ worker_global_scope , inside_port ] {
auto & realm = worker_global_scope - > realm ( ) ;
auto & vm = realm . vm ( ) ;
Web : : HTML : : TemporaryExecutionContext const context ( realm ) ;
Web : : HTML : : MessageEventInit event_init { } ;
event_init . data = GC : : Ref { vm . empty_string ( ) } ;
event_init . ports = { inside_port } ;
event_init . source = inside_port ;
auto message_event = Web : : HTML : : MessageEvent : : create ( realm , Web : : HTML : : EventNames : : connect , event_init ) ;
worker_global_scope - > dispatch_event ( message_event ) ;
} ) ) ;
}
2023-11-08 11:47:41 -07:00
// FIXME: 14. Enable the client message queue of the ServiceWorkerContainer object whose associated service
// worker client is worker global scope's relevant settings object.
// 15. Event loop: Run the responsible event loop specified by inside settings until it is destroyed.
2025-03-12 12:16:18 +00:00
inside_settings - > responsible_event_loop ( ) . schedule ( ) ;
2023-11-08 11:47:41 -07:00
// FIXME: We need to react to the closing flag being set on the responsible event loop
// And use that to shutdown the WorkerHost
// FIXME: 16. Clear the worker global scope's map of active timers.
// FIXME: 17. Disentangle all the ports in the list of the worker's ports.
// FIXME: 18. Empty worker global scope's owner set.
} ;
2025-03-12 12:16:18 +00:00
auto on_complete = Web : : HTML : : create_on_fetch_script_complete ( inside_settings - > vm ( ) . heap ( ) , move ( on_complete_function ) ) ;
2023-11-08 11:47:41 -07:00
2025-05-24 21:27:34 +12:00
// AD-HOC: Fetching a script performs actions such as for blobs checking that they are on the same partition
// based on origin. However, this is performed before the consume body algorithm is run, where
// this URL for that worker is set. As a workaround, set the URL upfront.
worker_global_scope - > set_url ( m_url ) ;
2025-11-27 11:37:53 +00:00
// 12. Obtain script by switching on the value of options's type member:
2024-09-10 20:57:48 -06:00
if ( m_type = = Web : : Bindings : : WorkerType : : Classic ) {
2025-11-27 11:37:53 +00:00
// -> "classic":
// Fetch a classic worker script given url, outside settings, destination, inside settings, and with
// onComplete and performFetch as defined below.
2025-03-12 12:16:18 +00:00
if ( auto err = Web : : HTML : : fetch_classic_worker_script ( m_url , outside_settings , destination , inside_settings , perform_fetch , on_complete ) ; err . is_error ( ) ) {
2024-03-05 09:48:10 -07:00
dbgln ( " Failed to run worker script " ) ;
// FIXME: Abort the worker properly
TODO ( ) ;
}
} else {
2025-11-27 11:37:53 +00:00
// -> "module":
// Fetch a module worker script graph given url, outside settings, destination, the value of the credentials
// member of options, inside settings, and with onComplete and performFetch as defined below.
2024-09-10 20:57:48 -06:00
VERIFY ( m_type = = Web : : Bindings : : WorkerType : : Module ) ;
2024-03-05 09:48:10 -07:00
// FIXME: Pass credentials
2025-03-12 12:16:18 +00:00
if ( auto err = Web : : HTML : : fetch_module_worker_script_graph ( m_url , outside_settings , destination , inside_settings , perform_fetch , on_complete ) ; err . is_error ( ) ) {
2024-03-05 09:48:10 -07:00
dbgln ( " Failed to run worker script " ) ;
// FIXME: Abort the worker properly
TODO ( ) ;
}
2023-11-08 11:47:41 -07:00
}
}
}