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/. */
7 #ifndef mozilla_dom_workers_scriptloader_h__
8 #define mozilla_dom_workers_scriptloader_h__
10 #include "js/loader/ScriptLoadRequest.h"
11 #include "js/loader/ModuleLoadRequest.h"
12 #include "js/loader/ModuleLoaderBase.h"
13 #include "mozilla/dom/WorkerBinding.h"
14 #include "mozilla/dom/WorkerCommon.h"
15 #include "mozilla/dom/WorkerLoadContext.h"
16 #include "mozilla/dom/WorkerRef.h"
17 #include "mozilla/dom/workerinternals/WorkerModuleLoader.h"
18 #include "mozilla/Maybe.h"
19 #include "nsIContentPolicy.h"
20 #include "nsStringFwd.h"
21 #include "nsTArrayForwardDeclare.h"
24 class nsICookieJarSettings
;
27 class nsIReferrerInfo
;
38 struct WorkerLoadInfo
;
40 class SerializedStackHolder
;
42 enum WorkerScriptType
{ WorkerScript
, DebuggerScript
};
44 namespace workerinternals
{
47 class ScriptExecutorRunnable
;
48 class ScriptLoaderRunnable
;
49 class CachePromiseHandler
;
50 class CacheLoadHandler
;
52 class NetworkLoadHandler
;
55 * [DOMDOC] WorkerScriptLoader
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.
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.
70 * The flow of requests across the boundaries looks like this:
72 * +----------------------------+
73 * | new WorkerScriptLoader(..) |
74 * +----------------------------+
77 * +-------------------------------------------+
78 * | WorkerScriptLoader::DispatchLoadScripts() |
79 * +-------------------------------------------+
82 * +............................+
83 * | new ScriptLoaderRunnable() |
84 * +............................+
87 * #####################################################################
89 * #####################################################################
92 * +.............................+ For each: Is a normal Worker?
93 * | ScriptLoaderRunnable::Run() |----------------------------------+
94 * +.............................+ |
96 * | +----------------------------------+
97 * | | WorkerScriptLoader::LoadScript() |
98 * | +----------------------------------+
100 * | For each request: Is a ServiceWorker? |
103 * +==================+ No script in cache? +====================+
104 * | CacheLoadHandler |------------------------>| NetworkLoadHandler |
105 * +==================+ +====================+
107 * : Loaded from Cache : Loaded by Network
108 * : +..........................................+ :
109 * +---| ScriptLoaderRunnable::OnStreamComplete() |<----+
110 * +..........................................+
112 * | A request is ready, is it in post order?
114 * | call DispatchPendingProcessRequests()
115 * | This creates ScriptExecutorRunnable
116 * +..............................+
117 * | new ScriptLoaderExecutable() |
118 * +..............................+
121 * #####################################################################
122 * Enter worker thread
123 * #####################################################################
126 * +...............................+ All Scripts Executed?
127 * | ScriptLoaderExecutable::Run() | -------------+
128 * +...............................+ :
130 * : yes. Do execution
134 * +--------------------------------------------+
135 * | WorkerScriptLoader::ShutdownScriptLoader() |
136 * +--------------------------------------------+
139 class WorkerScriptLoader
: public JS::loader::ScriptLoaderInterface
,
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
;
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
;
158 bool mExecutionAborted
= false;
159 bool mMutedErrorFlag
= false;
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
;
166 // Worker cancellation related Mutex
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.
174 Mutex
& CleanUpLock() MOZ_RETURN_CAPABILITY(mCleanUpLock
) {
178 bool CleanedUp() const MOZ_REQUIRES(mCleanUpLock
) {
179 mCleanUpLock
.AssertCurrentThreadOwns();
183 // Ensure the worker and the main thread won't race to access |mCleanedUp|.
184 // Should be a MutexSingleWriter, but that causes a lot of issues when you
185 // expose the lock via Lock().
189 NS_DECL_THREADSAFE_ISUPPORTS
191 WorkerScriptLoader(WorkerPrivate
* aWorkerPrivate
,
192 UniquePtr
<SerializedStackHolder
> aOriginStack
,
193 nsISerialEventTarget
* aSyncLoopTarget
,
194 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
);
196 bool CreateScriptRequests(const nsTArray
<nsString
>& aScriptURLs
,
197 const mozilla::Encoding
* aDocumentEncoding
,
200 ScriptLoadRequest
* GetMainScript();
202 already_AddRefed
<ScriptLoadRequest
> CreateScriptLoadRequest(
203 const nsString
& aScriptURL
, const mozilla::Encoding
* aDocumentEncoding
,
206 bool DispatchLoadScript(ScriptLoadRequest
* aRequest
);
208 bool DispatchLoadScripts();
212 WorkerScriptType
GetWorkerScriptType() { return mWorkerScriptType
; }
215 nsIURI
* GetBaseURI() const override
;
217 nsIURI
* GetInitialBaseURI();
219 nsIGlobalObject
* GetGlobal();
221 void MaybeMoveToLoadedList(ScriptLoadRequest
* aRequest
);
225 bool ProcessPendingRequests(JSContext
* aCx
);
227 bool AllScriptsExecuted() {
228 return mLoadingRequests
.isEmpty() && mLoadedRequests
.isEmpty();
231 bool IsDebuggerScript() const { return mWorkerScriptType
== DebuggerScript
; }
233 void SetController(const Maybe
<ServiceWorkerDescriptor
>& aDescriptor
) {
234 mController
= aDescriptor
;
237 Maybe
<ServiceWorkerDescriptor
>& GetController() { return mController
; }
239 nsresult
LoadScript(ThreadSafeRequestHandle
* aRequestHandle
);
241 void ShutdownScriptLoader(bool aResult
, bool aMutedError
);
244 ~WorkerScriptLoader() = default;
247 GetName(nsACString
& aName
) override
{
248 aName
.AssignLiteral("WorkerScriptLoader");
252 void InitModuleLoader();
254 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> GetLoadingList();
256 bool IsDynamicImport(ScriptLoadRequest
* aRequest
);
258 nsContentPolicyType
GetContentPolicyType(ScriptLoadRequest
* aRequest
);
260 bool EvaluateScript(JSContext
* aCx
, ScriptLoadRequest
* aRequest
);
262 nsresult
FillCompileOptionsForRequest(
263 JSContext
* cx
, ScriptLoadRequest
* aRequest
, JS::CompileOptions
* aOptions
,
264 JS::MutableHandle
<JSScript
*> aIntroductionScript
) override
;
266 void ReportErrorToConsole(ScriptLoadRequest
* aRequest
,
267 nsresult aResult
) const override
;
269 // Only used by import maps, crash if we get here.
270 void ReportWarningToConsole(
271 ScriptLoadRequest
* aRequest
, const char* aMessageName
,
272 const nsTArray
<nsString
>& aParams
= nsTArray
<nsString
>()) const override
{
273 MOZ_CRASH("Import maps have not been implemented for this context");
276 void LogExceptionToConsole(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
);
278 bool AllModuleRequestsLoaded() const;
279 void IncreaseLoadingModuleRequestCount();
280 void DecreaseLoadingModuleRequestCount();
283 /* ScriptLoaderRunnable
285 * Responsibilities of this class:
286 * - the actual dispatch
287 * - delegating the load back to WorkerScriptLoader
288 * - handling the collections of scripts being requested
289 * - handling main thread cancellation
290 * - dispatching back to the worker thread
292 class ScriptLoaderRunnable final
: public nsIRunnable
, public nsINamed
{
293 RefPtr
<WorkerScriptLoader
> mScriptLoader
;
294 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
295 nsTArrayView
<RefPtr
<ThreadSafeRequestHandle
>> mLoadingRequests
;
296 Maybe
<nsresult
> mCancelMainThread
;
297 RefPtr
<CacheCreator
> mCacheCreator
;
300 NS_DECL_THREADSAFE_ISUPPORTS
302 explicit ScriptLoaderRunnable(
303 WorkerScriptLoader
* aScriptLoader
,
304 nsTArray
<RefPtr
<ThreadSafeRequestHandle
>> aLoadingRequests
);
306 nsresult
OnStreamComplete(ThreadSafeRequestHandle
* aRequestHandle
,
309 void LoadingFinished(ThreadSafeRequestHandle
* aRequestHandle
, nsresult aRv
);
311 void MaybeExecuteFinishedScripts(ThreadSafeRequestHandle
* aRequestHandle
);
313 bool IsCancelled() { return mCancelMainThread
.isSome(); }
315 nsresult
GetCancelResult() {
316 return (IsCancelled()) ? mCancelMainThread
.ref() : NS_OK
;
319 void CancelMainThreadWithBindingAborted();
321 CacheCreator
* GetCacheCreator() { return mCacheCreator
; };
324 ~ScriptLoaderRunnable() = default;
326 void CancelMainThread(nsresult aCancelResult
);
328 void DispatchProcessPendingRequests();
334 GetName(nsACString
& aName
) override
{
335 aName
.AssignLiteral("ScriptLoaderRunnable");
340 } // namespace loader
342 nsresult
ChannelFromScriptURLMainThread(
343 nsIPrincipal
* aPrincipal
, Document
* aParentDoc
, nsILoadGroup
* aLoadGroup
,
344 nsIURI
* aScriptURL
, const WorkerType
& aWorkerType
,
345 const RequestCredentials
& aCredentials
,
346 const Maybe
<ClientInfo
>& aClientInfo
,
347 nsContentPolicyType aContentPolicyType
,
348 nsICookieJarSettings
* aCookieJarSettings
, nsIReferrerInfo
* aReferrerInfo
,
349 nsIChannel
** aChannel
);
351 nsresult
ChannelFromScriptURLWorkerThread(
352 JSContext
* aCx
, WorkerPrivate
* aParent
, const nsAString
& aScriptURL
,
353 const WorkerType
& aWorkerType
, const RequestCredentials
& aCredentials
,
354 WorkerLoadInfo
& aLoadInfo
);
356 void ReportLoadError(ErrorResult
& aRv
, nsresult aLoadResult
,
357 const nsAString
& aScriptURL
);
359 void LoadMainScript(WorkerPrivate
* aWorkerPrivate
,
360 UniquePtr
<SerializedStackHolder
> aOriginStack
,
361 const nsAString
& aScriptURL
,
362 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
,
363 const mozilla::Encoding
* aDocumentEncoding
);
365 void Load(WorkerPrivate
* aWorkerPrivate
,
366 UniquePtr
<SerializedStackHolder
> aOriginStack
,
367 const nsTArray
<nsString
>& aScriptURLs
,
368 WorkerScriptType aWorkerScriptType
, ErrorResult
& aRv
);
370 } // namespace workerinternals
373 } // namespace mozilla
375 #endif /* mozilla_dom_workers_scriptloader_h__ */