summaryrefslogtreecommitdiffstats
path: root/dom/script/ScriptLoadRequest.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/script/ScriptLoadRequest.h')
-rw-r--r--dom/script/ScriptLoadRequest.h409
1 files changed, 409 insertions, 0 deletions
diff --git a/dom/script/ScriptLoadRequest.h b/dom/script/ScriptLoadRequest.h
new file mode 100644
index 0000000000..cb1c50b7ea
--- /dev/null
+++ b/dom/script/ScriptLoadRequest.h
@@ -0,0 +1,409 @@
+/* -*- 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 mozilla_dom_ScriptLoadRequest_h
+#define mozilla_dom_ScriptLoadRequest_h
+
+#include "js/AllocPolicy.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/CORSMode.h"
+#include "mozilla/dom/SRIMetadata.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/PreloaderBase.h"
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+#include "mozilla/Variant.h"
+#include "mozilla/Vector.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIScriptElement.h"
+#include "ScriptKind.h"
+
+class nsICacheInfoChannel;
+
+namespace JS {
+class OffThreadToken;
+}
+
+namespace mozilla {
+namespace dom {
+
+class Element;
+class ModuleLoadRequest;
+class ScriptLoadRequestList;
+
+/*
+ * Some options used when fetching script resources. This only loosely
+ * corresponds to HTML's "script fetch options".
+ *
+ * These are common to all modules in a module graph, and hence a single
+ * instance is shared by all ModuleLoadRequest objects in a graph.
+ */
+
+class ScriptFetchOptions {
+ ~ScriptFetchOptions();
+
+ public:
+ NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ScriptFetchOptions)
+ NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ScriptFetchOptions)
+
+ ScriptFetchOptions(mozilla::CORSMode aCORSMode,
+ enum ReferrerPolicy aReferrerPolicy, Element* aElement,
+ nsIPrincipal* aTriggeringPrincipal);
+
+ const mozilla::CORSMode mCORSMode;
+ const enum ReferrerPolicy mReferrerPolicy;
+ bool mIsPreload;
+ nsCOMPtr<Element> mElement;
+ nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
+};
+
+/*
+ * A class that handles loading and evaluation of <script> elements.
+ */
+
+class ScriptLoadRequest
+ : public PreloaderBase,
+ private mozilla::LinkedListElement<ScriptLoadRequest> {
+ typedef LinkedListElement<ScriptLoadRequest> super;
+
+ // Allow LinkedListElement<ScriptLoadRequest> to cast us to itself as needed.
+ friend class mozilla::LinkedListElement<ScriptLoadRequest>;
+ friend class ScriptLoadRequestList;
+
+ protected:
+ virtual ~ScriptLoadRequest();
+
+ public:
+ ScriptLoadRequest(ScriptKind aKind, nsIURI* aURI,
+ ScriptFetchOptions* aFetchOptions,
+ const SRIMetadata& aIntegrity, nsIURI* aReferrer);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadRequest)
+
+ // PreloaderBase
+ static void PrioritizeAsPreload(nsIChannel* aChannel);
+ virtual void PrioritizeAsPreload() override;
+
+ bool IsModuleRequest() const { return mKind == ScriptKind::eModule; }
+
+ ModuleLoadRequest* AsModuleRequest();
+
+#ifdef MOZ_GECKO_PROFILER
+ TimeStamp mOffThreadParseStartTime;
+ TimeStamp mOffThreadParseStopTime;
+#endif
+
+ void FireScriptAvailable(nsresult aResult) {
+ bool isInlineClassicScript = mIsInline && !IsModuleRequest();
+ GetScriptElement()->ScriptAvailable(aResult, GetScriptElement(),
+ isInlineClassicScript, mURI, mLineNo);
+ }
+ void FireScriptEvaluated(nsresult aResult) {
+ GetScriptElement()->ScriptEvaluated(aResult, GetScriptElement(), mIsInline);
+ }
+
+ bool IsPreload() const {
+ MOZ_ASSERT_IF(mFetchOptions->mIsPreload, !GetScriptElement());
+ return mFetchOptions->mIsPreload;
+ }
+
+ virtual void Cancel();
+
+ bool IsCanceled() const { return mIsCanceled; }
+
+ virtual void SetReady();
+
+ JS::OffThreadToken** OffThreadTokenPtr() {
+ return mOffThreadToken ? &mOffThreadToken : nullptr;
+ }
+
+ bool IsTracking() const { return mIsTracking; }
+ void SetIsTracking() {
+ MOZ_ASSERT(!mIsTracking);
+ mIsTracking = true;
+ }
+
+ void BlockOnload(Document* aDocument);
+
+ void MaybeUnblockOnload();
+
+ enum class Progress : uint8_t {
+ eLoading, // Request either source or bytecode
+ eLoading_Source, // Explicitly Request source stream
+ eCompiling,
+ eFetchingImports,
+ eReady
+ };
+
+ bool IsReadyToRun() const { return mProgress == Progress::eReady; }
+ bool IsLoading() const {
+ return mProgress == Progress::eLoading ||
+ mProgress == Progress::eLoading_Source;
+ }
+ bool IsLoadingSource() const {
+ return mProgress == Progress::eLoading_Source;
+ }
+ bool InCompilingStage() const {
+ return mProgress == Progress::eCompiling ||
+ (IsReadyToRun() && mWasCompiledOMT);
+ }
+
+ // Type of data provided by the nsChannel.
+ enum class DataType : uint8_t {
+ eUnknown,
+ eTextSource,
+ eBinASTSource,
+ eBytecode
+ };
+
+ bool IsUnknownDataType() const { return mDataType == DataType::eUnknown; }
+ bool IsTextSource() const { return mDataType == DataType::eTextSource; }
+ bool IsBinASTSource() const { return false; }
+ bool IsSource() const { return IsTextSource() || IsBinASTSource(); }
+ bool IsBytecode() const { return mDataType == DataType::eBytecode; }
+
+ void SetUnknownDataType();
+ void SetTextSource();
+ void SetBinASTSource();
+ void SetBytecode();
+
+ // 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 = Vector<Unit, 0, js::MallocAllocPolicy>;
+
+ // BinAST data isn't transferred to the JS engine, so it doesn't need to use
+ // the JS allocator.
+ using BinASTSourceBuffer = Vector<uint8_t>;
+
+ 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>>();
+ }
+
+ const BinASTSourceBuffer& ScriptBinASTData() const {
+ MOZ_ASSERT(IsBinASTSource());
+ return mScriptData->as<BinASTSourceBuffer>();
+ }
+ BinASTSourceBuffer& ScriptBinASTData() {
+ MOZ_ASSERT(IsBinASTSource());
+ return mScriptData->as<BinASTSourceBuffer>();
+ }
+
+ size_t ScriptTextLength() const {
+ MOZ_ASSERT(IsTextSource());
+ return IsUTF16Text() ? ScriptText<char16_t>().length()
+ : ScriptText<Utf8Unit>().length();
+ }
+
+ void ClearScriptText() {
+ MOZ_ASSERT(IsTextSource());
+ return IsUTF16Text() ? ScriptText<char16_t>().clearAndFree()
+ : ScriptText<Utf8Unit>().clearAndFree();
+ }
+
+ enum class ScriptMode : uint8_t {
+ eBlocking,
+ eDeferred,
+ eAsync,
+ eLinkPreload // this is a load initiated by <link rel="preload"
+ // as="script"> tag
+ };
+
+ void SetScriptMode(bool aDeferAttr, bool aAsyncAttr, bool aLinkPreload);
+
+ bool IsLinkPreloadScript() const {
+ return mScriptMode == ScriptMode::eLinkPreload;
+ }
+
+ bool IsBlockingScript() const { return mScriptMode == ScriptMode::eBlocking; }
+
+ bool IsDeferredScript() const { return mScriptMode == ScriptMode::eDeferred; }
+
+ bool IsAsyncScript() const { return mScriptMode == ScriptMode::eAsync; }
+
+ virtual bool IsTopLevel() const {
+ // Classic scripts are always top level.
+ return true;
+ }
+
+ mozilla::CORSMode CORSMode() const { return mFetchOptions->mCORSMode; }
+ enum ReferrerPolicy ReferrerPolicy() const {
+ return mFetchOptions->mReferrerPolicy;
+ }
+ nsIScriptElement* GetScriptElement() const;
+ nsIPrincipal* TriggeringPrincipal() const {
+ return mFetchOptions->mTriggeringPrincipal;
+ }
+
+ // Make this request a preload (speculative) request.
+ void SetIsPreloadRequest() {
+ MOZ_ASSERT(!GetScriptElement());
+ MOZ_ASSERT(!IsPreload());
+ mFetchOptions->mIsPreload = true;
+ }
+
+ // Make a preload request into an actual load request for the given element.
+ void SetIsLoadRequest(nsIScriptElement* aElement);
+
+ FromParser GetParserCreated() const {
+ nsIScriptElement* element = GetScriptElement();
+ if (!element) {
+ return NOT_FROM_PARSER;
+ }
+ return element->GetParserCreated();
+ }
+
+ bool ShouldAcceptBinASTEncoding() const;
+
+ void ClearScriptSource();
+
+ void SetScript(JSScript* aScript);
+
+ void MaybeCancelOffThreadScript();
+ void DropBytecodeCacheReferences();
+
+ using super::getNext;
+ using super::isInList;
+
+ const ScriptKind mKind; // Whether this is a classic script or a module
+ // script.
+ ScriptMode mScriptMode; // Whether this is a blocking, defer or async script.
+ Progress mProgress; // Are we still waiting for a load to complete?
+ DataType mDataType; // Does this contain Source or Bytecode?
+ bool mScriptFromHead; // Synchronous head script block loading of other non
+ // js/css content.
+ bool mIsInline; // Is the script inline or loaded?
+ bool mInDeferList; // True if we live in mDeferRequests.
+ bool mInAsyncList; // True if we live in mLoadingAsyncRequests or
+ // mLoadedAsyncRequests.
+ bool mIsNonAsyncScriptInserted; // True if we live in
+ // mNonAsyncExternalScriptInsertedRequests
+ bool mIsXSLT; // True if we live in mXSLTRequests.
+ bool mIsCanceled; // True if we have been explicitly canceled.
+ bool mWasCompiledOMT; // True if the script has been compiled off main
+ // thread.
+ bool mIsTracking; // True if the script comes from a source on our
+ // tracking protection list.
+
+ RefPtr<ScriptFetchOptions> mFetchOptions;
+
+ JS::OffThreadToken* mOffThreadToken; // Off-thread parsing token.
+ Maybe<nsString> mSourceMapURL; // Holds source map url for loaded scripts
+
+ Atomic<Runnable*> mRunnable; // Runnable created when dispatching off thread
+ // compile. Tracked here so that it can be
+ // properly released during cancellation.
+
+ // Holds the top-level JSScript that corresponds to the current source, once
+ // it is parsed, and planned to be saved in the bytecode cache.
+ JS::Heap<JSScript*> mScript;
+
+ // Holds script source data for non-inline scripts.
+ Maybe<Variant<ScriptTextBuffer<char16_t>, ScriptTextBuffer<Utf8Unit>,
+ BinASTSourceBuffer>>
+ 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.
+ mozilla::Vector<uint8_t> mScriptBytecode;
+ uint32_t mBytecodeOffset; // Offset of the bytecode in mScriptBytecode
+
+ const nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIPrincipal> mOriginPrincipal;
+ nsAutoCString
+ mURL; // Keep the URI's filename alive during off thread parsing.
+ int32_t mLineNo;
+ const SRIMetadata mIntegrity;
+ const nsCOMPtr<nsIURI> mReferrer;
+
+ // Non-null if there is a document that this request is blocking from loading.
+ RefPtr<Document> mLoadBlockedDocument;
+
+ // 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;
+
+ // The base URL used for resolving relative module imports.
+ nsCOMPtr<nsIURI> mBaseURL;
+
+ // For preload requests, we defer reporting errors to the console until the
+ // request is used.
+ nsresult mUnreportedPreloadError;
+};
+
+class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest> {
+ typedef mozilla::LinkedList<ScriptLoadRequest> super;
+
+ public:
+ ~ScriptLoadRequestList();
+
+ void Clear();
+
+#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);
+ }
+
+ MOZ_MUST_USE
+ already_AddRefed<ScriptLoadRequest> Steal(ScriptLoadRequest* aElem) {
+ aElem->removeFrom(*this);
+ return dont_AddRef(aElem);
+ }
+
+ MOZ_MUST_USE
+ already_AddRefed<ScriptLoadRequest> StealFirst() {
+ MOZ_ASSERT(!isEmpty());
+ return Steal(getFirst());
+ }
+
+ void Remove(ScriptLoadRequest* aElem) {
+ aElem->removeFrom(*this);
+ NS_RELEASE(aElem);
+ }
+};
+
+void ImplCycleCollectionUnlink(ScriptLoadRequestList& aField);
+
+void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ ScriptLoadRequestList& aField,
+ const char* aName, uint32_t aFlags);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ScriptLoadRequest_h