| File: | Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/workers/WorkerOrWorkletScriptController.cpp |
| Warning: | line 462, column 60 Call argument is uncounted and unsafe |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * Copyright (C) 2008-2021 Apple Inc. All rights reserved. |
| 3 | * Copyright (C) 2011, 2012 Google Inc. All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * |
| 14 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 18 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 | */ |
| 26 | |
| 27 | #include "config.h" |
| 28 | #include "WorkerOrWorkletScriptController.h" |
| 29 | |
| 30 | #include "CommonVM.h" |
| 31 | #include "DedicatedWorkerGlobalScope.h" |
| 32 | #include "EventLoop.h" |
| 33 | #include "InspectorInstrumentation.h" |
| 34 | #include "JSAudioWorkletGlobalScope.h" |
| 35 | #include "JSDOMBinding.h" |
| 36 | #include "JSDedicatedWorkerGlobalScope.h" |
| 37 | #include "JSEventTarget.h" |
| 38 | #include "JSExecState.h" |
| 39 | #include "JSPaintWorkletGlobalScope.h" |
| 40 | #include "JSServiceWorkerGlobalScope.h" |
| 41 | #include "JSSharedWorkerGlobalScope.h" |
| 42 | #include "ModuleFetchFailureKind.h" |
| 43 | #include "ModuleFetchParameters.h" |
| 44 | #include "ScriptSourceCode.h" |
| 45 | #include "WebCoreJSClientData.h" |
| 46 | #include "WorkerConsoleClient.h" |
| 47 | #include "WorkerModuleScriptLoader.h" |
| 48 | #include "WorkerOrWorkletThread.h" |
| 49 | #include "WorkerRunLoop.h" |
| 50 | #include "WorkerScriptFetcher.h" |
| 51 | #include <JavaScriptCore/AbstractModuleRecord.h> |
| 52 | #include <JavaScriptCore/BuiltinNames.h> |
| 53 | #include <JavaScriptCore/Completion.h> |
| 54 | #include <JavaScriptCore/DeferTermination.h> |
| 55 | #include <JavaScriptCore/DeferredWorkTimer.h> |
| 56 | #include <JavaScriptCore/Exception.h> |
| 57 | #include <JavaScriptCore/ExceptionHelpers.h> |
| 58 | #include <JavaScriptCore/GCActivityCallback.h> |
| 59 | #include <JavaScriptCore/JSGlobalProxyInlines.h> |
| 60 | #include <JavaScriptCore/JSInternalPromise.h> |
| 61 | #include <JavaScriptCore/JSLock.h> |
| 62 | #include <JavaScriptCore/JSModuleRecord.h> |
| 63 | #include <JavaScriptCore/JSNativeStdFunction.h> |
| 64 | #include <JavaScriptCore/JSScriptFetchParameters.h> |
| 65 | #include <JavaScriptCore/JSScriptFetcher.h> |
| 66 | #include <JavaScriptCore/ScriptCallStack.h> |
| 67 | #include <JavaScriptCore/StrongInlines.h> |
| 68 | #include <JavaScriptCore/SyntheticModuleRecord.h> |
| 69 | #include <JavaScriptCore/VMTrapsInlines.h> |
| 70 | #include <JavaScriptCore/WebAssemblyModuleRecord.h> |
| 71 | #include <wtf/TZoneMallocInlines.h> |
| 72 | |
| 73 | namespace WebCore { |
| 74 | |
| 75 | WTF_MAKE_TZONE_ALLOCATED_IMPL(WorkerOrWorkletScriptController)::bmalloc::api::HeapRef WorkerOrWorkletScriptController::s_heapRef ; const TZoneSpecification WorkerOrWorkletScriptController::s_heapSpec = { &WorkerOrWorkletScriptController::s_heapRef, TZoneSpecification ::encodeSize<WorkerOrWorkletScriptController>(), TZoneSpecification ::encodeAlignment<WorkerOrWorkletScriptController>(), TZoneSpecification ::encodeCategory<WorkerOrWorkletScriptController>(), :: bmalloc::api::compactAllocationMode<WorkerOrWorkletScriptController >(), TZoneSpecification::encodeDescriptor<WorkerOrWorkletScriptController >(), }; void* WorkerOrWorkletScriptController::operatorNewSlow (size_t size) { if constexpr (::bmalloc::api::compactAllocationMode <WorkerOrWorkletScriptController>() == CompactAllocationMode ::Compact) return ::bmalloc::api::tzoneAllocateCompactSlow(size , s_heapSpec); return ::bmalloc::api::tzoneAllocateNonCompactSlow (size, s_heapSpec); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__((unused)) = int; |
| 76 | |
| 77 | using namespace JSC; |
| 78 | |
| 79 | WorkerOrWorkletScriptController::WorkerOrWorkletScriptController(WorkerThreadType type, Ref<VM>&& vm, WorkerOrWorkletGlobalScope* globalScope) |
| 80 | : m_vm(WTF::move(vm)) |
| 81 | , m_globalScope(globalScope) |
| 82 | , m_globalScopeWrapper(*m_vm) |
| 83 | { |
| 84 | if (!isMainThread() || m_vm != &commonVM()) { |
| 85 | m_vm->heap.acquireAccess(); // It's not clear that we have good discipline for heap access, so turn it on permanently. |
| 86 | { |
| 87 | JSLockHolder lock(m_vm.get()); |
| 88 | m_vm->ensureTerminationException(); |
| 89 | m_vm->forbidExecutionOnTermination(); |
| 90 | } |
| 91 | |
| 92 | JSVMClientData::initNormalWorld(m_vm.get(), type); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | WorkerOrWorkletScriptController::~WorkerOrWorkletScriptController() |
| 97 | { |
| 98 | JSLockHolder lock(vm()); |
| 99 | if (m_globalScopeWrapper) { |
| 100 | m_globalScopeWrapper->clearDOMGuardedObjects(); |
| 101 | m_globalScopeWrapper->setConsoleClient(nullptr); |
| 102 | |
| 103 | } |
| 104 | m_globalScopeWrapper.clear(); |
| 105 | m_vm = nullptr; |
| 106 | } |
| 107 | |
| 108 | void WorkerOrWorkletScriptController::attachDebugger(JSC::Debugger* debugger) |
| 109 | { |
| 110 | initScriptIfNeeded(); |
| 111 | debugger->attach(m_globalScopeWrapper.get()); |
| 112 | } |
| 113 | |
| 114 | void WorkerOrWorkletScriptController::detachDebugger(JSC::Debugger* debugger) |
| 115 | { |
| 116 | debugger->detach(m_globalScopeWrapper.get(), JSC::Debugger::TerminatingDebuggingSession); |
| 117 | } |
| 118 | |
| 119 | void WorkerOrWorkletScriptController::forbidExecution() |
| 120 | { |
| 121 | ASSERT(m_globalScope->isContextThread())((void)0); |
| 122 | m_vm->setExecutionForbidden(); |
| 123 | } |
| 124 | |
| 125 | bool WorkerOrWorkletScriptController::isExecutionForbidden() const |
| 126 | { |
| 127 | ASSERT(m_globalScope->isContextThread())((void)0); |
| 128 | return m_vm->executionForbidden(); |
| 129 | } |
| 130 | |
| 131 | void WorkerOrWorkletScriptController::scheduleExecutionTermination() |
| 132 | { |
| 133 | { |
| 134 | // The mutex provides a memory barrier to ensure that once |
| 135 | // termination is scheduled, isTerminatingExecution() will |
| 136 | // accurately reflect that lexicalGlobalObject when called from another thread. |
| 137 | Locker locker { m_scheduledTerminationLock }; |
| 138 | if (m_isTerminatingExecution) |
| 139 | return; |
| 140 | |
| 141 | m_isTerminatingExecution = true; |
| 142 | } |
| 143 | if (m_vm != &commonVM()) |
| 144 | m_vm->notifyNeedTermination(); |
| 145 | } |
| 146 | |
| 147 | bool WorkerOrWorkletScriptController::isTerminatingExecution() const |
| 148 | { |
| 149 | // See comments in scheduleExecutionTermination regarding mutex usage. |
| 150 | Locker locker { m_scheduledTerminationLock }; |
| 151 | return m_isTerminatingExecution; |
| 152 | } |
| 153 | |
| 154 | void WorkerOrWorkletScriptController::releaseHeapAccess() |
| 155 | { |
| 156 | m_vm->heap.releaseAccess(); |
| 157 | } |
| 158 | |
| 159 | void WorkerOrWorkletScriptController::acquireHeapAccess() |
| 160 | { |
| 161 | m_vm->heap.acquireAccess(); |
| 162 | } |
| 163 | |
| 164 | void WorkerOrWorkletScriptController::addTimerSetNotification(JSC::JSRunLoopTimer::TimerNotificationCallback callback) |
| 165 | { |
| 166 | auto processTimer = [&] (JSRunLoopTimer* timer) { |
| 167 | if (!timer) |
| 168 | return; |
| 169 | timer->addTimerSetNotification(callback); |
| 170 | }; |
| 171 | |
| 172 | processTimer(protect(m_vm->heap.fullActivityCallback()).get()); |
| 173 | processTimer(protect(m_vm->heap.edenActivityCallback()).get()); |
| 174 | processTimer(m_vm->deferredWorkTimer.ptr()); |
| 175 | } |
| 176 | |
| 177 | void WorkerOrWorkletScriptController::removeTimerSetNotification(JSC::JSRunLoopTimer::TimerNotificationCallback callback) |
| 178 | { |
| 179 | auto processTimer = [&] (JSRunLoopTimer* timer) { |
| 180 | if (!timer) |
| 181 | return; |
| 182 | timer->removeTimerSetNotification(callback); |
| 183 | }; |
| 184 | |
| 185 | processTimer(protect(m_vm->heap.fullActivityCallback()).get()); |
| 186 | processTimer(protect(m_vm->heap.edenActivityCallback()).get()); |
| 187 | processTimer(m_vm->deferredWorkTimer.ptr()); |
| 188 | } |
| 189 | |
| 190 | void WorkerOrWorkletScriptController::setException(JSC::Exception* exception) |
| 191 | { |
| 192 | JSC::JSGlobalObject* lexicalGlobalObject = m_globalScopeWrapper.get(); |
| 193 | VM& vm = lexicalGlobalObject->vm(); |
| 194 | auto scope = DECLARE_THROW_SCOPE(vm)JSC::ThrowScope((vm)); |
| 195 | throwException(lexicalGlobalObject, scope, exception); |
| 196 | } |
| 197 | |
| 198 | void WorkerOrWorkletScriptController::disableEval(const String& errorMessage) |
| 199 | { |
| 200 | initScriptIfNeeded(); |
| 201 | JSLockHolder lock { vm() }; |
| 202 | |
| 203 | m_globalScopeWrapper->setEvalEnabled(false, errorMessage); |
| 204 | } |
| 205 | |
| 206 | void WorkerOrWorkletScriptController::disableWebAssembly(const String& errorMessage) |
| 207 | { |
| 208 | initScriptIfNeeded(); |
| 209 | JSLockHolder lock { vm() }; |
| 210 | |
| 211 | m_globalScopeWrapper->setWebAssemblyEnabled(false, errorMessage); |
| 212 | } |
| 213 | |
| 214 | void WorkerOrWorkletScriptController::setTrustedTypesEnforcement(JSC::TrustedTypesEnforcement enforcement) |
| 215 | { |
| 216 | initScriptIfNeeded(); |
| 217 | JSLockHolder lock { vm() }; |
| 218 | |
| 219 | m_globalScopeWrapper->setTrustedTypesEnforcement(enforcement); |
| 220 | } |
| 221 | |
| 222 | void WorkerOrWorkletScriptController::evaluate(const ScriptSourceCode& sourceCode, String* returnedExceptionMessage) |
| 223 | { |
| 224 | if (isExecutionForbidden()) |
| 225 | return; |
| 226 | |
| 227 | VM& vm = this->vm(); |
| 228 | NakedPtr<JSC::Exception> uncaughtException; |
| 229 | evaluate(sourceCode, uncaughtException, returnedExceptionMessage); |
| 230 | if ((uncaughtException && vm.isTerminationException(uncaughtException)) || isTerminatingExecution()) { |
| 231 | forbidExecution(); |
| 232 | return; |
| 233 | } |
| 234 | if (uncaughtException) { |
| 235 | JSLockHolder lock(vm); |
| 236 | reportException(m_globalScopeWrapper.get(), uncaughtException); |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | void WorkerOrWorkletScriptController::evaluate(const ScriptSourceCode& sourceCode, NakedPtr<JSC::Exception>& returnedException, String* returnedExceptionMessage) |
| 241 | { |
| 242 | if (isExecutionForbidden()) |
| 243 | return; |
| 244 | |
| 245 | initScriptIfNeeded(); |
| 246 | |
| 247 | auto& globalObject = *m_globalScopeWrapper.get(); |
| 248 | VM& vm = globalObject.vm(); |
| 249 | JSLockHolder lock { vm }; |
| 250 | |
| 251 | const SourceCode& jsSourceCode = sourceCode.jsSourceCode(); |
| 252 | const URL& sourceURL = jsSourceCode.provider()->sourceOrigin().url(); |
| 253 | |
| 254 | RefPtr globalScope = m_globalScope.get(); |
| 255 | InspectorInstrumentation::willEvaluateScript(*globalScope, sourceURL.string(), sourceCode.startLine(), sourceCode.startColumn()); |
| 256 | |
| 257 | JSExecState::profiledEvaluate(&globalObject, JSC::ProfilingReason::Other, jsSourceCode, m_globalScopeWrapper->globalThis(), returnedException); |
| 258 | |
| 259 | InspectorInstrumentation::didEvaluateScript(*globalScope); |
| 260 | |
| 261 | if ((returnedException && vm.isTerminationException(returnedException)) || isTerminatingExecution()) { |
| 262 | forbidExecution(); |
| 263 | return; |
| 264 | } |
| 265 | |
| 266 | if (returnedException) { |
| 267 | if (globalScope->canIncludeErrorDetails(sourceCode.cachedScript(), sourceCode.url().string())) { |
| 268 | // FIXME: It's not great that this can run arbitrary code to string-ify the value of the exception. |
| 269 | // Do we need to do anything to handle that properly, if it, say, raises another exception? |
| 270 | if (returnedExceptionMessage) |
| 271 | *returnedExceptionMessage = returnedException->value().toWTFString(&globalObject); |
| 272 | } else { |
| 273 | // Overwrite the detailed error with a generic error. |
| 274 | String genericErrorMessage { "Script error."_s }; |
| 275 | if (returnedExceptionMessage) |
| 276 | *returnedExceptionMessage = genericErrorMessage; |
| 277 | returnedException = JSC::Exception::create(vm, createError(&globalObject, genericErrorMessage)); |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | static Identifier jsValueToModuleKey(JSGlobalObject* lexicalGlobalObject, JSValue value) |
| 283 | { |
| 284 | if (value.isSymbol()) |
| 285 | return Identifier::fromUid(jsCast<Symbol*>(value)->privateName()); |
| 286 | ASSERT(value.isString())((void)0); |
| 287 | return asString(value)->toIdentifier(lexicalGlobalObject); |
| 288 | } |
| 289 | |
| 290 | JSC::JSValue WorkerOrWorkletScriptController::evaluateModule(const URL& sourceURL, JSC::AbstractModuleRecord& moduleRecord, JSC::JSValue awaitedValue, JSC::JSValue resumeMode) |
| 291 | { |
| 292 | auto& globalObject = *m_globalScopeWrapper.get(); |
| 293 | VM& vm = globalObject.vm(); |
| 294 | JSLockHolder lock { vm }; |
| 295 | |
| 296 | #if ENABLE(WEBASSEMBLY)(defined 1 && 1) |
| 297 | const bool isWasmModule = moduleRecord.inherits<WebAssemblyModuleRecord>(); |
| 298 | #else |
| 299 | constexpr bool isWasmModule = false; |
| 300 | #endif |
| 301 | |
| 302 | RefPtr globalScope = m_globalScope.get(); |
| 303 | if (isWasmModule) { |
| 304 | // FIXME: Provide better inspector support for Wasm scripts. |
| 305 | InspectorInstrumentation::willEvaluateScript(*globalScope, sourceURL.string(), 1, 1); |
| 306 | } else if (moduleRecord.inherits<JSC::SyntheticModuleRecord>()) |
| 307 | InspectorInstrumentation::willEvaluateScript(*globalScope, sourceURL.string(), 1, 1); |
| 308 | else { |
| 309 | auto* jsModuleRecord = jsCast<JSModuleRecord*>(&moduleRecord); |
| 310 | const auto& jsSourceCode = jsModuleRecord->sourceCode(); |
| 311 | InspectorInstrumentation::willEvaluateScript(*globalScope, sourceURL.string(), jsSourceCode.firstLine().oneBasedInt(), jsSourceCode.startColumn().oneBasedInt()); |
| 312 | } |
| 313 | auto returnValue = moduleRecord.evaluate(&globalObject, awaitedValue, resumeMode); |
| 314 | InspectorInstrumentation::didEvaluateScript(*globalScope); |
| 315 | |
| 316 | return returnValue; |
| 317 | } |
| 318 | |
| 319 | bool WorkerOrWorkletScriptController::loadModuleSynchronously(WorkerScriptFetcher& scriptFetcher, const ScriptSourceCode& sourceCode) |
| 320 | { |
| 321 | if (isExecutionForbidden()) |
| 322 | return false; |
| 323 | |
| 324 | initScriptIfNeeded(); |
| 325 | |
| 326 | auto& globalObject = *m_globalScopeWrapper.get(); |
| 327 | VM& vm = globalObject.vm(); |
| 328 | JSLockHolder lock { vm }; |
| 329 | auto scope = DECLARE_THROW_SCOPE(vm)JSC::ThrowScope((vm)); |
| 330 | |
| 331 | Ref protector { scriptFetcher }; |
| 332 | { |
| 333 | auto* promise = JSExecState::loadModule(globalObject, sourceCode.jsSourceCode(), JSC::JSScriptFetcher::create(vm, { &scriptFetcher })); |
| 334 | scope.assertNoExceptionExceptTermination(); |
| 335 | RETURN_IF_EXCEPTION(scope, false)do { [[clang::suppress]] JSC::VM& vm = (scope).vm(); ((void )0); if (vm.traps().maybeNeedHandling()) [[unlikely]] { if (vm .hasExceptionsAfterHandlingTraps()) return false; } } while ( false); |
| 336 | |
| 337 | auto& fulfillHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [protector](JSGlobalObject* globalObject, CallFrame* callFrame) -> JSC::EncodedJSValue { |
| 338 | VM& vm = globalObject->vm(); |
| 339 | JSLockHolder lock { vm }; |
| 340 | auto scope = DECLARE_THROW_SCOPE(vm)JSC::ThrowScope((vm)); |
| 341 | Identifier moduleKey = jsValueToModuleKey(globalObject, callFrame->argument(0)); |
| 342 | RETURN_IF_EXCEPTION(scope, { })do { [[clang::suppress]] JSC::VM& vm = (scope).vm(); ((void )0); if (vm.traps().maybeNeedHandling()) [[unlikely]] { if (vm .hasExceptionsAfterHandlingTraps()) return { }; } } while (false ); |
| 343 | protector->notifyLoadCompleted(*moduleKey.impl()); |
| 344 | return JSValue::encode(jsUndefined()); |
| 345 | }); |
| 346 | |
| 347 | auto& rejectHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [protector](JSGlobalObject* globalObject, CallFrame* callFrame) { |
| 348 | VM& vm = globalObject->vm(); |
| 349 | JSLockHolder lock { vm }; |
| 350 | JSValue errorValue = callFrame->argument(0); |
| 351 | auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm)JSC::TopExceptionScope((vm)); |
| 352 | if (errorValue.isObject()) { |
| 353 | auto* object = JSC::asObject(errorValue); |
| 354 | if (JSValue failureKindValue = object->getDirect(vm, vm.propertyNames->builtinNames().moduleFetchFailureKindPrivateName())) { |
| 355 | // This is host propagated error in the module loader pipeline. |
| 356 | switch (static_cast<ModuleFetchFailureKind>(failureKindValue.asInt32())) { |
| 357 | case ModuleFetchFailureKind::WasPropagatedError: |
| 358 | protector->notifyLoadFailed(LoadableScript::Error { |
| 359 | LoadableScript::ErrorType::Fetch, |
| 360 | { }, |
| 361 | { } |
| 362 | }); |
| 363 | break; |
| 364 | // For a fetch error that was not propagated from further in the |
| 365 | // pipeline, we include the console error message but do not |
| 366 | // include an error value as it should not be reported. |
| 367 | case ModuleFetchFailureKind::WasFetchError: |
| 368 | protector->notifyLoadFailed(LoadableScript::Error { |
| 369 | LoadableScript::ErrorType::Fetch, |
| 370 | LoadableScript::ConsoleMessage { |
| 371 | MessageSource::JS, |
| 372 | MessageLevel::Error, |
| 373 | retrieveErrorMessage(*globalObject, vm, errorValue, scope), |
| 374 | }, |
| 375 | { } |
| 376 | }); |
| 377 | break; |
| 378 | case ModuleFetchFailureKind::WasResolveError: |
| 379 | protector->notifyLoadFailed(LoadableScript::Error { |
| 380 | LoadableScript::ErrorType::Resolve, |
| 381 | LoadableScript::ConsoleMessage { |
| 382 | MessageSource::JS, |
| 383 | MessageLevel::Error, |
| 384 | retrieveErrorMessage(*globalObject, vm, errorValue, scope), |
| 385 | }, |
| 386 | // The error value may need to be propagated here as it is in |
| 387 | // ScriptController in the future. |
| 388 | { } |
| 389 | }); |
| 390 | break; |
| 391 | case ModuleFetchFailureKind::WasCanceled: |
| 392 | protector->notifyLoadWasCanceled(); |
| 393 | break; |
| 394 | } |
| 395 | return JSValue::encode(jsUndefined()); |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | protector->notifyLoadFailed(LoadableScript::Error { |
| 400 | LoadableScript::ErrorType::Script, |
| 401 | LoadableScript::ConsoleMessage { |
| 402 | MessageSource::JS, |
| 403 | MessageLevel::Error, |
| 404 | retrieveErrorMessage(*globalObject, vm, errorValue, scope), |
| 405 | }, |
| 406 | // The error value may need to be propagated here as it is in |
| 407 | // ScriptController in the future. |
| 408 | { } |
| 409 | }); |
| 410 | return JSValue::encode(jsUndefined()); |
| 411 | }); |
| 412 | |
| 413 | promise->then(&globalObject, &fulfillHandler, &rejectHandler); |
| 414 | } |
| 415 | |
| 416 | RefPtr globalScope = m_globalScope.get(); |
| 417 | globalScope->eventLoop().performMicrotaskCheckpoint(*m_vm); |
| 418 | |
| 419 | // Drive RunLoop until we get either of "Worker is terminated", "Loading is done", or "Loading is failed". |
| 420 | RefPtr workerOrWorkletThread = globalScope->workerOrWorkletThread(); |
| 421 | auto& runLoop = workerOrWorkletThread->runLoop(); |
| 422 | |
| 423 | // We do not want to receive messages that are not related to asynchronous resource loading. |
| 424 | // Otherwise, a worker discards some messages from the main thread here in a racy way. |
| 425 | // For example, the main thread can postMessage just after creating a Worker. In that case, postMessage's |
| 426 | // task is queued in WorkerRunLoop before start running module scripts. This task should not be discarded |
| 427 | // in the following driving of the RunLoop which mainly attempt to collect initial load of module scripts. |
| 428 | String taskMode = WorkerModuleScriptLoader::taskMode(); |
| 429 | |
| 430 | // Allow tasks scheduled from the WorkerEventLoop. |
| 431 | constexpr bool allowEventLoopTasks = true; |
| 432 | |
| 433 | bool success = true; |
| 434 | while ((!protector->isLoaded() && !protector->wasCanceled()) && success) { |
| 435 | success = runLoop.runInMode(globalScope.get(), taskMode, allowEventLoopTasks); |
| 436 | if (success) |
| 437 | globalScope->eventLoop().performMicrotaskCheckpoint(*m_vm); |
| 438 | } |
| 439 | |
| 440 | return success; |
| 441 | } |
| 442 | |
| 443 | void WorkerOrWorkletScriptController::linkAndEvaluateModule(WorkerScriptFetcher& scriptFetcher, const ScriptSourceCode& sourceCode, String* returnedExceptionMessage) |
| 444 | { |
| 445 | if (isExecutionForbidden()) |
| 446 | return; |
| 447 | |
| 448 | initScriptIfNeeded(); |
| 449 | |
| 450 | auto& globalObject = *m_globalScopeWrapper.get(); |
| 451 | VM& vm = globalObject.vm(); |
| 452 | JSLockHolder lock { vm }; |
| 453 | |
| 454 | NakedPtr<JSC::Exception> returnedException; |
| 455 | JSExecState::linkAndEvaluateModule(globalObject, Identifier::fromUid(vm, protect(scriptFetcher.moduleKey()).get()), jsUndefined(), returnedException); |
| 456 | if ((returnedException && vm.isTerminationException(returnedException)) || isTerminatingExecution()) { |
| 457 | forbidExecution(); |
| 458 | return; |
| 459 | } |
| 460 | |
| 461 | if (returnedException) { |
| 462 | if (protect(globalScope())->canIncludeErrorDetails(sourceCode.cachedScript(), sourceCode.url().string())) { |
Call argument is uncounted and unsafe | |
| 463 | // FIXME: It's not great that this can run arbitrary code to string-ify the value of the exception. |
| 464 | // Do we need to do anything to handle that properly, if it, say, raises another exception? |
| 465 | if (returnedExceptionMessage) |
| 466 | *returnedExceptionMessage = returnedException->value().toWTFString(&globalObject); |
| 467 | } else { |
| 468 | String genericErrorMessage { "Script error."_s }; |
| 469 | if (returnedExceptionMessage) |
| 470 | *returnedExceptionMessage = genericErrorMessage; |
| 471 | } |
| 472 | |
| 473 | JSLockHolder lock(vm); |
| 474 | reportException(m_globalScopeWrapper.get(), returnedException); |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | void WorkerOrWorkletScriptController::loadAndEvaluateModule(const URL& moduleURL, FetchOptions::Credentials credentials, CompletionHandler<void(std::optional<Exception>&&)>&& completionHandler) |
| 479 | { |
| 480 | if (isExecutionForbidden()) { |
| 481 | completionHandler(Exception { ExceptionCode::NotAllowedError }); |
| 482 | return; |
| 483 | } |
| 484 | |
| 485 | initScriptIfNeeded(); |
| 486 | |
| 487 | auto& globalObject = *m_globalScopeWrapper.get(); |
| 488 | VM& vm = globalObject.vm(); |
| 489 | JSLockHolder lock { vm }; |
| 490 | |
| 491 | auto parameters = ModuleFetchParameters::create(JSC::ScriptFetchParameters::Type::JavaScript, emptyString(), /* isTopLevelModule */ true); |
| 492 | RefPtr globalScope = m_globalScope.get(); |
| 493 | auto scriptFetcher = WorkerScriptFetcher::create(WTF::move(parameters), credentials, globalScope->destination(), globalScope->referrerPolicy()); |
| 494 | |
| 495 | auto* promise = JSExecState::loadModule(globalObject, moduleURL, JSC::JSScriptFetchParameters::create(vm, scriptFetcher->parameters()), JSC::JSScriptFetcher::create(vm, { scriptFetcher.ptr() })); |
| 496 | if (promise) [[likely]] { |
| 497 | auto task = createSharedTask<void(std::optional<Exception>&&)>([completionHandler = WTF::move(completionHandler)](std::optional<Exception>&& exception) mutable { |
| 498 | completionHandler(WTF::move(exception)); |
| 499 | }); |
| 500 | |
| 501 | auto& fulfillHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [task, scriptFetcher](JSGlobalObject* globalObject, CallFrame* callFrame) -> JSC::EncodedJSValue { |
| 502 | // task->run(std::nullopt); |
| 503 | VM& vm = globalObject->vm(); |
| 504 | JSLockHolder lock { vm }; |
| 505 | auto scope = DECLARE_THROW_SCOPE(vm)JSC::ThrowScope((vm)); |
| 506 | |
| 507 | Identifier moduleKey = jsValueToModuleKey(globalObject, callFrame->argument(0)); |
| 508 | RETURN_IF_EXCEPTION(scope, { })do { [[clang::suppress]] JSC::VM& vm = (scope).vm(); ((void )0); if (vm.traps().maybeNeedHandling()) [[unlikely]] { if (vm .hasExceptionsAfterHandlingTraps()) return { }; } } while (false ); |
| 509 | scriptFetcher->notifyLoadCompleted(*moduleKey.impl()); |
| 510 | |
| 511 | RefPtr context = downcast<WorkerOrWorkletGlobalScope>(jsCast<JSDOMGlobalObject*>(globalObject)->scriptExecutionContext()); |
| 512 | if (!context || !context->script()) { |
| 513 | task->run(std::nullopt); |
| 514 | return JSValue::encode(jsUndefined()); |
| 515 | } |
| 516 | |
| 517 | NakedPtr<JSC::Exception> returnedException; |
| 518 | JSExecState::linkAndEvaluateModule(*globalObject, moduleKey, jsUndefined(), returnedException); |
| 519 | if ((returnedException && vm.isTerminationException(returnedException)) || context->script()->isTerminatingExecution()) { |
| 520 | if (context->script()) |
| 521 | context->script()->forbidExecution(); |
| 522 | task->run(std::nullopt); |
| 523 | return JSValue::encode(jsUndefined()); |
| 524 | } |
| 525 | |
| 526 | if (returnedException) { |
| 527 | String message; |
| 528 | if (context->canIncludeErrorDetails(nullptr, moduleKey.string())) { |
| 529 | // FIXME: It's not great that this can run arbitrary code to string-ify the value of the exception. |
| 530 | // Do we need to do anything to handle that properly, if it, say, raises another exception? |
| 531 | message = returnedException->value().toWTFString(globalObject); |
| 532 | } else |
| 533 | message = "Script error."_s; |
| 534 | context->reportException(message, { }, { }, { }, { }, { }); |
| 535 | } |
| 536 | |
| 537 | task->run(std::nullopt); |
| 538 | return JSValue::encode(jsUndefined()); |
| 539 | }); |
| 540 | |
| 541 | auto& rejectHandler = *JSNativeStdFunction::create(vm, &globalObject, 1, String(), [task](JSGlobalObject* globalObject, CallFrame* callFrame) { |
| 542 | VM& vm = globalObject->vm(); |
| 543 | JSLockHolder lock { vm }; |
| 544 | JSValue errorValue = callFrame->argument(0); |
| 545 | if (errorValue.isObject()) { |
| 546 | auto* object = JSC::asObject(errorValue); |
| 547 | if (JSValue failureKindValue = object->getDirect(vm, vm.propertyNames->builtinNames().moduleFetchFailureKindPrivateName())) { |
| 548 | auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm)JSC::TopExceptionScope((vm)); |
| 549 | String message = retrieveErrorMessageWithoutName(*globalObject, vm, object, catchScope); |
| 550 | switch (static_cast<ModuleFetchFailureKind>(failureKindValue.asInt32())) { |
| 551 | case ModuleFetchFailureKind::WasFetchError: |
| 552 | case ModuleFetchFailureKind::WasResolveError: |
| 553 | task->run(Exception { ExceptionCode::TypeError, message }); |
| 554 | break; |
| 555 | case ModuleFetchFailureKind::WasPropagatedError: |
| 556 | case ModuleFetchFailureKind::WasCanceled: |
| 557 | task->run(Exception { ExceptionCode::AbortError, message }); |
| 558 | break; |
| 559 | } |
| 560 | return JSValue::encode(jsUndefined()); |
| 561 | } |
| 562 | if (object->inherits<ErrorInstance>()) { |
| 563 | auto* error = jsCast<ErrorInstance*>(object); |
| 564 | switch (error->errorType()) { |
| 565 | case ErrorType::TypeError: { |
| 566 | auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm)JSC::TopExceptionScope((vm)); |
| 567 | String message = retrieveErrorMessageWithoutName(*globalObject, vm, error, catchScope); |
| 568 | task->run(Exception { ExceptionCode::TypeError, message }); |
| 569 | return JSValue::encode(jsUndefined()); |
| 570 | } |
| 571 | case ErrorType::SyntaxError: { |
| 572 | auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm)JSC::TopExceptionScope((vm)); |
| 573 | String message = retrieveErrorMessageWithoutName(*globalObject, vm, error, catchScope); |
| 574 | task->run(Exception { ExceptionCode::JSSyntaxError, message }); |
| 575 | return JSValue::encode(jsUndefined()); |
| 576 | } |
| 577 | default: |
| 578 | break; |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm)JSC::TopExceptionScope((vm)); |
| 584 | String message = retrieveErrorMessageWithoutName(*globalObject, vm, errorValue, catchScope); |
| 585 | task->run(Exception { ExceptionCode::AbortError, message }); |
| 586 | return JSValue::encode(jsUndefined()); |
| 587 | }); |
| 588 | |
| 589 | promise->then(&globalObject, &fulfillHandler, &rejectHandler); |
| 590 | } |
| 591 | globalScope->eventLoop().performMicrotaskCheckpoint(*m_vm); |
| 592 | } |
| 593 | |
| 594 | template<typename JSGlobalScopePrototype, typename JSGlobalScope, typename GlobalScope> |
| 595 | void WorkerOrWorkletScriptController::initScriptWithSubclass() |
| 596 | { |
| 597 | ASSERT(!m_globalScopeWrapper)((void)0); |
| 598 | |
| 599 | JSLockHolder lock { vm() }; |
| 600 | |
| 601 | // Explicitly protect the global object's prototype so it isn't collected |
| 602 | // when we allocate the global object. (Once the global object is fully |
| 603 | // constructed, it can mark its own prototype.) |
| 604 | Structure* contextPrototypeStructure = JSGlobalScopePrototype::createStructure(*m_vm, nullptr, jsNull()); |
| 605 | auto* contextPrototype = JSGlobalScopePrototype::create(*m_vm, nullptr, contextPrototypeStructure); |
| 606 | Structure* structure = JSGlobalScope::createStructure(*m_vm, nullptr, contextPrototype); |
| 607 | auto* proxyStructure = JSGlobalProxy::createStructure(*m_vm, nullptr, jsNull()); |
| 608 | auto* proxy = JSGlobalProxy::create(*m_vm, proxyStructure); |
| 609 | |
| 610 | RefPtr globalScope = m_globalScope.get(); |
| 611 | SUPPRESS_MEMORY_UNSAFE_CAST[[clang::suppress]] auto& scope = static_cast<GlobalScope&>(*globalScope); |
| 612 | m_globalScopeWrapper.set(*m_vm, JSGlobalScope::create(*m_vm, structure, scope, proxy)); |
| 613 | contextPrototypeStructure->setGlobalObject(*m_vm, m_globalScopeWrapper.get()); |
| 614 | ASSERT(structure->globalObject() == m_globalScopeWrapper)((void)0); |
| 615 | ASSERT(m_globalScopeWrapper->structure()->globalObject() == m_globalScopeWrapper)((void)0); |
| 616 | contextPrototype->structure()->setGlobalObject(*m_vm, m_globalScopeWrapper.get()); |
| 617 | auto* globalScopePrototype = JSGlobalScope::prototype(*m_vm, *m_globalScopeWrapper.get()); |
| 618 | globalScopePrototype->didBecomePrototype(*m_vm); |
| 619 | contextPrototype->structure()->setPrototypeWithoutTransition(*m_vm, globalScopePrototype); |
| 620 | |
| 621 | proxy->setTarget(*m_vm, m_globalScopeWrapper.get()); |
| 622 | |
| 623 | ASSERT(m_globalScopeWrapper->globalObject() == m_globalScopeWrapper)((void)0); |
| 624 | ASSERT(asObject(m_globalScopeWrapper->getPrototypeDirect())->globalObject() == m_globalScopeWrapper)((void)0); |
| 625 | |
| 626 | m_consoleClient = makeUnique<WorkerConsoleClient>(*globalScope); |
| 627 | m_globalScopeWrapper->setConsoleClient(*m_consoleClient); |
| 628 | // Worklet global scopes previously routed microtasks to the VM's default queue |
| 629 | // We preserve this behavior because AudioWorkletGlobalScope relies on DrainMicrotaskDelayScope to batch |
| 630 | // microtask draining between render quanta, which only controls the VM's default |
| 631 | // queue, not the event loop's queue. |
| 632 | if (!is<WorkletGlobalScope>(m_globalScope)) |
| 633 | globalScope->addMicrotaskGlobalObject(m_globalScopeWrapper.get()); |
| 634 | } |
| 635 | |
| 636 | void WorkerOrWorkletScriptController::initScript() |
| 637 | { |
| 638 | ASSERT(m_vm.get())((void)0); |
| 639 | JSC::DeferTermination deferTermination(*m_vm.get()); |
| 640 | |
| 641 | if (is<DedicatedWorkerGlobalScope>(m_globalScope)) { |
| 642 | initScriptWithSubclass<JSDedicatedWorkerGlobalScopePrototype, JSDedicatedWorkerGlobalScope, DedicatedWorkerGlobalScope>(); |
| 643 | return; |
| 644 | } |
| 645 | |
| 646 | if (is<SharedWorkerGlobalScope>(m_globalScope)) { |
| 647 | initScriptWithSubclass<JSSharedWorkerGlobalScopePrototype, JSSharedWorkerGlobalScope, SharedWorkerGlobalScope>(); |
| 648 | return; |
| 649 | } |
| 650 | |
| 651 | if (is<ServiceWorkerGlobalScope>(m_globalScope)) { |
| 652 | initScriptWithSubclass<JSServiceWorkerGlobalScopePrototype, JSServiceWorkerGlobalScope, ServiceWorkerGlobalScope>(); |
| 653 | return; |
| 654 | } |
| 655 | |
| 656 | if (is<PaintWorkletGlobalScope>(m_globalScope)) { |
| 657 | initScriptWithSubclass<JSPaintWorkletGlobalScopePrototype, JSPaintWorkletGlobalScope, PaintWorkletGlobalScope>(); |
| 658 | return; |
| 659 | } |
| 660 | |
| 661 | #if ENABLE(WEB_AUDIO)(defined 1 && 1) |
| 662 | if (is<AudioWorkletGlobalScope>(m_globalScope)) { |
| 663 | initScriptWithSubclass<JSAudioWorkletGlobalScopePrototype, JSAudioWorkletGlobalScope, AudioWorkletGlobalScope>(); |
| 664 | return; |
| 665 | } |
| 666 | #endif |
| 667 | |
| 668 | ASSERT_NOT_REACHED()((void)0); |
| 669 | } |
| 670 | |
| 671 | } // namespace WebCore |