WorkerModuleLoader.cpp (9625B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WorkerModuleLoader.h" 8 9 #include "js/experimental/JSStencil.h" // JS::Stencil, JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil 10 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 11 #include "js/loader/ModuleLoadRequest.h" 12 #include "mozilla/dom/RequestBinding.h" 13 #include "mozilla/dom/WorkerLoadContext.h" 14 #include "mozilla/dom/WorkerPrivate.h" 15 #include "mozilla/dom/WorkerScope.h" 16 #include "mozilla/dom/workerinternals/ScriptLoader.h" 17 #include "nsISupportsImpl.h" 18 19 namespace mozilla::dom::workerinternals::loader { 20 21 ////////////////////////////////////////////////////////////// 22 // WorkerModuleLoader 23 ////////////////////////////////////////////////////////////// 24 25 NS_IMPL_ADDREF_INHERITED(WorkerModuleLoader, JS::loader::ModuleLoaderBase) 26 NS_IMPL_RELEASE_INHERITED(WorkerModuleLoader, JS::loader::ModuleLoaderBase) 27 28 NS_IMPL_CYCLE_COLLECTION_INHERITED(WorkerModuleLoader, 29 JS::loader::ModuleLoaderBase) 30 31 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerModuleLoader) 32 NS_INTERFACE_MAP_END_INHERITING(JS::loader::ModuleLoaderBase) 33 34 WorkerModuleLoader::WorkerModuleLoader(WorkerScriptLoader* aScriptLoader, 35 nsIGlobalObject* aGlobalObject) 36 : ModuleLoaderBase(aScriptLoader, aGlobalObject) {} 37 38 nsIURI* WorkerModuleLoader::GetBaseURI() const { 39 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 40 return workerPrivate->GetBaseURI(); 41 } 42 43 nsIURI* WorkerModuleLoader::GetClientReferrerURI() { 44 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer 45 // Step 3. "client": 46 // 2. let referrerSource be environment’s creation URL. 47 // 48 // https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-creation-url 49 // https://html.spec.whatwg.org/multipage/workers.html#set-up-a-worker-environment-settings-object 50 return GetBaseURI(); 51 } 52 53 already_AddRefed<JS::loader::ScriptFetchOptions> 54 WorkerModuleLoader::CreateDefaultScriptFetchOptions() { 55 RefPtr<ScriptFetchOptions> options = ScriptFetchOptions::CreateDefault(); 56 return options.forget(); 57 } 58 59 already_AddRefed<ModuleLoadRequest> WorkerModuleLoader::CreateRequest( 60 JSContext* aCx, nsIURI* aURI, JS::Handle<JSObject*> aModuleRequest, 61 JS::Handle<JS::Value> aHostDefined, JS::Handle<JS::Value> aPayload, 62 bool aIsDynamicImport, ScriptFetchOptions* aOptions, 63 mozilla::dom::ReferrerPolicy aReferrerPolicy, nsIURI* aBaseURL, 64 const mozilla::dom::SRIMetadata& aSriMetadata) { 65 Maybe<ClientInfo> clientInfo = GetGlobalObject()->GetClientInfo(); 66 67 ModuleLoadRequest::Kind kind; 68 RefPtr<WorkerLoadContext> loadContext; 69 ModuleLoadRequest* root = nullptr; 70 if (aIsDynamicImport) { 71 if (!CreateDynamicImportLoader()) { 72 return nullptr; 73 } 74 75 loadContext = new WorkerLoadContext( 76 WorkerLoadContext::Kind::DynamicImport, clientInfo, 77 GetCurrentScriptLoader(), 78 // When dynamic import is supported in ServiceWorkers, 79 // the current plan in onlyExistingCachedResourcesAllowed 80 // is that only existing cached resources will be 81 // allowed. (`import()` will not be used for caching 82 // side effects, but instead a specific method will be 83 // used during installation.) 84 true); 85 86 kind = ModuleLoadRequest::Kind::DynamicImport; 87 } else { 88 MOZ_ASSERT(!aHostDefined.isUndefined()); 89 root = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate()); 90 MOZ_ASSERT(root); 91 WorkerLoadContext* context = root->mLoadContext->AsWorkerContext(); 92 loadContext = new WorkerLoadContext( 93 WorkerLoadContext::Kind::StaticImport, clientInfo, 94 context->mScriptLoader, context->mOnlyExistingCachedResourcesAllowed); 95 kind = ModuleLoadRequest::Kind::StaticImport; 96 } 97 98 JS::ModuleType moduleType = JS::GetModuleRequestType(aCx, aModuleRequest); 99 RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest( 100 moduleType, SRIMetadata(), aBaseURL, loadContext, kind, this, root); 101 102 request->mURL = aURI->GetSpecOrDefault(); 103 request->NoCacheEntryFound(aReferrerPolicy, aOptions, aURI); 104 return request.forget(); 105 } 106 107 bool WorkerModuleLoader::CreateDynamicImportLoader() { 108 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 109 workerPrivate->AssertIsOnWorkerThread(); 110 111 IgnoredErrorResult rv; 112 RefPtr<WorkerScriptLoader> loader = loader::WorkerScriptLoader::Create( 113 workerPrivate, nullptr, nullptr, 114 GetCurrentScriptLoader()->GetWorkerScriptType(), rv); 115 if (NS_WARN_IF(rv.Failed())) { 116 return false; 117 } 118 119 SetScriptLoader(loader); 120 return true; 121 } 122 123 bool WorkerModuleLoader::IsDynamicImportSupported() { 124 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 125 // Not supported for Service Workers. 126 // https://github.com/w3c/ServiceWorker/issues/1585 covers existing discussion 127 // about potentially supporting use of import(). 128 return !workerPrivate->IsServiceWorker(); 129 } 130 131 bool WorkerModuleLoader::IsForServiceWorker() const { 132 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 133 return workerPrivate && workerPrivate->IsServiceWorker(); 134 } 135 136 bool WorkerModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest, 137 nsresult* aRvOut) { 138 return true; 139 } 140 141 nsresult WorkerModuleLoader::StartFetch(ModuleLoadRequest* aRequest) { 142 if (!GetScriptLoaderFor(aRequest)->DispatchLoadScript(aRequest)) { 143 return NS_ERROR_FAILURE; 144 } 145 return NS_OK; 146 } 147 148 nsresult WorkerModuleLoader::CompileFetchedModule( 149 JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::CompileOptions& aOptions, 150 ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleScript) { 151 switch (aRequest->mModuleType) { 152 case JS::ModuleType::Unknown: 153 case JS::ModuleType::Bytes: 154 MOZ_CRASH("Unexpected module type"); 155 case JS::ModuleType::JavaScriptOrWasm: 156 return CompileJavaScriptOrWasmModule(aCx, aOptions, aRequest, 157 aModuleScript); 158 case JS::ModuleType::JSON: 159 return CompileJsonModule(aCx, aOptions, aRequest, aModuleScript); 160 case JS::ModuleType::CSS: 161 MOZ_CRASH("CSS modules are not supported in workers"); 162 } 163 164 MOZ_CRASH("Unhandled module type"); 165 } 166 167 nsresult WorkerModuleLoader::CompileJavaScriptOrWasmModule( 168 JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest, 169 JS::MutableHandle<JSObject*> aModuleScript) { 170 MOZ_ASSERT(aRequest->IsTextSource()); 171 MaybeSourceText maybeSource; 172 nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource, 173 aRequest->mLoadContext.get()); 174 NS_ENSURE_SUCCESS(rv, rv); 175 176 #ifdef NIGHTLY_BUILD 177 if (aRequest->HasWasmMimeTypeEssence()) { 178 auto compile = [&](auto& source) { 179 return JS::CompileWasmModule(aCx, aOptions, source); 180 }; 181 182 auto* wasmModule = maybeSource.mapNonEmpty(compile); 183 if (!wasmModule) { 184 return NS_ERROR_FAILURE; 185 } 186 187 aModuleScript.set(wasmModule); 188 return NS_OK; 189 } 190 #endif 191 192 RefPtr<JS::Stencil> stencil; 193 194 auto compile = [&](auto& source) { 195 return JS::CompileModuleScriptToStencil(aCx, aOptions, source); 196 }; 197 stencil = maybeSource.mapNonEmpty(compile); 198 199 if (!stencil) { 200 return NS_ERROR_FAILURE; 201 } 202 203 JS::InstantiateOptions instantiateOptions(aOptions); 204 aModuleScript.set( 205 JS::InstantiateModuleStencil(aCx, instantiateOptions, stencil)); 206 if (!aModuleScript) { 207 return NS_ERROR_FAILURE; 208 } 209 210 return NS_OK; 211 } 212 213 nsresult WorkerModuleLoader::CompileJsonModule( 214 JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest, 215 JS::MutableHandle<JSObject*> aModuleScript) { 216 MOZ_ASSERT(aRequest->IsTextSource()); 217 MaybeSourceText maybeSource; 218 nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource, 219 aRequest->mLoadContext.get()); 220 NS_ENSURE_SUCCESS(rv, rv); 221 222 auto compile = [&](auto& source) { 223 return JS::CompileJsonModule(aCx, aOptions, source); 224 }; 225 226 auto* jsonModule = maybeSource.mapNonEmpty(compile); 227 if (!jsonModule) { 228 return NS_ERROR_FAILURE; 229 } 230 231 aModuleScript.set(jsonModule); 232 return NS_OK; 233 } 234 235 WorkerScriptLoader* WorkerModuleLoader::GetCurrentScriptLoader() { 236 return static_cast<WorkerScriptLoader*>(mLoader.get()); 237 } 238 239 WorkerScriptLoader* WorkerModuleLoader::GetScriptLoaderFor( 240 ModuleLoadRequest* aRequest) { 241 return aRequest->GetWorkerLoadContext()->mScriptLoader; 242 } 243 244 void WorkerModuleLoader::OnModuleLoadComplete(ModuleLoadRequest* aRequest) { 245 if (aRequest->IsStaticImport()) { 246 return; 247 } 248 249 AutoJSAPI jsapi; 250 if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) { 251 return; 252 } 253 RefPtr<WorkerScriptLoader> requestScriptLoader = GetScriptLoaderFor(aRequest); 254 if (aRequest->IsDynamicImport()) { 255 aRequest->ProcessDynamicImport(); 256 requestScriptLoader->TryShutdown(); 257 } else { 258 requestScriptLoader->MaybeMoveToLoadedList(aRequest); 259 requestScriptLoader->ProcessPendingRequests(jsapi.cx()); 260 } 261 } 262 263 bool WorkerModuleLoader::IsModuleEvaluationAborted( 264 ModuleLoadRequest* aRequest) { 265 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 266 return !workerPrivate || !workerPrivate->GlobalScope() || 267 workerPrivate->GlobalScope()->IsDying(); 268 } 269 270 } // namespace mozilla::dom::workerinternals::loader