DedicatedWorkerHost.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/ConsoleObject.h>
  7. #include <LibWeb/Fetch/Fetching/Fetching.h>
  8. #include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
  9. #include <LibWeb/HTML/Scripting/ClassicScript.h>
  10. #include <LibWeb/HTML/Scripting/Fetching.h>
  11. #include <LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.h>
  12. #include <LibWeb/HTML/WorkerDebugConsoleClient.h>
  13. #include <LibWeb/HTML/WorkerGlobalScope.h>
  14. #include <LibWeb/Loader/ResourceLoader.h>
  15. #include <WebWorker/DedicatedWorkerHost.h>
  16. namespace WebWorker {
  17. DedicatedWorkerHost::DedicatedWorkerHost(Web::Page& page, AK::URL url, String type)
  18. : m_page(page)
  19. , m_url(move(url))
  20. , m_type(move(type))
  21. {
  22. }
  23. DedicatedWorkerHost::~DedicatedWorkerHost() = default;
  24. // https://html.spec.whatwg.org/multipage/workers.html#run-a-worker
  25. // FIXME: Extract out into a helper for both shared and dedicated workers
  26. void DedicatedWorkerHost::run()
  27. {
  28. bool const is_shared = false;
  29. // 7. Let realm execution context be the result of creating a new JavaScript realm given agent and the following customizations:
  30. auto realm_execution_context = Web::Bindings::create_a_new_javascript_realm(
  31. Web::Bindings::main_thread_vm(),
  32. [this](JS::Realm& realm) -> JS::Object* {
  33. // 7a. For the global object, if is shared is true, create a new SharedWorkerGlobalScope object.
  34. // 7b. Otherwise, create a new DedicatedWorkerGlobalScope object.
  35. // FIXME: Proper support for both SharedWorkerGlobalScope and DedicatedWorkerGlobalScope
  36. if (is_shared)
  37. TODO();
  38. return Web::Bindings::main_thread_vm().heap().allocate_without_realm<Web::HTML::WorkerGlobalScope>(realm, m_page);
  39. },
  40. nullptr);
  41. // 8. Let worker global scope be the global object of realm execution context's Realm component.
  42. // NOTE: This is the DedicatedWorkerGlobalScope or SharedWorkerGlobalScope object created in the previous step.
  43. JS::NonnullGCPtr<Web::HTML::WorkerGlobalScope> worker_global_scope = verify_cast<Web::HTML::WorkerGlobalScope>(realm_execution_context->realm->global_object());
  44. // 9. Set up a worker environment settings object with realm execution context,
  45. // outside settings, and unsafeWorkerCreationTime, and let inside settings be the result.
  46. auto inner_settings = Web::HTML::WorkerEnvironmentSettingsObject::setup(move(realm_execution_context));
  47. auto& console_object = *inner_settings->realm().intrinsics().console_object();
  48. m_console = adopt_ref(*new Web::HTML::WorkerDebugConsoleClient(console_object.console()));
  49. VERIFY(m_console);
  50. console_object.console().set_client(*m_console);
  51. // 10. Set worker global scope's name to the value of options's name member.
  52. // FIXME: name property requires the SharedWorkerGlobalScope or DedicatedWorkerGlobalScope child class to be used
  53. // 11. Append owner to worker global scope's owner set.
  54. // FIXME: support for 'owner' set on WorkerGlobalScope
  55. // 12. If is shared is true, then:
  56. if (is_shared) {
  57. // FIXME: Shared worker support
  58. // 1. Set worker global scope's constructor origin to outside settings's origin.
  59. // 2. Set worker global scope's constructor url to url.
  60. // 3. Set worker global scope's type to the value of options's type member.
  61. // 4. Set worker global scope's credentials to the value of options's credentials member.
  62. }
  63. // 13. Let destination be "sharedworker" if is shared is true, and "worker" otherwise.
  64. auto destination = is_shared ? Web::Fetch::Infrastructure::Request::Destination::SharedWorker
  65. : Web::Fetch::Infrastructure::Request::Destination::Worker;
  66. // In both cases, let performFetch be the following perform the fetch hook given request, isTopLevel and processCustomFetchResponse:
  67. auto perform_fetch_function = [inner_settings, worker_global_scope](JS::NonnullGCPtr<Web::Fetch::Infrastructure::Request> request, Web::HTML::IsTopLevel is_top_level, Web::Fetch::Infrastructure::FetchAlgorithms::ProcessResponseConsumeBodyFunction process_custom_fetch_response) -> Web::WebIDL::ExceptionOr<void> {
  68. auto& realm = inner_settings->realm();
  69. auto& vm = realm.vm();
  70. Web::Fetch::Infrastructure::FetchAlgorithms::Input fetch_algorithms_input {};
  71. // 1. If isTopLevel is false, fetch request with processResponseConsumeBody set to processCustomFetchResponse, and abort these steps.
  72. if (is_top_level == Web::HTML::IsTopLevel::No) {
  73. fetch_algorithms_input.process_response_consume_body = move(process_custom_fetch_response);
  74. TRY(Web::Fetch::Fetching::fetch(realm, request, Web::Fetch::Infrastructure::FetchAlgorithms::create(vm, move(fetch_algorithms_input))));
  75. return {};
  76. }
  77. // 2. Set request's reserved client to inside settings.
  78. request->set_reserved_client(JS::GCPtr<Web::HTML::EnvironmentSettingsObject>(inner_settings));
  79. // We need to store the process custom fetch response function on the heap here, because we're storing it in another heap function
  80. auto process_custom_fetch_response_function = JS::create_heap_function(vm.heap(), move(process_custom_fetch_response));
  81. // 3. Fetch request with processResponseConsumeBody set to the following steps given response response and null, failure, or a byte sequence bodyBytes:
  82. fetch_algorithms_input.process_response_consume_body = [worker_global_scope, process_custom_fetch_response_function](auto response, auto body_bytes) {
  83. // 1. Set worker global scope's url to response's url.
  84. worker_global_scope->set_url(response->url().value_or({}));
  85. // FIXME: 2. Initialize worker global scope's policy container given worker global scope, response, and inside settings.
  86. // FIXME: 3. If the Run CSP initialization for a global object algorithm returns "Blocked" when executed upon worker
  87. // global scope, set response to a network error. [CSP]
  88. // FIXME: 4. If worker global scope's embedder policy's value is compatible with cross-origin isolation and is shared is true,
  89. // then set agent's agent cluster's cross-origin isolation mode to "logical" or "concrete".
  90. // The one chosen is implementation-defined.
  91. // FIXME: 5. If the result of checking a global object's embedder policy with worker global scope, outside settings,
  92. // and response is false, then set response to a network error.
  93. // FIXME: 6. Set worker global scope's cross-origin isolated capability to true if agent's agent cluster's cross-origin
  94. // isolation mode is "concrete".
  95. if (!is_shared) {
  96. // 7. If is shared is false and owner's cross-origin isolated capability is false, then set worker
  97. // global scope's cross-origin isolated capability to false.
  98. // 8. If is shared is false and response's url's scheme is "data", then set worker global scope's
  99. // cross-origin isolated capability to false.
  100. }
  101. // 9. Run processCustomFetchResponse with response and bodyBytes.
  102. process_custom_fetch_response_function->function()(response, body_bytes);
  103. };
  104. TRY(Web::Fetch::Fetching::fetch(realm, request, Web::Fetch::Infrastructure::FetchAlgorithms::create(vm, move(fetch_algorithms_input))));
  105. return {};
  106. };
  107. auto perform_fetch = Web::HTML::create_perform_the_fetch_hook(inner_settings->heap(), move(perform_fetch_function));
  108. auto on_complete_function = [inner_settings, worker_global_scope](JS::GCPtr<Web::HTML::Script> script) {
  109. auto& realm = inner_settings->realm();
  110. // 1. If script is null or if script's error to rethrow is non-null, then:
  111. if (!script || !script->error_to_rethrow().is_null()) {
  112. // 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.
  113. // FIXME: Notify Worker parent through IPC to fire an error event at Worker
  114. // FIXME 2. Run the environment discarding steps for inside settings.
  115. // 3. Abort these steps.
  116. dbgln("Didn't get my script :(");
  117. return;
  118. }
  119. // FIXME: 2. Associate worker with worker global scope.
  120. // What does this even mean?
  121. // FIXME: 3. Let inside port be a new MessagePort object in inside settings's Realm.
  122. // FIXME: 4. Associate inside port with worker global scope.
  123. // FIXME: 5. Entangle outside port and inside port.
  124. // 6. Create a new WorkerLocation object and associate it with worker global scope.
  125. worker_global_scope->set_location(realm.heap().allocate<Web::HTML::WorkerLocation>(realm, *worker_global_scope));
  126. // FIXME: 7. Closing orphan workers: Start monitoring the worker such that no sooner than it
  127. // stops being a protected worker, and no later than it stops being a permissible worker,
  128. // worker global scope's closing flag is set to true.
  129. // FIXME: 8. Suspending workers: Start monitoring the worker, such that whenever worker global scope's
  130. // closing flag is false and the worker is a suspendable worker, the user agent suspends
  131. // execution of script in that worker until such time as either the closing flag switches to
  132. // true or the worker stops being a suspendable worker
  133. // 9. Set inside settings's execution ready flag.
  134. inner_settings->execution_ready = true;
  135. // 10. If script is a classic script, then run the classic script script.
  136. // Otherwise, it is a module script; run the module script script.
  137. if (is<Web::HTML::ClassicScript>(*script))
  138. (void)static_cast<Web::HTML::ClassicScript&>(*script).run();
  139. else
  140. (void)verify_cast<Web::HTML::JavaScriptModuleScript>(*script).run();
  141. // FIXME: 11. Enable outside port's port message queue.
  142. // FIXME: 12. If is shared is false, enable the port message queue of the worker's implicit port.
  143. // FIXME: 13. If is shared is true, then queue a global task on DOM manipulation task source given worker
  144. // global scope to fire an event named connect at worker global scope, using MessageEvent,
  145. // with the data attribute initialized to the empty string, the ports attribute initialized
  146. // to a new frozen array containing inside port, and the source attribute initialized to inside port.
  147. // FIXME: 14. Enable the client message queue of the ServiceWorkerContainer object whose associated service
  148. // worker client is worker global scope's relevant settings object.
  149. // 15. Event loop: Run the responsible event loop specified by inside settings until it is destroyed.
  150. inner_settings->responsible_event_loop().schedule();
  151. // FIXME: We need to react to the closing flag being set on the responsible event loop
  152. // And use that to shutdown the WorkerHost
  153. // FIXME: 16. Clear the worker global scope's map of active timers.
  154. // FIXME: 17. Disentangle all the ports in the list of the worker's ports.
  155. // FIXME: 18. Empty worker global scope's owner set.
  156. };
  157. auto on_complete = Web::HTML::create_on_fetch_script_complete(inner_settings->vm().heap(), move(on_complete_function));
  158. // 14. Obtain script by switching on the value of options's type member:
  159. // classic: Fetch a classic worker script given url, outside settings, destination, inside settings,
  160. // and with onComplete and performFetch as defined below.
  161. // module: Fetch a module worker script graph given url, outside settings, destination, the value of the credentials member of options, inside settings,
  162. // and with onComplete and performFetch as defined below.
  163. if (m_type != "classic"sv) {
  164. dbgln("Unsupported script type {} for LibWeb/Worker", m_type);
  165. TODO();
  166. }
  167. // FIXME: We don't have outside settings anymore, they live in the owner. https://github.com/whatwg/html/issues/9920
  168. if (auto err = Web::HTML::fetch_classic_worker_script(m_url, inner_settings, destination, inner_settings, perform_fetch, on_complete); err.is_error()) {
  169. dbgln("Failed to run worker script");
  170. // FIXME: Abort the worker properly
  171. TODO();
  172. }
  173. }
  174. }