ScriptLoadContext.h (11159B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_ScriptLoadContext_h 8 #define mozilla_dom_ScriptLoadContext_h 9 10 #include "js/AllocPolicy.h" 11 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin 12 #include "js/CompileOptions.h" // JS::OwningCompileOptions 13 #include "js/RootingAPI.h" 14 #include "js/SourceText.h" 15 #include "js/Transcoding.h" // JS::TranscodeResult 16 #include "js/TypeDecls.h" 17 #include "js/experimental/JSStencil.h" // JS::FrontendContext, JS::Stencil, JS::InstantiationStorage 18 #include "js/loader/LoadContextBase.h" 19 #include "js/loader/ScriptKind.h" 20 #include "mozilla/AlreadyAddRefed.h" 21 #include "mozilla/Assertions.h" 22 #include "mozilla/CORSMode.h" 23 #include "mozilla/Mutex.h" 24 #include "mozilla/PreloaderBase.h" 25 #include "mozilla/RefPtr.h" 26 #include "mozilla/StaticPrefs_dom.h" 27 #include "mozilla/TaskController.h" // mozilla::Task 28 #include "mozilla/Utf8.h" // mozilla::Utf8Unit 29 #include "mozilla/dom/SRIMetadata.h" 30 #include "mozilla/net/UrlClassifierCommon.h" 31 #include "nsCOMPtr.h" 32 #include "nsCycleCollectionParticipant.h" 33 #include "nsIClassifiedChannel.h" 34 #include "nsIScriptElement.h" 35 36 class nsICacheInfoChannel; 37 struct JSContext; 38 39 namespace mozilla::dom { 40 41 class Element; 42 43 /* 44 * DOM specific ScriptLoadContext. 45 * 46 * ScriptLoadContexts augment the loading of a ScriptLoadRequest. They 47 * describe how a ScriptLoadRequests loading and evaluation needs to be 48 * augmented, based on the information provided by the loading context. In 49 * the case of the DOM, the ScriptLoadContext is used to identify how a script 50 * should be loaded according to information found in the HTML document into 51 * which it will be loaded. The following fields describe how the 52 * ScriptLoadRequest will be loaded. 53 * 54 * * mScriptMode 55 * stores the mode (Async, Sync, Deferred), and preload, which 56 * allows the ScriptLoader to decide if the script should be pushed 57 * offThread, or if the preloaded request should be used. 58 * * mScriptFromHead 59 * Set when the script tag is in the head, and should be treated as 60 * a blocking script 61 * * mIsInline 62 * Set for scripts whose bodies are inline in the html. In this case, 63 * the script does not need to be fetched first. 64 * * mIsXSLT 65 * Set if we are in an XSLT request. 66 * * mIsPreload 67 * Set for scripts that are preloaded in a 68 * <link rel="preload" as="script"> or <link rel="modulepreload"> 69 * element. 70 * 71 * In addition to describing how the ScriptLoadRequest will be loaded by the 72 * DOM ScriptLoader, the ScriptLoadContext contains fields that facilitate 73 * those custom behaviors, including support for offthread parsing and preload 74 * element specific controls. 75 * 76 */ 77 78 // Base class for the off-thread compile or off-thread decode tasks. 79 class CompileOrDecodeTask : public mozilla::Task { 80 protected: 81 CompileOrDecodeTask(); 82 virtual ~CompileOrDecodeTask(); 83 84 nsresult InitFrontendContext(); 85 86 void DidRunTask(const MutexAutoLock& aProofOfLock, 87 RefPtr<JS::Stencil>&& aStencil); 88 89 bool IsCancelled(const MutexAutoLock& aProofOfLock) const { 90 return mIsCancelled; 91 } 92 93 public: 94 // Returns the result of the compilation or decode if it was successful. 95 // Returns nullptr otherwise, and sets pending exception on JSContext. 96 // 97 // aInstantiationStorage receives the storage allocated off main thread 98 // on successful case. 99 already_AddRefed<JS::Stencil> StealResult( 100 JSContext* aCx, JS::InstantiationStorage* aInstantiationStorage); 101 102 // Cancel the task. 103 // If the task is already running, this waits for the task to finish. 104 void Cancel(); 105 106 protected: 107 // This mutex is locked during running the task or cancelling task. 108 mozilla::Mutex mMutex; 109 110 // The result of decode task, to distinguish throwing case and decode error. 111 JS::TranscodeResult mResult = JS::TranscodeResult::Ok; 112 113 // An option used to compile the code, or the equivalent for decode. 114 // This holds the filename pointed by errors reported to JS::FrontendContext. 115 JS::OwningCompileOptions mOptions; 116 117 // Owning-pointer for the context associated with the script compilation. 118 // 119 // The context is allocated on main thread in InitFrontendContext method, 120 // and is freed on any thread in the destructor. 121 JS::FrontendContext* mFrontendContext = nullptr; 122 123 bool mIsCancelled = false; 124 125 private: 126 // The result of the compilation or decode. 127 RefPtr<JS::Stencil> mStencil; 128 129 JS::InstantiationStorage mInstantiationStorage; 130 }; 131 132 class ScriptLoadContext : public JS::loader::LoadContextBase, 133 public PreloaderBase { 134 protected: 135 virtual ~ScriptLoadContext(); 136 137 public: 138 explicit ScriptLoadContext(nsIScriptElement* aScriptElement = nullptr, 139 const nsAString& aSourceText = VoidString()); 140 141 NS_DECL_ISUPPORTS_INHERITED 142 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ScriptLoadContext, 143 JS::loader::LoadContextBase) 144 145 static void PrioritizeAsPreload(nsIChannel* aChannel); 146 147 bool IsPreload() const override; 148 149 bool CompileStarted() const; 150 151 net::ClassificationFlags& GetClassificationFlags() { 152 return mClassificationFlags; 153 } 154 void SetClassificationFlags( 155 const net::ClassificationFlags& aClassificationFlags) { 156 mClassificationFlags = aClassificationFlags; 157 } 158 159 void BlockOnload(Document* aDocument); 160 161 void MaybeUnblockOnload(); 162 163 enum class ScriptMode : uint8_t { 164 eBlocking, 165 eDeferred, 166 eAsync, 167 eLinkPreload // this is a load initiated by <link rel="preload" 168 // as="script"> or <link rel="modulepreload"> tag 169 }; 170 171 void SetScriptMode(bool aDeferAttr, bool aAsyncAttr, bool aLinkPreload); 172 173 bool IsLinkPreloadScript() const { 174 return mScriptMode == ScriptMode::eLinkPreload; 175 } 176 177 bool IsBlockingScript() const { return mScriptMode == ScriptMode::eBlocking; } 178 179 bool IsDeferredScript() const { return mScriptMode == ScriptMode::eDeferred; } 180 181 bool IsAsyncScript() const { return mScriptMode == ScriptMode::eAsync; } 182 183 // Accessors for the script element, for each purpose. 184 // 185 // The script element reference is guaranteed to be available only for: 186 // * inline/external classic script 187 // * inline/external top-level module 188 // 189 // The reference is valid only for specific purpose explained below. 190 191 // For aLoadingNode parameter of a new channel. 192 // TODO: This is basically unnecessary and a document can be used instead. 193 // Remove this. 194 inline nsIScriptElement* GetScriptElementForLoadingNode() const { 195 MOZ_ASSERT(mScriptElement); 196 return mScriptElement; 197 } 198 199 // For TRACE_FOR_TEST macros. 200 // NOTE: This is called also for imported modules. 201 // The consumer allows nullptr. 202 inline nsIScriptElement* GetScriptElementForTrace() const { 203 return mScriptElement; 204 } 205 206 // For ScriptLoader::mCurrentParserInsertedScript. 207 inline nsIScriptElement* GetScriptElementForCurrentParserInsertedScript() 208 const { 209 MOZ_ASSERT(mScriptElement); 210 return mScriptElement; 211 } 212 213 // For nsIScriptLoaderObserver. 214 inline nsIScriptElement* GetScriptElementForObserver() const { 215 MOZ_ASSERT(mScriptElement); 216 return mScriptElement; 217 } 218 219 // For URL classifier. 220 inline nsIScriptElement* GetScriptElementForUrlClassifier() const { 221 return mScriptElement; 222 } 223 224 // For AutoCurrentScriptUpdater. 225 // This is valid only for classic script. 226 inline nsIScriptElement* GetScriptElementForCurrentScript() const { 227 MOZ_ASSERT(mScriptElement); 228 return mScriptElement; 229 } 230 231 bool HasScriptElement() const; 232 233 void GetInlineScriptText(nsAString& aText) const; 234 235 void GetHintCharset(nsAString& aCharset) const; 236 237 // TODO: Reimplement with mLineNo/mColumnNo. 238 uint32_t GetScriptLineNumber() const; 239 JS::ColumnNumberOneOrigin GetScriptColumnNumber() const; 240 241 void BeginEvaluatingTopLevel() const; 242 void EndEvaluatingTopLevel() const; 243 244 void UnblockParser() const; 245 void ContinueParserAsync() const; 246 247 Document* GetScriptOwnerDocument() const; 248 249 // Make this request a preload (speculative) request. 250 void SetIsPreloadRequest() { 251 MOZ_ASSERT(!HasScriptElement()); 252 MOZ_ASSERT(!IsPreload()); 253 mIsPreload = true; 254 } 255 256 // Make a preload request into an actual load request for the given element. 257 void SetIsLoadRequest(nsIScriptElement* aElement); 258 259 FromParser GetParserCreated() const { 260 if (!mScriptElement) { 261 return NOT_FROM_PARSER; 262 } 263 return mScriptElement->GetParserCreated(); 264 } 265 266 // Used to output a string for the Gecko Profiler. 267 void GetProfilerLabel(nsACString& aOutString) override; 268 269 void MaybeCancelOffThreadScript(); 270 271 // Finish the off-main-thread compilation and return the result, or 272 // convert the compilation error to runtime error. 273 already_AddRefed<JS::Stencil> StealOffThreadResult( 274 JSContext* aCx, JS::InstantiationStorage* aInstantiationStorage); 275 276 ScriptMode mScriptMode; // Whether this is a blocking, defer or async script. 277 bool mScriptFromHead; // Synchronous head script block loading of other non 278 // js/css content. 279 bool mIsInline; // Is the script inline or loaded? 280 bool mInDeferList; // True if we live in mDeferRequests. 281 bool mInAsyncList; // True if we live in mLoadingAsyncRequests or 282 // mLoadedAsyncRequests. 283 bool mIsNonAsyncScriptInserted; // True if we live in 284 // mNonAsyncExternalScriptInsertedRequests 285 bool mIsXSLT; // True if we live in mXSLTRequests. 286 bool mInCompilingList; // True if we are in mOffThreadCompilingRequests. 287 net::ClassificationFlags // Classification flags 288 mClassificationFlags; // of the source of the script. 289 bool mWasCompiledOMT; // True if the script has been compiled off main 290 // thread. 291 292 // Task that performs off-thread compilation or off-thread decode. 293 // This field is used to take the result of the task, or cancel the task. 294 // 295 // Set to non-null on the task creation, and set to null when taking the 296 // result or cancelling the task. 297 RefPtr<CompileOrDecodeTask> mCompileOrDecodeTask; 298 299 uint32_t mLineNo; 300 JS::ColumnNumberOneOrigin mColumnNo; 301 302 // Set on scripts and top level modules. 303 bool mIsPreload; 304 305 // Non-null if there is a document that this request is blocking from loading. 306 RefPtr<Document> mLoadBlockedDocument; 307 308 // The script element which trigerred this script load. 309 // This is valid only for classic script and top-level module script. 310 nsCOMPtr<nsIScriptElement> mScriptElement; 311 312 nsString mSourceText; 313 314 // For preload requests, we defer reporting errors to the console until the 315 // request is used. 316 nsresult mUnreportedPreloadError; 317 }; 318 319 } // namespace mozilla::dom 320 321 #endif // mozilla_dom_ScriptLoadContext_h