Skip to content

Commit 62d7011

Browse files
AtkinsSJawesomekling
authored andcommitted
LibWeb/HTML: Update worker construction spec steps
This is largely editorial. One behaviour change is that events are now sent from a global task on the DOMManipulation task source. Somewhat awkwardly, the spec refers to `this` before the Worker exists. As it's for getting the relevant global object / settings object, I've had to work around that. Corresponds to: whatwg/html@917c2f6
1 parent edac716 commit 62d7011

File tree

6 files changed

+127
-101
lines changed

6 files changed

+127
-101
lines changed

Libraries/LibWeb/HTML/SharedWorker.cpp

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ GC_DEFINE_ALLOCATOR(SharedWorker);
2727
// https://html.spec.whatwg.org/multipage/workers.html#dom-sharedworker
2828
WebIDL::ExceptionOr<GC::Ref<SharedWorker>> SharedWorker::construct_impl(JS::Realm& realm, TrustedTypes::TrustedScriptURLOrString const& script_url, Variant<String, WorkerOptions>& options_value)
2929
{
30-
// 1. Let compliantScriptURL be the result of invoking the Get Trusted Type compliant string algorithm with
30+
// 1. Let compliantScriptURL be the result of invoking the get trusted type compliant string algorithm with
3131
// TrustedScriptURL, this's relevant global object, scriptURL, "SharedWorker constructor", and "script".
3232
auto const compliant_script_url = TRY(get_trusted_type_compliant_string(
3333
TrustedTypes::TrustedTypeName::TrustedScriptURL,
@@ -36,8 +36,8 @@ WebIDL::ExceptionOr<GC::Ref<SharedWorker>> SharedWorker::construct_impl(JS::Real
3636
TrustedTypes::InjectionSink::SharedWorker_constructor,
3737
TrustedTypes::Script.to_string()));
3838

39-
// 2. If options is a DOMString, set options to a new WorkerOptions dictionary whose name member is set to the value
40-
// of options and whose other members are set to their default values.
39+
// 2. If options is a DOMString, set options to a new WorkerOptions dictionary whose name member is set to the
40+
// value of options and whose other members are set to their default values.
4141
auto options = options_value.visit(
4242
[&](String& options) {
4343
return WorkerOptions { .name = move(options) };
@@ -46,122 +46,125 @@ WebIDL::ExceptionOr<GC::Ref<SharedWorker>> SharedWorker::construct_impl(JS::Real
4646
return move(options);
4747
});
4848

49-
// 3. Let outside settings be the current settings object.
49+
// 3. Let outside settings be this's relevant settings object.
50+
// FIXME: We don't have a `this` yet, so use the current principal settings object, as the previous spec did.
5051
auto& outside_settings = current_principal_settings_object();
5152

52-
// 4. Let urlRecord be the result of encoding-parsing a URL given compliantScriptURL, relative to outside settings.
53+
// 4. Let urlRecord be the result of encoding-parsing a URL given compliantScriptURL, relative to outsideSettings.
5354
auto url = outside_settings.encoding_parse_url(compliant_script_url.to_utf8_but_should_be_ported_to_utf16());
5455

5556
// 5. If urlRecord is failure, then throw a "SyntaxError" DOMException.
5657
if (!url.has_value())
5758
return WebIDL::SyntaxError::create(realm, "SharedWorker constructed with invalid URL"_utf16);
5859

59-
// 7. Let outside port be a new MessagePort in outside settings's realm.
60-
// NOTE: We do this first so that we can store the port as a GC::Ref.
60+
// 6. Let outsidePort be a new MessagePort in outsideSettings's realm.
6161
auto outside_port = MessagePort::create(outside_settings.realm());
6262

63-
// 6. Let worker be a new SharedWorker object.
64-
// 8. Assign outside port to the port attribute of worker.
63+
// 10. Let worker be this.
64+
// AD-HOC: We do this first so that we can use `this`.
65+
66+
// 7. Set this's port to outsidePort.
6567
auto worker = realm.create<SharedWorker>(realm, url.release_value(), options, outside_port);
6668

67-
// 9. Let callerIsSecureContext be true if outside settings is a secure context; otherwise, false.
69+
// 8. Let callerIsSecureContext be true if outside settings is a secure context; otherwise, false.
6870
auto caller_is_secure_context = HTML::is_secure_context(outside_settings);
6971

70-
// 10. Let outside storage key be the result of running obtain a storage key for non-storage purposes given outside settings.
72+
// 9. Let outsideStorageKey be the result of running obtain a storage key for non-storage purposes given outsideSettings.
7173
auto outside_storage_key = StorageAPI::obtain_a_storage_key_for_non_storage_purposes(outside_settings);
7274

75+
// 10. Let worker be this.
76+
// NB: This is done earlier.
77+
7378
// 11. Enqueue the following steps to the shared worker manager:
7479
// FIXME: "A user agent has an associated shared worker manager which is the result of starting a new parallel queue."
7580
// We just use the singular global event loop for now.
7681
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 {
77-
// 1. Let worker global scope be null.
82+
// 1. Let workerGlobalScope be null.
7883
GC::Ptr<SharedWorkerGlobalScope> worker_global_scope;
7984

8085
// 2. For each scope in the list of all SharedWorkerGlobalScope objects:
8186
for (auto& scope : all_shared_worker_global_scopes()) {
82-
// 1. Let worker storage key be the result of running obtain a storage key for non-storage purposes given
87+
// 1. Let workerStorageKey be the result of running obtain a storage key for non-storage purposes given
8388
// scope's relevant settings object.
8489
auto worker_storage_key = StorageAPI::obtain_a_storage_key_for_non_storage_purposes(HTML::relevant_settings_object(scope));
8590

8691
// 2. If all of the following are true:
8792
if (
88-
// * worker storage key equals outside storage key;
93+
// * workerStorageKey equals outsideStorageKey;
8994
worker_storage_key == outside_storage_key
9095

9196
// * scope's closing flag is false;
9297
&& !scope->is_closing()
9398

94-
// * scope's constructor url equals urlRecord; and
99+
// * scope's constructor URL equals urlRecord; and
95100
&& scope->url() == worker->m_script_url
96101

97-
// * scope's name equals the value of options's name member,
102+
// * scope's name equals options["name"],
98103
&& scope->name() == worker->m_options.name)
99104
// then:
100105
{
101-
// 1. Set worker global scope to scope.
106+
// 1. Set workerGlobalScope to scope.
102107
worker_global_scope = scope;
103108

104109
// 2. Break.
105110
break;
106111
}
107112
}
108113

109-
// FIXME: 3. If worker global scope is not null, but the user agent has been configured to disallow communication
110-
// between the worker represented by the worker global scope and the scripts whose settings object is outside
111-
// settings, then set worker global scope to null.
112-
// FIXME: 4. If worker global scope is not null, then check if worker global scope's type and credentials match the
113-
// options values. If not, queue a task to fire an event named error and abort these steps.
114+
// FIXME: 3. If workerGlobalScope is not null, but the user agent has been configured to disallow communication between the worker represented by the workerGlobalScope and the scripts whose settings object is outsideSettings, then set workerGlobalScope to null.
115+
// FIXME: 4. If workerGlobalScope is not null, and any of the following are true: ...
114116

115-
// 5. If worker global scope is not null, then run these subsubsteps:
117+
// 5. If workerGlobalScope is not null:
116118
if (worker_global_scope) {
117-
// 1. Let settings object be the relevant settings object for worker global scope.
118-
auto& settings_object = HTML::relevant_settings_object(*worker_global_scope);
119+
// 1. Let insideSettings be workerGlobalScope's relevant settings object.
120+
auto& inside_settings = relevant_settings_object(*worker_global_scope);
119121

120-
// 2. Let workerIsSecureContext be true if settings object is a secure context; otherwise, false.
121-
auto worker_is_secure_context = HTML::is_secure_context(settings_object);
122+
// 2. Let workerIsSecureContext be true if insideSettings is a secure context; otherwise, false.
123+
auto worker_is_secure_context = is_secure_context(inside_settings);
122124

123-
// 3. If workerIsSecureContext is not callerIsSecureContext, then queue a task to fire an event named error
124-
// at worker and abort these steps. [SECURE-CONTEXTS]
125+
// 3. If workerIsSecureContext is not callerIsSecureContext:
125126
if (worker_is_secure_context != caller_is_secure_context) {
126-
queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr, GC::create_function(worker->heap(), [worker]() {
127-
worker->dispatch_event(DOM::Event::create(worker->realm(), HTML::EventNames::error));
127+
// 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.
128+
queue_global_task(Task::Source::DOMManipulation, relevant_global_object(worker), GC::create_function(worker->heap(), [worker]() {
129+
worker->dispatch_event(DOM::Event::create(worker->realm(), EventNames::error));
128130
}));
129131

132+
// 2. Abort these steps.
130133
return;
131134
}
132135

133-
// FIXME: 4. Associate worker with worker global scope.
136+
// FIXME: 4. Associate worker with workerGlobalScope.
134137

135-
// 5. Let inside port be a new MessagePort in settings object's realm.
136-
auto inside_port = HTML::MessagePort::create(settings_object.realm());
138+
// 5. Let insidePort be a new MessagePort in insideSettings's realm.
139+
auto inside_port = HTML::MessagePort::create(inside_settings.realm());
137140

138-
// 6. Entangle outside port and inside port.
141+
// 6. Entangle outsidePort and insidePort.
139142
outside_port->entangle_with(inside_port);
140143

141-
// 7. Queue a task, using the DOM manipulation task source, to fire an event named connect at worker global
142-
// scope, using MessageEvent, with the data attribute initialized to the empty string, the ports attribute
143-
// initialized to a new frozen array containing only inside port, and the source attribute initialized to
144-
// inside port.
145-
queue_a_task(HTML::Task::Source::DOMManipulation, nullptr, nullptr, GC::create_function(worker->heap(), [worker_global_scope, inside_port]() {
144+
// 7. Queue a global task on the DOM manipulation task source given workerGlobalScope to fire an event
145+
// named connect at workerGlobalScope, using MessageEvent, with the data attribute initialized to the
146+
// empty string, the ports attribute initialized to a new frozen array containing only insidePort, and
147+
// the source attribute initialized to insidePort.
148+
queue_global_task(Task::Source::DOMManipulation, *worker_global_scope, GC::create_function(worker->heap(), [worker_global_scope, inside_port]() {
146149
auto& realm = worker_global_scope->realm();
147150

148151
MessageEventInit init;
149152
init.data = JS::PrimitiveString::create(realm.vm(), String {});
150153
init.ports.append(inside_port);
151154
init.source = inside_port;
152155

153-
worker_global_scope->dispatch_event(MessageEvent::create(realm, HTML::EventNames::connect, init));
156+
worker_global_scope->dispatch_event(MessageEvent::create(realm, EventNames::connect, init));
154157
}));
155158

156-
// FIXME: 8. Append the relevant owner to add given outside settings to worker global scope's owner set.
159+
// FIXME: 8. Append the relevant owner to add given outsideSettings to workerGlobalScope's owner set.
160+
157161
}
158-
// 6. Otherwise, in parallel, run a worker given worker, urlRecord, outside settings, outside port, and options.
162+
// 6. Otherwise, in parallel, run a worker given worker, urlRecord, outsideSettings, outsidePort, and options.
159163
else {
160164
run_a_worker(worker, worker->m_script_url, outside_settings, outside_port, worker->m_options);
161165
}
162166
}));
163167

164-
// 12. Return worker.
165168
return worker;
166169
}
167170

Libraries/LibWeb/HTML/SharedWorker.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ class SharedWorker final
2727

2828
virtual ~SharedWorker();
2929

30-
GC::Ref<MessagePort> port() { return m_port; }
30+
// https://html.spec.whatwg.org/multipage/workers.html#dom-sharedworker-port
31+
GC::Ref<MessagePort> port()
32+
{
33+
// The port getter steps are to return this's port.
34+
return m_port;
35+
}
3136

3237
void set_agent(WorkerAgentParent& agent) { m_agent = agent; }
3338

@@ -42,7 +47,11 @@ class SharedWorker final
4247

4348
URL::URL m_script_url;
4449
WorkerOptions m_options;
50+
51+
// Each SharedWorker has a port, a MessagePort set when the object is created.
52+
// https://html.spec.whatwg.org/multipage/workers.html#concept-sharedworker-port
4553
GC::Ref<MessagePort> m_port;
54+
4655
GC::Ptr<WorkerAgentParent> m_agent;
4756
};
4857

Libraries/LibWeb/HTML/Worker.cpp

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ WebIDL::ExceptionOr<GC::Ref<Worker>> Worker::create(TrustedTypes::TrustedScriptU
5555

5656
// 1. Let compliantScriptURL be the result of invoking the Get Trusted Type compliant string algorithm with
5757
// TrustedScriptURL, this's relevant global object, scriptURL, "Worker constructor", and "script".
58+
// FIXME: We don't have a `this` yet, so use the document.
5859
auto const compliant_script_url = TRY(TrustedTypes::get_trusted_type_compliant_string(
5960
TrustedTypes::TrustedTypeName::TrustedScriptURL,
6061
HTML::relevant_global_object(document),
@@ -64,35 +65,39 @@ WebIDL::ExceptionOr<GC::Ref<Worker>> Worker::create(TrustedTypes::TrustedScriptU
6465

6566
dbgln_if(WEB_WORKER_DEBUG, "WebWorker: Creating worker with compliant_script_url = {}", compliant_script_url);
6667

67-
// 2. Let outside settings be the current principal settings object.
68-
auto& outside_settings = current_principal_settings_object();
68+
// 2. Let outsideSettings be this's relevant settings object.
69+
// FIXME: We don't have a `this` yet, so use the document.
70+
auto& outside_settings = relevant_settings_object(document);
6971

70-
// 3. Parse the scriptURL argument relative to outside settings.
71-
auto url = outside_settings.parse_url(compliant_script_url.to_utf8_but_should_be_ported_to_utf16());
72+
// 3. Let workerURL be the result of encoding-parsing a URL given compliantScriptURL, relative to outsideSettings.
73+
auto worker_url = outside_settings.encoding_parse_url(compliant_script_url.to_utf8_but_should_be_ported_to_utf16());
7274

73-
// 4. If this fails, throw a "SyntaxError" DOMException.
74-
if (!url.has_value()) {
75+
// 4. If workerURL is failure, then throw a "SyntaxError" DOMException.
76+
if (!worker_url.has_value()) {
7577
dbgln_if(WEB_WORKER_DEBUG, "WebWorker: Invalid URL loaded '{}'.", compliant_script_url);
7678
return WebIDL::SyntaxError::create(document.realm(), "url is not valid"_utf16);
7779
}
7880

79-
// 5. Let worker URL be the resulting URL record.
81+
// 5. Let outsidePort be a new MessagePort in outsideSettings's realm.
82+
auto outside_port = MessagePort::create(outside_settings.realm());
8083

81-
// 6. Let worker be a new Worker object.
84+
// 8. Let worker be this.
85+
// AD-HOC: AD-HOC: We do this first so that we can use `this`.
8286
auto worker = document.realm().create<Worker>(compliant_script_url.to_utf8_but_should_be_ported_to_utf16(), options, document);
8387

84-
// 7. Let outside port be a new MessagePort in outside settings's Realm.
85-
auto outside_port = MessagePort::create(outside_settings.realm());
88+
// 6. Set outsidePort's message event target to this.
89+
outside_port->set_worker_event_target(worker);
8690

87-
// 8. Associate the outside port with worker
91+
// 7. Set this's outside port to outsidePort.
8892
worker->m_outside_port = outside_port;
89-
worker->m_outside_port->set_worker_event_target(worker);
93+
94+
// 8. Let worker be this.
95+
// NB: This is done earlier.
9096

9197
// 9. Run this step in parallel:
92-
// 1. Run a worker given worker, worker URL, outside settings, outside port, and options.
93-
run_a_worker(worker, url.value(), outside_settings, *outside_port, options);
98+
// 1. Run a worker given worker, workerURL, outsideSettings, outsidePort, and options.
99+
run_a_worker(worker, worker_url.value(), outside_settings, *outside_port, options);
94100

95-
// 10. Return worker
96101
return worker;
97102
}
98103

@@ -107,15 +112,10 @@ void run_a_worker(Variant<GC::Ref<Worker>, GC::Ref<SharedWorker>> worker, URL::U
107112
if (!is<HTML::WindowEnvironmentSettingsObject>(outside_settings))
108113
TODO();
109114

110-
// 3. Let parent worker global scope be null.
111-
// 4. If owner is a WorkerGlobalScope object (i.e., we are creating a nested dedicated worker),
112-
// then set parent worker global scope to owner.
113-
// FIXME: Support for nested workers.
114-
115-
// 5. Let unsafeWorkerCreationTime be the unsafe shared current time.
115+
// 3. Let unsafeWorkerCreationTime be the unsafe shared current time.
116116

117-
// 6. Let agent be the result of obtaining a dedicated/shared worker agent given outside settings
118-
// and is shared. Run the rest of these steps in that agent.
117+
// 4. Let agent be the result of obtaining a dedicated/shared worker agent given outside settings and is shared.
118+
// Run the rest of these steps in that agent.
119119

120120
// Note: This spawns a new process to act as the 'agent' for the worker.
121121
auto agent = outside_settings.realm().create<WorkerAgentParent>(url, options, port, outside_settings, agent_type);
@@ -127,6 +127,7 @@ WebIDL::ExceptionOr<void> Worker::terminate()
127127
{
128128
dbgln_if(WEB_WORKER_DEBUG, "WebWorker: Terminate");
129129

130+
// FIXME: The terminate() method steps are to terminate a worker given this's worker.
130131
return {};
131132
}
132133

Libraries/LibWeb/HTML/Worker.idl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ interface Worker : EventTarget {
1919
};
2020

2121
dictionary WorkerOptions {
22+
DOMString name = "";
2223
WorkerType type = "classic";
2324
RequestCredentials credentials = "same-origin";
24-
DOMString name = "";
2525
};
2626

2727
enum WorkerType { "classic", "module" };

Libraries/LibWeb/HTML/WorkerAgentParent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
namespace Web::HTML {
1616

1717
struct WorkerOptions {
18+
String name { String {} };
1819
Bindings::WorkerType type { Bindings::WorkerType::Classic };
1920
Bindings::RequestCredentials credentials { Bindings::RequestCredentials::SameOrigin };
20-
String name { String {} };
2121
};
2222

2323
// FIXME: Figure out a better naming convention for this type of parent/child process pattern.

0 commit comments

Comments
 (0)