forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			416 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			416 lines
		
	
	
	
		
			13 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 mozilla_dom_ScriptLoadRequest_h
 | |
| #define mozilla_dom_ScriptLoadRequest_h
 | |
| 
 | |
| #include "mozilla/Assertions.h"
 | |
| #include "mozilla/CORSMode.h"
 | |
| #include "mozilla/dom/Element.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 mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| 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) {
 | |
|     MOZ_ASSERT(!mLoadBlockedDocument);
 | |
|     aDocument->BlockOnload();
 | |
|     mLoadBlockedDocument = aDocument;
 | |
|   }
 | |
| 
 | |
|   void MaybeUnblockOnload() {
 | |
|     if (mLoadBlockedDocument) {
 | |
|       mLoadBlockedDocument->UnblockOnload(false);
 | |
|       mLoadBlockedDocument = nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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, JSMallocAllocPolicy>;
 | |
| 
 | |
|   // 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 {
 | |
|     nsCOMPtr<nsIScriptElement> scriptElement =
 | |
|         do_QueryInterface(mFetchOptions->mElement);
 | |
|     return scriptElement;
 | |
|   }
 | |
|   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) {
 | |
|     MOZ_ASSERT(aElement);
 | |
|     MOZ_ASSERT(!GetScriptElement());
 | |
|     MOZ_ASSERT(IsPreload());
 | |
|     mFetchOptions->mElement = do_QueryInterface(aElement);
 | |
|     mFetchOptions->mIsPreload = false;
 | |
|   }
 | |
| 
 | |
|   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
 | |
| 
 | |
|   // 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
 | 
