ScriptLoader.h (13920B)
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 #ifndef mozilla_dom_workers_scriptloader_h__ 8 #define mozilla_dom_workers_scriptloader_h__ 9 10 #include "js/loader/ModuleLoaderBase.h" 11 #include "js/loader/ScriptLoadRequest.h" 12 #include "js/loader/ScriptLoadRequestList.h" 13 #include "mozilla/Maybe.h" 14 #include "mozilla/dom/WorkerBinding.h" 15 #include "mozilla/dom/WorkerCommon.h" 16 #include "mozilla/dom/WorkerLoadContext.h" 17 #include "mozilla/dom/WorkerRef.h" 18 #include "mozilla/dom/workerinternals/WorkerModuleLoader.h" 19 #include "nsIContentPolicy.h" 20 #include "nsStringFwd.h" 21 #include "nsTArrayForwardDeclare.h" 22 23 class nsIChannel; 24 class nsICookieJarSettings; 25 class nsILoadGroup; 26 class nsIPrincipal; 27 class nsIReferrerInfo; 28 class nsIURI; 29 30 namespace mozilla { 31 32 class ErrorResult; 33 34 namespace dom { 35 36 class ClientInfo; 37 class Document; 38 struct WorkerLoadInfo; 39 class WorkerPrivate; 40 class SerializedStackHolder; 41 42 enum WorkerScriptType { WorkerScript, DebuggerScript }; 43 44 namespace workerinternals { 45 46 namespace loader { 47 class ScriptExecutorRunnable; 48 class ScriptLoaderRunnable; 49 class CachePromiseHandler; 50 class CacheLoadHandler; 51 class CacheCreator; 52 class NetworkLoadHandler; 53 54 /* 55 * [DOMDOC] WorkerScriptLoader 56 * 57 * The WorkerScriptLoader is the primary class responsible for loading all 58 * Workers, including: ServiceWorkers, SharedWorkers, RemoteWorkers, and 59 * dedicated Workers. Our implementation also includes a subtype of dedicated 60 * workers: ChromeWorker, which exposes information that isn't normally 61 * accessible on a dedicated worker. See [1] for more information. 62 * 63 * Due to constraints around fetching, this class currently delegates the 64 * "Fetch" portion of its work load to the main thread. Unlike the DOM 65 * ScriptLoader, the WorkerScriptLoader is not persistent and is not reused for 66 * subsequent loads. That means for each iteration of loading (for example, 67 * loading the main script, followed by a load triggered by ImportScripts), we 68 * recreate this class, and handle the case independently. 69 * 70 * The flow of requests across the boundaries looks like this: 71 * 72 * +----------------------------+ 73 * | new WorkerScriptLoader(..) | 74 * +----------------------------+ 75 * | 76 * V 77 * +-------------------------------------------+ 78 * | WorkerScriptLoader::DispatchLoadScripts() | 79 * +-------------------------------------------+ 80 * | 81 * V 82 * +............................+ 83 * | new ScriptLoaderRunnable() | 84 * +............................+ 85 * : 86 * V 87 * ##################################################################### 88 * Enter Main thread 89 * ##################################################################### 90 * : 91 * V 92 * +.............................+ For each: Is a normal Worker? 93 * | ScriptLoaderRunnable::Run() |----------------------------------+ 94 * +.............................+ | 95 * | V 96 * | +----------------------------------+ 97 * | | WorkerScriptLoader::LoadScript() | 98 * | +----------------------------------+ 99 * | | 100 * | For each request: Is a ServiceWorker? | 101 * | | 102 * V V 103 * +==================+ No script in cache? +====================+ 104 * | CacheLoadHandler |------------------------>| NetworkLoadHandler | 105 * +==================+ +====================+ 106 * : : 107 * : Loaded from Cache : Loaded by Network 108 * : +..........................................+ : 109 * +---| ScriptLoaderRunnable::OnStreamComplete() |<----+ 110 * +..........................................+ 111 * | 112 * | A request is ready, is it in post order? 113 * | 114 * | call DispatchPendingProcessRequests() 115 * | This creates ScriptExecutorRunnable 116 * +..............................+ 117 * | new ScriptLoaderExecutable() | 118 * +..............................+ 119 * : 120 * V 121 * ##################################################################### 122 * Enter worker thread 123 * ##################################################################### 124 * : 125 * V 126 * +...............................+ All Scripts Executed? 127 * | ScriptLoaderExecutable::Run() | -------------+ 128 * +...............................+ : 129 * : 130 * : yes. Do execution 131 * : then shutdown. 132 * : 133 * V 134 * +--------------------------------------------+ 135 * | WorkerScriptLoader::ShutdownScriptLoader() | 136 * +--------------------------------------------+ 137 */ 138 139 class WorkerScriptLoader : public JS::loader::ScriptLoaderInterface, 140 public nsINamed { 141 friend class ScriptExecutorRunnable; 142 friend class ScriptLoaderRunnable; 143 friend class CachePromiseHandler; 144 friend class CacheLoadHandler; 145 friend class CacheCreator; 146 friend class NetworkLoadHandler; 147 friend class WorkerModuleLoader; 148 149 RefPtr<ThreadSafeWorkerRef> mWorkerRef; 150 UniquePtr<SerializedStackHolder> mOriginStack; 151 nsString mOriginStackJSON; 152 nsCOMPtr<nsISerialEventTarget> mSyncLoopTarget; 153 ScriptLoadRequestList mLoadingRequests; 154 ScriptLoadRequestList mLoadedRequests; 155 Maybe<ServiceWorkerDescriptor> mController; 156 WorkerScriptType mWorkerScriptType; 157 ErrorResult& mRv; 158 bool mExecutionAborted = false; 159 bool mMutedErrorFlag = false; 160 161 // Count of loading module requests. mLoadingRequests doesn't keep track of 162 // child module requests. 163 // This member should be accessed on worker thread. 164 uint32_t mLoadingModuleRequestCount; 165 166 // Worker cancellation related Mutex 167 // 168 // Modified on the worker thread. 169 // It is ok to *read* this without a lock on the worker. 170 // Main thread must always acquire a lock. 171 bool mCleanedUp MOZ_GUARDED_BY( 172 mCleanUpLock); // To specify if the cleanUp() has been done. 173 174 Mutex& CleanUpLock() MOZ_RETURN_CAPABILITY(mCleanUpLock) { 175 return mCleanUpLock; 176 } 177 178 bool CleanedUp() const MOZ_REQUIRES(mCleanUpLock) { 179 mCleanUpLock.AssertCurrentThreadOwns(); 180 return mCleanedUp; 181 } 182 183 // Ensure the worker and the main thread won't race to access |mCleanedUp|. 184 // This should perhaps be a EventTargetAndLockCapability to model the 185 // reader/writer behaviour on mCleanedUp better. 186 Mutex mCleanUpLock; 187 188 public: 189 NS_DECL_THREADSAFE_ISUPPORTS 190 191 static already_AddRefed<WorkerScriptLoader> Create( 192 WorkerPrivate* aWorkerPrivate, 193 UniquePtr<SerializedStackHolder> aOriginStack, 194 nsISerialEventTarget* aSyncLoopTarget, WorkerScriptType aWorkerScriptType, 195 ErrorResult& aRv); 196 197 bool CreateScriptRequests(const nsTArray<nsString>& aScriptURLs, 198 const mozilla::Encoding* aDocumentEncoding, 199 bool aIsMainScript); 200 201 ScriptLoadRequest* GetMainScript(); 202 203 already_AddRefed<ScriptLoadRequest> CreateScriptLoadRequest( 204 const nsString& aScriptURL, const mozilla::Encoding* aDocumentEncoding, 205 bool aIsMainScript, nsresult* aRv); 206 207 bool DispatchLoadScript(ScriptLoadRequest* aRequest); 208 209 bool DispatchLoadScripts( 210 nsTArray<RefPtr<ThreadSafeRequestHandle>>&& aLoadingList = {}); 211 212 void TryShutdown(); 213 214 WorkerScriptType GetWorkerScriptType() { return mWorkerScriptType; } 215 216 protected: 217 nsIURI* GetBaseURI() const override; 218 219 nsIURI* GetInitialBaseURI(); 220 221 nsIGlobalObject* GetGlobal(); 222 223 void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest); 224 225 bool StoreCSP(); 226 227 bool ProcessPendingRequests(JSContext* aCx); 228 229 bool AllScriptsExecuted() { 230 return mLoadingRequests.isEmpty() && mLoadedRequests.isEmpty(); 231 } 232 233 bool IsDebuggerScript() const { return mWorkerScriptType == DebuggerScript; } 234 235 void SetController(const Maybe<ServiceWorkerDescriptor>& aDescriptor) { 236 mController = aDescriptor; 237 } 238 239 Maybe<ServiceWorkerDescriptor>& GetController() { return mController; } 240 241 nsresult LoadScript(ThreadSafeRequestHandle* aRequestHandle); 242 243 void ShutdownScriptLoader(bool aResult, bool aMutedError); 244 245 private: 246 WorkerScriptLoader(UniquePtr<SerializedStackHolder> aOriginStack, 247 nsISerialEventTarget* aSyncLoopTarget, 248 WorkerScriptType aWorkerScriptType, ErrorResult& aRv); 249 250 ~WorkerScriptLoader() = default; 251 252 NS_IMETHOD 253 GetName(nsACString& aName) override { 254 aName.AssignLiteral("WorkerScriptLoader"); 255 return NS_OK; 256 } 257 258 void InitModuleLoader(); 259 260 nsTArray<RefPtr<ThreadSafeRequestHandle>> GetLoadingList(); 261 262 bool IsDynamicImport(ScriptLoadRequest* aRequest); 263 264 nsContentPolicyType GetContentPolicyType(ScriptLoadRequest* aRequest); 265 266 bool EvaluateScript(JSContext* aCx, ScriptLoadRequest* aRequest); 267 268 nsresult FillCompileOptionsForRequest( 269 JSContext* cx, ScriptLoadRequest* aRequest, JS::CompileOptions* aOptions, 270 JS::MutableHandle<JSScript*> aIntroductionScript) override; 271 272 void ReportErrorToConsole(ScriptLoadRequest* aRequest, 273 nsresult aResult) const override; 274 275 // Only used by import maps, crash if we get here. 276 void ReportWarningToConsole( 277 ScriptLoadRequest* aRequest, const char* aMessageName, 278 const nsTArray<nsString>& aParams = nsTArray<nsString>()) const override { 279 MOZ_CRASH("Import maps have not been implemented for this context"); 280 } 281 282 void LogExceptionToConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate); 283 284 bool AllModuleRequestsLoaded() const; 285 void IncreaseLoadingModuleRequestCount(); 286 void DecreaseLoadingModuleRequestCount(); 287 }; 288 289 /* ScriptLoaderRunnable 290 * 291 * Responsibilities of this class: 292 * - the actual dispatch 293 * - delegating the load back to WorkerScriptLoader 294 * - handling the collections of scripts being requested 295 * - handling main thread cancellation 296 * - dispatching back to the worker thread 297 */ 298 class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed { 299 RefPtr<WorkerScriptLoader> mScriptLoader; 300 RefPtr<ThreadSafeWorkerRef> mWorkerRef; 301 nsTArrayView<RefPtr<ThreadSafeRequestHandle>> mLoadingRequests; 302 Maybe<nsresult> mCancelMainThread; 303 RefPtr<CacheCreator> mCacheCreator; 304 305 public: 306 NS_DECL_THREADSAFE_ISUPPORTS 307 308 explicit ScriptLoaderRunnable( 309 WorkerScriptLoader* aScriptLoader, 310 nsTArray<RefPtr<ThreadSafeRequestHandle>> aLoadingRequests); 311 312 nsresult OnStreamComplete(ThreadSafeRequestHandle* aRequestHandle, 313 nsresult aStatus); 314 315 void LoadingFinished(ThreadSafeRequestHandle* aRequestHandle, nsresult aRv); 316 317 void MaybeExecuteFinishedScripts(ThreadSafeRequestHandle* aRequestHandle); 318 319 bool IsCancelled() { return mCancelMainThread.isSome(); } 320 321 nsresult GetCancelResult() { 322 return (IsCancelled()) ? mCancelMainThread.ref() : NS_OK; 323 } 324 325 void CancelMainThreadWithBindingAborted(); 326 327 CacheCreator* GetCacheCreator() { return mCacheCreator; }; 328 329 private: 330 ~ScriptLoaderRunnable() = default; 331 332 void CancelMainThread(nsresult aCancelResult); 333 334 void DispatchProcessPendingRequests(); 335 336 NS_IMETHOD 337 Run() override; 338 339 NS_IMETHOD 340 GetName(nsACString& aName) override { 341 aName.AssignLiteral("ScriptLoaderRunnable"); 342 return NS_OK; 343 } 344 }; 345 346 } // namespace loader 347 348 nsresult ChannelFromScriptURLMainThread( 349 nsIPrincipal* aPrincipal, Document* aParentDoc, nsILoadGroup* aLoadGroup, 350 nsIURI* aScriptURL, const WorkerType& aWorkerType, 351 const RequestCredentials& aCredentials, 352 const Maybe<ClientInfo>& aClientInfo, 353 nsContentPolicyType aContentPolicyType, 354 nsICookieJarSettings* aCookieJarSettings, nsIReferrerInfo* aReferrerInfo, 355 nsIChannel** aChannel); 356 357 nsresult ChannelFromScriptURLWorkerThread( 358 JSContext* aCx, WorkerPrivate* aParent, const nsAString& aScriptURL, 359 const WorkerType& aWorkerType, const RequestCredentials& aCredentials, 360 WorkerLoadInfo& aLoadInfo); 361 362 void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult, 363 const nsAString& aScriptURL); 364 365 void LoadMainScript(WorkerPrivate* aWorkerPrivate, 366 UniquePtr<SerializedStackHolder> aOriginStack, 367 const nsAString& aScriptURL, 368 WorkerScriptType aWorkerScriptType, ErrorResult& aRv, 369 const mozilla::Encoding* aDocumentEncoding); 370 371 void Load(WorkerPrivate* aWorkerPrivate, 372 UniquePtr<SerializedStackHolder> aOriginStack, 373 const nsTArray<nsString>& aScriptURLs, 374 WorkerScriptType aWorkerScriptType, ErrorResult& aRv); 375 376 } // namespace workerinternals 377 378 } // namespace dom 379 } // namespace mozilla 380 381 #endif /* mozilla_dom_workers_scriptloader_h__ */