ScriptLoader.h (34337B)
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_ScriptLoader_h 8 #define mozilla_dom_ScriptLoader_h 9 10 #include "ModuleLoader.h" 11 #include "SharedScriptCache.h" 12 #include "js/TypeDecls.h" 13 #include "js/Utility.h" // JS::FreePolicy 14 #include "js/experimental/CompileScript.h" // JS::FrontendContext 15 #include "js/loader/LoadedScript.h" 16 #include "js/loader/ModuleLoaderBase.h" 17 #include "js/loader/ScriptKind.h" 18 #include "js/loader/ScriptLoadRequest.h" 19 #include "js/loader/ScriptLoadRequestList.h" 20 #include "mozilla/CORSMode.h" 21 #include "mozilla/MaybeOneOf.h" 22 #include "mozilla/MozPromise.h" 23 #include "mozilla/dom/ScriptLoadContext.h" 24 #include "mozilla/dom/ScriptLoadRequestType.h" 25 #include "nsCOMArray.h" 26 #include "nsCOMPtr.h" 27 #include "nsCycleCollectionParticipant.h" 28 #include "nsILoadInfo.h" // nsSecurityFlags 29 #include "nsINode.h" 30 #include "nsIObserver.h" 31 #include "nsIScriptElement.h" 32 #include "nsIScriptLoaderObserver.h" 33 #include "nsRefPtrHashtable.h" 34 #include "nsTArray.h" 35 #include "nsURIHashKey.h" 36 37 class nsCycleCollectionTraversalCallback; 38 class nsIChannel; 39 class nsIConsoleReportCollector; 40 class nsIContent; 41 class nsIIncrementalStreamLoader; 42 class nsIPrincipal; 43 class nsIScriptGlobalObject; 44 class nsITimer; 45 class nsIURI; 46 47 namespace JS { 48 49 class CompileOptions; 50 51 template <typename UnitT> 52 class SourceText; 53 54 namespace loader { 55 56 class LoadedScript; 57 class ModuleLoadRequest; 58 class ModuleScript; 59 class ScriptLoadRequest; 60 61 } // namespace loader 62 } // namespace JS 63 64 namespace mozilla { 65 66 class LazyLogModule; 67 union Utf8Unit; 68 69 namespace dom { 70 71 class AutoJSAPI; 72 class DocGroup; 73 class Document; 74 class ModuleLoader; 75 class SRICheckDataVerifier; 76 class SRIMetadata; 77 class ScriptLoadHandler; 78 class ScriptLoadContext; 79 class ScriptLoader; 80 class ScriptRequestProcessor; 81 82 enum class ReferrerPolicy : uint8_t; 83 enum class RequestPriority : uint8_t; 84 85 class AsyncCompileShutdownObserver final : public nsIObserver { 86 ~AsyncCompileShutdownObserver() { Unregister(); } 87 88 public: 89 explicit AsyncCompileShutdownObserver(ScriptLoader* aLoader) 90 : mScriptLoader(aLoader) {} 91 92 void OnShutdown(); 93 void Unregister(); 94 95 NS_DECL_ISUPPORTS 96 NS_DECL_NSIOBSERVER 97 98 private: 99 // Defined during registration in ScriptLoader constructor, and 100 // cleared during destructor, ScriptLoader::Destroy() or Shutdown. 101 ScriptLoader* mScriptLoader; 102 }; 103 104 ////////////////////////////////////////////////////////////// 105 // Script loader implementation 106 ////////////////////////////////////////////////////////////// 107 108 class ScriptLoader final : public JS::loader::ScriptLoaderInterface { 109 class MOZ_STACK_CLASS AutoCurrentScriptUpdater { 110 public: 111 AutoCurrentScriptUpdater(ScriptLoader* aScriptLoader, 112 nsIScriptElement* aCurrentScript) 113 : mOldScript(aScriptLoader->mCurrentScript), 114 mScriptLoader(aScriptLoader) { 115 nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentScript); 116 mScriptLoader->mCurrentScript = 117 node && !node->IsInShadowTree() ? aCurrentScript : nullptr; 118 } 119 120 ~AutoCurrentScriptUpdater() { 121 mScriptLoader->mCurrentScript.swap(mOldScript); 122 } 123 124 private: 125 nsCOMPtr<nsIScriptElement> mOldScript; 126 ScriptLoader* mScriptLoader; 127 }; 128 129 friend class JS::loader::ModuleLoadRequest; 130 friend class ScriptRequestProcessor; 131 friend class ModuleLoader; 132 friend class ScriptLoadHandler; 133 friend class AutoCurrentScriptUpdater; 134 135 public: 136 using MaybeSourceText = 137 mozilla::MaybeOneOf<JS::SourceText<char16_t>, JS::SourceText<Utf8Unit>>; 138 using ScriptLoadRequest = JS::loader::ScriptLoadRequest; 139 140 explicit ScriptLoader(Document* aDocument); 141 142 NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL 143 NS_DECL_CYCLE_COLLECTION_CLASS(ScriptLoader) 144 145 /** 146 * Called when the document that owns this script loader changes global. The 147 * argument is null when the document is detached from a window. 148 */ 149 void SetGlobalObject(nsIGlobalObject* aGlobalObject); 150 151 /** 152 * The loader maintains a weak reference to the document with 153 * which it is initialized. This call forces the reference to 154 * be dropped. 155 */ 156 void DropDocumentReference(); 157 158 /** 159 * Register this loader to the shared script cache. 160 * 161 * When the principal for the document for the loader is modified, 162 * DeregisterFromCache and RegisterToCache should be called to reflect the 163 * modification (See Document::SetPrincipals). 164 */ 165 void RegisterToCache(); 166 167 /** 168 * Deregister this loader from the shared script cache and 169 * clear the cache data associated with this loader. 170 */ 171 void DeregisterFromCache(); 172 173 // Methods for SharedScriptCache. 174 nsIPrincipal* LoaderPrincipal() const; 175 nsIPrincipal* PartitionedPrincipal() const; 176 177 bool ShouldBypassCache() const; 178 179 template <typename T> 180 bool HasLoaded(const T& aKey) { 181 // NOTE: ScriptLoader doesn't cache pending/loading requests, and 182 // this method is unsed. 183 return false; 184 } 185 186 /** 187 * Add an observer for all scripts loaded through this loader. 188 * 189 * @param aObserver observer for all script processing. 190 */ 191 nsresult AddObserver(nsIScriptLoaderObserver* aObserver) { 192 return mObservers.AppendObject(aObserver) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 193 } 194 195 /** 196 * Remove an observer. 197 * 198 * @param aObserver observer to be removed 199 */ 200 void RemoveObserver(nsIScriptLoaderObserver* aObserver) { 201 mObservers.RemoveObject(aObserver); 202 } 203 204 /** 205 * Process a script element. This will include both loading the 206 * source of the element if it is not inline and evaluating 207 * the script itself. 208 * 209 * If the script is an inline script that can be executed immediately 210 * (i.e. there are no other scripts pending) then ScriptAvailable 211 * and ScriptEvaluated will be called before the function returns. 212 * 213 * If true is returned the script could not be executed immediately. 214 * In this case ScriptAvailable is guaranteed to be called at a later 215 * point (as well as possibly ScriptEvaluated). 216 * 217 * @param aElement The element representing the script to be loaded and 218 * evaluated. 219 * @param aSourceText For inline non-trusted script, the source text after 220 * application of the default Trusted Types policy, a void string otherwise. 221 * See https://html.spec.whatwg.org/#prepare-the-script-element 222 */ 223 bool ProcessScriptElement(nsIScriptElement* aElement, 224 const nsAString& aSourceText); 225 226 /** 227 * Gets the currently executing script. This is useful if you want to 228 * generate a unique key based on the currently executing script. 229 */ 230 nsIScriptElement* GetCurrentScript() { return mCurrentScript; } 231 232 void ContinueParsingDocumentAfterCurrentScript() { 233 MOZ_ASSERT(mCurrentScript); 234 mContinueParsingDocumentAfterCurrentScript = true; 235 } 236 237 nsIScriptElement* GetCurrentParserInsertedScript() { 238 return mCurrentParserInsertedScript; 239 } 240 241 /** 242 * Whether the loader is enabled or not. 243 * When disabled, processing of new script elements is disabled. 244 * Any call to ProcessScriptElement() will return false. Note that 245 * this DOES NOT disable currently loading or executing scripts. 246 */ 247 bool GetEnabled() { return mEnabled; } 248 249 void SetEnabled(bool aEnabled) { 250 if (!mEnabled && aEnabled) { 251 ProcessPendingRequestsAsync(); 252 } 253 mEnabled = aEnabled; 254 } 255 256 ModuleLoader* GetModuleLoader() { return mModuleLoader; } 257 258 void RegisterContentScriptModuleLoader(ModuleLoader* aLoader); 259 void RegisterShadowRealmModuleLoader(ModuleLoader* aLoader); 260 261 /** 262 * Check whether to speculatively OMT parse scripts as soon as 263 * they are fetched, even if not a parser blocking request. 264 * Controlled by 265 * dom.script_loader.external_scripts.speculative_omt_parse_enabled 266 */ 267 bool SpeculativeOMTParsingEnabled() const { 268 return mSpeculativeOMTParsingEnabled; 269 } 270 271 /** 272 * Add/remove a blocker for parser-blocking scripts (and XSLT 273 * scripts). Blockers will stop such scripts from executing, but not from 274 * loading. 275 */ 276 void AddParserBlockingScriptExecutionBlocker() { 277 ++mParserBlockingBlockerCount; 278 } 279 280 void RemoveParserBlockingScriptExecutionBlocker() { 281 if (!--mParserBlockingBlockerCount && ReadyToExecuteScripts()) { 282 ProcessPendingRequestsAsync(); 283 } 284 } 285 286 /** 287 * Add/remove a blocker for execution of all scripts. Blockers will stop 288 * scripts from executing, but not from loading. 289 */ 290 void AddExecuteBlocker() { ++mBlockerCount; } 291 292 void RemoveExecuteBlocker() { 293 MOZ_ASSERT(mBlockerCount); 294 if (!--mBlockerCount) { 295 ProcessPendingRequestsAsync(); 296 } 297 } 298 299 /** 300 * Convert the given buffer to a UTF-16 string. If the buffer begins with a 301 * BOM, it is interpreted as that encoding; otherwise the first of |aChannel|, 302 * |aHintCharset|, or |aDocument| that provides a recognized encoding is used, 303 * or Windows-1252 if none of them do. 304 * 305 * Encoding errors in the buffer are converted to replacement characters, so 306 * allocation failure is the only way this function can fail. 307 * 308 * @param aChannel Channel corresponding to the data. May be null. 309 * @param aData The data to convert 310 * @param aLength Length of the data 311 * @param aHintCharset Character set hint (e.g., from a charset attribute). 312 * @param aDocument Document which the data is loaded for. May be null. 313 * @param aBufOut [out] fresh char16_t array containing data converted to 314 * Unicode. 315 * @param aLengthOut [out] Length of array returned in aBufOut in number 316 * of char16_t code units. 317 */ 318 static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, 319 uint32_t aLength, 320 const nsAString& aHintCharset, 321 Document* aDocument, 322 UniquePtr<char16_t[], JS::FreePolicy>& aBufOut, 323 size_t& aLengthOut); 324 325 /** 326 * Convert the given buffer to a UTF-8 string. If the buffer begins with a 327 * BOM, it is interpreted as that encoding; otherwise the first of |aChannel|, 328 * |aHintCharset|, or |aDocument| that provides a recognized encoding is used, 329 * or Windows-1252 if none of them do. 330 * 331 * Encoding errors in the buffer are converted to replacement characters, so 332 * allocation failure is the only way this function can fail. 333 * 334 * @param aChannel Channel corresponding to the data. May be null. 335 * @param aData The data to convert 336 * @param aLength Length of the data 337 * @param aHintCharset Character set hint (e.g., from a charset attribute). 338 * @param aDocument Document which the data is loaded for. May be null. 339 * @param aBufOut [out] fresh Utf8Unit array containing data converted to 340 * Unicode. 341 * @param aLengthOut [out] Length of array returned in aBufOut in UTF-8 code 342 * units (i.e. in bytes). 343 */ 344 static nsresult ConvertToUTF8(nsIChannel* aChannel, const uint8_t* aData, 345 uint32_t aLength, const nsAString& aHintCharset, 346 Document* aDocument, 347 UniquePtr<Utf8Unit[], JS::FreePolicy>& aBufOut, 348 size_t& aLengthOut); 349 350 /** 351 * Handle the completion of a stream. This is called by the 352 * ScriptLoadHandler object which observes the IncrementalStreamLoader 353 * loading the script. The streamed content is expected to be stored on the 354 * aRequest argument. 355 */ 356 nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader, 357 ScriptLoadRequest* aRequest, 358 nsresult aChannelStatus, nsresult aSRIStatus, 359 SRICheckDataVerifier* aSRIDataVerifier); 360 361 /** 362 * Returns wether any request is queued, and not executed yet. 363 */ 364 bool HasPendingRequests() const; 365 366 /** 367 * Returns wether there are any dynamic module import requests pending. 368 */ 369 bool HasPendingDynamicImports() const; 370 371 /** 372 * Processes any pending requests that are ready for processing. 373 */ 374 void ProcessPendingRequests(bool aAllowBypassingParserBlocking = false); 375 376 /** 377 * Starts deferring deferred scripts and puts them in the mDeferredRequests 378 * queue instead. 379 */ 380 void BeginDeferringScripts(); 381 382 /** 383 * Notifies the script loader that parsing is done. If aTerminated is true, 384 * this will drop any pending scripts that haven't run yet, otherwise it will 385 * do nothing. 386 */ 387 void ParsingComplete(bool aTerminated); 388 389 /** 390 * Notifies the script loader that the checkpoint to begin execution of defer 391 * scripts has been reached. This is either the end of of the document parse 392 * or the end of loading of parser-inserted stylesheets, whatever happens 393 * last. 394 * 395 * Otherwise, it will stop deferring scripts and immediately processes the 396 * mDeferredRequests queue. 397 * 398 * WARNING: This function will synchronously execute content scripts, so be 399 * prepared that the world might change around you. 400 */ 401 void DeferCheckpointReached(); 402 403 /** 404 * Returns the number of pending scripts, deferred or not. 405 */ 406 uint32_t HasPendingOrCurrentScripts() { 407 return mCurrentScript || mParserBlockingRequest; 408 } 409 410 /** 411 * Adds aURI to the preload list and starts loading it. 412 * 413 * @param aURI The URI of the external script. 414 * @param aCharset The charset parameter for the script. 415 * @param aType The type parameter for the script. 416 * @param aCrossOrigin The crossorigin attribute for the script. 417 * Void if not present. 418 * @param aFetchPriority 419 * <https://html.spec.whatwg.org/#the-script-element:attr-script-fetchpriority>. 420 * @param aIntegrity The expect hash url, if avail, of the request 421 422 * @param aScriptFromHead Whether or not the script was a child of head 423 */ 424 void PreloadURI(nsIURI* aURI, const nsAString& aCharset, 425 const nsAString& aType, const nsAString& aCrossOrigin, 426 const nsAString& aNonce, const nsAString& aFetchPriority, 427 const nsAString& aIntegrity, bool aScriptFromHead, 428 bool aAsync, bool aDefer, bool aLinkPreload, 429 const ReferrerPolicy aReferrerPolicy, 430 uint64_t aEarlyHintPreloaderId); 431 432 /** 433 * Process a request that was deferred so that the script could be compiled 434 * off thread. 435 */ 436 nsresult ProcessOffThreadRequest(ScriptLoadRequest* aRequest); 437 438 bool AddPendingChildLoader(ScriptLoader* aChild) { 439 // XXX(Bug 1631371) Check if this should use a fallible operation as it 440 // pretended earlier. Else, change the return type to void. 441 mPendingChildLoaders.AppendElement(aChild); 442 return true; 443 } 444 445 mozilla::dom::DocGroup* GetDocGroup() const; 446 447 /** 448 * Register the fact that we saw the load event, and that we need to perform 449 * the caching at the next loop cycle unless new scripts are waiting in the 450 * pipeline. 451 */ 452 void LoadEventFired(); 453 454 /** 455 * Destroy and prevent the ScriptLoader or the ScriptLoadRequests from owning 456 * any references to the JSScript or to the Request which might be used for 457 * caching. 458 */ 459 void Destroy(); 460 461 /* 462 * Get the currently active script. This is used as the initiating script when 463 * executing timeout handler scripts. 464 */ 465 static JS::loader::LoadedScript* GetActiveScript(JSContext* aCx); 466 467 Document* GetDocument() const { return mDocument; } 468 469 nsIURI* GetBaseURI() const override; 470 471 private: 472 ~ScriptLoader(); 473 474 already_AddRefed<ScriptLoadRequest> CreateLoadRequest( 475 JS::loader::ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement, 476 const nsAString& aScriptContent, nsIPrincipal* aTriggeringPrincipal, 477 mozilla::CORSMode aCORSMode, const nsAString& aNonce, 478 RequestPriority aRequestPriority, const SRIMetadata& aIntegrity, 479 ReferrerPolicy aReferrerPolicy, 480 JS::loader::ParserMetadata aParserMetadata, 481 ScriptLoadRequestType aRequestType); 482 483 /** 484 * Helper function to lookup the cache entry and associate it to the 485 * request if any. 486 */ 487 void TryUseCache( 488 ReferrerPolicy aReferrerPolicy, ScriptFetchOptions* aFetchOptions, 489 nsIURI* aURI, ScriptLoadRequest* aRequest, 490 nsIScriptElement* aElement = nullptr, const nsAString& aNonce = u""_ns, 491 ScriptLoadRequestType aRequestType = ScriptLoadRequestType::External); 492 493 /** 494 * Helper function to notify network observers for cached request. 495 */ 496 void EmulateNetworkEvents(ScriptLoadRequest* aRequest); 497 498 void NotifyObserversForCachedScript( 499 nsIURI* aURI, nsINode* aContext, nsIPrincipal* aTriggeringPrincipal, 500 nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType, 501 SubResourceNetworkMetadataHolder* aNetworkMetadata); 502 503 /** 504 * Unblocks the creator parser of the parser-blocking scripts. 505 */ 506 void UnblockParser(ScriptLoadRequest* aParserBlockingRequest); 507 508 /** 509 * Asynchronously resumes the creator parser of the parser-blocking scripts. 510 */ 511 void ContinueParserAsync(ScriptLoadRequest* aParserBlockingRequest); 512 513 bool ProcessExternalScript(nsIScriptElement* aElement, 514 JS::loader::ScriptKind aScriptKind, 515 nsIContent* aScriptContent); 516 517 bool ProcessInlineScript(nsIScriptElement* aElement, 518 JS::loader::ScriptKind aScriptKind, 519 const nsAString& aSourceText); 520 521 enum class CacheBehavior : uint8_t { 522 DoNothing, 523 Insert, 524 Evict, 525 }; 526 527 CacheBehavior GetCacheBehavior(ScriptLoadRequest* aRequest); 528 529 void TryCacheRequest(ScriptLoadRequest* aRequest); 530 531 JS::loader::ScriptLoadRequest* LookupPreloadRequest( 532 nsIScriptElement* aElement, JS::loader::ScriptKind aScriptKind, 533 const SRIMetadata& aSRIMetadata); 534 535 void GetSRIMetadata(const nsAString& aIntegrityAttr, 536 SRIMetadata* aMetadataOut); 537 538 /** 539 * Given a script element, get the referrer policy should be applied to load 540 * requests. 541 */ 542 ReferrerPolicy GetReferrerPolicy(nsIScriptElement* aElement); 543 544 /** 545 * Helper function to check the content policy for a given request. 546 */ 547 nsresult CheckContentPolicy(nsIScriptElement* aElement, 548 const nsAString& aNonce, 549 ScriptLoadRequest* aRequest, 550 ScriptFetchOptions* aFetchOptions, nsIURI* aURI); 551 552 /** 553 * Helper function to determine whether an about: page loads a chrome: URI. 554 * Please note that this function only returns true if: 555 * * the about: page uses a ContentPrincipal with scheme about: 556 * * the about: page is not linkable from content 557 * (e.g. the function will return false for about:blank or about:srcdoc) 558 */ 559 static bool IsAboutPageLoadingChromeURI(ScriptLoadRequest* aRequest, 560 Document* aDocument); 561 562 /** 563 * Start a load for aRequest's URI. 564 */ 565 nsresult StartLoad(ScriptLoadRequest* aRequest, 566 const Maybe<nsAutoString>& aCharsetForPreload); 567 /** 568 * Start a load for a classic script URI. 569 * Sets up the necessary security flags before calling StartLoadInternal. 570 */ 571 nsresult StartClassicLoad(ScriptLoadRequest* aRequest, 572 const Maybe<nsAutoString>& aCharsetForPreload); 573 574 static void PrepareCacheInfoChannel(nsIChannel* aChannel, 575 ScriptLoadRequest* aRequest); 576 577 static void PrepareRequestPriorityAndRequestDependencies( 578 nsIChannel* aChannel, ScriptLoadRequest* aRequest); 579 580 [[nodiscard]] static nsresult PrepareHttpRequestAndInitiatorType( 581 nsIChannel* aChannel, ScriptLoadRequest* aRequest, 582 const Maybe<nsAutoString>& aCharsetForPreload); 583 584 [[nodiscard]] nsresult PrepareIncrementalStreamLoader( 585 nsIIncrementalStreamLoader** aOutLoader, nsIChannel* aChannel, 586 ScriptLoadRequest* aRequest); 587 588 /** 589 * Start a load for a script (module or classic) URI. 590 * 591 * aCharsetForPreload is only needed when this load is a preload (via 592 * ScriptLoader::PreloadURI), because ScriptLoadRequest doesn't 593 * have this information. 594 */ 595 nsresult StartLoadInternal(ScriptLoadRequest* aRequest, 596 nsSecurityFlags securityFlags, 597 const Maybe<nsAutoString>& aCharsetForPreload); 598 599 /** 600 * Abort the current stream, and re-start with a new load request from scratch 601 * without requesting any alternate data. Returns NS_BINDING_RETARGETED on 602 * success, as this error code is used to abort the input stream. 603 */ 604 nsresult RestartLoad(ScriptLoadRequest* aRequest); 605 606 void HandleLoadError(ScriptLoadRequest* aRequest, nsresult aResult); 607 608 void HandleLoadErrorAndProcessPendingRequests(ScriptLoadRequest* aRequest, 609 nsresult aResult); 610 611 /** 612 * Process any pending requests asynchronously (i.e. off an event) if there 613 * are any. Note that this is a no-op if there aren't any currently pending 614 * requests. 615 * 616 * This function is virtual to allow cross-library calls to SetEnabled() 617 */ 618 void ProcessPendingRequestsAsync(); 619 620 void ProcessPendingRequestsAsyncBypassParserBlocking(); 621 622 /** 623 * If true, the loader is ready to execute parser-blocking scripts, and so are 624 * all its ancestors. If the loader itself is ready but some ancestor is not, 625 * this function will add an execute blocker and ask the ancestor to remove it 626 * once it becomes ready. 627 */ 628 bool ReadyToExecuteParserBlockingScripts(); 629 630 /** 631 * Return whether just this loader is ready to execute parser-blocking 632 * scripts. 633 */ 634 bool SelfReadyToExecuteParserBlockingScripts() { 635 return ReadyToExecuteScripts() && !mParserBlockingBlockerCount; 636 } 637 638 /** 639 * Return whether this loader is ready to execute scripts in general. 640 */ 641 bool ReadyToExecuteScripts() { return mEnabled && !mBlockerCount; } 642 643 nsresult VerifySRI(ScriptLoadRequest* aRequest, 644 nsIIncrementalStreamLoader* aLoader, nsresult aSRIStatus, 645 SRICheckDataVerifier* aSRIDataVerifier) const; 646 647 nsresult SaveSRIHash(ScriptLoadRequest* aRequest, 648 SRICheckDataVerifier* aSRIDataVerifier) const; 649 650 void ReportErrorToConsole(ScriptLoadRequest* aRequest, 651 nsresult aResult) const override; 652 653 void ReportWarningToConsole( 654 ScriptLoadRequest* aRequest, const char* aMessageName, 655 const nsTArray<nsString>& aParams = nsTArray<nsString>()) const override; 656 657 void ReportPreloadErrorsToConsole(ScriptLoadRequest* aRequest); 658 659 nsIConsoleReportCollector* GetConsoleReportCollector() const override { 660 return mReporter; 661 } 662 663 nsresult AttemptOffThreadScriptCompile(ScriptLoadRequest* aRequest, 664 bool* aCouldCompileOut); 665 666 nsresult CreateOffThreadTask(JSContext* aCx, ScriptLoadRequest* aRequest, 667 JS::CompileOptions& aOptions, 668 CompileOrDecodeTask** aCompileOrDecodeTask); 669 670 nsresult ProcessRequest(ScriptLoadRequest* aRequest); 671 nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest); 672 void FireScriptAvailable(nsresult aResult, ScriptLoadRequest* aRequest); 673 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) 674 MOZ_CAN_RUN_SCRIPT_BOUNDARY void FireScriptEvaluated( 675 nsresult aResult, ScriptLoadRequest* aRequest); 676 677 // Implements https://html.spec.whatwg.org/#execute-the-script-block 678 nsresult EvaluateScriptElement(ScriptLoadRequest* aRequest); 679 680 // Instantiate classic script from one of the following data: 681 // * text source 682 // * serialized stencil 683 // * cached stencil 684 void InstantiateClassicScriptFromAny( 685 JSContext* aCx, JS::CompileOptions& aCompileOptions, 686 ScriptLoadRequest* aRequest, JS::MutableHandle<JSScript*> aScript, 687 JS::Handle<JS::Value> aDebuggerPrivateValue, 688 JS::Handle<JSScript*> aDebuggerIntroductionScript, ErrorResult& aRv); 689 690 // Instantiate classic script from one of the following data: 691 // * text source 692 // * serialized stencil 693 // 694 // aStencilOut is set to the compiled stencil. 695 void InstantiateClassicScriptFromMaybeEncodedSource( 696 JSContext* aCx, JS::CompileOptions& aCompileOptions, 697 ScriptLoadRequest* aRequest, JS::MutableHandle<JSScript*> aScript, 698 JS::Handle<JS::Value> aDebuggerPrivateValue, 699 JS::Handle<JSScript*> aDebuggerIntroductionScript, ErrorResult& aRv); 700 701 // Instantiate classic script from the following data: 702 // * cached stencil 703 void InstantiateClassicScriptFromCachedStencil( 704 JSContext* aCx, JS::CompileOptions& aCompileOptions, 705 ScriptLoadRequest* aRequest, JS::Stencil* aStencil, 706 JS::MutableHandle<JSScript*> aScript, 707 JS::Handle<JS::Value> aDebuggerPrivateValue, 708 JS::Handle<JSScript*> aDebuggerIntroductionScript, ErrorResult& aRv); 709 710 static nsCString& BytecodeMimeTypeFor(const ScriptLoadRequest* aRequest); 711 static nsCString& BytecodeMimeTypeFor( 712 const JS::loader::LoadedScript* aLoadedScript); 713 714 // Queue the script load request for caching if we decided to cache it, or 715 // cleanup the script load request fields otherwise. 716 // 717 // This method must be called after executing the script. 718 nsresult MaybePrepareForDiskCacheAfterExecute(ScriptLoadRequest* aRequest, 719 nsresult aRv); 720 721 // Queue the top-level module load request for caching if we decided to cache 722 // it, or cleanup the module load request fields otherwise. 723 // 724 // This method must be called after executing the script. 725 nsresult MaybePrepareModuleForDiskCacheAfterExecute( 726 ModuleLoadRequest* aRequest, nsresult aRv) override; 727 728 // Implements https://html.spec.whatwg.org/#run-a-classic-script 729 nsresult EvaluateScript(nsIGlobalObject* aGlobalObject, 730 ScriptLoadRequest* aRequest); 731 732 /** 733 * Register the script load request to be cached on the disk. 734 * 735 * The caller can call this at the same time instantiating the stencil, 736 * and also start collecting delazifications. 737 * 738 * The cache handling will be performed when the page initialization ends. 739 * The page initialization end is defined as being the time when the load 740 * event got received, and when no more scripts are waiting to be executed. 741 */ 742 void RegisterForDiskCache(ScriptLoadRequest* aRequest); 743 744 /** 745 * Check if all conditions are met, i-e that the onLoad event fired and that 746 * no more script have to be processed. If all conditions are met, queue an 747 * event to perform the cache handling, which saves them to the necko cache. 748 */ 749 void MaybeUpdateDiskCache() override; 750 751 /** 752 * Iterate over all scripts and save them to the necko cache. 753 */ 754 void UpdateDiskCache(); 755 756 public: 757 /** 758 * Encode the stencils and compress it. 759 * aLoadedScript is used only for logging purpose, in order to allow 760 * performing this off main thread. 761 */ 762 static bool EncodeAndCompress(JS::FrontendContext* aFc, 763 const JS::loader::LoadedScript* aLoadedScript, 764 JS::Stencil* aStencil, 765 const JS::TranscodeBuffer& aSRI, 766 Vector<uint8_t>& aCompressed); 767 768 /** 769 * Save the serialized and maybe-compressed stencil to the necko cache. 770 */ 771 static bool SaveToDiskCache(const JS::loader::LoadedScript* aLoadedScript, 772 const Vector<uint8_t>& aCompressed); 773 774 private: 775 /** 776 * Discard all disk-cache-related info for scripts queued for the disk cache. 777 * 778 * This should be used when the ScriptLoader is getting destroyed, or 779 * when it hits any critical error. 780 */ 781 void GiveUpDiskCaching(); 782 783 already_AddRefed<nsIGlobalObject> GetGlobalForRequest( 784 ScriptLoadRequest* aRequest); 785 786 already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject(); 787 788 // Fill in CompileOptions, as well as produce the introducer script for 789 // subsequent calls to UpdateDebuggerMetadata 790 nsresult FillCompileOptionsForRequest( 791 JSContext* aCx, ScriptLoadRequest* aRequest, JS::CompileOptions* aOptions, 792 JS::MutableHandle<JSScript*> aIntroductionScript) override; 793 794 uint32_t NumberOfProcessors(); 795 int32_t PhysicalSizeOfMemoryInGB(); 796 797 nsresult PrepareLoadedRequest(ScriptLoadRequest* aRequest, 798 nsIIncrementalStreamLoader* aLoader, 799 nsresult aStatus); 800 801 void AddDeferRequest(ScriptLoadRequest* aRequest); 802 void AddAsyncRequest(ScriptLoadRequest* aRequest); 803 bool MaybeRemovedDeferRequests(); 804 805 bool ShouldApplyDelazifyStrategy(ScriptLoadRequest* aRequest); 806 void ApplyDelazifyStrategy(JS::CompileOptions* aOptions); 807 808 bool ShouldCompileOffThread(ScriptLoadRequest* aRequest); 809 810 void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest); 811 812 bool IsBeforeFCP(); 813 814 public: 815 struct DiskCacheStrategy { 816 bool mIsDisabled = false; 817 bool mHasSourceLengthMin = false; 818 bool mHasFetchCountMin = false; 819 uint8_t mFetchCountMin = 0; 820 size_t mSourceLengthMin = 0; 821 }; 822 823 static DiskCacheStrategy GetDiskCacheStrategy(); 824 825 private: 826 // Check whether the request should be saved to the following or not: 827 // * in-memory cache as Stencil 828 // * necko alternative stream as Stencil XDR 829 // 830 // If the request is a non-top-level module request and it passed the 831 // condition, it's stored into mDiskCacheableDependencyModules in order 832 // to iterate over them later. 833 void CalculateCacheFlag(ScriptLoadRequest* aRequest); 834 835 void RunScriptWhenSafe(ScriptLoadRequest* aRequest); 836 837 /** 838 * Cancel and remove all outstanding load requests, including waiting for any 839 * off thread compilations to finish. 840 */ 841 void CancelAndClearScriptLoadRequests(); 842 843 Document* mDocument; // [WEAK] 844 nsCOMArray<nsIScriptLoaderObserver> mObservers; 845 846 // The following lists maintains the list of requests for each phase and 847 // situation. 848 // Each request can be a part of at most one list. 849 850 // Holds non-async, non-parser-created requests until it's evaluated or it 851 // hits load error. 852 JS::loader::ScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests; 853 854 // Holds async requests until it's loaded or it hits load error. 855 // When they have been loaded they are moved to mLoadedAsyncRequests. 856 JS::loader::ScriptLoadRequestList mLoadingAsyncRequests; 857 858 // Holds async script requests and dynamic module import 859 // requests, which are processed in the same way, until it's evaluated, 860 // or it's passed to off-thread. 861 JS::loader::ScriptLoadRequestList mLoadedAsyncRequests; 862 863 // Holds non-async, parser-created, defer requests, until it's evaluated 864 // or it hits load error. 865 JS::loader::ScriptLoadRequestList mDeferRequests; 866 867 // Holds parser-created XSLT requests, until it's evaluated or it hits 868 // load error. 869 JS::loader::ScriptLoadRequestList mXSLTRequests; 870 871 RefPtr<ScriptLoadRequest> mParserBlockingRequest; 872 873 // Holds requests which is passed to off-thread compilation. 874 // When the off-thread compilation finishes, the request is added back to 875 // the original list if any. 876 JS::loader::ScriptLoadRequestList mOffThreadCompilingRequests; 877 878 // Holds non-top-level module requests which passed disk caching conditions, 879 // until it's queued to mDiskCacheQueue. 880 // 881 // TODO: Remove this and per-ScriptLoader caching queue (bug 1902951). 882 JS::loader::ScriptLoadRequestList mDiskCacheableDependencyModules; 883 884 // Holds already-evaluted requests' script that are holding a stencil which 885 // has to be saved on the disk cache, until it's cached or the caching is 886 // aborted. 887 nsTArray<RefPtr<JS::loader::LoadedScript>> mDiskCacheQueue; 888 889 // In mRequests, the additional information here is stored by the element. 890 struct PreloadInfo { 891 RefPtr<ScriptLoadRequest> mRequest; 892 nsString mCharset; 893 }; 894 895 friend void ImplCycleCollectionUnlink(ScriptLoader::PreloadInfo& aField); 896 friend void ImplCycleCollectionTraverse( 897 nsCycleCollectionTraversalCallback& aCallback, 898 ScriptLoader::PreloadInfo& aField, const char* aName, uint32_t aFlags); 899 900 struct PreloadRequestComparator { 901 bool Equals(const PreloadInfo& aPi, 902 ScriptLoadRequest* const& aRequest) const { 903 return aRequest == aPi.mRequest; 904 } 905 }; 906 907 struct PreloadURIComparator { 908 bool Equals(const PreloadInfo& aPi, nsIURI* const& aURI) const; 909 }; 910 911 nsTArray<PreloadInfo> mPreloads; 912 913 nsCOMPtr<nsIScriptElement> mCurrentScript; 914 nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript; 915 nsTArray<RefPtr<ScriptLoader>> mPendingChildLoaders; 916 uint32_t mParserBlockingBlockerCount; 917 uint32_t mBlockerCount; 918 uint32_t mNumberOfProcessors; 919 uint32_t mTotalFullParseSize; 920 int32_t mPhysicalSizeOfMemory; 921 bool mEnabled; 922 bool mDeferEnabled; 923 bool mSpeculativeOMTParsingEnabled; 924 bool mDeferCheckpointReached; 925 bool mBlockingDOMContentLoaded; 926 bool mLoadEventFired; 927 bool mGiveUpDiskCaching; 928 bool mContinueParsingDocumentAfterCurrentScript; 929 bool mHadFCPDoNotUseDirectly; 930 931 TimeDuration mMainThreadParseTime; 932 933 nsCOMPtr<nsIConsoleReportCollector> mReporter; 934 935 // ShutdownObserver for off thread compilations 936 RefPtr<AsyncCompileShutdownObserver> mShutdownObserver; 937 938 RefPtr<ModuleLoader> mModuleLoader; 939 nsTArray<RefPtr<ModuleLoader>> mWebExtModuleLoaders; 940 nsTArray<RefPtr<ModuleLoader>> mShadowRealmModuleLoaders; 941 942 RefPtr<SharedScriptCache> mCache; 943 944 nsCOMPtr<nsITimer> mProcessPendingRequestsAsyncBypassParserBlocking; 945 946 // Logging 947 public: 948 static LazyLogModule gCspPRLog; 949 static LazyLogModule gScriptLoaderLog; 950 }; 951 952 class nsAutoScriptLoaderDisabler { 953 public: 954 explicit nsAutoScriptLoaderDisabler(Document* aDoc); 955 956 ~nsAutoScriptLoaderDisabler(); 957 958 bool mWasEnabled; 959 RefPtr<ScriptLoader> mLoader; 960 }; 961 962 } // namespace dom 963 } // namespace mozilla 964 965 #endif // mozilla_dom_ScriptLoader_h