diff options
Diffstat (limited to 'js/loader/ScriptLoadRequest.h')
-rw-r--r-- | js/loader/ScriptLoadRequest.h | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/js/loader/ScriptLoadRequest.h b/js/loader/ScriptLoadRequest.h new file mode 100644 index 0000000000..442a456b15 --- /dev/null +++ b/js/loader/ScriptLoadRequest.h @@ -0,0 +1,323 @@ +/* -*- 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" +#include "nsIScriptElement.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, + 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 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(); + + void MarkForBytecodeEncoding(JSScript* aScript); + + bool IsMarkedForBytecodeEncoding() const; + + 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. + + // 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 planned to be saved in the bytecode cache. + // + // NOTE: This field is not used for ModuleLoadRequest. + // See ModuleLoadRequest::mIsMarkedForBytecodeEncoding. + 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 |