tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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