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_ScriptLoader_h
8 #define mozilla_dom_ScriptLoader_h
9 
10 #include "nsCOMPtr.h"
11 #include "nsRefPtrHashtable.h"
12 #include "mozilla/Encoding.h"
13 #include "nsIScriptElement.h"
14 #include "nsCOMArray.h"
15 #include "nsCycleCollectionParticipant.h"
16 #include "nsTArray.h"
17 #include "mozilla/dom/Document.h"
18 #include "nsIIncrementalStreamLoader.h"
19 #include "nsURIHashKey.h"
20 #include "mozilla/CORSMode.h"
21 #include "mozilla/dom/ScriptLoadRequest.h"
22 #include "mozilla/dom/SRIMetadata.h"
23 #include "mozilla/dom/SRICheck.h"
24 #include "mozilla/MaybeOneOf.h"
25 #include "mozilla/MozPromise.h"
26 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
27 #include "mozilla/Vector.h"
28 #include "ScriptKind.h"
29 
30 class nsIURI;
31 
32 namespace JS {
33 
34 template <typename UnitT>
35 class SourceText;
36 
37 }  // namespace JS
38 
39 namespace mozilla {
40 namespace dom {
41 
42 class AutoJSAPI;
43 class LoadedScript;
44 class ModuleLoadRequest;
45 class ModuleScript;
46 class ScriptLoadHandler;
47 class ScriptRequestProcessor;
48 
49 //////////////////////////////////////////////////////////////
50 // Script loader implementation
51 //////////////////////////////////////////////////////////////
52 
53 class ScriptLoader final : public nsISupports {
54   class MOZ_STACK_CLASS AutoCurrentScriptUpdater {
55    public:
AutoCurrentScriptUpdater(ScriptLoader * aScriptLoader,nsIScriptElement * aCurrentScript)56     AutoCurrentScriptUpdater(ScriptLoader* aScriptLoader,
57                              nsIScriptElement* aCurrentScript)
58         : mOldScript(aScriptLoader->mCurrentScript),
59           mScriptLoader(aScriptLoader) {
60       nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentScript);
61       mScriptLoader->mCurrentScript =
62           node && !node->IsInShadowTree() ? aCurrentScript : nullptr;
63     }
64 
~AutoCurrentScriptUpdater()65     ~AutoCurrentScriptUpdater() {
66       mScriptLoader->mCurrentScript.swap(mOldScript);
67     }
68 
69    private:
70     nsCOMPtr<nsIScriptElement> mOldScript;
71     ScriptLoader* mScriptLoader;
72   };
73 
74   friend class ModuleLoadRequest;
75   friend class ScriptRequestProcessor;
76   friend class ScriptLoadHandler;
77   friend class AutoCurrentScriptUpdater;
78 
79  public:
80   explicit ScriptLoader(Document* aDocument);
81 
82   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(ScriptLoader)83   NS_DECL_CYCLE_COLLECTION_CLASS(ScriptLoader)
84 
85   /**
86    * The loader maintains a weak reference to the document with
87    * which it is initialized. This call forces the reference to
88    * be dropped.
89    */
90   void DropDocumentReference() { mDocument = nullptr; }
91 
92   /**
93    * Add an observer for all scripts loaded through this loader.
94    *
95    * @param aObserver observer for all script processing.
96    */
AddObserver(nsIScriptLoaderObserver * aObserver)97   nsresult AddObserver(nsIScriptLoaderObserver* aObserver) {
98     return mObservers.AppendObject(aObserver) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
99   }
100 
101   /**
102    * Remove an observer.
103    *
104    * @param aObserver observer to be removed
105    */
RemoveObserver(nsIScriptLoaderObserver * aObserver)106   void RemoveObserver(nsIScriptLoaderObserver* aObserver) {
107     mObservers.RemoveObject(aObserver);
108   }
109 
110   /**
111    * Process a script element. This will include both loading the
112    * source of the element if it is not inline and evaluating
113    * the script itself.
114    *
115    * If the script is an inline script that can be executed immediately
116    * (i.e. there are no other scripts pending) then ScriptAvailable
117    * and ScriptEvaluated will be called before the function returns.
118    *
119    * If true is returned the script could not be executed immediately.
120    * In this case ScriptAvailable is guaranteed to be called at a later
121    * point (as well as possibly ScriptEvaluated).
122    *
123    * @param aElement The element representing the script to be loaded and
124    *        evaluated.
125    */
126   bool ProcessScriptElement(nsIScriptElement* aElement);
127 
128   /**
129    * Gets the currently executing script. This is useful if you want to
130    * generate a unique key based on the currently executing script.
131    */
GetCurrentScript()132   nsIScriptElement* GetCurrentScript() { return mCurrentScript; }
133 
GetCurrentParserInsertedScript()134   nsIScriptElement* GetCurrentParserInsertedScript() {
135     return mCurrentParserInsertedScript;
136   }
137 
138   /**
139    * Whether the loader is enabled or not.
140    * When disabled, processing of new script elements is disabled.
141    * Any call to ProcessScriptElement() will return false. Note that
142    * this DOES NOT disable currently loading or executing scripts.
143    */
GetEnabled()144   bool GetEnabled() { return mEnabled; }
145 
SetEnabled(bool aEnabled)146   void SetEnabled(bool aEnabled) {
147     if (!mEnabled && aEnabled) {
148       ProcessPendingRequestsAsync();
149     }
150     mEnabled = aEnabled;
151   }
152 
153   /**
154    * Add/remove a blocker for parser-blocking scripts (and XSLT
155    * scripts). Blockers will stop such scripts from executing, but not from
156    * loading.
157    */
AddParserBlockingScriptExecutionBlocker()158   void AddParserBlockingScriptExecutionBlocker() {
159     ++mParserBlockingBlockerCount;
160   }
161 
RemoveParserBlockingScriptExecutionBlocker()162   void RemoveParserBlockingScriptExecutionBlocker() {
163     if (!--mParserBlockingBlockerCount && ReadyToExecuteScripts()) {
164       ProcessPendingRequestsAsync();
165     }
166   }
167 
168   /**
169    * Add/remove a blocker for execution of all scripts.  Blockers will stop
170    * scripts from executing, but not from loading.
171    */
AddExecuteBlocker()172   void AddExecuteBlocker() { ++mBlockerCount; }
173 
RemoveExecuteBlocker()174   void RemoveExecuteBlocker() {
175     MOZ_ASSERT(mBlockerCount);
176     if (!--mBlockerCount) {
177       ProcessPendingRequestsAsync();
178     }
179   }
180 
181   /**
182    * Convert the given buffer to a UTF-16 string.  If the buffer begins with a
183    * BOM, it is interpreted as that encoding; otherwise the first of |aChannel|,
184    * |aHintCharset|, or |aDocument| that provides a recognized encoding is used,
185    * or Windows-1252 if none of them do.
186    *
187    * Encoding errors in the buffer are converted to replacement characters, so
188    * allocation failure is the only way this function can fail.
189    *
190    * @param aChannel     Channel corresponding to the data. May be null.
191    * @param aData        The data to convert
192    * @param aLength      Length of the data
193    * @param aHintCharset Character set hint (e.g., from a charset attribute).
194    * @param aDocument    Document which the data is loaded for. May be null.
195    * @param aBufOut      [out] fresh char16_t array containing data converted to
196    *                     Unicode.  Caller must js_free() this data when finished
197    *                     with it.
198    * @param aLengthOut   [out] Length of array returned in aBufOut in number
199    *                     of char16_t code units.
200    */
201   static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
202                                  uint32_t aLength,
203                                  const nsAString& aHintCharset,
204                                  Document* aDocument, char16_t*& aBufOut,
205                                  size_t& aLengthOut);
206 
ConvertToUTF16(nsIChannel * aChannel,const uint8_t * aData,uint32_t aLength,const nsAString & aHintCharset,Document * aDocument,JS::UniqueTwoByteChars & aBufOut,size_t & aLengthOut)207   static inline nsresult ConvertToUTF16(nsIChannel* aChannel,
208                                         const uint8_t* aData, uint32_t aLength,
209                                         const nsAString& aHintCharset,
210                                         Document* aDocument,
211                                         JS::UniqueTwoByteChars& aBufOut,
212                                         size_t& aLengthOut) {
213     char16_t* bufOut;
214     nsresult rv = ConvertToUTF16(aChannel, aData, aLength, aHintCharset,
215                                  aDocument, bufOut, aLengthOut);
216     if (NS_SUCCEEDED(rv)) {
217       aBufOut.reset(bufOut);
218     }
219     return rv;
220   };
221 
222   /**
223    * Convert the given buffer to a UTF-8 string.  If the buffer begins with a
224    * BOM, it is interpreted as that encoding; otherwise the first of |aChannel|,
225    * |aHintCharset|, or |aDocument| that provides a recognized encoding is used,
226    * or Windows-1252 if none of them do.
227    *
228    * Encoding errors in the buffer are converted to replacement characters, so
229    * allocation failure is the only way this function can fail.
230    *
231    * @param aChannel     Channel corresponding to the data. May be null.
232    * @param aData        The data to convert
233    * @param aLength      Length of the data
234    * @param aHintCharset Character set hint (e.g., from a charset attribute).
235    * @param aDocument    Document which the data is loaded for. May be null.
236    * @param aBufOut      [out] fresh Utf8Unit array containing data converted to
237    *                     Unicode.  Caller must js_free() this data when finished
238    *                     with it.
239    * @param aLengthOut   [out] Length of array returned in aBufOut in UTF-8 code
240    *                     units (i.e. in bytes).
241    */
242   static nsresult ConvertToUTF8(nsIChannel* aChannel, const uint8_t* aData,
243                                 uint32_t aLength, const nsAString& aHintCharset,
244                                 Document* aDocument, Utf8Unit*& aBufOut,
245                                 size_t& aLengthOut);
246 
247   /**
248    * Handle the completion of a stream.  This is called by the
249    * ScriptLoadHandler object which observes the IncrementalStreamLoader
250    * loading the script. The streamed content is expected to be stored on the
251    * aRequest argument.
252    */
253   nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
254                             ScriptLoadRequest* aRequest,
255                             nsresult aChannelStatus, nsresult aSRIStatus,
256                             SRICheckDataVerifier* aSRIDataVerifier);
257 
258   /**
259    * Returns wether any request is queued, and not executed yet.
260    */
261   bool HasPendingRequests();
262 
263   /**
264    * Processes any pending requests that are ready for processing.
265    */
266   void ProcessPendingRequests();
267 
268   /**
269    * Starts deferring deferred scripts and puts them in the mDeferredRequests
270    * queue instead.
271    */
BeginDeferringScripts()272   void BeginDeferringScripts() {
273     mDeferEnabled = true;
274     if (mDeferCheckpointReached) {
275       // We already completed a parse and were just waiting for some async
276       // scripts to load (and were already blocking the load event waiting for
277       // that to happen), when document.open() happened and now we're doing a
278       // new parse.  We shouldn't block the load event again, but _should_ reset
279       // mDeferCheckpointReached to false.  It'll get set to true again when the
280       // DeferCheckpointReached call that corresponds to this
281       // BeginDeferringScripts call happens (on document.close()), since we just
282       // set mDeferEnabled to true.
283       mDeferCheckpointReached = false;
284     } else {
285       if (mDocument) {
286         mDocument->BlockOnload();
287       }
288     }
289   }
290 
291   /**
292    * Notifies the script loader that parsing is done.  If aTerminated is true,
293    * this will drop any pending scripts that haven't run yet, otherwise it will
294    * do nothing.
295    */
296   void ParsingComplete(bool aTerminated);
297 
298   /**
299    * Notifies the script loader that the checkpoint to begin execution of defer
300    * scripts has been reached. This is either the end of of the document parse
301    * or the end of loading of parser-inserted stylesheets, whatever happens
302    * last.
303    *
304    * Otherwise, it will stop deferring scripts and immediately processes the
305    * mDeferredRequests queue.
306    *
307    * WARNING: This function will synchronously execute content scripts, so be
308    * prepared that the world might change around you.
309    */
310   void DeferCheckpointReached();
311 
312   /**
313    * Returns the number of pending scripts, deferred or not.
314    */
HasPendingOrCurrentScripts()315   uint32_t HasPendingOrCurrentScripts() {
316     return mCurrentScript || mParserBlockingRequest;
317   }
318 
319   /**
320    * Adds aURI to the preload list and starts loading it.
321    *
322    * @param aURI The URI of the external script.
323    * @param aCharset The charset parameter for the script.
324    * @param aType The type parameter for the script.
325    * @param aCrossOrigin The crossorigin attribute for the script.
326    *                     Void if not present.
327    * @param aIntegrity The expect hash url, if avail, of the request
328    * @param aScriptFromHead Whether or not the script was a child of head
329    */
330   virtual void PreloadURI(nsIURI* aURI, const nsAString& aCharset,
331                           const nsAString& aType, const nsAString& aCrossOrigin,
332                           const nsAString& aIntegrity, bool aScriptFromHead,
333                           bool aAsync, bool aDefer, bool aNoModule,
334                           bool aLinkPreload,
335                           const ReferrerPolicy aReferrerPolicy);
336 
337   /**
338    * Process a request that was deferred so that the script could be compiled
339    * off thread.
340    */
341   nsresult ProcessOffThreadRequest(ScriptLoadRequest* aRequest);
342 
AddPendingChildLoader(ScriptLoader * aChild)343   bool AddPendingChildLoader(ScriptLoader* aChild) {
344     // XXX(Bug 1631371) Check if this should use a fallible operation as it
345     // pretended earlier. Else, change the return type to void.
346     mPendingChildLoaders.AppendElement(aChild);
347     return true;
348   }
349 
GetDocGroup()350   mozilla::dom::DocGroup* GetDocGroup() const {
351     return mDocument->GetDocGroup();
352   }
353 
354   /**
355    * Register the fact that we saw the load event, and that we need to save the
356    * bytecode at the next loop cycle unless new scripts are waiting in the
357    * pipeline.
358    */
359   void LoadEventFired();
360 
361   /**
362    * Destroy and prevent the ScriptLoader or the ScriptLoadRequests from owning
363    * any references to the JSScript or to the Request which might be used for
364    * caching the encoded bytecode.
365    */
Destroy()366   void Destroy() { GiveUpBytecodeEncoding(); }
367 
368   /**
369    * Implement the HostResolveImportedModule abstract operation.
370    *
371    * Resolve a module specifier string and look this up in the module
372    * map, returning the result. This is only called for previously
373    * loaded modules and always succeeds.
374    *
375    * @param aReferencingPrivate A JS::Value which is either undefined
376    *                            or contains a LoadedScript private pointer.
377    * @param aSpecifier The module specifier.
378    * @param aModuleOut This is set to the module found.
379    */
380   static void ResolveImportedModule(JSContext* aCx,
381                                     JS::Handle<JS::Value> aReferencingPrivate,
382                                     JS::Handle<JSString*> aSpecifier,
383                                     JS::MutableHandle<JSObject*> aModuleOut);
384 
385   void StartDynamicImport(ModuleLoadRequest* aRequest);
386   void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
387   void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
388                            nsresult aResult);
389 
390   /*
391    * Get the currently active script. This is used as the initiating script when
392    * executing timeout handler scripts.
393    */
394   static LoadedScript* GetActiveScript(JSContext* aCx);
395 
GetDocument()396   Document* GetDocument() const { return mDocument; }
397 
398  private:
399   virtual ~ScriptLoader();
400 
401   void EnsureModuleHooksInitialized();
402 
403   ScriptLoadRequest* CreateLoadRequest(ScriptKind aKind, nsIURI* aURI,
404                                        nsIScriptElement* aElement,
405                                        nsIPrincipal* aTriggeringPrincipal,
406                                        mozilla::CORSMode aCORSMode,
407                                        const SRIMetadata& aIntegrity,
408                                        ReferrerPolicy aReferrerPolicy);
409 
410   /**
411    * Unblocks the creator parser of the parser-blocking scripts.
412    */
413   void UnblockParser(ScriptLoadRequest* aParserBlockingRequest);
414 
415   /**
416    * Asynchronously resumes the creator parser of the parser-blocking scripts.
417    */
418   void ContinueParserAsync(ScriptLoadRequest* aParserBlockingRequest);
419 
420   bool ProcessExternalScript(nsIScriptElement* aElement, ScriptKind aScriptKind,
421                              nsAutoString aTypeAttr,
422                              nsIContent* aScriptContent);
423 
424   bool ProcessInlineScript(nsIScriptElement* aElement, ScriptKind aScriptKind);
425 
426   ScriptLoadRequest* LookupPreloadRequest(nsIScriptElement* aElement,
427                                           ScriptKind aScriptKind,
428                                           const SRIMetadata& aSRIMetadata);
429 
430   void GetSRIMetadata(const nsAString& aIntegrityAttr,
431                       SRIMetadata* aMetadataOut);
432 
433   /**
434    * Given a script element, get the referrer policy should be applied to load
435    * requests.
436    */
437   ReferrerPolicy GetReferrerPolicy(nsIScriptElement* aElement);
438 
439   /**
440    * Helper function to check the content policy for a given request.
441    */
442   static nsresult CheckContentPolicy(Document* aDocument, nsISupports* aContext,
443                                      const nsAString& aType,
444                                      ScriptLoadRequest* aRequest);
445 
446   /**
447    * Helper function to determine whether an about: page loads a chrome: URI.
448    * Please note that this function only returns true if:
449    *   * the about: page uses a ContentPrincipal with scheme about:
450    *   * the about: page is not linkable from content
451    *     (e.g. the function will return false for about:blank or about:srcdoc)
452    */
453   static bool IsAboutPageLoadingChromeURI(ScriptLoadRequest* aRequest);
454 
455   /**
456    * Start a load for aRequest's URI.
457    */
458   nsresult StartLoad(ScriptLoadRequest* aRequest);
459 
460   /**
461    * Abort the current stream, and re-start with a new load request from scratch
462    * without requesting any alternate data. Returns NS_BINDING_RETARGETED on
463    * success, as this error code is used to abort the input stream.
464    */
465   nsresult RestartLoad(ScriptLoadRequest* aRequest);
466 
467   void HandleLoadError(ScriptLoadRequest* aRequest, nsresult aResult);
468 
469   /**
470    * Process any pending requests asynchronously (i.e. off an event) if there
471    * are any. Note that this is a no-op if there aren't any currently pending
472    * requests.
473    *
474    * This function is virtual to allow cross-library calls to SetEnabled()
475    */
476   virtual void ProcessPendingRequestsAsync();
477 
478   /**
479    * If true, the loader is ready to execute parser-blocking scripts, and so are
480    * all its ancestors.  If the loader itself is ready but some ancestor is not,
481    * this function will add an execute blocker and ask the ancestor to remove it
482    * once it becomes ready.
483    */
484   bool ReadyToExecuteParserBlockingScripts();
485 
486   /**
487    * Return whether just this loader is ready to execute parser-blocking
488    * scripts.
489    */
SelfReadyToExecuteParserBlockingScripts()490   bool SelfReadyToExecuteParserBlockingScripts() {
491     return ReadyToExecuteScripts() && !mParserBlockingBlockerCount;
492   }
493 
494   /**
495    * Return whether this loader is ready to execute scripts in general.
496    */
ReadyToExecuteScripts()497   bool ReadyToExecuteScripts() { return mEnabled && !mBlockerCount; }
498 
499   nsresult VerifySRI(ScriptLoadRequest* aRequest,
500                      nsIIncrementalStreamLoader* aLoader, nsresult aSRIStatus,
501                      SRICheckDataVerifier* aSRIDataVerifier) const;
502 
503   nsresult SaveSRIHash(ScriptLoadRequest* aRequest,
504                        SRICheckDataVerifier* aSRIDataVerifier) const;
505 
506   void ReportErrorToConsole(ScriptLoadRequest* aRequest,
507                             nsresult aResult) const;
508   void ReportPreloadErrorsToConsole(ScriptLoadRequest* aRequest);
509 
510   nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
511                                      bool* aCouldCompileOut);
512   nsresult ProcessRequest(ScriptLoadRequest* aRequest);
513   void ProcessDynamicImport(ModuleLoadRequest* aRequest);
514   nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
515   void FireScriptAvailable(nsresult aResult, ScriptLoadRequest* aRequest);
516   void FireScriptEvaluated(nsresult aResult, ScriptLoadRequest* aRequest);
517   nsresult EvaluateScript(ScriptLoadRequest* aRequest);
518 
519   /**
520    * Queue the current script load request to be saved, when the page
521    * initialization ends. The page initialization end is defined as being the
522    * time when the load event got received, and when no more scripts are waiting
523    * to be executed.
524    */
525   void RegisterForBytecodeEncoding(ScriptLoadRequest* aRequest);
526 
527   /**
528    * Check if all conditions are met, i-e that the onLoad event fired and that
529    * no more script have to be processed.  If all conditions are met, queue an
530    * event to encode all the bytecode and save them on the cache.
531    */
532   void MaybeTriggerBytecodeEncoding();
533 
534   /**
535    * Iterate over all script load request and save the bytecode of executed
536    * functions on the cache provided by the channel.
537    */
538   void EncodeBytecode();
539   void EncodeRequestBytecode(JSContext* aCx, ScriptLoadRequest* aRequest);
540 
541   void GiveUpBytecodeEncoding();
542 
543   already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();
544   nsresult FillCompileOptionsForRequest(const mozilla::dom::AutoJSAPI& jsapi,
545                                         ScriptLoadRequest* aRequest,
546                                         JS::Handle<JSObject*> aScopeChain,
547                                         JS::CompileOptions* aOptions);
548 
549   uint32_t NumberOfProcessors();
550   nsresult PrepareLoadedRequest(ScriptLoadRequest* aRequest,
551                                 nsIIncrementalStreamLoader* aLoader,
552                                 nsresult aStatus);
553 
554   void AddDeferRequest(ScriptLoadRequest* aRequest);
555   void AddAsyncRequest(ScriptLoadRequest* aRequest);
556   bool MaybeRemovedDeferRequests();
557 
558   void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest);
559 
560   using MaybeSourceText =
561       mozilla::MaybeOneOf<JS::SourceText<char16_t>, JS::SourceText<Utf8Unit>>;
562 
563   // Get source text.  On success |aMaybeSource| will contain either UTF-8 or
564   // UTF-16 source; on failure it will remain in its initial state.
565   MOZ_MUST_USE nsresult GetScriptSource(JSContext* aCx,
566                                         ScriptLoadRequest* aRequest,
567                                         MaybeSourceText* aMaybeSource);
568 
569   void SetModuleFetchStarted(ModuleLoadRequest* aRequest);
570   void SetModuleFetchFinishedAndResumeWaitingRequests(
571       ModuleLoadRequest* aRequest, nsresult aResult);
572 
573   bool IsFetchingModule(ModuleLoadRequest* aRequest) const;
574 
575   bool ModuleMapContainsURL(nsIURI* aURL) const;
576   RefPtr<mozilla::GenericNonExclusivePromise> WaitForModuleFetch(nsIURI* aURL);
577   ModuleScript* GetFetchedModule(nsIURI* aURL) const;
578 
579   friend JSObject* HostResolveImportedModule(
580       JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
581       JS::Handle<JSString*> aSpecifier);
582 
583   // Returns wether we should save the bytecode of this script after the
584   // execution of the script.
585   static bool ShouldCacheBytecode(ScriptLoadRequest* aRequest);
586 
587   nsresult CreateModuleScript(ModuleLoadRequest* aRequest);
588   nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest);
589   void CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest);
590   void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest);
591   bool InstantiateModuleTree(ModuleLoadRequest* aRequest);
592   JS::Value FindFirstParseError(ModuleLoadRequest* aRequest);
593   void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest);
594 
595   RefPtr<mozilla::GenericPromise> StartFetchingModuleAndDependencies(
596       ModuleLoadRequest* aParent, nsIURI* aURI);
597 
598   nsresult InitDebuggerDataForModuleTree(JSContext* aCx,
599                                          ModuleLoadRequest* aRequest);
600 
601   void RunScriptWhenSafe(ScriptLoadRequest* aRequest);
602 
603   Document* mDocument;  // [WEAK]
604   nsCOMArray<nsIScriptLoaderObserver> mObservers;
605   ScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests;
606   // mLoadingAsyncRequests holds async requests while they're loading; when they
607   // have been loaded they are moved to mLoadedAsyncRequests.
608   ScriptLoadRequestList mLoadingAsyncRequests;
609   ScriptLoadRequestList mLoadedAsyncRequests;
610   ScriptLoadRequestList mDeferRequests;
611   ScriptLoadRequestList mXSLTRequests;
612   ScriptLoadRequestList mDynamicImportRequests;
613   RefPtr<ScriptLoadRequest> mParserBlockingRequest;
614 
615   // List of script load request that are holding a buffer which has to be saved
616   // on the cache.
617   ScriptLoadRequestList mBytecodeEncodingQueue;
618 
619   // In mRequests, the additional information here is stored by the element.
620   struct PreloadInfo {
621     RefPtr<ScriptLoadRequest> mRequest;
622     nsString mCharset;
623   };
624 
625   friend void ImplCycleCollectionUnlink(ScriptLoader::PreloadInfo& aField);
626   friend void ImplCycleCollectionTraverse(
627       nsCycleCollectionTraversalCallback& aCallback,
628       ScriptLoader::PreloadInfo& aField, const char* aName, uint32_t aFlags);
629 
630   struct PreloadRequestComparator {
EqualsPreloadRequestComparator631     bool Equals(const PreloadInfo& aPi,
632                 ScriptLoadRequest* const& aRequest) const {
633       return aRequest == aPi.mRequest;
634     }
635   };
636 
637   struct PreloadURIComparator {
638     bool Equals(const PreloadInfo& aPi, nsIURI* const& aURI) const;
639   };
640 
641   nsTArray<PreloadInfo> mPreloads;
642 
643   nsCOMPtr<nsIScriptElement> mCurrentScript;
644   nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript;
645   nsTArray<RefPtr<ScriptLoader>> mPendingChildLoaders;
646   uint32_t mParserBlockingBlockerCount;
647   uint32_t mBlockerCount;
648   uint32_t mNumberOfProcessors;
649   bool mEnabled;
650   bool mDeferEnabled;
651   bool mDeferCheckpointReached;
652   bool mBlockingDOMContentLoaded;
653   bool mLoadEventFired;
654   bool mGiveUpEncoding;
655 
656   // Module map
657   nsRefPtrHashtable<nsURIHashKey, mozilla::GenericNonExclusivePromise::Private>
658       mFetchingModules;
659   nsRefPtrHashtable<nsURIHashKey, ModuleScript> mFetchedModules;
660 
661   nsCOMPtr<nsIConsoleReportCollector> mReporter;
662 
663   // Logging
664  public:
665   static LazyLogModule gCspPRLog;
666   static LazyLogModule gScriptLoaderLog;
667 };
668 
669 class nsAutoScriptLoaderDisabler {
670  public:
nsAutoScriptLoaderDisabler(Document * aDoc)671   explicit nsAutoScriptLoaderDisabler(Document* aDoc) {
672     mLoader = aDoc->ScriptLoader();
673     mWasEnabled = mLoader->GetEnabled();
674     if (mWasEnabled) {
675       mLoader->SetEnabled(false);
676     }
677   }
678 
~nsAutoScriptLoaderDisabler()679   ~nsAutoScriptLoaderDisabler() {
680     if (mWasEnabled) {
681       mLoader->SetEnabled(true);
682     }
683   }
684 
685   bool mWasEnabled;
686   RefPtr<ScriptLoader> mLoader;
687 };
688 
689 }  // namespace dom
690 }  // namespace mozilla
691 
692 #endif  // mozilla_dom_ScriptLoader_h
693