forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			364 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #ifndef js_loader_ScriptLoadRequest_h
 | |
| #define js_loader_ScriptLoadRequest_h
 | |
| 
 | |
| #include "js/RootingAPI.h"
 | |
| #include "js/SourceText.h"
 | |
| #include "js/TypeDecls.h"
 | |
| #include "mozilla/Atomics.h"
 | |
| #include "mozilla/Assertions.h"
 | |
| #include "mozilla/dom/SRIMetadata.h"
 | |
| #include "mozilla/LinkedList.h"
 | |
| #include "mozilla/Maybe.h"
 | |
| #include "mozilla/PreloaderBase.h"
 | |
| #include "mozilla/StaticPrefs_dom.h"
 | |
| #include "mozilla/Variant.h"
 | |
| #include "mozilla/Vector.h"
 | |
| #include "nsCycleCollectionParticipant.h"
 | |
| #include "nsIGlobalObject.h"
 | |
| #include "LoadedScript.h"
 | |
| #include "ScriptKind.h"
 | |
| #include "ScriptFetchOptions.h"
 | |
| 
 | |
| class nsICacheInfoChannel;
 | |
| 
 | |
| namespace mozilla::dom {
 | |
| 
 | |
| class ScriptLoadContext;
 | |
| class WorkerLoadContext;
 | |
| class WorkletLoadContext;
 | |
| enum class RequestPriority : uint8_t;
 | |
| 
 | |
| }  // namespace mozilla::dom
 | |
| 
 | |
| namespace mozilla::loader {
 | |
| class SyncLoadContext;
 | |
| }  // namespace mozilla::loader
 | |
| 
 | |
| namespace JS {
 | |
| namespace loader {
 | |
| 
 | |
| class LoadContextBase;
 | |
| class ModuleLoadRequest;
 | |
| class ScriptLoadRequestList;
 | |
| 
 | |
| /*
 | |
|  * ScriptLoadRequest
 | |
|  *
 | |
|  * ScriptLoadRequest is a generic representation of a JavaScript script that
 | |
|  * will be loaded by a Script/Module loader. This representation is used by the
 | |
|  * DOM ScriptLoader and will be used by workers and MOZJSComponentLoader.
 | |
|  *
 | |
|  * The ScriptLoadRequest contains information about the kind of script (classic
 | |
|  * or module), the URI, and the ScriptFetchOptions associated with the script.
 | |
|  * It is responsible for holding the script data once the fetch is complete, or
 | |
|  * if the request is cached, the bytecode.
 | |
|  *
 | |
|  * Relationship to ScriptLoadContext:
 | |
|  *
 | |
|  * ScriptLoadRequest and ScriptLoadContexts have a circular pointer.  A
 | |
|  * ScriptLoadContext augments the loading of a ScriptLoadRequest by providing
 | |
|  * additional information regarding the loading and evaluation behavior (see
 | |
|  * the ScriptLoadContext class for details).  In terms of responsibility,
 | |
|  * the ScriptLoadRequest represents "What" is being loaded, and the
 | |
|  * ScriptLoadContext represents "How".
 | |
|  *
 | |
|  * TODO: see if we can use it in the jsshell script loader. We need to either
 | |
|  * remove ISUPPORTS or find a way to encorporate that in the jsshell. We would
 | |
|  * then only have one implementation of the script loader, and it would be
 | |
|  * tested whenever jsshell tests are run. This would mean finding another way to
 | |
|  * create ScriptLoadRequest lists.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| class ScriptLoadRequest : public nsISupports,
 | |
|                           private mozilla::LinkedListElement<ScriptLoadRequest>,
 | |
|                           public LoadedScriptDelegate<ScriptLoadRequest> {
 | |
|   using super = LinkedListElement<ScriptLoadRequest>;
 | |
| 
 | |
|   // Allow LinkedListElement<ScriptLoadRequest> to cast us to itself as needed.
 | |
|   friend class mozilla::LinkedListElement<ScriptLoadRequest>;
 | |
|   friend class ScriptLoadRequestList;
 | |
| 
 | |
|  protected:
 | |
|   virtual ~ScriptLoadRequest();
 | |
| 
 | |
|  public:
 | |
|   using SRIMetadata = mozilla::dom::SRIMetadata;
 | |
|   ScriptLoadRequest(ScriptKind aKind, nsIURI* aURI,
 | |
|                     mozilla::dom::ReferrerPolicy aReferrerPolicy,
 | |
|                     ScriptFetchOptions* aFetchOptions,
 | |
|                     const SRIMetadata& aIntegrity, nsIURI* aReferrer,
 | |
|                     LoadContextBase* aContext);
 | |
| 
 | |
|   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 | |
|   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadRequest)
 | |
| 
 | |
|   using super::getNext;
 | |
|   using super::isInList;
 | |
| 
 | |
|   template <typename T, typename D = JS::DeletePolicy<T>>
 | |
|   using UniquePtr = mozilla::UniquePtr<T, D>;
 | |
| 
 | |
|   bool IsModuleRequest() const { return mKind == ScriptKind::eModule; }
 | |
|   bool IsImportMapRequest() const { return mKind == ScriptKind::eImportMap; }
 | |
| 
 | |
|   ModuleLoadRequest* AsModuleRequest();
 | |
|   const ModuleLoadRequest* AsModuleRequest() const;
 | |
| 
 | |
|   virtual bool IsTopLevel() const { return true; };
 | |
| 
 | |
|   virtual void Cancel();
 | |
| 
 | |
|   virtual void SetReady();
 | |
| 
 | |
|   enum class State : uint8_t {
 | |
|     CheckingCache,
 | |
|     PendingFetchingError,
 | |
|     Fetching,
 | |
|     Compiling,
 | |
|     LoadingImports,
 | |
|     CancelingImports,
 | |
|     Ready,
 | |
|     Canceled
 | |
|   };
 | |
| 
 | |
|   // Before any attempt at fetching resources from the cache we should first
 | |
|   // make sure that the resource does not yet exists in the cache. In which case
 | |
|   // we might simply alias its LoadedScript. Otherwise a new one would be
 | |
|   // created.
 | |
|   bool IsCheckingCache() const { return mState == State::CheckingCache; }
 | |
| 
 | |
|   // Setup and load resources, to fill the LoadedScript and make it usable by
 | |
|   // the JavaScript engine.
 | |
|   bool IsFetching() const { return mState == State::Fetching; }
 | |
|   bool IsCompiling() const { return mState == State::Compiling; }
 | |
|   bool IsLoadingImports() const { return mState == State::LoadingImports; }
 | |
|   bool IsCancelingImports() const { return mState == State::CancelingImports; }
 | |
|   bool IsCanceled() const { return mState == State::Canceled; }
 | |
| 
 | |
|   bool IsPendingFetchingError() const {
 | |
|     return mState == State::PendingFetchingError;
 | |
|   }
 | |
| 
 | |
|   // Return whether the request has been completed, either successfully or
 | |
|   // otherwise.
 | |
|   bool IsFinished() const {
 | |
|     return mState == State::Ready || mState == State::Canceled;
 | |
|   }
 | |
| 
 | |
|   mozilla::dom::RequestPriority FetchPriority() const {
 | |
|     return mFetchOptions->mFetchPriority;
 | |
|   }
 | |
| 
 | |
|   enum mozilla::dom::ReferrerPolicy ReferrerPolicy() const {
 | |
|     return mReferrerPolicy;
 | |
|   }
 | |
| 
 | |
|   void UpdateReferrerPolicy(mozilla::dom::ReferrerPolicy aReferrerPolicy) {
 | |
|     mReferrerPolicy = aReferrerPolicy;
 | |
|   }
 | |
| 
 | |
|   enum ParserMetadata ParserMetadata() const {
 | |
|     return mFetchOptions->mParserMetadata;
 | |
|   }
 | |
| 
 | |
|   const nsString& Nonce() const { return mFetchOptions->mNonce; }
 | |
| 
 | |
|   nsIPrincipal* TriggeringPrincipal() const {
 | |
|     return mFetchOptions->mTriggeringPrincipal;
 | |
|   }
 | |
| 
 | |
|   // Convert a CheckingCache ScriptLoadRequest into a Fetching one, by creating
 | |
|   // a new LoadedScript which is matching the ScriptKind provided when
 | |
|   // constructing this ScriptLoadRequest.
 | |
|   void NoCacheEntryFound();
 | |
| 
 | |
|   void SetPendingFetchingError();
 | |
| 
 | |
|   bool PassedConditionForBytecodeEncoding() const {
 | |
|     return mBytecodeEncodingPlan == BytecodeEncodingPlan::PassedCondition ||
 | |
|            mBytecodeEncodingPlan == BytecodeEncodingPlan::MarkedForEncode;
 | |
|   }
 | |
| 
 | |
|   void MarkSkippedBytecodeEncoding() {
 | |
|     MOZ_ASSERT(mBytecodeEncodingPlan == BytecodeEncodingPlan::Uninitialized);
 | |
|     mBytecodeEncodingPlan = BytecodeEncodingPlan::Skipped;
 | |
|   }
 | |
| 
 | |
|   void MarkPassedConditionForBytecodeEncoding() {
 | |
|     MOZ_ASSERT(mBytecodeEncodingPlan == BytecodeEncodingPlan::Uninitialized);
 | |
|     mBytecodeEncodingPlan = BytecodeEncodingPlan::PassedCondition;
 | |
|   }
 | |
| 
 | |
|   bool IsMarkedForBytecodeEncoding() const {
 | |
|     return mBytecodeEncodingPlan == BytecodeEncodingPlan::MarkedForEncode;
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   void MarkForBytecodeEncoding() {
 | |
|     MOZ_ASSERT(mBytecodeEncodingPlan == BytecodeEncodingPlan::PassedCondition);
 | |
|     mBytecodeEncodingPlan = BytecodeEncodingPlan::MarkedForEncode;
 | |
|   }
 | |
| 
 | |
|  public:
 | |
|   void MarkScriptForBytecodeEncoding(JSScript* aScript);
 | |
| 
 | |
|   mozilla::CORSMode CORSMode() const { return mFetchOptions->mCORSMode; }
 | |
| 
 | |
|   void DropBytecodeCacheReferences();
 | |
| 
 | |
|   bool HasLoadContext() const { return mLoadContext; }
 | |
|   bool HasScriptLoadContext() const;
 | |
|   bool HasWorkerLoadContext() const;
 | |
| 
 | |
|   mozilla::dom::ScriptLoadContext* GetScriptLoadContext();
 | |
| 
 | |
|   mozilla::loader::SyncLoadContext* GetSyncLoadContext();
 | |
| 
 | |
|   mozilla::dom::WorkerLoadContext* GetWorkerLoadContext();
 | |
| 
 | |
|   mozilla::dom::WorkletLoadContext* GetWorkletLoadContext();
 | |
| 
 | |
|   const LoadedScript* getLoadedScript() const { return mLoadedScript.get(); }
 | |
|   LoadedScript* getLoadedScript() { return mLoadedScript.get(); }
 | |
| 
 | |
|   /*
 | |
|    * Set the request's mBaseURL, based on aChannel.
 | |
|    * aOriginalURI is the result of aChannel->GetOriginalURI.
 | |
|    */
 | |
|   void SetBaseURLFromChannelAndOriginalURI(nsIChannel* aChannel,
 | |
|                                            nsIURI* aOriginalURI);
 | |
| 
 | |
|   const ScriptKind mKind;  // Whether this is a classic script or a module
 | |
|                            // script.
 | |
| 
 | |
|   State mState;           // Are we still waiting for a load to complete?
 | |
|   bool mFetchSourceOnly;  // Request source, not cached bytecode.
 | |
| 
 | |
|   enum class BytecodeEncodingPlan : uint8_t {
 | |
|     // This is not yet considered for encoding.
 | |
|     Uninitialized,
 | |
| 
 | |
|     // This is marked for skipping the encoding.
 | |
|     Skipped,
 | |
| 
 | |
|     // This fits the condition for the encoding (e.g. file size, fetch count).
 | |
|     PassedCondition,
 | |
| 
 | |
|     // This is marked for encoding, with setting sufficient input,
 | |
|     // e.g. mScriptForBytecodeEncoding for script.
 | |
|     MarkedForEncode,
 | |
|   };
 | |
|   BytecodeEncodingPlan mBytecodeEncodingPlan =
 | |
|       BytecodeEncodingPlan::Uninitialized;
 | |
| 
 | |
|   // The referrer policy used for the initial fetch and for fetching any
 | |
|   // imported modules
 | |
|   enum mozilla::dom::ReferrerPolicy mReferrerPolicy;
 | |
|   RefPtr<ScriptFetchOptions> mFetchOptions;
 | |
|   const SRIMetadata mIntegrity;
 | |
|   const nsCOMPtr<nsIURI> mReferrer;
 | |
|   mozilla::Maybe<nsString>
 | |
|       mSourceMapURL;  // Holds source map url for loaded scripts
 | |
| 
 | |
|   const nsCOMPtr<nsIURI> mURI;
 | |
|   nsCOMPtr<nsIPrincipal> mOriginPrincipal;
 | |
| 
 | |
|   // Keep the URI's filename alive during off thread parsing.
 | |
|   // Also used by workers to report on errors while loading, and used by
 | |
|   // worklets as the file name in compile options.
 | |
|   nsAutoCString mURL;
 | |
| 
 | |
|   // The base URL used for resolving relative module imports.
 | |
|   nsCOMPtr<nsIURI> mBaseURL;
 | |
| 
 | |
|   // The loaded script holds the source / bytecode which is loaded.
 | |
|   //
 | |
|   // Currently it is used to hold information which are needed by the Debugger.
 | |
|   // Soon it would be used as a way to dissociate the LoadRequest from the
 | |
|   // loaded value, such that multiple request referring to the same content
 | |
|   // would share the same loaded script.
 | |
|   RefPtr<LoadedScript> mLoadedScript;
 | |
| 
 | |
|   // Holds the top-level JSScript that corresponds to the current source, once
 | |
|   // it is parsed, and marked to be saved in the bytecode cache.
 | |
|   //
 | |
|   // NOTE: This field is not used for ModuleLoadRequest.
 | |
|   JS::Heap<JSScript*> mScriptForBytecodeEncoding;
 | |
| 
 | |
|   // Holds the Cache information, which is used to register the bytecode
 | |
|   // on the cache entry, such that we can load it the next time.
 | |
|   nsCOMPtr<nsICacheInfoChannel> mCacheInfo;
 | |
| 
 | |
|   // LoadContext for augmenting the load depending on the loading
 | |
|   // context (DOM, Worker, etc.)
 | |
|   RefPtr<LoadContextBase> mLoadContext;
 | |
| 
 | |
|   // EarlyHintRegistrar id to connect the http channel back to the preload, with
 | |
|   // a default of value of 0 indicating that this request is not an early hints
 | |
|   // preload.
 | |
|   uint64_t mEarlyHintPreloaderId;
 | |
| };
 | |
| 
 | |
| class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> {
 | |
|   using super = mozilla::LinkedList<ScriptLoadRequest>;
 | |
| 
 | |
|  public:
 | |
|   ~ScriptLoadRequestList();
 | |
| 
 | |
|   void CancelRequestsAndClear();
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   bool Contains(ScriptLoadRequest* aElem) const;
 | |
| #endif  // DEBUG
 | |
| 
 | |
|   using super::getFirst;
 | |
|   using super::isEmpty;
 | |
| 
 | |
|   void AppendElement(ScriptLoadRequest* aElem) {
 | |
|     MOZ_ASSERT(!aElem->isInList());
 | |
|     NS_ADDREF(aElem);
 | |
|     insertBack(aElem);
 | |
|   }
 | |
| 
 | |
|   already_AddRefed<ScriptLoadRequest> Steal(ScriptLoadRequest* aElem) {
 | |
|     aElem->removeFrom(*this);
 | |
|     return dont_AddRef(aElem);
 | |
|   }
 | |
| 
 | |
|   already_AddRefed<ScriptLoadRequest> StealFirst() {
 | |
|     MOZ_ASSERT(!isEmpty());
 | |
|     return Steal(getFirst());
 | |
|   }
 | |
| 
 | |
|   void Remove(ScriptLoadRequest* aElem) {
 | |
|     aElem->removeFrom(*this);
 | |
|     NS_RELEASE(aElem);
 | |
|   }
 | |
| };
 | |
| 
 | |
| inline void ImplCycleCollectionUnlink(ScriptLoadRequestList& aField) {
 | |
|   while (!aField.isEmpty()) {
 | |
|     RefPtr<ScriptLoadRequest> first = aField.StealFirst();
 | |
|   }
 | |
| }
 | |
| 
 | |
| inline void ImplCycleCollectionTraverse(
 | |
|     nsCycleCollectionTraversalCallback& aCallback,
 | |
|     ScriptLoadRequestList& aField, const char* aName, uint32_t aFlags) {
 | |
|   for (ScriptLoadRequest* request = aField.getFirst(); request;
 | |
|        request = request->getNext()) {
 | |
|     CycleCollectionNoteChild(aCallback, request, aName, aFlags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace loader
 | |
| }  // namespace JS
 | |
| 
 | |
| #endif  // js_loader_ScriptLoadRequest_h
 | 
