239static Identifier jsValueToModuleKey(JSGlobalObject* lexicalGlobalObject, JSValue value)
240{
241 if (value.isSymbol())
242 return Identifier::fromUid(jsCast<Symbol*>(value)->privateName());
243 ASSERT(value.isString());
244 return asString(value)->toIdentifier(lexicalGlobalObject);
245}
246
247JSC::JSValue WorkerOrWorkletScriptController::evaluateModule(JSC::JSModuleRecord& moduleRecord)
248{
249 auto& globalObject = *m_globalScopeWrapper.get();
250 VM& vm = globalObject.vm();
251 JSLockHolder lock { vm };
252 return moduleRecord.evaluate(&globalObject);
253}
254
255MessageQueueWaitResult WorkerOrWorkletScriptController::loadModuleSynchronously(WorkerScriptFetcher& scriptFetcher, const ScriptSourceCode& sourceCode)
256{
257 if (isExecutionForbidden())
258 return MessageQueueTerminated;
259
260 initScriptIfNeeded();
261
262 auto& globalObject = *m_globalScopeWrapper.get();
263 VM& vm = globalObject.vm();
264 JSLockHolder lock { vm };
265
266 RefPtr<WorkerScriptFetcher> fetcherRef(&scriptFetcher);
267 {
268 auto& promise = JSExecState::loadModule(globalObject, sourceCode.jsSourceCode(), JSC::JSScriptFetcher::create(vm, { &scriptFetcher }));
269
270 auto& fulfillHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [fetcherRef](JSGlobalObject* globalObject, CallFrame* callFrame) -> JSC::EncodedJSValue {
271 VM& vm = globalObject->vm();
272 auto scope = DECLARE_THROW_SCOPE(vm);
273 Identifier moduleKey = jsValueToModuleKey(globalObject, callFrame->argument(0));
274 RETURN_IF_EXCEPTION(scope, { });
275 fetcherRef->notifyLoadCompleted(*moduleKey.impl());
276 return JSValue::encode(jsUndefined());
277 });
278
279 auto& rejectHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [fetcherRef](JSGlobalObject* globalObject, CallFrame* callFrame) {
280 VM& vm = globalObject->vm();
281 JSValue errorValue = callFrame->argument(0);
282 if (errorValue.isObject()) {
283 auto* object = JSC::asObject(errorValue);
284 if (JSValue failureKindValue = object->getDirect(vm, static_cast<JSVMClientData&>(*vm.clientData).builtinNames().failureKindPrivateName())) {
285 // This is host propagated error in the module loader pipeline.
286 switch (static_cast<ModuleFetchFailureKind>(failureKindValue.asInt32())) {
287 case ModuleFetchFailureKind::WasErrored:
288 fetcherRef->notifyLoadFailed(LoadableScript::Error {
289 LoadableScript::ErrorType::CachedScript,
290 WTF::nullopt
291 });
292 break;
293 case ModuleFetchFailureKind::WasCanceled:
294 fetcherRef->notifyLoadWasCanceled();
295 break;
296 }
297 return JSValue::encode(jsUndefined());
298 }
299 }
300
301 auto scope = DECLARE_CATCH_SCOPE(vm);
302 fetcherRef->notifyLoadFailed(LoadableScript::Error {
303 LoadableScript::ErrorType::CachedScript,
304 LoadableScript::ConsoleMessage {
305 MessageSource::JS,
306 MessageLevel::Error,
307 retrieveErrorMessage(*globalObject, vm, errorValue, scope),
308 }
309 });
310 return JSValue::encode(jsUndefined());
311 });
312
313 promise.then(&globalObject, &fulfillHandler, &rejectHandler);
314 }
315 m_globalScope->eventLoop().performMicrotaskCheckpoint();
316
317 // Drive RunLoop until we get either of "Worker is terminated", "Loading is done", or "Loading is failed".
318 WorkerRunLoop& runLoop = m_globalScope->workerOrWorkletThread()->runLoop();
319
320 // We do not want to receive messages that are not related to asynchronous resource loading.
321 // Otherwise, a worker discards some messages from the main thread here in a racy way.
322 // For example, the main thread can postMessage just after creating a Worker. In that case, postMessage's
323 // task is queued in WorkerRunLoop before start running module scripts. This task should not be discarded
324 // in the following driving of the RunLoop which mainly attempt to collect initial load of module scripts.
325 String taskMode = "loadModulesInWorkerOrWorkletMode"_s;
326 MessageQueueWaitResult result = MessageQueueMessageReceived;
327 while ((!fetcherRef->isLoaded() && !fetcherRef->wasCanceled()) && result != MessageQueueTerminated) {
328 result = runLoop.runInMode(m_globalScope, taskMode);
329 if (result != MessageQueueTerminated)
330 m_globalScope->eventLoop().performMicrotaskCheckpoint();
331 }
332
333 // FIXME: Currently we are not offering cancelling.
334 // if (!loader->done() && result == MessageQueueTerminated)
335 // loader->cancel();
336
337 return result;
338}
339
340void WorkerOrWorkletScriptController::linkAndEvaluateModule(WorkerScriptFetcher& scriptFetcher, const ScriptSourceCode& sourceCode, String* returnedExceptionMessage)
341{
342 if (isExecutionForbidden())
343 return;
344
345 initScriptIfNeeded();
346
347 auto& globalObject = *m_globalScopeWrapper.get();
348 VM& vm = globalObject.vm();
349 JSLockHolder lock { vm };
350
351 NakedPtr<JSC::Exception> returnedException;
352 JSExecState::linkAndEvaluateModule(globalObject, Identifier::fromUid(vm, scriptFetcher.moduleKey()), jsUndefined(), returnedException);
353 if ((returnedException && isTerminatedExecutionException(vm, returnedException)) || isTerminatingExecution()) {
354 forbidExecution();
355 return;
356 }
357
358 if (returnedException) {
359 if (m_globalScope->canIncludeErrorDetails(sourceCode.cachedScript(), sourceCode.url().string())) {
360 // FIXME: It's not great that this can run arbitrary code to string-ify the value of the exception.
361 // Do we need to do anything to handle that properly, if it, say, raises another exception?
362 if (returnedExceptionMessage)
363 *returnedExceptionMessage = returnedException->value().toWTFString(&globalObject);
364 } else {
365 // Overwrite the detailed error with a generic error.
366 String genericErrorMessage { "Script error."_s };
367 if (returnedExceptionMessage)
368 *returnedExceptionMessage = genericErrorMessage;
369 returnedException = JSC::Exception::create(vm, createError(&globalObject, genericErrorMessage));
370 }
371 }
372}
373
374void WorkerOrWorkletScriptController::loadAndEvaluateModule(const URL& moduleURL, FetchOptions::Credentials credentials, CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
375{
376 if (isExecutionForbidden()) {
377 completionHandler(Exception { NotAllowedError });
378 return;
379 }
380
381 initScriptIfNeeded();
382
383 auto& globalObject = *m_globalScopeWrapper.get();
384 VM& vm = globalObject.vm();
385 JSLockHolder lock { vm };
386
387 auto scriptFetcher = WorkerScriptFetcher::create(credentials);
388 {
389 auto& promise = JSExecState::loadAndEvaluateModule(globalObject, moduleURL.string(), jsUndefined(), JSC::JSScriptFetcher::create(vm, { scriptFetcher.ptr() }));
390
391 auto task = createSharedTask<void(Optional<Exception>&&)>([completionHandler = WTFMove(completionHandler)](Optional<Exception>&& exception) mutable {
392 completionHandler(WTFMove(exception));
393 });
394
395 auto& fulfillHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [task](JSGlobalObject* globalObject, CallFrame* callFrame) -> JSC::EncodedJSValue {
396 VM& vm = globalObject->vm();
397 auto scope = DECLARE_THROW_SCOPE(vm);
398 Identifier moduleKey = jsValueToModuleKey(globalObject, callFrame->argument(0));
399 RETURN_IF_EXCEPTION(scope, { });
400 task->run(WTF::nullopt);
401 return JSValue::encode(jsUndefined());
402 });
403
404 auto& rejectHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [task](JSGlobalObject* globalObject, CallFrame* callFrame) {
405 VM& vm = globalObject->vm();
406 JSValue errorValue = callFrame->argument(0);
407 if (errorValue.isObject()) {
408 auto* object = JSC::asObject(errorValue);
409 if (JSValue failureKindValue = object->getDirect(vm, static_cast<JSVMClientData&>(*vm.clientData).builtinNames().failureKindPrivateName())) {
410 // This is host propagated error in the module loader pipeline.
411 switch (static_cast<ModuleFetchFailureKind>(failureKindValue.asInt32())) {
412 case ModuleFetchFailureKind::WasErrored:
413 case ModuleFetchFailureKind::WasCanceled:
414 task->run(Exception { NetworkError });
415 break;
416 }
417 return JSValue::encode(jsUndefined());
418 }
419 }
420
421 auto scope = DECLARE_CATCH_SCOPE(vm);
422 String string = retrieveErrorMessage(*globalObject, vm, errorValue, scope);
423 task->run(Exception { TypeError, string });
424 return JSValue::encode(jsUndefined());
425 });
426
427 promise.then(&globalObject, &fulfillHandler, &rejectHandler);
428 }
429 m_globalScope->eventLoop().performMicrotaskCheckpoint();
430}
431