ScriptLoadRequest.h (11983B)
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 js_loader_ScriptLoadRequest_h 8 #define js_loader_ScriptLoadRequest_h 9 10 #include "js/experimental/JSStencil.h" 11 #include "js/RootingAPI.h" 12 #include "js/SourceText.h" 13 #include "js/TypeDecls.h" 14 #include "mozilla/Assertions.h" 15 #include "mozilla/dom/CacheExpirationTime.h" 16 #include "mozilla/dom/SRIMetadata.h" 17 #include "mozilla/LinkedList.h" 18 #include "mozilla/PreloaderBase.h" 19 #include "mozilla/RefPtr.h" 20 #include "mozilla/SharedSubResourceCache.h" // mozilla::SubResourceNetworkMetadataHolder 21 #include "mozilla/StaticPrefs_dom.h" 22 #include "nsCycleCollectionParticipant.h" 23 #include "nsIGlobalObject.h" 24 #include "LoadedScript.h" 25 #include "ScriptKind.h" 26 #include "ScriptFetchOptions.h" 27 28 namespace mozilla::dom { 29 30 class ScriptLoadContext; 31 class WorkerLoadContext; 32 class WorkletLoadContext; 33 enum class RequestPriority : uint8_t; 34 35 } // namespace mozilla::dom 36 37 namespace mozilla::loader { 38 class SyncLoadContext; 39 } // namespace mozilla::loader 40 41 namespace JS::loader { 42 43 class LoadContextBase; 44 class ModuleLoadRequest; 45 class ScriptLoadRequestList; 46 47 /* 48 * ScriptLoadRequest 49 * 50 * ScriptLoadRequest is a generic representation of a request/response for 51 * JavaScript file that will be loaded by a Script/Module loader. This 52 * representation is used by the following: 53 * - DOM ScriptLoader / ModuleLoader 54 * - worker ScriptLoader / ModuleLoader 55 * - worklet ScriptLoader 56 * - SyncModuleLoader 57 * 58 * The ScriptLoadRequest contains information specific to the current request, 59 * such as the kind of script (classic, module, etc), and the reference to the 60 * LoadedScript which contains the information independent of the current 61 * request, such as the URI, the ScriptFetchOptions, etc. 62 * 63 * Relationship to ScriptLoadContext: 64 * 65 * ScriptLoadRequest and ScriptLoadContexts have a circular pointer. A 66 * ScriptLoadContext augments the loading of a ScriptLoadRequest by providing 67 * additional information regarding the loading and evaluation behavior (see 68 * the ScriptLoadContext class for details). In terms of responsibility, 69 * the ScriptLoadRequest represents "What" is being loaded, and the 70 * ScriptLoadContext represents "How". 71 * 72 * TODO: see if we can use it in the jsshell script loader. We need to either 73 * remove ISUPPORTS or find a way to encorporate that in the jsshell. We would 74 * then only have one implementation of the script loader, and it would be 75 * tested whenever jsshell tests are run. This would mean finding another way to 76 * create ScriptLoadRequest lists. 77 * 78 */ 79 80 class ScriptLoadRequest : public nsISupports, 81 private mozilla::LinkedListElement<ScriptLoadRequest>, 82 public LoadedScriptDelegate<ScriptLoadRequest> { 83 using super = LinkedListElement<ScriptLoadRequest>; 84 85 // Allow LinkedListElement<ScriptLoadRequest> to cast us to itself as needed. 86 friend class mozilla::LinkedListElement<ScriptLoadRequest>; 87 friend class ScriptLoadRequestList; 88 89 protected: 90 virtual ~ScriptLoadRequest(); 91 92 public: 93 using SRIMetadata = mozilla::dom::SRIMetadata; 94 ScriptLoadRequest(ScriptKind aKind, const SRIMetadata& aIntegrity, 95 nsIURI* aReferrer, LoadContextBase* aContext); 96 97 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 98 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadRequest) 99 100 using super::getNext; 101 using super::isInList; 102 103 template <typename T, typename D = DeletePolicy<T>> 104 using UniquePtr = mozilla::UniquePtr<T, D>; 105 106 bool IsModuleRequest() const { return mKind == ScriptKind::eModule; } 107 bool IsImportMapRequest() const { return mKind == ScriptKind::eImportMap; } 108 109 ModuleLoadRequest* AsModuleRequest(); 110 const ModuleLoadRequest* AsModuleRequest() const; 111 112 CacheExpirationTime ExpirationTime() const { 113 // The request's expiration time is used only when it's received from 114 // necko. For in-memory cached, case, the 115 // SharedSubResourceCache::CompleteSubResource::mExpirationTime field is 116 // used instead. 117 MOZ_ASSERT(!IsCachedStencil()); 118 return mExpirationTime; 119 } 120 121 void SetMinimumExpirationTime(const CacheExpirationTime& aExpirationTime) { 122 mExpirationTime.SetMinimum(aExpirationTime); 123 } 124 125 virtual bool IsTopLevel() const { return true; }; 126 127 virtual void Cancel(); 128 129 virtual void SetReady(); 130 131 enum class State : uint8_t { 132 CheckingCache, 133 Fetching, 134 Compiling, 135 Ready, 136 Canceled 137 }; 138 139 // Before any attempt at fetching resources from the cache we should first 140 // make sure that the resource does not yet exists in the cache. In which case 141 // we might simply alias its LoadedScript. Otherwise a new one would be 142 // created. 143 bool IsCheckingCache() const { return mState == State::CheckingCache; } 144 145 // Setup and load resources, to fill the LoadedScript and make it usable by 146 // the JavaScript engine. 147 bool IsFetching() const { return mState == State::Fetching; } 148 bool IsCompiling() const { return mState == State::Compiling; } 149 bool IsCanceled() const { return mState == State::Canceled; } 150 151 // Return whether the request has been completed, either successfully or 152 // otherwise. 153 bool IsFinished() const { 154 return mState == State::Ready || mState == State::Canceled; 155 } 156 157 mozilla::dom::RequestPriority FetchPriority() const { 158 return FetchOptions()->mFetchPriority; 159 } 160 161 enum ParserMetadata ParserMetadata() const { 162 return FetchOptions()->mParserMetadata; 163 } 164 165 const nsString& Nonce() const { return FetchOptions()->mNonce; } 166 167 nsIPrincipal* TriggeringPrincipal() const { 168 return FetchOptions()->mTriggeringPrincipal; 169 } 170 171 // Convert a CheckingCache ScriptLoadRequest into a Ready one, by populating 172 // the script data from cached script. 173 void CacheEntryFound(LoadedScript* aLoadedScript); 174 175 void CacheEntryRevived(LoadedScript* aLoadedScript); 176 177 // Convert a CheckingCache ScriptLoadRequest into a Fetching one, by creating 178 // a new LoadedScript which is matching the ScriptKind provided when 179 // constructing this ScriptLoadRequest. 180 void NoCacheEntryFound(mozilla::dom::ReferrerPolicy aReferrerPolicy, 181 ScriptFetchOptions* aFetchOptions, nsIURI* aURI); 182 183 private: 184 void SetCacheEntry(LoadedScript* aLoadedScript); 185 186 public: 187 bool PassedConditionForDiskCache() const { 188 return mDiskCachingPlan == CachingPlan::PassedCondition; 189 } 190 191 bool PassedConditionForMemoryCache() const { 192 return mMemoryCachingPlan == CachingPlan::PassedCondition; 193 } 194 195 bool PassedConditionForEitherCache() const { 196 return PassedConditionForDiskCache() || PassedConditionForMemoryCache(); 197 } 198 199 void MarkNotCacheable() { 200 mDiskCachingPlan = CachingPlan::NotCacheable; 201 mMemoryCachingPlan = CachingPlan::NotCacheable; 202 } 203 204 bool IsMarkedNotCacheable() const { 205 MOZ_ASSERT_IF(mDiskCachingPlan == CachingPlan::NotCacheable, 206 mMemoryCachingPlan == CachingPlan::NotCacheable); 207 MOZ_ASSERT_IF(mDiskCachingPlan != CachingPlan::NotCacheable, 208 mMemoryCachingPlan != CachingPlan::NotCacheable); 209 return mDiskCachingPlan == CachingPlan::NotCacheable; 210 } 211 212 void MarkSkippedDiskCaching() { 213 MOZ_ASSERT(mDiskCachingPlan == CachingPlan::Uninitialized || 214 mDiskCachingPlan == CachingPlan::PassedCondition); 215 mDiskCachingPlan = CachingPlan::Skipped; 216 } 217 218 void MarkSkippedMemoryCaching() { 219 MOZ_ASSERT(mMemoryCachingPlan == CachingPlan::Uninitialized || 220 mMemoryCachingPlan == CachingPlan::PassedCondition); 221 mMemoryCachingPlan = CachingPlan::Skipped; 222 } 223 224 void MarkSkippedAllCaching() { 225 MarkSkippedDiskCaching(); 226 MarkSkippedMemoryCaching(); 227 } 228 229 void MarkPassedConditionForDiskCache() { 230 MOZ_ASSERT(mDiskCachingPlan == CachingPlan::Uninitialized); 231 mDiskCachingPlan = CachingPlan::PassedCondition; 232 } 233 234 void MarkPassedConditionForMemoryCache() { 235 MOZ_ASSERT(mMemoryCachingPlan == CachingPlan::Uninitialized); 236 mMemoryCachingPlan = CachingPlan::PassedCondition; 237 } 238 239 mozilla::CORSMode CORSMode() const { return FetchOptions()->mCORSMode; } 240 241 bool HasLoadContext() const { return mLoadContext; } 242 bool HasScriptLoadContext() const; 243 bool HasWorkerLoadContext() const; 244 245 mozilla::dom::ScriptLoadContext* GetScriptLoadContext(); 246 const mozilla::dom::ScriptLoadContext* GetScriptLoadContext() const; 247 248 mozilla::loader::SyncLoadContext* GetSyncLoadContext(); 249 250 mozilla::dom::WorkerLoadContext* GetWorkerLoadContext(); 251 252 mozilla::dom::WorkletLoadContext* GetWorkletLoadContext(); 253 254 const LoadedScript* getLoadedScript() const { return mLoadedScript.get(); } 255 LoadedScript* getLoadedScript() { return mLoadedScript.get(); } 256 257 bool HasSourceMapURL() const { return mHasSourceMapURL_; } 258 const nsString& GetSourceMapURL() const { 259 MOZ_ASSERT(mHasSourceMapURL_); 260 return mMaybeSourceMapURL_; 261 } 262 void SetSourceMapURL(const nsString& aSourceMapURL) { 263 MOZ_ASSERT(!mHasSourceMapURL_); 264 mMaybeSourceMapURL_ = aSourceMapURL; 265 mHasSourceMapURL_ = true; 266 } 267 268 bool HasDirtyCache() const { return mHasDirtyCache_; } 269 void SetHasDirtyCache() { mHasDirtyCache_ = true; } 270 271 bool HadPostponed() const { return mHadPostponed_; } 272 void SetHadPostponed() { mHadPostponed_ = true; } 273 274 public: 275 // Fields. 276 277 // Whether this is a classic script, a module script, or an import map. 278 const ScriptKind mKind; 279 280 // Are we still waiting for a load to complete? 281 State mState; 282 283 // Request source, not cached serialized Stencil. 284 bool mFetchSourceOnly : 1; 285 286 // Becomes true if this has source map url. 287 // 288 // Do not access directly. 289 // Use HasSourceMapURL(), SetSourceMapURL(), and GetSourceMapURL(). 290 bool mHasSourceMapURL_ : 1; 291 292 // Set to true if this response is found in the in-memory cache, but the 293 // cache is marked as dirty, and needs validation. 294 // 295 // This request should go to necko, and when the response is received, 296 // the cache should be either revived or evicted. 297 bool mHasDirtyCache_ : 1; 298 299 // Set to true if this script had already been postponed in the scheduling. 300 bool mHadPostponed_ : 1; 301 302 enum class CachingPlan : uint8_t { 303 // This is not yet considered for caching. 304 Uninitialized, 305 306 // This request is not cacheable (e.g. inline script, JSON module). 307 NotCacheable, 308 309 // This request is cacheable, but is marked for skipping due to 310 // not passing conditions. 311 Skipped, 312 313 // This fits the condition for the caching (e.g. file size, fetch count). 314 PassedCondition, 315 }; 316 CachingPlan mDiskCachingPlan : 2; 317 CachingPlan mMemoryCachingPlan : 2; 318 319 CacheExpirationTime mExpirationTime = CacheExpirationTime::Never(); 320 321 RefPtr<mozilla::SubResourceNetworkMetadataHolder> mNetworkMetadata; 322 const SRIMetadata mIntegrity; 323 const nsCOMPtr<nsIURI> mReferrer; 324 325 // Holds source map url for loaded scripts. 326 // 327 // Do not access directly. 328 // Use HasSourceMapURL(), SetSourceMapURL(), and GetSourceMapURL(). 329 nsString mMaybeSourceMapURL_; 330 331 nsCOMPtr<nsIPrincipal> mOriginPrincipal; 332 333 // Keep the URI's filename alive during off thread parsing. 334 // Also used by workers to report on errors while loading, and used by 335 // worklets as the file name in compile options. 336 nsAutoCString mURL; 337 338 // The loaded script holds the data which can be shared among similar requests 339 RefPtr<LoadedScript> mLoadedScript; 340 341 // LoadContext for augmenting the load depending on the loading 342 // context (DOM, Worker, etc.) 343 RefPtr<LoadContextBase> mLoadContext; 344 345 // EarlyHintRegistrar id to connect the http channel back to the preload, with 346 // a default of value of 0 indicating that this request is not an early hints 347 // preload. 348 uint64_t mEarlyHintPreloaderId; 349 }; 350 351 } // namespace JS::loader 352 353 #endif // js_loader_ScriptLoadRequest_h