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