tor-browser

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

LoadedScript.h (22881B)


      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_LoadedScript_h
      8 #define js_loader_LoadedScript_h
      9 
     10 #include "js/AllocPolicy.h"
     11 #include "js/experimental/JSStencil.h"
     12 #include "js/Transcoding.h"
     13 
     14 #include "mozilla/AlreadyAddRefed.h"
     15 #include "mozilla/Maybe.h"
     16 #include "mozilla/MaybeOneOf.h"
     17 #include "mozilla/MemoryReporting.h"
     18 #include "mozilla/RefPtr.h"
     19 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
     20 #include "mozilla/Variant.h"
     21 #include "mozilla/Vector.h"
     22 
     23 #include "nsCOMPtr.h"
     24 #include "nsCycleCollectionParticipant.h"
     25 #include "nsICacheInfoChannel.h"  // nsICacheInfoChannel
     26 #include "nsIMemoryReporter.h"
     27 
     28 #include "jsapi.h"
     29 #include "ScriptKind.h"
     30 #include "ScriptFetchOptions.h"
     31 
     32 class nsIURI;
     33 
     34 namespace JS::loader {
     35 
     36 class ScriptLoadRequest;
     37 
     38 using Utf8Unit = mozilla::Utf8Unit;
     39 
     40 void HostAddRefTopLevelScript(const Value& aPrivate);
     41 void HostReleaseTopLevelScript(const Value& aPrivate);
     42 
     43 class ClassicScript;
     44 class ModuleScript;
     45 class EventScript;
     46 class LoadContextBase;
     47 
     48 // A LoadedScript is a place where the Script is stored once it is loaded. It is
     49 // not unique to a load, and can be shared across loads as long as it is
     50 // properly ref-counted by each load instance.
     51 //
     52 // When the load is not performed, the URI represents the resource to be loaded,
     53 // and it is replaced by the absolute resource location once loaded.
     54 //
     55 // As the LoadedScript can be shared, using the SharedSubResourceCache, it is
     56 // exposed to the memory reporter such that sharing might be accounted for
     57 // properly.
     58 class LoadedScript : public nsIMemoryReporter {
     59 protected:
     60  LoadedScript(ScriptKind aKind, mozilla::dom::ReferrerPolicy aReferrerPolicy,
     61               ScriptFetchOptions* aFetchOptions, nsIURI* aURI);
     62 
     63  LoadedScript(const LoadedScript& aOther);
     64 
     65  template <typename T, typename... Args>
     66  friend RefPtr<T> mozilla::MakeRefPtr(Args&&... aArgs);
     67 
     68  virtual ~LoadedScript();
     69 
     70 public:
     71  // When the memory should be reported, register it using RegisterMemoryReport,
     72  // and make sure to call SizeOfIncludingThis in the enclosing container.
     73  //
     74  // Each reported script would be listed under
     75  // `explicit/js/script/loaded-script/<kind>`.
     76  void RegisterMemoryReport();
     77  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
     78 
     79 public:
     80  NS_DECL_CYCLE_COLLECTING_ISUPPORTS;
     81  NS_DECL_NSIMEMORYREPORTER;
     82  NS_DECL_CYCLE_COLLECTION_CLASS(LoadedScript)
     83 
     84  bool IsClassicScript() const { return mKind == ScriptKind::eClassic; }
     85  bool IsModuleScript() const { return mKind == ScriptKind::eModule; }
     86  bool IsEventScript() const { return mKind == ScriptKind::eEvent; }
     87  bool IsImportMapScript() const { return mKind == ScriptKind::eImportMap; }
     88 
     89  inline ClassicScript* AsClassicScript();
     90  inline ModuleScript* AsModuleScript();
     91 
     92  // Used to propagate Fetch Options to child modules
     93  ScriptFetchOptions* GetFetchOptions() const { return mFetchOptions; }
     94 
     95  mozilla::dom::ReferrerPolicy ReferrerPolicy() const {
     96    return mReferrerPolicy;
     97  }
     98 
     99  nsIURI* GetURI() const { return mURI; }
    100  void SetBaseURL(nsIURI* aBaseURL) { mBaseURL = aBaseURL; }
    101  nsIURI* BaseURL() const { return mBaseURL; }
    102 
    103  void AssociateWithScript(JSScript* aScript);
    104 
    105 public:
    106  // ===========================================================================
    107  // Encoding of the content provided by the network, or refined by the JS
    108  // engine.
    109  template <typename... Ts>
    110  using Variant = mozilla::Variant<Ts...>;
    111 
    112  template <typename... Ts>
    113  using VariantType = mozilla::VariantType<Ts...>;
    114 
    115  // Type of data this instance holds, which is either provided by the nsChannel
    116  // or retrieved from the cache.
    117  enum class DataType : uint8_t {
    118    // This script haven't yet received the data.
    119    eUnknown,
    120 
    121    // This script is received as a plain text from the channel.
    122    // mScriptData holds the text source, and mStencil holds the compiled
    123    // stencil.
    124    // mSRIAndSerializedStencil holds the SRI.
    125    eTextSource,
    126 
    127    // This script is received as a serialized stencil from the channel,
    128    // mSRIAndSerializedStencil holds the SRI and the serialized stencil, and
    129    // mStencil holds the decoded stencil.
    130    eSerializedStencil,
    131 
    132    // This script is cached from the previous load.
    133    // mStencil holds the cached stencil, and mSRIAndSerializedStencil holds
    134    // the SRI. mScriptData is unused.
    135    eCachedStencil
    136  };
    137 
    138  // Use a vector backed by the JS allocator for script text so that contents
    139  // can be transferred in constant time to the JS engine, not copied in linear
    140  // time.
    141  template <typename Unit>
    142  using ScriptTextBuffer = mozilla::Vector<Unit, 0, js::MallocAllocPolicy>;
    143 
    144  using MaybeSourceText =
    145      mozilla::MaybeOneOf<SourceText<char16_t>, SourceText<Utf8Unit>>;
    146 
    147  // ==== Methods to query the data type ====
    148 
    149  bool IsUnknownDataType() const { return mDataType == DataType::eUnknown; }
    150  bool IsTextSource() const { return mDataType == DataType::eTextSource; }
    151  bool IsSerializedStencil() const {
    152    return mDataType == DataType::eSerializedStencil;
    153  }
    154  bool IsCachedStencil() const { return mDataType == DataType::eCachedStencil; }
    155 
    156  // ==== Methods to convert the data type ====
    157 
    158  void SetUnknownDataType() {
    159    mDataType = DataType::eUnknown;
    160    mScriptData.reset();
    161  }
    162 
    163  void SetTextSource(LoadContextBase* maybeLoadContext) {
    164    MOZ_ASSERT(IsUnknownDataType());
    165    mDataType = DataType::eTextSource;
    166    mScriptData.emplace(VariantType<ScriptTextBuffer<Utf8Unit>>());
    167  }
    168 
    169  void SetSerializedStencil() {
    170    MOZ_ASSERT(IsUnknownDataType());
    171    mDataType = DataType::eSerializedStencil;
    172  }
    173 
    174  void ConvertToCachedStencil() {
    175    MOZ_ASSERT(HasStencil());
    176    SetUnknownDataType();
    177    mDataType = DataType::eCachedStencil;
    178  }
    179 
    180  bool IsUTF16Text() const {
    181    return mScriptData->is<ScriptTextBuffer<char16_t>>();
    182  }
    183  bool IsUTF8Text() const {
    184    return mScriptData->is<ScriptTextBuffer<Utf8Unit>>();
    185  }
    186 
    187  // ==== Methods to access the text soutce ====
    188 
    189  template <typename Unit>
    190  const ScriptTextBuffer<Unit>& ScriptText() const {
    191    MOZ_ASSERT(IsTextSource());
    192    return mScriptData->as<ScriptTextBuffer<Unit>>();
    193  }
    194  template <typename Unit>
    195  ScriptTextBuffer<Unit>& ScriptText() {
    196    MOZ_ASSERT(IsTextSource());
    197    return mScriptData->as<ScriptTextBuffer<Unit>>();
    198  }
    199 
    200  size_t ScriptTextLength() const {
    201    MOZ_ASSERT(IsTextSource());
    202    return IsUTF16Text() ? ScriptText<char16_t>().length()
    203                         : ScriptText<Utf8Unit>().length();
    204  }
    205 
    206  // Get source text.  On success |aMaybeSource| will contain either UTF-8 or
    207  // UTF-16 source; on failure it will remain in its initial state.
    208  nsresult GetScriptSource(JSContext* aCx, MaybeSourceText* aMaybeSource,
    209                           LoadContextBase* aMaybeLoadContext);
    210 
    211  void ClearScriptText() {
    212    MOZ_ASSERT(IsTextSource());
    213    return IsUTF16Text() ? ScriptText<char16_t>().clearAndFree()
    214                         : ScriptText<Utf8Unit>().clearAndFree();
    215  }
    216 
    217  size_t ReceivedScriptTextLength() const { return mReceivedScriptTextLength; }
    218 
    219  void SetReceivedScriptTextLength(size_t aLength) {
    220    mReceivedScriptTextLength = aLength;
    221  }
    222 
    223  // ==== Methods to access the serialized data or the SRI part ====
    224  // mSRIAndSerializedStencil field is shared between two separate consumers.
    225  // See mSRIAndSerializedStencil comment for more info.
    226 
    227  // ---- For SRI-only consumers ----
    228 
    229  bool CanHaveSRIOnly() const { return IsTextSource() || IsCachedStencil(); }
    230 
    231  bool HasSRI() {
    232    MOZ_ASSERT(CanHaveSRIOnly());
    233    return !mSRIAndSerializedStencil.empty();
    234  }
    235 
    236  TranscodeBuffer& SRI() {
    237    MOZ_ASSERT(CanHaveSRIOnly());
    238    return mSRIAndSerializedStencil;
    239  }
    240 
    241  void DropSRI() {
    242    MOZ_ASSERT(CanHaveSRIOnly());
    243    mSRIAndSerializedStencil.clearAndFree();
    244  }
    245 
    246  // ---- For SRI and serialized Stencil consumers ---
    247 
    248  bool CanHaveSRIAndSerializedStencil() const { return IsSerializedStencil(); }
    249 
    250  TranscodeBuffer& SRIAndSerializedStencil() {
    251    MOZ_ASSERT(CanHaveSRIAndSerializedStencil());
    252    return mSRIAndSerializedStencil;
    253  }
    254  TranscodeRange SerializedStencil() const {
    255    MOZ_ASSERT(CanHaveSRIAndSerializedStencil());
    256    const auto& buf = mSRIAndSerializedStencil;
    257    auto offset = mSerializedStencilOffset;
    258    return TranscodeRange(buf.begin() + offset, buf.length() - offset);
    259  }
    260 
    261  // ---- Methods shared between both consumers ----
    262 
    263  size_t GetSRILength() const {
    264    MOZ_ASSERT(CanHaveSRIOnly() || CanHaveSRIAndSerializedStencil());
    265    return mSerializedStencilOffset;
    266  }
    267  void SetSRILength(size_t sriLength) {
    268    MOZ_ASSERT(CanHaveSRIOnly() || CanHaveSRIAndSerializedStencil());
    269    mSerializedStencilOffset = AlignTranscodingBytecodeOffset(sriLength);
    270  }
    271 
    272  bool HasNoSRIOrSRIAndSerializedStencil() const {
    273    MOZ_ASSERT(CanHaveSRIOnly() || CanHaveSRIAndSerializedStencil());
    274    return mSRIAndSerializedStencil.empty();
    275  }
    276 
    277  void DropSRIOrSRIAndSerializedStencil() {
    278    MOZ_ASSERT(CanHaveSRIOnly() || CanHaveSRIAndSerializedStencil());
    279    mSRIAndSerializedStencil.clearAndFree();
    280  }
    281 
    282  // ==== Methods to access the stencil ====
    283 
    284  bool HasStencil() const { return mStencil; }
    285 
    286  Stencil* GetStencil() const {
    287    MOZ_ASSERT(!IsUnknownDataType());
    288    MOZ_ASSERT(HasStencil());
    289    return mStencil;
    290  }
    291 
    292  void SetStencil(Stencil* aStencil) {
    293    MOZ_ASSERT(aStencil);
    294    MOZ_ASSERT(!HasStencil());
    295    mStencil = aStencil;
    296  }
    297 
    298  void ClearStencil() { mStencil = nullptr; }
    299 
    300  // ==== Methods to access the disk cache reference ====
    301 
    302  // Check the reference to the cache info channel, which is used by the disk
    303  // cache.
    304  bool HasDiskCacheReference() const { return !!mCacheEntry; }
    305 
    306  // Drop the reference to the cache info channel.
    307  void DropDiskCacheReference() { mCacheEntry = nullptr; }
    308 
    309  void DropDiskCacheReferenceAndSRI() {
    310    DropDiskCacheReference();
    311    if (IsTextSource()) {
    312      DropSRI();
    313    }
    314  }
    315 
    316  // ==== Other methods ====
    317 
    318  void SetTookLongInPreviousRuns() { mTookLongInPreviousRuns = true; }
    319  bool TookLongInPreviousRuns() const { return mTookLongInPreviousRuns; }
    320 
    321  /*
    322   * Set the mBaseURL, based on aChannel.
    323   * aOriginalURI is the result of aChannel->GetOriginalURI.
    324   */
    325  void SetBaseURLFromChannelAndOriginalURI(nsIChannel* aChannel,
    326                                           nsIURI* aOriginalURI);
    327 
    328  bool IsDirty() const { return mIsDirty; }
    329  void SetDirty() {
    330    MOZ_ASSERT(HasCacheEntryId());
    331    mIsDirty = true;
    332  }
    333  void UnsetDirty() {
    334    MOZ_ASSERT(HasCacheEntryId());
    335    mIsDirty = false;
    336  }
    337 
    338  bool HasCacheEntryId() const { return mCacheEntryId != InvalidCacheEntryId; }
    339  uint64_t CacheEntryId() const {
    340    MOZ_ASSERT(HasCacheEntryId());
    341    return mCacheEntryId;
    342  }
    343  void SetCacheEntryId(uint64_t aId) {
    344    mCacheEntryId = aId;
    345 
    346    // mCacheEntryId is 48bits.  Verify no overflow happened.
    347    MOZ_ASSERT(mCacheEntryId == aId);
    348  }
    349 
    350  void AddFetchCount() {
    351    if (mFetchCount < UINT8_MAX) {
    352      mFetchCount++;
    353    }
    354  }
    355 
    356 public:
    357  // Fields.
    358 
    359  // Determine whether the mScriptData or mSRIAndSerializedStencil is used.
    360  // See DataType description for more info.
    361  DataType mDataType;
    362 
    363  // The consumer-defined number of times that this loaded script is used.
    364  //
    365  // In DOM ScriptLoader, this is used for counting the number of times that
    366  // the in-memory-cached script is used, clamped at UINT8_MAX.
    367  uint8_t mFetchCount = 0;
    368 
    369 private:
    370  const ScriptKind mKind;
    371 
    372 protected:
    373  // The referrer policy used for the initial fetch and for fetching any
    374  // imported modules
    375  mozilla::dom::ReferrerPolicy mReferrerPolicy;
    376 
    377 public:
    378  // Offset of the serialized Stencil in mSRIAndSerializedStencil.
    379  uint32_t mSerializedStencilOffset;
    380 
    381 private:
    382  static constexpr uint64_t InvalidCacheEntryId = 0;
    383 
    384  // The cache entry ID of this script.
    385  //
    386  // 0 if the response doesn't have the corresponding cache entry,
    387  // or any other failure happened.
    388  //
    389  // This value comes from mozilla::net::CacheEntry::mCacheEntryId,
    390  // which comes from mozilla::net::CacheEntry::GetNextId.
    391  // It generates sequential IDs from 1 (thus 0 is treated as invalid value),
    392  // and the ID is valid within single browser session.
    393  //
    394  // In order to pack this field with mIsDirty below, we use shorter bits than
    395  // the original mozilla::net::CacheEntry::mCacheEntryId type (uint64_t).
    396  //
    397  // As long as the per-session sequential ID is the sole source of this value,
    398  // 48 bits should be sufficient.  1000 new IDs per second for 365 days
    399  // becomes 0x7_57b1_2c00, which is 35 bits.
    400  uint64_t mCacheEntryId : 48;
    401 
    402  // Set to true in the following situation:
    403  //   * this is cached in SharedScriptCache
    404  //   * A behavior around the network request is modified, and
    405  //     the cache needs validation on the necko side
    406  //
    407  // NOTE: In order to pack this with the mCacheEntryId above on windows,
    408  //       this must be uint64_t.
    409  uint64_t mIsDirty : 1;
    410 
    411  // Set to true if executing the top-level script takes long.
    412  // This can be used for scheduling the script execution in subsequent loads.
    413  // The threshold of "takes long" is user-defined.
    414  // See dom::ScriptLoader::EvaluateScript for the example case
    415  //
    416  // TODO: Move this into JS::Stencil, and save to the disk cache (bug 2005128)
    417  uint64_t mTookLongInPreviousRuns : 1;
    418 
    419  RefPtr<ScriptFetchOptions> mFetchOptions;
    420  nsCOMPtr<nsIURI> mURI;
    421 
    422  // The base URL used for resolving relative module imports.
    423  nsCOMPtr<nsIURI> mBaseURL;
    424 
    425 public:
    426  // Holds script source data for non-inline scripts.
    427  mozilla::Maybe<
    428      Variant<ScriptTextBuffer<char16_t>, ScriptTextBuffer<Utf8Unit>>>
    429      mScriptData;
    430 
    431  // The length of script source text, set when reading completes. This is used
    432  // since mScriptData is cleared when the source is passed to the JS engine.
    433  size_t mReceivedScriptTextLength;
    434 
    435  // Holds either of the following for non-inline scripts:
    436  //   * The SRI serialized hash and the paddings, which is calculated when
    437  //     receiving the source text
    438  //   * The SRI, padding, and the serialized Stencil, which is received
    439  //     from necko. The data is laid out according to ScriptBytecodeDataLayout
    440  //     or, if compression is enabled, ScriptBytecodeCompressedDataLayout.
    441  TranscodeBuffer mSRIAndSerializedStencil;
    442 
    443  // Holds the stencil for the script.  This field is used in all DataType.
    444  RefPtr<Stencil> mStencil;
    445 
    446  // The cache info channel used when saving the serialized Stencil to the
    447  // necko cache.
    448  //
    449  // This field is populated if the cache is enabled and this is either
    450  // IsTextSource() or IsCachedStencil(), and it's cleared after saving to the
    451  // necko cache, and thus, this field is used only once.
    452  nsCOMPtr<nsICacheEntryWriteHandle> mCacheEntry;
    453 };
    454 
    455 // Provide accessors for any classes `Derived` which is providing the
    456 // `getLoadedScript` function as interface. The accessors are meant to be
    457 // inherited by the `Derived` class.
    458 template <typename Derived>
    459 class LoadedScriptDelegate {
    460 private:
    461  // Use a static_cast<Derived> instead of declaring virtual functions. This is
    462  // meant to avoid relying on virtual table, and improve inlining for non-final
    463  // classes.
    464  const LoadedScript* GetLoadedScript() const {
    465    return static_cast<const Derived*>(this)->getLoadedScript();
    466  }
    467  LoadedScript* GetLoadedScript() {
    468    return static_cast<Derived*>(this)->getLoadedScript();
    469  }
    470 
    471 public:
    472  template <typename Unit>
    473  using ScriptTextBuffer = LoadedScript::ScriptTextBuffer<Unit>;
    474  using MaybeSourceText = LoadedScript::MaybeSourceText;
    475 
    476  mozilla::dom::ReferrerPolicy ReferrerPolicy() const {
    477    return GetLoadedScript()->ReferrerPolicy();
    478  }
    479  void UpdateReferrerPolicy(mozilla::dom::ReferrerPolicy aReferrerPolicy) {
    480    GetLoadedScript()->AsModuleScript()->UpdateReferrerPolicy(aReferrerPolicy);
    481  }
    482 
    483  ScriptFetchOptions* FetchOptions() const {
    484    return GetLoadedScript()->GetFetchOptions();
    485  }
    486 
    487  nsIURI* URI() const { return GetLoadedScript()->GetURI(); }
    488 
    489  nsIURI* BaseURL() const { return GetLoadedScript()->BaseURL(); }
    490  void SetBaseURL(nsIURI* aBaseURL) { GetLoadedScript()->SetBaseURL(aBaseURL); }
    491  void SetBaseURLFromChannelAndOriginalURI(nsIChannel* aChannel,
    492                                           nsIURI* aOriginalURI) {
    493    GetLoadedScript()->SetBaseURLFromChannelAndOriginalURI(aChannel,
    494                                                           aOriginalURI);
    495  }
    496 
    497  bool IsUnknownDataType() const {
    498    return GetLoadedScript()->IsUnknownDataType();
    499  }
    500  bool IsTextSource() const { return GetLoadedScript()->IsTextSource(); }
    501  bool IsSerializedStencil() const {
    502    return GetLoadedScript()->IsSerializedStencil();
    503  }
    504  bool IsCachedStencil() const { return GetLoadedScript()->IsCachedStencil(); }
    505 
    506  void SetUnknownDataType() { GetLoadedScript()->SetUnknownDataType(); }
    507 
    508  void SetTextSource(LoadContextBase* maybeLoadContext) {
    509    GetLoadedScript()->SetTextSource(maybeLoadContext);
    510  }
    511 
    512  void SetSerializedStencil() { GetLoadedScript()->SetSerializedStencil(); }
    513 
    514  bool IsUTF16Text() const { return GetLoadedScript()->IsUTF16Text(); }
    515  bool IsUTF8Text() const { return GetLoadedScript()->IsUTF8Text(); }
    516 
    517  template <typename Unit>
    518  const ScriptTextBuffer<Unit>& ScriptText() const {
    519    const LoadedScript* loader = GetLoadedScript();
    520    return loader->ScriptText<Unit>();
    521  }
    522  template <typename Unit>
    523  ScriptTextBuffer<Unit>& ScriptText() {
    524    LoadedScript* loader = GetLoadedScript();
    525    return loader->ScriptText<Unit>();
    526  }
    527 
    528  size_t ScriptTextLength() const {
    529    return GetLoadedScript()->ScriptTextLength();
    530  }
    531 
    532  size_t ReceivedScriptTextLength() const {
    533    return GetLoadedScript()->ReceivedScriptTextLength();
    534  }
    535 
    536  void SetReceivedScriptTextLength(size_t aLength) {
    537    GetLoadedScript()->SetReceivedScriptTextLength(aLength);
    538  }
    539 
    540  // Get source text.  On success |aMaybeSource| will contain either UTF-8 or
    541  // UTF-16 source; on failure it will remain in its initial state.
    542  nsresult GetScriptSource(JSContext* aCx, MaybeSourceText* aMaybeSource,
    543                           LoadContextBase* aLoadContext) {
    544    return GetLoadedScript()->GetScriptSource(aCx, aMaybeSource, aLoadContext);
    545  }
    546 
    547  void ClearScriptText() { GetLoadedScript()->ClearScriptText(); }
    548 
    549  bool HasNoSRIOrSRIAndSerializedStencil() const {
    550    return GetLoadedScript()->HasNoSRIOrSRIAndSerializedStencil();
    551  }
    552 
    553  TranscodeBuffer& SRI() { return GetLoadedScript()->SRI(); }
    554  TranscodeBuffer& SRIAndSerializedStencil() {
    555    return GetLoadedScript()->SRIAndSerializedStencil();
    556  }
    557  TranscodeRange SerializedStencil() const {
    558    return GetLoadedScript()->SerializedStencil();
    559  }
    560 
    561  size_t GetSRILength() const { return GetLoadedScript()->GetSRILength(); }
    562  void SetSRILength(size_t sriLength) {
    563    GetLoadedScript()->SetSRILength(sriLength);
    564  }
    565 
    566  void DropSRIOrSRIAndSerializedStencil() {
    567    GetLoadedScript()->DropSRIOrSRIAndSerializedStencil();
    568  }
    569 
    570  bool HasStencil() const { return GetLoadedScript()->HasStencil(); }
    571  Stencil* GetStencil() const { return GetLoadedScript()->GetStencil(); }
    572  void SetStencil(Stencil* aStencil) {
    573    GetLoadedScript()->SetStencil(aStencil);
    574  }
    575  void ClearStencil() { GetLoadedScript()->ClearStencil(); }
    576 
    577  void SetTookLongInPreviousRuns() {
    578    GetLoadedScript()->SetTookLongInPreviousRuns();
    579  }
    580  bool TookLongInPreviousRuns() const {
    581    return GetLoadedScript()->TookLongInPreviousRuns();
    582  }
    583 };
    584 
    585 class ClassicScript final : public LoadedScript {
    586  ~ClassicScript() = default;
    587 
    588 private:
    589  // Scripts can be created only by ScriptLoadRequest::NoCacheEntryFound.
    590  ClassicScript(mozilla::dom::ReferrerPolicy aReferrerPolicy,
    591                ScriptFetchOptions* aFetchOptions, nsIURI* aURI);
    592 
    593  friend class ScriptLoadRequest;
    594 };
    595 
    596 class EventScript final : public LoadedScript {
    597  ~EventScript() = default;
    598 
    599 public:
    600  EventScript(mozilla::dom::ReferrerPolicy aReferrerPolicy,
    601              ScriptFetchOptions* aFetchOptions, nsIURI* aURI);
    602 };
    603 
    604 class ImportMapScript final : public LoadedScript {
    605  ~ImportMapScript() = default;
    606 
    607 public:
    608  ImportMapScript(mozilla::dom::ReferrerPolicy aReferrerPolicy,
    609                  ScriptFetchOptions* aFetchOptions, nsIURI* aURI);
    610 };
    611 
    612 // A single module script. May be used to satisfy multiple load requests.
    613 
    614 class ModuleScript final : public LoadedScript {
    615  // Those fields are used only after instantiated, and they're reset to
    616  // null and false when stored into the cache as LoadedScript instance.
    617  Heap<JSObject*> mModuleRecord;
    618  Heap<Value> mParseError;
    619  Heap<Value> mErrorToRethrow;
    620  bool mForPreload = false;
    621  bool mHadImportMap = false;
    622 
    623  ~ModuleScript();
    624 
    625 public:
    626  NS_DECL_ISUPPORTS_INHERITED
    627  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleScript,
    628                                                         LoadedScript)
    629 
    630 private:
    631  // Scripts can be created only by ScriptLoadRequest::NoCacheEntryFound.
    632  ModuleScript(mozilla::dom::ReferrerPolicy aReferrerPolicy,
    633               ScriptFetchOptions* aFetchOptions, nsIURI* aURI);
    634 
    635  explicit ModuleScript(const LoadedScript& other);
    636 
    637  template <typename T, typename... Args>
    638  friend RefPtr<T> mozilla::MakeRefPtr(Args&&... aArgs);
    639 
    640  friend class ScriptLoadRequest;
    641 
    642 public:
    643  // Convert between cacheable LoadedScript instance, which is used by
    644  // mozilla::dom::SharedScriptCache.
    645  static already_AddRefed<ModuleScript> FromCache(const LoadedScript& aScript);
    646  already_AddRefed<LoadedScript> ToCache();
    647 
    648  void SetModuleRecord(Handle<JSObject*> aModuleRecord);
    649  void SetParseError(const Value& aError);
    650  void SetErrorToRethrow(const Value& aError);
    651  void SetForPreload(bool aValue);
    652  void SetHadImportMap(bool aValue);
    653 
    654  JSObject* ModuleRecord() const { return mModuleRecord; }
    655 
    656  Value ParseError() const { return mParseError; }
    657  Value ErrorToRethrow() const { return mErrorToRethrow; }
    658  bool HasParseError() const { return !mParseError.isUndefined(); }
    659  bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); }
    660  bool ForPreload() const { return mForPreload; }
    661  bool HadImportMap() const { return mHadImportMap; }
    662 
    663  void Shutdown();
    664 
    665  void UnlinkModuleRecord();
    666 
    667  friend void CheckModuleScriptPrivate(LoadedScript*, const Value&);
    668 
    669  void UpdateReferrerPolicy(mozilla::dom::ReferrerPolicy aReferrerPolicy) {
    670    mReferrerPolicy = aReferrerPolicy;
    671  }
    672 };
    673 
    674 ClassicScript* LoadedScript::AsClassicScript() {
    675  MOZ_ASSERT(!IsModuleScript());
    676  return static_cast<ClassicScript*>(this);
    677 }
    678 
    679 ModuleScript* LoadedScript::AsModuleScript() {
    680  MOZ_ASSERT(IsModuleScript());
    681  return static_cast<ModuleScript*>(this);
    682 }
    683 
    684 }  // namespace JS::loader
    685 
    686 #endif  // js_loader_LoadedScript_h