summaryrefslogtreecommitdiffstats
path: root/js/loader/ScriptLoadRequest.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/loader/ScriptLoadRequest.h')
-rw-r--r--js/loader/ScriptLoadRequest.h429
1 files changed, 429 insertions, 0 deletions
diff --git a/js/loader/ScriptLoadRequest.h b/js/loader/ScriptLoadRequest.h
new file mode 100644
index 0000000000..2624c3f25e
--- /dev/null
+++ b/js/loader/ScriptLoadRequest.h
@@ -0,0 +1,429 @@
+/* -*- 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/AllocPolicy.h"
+#include "js/RootingAPI.h"
+#include "js/SourceText.h"
+#include "js/TypeDecls.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/CORSMode.h"
+#include "mozilla/dom/SRIMetadata.h"
+#include "mozilla/dom/ReferrerPolicyBinding.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MaybeOneOf.h"
+#include "mozilla/PreloaderBase.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+#include "mozilla/Variant.h"
+#include "mozilla/Vector.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIGlobalObject.h"
+#include "ScriptKind.h"
+#include "nsIScriptElement.h"
+
+class nsICacheInfoChannel;
+
+namespace mozilla::dom {
+
+class ScriptLoadContext;
+class WorkerLoadContext;
+class WorkletLoadContext;
+
+} // namespace mozilla::dom
+
+namespace mozilla::loader {
+class ComponentLoadContext;
+} // namespace mozilla::loader
+
+namespace JS {
+class OffThreadToken;
+
+namespace loader {
+
+using Utf8Unit = mozilla::Utf8Unit;
+
+class LoadContextBase;
+class ModuleLoadRequest;
+class ScriptLoadRequestList;
+
+/*
+ * ScriptFetchOptions loosely corresponds to HTML's "script fetch options",
+ * https://html.spec.whatwg.org/multipage/webappapis.html#script-fetch-options
+ * with the exception of the following properties:
+ * cryptographic nonce
+ * The cryptographic nonce metadata used for the initial fetch and for
+ * fetching any imported modules. As this is populated by a DOM element,
+ * this is implemented via mozilla::dom::Element as the field
+ * mElement. The default value is an empty string, and is indicated
+ * when this field is a nullptr. Nonce is not represented on the dom
+ * side as per bug 1374612.
+ * parser metadata
+ * The parser metadata used for the initial fetch and for fetching any
+ * imported modules. This is populated from a mozilla::dom::Element and is
+ * handled by the field mElement. The default value is an empty string,
+ * and is indicated when this field is a nullptr.
+ * integrity metadata
+ * The integrity metadata used for the initial fetch. This is
+ * implemented in ScriptLoadRequest, as it changes for every
+ * ScriptLoadRequest.
+ *
+ * In the case of classic scripts without dynamic import, this object is
+ * used once. For modules, this object is propogated throughout the module
+ * tree. If there is a dynamically imported module in any type of script,
+ * the ScriptFetchOptions object will be propogated from its importer.
+ */
+
+class ScriptFetchOptions {
+ ~ScriptFetchOptions();
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ScriptFetchOptions)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ScriptFetchOptions)
+
+ ScriptFetchOptions(mozilla::CORSMode aCORSMode,
+ enum mozilla::dom::ReferrerPolicy aReferrerPolicy,
+ nsIPrincipal* aTriggeringPrincipal,
+ mozilla::dom::Element* aElement = nullptr);
+
+ /*
+ * The credentials mode used for the initial fetch (for module scripts)
+ * and for fetching any imported modules (for both module scripts and
+ * classic scripts)
+ */
+ const mozilla::CORSMode mCORSMode;
+
+ /*
+ * The referrer policy used for the initial fetch and for fetching any
+ * imported modules
+ */
+ const enum mozilla::dom::ReferrerPolicy mReferrerPolicy;
+
+ /*
+ * Used to determine CSP and if we are on the About page.
+ * Only used in DOM content scripts.
+ * TODO: Move to ScriptLoadContext
+ */
+ nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+ /*
+ * Represents fields populated by DOM elements (nonce, parser metadata)
+ * Leave this field as a nullptr for any fetch that requires the
+ * default classic script options.
+ * (https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options)
+ * TODO: extract necessary fields rather than passing this object
+ */
+ nsCOMPtr<mozilla::dom::Element> mElement;
+};
+
+/*
+ * 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> {
+ 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,
+ 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>
+ using VariantType = mozilla::VariantType<T>;
+
+ template <typename... Ts>
+ using Variant = mozilla::Variant<Ts...>;
+
+ template <typename T, typename D = JS::DeletePolicy<T>>
+ using UniquePtr = mozilla::UniquePtr<T, D>;
+
+ using MaybeSourceText =
+ mozilla::MaybeOneOf<JS::SourceText<char16_t>, JS::SourceText<Utf8Unit>>;
+
+ 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 {
+ Fetching,
+ Compiling,
+ LoadingImports,
+ Ready,
+ Canceled
+ };
+
+ bool IsFetching() const { return mState == State::Fetching; }
+ bool IsCompiling() const { return mState == State::Compiling; }
+ bool IsLoadingImports() const { return mState == State::LoadingImports; }
+
+ bool IsReadyToRun() const {
+ return mState == State::Ready || mState == State::Canceled;
+ }
+
+ bool IsCanceled() const { return mState == State::Canceled; }
+
+ // Type of data provided by the nsChannel.
+ enum class DataType : uint8_t { eUnknown, eTextSource, eBytecode };
+
+ bool IsUnknownDataType() const { return mDataType == DataType::eUnknown; }
+ bool IsTextSource() const { return mDataType == DataType::eTextSource; }
+ bool IsSource() const { return IsTextSource(); }
+
+ void SetUnknownDataType() {
+ mDataType = DataType::eUnknown;
+ mScriptData.reset();
+ }
+
+ bool IsUTF8ParsingEnabled();
+
+ void SetTextSource() {
+ MOZ_ASSERT(IsUnknownDataType());
+ mDataType = DataType::eTextSource;
+ if (IsUTF8ParsingEnabled()) {
+ mScriptData.emplace(VariantType<ScriptTextBuffer<Utf8Unit>>());
+ } else {
+ mScriptData.emplace(VariantType<ScriptTextBuffer<char16_t>>());
+ }
+ }
+
+ // Use a vector backed by the JS allocator for script text so that contents
+ // can be transferred in constant time to the JS engine, not copied in linear
+ // time.
+ template <typename Unit>
+ using ScriptTextBuffer = mozilla::Vector<Unit, 0, js::MallocAllocPolicy>;
+
+ bool IsUTF16Text() const {
+ return mScriptData->is<ScriptTextBuffer<char16_t>>();
+ }
+ bool IsUTF8Text() const {
+ return mScriptData->is<ScriptTextBuffer<Utf8Unit>>();
+ }
+
+ template <typename Unit>
+ const ScriptTextBuffer<Unit>& ScriptText() const {
+ MOZ_ASSERT(IsTextSource());
+ return mScriptData->as<ScriptTextBuffer<Unit>>();
+ }
+ template <typename Unit>
+ ScriptTextBuffer<Unit>& ScriptText() {
+ MOZ_ASSERT(IsTextSource());
+ return mScriptData->as<ScriptTextBuffer<Unit>>();
+ }
+
+ size_t ScriptTextLength() const {
+ MOZ_ASSERT(IsTextSource());
+ return IsUTF16Text() ? ScriptText<char16_t>().length()
+ : ScriptText<Utf8Unit>().length();
+ }
+
+ // Get source text. On success |aMaybeSource| will contain either UTF-8 or
+ // UTF-16 source; on failure it will remain in its initial state.
+ nsresult GetScriptSource(JSContext* aCx, MaybeSourceText* aMaybeSource);
+
+ void ClearScriptText() {
+ MOZ_ASSERT(IsTextSource());
+ return IsUTF16Text() ? ScriptText<char16_t>().clearAndFree()
+ : ScriptText<Utf8Unit>().clearAndFree();
+ }
+
+ enum mozilla::dom::ReferrerPolicy ReferrerPolicy() const {
+ return mFetchOptions->mReferrerPolicy;
+ }
+
+ nsIPrincipal* TriggeringPrincipal() const {
+ return mFetchOptions->mTriggeringPrincipal;
+ }
+
+ void ClearScriptSource();
+
+ void MarkForBytecodeEncoding(JSScript* aScript);
+
+ bool IsMarkedForBytecodeEncoding() const;
+
+ bool IsBytecode() const { return mDataType == DataType::eBytecode; }
+
+ void SetBytecode();
+
+ 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::ComponentLoadContext* GetComponentLoadContext();
+
+ mozilla::dom::WorkerLoadContext* GetWorkerLoadContext();
+
+ mozilla::dom::WorkletLoadContext* GetWorkletLoadContext();
+
+ 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.
+ DataType mDataType; // Does this contain Source or Bytecode?
+ RefPtr<ScriptFetchOptions> mFetchOptions;
+ const SRIMetadata mIntegrity;
+ const nsCOMPtr<nsIURI> mReferrer;
+ mozilla::Maybe<nsString>
+ mSourceMapURL; // Holds source map url for loaded scripts
+
+ // Holds script source data for non-inline scripts.
+ mozilla::Maybe<
+ Variant<ScriptTextBuffer<char16_t>, ScriptTextBuffer<Utf8Unit>>>
+ mScriptData;
+
+ // The length of script source text, set when reading completes. This is used
+ // since mScriptData is cleared when the source is passed to the JS engine.
+ size_t mScriptTextLength;
+
+ // Holds the SRI serialized hash and the script bytecode for non-inline
+ // scripts. The data is laid out according to ScriptBytecodeDataLayout
+ // or, if compression is enabled, ScriptBytecodeCompressedDataLayout.
+ mozilla::Vector<uint8_t> mScriptBytecode;
+ uint32_t mBytecodeOffset; // Offset of the bytecode in mScriptBytecode
+
+ 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;
+
+ // 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;
+};
+
+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