mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-16 21:20:18 +00:00
This was almost a no-op, except we intern JS exception messages. So the bulk of this patch is porting exception messages to UTF-16.
184 lines
8.5 KiB
C++
184 lines
8.5 KiB
C++
/*
|
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibWeb/Bindings/SharedWorkerPrototype.h>
|
|
#include <LibWeb/DOM/Document.h>
|
|
#include <LibWeb/DOM/Event.h>
|
|
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
|
#include <LibWeb/HTML/EventNames.h>
|
|
#include <LibWeb/HTML/MessageEvent.h>
|
|
#include <LibWeb/HTML/MessagePort.h>
|
|
#include <LibWeb/HTML/Scripting/Environments.h>
|
|
#include <LibWeb/HTML/SharedWorker.h>
|
|
#include <LibWeb/HTML/SharedWorkerGlobalScope.h>
|
|
#include <LibWeb/HTML/Window.h>
|
|
#include <LibWeb/HTML/Worker.h>
|
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
|
|
|
namespace Web::HTML {
|
|
|
|
GC_DEFINE_ALLOCATOR(SharedWorker);
|
|
|
|
// https://html.spec.whatwg.org/multipage/workers.html#dom-sharedworker
|
|
WebIDL::ExceptionOr<GC::Ref<SharedWorker>> SharedWorker::construct_impl(JS::Realm& realm, String const& script_url, Variant<String, WorkerOptions>& options_value)
|
|
{
|
|
// FIXME: 1. Let compliantScriptURL be the result of invoking the Get Trusted Type compliant string algorithm with
|
|
// TrustedScriptURL, this's relevant global object, scriptURL, "SharedWorker constructor", and "script".
|
|
auto const& compliant_script_url = script_url;
|
|
|
|
// 2. If options is a DOMString, set options to a new WorkerOptions dictionary whose name member is set to the value
|
|
// of options and whose other members are set to their default values.
|
|
auto options = options_value.visit(
|
|
[&](String& options) {
|
|
return WorkerOptions { .name = move(options) };
|
|
},
|
|
[&](WorkerOptions& options) {
|
|
return move(options);
|
|
});
|
|
|
|
// 3. Let outside settings be the current settings object.
|
|
auto& outside_settings = current_principal_settings_object();
|
|
|
|
// 4. Let urlRecord be the result of encoding-parsing a URL given compliantScriptURL, relative to outside settings.
|
|
auto url = outside_settings.encoding_parse_url(compliant_script_url);
|
|
|
|
// 5. If urlRecord is failure, then throw a "SyntaxError" DOMException.
|
|
if (!url.has_value())
|
|
return WebIDL::SyntaxError::create(realm, "SharedWorker constructed with invalid URL"_utf16);
|
|
|
|
// 7. Let outside port be a new MessagePort in outside settings's realm.
|
|
// NOTE: We do this first so that we can store the port as a GC::Ref.
|
|
auto outside_port = MessagePort::create(outside_settings.realm());
|
|
|
|
// 6. Let worker be a new SharedWorker object.
|
|
// 8. Assign outside port to the port attribute of worker.
|
|
auto worker = realm.create<SharedWorker>(realm, url.release_value(), options, outside_port);
|
|
|
|
// 9. Let callerIsSecureContext be true if outside settings is a secure context; otherwise, false.
|
|
auto caller_is_secure_context = HTML::is_secure_context(outside_settings);
|
|
|
|
// 10. Let outside storage key be the result of running obtain a storage key for non-storage purposes given outside settings.
|
|
auto outside_storage_key = StorageAPI::obtain_a_storage_key_for_non_storage_purposes(outside_settings);
|
|
|
|
// 11. Enqueue the following steps to the shared worker manager:
|
|
// FIXME: "A user agent has an associated shared worker manager which is the result of starting a new parallel queue."
|
|
// We just use the singular global event loop for now.
|
|
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [worker, outside_port, &outside_settings, caller_is_secure_context, outside_storage_key = move(outside_storage_key)]() mutable {
|
|
// 1. Let worker global scope be null.
|
|
GC::Ptr<SharedWorkerGlobalScope> worker_global_scope;
|
|
|
|
// 2. For each scope in the list of all SharedWorkerGlobalScope objects:
|
|
for (auto& scope : all_shared_worker_global_scopes()) {
|
|
// 1. Let worker storage key be the result of running obtain a storage key for non-storage purposes given
|
|
// scope's relevant settings object.
|
|
auto worker_storage_key = StorageAPI::obtain_a_storage_key_for_non_storage_purposes(HTML::relevant_settings_object(scope));
|
|
|
|
// 2. If all of the following are true:
|
|
if (
|
|
// * worker storage key equals outside storage key;
|
|
worker_storage_key == outside_storage_key
|
|
|
|
// * scope's closing flag is false;
|
|
&& !scope->is_closing()
|
|
|
|
// * scope's constructor url equals urlRecord; and
|
|
&& scope->url() == worker->m_script_url
|
|
|
|
// * scope's name equals the value of options's name member,
|
|
&& scope->name() == worker->m_options.name)
|
|
// then:
|
|
{
|
|
// 1. Set worker global scope to scope.
|
|
worker_global_scope = scope;
|
|
|
|
// 2. Break.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// FIXME: 3. If worker global scope is not null, but the user agent has been configured to disallow communication
|
|
// between the worker represented by the worker global scope and the scripts whose settings object is outside
|
|
// settings, then set worker global scope to null.
|
|
// FIXME: 4. If worker global scope is not null, then check if worker global scope's type and credentials match the
|
|
// options values. If not, queue a task to fire an event named error and abort these steps.
|
|
|
|
// 5. If worker global scope is not null, then run these subsubsteps:
|
|
if (worker_global_scope) {
|
|
// 1. Let settings object be the relevant settings object for worker global scope.
|
|
auto& settings_object = HTML::relevant_settings_object(*worker_global_scope);
|
|
|
|
// 2. Let workerIsSecureContext be true if settings object is a secure context; otherwise, false.
|
|
auto worker_is_secure_context = HTML::is_secure_context(settings_object);
|
|
|
|
// 3. If workerIsSecureContext is not callerIsSecureContext, then queue a task to fire an event named error
|
|
// at worker and abort these steps. [SECURE-CONTEXTS]
|
|
if (worker_is_secure_context != caller_is_secure_context) {
|
|
queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr, GC::create_function(worker->heap(), [worker]() {
|
|
worker->dispatch_event(DOM::Event::create(worker->realm(), HTML::EventNames::error));
|
|
}));
|
|
|
|
return;
|
|
}
|
|
|
|
// FIXME: 4. Associate worker with worker global scope.
|
|
|
|
// 5. Let inside port be a new MessagePort in settings object's realm.
|
|
auto inside_port = HTML::MessagePort::create(settings_object.realm());
|
|
|
|
// 6. Entangle outside port and inside port.
|
|
outside_port->entangle_with(inside_port);
|
|
|
|
// 7. Queue a task, using the DOM manipulation task source, 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 only inside port, and the source attribute initialized to
|
|
// inside port.
|
|
queue_a_task(HTML::Task::Source::DOMManipulation, nullptr, nullptr, GC::create_function(worker->heap(), [worker_global_scope, inside_port]() {
|
|
auto& realm = worker_global_scope->realm();
|
|
|
|
MessageEventInit init;
|
|
init.data = JS::PrimitiveString::create(realm.vm(), String {});
|
|
init.ports.append(inside_port);
|
|
init.source = inside_port;
|
|
|
|
worker_global_scope->dispatch_event(MessageEvent::create(realm, HTML::EventNames::connect, init));
|
|
}));
|
|
|
|
// FIXME: 8. Append the relevant owner to add given outside settings to worker global scope's owner set.
|
|
}
|
|
// 6. Otherwise, in parallel, run a worker given worker, urlRecord, outside settings, outside port, and options.
|
|
else {
|
|
run_a_worker(worker, worker->m_script_url, outside_settings, outside_port, worker->m_options);
|
|
}
|
|
}));
|
|
|
|
// 12. Return worker.
|
|
return worker;
|
|
}
|
|
|
|
SharedWorker::SharedWorker(JS::Realm& realm, URL::URL script_url, WorkerOptions options, MessagePort& port)
|
|
: DOM::EventTarget(realm)
|
|
, m_script_url(move(script_url))
|
|
, m_options(move(options))
|
|
, m_port(port)
|
|
{
|
|
}
|
|
|
|
SharedWorker::~SharedWorker() = default;
|
|
|
|
void SharedWorker::initialize(JS::Realm& realm)
|
|
{
|
|
WEB_SET_PROTOTYPE_FOR_INTERFACE(SharedWorker);
|
|
Base::initialize(realm);
|
|
}
|
|
|
|
void SharedWorker::visit_edges(Cell::Visitor& visitor)
|
|
{
|
|
Base::visit_edges(visitor);
|
|
visitor.visit(m_port);
|
|
visitor.visit(m_agent);
|
|
}
|
|
|
|
}
|