tor-browser

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

XPCJSRuntime.cpp (129541B)


      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 /* Per JSRuntime object */
      8 
      9 #include "mozilla/ArrayUtils.h"
     10 #include "mozilla/AutoRestore.h"
     11 #include "mozilla/AppShutdown.h"
     12 #include "mozilla/MemoryReporting.h"
     13 #include "mozilla/UniquePtr.h"
     14 
     15 #include "xpcprivate.h"
     16 #include "xpcpublic.h"
     17 #include "XPCMaps.h"
     18 #include "XPCJSMemoryReporter.h"
     19 #include "XrayWrapper.h"
     20 #include "WrapperFactory.h"
     21 #include "mozJSModuleLoader.h"
     22 #include "nsNetUtil.h"
     23 #include "nsContentSecurityUtils.h"
     24 
     25 #include "nsExceptionHandler.h"
     26 #include "nsIMemoryInfoDumper.h"
     27 #include "nsIMemoryReporter.h"
     28 #include "nsIObserverService.h"
     29 #include "mozilla/dom/Document.h"
     30 #include "nsIRunnable.h"
     31 #include "nsPIDOMWindow.h"
     32 #include "nsPrintfCString.h"
     33 #include "nsScriptSecurityManager.h"
     34 #include "nsWindowSizes.h"
     35 #include "mozilla/BasePrincipal.h"
     36 #include "mozilla/Preferences.h"
     37 #include "mozilla/Services.h"
     38 #include "mozilla/dom/ScriptLoader.h"
     39 #include "mozilla/dom/ScriptSettings.h"
     40 #include "mozilla/glean/JsXpconnectMetrics.h"
     41 #include "mozilla/glean/XpcomMetrics.h"
     42 
     43 #include "nsContentUtils.h"
     44 #include "nsCCUncollectableMarker.h"
     45 #include "nsCycleCollectionNoteRootCallback.h"
     46 #include "nsCycleCollector.h"
     47 #include "jsapi.h"
     48 #include "js/BuildId.h"  // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
     49 #include "js/experimental/SourceHook.h"  // js::{,Set}SourceHook
     50 #include "js/GCAPI.h"
     51 #include "js/MemoryFunctions.h"
     52 #include "js/MemoryMetrics.h"
     53 #include "js/Object.h"  // JS::GetClass
     54 #include "js/RealmIterators.h"
     55 #include "js/SliceBudget.h"
     56 #include "js/UbiNode.h"
     57 #include "js/UbiNodeUtils.h"
     58 #include "js/friend/UsageStatistics.h"  // JSMetric, JS_SetAccumulateTelemetryCallback
     59 #include "js/friend/WindowProxy.h"  // js::SetWindowProxyClass
     60 #include "js/friend/XrayJitInfo.h"  // JS::SetXrayJitInfo
     61 #include "js/Utility.h"             // JS::UniqueTwoByteChars
     62 #include "mozilla/dom/AbortSignalBinding.h"
     63 #include "mozilla/dom/GeneratedAtomList.h"
     64 #include "mozilla/dom/BindingUtils.h"
     65 #include "mozilla/dom/Element.h"
     66 #include "mozilla/dom/FetchUtil.h"
     67 #include "mozilla/dom/WindowBinding.h"
     68 #include "mozilla/Attributes.h"
     69 #include "mozilla/ProcessHangMonitor.h"
     70 #include "mozilla/ProfilerLabels.h"
     71 #include "mozilla/Sprintf.h"
     72 #include "AccessCheck.h"
     73 #include "nsGlobalWindowInner.h"
     74 #include "nsAboutProtocolUtils.h"
     75 
     76 #include "NodeUbiReporting.h"
     77 #include "ExpandedPrincipal.h"
     78 #include "nsIInputStream.h"
     79 #include "nsJSPrincipals.h"
     80 #include "nsJSEnvironment.h"
     81 #include "XPCInlines.h"
     82 
     83 #ifdef XP_WIN
     84 #  include <windows.h>
     85 #endif
     86 
     87 using namespace mozilla;
     88 using namespace mozilla::dom;
     89 using namespace xpc;
     90 using namespace JS;
     91 using namespace js;
     92 using mozilla::dom::PerThreadAtomCache;
     93 
     94 /***************************************************************************/
     95 
     96 const char* const XPCJSRuntime::mStrings[] = {
     97    "constructor",      // IDX_CONSTRUCTOR
     98    "toString",         // IDX_TO_STRING
     99    "toSource",         // IDX_TO_SOURCE
    100    "value",            // IDX_VALUE
    101    "QueryInterface",   // IDX_QUERY_INTERFACE
    102    "Components",       // IDX_COMPONENTS
    103    "Cc",               // IDX_CC
    104    "Ci",               // IDX_CI
    105    "Cr",               // IDX_CR
    106    "Cu",               // IDX_CU
    107    "Services",         // IDX_SERVICES
    108    "wrappedJSObject",  // IDX_WRAPPED_JSOBJECT
    109    "prototype",        // IDX_PROTOTYPE
    110    "eval",             // IDX_EVAL
    111    "controllers",      // IDX_CONTROLLERS
    112    "Controllers",      // IDX_CONTROLLERS_CLASS
    113    "length",           // IDX_LENGTH
    114    "name",             // IDX_NAME
    115    "undefined",        // IDX_UNDEFINED
    116    "",                 // IDX_EMPTYSTRING
    117    "fileName",         // IDX_FILENAME
    118    "lineNumber",       // IDX_LINENUMBER
    119    "columnNumber",     // IDX_COLUMNNUMBER
    120    "stack",            // IDX_STACK
    121    "message",          // IDX_MESSAGE
    122    "cause",            // IDX_CAUSE
    123    "errors",           // IDX_ERRORS
    124    "lastIndex",        // IDX_LASTINDEX
    125    "then",             // IDX_THEN
    126    "isInstance",       // IDX_ISINSTANCE
    127    "Infinity",         // IDX_INFINITY
    128    "NaN",              // IDX_NAN
    129    "classId",          // IDX_CLASS_ID
    130    "interfaceId",      // IDX_INTERFACE_ID
    131    "initializer",      // IDX_INITIALIZER
    132    "print",            // IDX_PRINT
    133    "fetch",            // IDX_FETCH
    134    "crypto",           // IDX_CRYPTO
    135    "indexedDB",        // IDX_INDEXEDDB
    136    "structuredClone",  // IDX_STRUCTUREDCLONE
    137    "locks",            // IDX_LOCKS
    138 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    139    "suppressed",  // IDX_SUPPRESSED
    140    "error",       // IDX_ERROR
    141 #endif
    142 };
    143 
    144 /***************************************************************************/
    145 
    146 // *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
    147 // *All* NativeSets are referenced from mNativeSetMap.
    148 // So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
    149 // In mNativeSetMap we clear the references to the unmarked *and* delete them.
    150 
    151 class AsyncFreeSnowWhite : public Runnable {
    152 public:
    153  NS_IMETHOD Run() override {
    154    AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental CC", GCCC);
    155    AUTO_PROFILER_LABEL("AsyncFreeSnowWhite::Run", GCCC_FreeSnowWhite);
    156 
    157    auto timerId = glean::cycle_collector::async_snow_white_freeing.Start();
    158    // 2 ms budget, given that kICCSliceBudget is only 3 ms
    159    SliceBudget budget = SliceBudget(TimeBudget(2));
    160    bool hadSnowWhiteObjects =
    161        nsCycleCollector_doDeferredDeletionWithBudget(budget);
    162    glean::cycle_collector::async_snow_white_freeing.StopAndAccumulate(
    163        std::move(timerId));
    164    if (hadSnowWhiteObjects && !mContinuation) {
    165      mContinuation = true;
    166      if (NS_FAILED(Dispatch())) {
    167        mActive = false;
    168      }
    169    } else {
    170      mActive = false;
    171    }
    172    return NS_OK;
    173  }
    174 
    175  nsresult Dispatch() {
    176    nsCOMPtr<nsIRunnable> self(this);
    177    return NS_DispatchToCurrentThreadQueue(self.forget(), 1000,
    178                                           EventQueuePriority::Idle);
    179  }
    180 
    181  void Start(bool aContinuation = false, bool aPurge = false) {
    182    if (mContinuation) {
    183      mContinuation = aContinuation;
    184    }
    185    mPurge = aPurge;
    186    if (!mActive && NS_SUCCEEDED(Dispatch())) {
    187      mActive = true;
    188    }
    189  }
    190 
    191  AsyncFreeSnowWhite()
    192      : Runnable("AsyncFreeSnowWhite"),
    193        mContinuation(false),
    194        mActive(false),
    195        mPurge(false) {}
    196 
    197 public:
    198  bool mContinuation;
    199  bool mActive;
    200  bool mPurge;
    201 };
    202 
    203 namespace xpc {
    204 
    205 CompartmentPrivate::CompartmentPrivate(
    206    JS::Compartment* c, mozilla::UniquePtr<XPCWrappedNativeScope> scope,
    207    mozilla::BasePrincipal* origin, const SiteIdentifier& site)
    208    : originInfo(origin, site),
    209      wantXrays(false),
    210      allowWaivers(true),
    211      isWebExtensionContentScript(false),
    212      isUAWidgetCompartment(false),
    213      hasExclusiveExpandos(false),
    214      wasShutdown(false),
    215      mWrappedJSMap(mozilla::MakeUnique<JSObject2WrappedJSMap>()),
    216      mScope(std::move(scope)) {
    217  MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
    218 }
    219 
    220 CompartmentPrivate::~CompartmentPrivate() {
    221  MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
    222 }
    223 
    224 void CompartmentPrivate::SystemIsBeingShutDown() {
    225  // We may call this multiple times when the compartment contains more than one
    226  // realm.
    227  if (!wasShutdown) {
    228    mWrappedJSMap->ShutdownMarker();
    229    wasShutdown = true;
    230  }
    231 }
    232 
    233 RealmPrivate::RealmPrivate(JS::Realm* realm) : scriptability(realm) {
    234  mozilla::PodArrayZero(wrapperDenialWarnings);
    235 }
    236 
    237 /* static */
    238 void RealmPrivate::Init(HandleObject aGlobal, const SiteIdentifier& aSite) {
    239  MOZ_ASSERT(aGlobal);
    240  DebugOnly<const JSClass*> clasp = JS::GetClass(aGlobal);
    241  MOZ_ASSERT(clasp->slot0IsISupports() || dom::IsDOMClass(clasp));
    242 
    243  Realm* realm = GetObjectRealmOrNull(aGlobal);
    244 
    245  // Create the realm private.
    246  RealmPrivate* realmPriv = new RealmPrivate(realm);
    247  MOZ_ASSERT(!GetRealmPrivate(realm));
    248  SetRealmPrivate(realm, realmPriv);
    249 
    250  nsIPrincipal* principal = GetRealmPrincipal(realm);
    251  Compartment* c = JS::GetCompartment(aGlobal);
    252 
    253  // Create the compartment private if needed.
    254  if (CompartmentPrivate* priv = CompartmentPrivate::Get(c)) {
    255    MOZ_ASSERT(priv->originInfo.IsSameOrigin(principal));
    256  } else {
    257    auto scope = mozilla::MakeUnique<XPCWrappedNativeScope>(c, aGlobal);
    258    priv = new CompartmentPrivate(c, std::move(scope),
    259                                  BasePrincipal::Cast(principal), aSite);
    260    JS_SetCompartmentPrivate(c, priv);
    261  }
    262 }
    263 
    264 // As XPCJSRuntime can live longer than when we shutdown the observer service,
    265 // we have our own getter to account for this.
    266 static nsCOMPtr<nsIObserverService> GetObserverService() {
    267  if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
    268    return nullptr;
    269  }
    270  return mozilla::services::GetObserverService();
    271 }
    272 
    273 static bool TryParseLocationURICandidate(
    274    const nsACString& uristr, RealmPrivate::LocationHint aLocationHint,
    275    nsIURI** aURI) {
    276  static constexpr auto kGRE = "resource://gre/"_ns;
    277  static constexpr auto kToolkit = "chrome://global/"_ns;
    278  static constexpr auto kBrowser = "chrome://browser/"_ns;
    279 
    280  if (aLocationHint == RealmPrivate::LocationHintAddon) {
    281    // Blacklist some known locations which are clearly not add-on related.
    282    if (StringBeginsWith(uristr, kGRE) || StringBeginsWith(uristr, kToolkit) ||
    283        StringBeginsWith(uristr, kBrowser)) {
    284      return false;
    285    }
    286 
    287    // -- GROSS HACK ALERT --
    288    // The Yandex Elements 8.10.2 extension implements its own "xb://" URL
    289    // scheme. If we call NS_NewURI() on an "xb://..." URL, we'll end up
    290    // calling into the extension's own JS-implemented nsIProtocolHandler
    291    // object, which we can't allow while we're iterating over the JS heap.
    292    // So just skip any such URL.
    293    // -- GROSS HACK ALERT --
    294    if (StringBeginsWith(uristr, "xb"_ns)) {
    295      return false;
    296    }
    297  }
    298 
    299  nsCOMPtr<nsIURI> uri;
    300  if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr))) {
    301    return false;
    302  }
    303 
    304  nsAutoCString scheme;
    305  if (NS_FAILED(uri->GetScheme(scheme))) {
    306    return false;
    307  }
    308 
    309  // Cannot really map data: and blob:.
    310  // Also, data: URIs are pretty memory hungry, which is kinda bad
    311  // for memory reporter use.
    312  if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob")) {
    313    return false;
    314  }
    315 
    316  uri.forget(aURI);
    317  return true;
    318 }
    319 
    320 bool RealmPrivate::TryParseLocationURI(RealmPrivate::LocationHint aLocationHint,
    321                                       nsIURI** aURI) {
    322  if (!aURI) {
    323    return false;
    324  }
    325 
    326  // Need to parse the URI.
    327  if (location.IsEmpty()) {
    328    return false;
    329  }
    330 
    331  // Handle Sandbox location strings.
    332  // A sandbox string looks like this, for anonymous sandboxes, and builds
    333  // where Sandbox location tagging is enabled:
    334  //
    335  // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
    336  //
    337  // where <sandboxName> is user-provided via Cu.Sandbox()
    338  // and <js-stack-frame-filename> and <lineno> is the stack frame location
    339  // from where Cu.Sandbox was called.
    340  //
    341  // Otherwise, it is simply the caller-provided name, which is usually a URI.
    342  //
    343  // <js-stack-frame-filename> furthermore is "free form", often using a
    344  // "uri -> uri -> ..." chain. The following code will and must handle this
    345  // common case.
    346  //
    347  // It should be noted that other parts of the code may already rely on the
    348  // "format" of these strings.
    349 
    350  static const nsDependentCString from("(from: ");
    351  static const nsDependentCString arrow(" -> ");
    352  static const size_t fromLength = from.Length();
    353  static const size_t arrowLength = arrow.Length();
    354 
    355  // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
    356  int32_t idx = location.Find(from);
    357  if (idx < 0) {
    358    return TryParseLocationURICandidate(location, aLocationHint, aURI);
    359  }
    360 
    361  // When parsing we're looking for the right-most URI. This URI may be in
    362  // <sandboxName>, so we try this first.
    363  if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint,
    364                                   aURI)) {
    365    return true;
    366  }
    367 
    368  // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and
    369  // the chain that is potentially contained within and grab the rightmost
    370  // item that is actually a URI.
    371 
    372  // First, hack off the :<lineno>) part as well
    373  int32_t ridx = location.RFind(":"_ns);
    374  nsAutoCString chain(
    375      Substring(location, idx + fromLength, ridx - idx - fromLength));
    376 
    377  // Loop over the "->" chain. This loop also works for non-chains, or more
    378  // correctly chains with only one item.
    379  for (;;) {
    380    idx = chain.RFind(arrow);
    381    if (idx < 0) {
    382      // This is the last chain item. Try to parse what is left.
    383      return TryParseLocationURICandidate(chain, aLocationHint, aURI);
    384    }
    385 
    386    // Try to parse current chain item
    387    if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength),
    388                                     aLocationHint, aURI)) {
    389      return true;
    390    }
    391 
    392    // Current chain item couldn't be parsed.
    393    // Strip current item and continue.
    394    chain = Substring(chain, 0, idx);
    395  }
    396 
    397  MOZ_CRASH("Chain parser loop does not terminate");
    398 }
    399 
    400 static bool PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal) {
    401  // System principal gets a free pass.
    402  if (aPrincipal->IsSystemPrincipal()) {
    403    return true;
    404  }
    405 
    406  auto* principal = BasePrincipal::Cast(aPrincipal);
    407 
    408  // ExpandedPrincipal gets a free pass.
    409  if (principal->Is<ExpandedPrincipal>()) {
    410    return true;
    411  }
    412 
    413  // WebExtension principals get a free pass.
    414  if (principal->AddonPolicy()) {
    415    return true;
    416  }
    417 
    418  // pdf.js is a special-case too.
    419  if (nsContentUtils::IsPDFJS(principal)) {
    420    return true;
    421  }
    422 
    423  // Check whether our URI is an "about:" URI that allows scripts.  If it is,
    424  // we need to allow JS to run.
    425  if (aPrincipal->SchemeIs("about")) {
    426    uint32_t flags;
    427    nsresult rv = aPrincipal->GetAboutModuleFlags(&flags);
    428    if (NS_SUCCEEDED(rv) && (flags & nsIAboutModule::ALLOW_SCRIPT)) {
    429      return true;
    430    }
    431  }
    432 
    433  return false;
    434 }
    435 
    436 void RealmPrivate::RegisterStackFrame(JSStackFrameBase* aFrame) {
    437  mJSStackFrames.PutEntry(aFrame);
    438 }
    439 
    440 void RealmPrivate::UnregisterStackFrame(JSStackFrameBase* aFrame) {
    441  mJSStackFrames.RemoveEntry(aFrame);
    442 }
    443 
    444 void RealmPrivate::NukeJSStackFrames() {
    445  for (const auto& key : mJSStackFrames.Keys()) {
    446    key->Clear();
    447  }
    448 
    449  mJSStackFrames.Clear();
    450 }
    451 
    452 void RegisterJSStackFrame(JS::Realm* aRealm, JSStackFrameBase* aStackFrame) {
    453  RealmPrivate* realmPrivate = RealmPrivate::Get(aRealm);
    454  if (!realmPrivate) {
    455    return;
    456  }
    457 
    458  realmPrivate->RegisterStackFrame(aStackFrame);
    459 }
    460 
    461 void UnregisterJSStackFrame(JS::Realm* aRealm, JSStackFrameBase* aStackFrame) {
    462  RealmPrivate* realmPrivate = RealmPrivate::Get(aRealm);
    463  if (!realmPrivate) {
    464    return;
    465  }
    466 
    467  realmPrivate->UnregisterStackFrame(aStackFrame);
    468 }
    469 
    470 void NukeJSStackFrames(JS::Realm* aRealm) {
    471  RealmPrivate* realmPrivate = RealmPrivate::Get(aRealm);
    472  if (!realmPrivate) {
    473    return;
    474  }
    475 
    476  realmPrivate->NukeJSStackFrames();
    477 }
    478 
    479 Scriptability::Scriptability(JS::Realm* realm)
    480    : mScriptBlocks(0),
    481      mWindowAllowsScript(true),
    482      mScriptBlockedByPolicy(false) {
    483  nsIPrincipal* prin = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
    484 
    485  mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
    486  if (mImmuneToScriptPolicy) {
    487    return;
    488  }
    489  // If we're not immune, we should have a real principal with a URI.
    490  // Check the principal against the new-style domain policy.
    491  bool policyAllows;
    492  nsresult rv = prin->GetIsScriptAllowedByPolicy(&policyAllows);
    493  if (NS_SUCCEEDED(rv)) {
    494    mScriptBlockedByPolicy = !policyAllows;
    495    return;
    496  }
    497  // Something went wrong - be safe and block script.
    498  mScriptBlockedByPolicy = true;
    499 }
    500 
    501 bool Scriptability::Allowed() {
    502  return mWindowAllowsScript && !mScriptBlockedByPolicy && mScriptBlocks == 0;
    503 }
    504 
    505 bool Scriptability::IsImmuneToScriptPolicy() { return mImmuneToScriptPolicy; }
    506 
    507 void Scriptability::Block() { ++mScriptBlocks; }
    508 
    509 void Scriptability::Unblock() {
    510  MOZ_ASSERT(mScriptBlocks > 0);
    511  --mScriptBlocks;
    512 }
    513 
    514 void Scriptability::SetWindowAllowsScript(bool aAllowed) {
    515  mWindowAllowsScript = aAllowed || mImmuneToScriptPolicy;
    516 }
    517 
    518 /* static */
    519 bool Scriptability::AllowedIfExists(JSObject* aScope) {
    520  RealmPrivate* realmPrivate = RealmPrivate::Get(aScope);
    521  return realmPrivate ? realmPrivate->scriptability.Allowed() : true;
    522 }
    523 
    524 /* static */
    525 Scriptability& Scriptability::Get(JSObject* aScope) {
    526  return RealmPrivate::Get(aScope)->scriptability;
    527 }
    528 
    529 bool IsUAWidgetCompartment(JS::Compartment* compartment) {
    530  // We always eagerly create compartment privates for UA Widget compartments.
    531  CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
    532  return priv && priv->isUAWidgetCompartment;
    533 }
    534 
    535 bool IsUAWidgetScope(JS::Realm* realm) {
    536  return IsUAWidgetCompartment(JS::GetCompartmentForRealm(realm));
    537 }
    538 
    539 bool IsInUAWidgetScope(JSObject* obj) {
    540  return IsUAWidgetCompartment(JS::GetCompartment(obj));
    541 }
    542 
    543 bool CompartmentOriginInfo::MightBeWebContent() const {
    544  // Compartments with principals that are either the system principal or an
    545  // expanded principal are definitely not web content.
    546  return !nsContentUtils::IsSystemOrExpandedPrincipal(mOrigin);
    547 }
    548 
    549 bool MightBeWebContentCompartment(JS::Compartment* compartment) {
    550  if (CompartmentPrivate* priv = CompartmentPrivate::Get(compartment)) {
    551    return priv->originInfo.MightBeWebContent();
    552  }
    553 
    554  // No CompartmentPrivate; try IsSystemCompartment.
    555  return !js::IsSystemCompartment(compartment);
    556 }
    557 
    558 bool CompartmentOriginInfo::IsSameOrigin(nsIPrincipal* aOther) const {
    559  return mOrigin->FastEquals(aOther);
    560 }
    561 
    562 /* static */
    563 bool CompartmentOriginInfo::Subsumes(JS::Compartment* aCompA,
    564                                     JS::Compartment* aCompB) {
    565  CompartmentPrivate* apriv = CompartmentPrivate::Get(aCompA);
    566  CompartmentPrivate* bpriv = CompartmentPrivate::Get(aCompB);
    567  MOZ_ASSERT(apriv);
    568  MOZ_ASSERT(bpriv);
    569  return apriv->originInfo.mOrigin->FastSubsumes(bpriv->originInfo.mOrigin);
    570 }
    571 
    572 /* static */
    573 bool CompartmentOriginInfo::SubsumesIgnoringFPD(JS::Compartment* aCompA,
    574                                                JS::Compartment* aCompB) {
    575  CompartmentPrivate* apriv = CompartmentPrivate::Get(aCompA);
    576  CompartmentPrivate* bpriv = CompartmentPrivate::Get(aCompB);
    577  MOZ_ASSERT(apriv);
    578  MOZ_ASSERT(bpriv);
    579  return apriv->originInfo.mOrigin->FastSubsumesIgnoringFPD(
    580      bpriv->originInfo.mOrigin);
    581 }
    582 
    583 void SetCompartmentChangedDocumentDomain(JS::Compartment* compartment) {
    584  // Note: we call this for all compartments that contain realms with a
    585  // particular principal. Not all of these compartments have a
    586  // CompartmentPrivate (for instance the temporary compartment/realm
    587  // created by the JS engine for off-thread parsing).
    588  if (CompartmentPrivate* priv = CompartmentPrivate::Get(compartment)) {
    589    priv->originInfo.SetChangedDocumentDomain();
    590  }
    591 }
    592 
    593 JSObject* UnprivilegedJunkScope() {
    594  return XPCJSRuntime::Get()->UnprivilegedJunkScope();
    595 }
    596 
    597 JSObject* UnprivilegedJunkScope(const fallible_t&) {
    598  return XPCJSRuntime::Get()->UnprivilegedJunkScope(fallible);
    599 }
    600 
    601 bool IsUnprivilegedJunkScope(JSObject* obj) {
    602  return XPCJSRuntime::Get()->IsUnprivilegedJunkScope(obj);
    603 }
    604 
    605 JSObject* NACScope(JSObject* global) {
    606  // If we're a chrome global, just use ourselves.
    607  if (AccessCheck::isChrome(global)) {
    608    return global;
    609  }
    610 
    611  JSObject* scope = UnprivilegedJunkScope();
    612  JS::ExposeObjectToActiveJS(scope);
    613  return scope;
    614 }
    615 
    616 JSObject* PrivilegedJunkScope() {
    617  return mozJSModuleLoader::Get()->GetSharedGlobal();
    618 }
    619 
    620 JSObject* CompilationScope() {
    621  return mozJSModuleLoader::Get()->GetSharedGlobal();
    622 }
    623 
    624 nsGlobalWindowInner* WindowOrNull(JSObject* aObj) {
    625  MOZ_ASSERT(aObj);
    626  MOZ_ASSERT(!js::IsWrapper(aObj));
    627 
    628  nsGlobalWindowInner* win = nullptr;
    629  UNWRAP_NON_WRAPPER_OBJECT(Window, aObj, win);
    630  return win;
    631 }
    632 
    633 nsGlobalWindowInner* WindowGlobalOrNull(JSObject* aObj) {
    634  MOZ_ASSERT(aObj);
    635  JSObject* glob = JS::GetNonCCWObjectGlobal(aObj);
    636 
    637  return WindowOrNull(glob);
    638 }
    639 
    640 JSObject* SandboxPrototypeOrNull(JSContext* aCx, JSObject* aObj) {
    641  MOZ_ASSERT(aObj);
    642 
    643  if (!IsSandbox(aObj)) {
    644    return nullptr;
    645  }
    646 
    647  // Sandbox can't be a Proxy so it must have a static prototype.
    648  JSObject* proto = GetStaticPrototype(aObj);
    649  if (!proto || !IsSandboxPrototypeProxy(proto)) {
    650    return nullptr;
    651  }
    652 
    653  return js::CheckedUnwrapDynamic(proto, aCx, /* stopAtWindowProxy = */ false);
    654 }
    655 
    656 nsGlobalWindowInner* CurrentWindowOrNull(JSContext* cx) {
    657  JSObject* glob = JS::CurrentGlobalOrNull(cx);
    658  return glob ? WindowOrNull(glob) : nullptr;
    659 }
    660 
    661 // Nukes all wrappers into or out of the given realm, and prevents new
    662 // wrappers from being created. Additionally marks the realm as
    663 // unscriptable after wrappers have been nuked.
    664 //
    665 // Note: This should *only* be called for browser or extension realms.
    666 // Wrappers between web compartments must never be cut in web-observable
    667 // ways.
    668 void NukeAllWrappersForRealm(
    669    JSContext* cx, JS::Realm* realm,
    670    js::NukeReferencesToWindow nukeReferencesToWindow) {
    671  // We do the following:
    672  // * Nuke all wrappers into the realm.
    673  // * Nuke all wrappers out of the realm's compartment, once we have nuked all
    674  //   realms in it.
    675  js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(), realm,
    676                                   nukeReferencesToWindow,
    677                                   js::NukeAllReferences);
    678 
    679  // Mark the realm as unscriptable.
    680  xpc::RealmPrivate::Get(realm)->scriptability.Block();
    681 }
    682 
    683 }  // namespace xpc
    684 
    685 static void CompartmentDestroyedCallback(JS::GCContext* gcx,
    686                                         JS::Compartment* compartment) {
    687  // NB - This callback may be called in JS_DestroyContext, which happens
    688  // after the XPCJSRuntime has been torn down.
    689 
    690  // Get the current compartment private into a UniquePtr (which will do the
    691  // cleanup for us), and null out the private (which may already be null).
    692  mozilla::UniquePtr<CompartmentPrivate> priv(
    693      CompartmentPrivate::Get(compartment));
    694  JS_SetCompartmentPrivate(compartment, nullptr);
    695 }
    696 
    697 static size_t CompartmentSizeOfIncludingThisCallback(
    698    MallocSizeOf mallocSizeOf, JS::Compartment* compartment) {
    699  CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
    700  return priv ? priv->SizeOfIncludingThis(mallocSizeOf) : 0;
    701 }
    702 
    703 /*
    704 * Return true if there exists a non-system inner window which is a current
    705 * inner window and whose reflector is gray.  We don't merge system
    706 * compartments, so we don't use them to trigger merging CCs.
    707 */
    708 bool XPCJSRuntime::UsefulToMergeZones() const {
    709  MOZ_ASSERT(NS_IsMainThread());
    710 
    711  // Turns out, actually making this return true often enough makes Windows
    712  // mochitest-gl OOM a lot.  Need to figure out what's going on there; see
    713  // bug 1277036.
    714 
    715  return false;
    716 }
    717 
    718 void XPCJSRuntime::TraceAdditionalNativeBlackRoots(JSTracer* trc) {
    719  if (CycleCollectedJSContext* ccx = GetContext()) {
    720    const auto* cx = static_cast<const XPCJSContext*>(ccx);
    721    if (AutoMarkingPtr* roots = cx->mAutoRoots) {
    722      roots->TraceJSAll(trc);
    723    }
    724  }
    725 
    726  if (mIID2NativeInterfaceMap) {
    727    mIID2NativeInterfaceMap->Trace(trc);
    728  }
    729 
    730  dom::TraceBlackJS(trc);
    731 }
    732 
    733 void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer* trc) {
    734  XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(this, trc);
    735 }
    736 
    737 void XPCJSRuntime::TraverseAdditionalNativeRoots(
    738    nsCycleCollectionNoteRootCallback& cb) {
    739  XPCWrappedNativeScope::SuspectAllWrappers(cb);
    740 
    741  auto* parti = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS);
    742  for (auto* wjs : mSubjectToFinalizationWJS) {
    743    MOZ_DIAGNOSTIC_ASSERT(wjs->IsSubjectToFinalization());
    744    cb.NoteXPCOMRoot(ToSupports(wjs), parti);
    745  }
    746 }
    747 
    748 void XPCJSRuntime::UnmarkSkippableJSHolders() {
    749  CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
    750 }
    751 
    752 void XPCJSRuntime::PrepareForForgetSkippable() {
    753  nsCCUncollectableMarker::CleanupForForgetSkippable();
    754 }
    755 
    756 void XPCJSRuntime::BeginCycleCollectionCallback(CCReason aReason) {
    757  nsJSContext::BeginCycleCollectionCallback(aReason);
    758 
    759  nsCOMPtr<nsIObserverService> obs = xpc::GetObserverService();
    760  if (obs) {
    761    obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
    762  }
    763 }
    764 
    765 void XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults& aResults) {
    766  nsJSContext::EndCycleCollectionCallback(aResults);
    767 
    768  nsCOMPtr<nsIObserverService> obs = xpc::GetObserverService();
    769  if (obs) {
    770    obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
    771  }
    772 }
    773 
    774 void XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation, bool aPurge) {
    775  mAsyncSnowWhiteFreer->Start(aContinuation, aPurge);
    776 }
    777 
    778 void xpc_UnmarkSkippableJSHolders() {
    779  if (nsXPConnect::GetRuntimeInstance()) {
    780    nsXPConnect::GetRuntimeInstance()->UnmarkSkippableJSHolders();
    781  }
    782 }
    783 
    784 /* static */
    785 void XPCJSRuntime::GCSliceCallback(JSContext* cx, JS::GCProgress progress,
    786                                   const JS::GCDescription& desc) {
    787  XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
    788  if (!self) {
    789    return;
    790  }
    791 
    792  nsCOMPtr<nsIObserverService> obs = xpc::GetObserverService();
    793  if (obs) {
    794    switch (progress) {
    795      case JS::GC_CYCLE_BEGIN:
    796        obs->NotifyObservers(nullptr, "garbage-collector-begin", nullptr);
    797        break;
    798      case JS::GC_CYCLE_END:
    799        obs->NotifyObservers(nullptr, "garbage-collector-end", nullptr);
    800        break;
    801      default:
    802        break;
    803    }
    804  }
    805 
    806  CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN);
    807 
    808  if (self->mPrevGCSliceCallback) {
    809    (*self->mPrevGCSliceCallback)(cx, progress, desc);
    810  }
    811 }
    812 
    813 /* static */
    814 void XPCJSRuntime::DoCycleCollectionCallback(JSContext* cx) {
    815  // The GC has detected that a CC at this point would collect a tremendous
    816  // amount of garbage that is being revivified unnecessarily.
    817  //
    818  // The GC_WAITING reason is a little overloaded here, but we want to do
    819  // a CC to allow Realms to be collected when they are referenced by a cycle.
    820  NS_DispatchToCurrentThread(NS_NewRunnableFunction(
    821      "XPCJSRuntime::DoCycleCollectionCallback",
    822      []() { nsJSContext::CycleCollectNow(CCReason::GC_WAITING, nullptr); }));
    823 
    824  XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
    825  if (!self) {
    826    return;
    827  }
    828 
    829  if (self->mPrevDoCycleCollectionCallback) {
    830    (*self->mPrevDoCycleCollectionCallback)(cx);
    831  }
    832 }
    833 
    834 void XPCJSRuntime::CustomGCCallback(JSGCStatus status) {
    835  nsTArray<xpcGCCallback> callbacks(extraGCCallbacks.Clone());
    836  for (uint32_t i = 0; i < callbacks.Length(); ++i) {
    837    callbacks[i](status);
    838  }
    839 }
    840 
    841 /* static */
    842 void XPCJSRuntime::FinalizeCallback(JS::GCContext* gcx, JSFinalizeStatus status,
    843                                    void* data) {
    844  XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
    845  if (!self) {
    846    return;
    847  }
    848 
    849  switch (status) {
    850    case JSFINALIZE_GROUP_PREPARE: {
    851      MOZ_ASSERT(!self->mDoingFinalization, "bad state");
    852 
    853      MOZ_ASSERT(!self->mGCIsRunning, "bad state");
    854      self->mGCIsRunning = true;
    855 
    856      self->mDoingFinalization = true;
    857 
    858      break;
    859    }
    860    case JSFINALIZE_GROUP_START: {
    861      MOZ_ASSERT(self->mDoingFinalization, "bad state");
    862 
    863      MOZ_ASSERT(self->mGCIsRunning, "bad state");
    864      self->mGCIsRunning = false;
    865 
    866      break;
    867    }
    868    case JSFINALIZE_GROUP_END: {
    869      MOZ_ASSERT(self->mDoingFinalization, "bad state");
    870      self->mDoingFinalization = false;
    871 
    872      break;
    873    }
    874    case JSFINALIZE_COLLECTION_END: {
    875      MOZ_ASSERT(!self->mGCIsRunning, "bad state");
    876      self->mGCIsRunning = true;
    877 
    878      if (CycleCollectedJSContext* ccx = self->GetContext()) {
    879        const auto* cx = static_cast<const XPCJSContext*>(ccx);
    880        if (AutoMarkingPtr* roots = cx->mAutoRoots) {
    881          roots->MarkAfterJSFinalizeAll();
    882        }
    883 
    884        // Now we are going to recycle any unused WrappedNativeTearoffs.
    885        // We do this by iterating all the live callcontexts
    886        // and marking the tearoffs in use. And then we
    887        // iterate over all the WrappedNative wrappers and sweep their
    888        // tearoffs.
    889        //
    890        // This allows us to perhaps minimize the growth of the
    891        // tearoffs. And also makes us not hold references to interfaces
    892        // on our wrapped natives that we are not actually using.
    893        //
    894        // XXX We may decide to not do this on *every* gc cycle.
    895 
    896        XPCCallContext* ccxp = cx->GetCallContext();
    897        while (ccxp) {
    898          // Deal with the strictness of callcontext that
    899          // complains if you ask for a tearoff when
    900          // it is in a state where the tearoff could not
    901          // possibly be valid.
    902          if (ccxp->CanGetTearOff()) {
    903            XPCWrappedNativeTearOff* to = ccxp->GetTearOff();
    904            if (to) {
    905              to->Mark();
    906            }
    907          }
    908          ccxp = ccxp->GetPrevCallContext();
    909        }
    910      }
    911 
    912      XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
    913 
    914      // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
    915      //
    916      // We transferred these native objects to this list when their JSObjects
    917      // were finalized. We did not destroy them immediately at that point
    918      // because the ordering of JS finalization is not deterministic and we did
    919      // not yet know if any wrappers that might still be referencing the protos
    920      // were still yet to be finalized and destroyed. We *do* know that the
    921      // protos' JSObjects would not have been finalized if there were any
    922      // wrappers that referenced the proto but were not themselves slated for
    923      // finalization in this gc cycle.
    924      //
    925      // At this point we know that any and all wrappers that might have been
    926      // referencing the protos in the dying list are themselves dead. So, we
    927      // can safely delete all the protos in the list.
    928      self->mDyingWrappedNativeProtos.clear();
    929 
    930      MOZ_ASSERT(self->mGCIsRunning, "bad state");
    931      self->mGCIsRunning = false;
    932 
    933      break;
    934    }
    935  }
    936 }
    937 
    938 /* static */
    939 void XPCJSRuntime::WeakPointerZonesCallback(JSTracer* trc, void* data) {
    940  // Called before each sweeping slice -- after processing any final marking
    941  // triggered by barriers -- to clear out any references to things that are
    942  // about to be finalized and update any pointers to moved GC things.
    943  XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
    944 
    945  // This callback is always called from within the GC so set the mGCIsRunning
    946  // flag to prevent AssertInvalidWrappedJSNotInTable from trying to call back
    947  // into the JS API. This has often already been set by FinalizeCallback by the
    948  // time we get here, but it may not be if we are doing a shutdown GC or if we
    949  // are called for compacting GC.
    950  AutoRestore<bool> restoreState(self->mGCIsRunning);
    951  self->mGCIsRunning = true;
    952 
    953  self->mWrappedJSMap->UpdateWeakPointersAfterGC(trc);
    954  self->mUAWidgetScopeMap.traceWeak(trc);
    955 }
    956 
    957 /* static */
    958 void XPCJSRuntime::WeakPointerCompartmentCallback(JSTracer* trc,
    959                                                  JS::Compartment* comp,
    960                                                  void* data) {
    961  // Called immediately after the ZoneGroup weak pointer callback, but only
    962  // once for each compartment that is being swept.
    963  CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
    964  if (xpcComp) {
    965    xpcComp->UpdateWeakPointersAfterGC(trc);
    966  }
    967 }
    968 
    969 void CompartmentPrivate::UpdateWeakPointersAfterGC(JSTracer* trc) {
    970  mRemoteProxies.traceWeak(trc);
    971  mWrappedJSMap->UpdateWeakPointersAfterGC(trc);
    972  mScope->UpdateWeakPointersAfterGC(trc);
    973 }
    974 
    975 void XPCJSRuntime::CustomOutOfMemoryCallback() {
    976  if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
    977    return;
    978  }
    979 
    980  nsCOMPtr<nsIMemoryInfoDumper> dumper =
    981      do_GetService("@mozilla.org/memory-info-dumper;1");
    982  if (!dumper) {
    983    return;
    984  }
    985 
    986  // If this fails, it fails silently.
    987  dumper->DumpMemoryInfoToTempDir(u"due-to-JS-OOM"_ns,
    988                                  /* anonymize = */ false,
    989                                  /* minimizeMemoryUsage = */ false);
    990 }
    991 
    992 void XPCJSRuntime::OnLargeAllocationFailure() {
    993  CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState::Reporting);
    994 
    995  nsCOMPtr<nsIObserverService> os = xpc::GetObserverService();
    996  if (os) {
    997    os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
    998  }
    999 
   1000  CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState::Reported);
   1001 }
   1002 
   1003 class LargeAllocationFailureRunnable final : public Runnable {
   1004  Mutex mMutex MOZ_UNANNOTATED;
   1005  CondVar mCondVar;
   1006  bool mWaiting;
   1007 
   1008  virtual ~LargeAllocationFailureRunnable() { MOZ_ASSERT(!mWaiting); }
   1009 
   1010 protected:
   1011  NS_IMETHOD Run() override {
   1012    MOZ_ASSERT(NS_IsMainThread());
   1013 
   1014    XPCJSRuntime::Get()->OnLargeAllocationFailure();
   1015 
   1016    MutexAutoLock lock(mMutex);
   1017    MOZ_ASSERT(mWaiting);
   1018 
   1019    mWaiting = false;
   1020    mCondVar.Notify();
   1021    return NS_OK;
   1022  }
   1023 
   1024 public:
   1025  LargeAllocationFailureRunnable()
   1026      : mozilla::Runnable("LargeAllocationFailureRunnable"),
   1027        mMutex("LargeAllocationFailureRunnable::mMutex"),
   1028        mCondVar(mMutex, "LargeAllocationFailureRunnable::mCondVar"),
   1029        mWaiting(true) {
   1030    MOZ_ASSERT(!NS_IsMainThread());
   1031  }
   1032 
   1033  void BlockUntilDone() {
   1034    MOZ_ASSERT(!NS_IsMainThread());
   1035 
   1036    MutexAutoLock lock(mMutex);
   1037    while (mWaiting) {
   1038      mCondVar.Wait();
   1039    }
   1040  }
   1041 };
   1042 
   1043 static void OnLargeAllocationFailureCallback() {
   1044  // This callback can be called from any thread, including internal JS helper
   1045  // and DOM worker threads. We need to send the low-memory event via the
   1046  // observer service which can only be called on the main thread, so proxy to
   1047  // the main thread if we're not there already. The purpose of this callback
   1048  // is to synchronously free some memory so the caller can retry a failed
   1049  // allocation, so block on the completion.
   1050 
   1051  if (NS_IsMainThread()) {
   1052    XPCJSRuntime::Get()->OnLargeAllocationFailure();
   1053    return;
   1054  }
   1055 
   1056  RefPtr<LargeAllocationFailureRunnable> r = new LargeAllocationFailureRunnable;
   1057  if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
   1058    return;
   1059  }
   1060 
   1061  r->BlockUntilDone();
   1062 }
   1063 
   1064 // Usually this is used through nsIPlatformInfo. However, being able to query
   1065 // this interface on all threads risk triggering some main-thread assertions
   1066 // which is not guaranteed by the callers of GetBuildId.
   1067 extern const char gToolkitBuildID[];
   1068 
   1069 bool mozilla::GetBuildId(JS::BuildIdCharVector* aBuildID) {
   1070  size_t length = std::char_traits<char>::length(gToolkitBuildID);
   1071  return aBuildID->append(gToolkitBuildID, length);
   1072 }
   1073 
   1074 size_t XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) {
   1075  size_t n = 0;
   1076  n += mallocSizeOf(this);
   1077  n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
   1078  n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
   1079  n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
   1080  n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
   1081 
   1082  n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf);
   1083 
   1084  // There are other XPCJSRuntime members that could be measured; the above
   1085  // ones have been seen by DMD to be worth measuring.  More stuff may be
   1086  // added later.
   1087 
   1088  return n;
   1089 }
   1090 
   1091 size_t CompartmentPrivate::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) {
   1092  size_t n = mallocSizeOf(this);
   1093  n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
   1094  n += mWrappedJSMap->SizeOfWrappedJS(mallocSizeOf);
   1095  return n;
   1096 }
   1097 
   1098 /***************************************************************************/
   1099 
   1100 void XPCJSRuntime::Shutdown(JSContext* cx) {
   1101  // This destructor runs before ~CycleCollectedJSContext, which does the actual
   1102  // JS_DestroyContext() call. But destroying the context triggers one final GC,
   1103  // which can call back into the context with various callbacks if we aren't
   1104  // careful. Remove the relevant callbacks, but leave the weak pointer
   1105  // callbacks to clear out any remaining table entries.
   1106  JS_RemoveFinalizeCallback(cx, FinalizeCallback);
   1107  xpc_DelocalizeRuntime(JS_GetRuntime(cx));
   1108 
   1109  JS::SetGCSliceCallback(cx, mPrevGCSliceCallback);
   1110 
   1111  nsScriptSecurityManager::ClearJSCallbacks(cx);
   1112 
   1113  // Clean up and destroy maps. Any remaining entries in mWrappedJSMap will be
   1114  // cleaned up by the weak pointer callbacks.
   1115  mIID2NativeInterfaceMap = nullptr;
   1116 
   1117  mClassInfo2NativeSetMap = nullptr;
   1118 
   1119  mNativeSetMap = nullptr;
   1120 
   1121  // Prevent ~LinkedList assertion failures if we leaked things.
   1122  mWrappedNativeScopes.clear();
   1123 
   1124  mSubjectToFinalizationWJS.clear();
   1125 
   1126  CycleCollectedJSRuntime::Shutdown(cx);
   1127 }
   1128 
   1129 XPCJSRuntime::~XPCJSRuntime() {
   1130  MOZ_COUNT_DTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
   1131 }
   1132 
   1133 // If |*anonymizeID| is non-zero and this is a user realm, the name will
   1134 // be anonymized.
   1135 static void GetRealmName(JS::Realm* realm, nsCString& name, int* anonymizeID,
   1136                         bool replaceSlashes) {
   1137  if (*anonymizeID && !js::IsSystemRealm(realm)) {
   1138    name.AppendPrintf("<anonymized-%d>", *anonymizeID);
   1139    *anonymizeID += 1;
   1140  } else if (JSPrincipals* principals = JS::GetRealmPrincipals(realm)) {
   1141    nsresult rv = nsJSPrincipals::get(principals)->GetScriptLocation(name);
   1142    if (NS_FAILED(rv)) {
   1143      name.AssignLiteral("(unknown)");
   1144    }
   1145 
   1146    // If the realm's location (name) differs from the principal's script
   1147    // location, append the realm's location to allow differentiation of
   1148    // multiple realms owned by the same principal (e.g. components owned
   1149    // by the system or null principal).
   1150    RealmPrivate* realmPrivate = RealmPrivate::Get(realm);
   1151    if (realmPrivate) {
   1152      const nsACString& location = realmPrivate->GetLocation();
   1153      if (!location.IsEmpty() && !location.Equals(name)) {
   1154        name.AppendLiteral(", ");
   1155        name.Append(location);
   1156      }
   1157    }
   1158 
   1159    if (*anonymizeID) {
   1160      // We might have a file:// URL that includes a path from the local
   1161      // filesystem, which should be omitted if we're anonymizing.
   1162      static const char* filePrefix = "file://";
   1163      int filePos = name.Find(filePrefix);
   1164      if (filePos >= 0) {
   1165        int pathPos = filePos + strlen(filePrefix);
   1166        int lastSlashPos = -1;
   1167        for (int i = pathPos; i < int(name.Length()); i++) {
   1168          if (name[i] == '/' || name[i] == '\\') {
   1169            lastSlashPos = i;
   1170          }
   1171        }
   1172        if (lastSlashPos != -1) {
   1173          name.ReplaceLiteral(pathPos, lastSlashPos - pathPos, "<anonymized>");
   1174        } else {
   1175          // Something went wrong. Anonymize the entire path to be
   1176          // safe.
   1177          name.Truncate(pathPos);
   1178          name += "<anonymized?!>";
   1179        }
   1180      }
   1181 
   1182      // We might have a location like this:
   1183      //   inProcessBrowserChildGlobal?ownedBy=http://www.example.com/
   1184      // The owner should be omitted if it's not a chrome: URI and we're
   1185      // anonymizing.
   1186      static const char* ownedByPrefix = "inProcessBrowserChildGlobal?ownedBy=";
   1187      int ownedByPos = name.Find(ownedByPrefix);
   1188      if (ownedByPos >= 0) {
   1189        const char* chrome = "chrome:";
   1190        int ownerPos = ownedByPos + strlen(ownedByPrefix);
   1191        const nsDependentCSubstring& ownerFirstPart =
   1192            Substring(name, ownerPos, strlen(chrome));
   1193        if (!ownerFirstPart.EqualsASCII(chrome)) {
   1194          name.Truncate(ownerPos);
   1195          name += "<anonymized>";
   1196        }
   1197      }
   1198    }
   1199 
   1200    // A hack: replace forward slashes with '\\' so they aren't
   1201    // treated as path separators.  Users of the reporters
   1202    // (such as about:memory) have to undo this change.
   1203    if (replaceSlashes) {
   1204      name.ReplaceChar('/', '\\');
   1205    }
   1206  } else {
   1207    name.AssignLiteral("null-principal");
   1208  }
   1209 }
   1210 
   1211 extern void xpc::GetCurrentRealmName(JSContext* cx, nsCString& name) {
   1212  RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   1213  if (!global) {
   1214    name.AssignLiteral("no global");
   1215    return;
   1216  }
   1217 
   1218  JS::Realm* realm = GetNonCCWObjectRealm(global);
   1219  int anonymizeID = 0;
   1220  GetRealmName(realm, name, &anonymizeID, false);
   1221 }
   1222 
   1223 void xpc::AddGCCallback(xpcGCCallback cb) {
   1224  XPCJSRuntime::Get()->AddGCCallback(cb);
   1225 }
   1226 
   1227 void xpc::RemoveGCCallback(xpcGCCallback cb) {
   1228  XPCJSRuntime::Get()->RemoveGCCallback(cb);
   1229 }
   1230 
   1231 static int64_t JSMainRuntimeGCHeapDistinguishedAmount() {
   1232  JSContext* cx = danger::GetJSContext();
   1233  return int64_t(JS_GetGCParameter(cx, JSGC_TOTAL_CHUNKS)) * js::gc::ChunkSize;
   1234 }
   1235 
   1236 static int64_t JSMainRuntimeTemporaryPeakDistinguishedAmount() {
   1237  JSContext* cx = danger::GetJSContext();
   1238  return JS::PeakSizeOfTemporary(cx);
   1239 }
   1240 
   1241 static int64_t JSMainRuntimeCompartmentsSystemDistinguishedAmount() {
   1242  JSContext* cx = danger::GetJSContext();
   1243  return JS::SystemCompartmentCount(cx);
   1244 }
   1245 
   1246 static int64_t JSMainRuntimeCompartmentsUserDistinguishedAmount() {
   1247  JSContext* cx = XPCJSContext::Get()->Context();
   1248  return JS::UserCompartmentCount(cx);
   1249 }
   1250 
   1251 static int64_t JSMainRuntimeRealmsSystemDistinguishedAmount() {
   1252  JSContext* cx = danger::GetJSContext();
   1253  return JS::SystemRealmCount(cx);
   1254 }
   1255 
   1256 static int64_t JSMainRuntimeRealmsUserDistinguishedAmount() {
   1257  JSContext* cx = XPCJSContext::Get()->Context();
   1258  return JS::UserRealmCount(cx);
   1259 }
   1260 
   1261 class JSMainRuntimeTemporaryPeakReporter final : public nsIMemoryReporter {
   1262  ~JSMainRuntimeTemporaryPeakReporter() = default;
   1263 
   1264 public:
   1265  NS_DECL_ISUPPORTS
   1266 
   1267  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
   1268                            nsISupports* aData, bool aAnonymize) override {
   1269    MOZ_COLLECT_REPORT(
   1270        "js-main-runtime-temporary-peak", KIND_OTHER, UNITS_BYTES,
   1271        JSMainRuntimeTemporaryPeakDistinguishedAmount(),
   1272        "Peak transient data size in the main JSRuntime (the current size "
   1273        "of which is reported as "
   1274        "'explicit/js-non-window/runtime/temporary').");
   1275 
   1276    return NS_OK;
   1277  }
   1278 };
   1279 
   1280 NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
   1281 
   1282 // The REPORT* macros do an unconditional report.  The ZRREPORT* macros are for
   1283 // realms and zones; they aggregate any entries smaller than
   1284 // SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
   1285 // entries for the realm.
   1286 
   1287 #define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
   1288 
   1289 #define REPORT(_path, _kind, _units, _amount, _desc)             \
   1290  handleReport->Callback(""_ns, _path, nsIMemoryReporter::_kind, \
   1291                         nsIMemoryReporter::_units, _amount,     \
   1292                         nsLiteralCString(_desc), data);
   1293 
   1294 #define REPORT_BYTES(_path, _kind, _amount, _desc) \
   1295  REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
   1296 
   1297 #define REPORT_GC_BYTES(_path, _amount, _desc)                            \
   1298  do {                                                                    \
   1299    size_t amount = _amount; /* evaluate _amount only once */             \
   1300    handleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_NONHEAP, \
   1301                           nsIMemoryReporter::UNITS_BYTES, amount,        \
   1302                           nsLiteralCString(_desc), data);                \
   1303    gcTotal += amount;                                                    \
   1304  } while (0)
   1305 
   1306 // Report realm/zone non-GC (KIND_HEAP) bytes.
   1307 #define ZRREPORT_BYTES(_path, _amount, _desc)                            \
   1308  do {                                                                   \
   1309    /* Assign _descLiteral plus "" into a char* to prove that it's */    \
   1310    /* actually a literal. */                                            \
   1311    size_t amount = _amount; /* evaluate _amount only once */            \
   1312    if (amount >= SUNDRIES_THRESHOLD) {                                  \
   1313      handleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_HEAP, \
   1314                             nsIMemoryReporter::UNITS_BYTES, amount,     \
   1315                             nsLiteralCString(_desc), data);             \
   1316    } else {                                                             \
   1317      sundriesMallocHeap += amount;                                      \
   1318    }                                                                    \
   1319  } while (0)
   1320 
   1321 // Report realm/zone GC bytes.
   1322 #define ZRREPORT_GC_BYTES(_path, _amount, _desc)                            \
   1323  do {                                                                      \
   1324    size_t amount = _amount; /* evaluate _amount only once */               \
   1325    if (amount >= SUNDRIES_THRESHOLD) {                                     \
   1326      handleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_NONHEAP, \
   1327                             nsIMemoryReporter::UNITS_BYTES, amount,        \
   1328                             nsLiteralCString(_desc), data);                \
   1329      gcTotal += amount;                                                    \
   1330    } else {                                                                \
   1331      sundriesGCHeap += amount;                                             \
   1332    }                                                                       \
   1333  } while (0)
   1334 
   1335 // Report realm/zone non-heap bytes.
   1336 #define ZRREPORT_NONHEAP_BYTES(_path, _amount, _desc)                       \
   1337  do {                                                                      \
   1338    size_t amount = _amount; /* evaluate _amount only once */               \
   1339    if (amount >= SUNDRIES_THRESHOLD) {                                     \
   1340      handleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_NONHEAP, \
   1341                             nsIMemoryReporter::UNITS_BYTES, amount,        \
   1342                             nsLiteralCString(_desc), data);                \
   1343    } else {                                                                \
   1344      sundriesNonHeap += amount;                                            \
   1345    }                                                                       \
   1346  } while (0)
   1347 
   1348 // Report runtime bytes.
   1349 #define RREPORT_BYTES(_path, _kind, _amount, _desc)                \
   1350  do {                                                             \
   1351    size_t amount = _amount; /* evaluate _amount only once */      \
   1352    handleReport->Callback(""_ns, _path, nsIMemoryReporter::_kind, \
   1353                           nsIMemoryReporter::UNITS_BYTES, amount, \
   1354                           nsLiteralCString(_desc), data);         \
   1355    rtTotal += amount;                                             \
   1356  } while (0)
   1357 
   1358 // Report GC thing bytes.
   1359 #define MREPORT_BYTES(_path, _kind, _amount, _desc)                \
   1360  do {                                                             \
   1361    size_t amount = _amount; /* evaluate _amount only once */      \
   1362    handleReport->Callback(""_ns, _path, nsIMemoryReporter::_kind, \
   1363                           nsIMemoryReporter::UNITS_BYTES, amount, \
   1364                           nsLiteralCString(_desc), data);         \
   1365    gcThingTotal += amount;                                        \
   1366  } while (0)
   1367 
   1368 MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
   1369 
   1370 namespace xpc {
   1371 
   1372 static void ReportZoneStats(const JS::ZoneStats& zStats,
   1373                            const xpc::ZoneStatsExtras& extras,
   1374                            nsIHandleReportCallback* handleReport,
   1375                            nsISupports* data, bool anonymize,
   1376                            size_t* gcTotalOut = nullptr) {
   1377  const nsCString& pathPrefix = extras.pathPrefix;
   1378  size_t gcTotal = 0;
   1379  size_t sundriesGCHeap = 0;
   1380  size_t sundriesMallocHeap = 0;
   1381  size_t sundriesNonHeap = 0;
   1382 
   1383  MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
   1384 
   1385  ZRREPORT_GC_BYTES(pathPrefix + "symbols/gc-heap"_ns, zStats.symbolsGCHeap,
   1386                    "Symbols.");
   1387 
   1388  ZRREPORT_GC_BYTES(
   1389      pathPrefix + "gc-heap-arena-admin"_ns, zStats.gcHeapArenaAdmin,
   1390      "Bookkeeping information and alignment padding within GC arenas.");
   1391 
   1392  ZRREPORT_GC_BYTES(pathPrefix + "unused-gc-things"_ns,
   1393                    zStats.unusedGCThings.totalSize(),
   1394                    "Unused GC thing cells within non-empty arenas.");
   1395 
   1396  ZRREPORT_BYTES(pathPrefix + "unique-id-map"_ns, zStats.uniqueIdMap,
   1397                 "Address-independent cell identities.");
   1398 
   1399  ZRREPORT_BYTES(pathPrefix + "propmap-tables"_ns, zStats.initialPropMapTable,
   1400                 "Tables storing property map information.");
   1401 
   1402  ZRREPORT_BYTES(pathPrefix + "shape-tables"_ns, zStats.shapeTables,
   1403                 "Tables storing shape information.");
   1404 
   1405  ZRREPORT_BYTES(pathPrefix + "compartments/compartment-objects"_ns,
   1406                 zStats.compartmentObjects,
   1407                 "The JS::Compartment objects in this zone.");
   1408 
   1409  ZRREPORT_BYTES(
   1410      pathPrefix + "compartments/cross-compartment-wrapper-tables"_ns,
   1411      zStats.crossCompartmentWrappersTables,
   1412      "The cross-compartment wrapper tables.");
   1413 
   1414  ZRREPORT_BYTES(
   1415      pathPrefix + "compartments/private-data"_ns,
   1416      zStats.compartmentsPrivateData,
   1417      "Extra data attached to each compartment by XPConnect, including "
   1418      "its wrapped-js.");
   1419 
   1420  ZRREPORT_GC_BYTES(pathPrefix + "bigints/gc-heap"_ns, zStats.bigIntsGCHeap,
   1421                    "BigInt values.");
   1422 
   1423  ZRREPORT_BYTES(pathPrefix + "bigints/malloc-heap"_ns,
   1424                 zStats.bigIntsMallocHeap, "BigInt values.");
   1425 
   1426  ZRREPORT_GC_BYTES(pathPrefix + "jit-codes-gc-heap"_ns, zStats.jitCodesGCHeap,
   1427                    "References to executable code pools used by the JITs.");
   1428 
   1429  ZRREPORT_GC_BYTES(pathPrefix + "getter-setters-gc-heap"_ns,
   1430                    zStats.getterSettersGCHeap,
   1431                    "Information for getter/setter properties.");
   1432 
   1433  ZRREPORT_GC_BYTES(pathPrefix + "property-maps/gc-heap/compact"_ns,
   1434                    zStats.compactPropMapsGCHeap,
   1435                    "Information about object properties.");
   1436 
   1437  ZRREPORT_GC_BYTES(pathPrefix + "property-maps/gc-heap/normal"_ns,
   1438                    zStats.normalPropMapsGCHeap,
   1439                    "Information about object properties.");
   1440 
   1441  ZRREPORT_GC_BYTES(pathPrefix + "property-maps/gc-heap/dict"_ns,
   1442                    zStats.dictPropMapsGCHeap,
   1443                    "Information about dictionary mode object properties.");
   1444 
   1445  ZRREPORT_BYTES(pathPrefix + "property-maps/malloc-heap/children"_ns,
   1446                 zStats.propMapChildren, "Tables for PropMap children.");
   1447 
   1448  ZRREPORT_BYTES(pathPrefix + "property-maps/malloc-heap/tables"_ns,
   1449                 zStats.propMapTables, "HashTables for PropMaps.");
   1450 
   1451  ZRREPORT_GC_BYTES(pathPrefix + "scopes/gc-heap"_ns, zStats.scopesGCHeap,
   1452                    "Scope information for scripts.");
   1453 
   1454  ZRREPORT_BYTES(pathPrefix + "scopes/malloc-heap"_ns, zStats.scopesMallocHeap,
   1455                 "Arrays of binding names and other binding-related data.");
   1456 
   1457  ZRREPORT_GC_BYTES(pathPrefix + "regexp-shareds/gc-heap"_ns,
   1458                    zStats.regExpSharedsGCHeap, "Shared compiled regexp data.");
   1459 
   1460  ZRREPORT_BYTES(pathPrefix + "regexp-shareds/malloc-heap"_ns,
   1461                 zStats.regExpSharedsMallocHeap,
   1462                 "Shared compiled regexp data.");
   1463 
   1464  ZRREPORT_BYTES(pathPrefix + "zone-object"_ns, zStats.zoneObject,
   1465                 "The JS::Zone object itself.");
   1466 
   1467  ZRREPORT_BYTES(pathPrefix + "regexp-zone"_ns, zStats.regexpZone,
   1468                 "The regexp zone and regexp data.");
   1469 
   1470  ZRREPORT_BYTES(pathPrefix + "jit-zone"_ns, zStats.jitZone, "The JIT zone.");
   1471 
   1472  ZRREPORT_BYTES(pathPrefix + "cacheir-stubs"_ns, zStats.cacheIRStubs,
   1473                 "The JIT's IC stubs (excluding code).");
   1474 
   1475  ZRREPORT_BYTES(pathPrefix + "object-fuses"_ns, zStats.objectFuses,
   1476                 "Information about constant object properties.");
   1477 
   1478  ZRREPORT_BYTES(pathPrefix + "script-counts-map"_ns, zStats.scriptCountsMap,
   1479                 "Profiling-related information for scripts.");
   1480 
   1481  ZRREPORT_NONHEAP_BYTES(pathPrefix + "code/ion"_ns, zStats.code.ion,
   1482                         "Code generated by the IonMonkey JIT.");
   1483 
   1484  ZRREPORT_NONHEAP_BYTES(pathPrefix + "code/baseline"_ns, zStats.code.baseline,
   1485                         "Code generated by the Baseline JIT.");
   1486 
   1487  ZRREPORT_NONHEAP_BYTES(pathPrefix + "code/regexp"_ns, zStats.code.regexp,
   1488                         "Code generated by the regexp JIT.");
   1489 
   1490  ZRREPORT_NONHEAP_BYTES(
   1491      pathPrefix + "code/other"_ns, zStats.code.other,
   1492      "Code generated by the JITs for wrappers and trampolines.");
   1493 
   1494  ZRREPORT_NONHEAP_BYTES(pathPrefix + "code/unused"_ns, zStats.code.unused,
   1495                         "Memory allocated by one of the JITs to hold code, "
   1496                         "but which is currently unused.");
   1497 
   1498  size_t stringsNotableAboutMemoryGCHeap = 0;
   1499  size_t stringsNotableAboutMemoryMallocHeap = 0;
   1500 
   1501 #define MAYBE_INLINE "The characters may be inline or on the malloc heap."
   1502 #define MAYBE_OVERALLOCATED \
   1503  "Sometimes over-allocated to simplify string concatenation."
   1504 
   1505  for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
   1506    const JS::NotableStringInfo& info = zStats.notableStrings[i];
   1507 
   1508    MOZ_ASSERT(!zStats.isTotals);
   1509 
   1510    // We don't do notable string detection when anonymizing, because
   1511    // there's a good chance its for crash submission, and the memory
   1512    // required for notable string detection is high.
   1513    MOZ_ASSERT(!anonymize);
   1514 
   1515    nsDependentCString notableString(info.buffer.get());
   1516 
   1517    // Viewing about:memory generates many notable strings which contain
   1518    // "string(length=".  If we report these as notable, then we'll create
   1519    // even more notable strings the next time we open about:memory (unless
   1520    // there's a GC in the meantime), and so on ad infinitum.
   1521    //
   1522    // To avoid cluttering up about:memory like this, we stick notable
   1523    // strings which contain "string(length=" into their own bucket.
   1524 #define STRING_LENGTH "string(length="
   1525    if (FindInReadable(nsLiteralCString(STRING_LENGTH), notableString)) {
   1526      stringsNotableAboutMemoryGCHeap += info.gcHeapLatin1;
   1527      stringsNotableAboutMemoryGCHeap += info.gcHeapTwoByte;
   1528      stringsNotableAboutMemoryMallocHeap += info.mallocHeapLatin1;
   1529      stringsNotableAboutMemoryMallocHeap += info.mallocHeapTwoByte;
   1530      continue;
   1531    }
   1532 
   1533    // Escape / to \ before we put notableString into the memory reporter
   1534    // path, because we don't want any forward slashes in the string to
   1535    // count as path separators.
   1536    nsCString escapedString(notableString);
   1537    escapedString.ReplaceSubstring("/", "\\");
   1538 
   1539    bool truncated = notableString.Length() < info.length;
   1540 
   1541    nsCString path =
   1542        pathPrefix +
   1543        nsPrintfCString("strings/" STRING_LENGTH "%zu, copies=%d, \"%s\"%s)/",
   1544                        info.length, info.numCopies, escapedString.get(),
   1545                        truncated ? " (truncated)" : "");
   1546 
   1547    if (info.gcHeapLatin1 > 0) {
   1548      REPORT_GC_BYTES(path + "gc-heap/latin1"_ns, info.gcHeapLatin1,
   1549                      "Latin1 strings. " MAYBE_INLINE);
   1550    }
   1551 
   1552    if (info.gcHeapTwoByte > 0) {
   1553      REPORT_GC_BYTES(path + "gc-heap/two-byte"_ns, info.gcHeapTwoByte,
   1554                      "TwoByte strings. " MAYBE_INLINE);
   1555    }
   1556 
   1557    if (info.mallocHeapLatin1 > 0) {
   1558      REPORT_BYTES(path + "malloc-heap/latin1"_ns, KIND_HEAP,
   1559                   info.mallocHeapLatin1,
   1560                   "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
   1561    }
   1562 
   1563    if (info.mallocHeapTwoByte > 0) {
   1564      REPORT_BYTES(
   1565          path + "malloc-heap/two-byte"_ns, KIND_HEAP, info.mallocHeapTwoByte,
   1566          "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
   1567    }
   1568  }
   1569 
   1570  nsCString nonNotablePath = pathPrefix;
   1571  nonNotablePath += (zStats.isTotals || anonymize)
   1572                        ? "strings/"_ns
   1573                        : "strings/string(<non-notable strings>)/"_ns;
   1574 
   1575  if (zStats.stringInfo.gcHeapLatin1 > 0) {
   1576    REPORT_GC_BYTES(nonNotablePath + "gc-heap/latin1"_ns,
   1577                    zStats.stringInfo.gcHeapLatin1,
   1578                    "Latin1 strings. " MAYBE_INLINE);
   1579  }
   1580 
   1581  if (zStats.stringInfo.gcHeapTwoByte > 0) {
   1582    REPORT_GC_BYTES(nonNotablePath + "gc-heap/two-byte"_ns,
   1583                    zStats.stringInfo.gcHeapTwoByte,
   1584                    "TwoByte strings. " MAYBE_INLINE);
   1585  }
   1586 
   1587  if (zStats.stringInfo.mallocHeapLatin1 > 0) {
   1588    REPORT_BYTES(nonNotablePath + "malloc-heap/latin1"_ns, KIND_HEAP,
   1589                 zStats.stringInfo.mallocHeapLatin1,
   1590                 "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
   1591  }
   1592 
   1593  if (zStats.stringInfo.mallocHeapTwoByte > 0) {
   1594    REPORT_BYTES(nonNotablePath + "malloc-heap/two-byte"_ns, KIND_HEAP,
   1595                 zStats.stringInfo.mallocHeapTwoByte,
   1596                 "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
   1597  }
   1598 
   1599  if (stringsNotableAboutMemoryGCHeap > 0) {
   1600    MOZ_ASSERT(!zStats.isTotals);
   1601    REPORT_GC_BYTES(
   1602        pathPrefix + "strings/string(<about-memory>)/gc-heap"_ns,
   1603        stringsNotableAboutMemoryGCHeap,
   1604        "Strings that contain the characters '" STRING_LENGTH
   1605        "', which "
   1606        "are probably from about:memory itself." MAYBE_INLINE
   1607        " We filter them out rather than display them, because displaying "
   1608        "them would create even more such strings every time about:memory "
   1609        "is refreshed.");
   1610  }
   1611 
   1612  if (stringsNotableAboutMemoryMallocHeap > 0) {
   1613    MOZ_ASSERT(!zStats.isTotals);
   1614    REPORT_BYTES(
   1615        pathPrefix + "strings/string(<about-memory>)/malloc-heap"_ns, KIND_HEAP,
   1616        stringsNotableAboutMemoryMallocHeap,
   1617        "Non-inline string characters of strings that contain the "
   1618        "characters '" STRING_LENGTH
   1619        "', which are probably from "
   1620        "about:memory itself. " MAYBE_OVERALLOCATED
   1621        " We filter them out rather than display them, because displaying "
   1622        "them would create even more such strings every time about:memory "
   1623        "is refreshed.");
   1624  }
   1625 
   1626  const JS::ShapeInfo& shapeInfo = zStats.shapeInfo;
   1627  if (shapeInfo.shapesGCHeapShared > 0) {
   1628    REPORT_GC_BYTES(pathPrefix + "shapes/gc-heap/shared"_ns,
   1629                    shapeInfo.shapesGCHeapShared, "Shared shapes.");
   1630  }
   1631 
   1632  if (shapeInfo.shapesGCHeapDict > 0) {
   1633    REPORT_GC_BYTES(pathPrefix + "shapes/gc-heap/dict"_ns,
   1634                    shapeInfo.shapesGCHeapDict, "Shapes in dictionary mode.");
   1635  }
   1636 
   1637  if (shapeInfo.shapesGCHeapBase > 0) {
   1638    REPORT_GC_BYTES(pathPrefix + "shapes/gc-heap/base"_ns,
   1639                    shapeInfo.shapesGCHeapBase,
   1640                    "Base shapes, which collate data common to many shapes.");
   1641  }
   1642 
   1643  if (shapeInfo.shapesMallocHeapCache > 0) {
   1644    REPORT_BYTES(pathPrefix + "shapes/malloc-heap/shape-cache"_ns, KIND_HEAP,
   1645                 shapeInfo.shapesMallocHeapCache,
   1646                 "Shape cache hash set for adding properties.");
   1647  }
   1648 
   1649  if (sundriesGCHeap > 0) {
   1650    // We deliberately don't use ZRREPORT_GC_BYTES here.
   1651    REPORT_GC_BYTES(
   1652        pathPrefix + "sundries/gc-heap"_ns, sundriesGCHeap,
   1653        "The sum of all 'gc-heap' measurements that are too small to be "
   1654        "worth showing individually.");
   1655  }
   1656 
   1657  if (sundriesMallocHeap > 0) {
   1658    // We deliberately don't use ZRREPORT_BYTES here.
   1659    REPORT_BYTES(
   1660        pathPrefix + "sundries/malloc-heap"_ns, KIND_HEAP, sundriesMallocHeap,
   1661        "The sum of all 'malloc-heap' measurements that are too small to "
   1662        "be worth showing individually.");
   1663  }
   1664 
   1665  if (sundriesNonHeap > 0) {
   1666    // We deliberately don't use ZRREPORT_NONHEAP_BYTES here.
   1667    REPORT_BYTES(pathPrefix + "sundries/other-heap"_ns, KIND_NONHEAP,
   1668                 sundriesNonHeap,
   1669                 "The sum of non-malloc/gc measurements that are too small to "
   1670                 "be worth showing individually.");
   1671  }
   1672 
   1673  if (gcTotalOut) {
   1674    *gcTotalOut += gcTotal;
   1675  }
   1676 
   1677 #undef STRING_LENGTH
   1678 }
   1679 
   1680 static void ReportClassStats(const ClassInfo& classInfo, const nsACString& path,
   1681                             nsIHandleReportCallback* handleReport,
   1682                             nsISupports* data, size_t& gcTotal) {
   1683  // We deliberately don't use ZRREPORT_BYTES, so that these per-class values
   1684  // don't go into sundries.
   1685 
   1686  if (classInfo.objectsGCHeap > 0) {
   1687    REPORT_GC_BYTES(path + "objects/gc-heap"_ns, classInfo.objectsGCHeap,
   1688                    "Objects, including fixed slots.");
   1689  }
   1690 
   1691  if (classInfo.objectsMallocHeapSlots > 0) {
   1692    REPORT_BYTES(path + "objects/gc-buffers/slots"_ns, KIND_NONHEAP,
   1693                 classInfo.objectsMallocHeapSlots, "Non-fixed object slots.");
   1694  }
   1695 
   1696  if (classInfo.objectsMallocHeapElementsNormal > 0) {
   1697    REPORT_BYTES(path + "objects/gc-buffers/elements/normal"_ns, KIND_NONHEAP,
   1698                 classInfo.objectsMallocHeapElementsNormal,
   1699                 "Normal (non-wasm) indexed elements.");
   1700  }
   1701 
   1702  if (classInfo.objectsMallocHeapElementsAsmJS > 0) {
   1703    REPORT_BYTES(path + "objects/malloc-heap/elements/asm.js"_ns, KIND_HEAP,
   1704                 classInfo.objectsMallocHeapElementsAsmJS,
   1705                 "asm.js array buffer elements allocated in the malloc heap.");
   1706  }
   1707 
   1708  if (classInfo.objectsMallocHeapGlobalData > 0) {
   1709    REPORT_BYTES(path + "objects/malloc-heap/global-data"_ns, KIND_HEAP,
   1710                 classInfo.objectsMallocHeapGlobalData,
   1711                 "Data for global objects.");
   1712  }
   1713 
   1714  if (classInfo.objectsMallocHeapMisc > 0) {
   1715    REPORT_BYTES(path + "objects/malloc-heap/misc"_ns, KIND_HEAP,
   1716                 classInfo.objectsMallocHeapMisc, "Miscellaneous object data.");
   1717  }
   1718 
   1719  if (classInfo.objectsNonHeapElementsNormal > 0) {
   1720    REPORT_BYTES(path + "objects/non-heap/elements/normal"_ns, KIND_NONHEAP,
   1721                 classInfo.objectsNonHeapElementsNormal,
   1722                 "Memory-mapped non-shared array buffer elements.");
   1723  }
   1724 
   1725  if (classInfo.objectsNonHeapElementsShared > 0) {
   1726    REPORT_BYTES(
   1727        path + "objects/non-heap/elements/shared"_ns, KIND_NONHEAP,
   1728        classInfo.objectsNonHeapElementsShared,
   1729        "Memory-mapped shared array buffer elements. These elements are "
   1730        "shared between one or more runtimes; the reported size is divided "
   1731        "by the buffer's refcount.");
   1732  }
   1733 
   1734  // WebAssembly memories are always non-heap-allocated (mmap). We never put
   1735  // these under sundries, because (a) in practice they're almost always
   1736  // larger than the sundries threshold, and (b) we'd need a third category of
   1737  // sundries ("non-heap"), which would be a pain.
   1738  if (classInfo.objectsNonHeapElementsWasm > 0) {
   1739    REPORT_BYTES(path + "objects/non-heap/elements/wasm"_ns, KIND_NONHEAP,
   1740                 classInfo.objectsNonHeapElementsWasm,
   1741                 "wasm/asm.js array buffer elements allocated outside both the "
   1742                 "malloc heap and the GC heap.");
   1743  }
   1744  if (classInfo.objectsNonHeapElementsWasmShared > 0) {
   1745    REPORT_BYTES(
   1746        path + "objects/non-heap/elements/wasm-shared"_ns, KIND_NONHEAP,
   1747        classInfo.objectsNonHeapElementsWasmShared,
   1748        "wasm/asm.js array buffer elements allocated outside both the "
   1749        "malloc heap and the GC heap. These elements are shared between "
   1750        "one or more runtimes; the reported size is divided by the "
   1751        "buffer's refcount.");
   1752  }
   1753 
   1754  if (classInfo.objectsNonHeapCodeWasm > 0) {
   1755    REPORT_BYTES(path + "objects/non-heap/code/wasm"_ns, KIND_NONHEAP,
   1756                 classInfo.objectsNonHeapCodeWasm,
   1757                 "AOT-compiled wasm/asm.js code.");
   1758  }
   1759 }
   1760 
   1761 static void ReportRealmStats(const JS::RealmStats& realmStats,
   1762                             const xpc::RealmStatsExtras& extras,
   1763                             nsIHandleReportCallback* handleReport,
   1764                             nsISupports* data, size_t* gcTotalOut = nullptr) {
   1765  static const nsDependentCString addonPrefix("explicit/add-ons/");
   1766 
   1767  size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
   1768  nsAutoCString realmJSPathPrefix(extras.jsPathPrefix);
   1769  nsAutoCString realmDOMPathPrefix(extras.domPathPrefix);
   1770 
   1771  MOZ_ASSERT(!gcTotalOut == realmStats.isTotals);
   1772 
   1773  nsCString nonNotablePath = realmJSPathPrefix;
   1774  nonNotablePath += realmStats.isTotals
   1775                        ? "classes/"_ns
   1776                        : "classes/class(<non-notable classes>)/"_ns;
   1777 
   1778  ReportClassStats(realmStats.classInfo, nonNotablePath, handleReport, data,
   1779                   gcTotal);
   1780 
   1781  for (size_t i = 0; i < realmStats.notableClasses.length(); i++) {
   1782    MOZ_ASSERT(!realmStats.isTotals);
   1783    const JS::NotableClassInfo& classInfo = realmStats.notableClasses[i];
   1784 
   1785    nsCString classPath =
   1786        realmJSPathPrefix +
   1787        nsPrintfCString("classes/class(%s)/", classInfo.className_.get());
   1788 
   1789    ReportClassStats(classInfo, classPath, handleReport, data, gcTotal);
   1790  }
   1791 
   1792  // Note that we use realmDOMPathPrefix here.  This is because we measure
   1793  // orphan DOM nodes in the JS reporter, but we want to report them in a "dom"
   1794  // sub-tree rather than a "js" sub-tree.
   1795  ZRREPORT_BYTES(
   1796      realmDOMPathPrefix + "orphan-nodes"_ns, realmStats.objectsPrivate,
   1797      "Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
   1798      "objects.");
   1799 
   1800  ZRREPORT_GC_BYTES(
   1801      realmJSPathPrefix + "scripts/gc-heap"_ns, realmStats.scriptsGCHeap,
   1802      "JSScript instances. There is one per user-defined function in a "
   1803      "script, and one for the top-level code in a script.");
   1804 
   1805  ZRREPORT_BYTES(realmJSPathPrefix + "scripts/malloc-heap/data"_ns,
   1806                 realmStats.scriptsMallocHeapData,
   1807                 "Various variable-length tables in JSScripts.");
   1808 
   1809  ZRREPORT_BYTES(realmJSPathPrefix + "baseline/data"_ns,
   1810                 realmStats.baselineData,
   1811                 "The Baseline JIT's compilation data (BaselineScripts).");
   1812 
   1813  ZRREPORT_BYTES(realmJSPathPrefix + "alloc-sites"_ns, realmStats.allocSites,
   1814                 "GC allocation site data associated with IC stubs.");
   1815 
   1816  ZRREPORT_BYTES(realmJSPathPrefix + "ion-data"_ns, realmStats.ionData,
   1817                 "The IonMonkey JIT's compilation data (IonScripts).");
   1818 
   1819  ZRREPORT_BYTES(realmJSPathPrefix + "jit-scripts"_ns, realmStats.jitScripts,
   1820                 "JIT data associated with scripts.");
   1821 
   1822  ZRREPORT_BYTES(realmJSPathPrefix + "realm-object"_ns, realmStats.realmObject,
   1823                 "The JS::Realm object itself.");
   1824 
   1825  ZRREPORT_BYTES(realmJSPathPrefix + "realm-tables"_ns, realmStats.realmTables,
   1826                 "Realm-wide tables storing wasm instances.");
   1827 
   1828  ZRREPORT_BYTES(realmJSPathPrefix + "inner-views"_ns,
   1829                 realmStats.innerViewsTable,
   1830                 "The table for array buffer inner views.");
   1831 
   1832  ZRREPORT_BYTES(
   1833      realmJSPathPrefix + "object-metadata"_ns, realmStats.objectMetadataTable,
   1834      "The table used by debugging tools for tracking object metadata");
   1835 
   1836  ZRREPORT_BYTES(realmJSPathPrefix + "saved-stacks-set"_ns,
   1837                 realmStats.savedStacksSet, "The saved stacks set.");
   1838 
   1839  ZRREPORT_BYTES(realmJSPathPrefix + "non-syntactic-lexical-scopes-table"_ns,
   1840                 realmStats.nonSyntacticLexicalScopesTable,
   1841                 "The non-syntactic lexical scopes table.");
   1842 
   1843  if (sundriesGCHeap > 0) {
   1844    // We deliberately don't use ZRREPORT_GC_BYTES here.
   1845    REPORT_GC_BYTES(
   1846        realmJSPathPrefix + "sundries/gc-heap"_ns, sundriesGCHeap,
   1847        "The sum of all 'gc-heap' measurements that are too small to be "
   1848        "worth showing individually.");
   1849  }
   1850 
   1851  if (sundriesMallocHeap > 0) {
   1852    // We deliberately don't use ZRREPORT_BYTES here.
   1853    REPORT_BYTES(
   1854        realmJSPathPrefix + "sundries/malloc-heap"_ns, KIND_HEAP,
   1855        sundriesMallocHeap,
   1856        "The sum of all 'malloc-heap' measurements that are too small to "
   1857        "be worth showing individually.");
   1858  }
   1859 
   1860  if (gcTotalOut) {
   1861    *gcTotalOut += gcTotal;
   1862  }
   1863 }
   1864 
   1865 static void ReportScriptSourceStats(const ScriptSourceInfo& scriptSourceInfo,
   1866                                    const nsACString& path,
   1867                                    nsIHandleReportCallback* handleReport,
   1868                                    nsISupports* data, size_t& rtTotal) {
   1869  if (scriptSourceInfo.misc > 0) {
   1870    RREPORT_BYTES(path + "misc"_ns, KIND_HEAP, scriptSourceInfo.misc,
   1871                  "Miscellaneous data relating to JavaScript source code.");
   1872  }
   1873 }
   1874 
   1875 void ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
   1876                                      const nsACString& rtPath,
   1877                                      nsIHandleReportCallback* handleReport,
   1878                                      nsISupports* data, bool anonymize,
   1879                                      size_t* rtTotalOut) {
   1880  size_t gcTotal = 0;
   1881 
   1882  for (const auto& zStats : rtStats.zoneStatsVector) {
   1883    const xpc::ZoneStatsExtras* extras =
   1884        static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
   1885    ReportZoneStats(zStats, *extras, handleReport, data, anonymize, &gcTotal);
   1886  }
   1887 
   1888  for (const auto& realmStats : rtStats.realmStatsVector) {
   1889    const xpc::RealmStatsExtras* extras =
   1890        static_cast<const xpc::RealmStatsExtras*>(realmStats.extra);
   1891 
   1892    ReportRealmStats(realmStats, *extras, handleReport, data, &gcTotal);
   1893  }
   1894 
   1895  // Report the rtStats.runtime numbers under "runtime/", and compute their
   1896  // total for later.
   1897 
   1898  size_t rtTotal = 0;
   1899 
   1900  RREPORT_BYTES(rtPath + "runtime/runtime-object"_ns, KIND_HEAP,
   1901                rtStats.runtime.object, "The JSRuntime object.");
   1902 
   1903  RREPORT_BYTES(rtPath + "runtime/atoms-table"_ns, KIND_HEAP,
   1904                rtStats.runtime.atomsTable, "The atoms table.");
   1905 
   1906  RREPORT_BYTES(rtPath + "runtime/atoms-mark-bitmaps"_ns, KIND_HEAP,
   1907                rtStats.runtime.atomsMarkBitmaps,
   1908                "Mark bitmaps for atoms held by each zone.");
   1909 
   1910  RREPORT_BYTES(rtPath + "runtime/self-host-stencil"_ns, KIND_HEAP,
   1911                rtStats.runtime.selfHostStencil,
   1912                "The self-hosting CompilationStencil.");
   1913 
   1914  RREPORT_BYTES(rtPath + "runtime/contexts"_ns, KIND_HEAP,
   1915                rtStats.runtime.contexts,
   1916                "JSContext objects and structures that belong to them.");
   1917 
   1918  RREPORT_BYTES(
   1919      rtPath + "runtime/temporary"_ns, KIND_HEAP, rtStats.runtime.temporary,
   1920      "Transient data (mostly parse nodes) held by the JSRuntime during "
   1921      "compilation.");
   1922 
   1923  RREPORT_BYTES(rtPath + "runtime/interpreter-stack"_ns, KIND_HEAP,
   1924                rtStats.runtime.interpreterStack, "JS interpreter frames.");
   1925 
   1926  RREPORT_BYTES(
   1927      rtPath + "runtime/shared-immutable-strings-cache"_ns, KIND_HEAP,
   1928      rtStats.runtime.sharedImmutableStringsCache,
   1929      "Immutable strings (such as JS scripts' source text) shared across all "
   1930      "JSRuntimes.");
   1931 
   1932  RREPORT_BYTES(rtPath + "runtime/shared-intl-data"_ns, KIND_HEAP,
   1933                rtStats.runtime.sharedIntlData,
   1934                "Shared internationalization data.");
   1935 
   1936  RREPORT_BYTES(rtPath + "runtime/uncompressed-source-cache"_ns, KIND_HEAP,
   1937                rtStats.runtime.uncompressedSourceCache,
   1938                "The uncompressed source code cache.");
   1939 
   1940  RREPORT_BYTES(rtPath + "runtime/script-data"_ns, KIND_HEAP,
   1941                rtStats.runtime.scriptData,
   1942                "The table holding script data shared in the runtime.");
   1943 
   1944  nsCString nonNotablePath =
   1945      rtPath +
   1946      nsPrintfCString(
   1947          "runtime/script-sources/source(scripts=%d, <non-notable files>)/",
   1948          rtStats.runtime.scriptSourceInfo.numScripts);
   1949 
   1950  ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo, nonNotablePath,
   1951                          handleReport, data, rtTotal);
   1952 
   1953  for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) {
   1954    const JS::NotableScriptSourceInfo& scriptSourceInfo =
   1955        rtStats.runtime.notableScriptSources[i];
   1956 
   1957    // Escape / to \ before we put the filename into the memory reporter
   1958    // path, because we don't want any forward slashes in the string to
   1959    // count as path separators. Consumers of memory reporters (e.g.
   1960    // about:memory) will convert them back to / after doing path
   1961    // splitting.
   1962    nsCString escapedFilename;
   1963    if (anonymize) {
   1964      escapedFilename.AppendPrintf("<anonymized-source-%d>", int(i));
   1965    } else {
   1966      nsDependentCString filename(scriptSourceInfo.filename_.get());
   1967      escapedFilename.Append(filename);
   1968      escapedFilename.ReplaceSubstring("/", "\\");
   1969    }
   1970 
   1971    nsCString notablePath =
   1972        rtPath +
   1973        nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
   1974                        scriptSourceInfo.numScripts, escapedFilename.get());
   1975 
   1976    ReportScriptSourceStats(scriptSourceInfo, notablePath, handleReport, data,
   1977                            rtTotal);
   1978  }
   1979 
   1980  RREPORT_BYTES(rtPath + "runtime/gc/marker"_ns, KIND_HEAP,
   1981                rtStats.runtime.gc.marker, "The GC mark stack and gray roots.");
   1982 
   1983  RREPORT_BYTES(rtPath + "runtime/gc/nursery-committed"_ns, KIND_NONHEAP,
   1984                rtStats.runtime.gc.nurseryCommitted,
   1985                "Memory being used by the GC's nursery.");
   1986 
   1987  RREPORT_BYTES(
   1988      rtPath + "runtime/gc/nursery-malloced-buffers"_ns, KIND_HEAP,
   1989      rtStats.runtime.gc.nurseryMallocedBuffers,
   1990      "Out-of-line slots and elements belonging to objects in the nursery.");
   1991 
   1992  RREPORT_BYTES(rtPath + "runtime/gc/store-buffer/vals"_ns, KIND_HEAP,
   1993                rtStats.runtime.gc.storeBufferVals,
   1994                "Values in the store buffer.");
   1995 
   1996  RREPORT_BYTES(rtPath + "runtime/gc/store-buffer/cells"_ns, KIND_HEAP,
   1997                rtStats.runtime.gc.storeBufferCells,
   1998                "Cells in the store buffer.");
   1999 
   2000  RREPORT_BYTES(rtPath + "runtime/gc/store-buffer/slots"_ns, KIND_HEAP,
   2001                rtStats.runtime.gc.storeBufferSlots,
   2002                "Slots in the store buffer.");
   2003 
   2004  RREPORT_BYTES(rtPath + "runtime/gc/store-buffer/whole-cells"_ns, KIND_HEAP,
   2005                rtStats.runtime.gc.storeBufferWholeCells,
   2006                "Whole cells in the store buffer.");
   2007 
   2008  RREPORT_BYTES(rtPath + "runtime/gc/store-buffer/generics"_ns, KIND_HEAP,
   2009                rtStats.runtime.gc.storeBufferGenerics,
   2010                "Generic things in the store buffer.");
   2011 
   2012  RREPORT_BYTES(rtPath + "runtime/jit-lazylink"_ns, KIND_HEAP,
   2013                rtStats.runtime.jitLazyLink,
   2014                "IonMonkey compilations waiting for lazy linking.");
   2015 
   2016  if (rtTotalOut) {
   2017    *rtTotalOut = rtTotal;
   2018  }
   2019 
   2020  // Report GC numbers that don't belong to a realm.
   2021 
   2022  // We don't want to report decommitted memory in "explicit", so we just
   2023  // change the leading "explicit/" to "decommitted/".
   2024  nsCString rtPath2(rtPath);
   2025  rtPath2.ReplaceLiteral(0, strlen("explicit"), "decommitted");
   2026 
   2027  REPORT_GC_BYTES(
   2028      rtPath2 + "gc-heap/decommitted-pages"_ns, rtStats.gcHeapDecommittedPages,
   2029      "GC arenas in non-empty chunks that is decommitted, i.e. it takes up "
   2030      "address space but no physical memory or swap space.");
   2031 
   2032  REPORT_GC_BYTES(
   2033      rtPath + "gc-heap/unused-chunks"_ns, rtStats.gcHeapUnusedChunks,
   2034      "Empty GC chunks which will soon be released unless claimed for new "
   2035      "allocations.");
   2036 
   2037  REPORT_GC_BYTES(rtPath + "gc-heap/unused-arenas"_ns,
   2038                  rtStats.gcHeapUnusedArenas,
   2039                  "Empty GC arenas within non-empty chunks.");
   2040 
   2041  REPORT_GC_BYTES(rtPath + "gc-heap/chunk-admin"_ns, rtStats.gcHeapChunkAdmin,
   2042                  "Bookkeeping information within GC chunks.");
   2043 
   2044  // gcTotal is the sum of everything we've reported for the GC heap.  It
   2045  // should equal rtStats.gcHeapChunkTotal.
   2046  MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
   2047 }
   2048 
   2049 }  // namespace xpc
   2050 
   2051 class JSMainRuntimeRealmsReporter final : public nsIMemoryReporter {
   2052  ~JSMainRuntimeRealmsReporter() = default;
   2053 
   2054 public:
   2055  NS_DECL_ISUPPORTS
   2056 
   2057  struct Data {
   2058    int anonymizeID;
   2059    js::Vector<nsCString, 0, js::SystemAllocPolicy> paths;
   2060  };
   2061 
   2062  static void RealmCallback(JSContext* cx, void* vdata, Realm* realm,
   2063                            const JS::AutoRequireNoGC& nogc) {
   2064    // silently ignore OOM errors
   2065    Data* data = static_cast<Data*>(vdata);
   2066    nsCString path;
   2067    GetRealmName(realm, path, &data->anonymizeID, /* replaceSlashes = */ true);
   2068    path.Insert(js::IsSystemRealm(realm) ? "js-main-runtime-realms/system/"_ns
   2069                                         : "js-main-runtime-realms/user/"_ns,
   2070                0);
   2071    (void)data->paths.append(path);
   2072  }
   2073 
   2074  NS_IMETHOD CollectReports(nsIHandleReportCallback* handleReport,
   2075                            nsISupports* data, bool anonymize) override {
   2076    // First we collect the realm paths.  Then we report them.  Doing
   2077    // the two steps interleaved is a bad idea, because calling
   2078    // |handleReport| from within RealmCallback() leads to all manner
   2079    // of assertions.
   2080 
   2081    Data d;
   2082    d.anonymizeID = anonymize ? 1 : 0;
   2083    JS::IterateRealms(XPCJSContext::Get()->Context(), &d, RealmCallback);
   2084 
   2085    for (auto& path : d.paths) {
   2086      REPORT(nsCString(path), KIND_OTHER, UNITS_COUNT, 1,
   2087             "A live realm in the main JSRuntime.");
   2088    }
   2089 
   2090    return NS_OK;
   2091  }
   2092 };
   2093 
   2094 NS_IMPL_ISUPPORTS(JSMainRuntimeRealmsReporter, nsIMemoryReporter)
   2095 
   2096 MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
   2097 
   2098 namespace xpc {
   2099 
   2100 class OrphanReporter : public JS::ObjectPrivateVisitor {
   2101 public:
   2102  explicit OrphanReporter(GetISupportsFun aGetISupports)
   2103      : JS::ObjectPrivateVisitor(aGetISupports), mState(OrphanMallocSizeOf) {}
   2104 
   2105  virtual size_t sizeOfIncludingThis(nsISupports* aSupports) override {
   2106    nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
   2107    if (!node || node->IsInComposedDoc()) {
   2108      return 0;
   2109    }
   2110 
   2111    // This is an orphan node.  If we haven't already handled the sub-tree that
   2112    // this node belongs to, measure the sub-tree's size and then record its
   2113    // root so we don't measure it again.
   2114    nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
   2115    if (!orphanTree || mState.HaveSeenPtr(orphanTree.get())) {
   2116      return 0;
   2117    }
   2118 
   2119    nsWindowSizes sizes(mState);
   2120    mozilla::dom::Document::AddSizeOfNodeTree(*orphanTree, sizes);
   2121 
   2122    // We combine the node size with nsStyleSizes here. It's not ideal, but it's
   2123    // hard to get the style structs measurements out to nsWindowMemoryReporter.
   2124    // Also, we drop mServoData in UnbindFromTree(), so in theory any
   2125    // non-in-tree element won't have any style data to measure.
   2126    //
   2127    // FIXME(emilio): We should ideally not do this, since ShadowRoots keep
   2128    // their StyleSheets alive even when detached from a document, and those
   2129    // could be significant in theory.
   2130    return sizes.getTotalSize();
   2131  }
   2132 
   2133 private:
   2134  SizeOfState mState;
   2135 };
   2136 
   2137 #ifdef DEBUG
   2138 static bool StartsWithExplicit(nsACString& s) {
   2139  return StringBeginsWith(s, "explicit/"_ns);
   2140 }
   2141 #endif
   2142 
   2143 class XPCJSRuntimeStats : public JS::RuntimeStats {
   2144  WindowPaths* mWindowPaths;
   2145  WindowPaths* mTopWindowPaths;
   2146  int mAnonymizeID;
   2147 
   2148 public:
   2149  XPCJSRuntimeStats(WindowPaths* windowPaths, WindowPaths* topWindowPaths,
   2150                    bool anonymize)
   2151      : JS::RuntimeStats(JSMallocSizeOf),
   2152        mWindowPaths(windowPaths),
   2153        mTopWindowPaths(topWindowPaths),
   2154        mAnonymizeID(anonymize ? 1 : 0) {}
   2155 
   2156  ~XPCJSRuntimeStats() {
   2157    for (size_t i = 0; i != realmStatsVector.length(); ++i) {
   2158      delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
   2159    }
   2160 
   2161    for (size_t i = 0; i != zoneStatsVector.length(); ++i) {
   2162      delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
   2163    }
   2164  }
   2165 
   2166  virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats,
   2167                                  const JS::AutoRequireNoGC& nogc) override {
   2168    xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
   2169    extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
   2170 
   2171    // Get some global in this zone.
   2172    Rooted<Realm*> realm(dom::RootingCx(), js::GetAnyRealmInZone(zone));
   2173    if (realm) {
   2174      RootedObject global(dom::RootingCx(), JS::GetRealmGlobalOrNull(realm));
   2175      if (global) {
   2176        RefPtr<nsGlobalWindowInner> window;
   2177        if (NS_SUCCEEDED(UNWRAP_NON_WRAPPER_OBJECT(Window, global, window))) {
   2178          // The global is a |window| object.  Use the path prefix that
   2179          // we should have already created for it.
   2180          if (mTopWindowPaths->Get(window->WindowID(), &extras->pathPrefix)) {
   2181            extras->pathPrefix.AppendLiteral("/js-");
   2182          }
   2183        }
   2184      }
   2185    }
   2186 
   2187    extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)zone);
   2188 
   2189    MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
   2190 
   2191    zStats->extra = extras;
   2192  }
   2193 
   2194  virtual void initExtraRealmStats(Realm* realm, JS::RealmStats* realmStats,
   2195                                   const JS::AutoRequireNoGC& nogc) override {
   2196    xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
   2197    nsCString rName;
   2198    GetRealmName(realm, rName, &mAnonymizeID, /* replaceSlashes = */ true);
   2199 
   2200    // Get the realm's global.
   2201    bool needZone = true;
   2202    RootedObject global(dom::RootingCx(), JS::GetRealmGlobalOrNull(realm));
   2203    if (global) {
   2204      RefPtr<nsGlobalWindowInner> window;
   2205      if (NS_SUCCEEDED(UNWRAP_NON_WRAPPER_OBJECT(Window, global, window))) {
   2206        // The global is a |window| object.  Use the path prefix that
   2207        // we should have already created for it.
   2208        if (mWindowPaths->Get(window->WindowID(), &extras->jsPathPrefix)) {
   2209          extras->domPathPrefix.Assign(extras->jsPathPrefix);
   2210          extras->domPathPrefix.AppendLiteral("/dom/");
   2211          extras->jsPathPrefix.AppendLiteral("/js-");
   2212          needZone = false;
   2213        } else {
   2214          extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
   2215          extras->domPathPrefix.AssignLiteral(
   2216              "explicit/dom/unknown-window-global?!/");
   2217        }
   2218      } else {
   2219        extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
   2220        extras->domPathPrefix.AssignLiteral(
   2221            "explicit/dom/non-window-global?!/");
   2222      }
   2223    } else {
   2224      extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
   2225      extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
   2226    }
   2227 
   2228    if (needZone) {
   2229      extras->jsPathPrefix +=
   2230          nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(realm));
   2231    }
   2232 
   2233    extras->jsPathPrefix += "realm("_ns + rName + ")/"_ns;
   2234 
   2235    // extras->jsPathPrefix is used for almost all the realm-specific
   2236    // reports. At this point it has the form
   2237    // "<something>realm(<rname>)/".
   2238    //
   2239    // extras->domPathPrefix is used for DOM orphan nodes, which are
   2240    // counted by the JS reporter but reported as part of the DOM
   2241    // measurements. At this point it has the form "<something>/dom/" if
   2242    // this realm belongs to an nsGlobalWindow, and
   2243    // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
   2244    // be used, because non-nsGlobalWindow realms shouldn't have
   2245    // orphan DOM nodes).
   2246 
   2247    MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
   2248    MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
   2249 
   2250    realmStats->extra = extras;
   2251  }
   2252 };
   2253 
   2254 void JSReporter::CollectReports(WindowPaths* windowPaths,
   2255                                WindowPaths* topWindowPaths,
   2256                                nsIHandleReportCallback* handleReport,
   2257                                nsISupports* data, bool anonymize) {
   2258  XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
   2259 
   2260  // In the first step we get all the stats and stash them in a local
   2261  // data structure.  In the second step we pass all the stashed stats to
   2262  // the callback.  Separating these steps is important because the
   2263  // callback may be a JS function, and executing JS while getting these
   2264  // stats seems like a bad idea.
   2265 
   2266  XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, anonymize);
   2267  OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
   2268  JSContext* cx = XPCJSContext::Get()->Context();
   2269  if (!JS::CollectRuntimeStats(cx, &rtStats, &orphanReporter, anonymize)) {
   2270    return;
   2271  }
   2272 
   2273  // Collect JS stats not associated with a Runtime such as helper threads or
   2274  // global tracelogger data. We do this here in JSReporter::CollectReports
   2275  // as this is used for the main Runtime in process.
   2276  JS::GlobalStats gStats(JSMallocSizeOf);
   2277  if (!JS::CollectGlobalStats(&gStats)) {
   2278    return;
   2279  }
   2280 
   2281  size_t xpcJSRuntimeSize = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
   2282 
   2283  size_t wrappedJSSize =
   2284      xpcrt->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
   2285 
   2286  XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
   2287  XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(cx, &sizeInfo);
   2288 
   2289  mozJSModuleLoader* loader = mozJSModuleLoader::Get();
   2290  size_t jsModuleLoaderSize =
   2291      loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
   2292  mozJSModuleLoader* devToolsLoader = mozJSModuleLoader::GetDevToolsLoader();
   2293  size_t jsDevToolsModuleLoaderSize =
   2294      devToolsLoader ? devToolsLoader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
   2295 
   2296  // This is the second step (see above).  First we report stuff in the
   2297  // "explicit" tree, then we report other stuff.
   2298 
   2299  size_t rtTotal = 0;
   2300  xpc::ReportJSRuntimeExplicitTreeStats(rtStats, "explicit/js-non-window/"_ns,
   2301                                        handleReport, data, anonymize,
   2302                                        &rtTotal);
   2303 
   2304  // Report the sums of the realm numbers.
   2305  xpc::RealmStatsExtras realmExtrasTotal;
   2306  realmExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/realms/");
   2307  realmExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
   2308  ReportRealmStats(rtStats.realmTotals, realmExtrasTotal, handleReport, data);
   2309 
   2310  xpc::ZoneStatsExtras zExtrasTotal;
   2311  zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
   2312  ReportZoneStats(rtStats.zTotals, zExtrasTotal, handleReport, data, anonymize);
   2313 
   2314  // Report the sum of the runtime/ numbers.
   2315  REPORT_BYTES(
   2316      "js-main-runtime/runtime"_ns, KIND_OTHER, rtTotal,
   2317      "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
   2318 
   2319  // Report the number of HelperThread
   2320 
   2321  REPORT("js-helper-threads/idle"_ns, KIND_OTHER, UNITS_COUNT,
   2322         gStats.helperThread.idleThreadCount,
   2323         "The current number of idle JS HelperThreads.");
   2324 
   2325  REPORT(
   2326      "js-helper-threads/active"_ns, KIND_OTHER, UNITS_COUNT,
   2327      gStats.helperThread.activeThreadCount,
   2328      "The current number of active JS HelperThreads. Memory held by these is"
   2329      " not reported.");
   2330 
   2331  // Report the numbers for memory used by wasm Runtime state.
   2332  REPORT_BYTES("wasm-runtime"_ns, KIND_OTHER, rtStats.runtime.wasmRuntime,
   2333               "The memory used for wasm runtime bookkeeping.");
   2334 
   2335  // Although wasm guard pages aren't committed in memory they can be very
   2336  // large and contribute greatly to vsize and so are worth reporting.
   2337  if (rtStats.runtime.wasmGuardPages > 0) {
   2338    REPORT_BYTES(
   2339        "wasm-guard-pages"_ns, KIND_OTHER, rtStats.runtime.wasmGuardPages,
   2340        "Guard pages mapped after the end of wasm memories, reserved for "
   2341        "optimization tricks, but not committed and thus never contributing"
   2342        " to RSS, only vsize.");
   2343  }
   2344 
   2345  // Report the numbers for memory outside of realms.
   2346 
   2347  REPORT_BYTES("js-main-runtime/gc-heap/unused-chunks"_ns, KIND_OTHER,
   2348               rtStats.gcHeapUnusedChunks,
   2349               "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
   2350 
   2351  REPORT_BYTES("js-main-runtime/gc-heap/unused-arenas"_ns, KIND_OTHER,
   2352               rtStats.gcHeapUnusedArenas,
   2353               "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
   2354 
   2355  REPORT_BYTES("js-main-runtime/gc-heap/chunk-admin"_ns, KIND_OTHER,
   2356               rtStats.gcHeapChunkAdmin,
   2357               "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
   2358 
   2359  // Report a breakdown of the committed GC space.
   2360 
   2361  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/chunks"_ns, KIND_OTHER,
   2362               rtStats.gcHeapUnusedChunks,
   2363               "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
   2364 
   2365  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/arenas"_ns, KIND_OTHER,
   2366               rtStats.gcHeapUnusedArenas,
   2367               "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
   2368 
   2369  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/objects"_ns,
   2370               KIND_OTHER, rtStats.zTotals.unusedGCThings.object,
   2371               "Unused object cells within non-empty arenas.");
   2372 
   2373  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/strings"_ns,
   2374               KIND_OTHER, rtStats.zTotals.unusedGCThings.string,
   2375               "Unused string cells within non-empty arenas.");
   2376 
   2377  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/symbols"_ns,
   2378               KIND_OTHER, rtStats.zTotals.unusedGCThings.symbol,
   2379               "Unused symbol cells within non-empty arenas.");
   2380 
   2381  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/shapes"_ns,
   2382               KIND_OTHER, rtStats.zTotals.unusedGCThings.shape,
   2383               "Unused shape cells within non-empty arenas.");
   2384 
   2385  REPORT_BYTES(
   2386      "js-main-runtime-gc-heap-committed/unused/gc-things/base-shapes"_ns,
   2387      KIND_OTHER, rtStats.zTotals.unusedGCThings.baseShape,
   2388      "Unused base shape cells within non-empty arenas.");
   2389 
   2390  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/bigints"_ns,
   2391               KIND_OTHER, rtStats.zTotals.unusedGCThings.bigInt,
   2392               "Unused BigInt cells within non-empty arenas.");
   2393 
   2394  REPORT_BYTES(
   2395      "js-main-runtime-gc-heap-committed/unused/gc-things/getter-setters"_ns,
   2396      KIND_OTHER, rtStats.zTotals.unusedGCThings.getterSetter,
   2397      "Unused getter-setter cells within non-empty arenas.");
   2398 
   2399  REPORT_BYTES(
   2400      "js-main-runtime-gc-heap-committed/unused/gc-things/property-maps"_ns,
   2401      KIND_OTHER, rtStats.zTotals.unusedGCThings.propMap,
   2402      "Unused property map cells within non-empty arenas.");
   2403 
   2404  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/scopes"_ns,
   2405               KIND_OTHER, rtStats.zTotals.unusedGCThings.scope,
   2406               "Unused scope cells within non-empty arenas.");
   2407 
   2408  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/scripts"_ns,
   2409               KIND_OTHER, rtStats.zTotals.unusedGCThings.script,
   2410               "Unused script cells within non-empty arenas.");
   2411 
   2412  REPORT_BYTES("js-main-runtime-gc-heap-committed/unused/gc-things/jitcode"_ns,
   2413               KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode,
   2414               "Unused jitcode cells within non-empty arenas.");
   2415 
   2416  REPORT_BYTES(
   2417      "js-main-runtime-gc-heap-committed/unused/gc-things/regexp-shareds"_ns,
   2418      KIND_OTHER, rtStats.zTotals.unusedGCThings.regExpShared,
   2419      "Unused regexpshared cells within non-empty arenas.");
   2420 
   2421  REPORT_BYTES(
   2422      "js-main-runtime-gc-heap-committed/unused/gc-things/small-buffers"_ns,
   2423      KIND_OTHER, rtStats.zTotals.unusedGCThings.smallBuffer,
   2424      "Unused small buffer cells within non-empty arenas.");
   2425 
   2426  REPORT_BYTES("js-main-runtime-gc-heap-committed/used/chunk-admin"_ns,
   2427               KIND_OTHER, rtStats.gcHeapChunkAdmin,
   2428               "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
   2429 
   2430  REPORT_BYTES("js-main-runtime-gc-heap-committed/used/arena-admin"_ns,
   2431               KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
   2432               "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
   2433 
   2434  size_t gcThingTotal = 0;
   2435 
   2436  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/objects"_ns,
   2437                KIND_OTHER, rtStats.realmTotals.classInfo.objectsGCHeap,
   2438                "Used object cells.");
   2439 
   2440  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/strings"_ns,
   2441                KIND_OTHER, rtStats.zTotals.stringInfo.sizeOfLiveGCThings(),
   2442                "Used string cells.");
   2443 
   2444  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/symbols"_ns,
   2445                KIND_OTHER, rtStats.zTotals.symbolsGCHeap,
   2446                "Used symbol cells.");
   2447 
   2448  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/shapes"_ns,
   2449                KIND_OTHER,
   2450                rtStats.zTotals.shapeInfo.shapesGCHeapShared +
   2451                    rtStats.zTotals.shapeInfo.shapesGCHeapDict,
   2452                "Used shape cells.");
   2453 
   2454  MREPORT_BYTES(
   2455      "js-main-runtime-gc-heap-committed/used/gc-things/base-shapes"_ns,
   2456      KIND_OTHER, rtStats.zTotals.shapeInfo.shapesGCHeapBase,
   2457      "Used base shape cells.");
   2458 
   2459  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/bigints"_ns,
   2460                KIND_OTHER, rtStats.zTotals.bigIntsGCHeap,
   2461                "Used BigInt cells.");
   2462 
   2463  MREPORT_BYTES(
   2464      "js-main-runtime-gc-heap-committed/used/gc-things/getter-setters"_ns,
   2465      KIND_OTHER, rtStats.zTotals.getterSettersGCHeap,
   2466      "Used getter/setter cells.");
   2467 
   2468  MREPORT_BYTES(
   2469      "js-main-runtime-gc-heap-committed/used/gc-things/property-maps"_ns,
   2470      KIND_OTHER,
   2471      rtStats.zTotals.dictPropMapsGCHeap +
   2472          rtStats.zTotals.compactPropMapsGCHeap +
   2473          rtStats.zTotals.normalPropMapsGCHeap,
   2474      "Used property map cells.");
   2475 
   2476  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/scopes"_ns,
   2477                KIND_OTHER, rtStats.zTotals.scopesGCHeap, "Used scope cells.");
   2478 
   2479  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/scripts"_ns,
   2480                KIND_OTHER, rtStats.realmTotals.scriptsGCHeap,
   2481                "Used script cells.");
   2482 
   2483  MREPORT_BYTES("js-main-runtime-gc-heap-committed/used/gc-things/jitcode"_ns,
   2484                KIND_OTHER, rtStats.zTotals.jitCodesGCHeap,
   2485                "Used jitcode cells.");
   2486 
   2487  MREPORT_BYTES(
   2488      "js-main-runtime-gc-heap-committed/used/gc-things/regexp-shareds"_ns,
   2489      KIND_OTHER, rtStats.zTotals.regExpSharedsGCHeap,
   2490      "Used regexpshared cells.");
   2491 
   2492  MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings);
   2493  (void)gcThingTotal;
   2494 
   2495  // Report totals from per-zone GC buffer allocators.
   2496 
   2497  MREPORT_BYTES("js-main-runtime-gc-buffers/used"_ns, KIND_OTHER,
   2498                rtStats.zTotals.gcBuffers.usedBytes,
   2499                "Bookeeping information and padding within GC buffer memeory.");
   2500 
   2501  MREPORT_BYTES("js-main-runtime-gc-buffers/free"_ns, KIND_OTHER,
   2502                rtStats.zTotals.gcBuffers.freeBytes,
   2503                "Free space within GC buffer memeory.");
   2504 
   2505  MREPORT_BYTES("js-main-runtime-gc-buffers/admin"_ns, KIND_OTHER,
   2506                rtStats.zTotals.gcBuffers.adminBytes,
   2507                "Bookeeping information and padding within GC buffer memeory.");
   2508 
   2509  REPORT("js-main-runtime-zone-count"_ns, KIND_OTHER, UNITS_COUNT,
   2510         rtStats.zoneStatsVector.length(), "Count of GC zones in the runtime.");
   2511 
   2512  // Report xpconnect.
   2513 
   2514  REPORT_BYTES("explicit/xpconnect/runtime"_ns, KIND_HEAP, xpcJSRuntimeSize,
   2515               "The XPConnect runtime.");
   2516 
   2517  REPORT_BYTES("explicit/xpconnect/wrappedjs"_ns, KIND_HEAP, wrappedJSSize,
   2518               "Wrappers used to implement XPIDL interfaces with JS.");
   2519 
   2520  REPORT_BYTES("explicit/xpconnect/scopes"_ns, KIND_HEAP,
   2521               sizeInfo.mScopeAndMapSize, "XPConnect scopes.");
   2522 
   2523  REPORT_BYTES("explicit/xpconnect/proto-iface-cache"_ns, KIND_HEAP,
   2524               sizeInfo.mProtoAndIfaceCacheSize,
   2525               "Prototype and interface binding caches.");
   2526 
   2527  REPORT_BYTES("explicit/xpconnect/js-module-loader"_ns, KIND_HEAP,
   2528               jsModuleLoaderSize, "XPConnect's JS module loader.");
   2529  REPORT_BYTES("explicit/xpconnect/js-devtools-module-loader"_ns, KIND_HEAP,
   2530               jsDevToolsModuleLoaderSize, "DevTools's JS module loader.");
   2531 
   2532  // Report HelperThreadState.
   2533 
   2534  REPORT_BYTES("explicit/js-non-window/helper-thread/heap-other"_ns, KIND_HEAP,
   2535               gStats.helperThread.stateData,
   2536               "Memory used by HelperThreadState.");
   2537 
   2538  REPORT_BYTES(
   2539      "explicit/js-non-window/helper-thread/ion-compile-task"_ns, KIND_HEAP,
   2540      gStats.helperThread.ionCompileTask,
   2541      "The memory used by IonCompileTasks waiting in HelperThreadState.");
   2542 
   2543  REPORT_BYTES(
   2544      "explicit/js-non-window/helper-thread/wasm-compile"_ns, KIND_HEAP,
   2545      gStats.helperThread.wasmCompile,
   2546      "The memory used by Wasm compilations waiting in HelperThreadState.");
   2547 
   2548  REPORT_BYTES("explicit/js-non-window/helper-thread/contexts"_ns, KIND_HEAP,
   2549               gStats.helperThread.contexts,
   2550               "The memory used by the JSContexts in HelperThreadState.");
   2551 }
   2552 
   2553 static nsresult JSSizeOfTab(JSObject* obj, size_t* jsObjectsSize,
   2554                            size_t* jsStringsSize, size_t* jsPrivateSize,
   2555                            size_t* jsOtherSize) {
   2556  JSContext* cx = XPCJSContext::Get()->Context();
   2557  JS::Zone* zone = JS::GetObjectZone(obj);
   2558  if (JS::IsIncrementalGCInProgress(cx)) {
   2559    JS::FinishIncrementalGC(cx, JS::GCReason::PREPARE_FOR_TRACING);
   2560  }
   2561  JS::AutoCheckCannotGC nogc(cx);
   2562 
   2563  TabSizes sizes;
   2564  OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
   2565  NS_ENSURE_TRUE(JS::AddSizeOfTab(cx, zone, moz_malloc_size_of, &orphanReporter,
   2566                                  &sizes, nogc),
   2567                 NS_ERROR_OUT_OF_MEMORY);
   2568 
   2569  *jsObjectsSize = sizes.objects_;
   2570  *jsStringsSize = sizes.strings_;
   2571  *jsPrivateSize = sizes.private_;
   2572  *jsOtherSize = sizes.other_;
   2573  return NS_OK;
   2574 }
   2575 
   2576 }  // namespace xpc
   2577 
   2578 static void AccumulateTelemetryCallback(JSMetric id, uint32_t sample) {
   2579  switch (id) {
   2580    case JSMetric::GC_MS:
   2581      glean::javascript_gc::total_time.AccumulateRawDuration(
   2582          TimeDuration::FromMilliseconds(sample));
   2583      break;
   2584    case JSMetric::GC_MINOR_US:
   2585      glean::javascript_gc::minor_time.AccumulateRawDuration(
   2586          TimeDuration::FromMicroseconds(sample));
   2587      break;
   2588    case JSMetric::GC_PREPARE_MS:
   2589      glean::javascript_gc::prepare_time.AccumulateRawDuration(
   2590          TimeDuration::FromMilliseconds(sample));
   2591      break;
   2592    case JSMetric::GC_MARK_ROOTS_US:
   2593      glean::javascript_gc::mark_roots_time.AccumulateRawDuration(
   2594          TimeDuration::FromMicroseconds(sample));
   2595      break;
   2596    case JSMetric::GC_MARK_MS:
   2597      glean::javascript_gc::mark_time.AccumulateRawDuration(
   2598          TimeDuration::FromMilliseconds(sample));
   2599      break;
   2600    case JSMetric::GC_SWEEP_MS:
   2601      glean::javascript_gc::sweep_time.AccumulateRawDuration(
   2602          TimeDuration::FromMilliseconds(sample));
   2603      break;
   2604    case JSMetric::GC_COMPACT_MS:
   2605      glean::javascript_gc::compact_time.AccumulateRawDuration(
   2606          TimeDuration::FromMilliseconds(sample));
   2607      break;
   2608    case JSMetric::GC_SLICE_MS:
   2609      glean::javascript_gc::slice_time.AccumulateRawDuration(
   2610          TimeDuration::FromMilliseconds(sample));
   2611      break;
   2612    case JSMetric::ION_COMPILE_TIME:
   2613      glean::javascript_ion::compile_time.AccumulateRawDuration(
   2614          TimeDuration::FromMicroseconds(sample));
   2615      break;
   2616    case JSMetric::GC_BUDGET_MS_2:
   2617      glean::javascript_gc::budget.AccumulateRawDuration(
   2618          TimeDuration::FromMilliseconds(sample));
   2619      break;
   2620    case JSMetric::GC_BUDGET_OVERRUN:
   2621      glean::javascript_gc::budget_overrun.AccumulateRawDuration(
   2622          TimeDuration::FromMicroseconds(sample));
   2623      break;
   2624    case JSMetric::GC_ANIMATION_MS:
   2625      glean::javascript_gc::animation.AccumulateRawDuration(
   2626          TimeDuration::FromMilliseconds(sample));
   2627      break;
   2628    case JSMetric::GC_MAX_PAUSE_MS_2:
   2629      glean::javascript_gc::max_pause.AccumulateRawDuration(
   2630          TimeDuration::FromMilliseconds(sample));
   2631      break;
   2632    case JSMetric::GC_MARK_GRAY_MS_2:
   2633      glean::javascript_gc::mark_gray.AccumulateRawDuration(
   2634          TimeDuration::FromMilliseconds(sample));
   2635      break;
   2636    case JSMetric::GC_MARK_WEAK_MS:
   2637      glean::javascript_gc::mark_weak.AccumulateRawDuration(
   2638          TimeDuration::FromMilliseconds(sample));
   2639      break;
   2640    case JSMetric::GC_TIME_BETWEEN_S:
   2641      glean::javascript_gc::time_between.AccumulateRawDuration(
   2642          TimeDuration::FromSeconds(sample));
   2643      break;
   2644    case JSMetric::GC_TIME_BETWEEN_SLICES_MS:
   2645      glean::javascript_gc::time_between_slices.AccumulateRawDuration(
   2646          TimeDuration::FromMilliseconds(sample));
   2647      break;
   2648    case JSMetric::GC_TIME_BETWEEN_MINOR_MS:
   2649      glean::javascript_gc::time_between_minor.AccumulateRawDuration(
   2650          TimeDuration::FromMilliseconds(sample));
   2651      break;
   2652    case JSMetric::GC_TASK_START_DELAY_US:
   2653      glean::javascript_gc::task_start_delay.AccumulateRawDuration(
   2654          TimeDuration::FromMicroseconds(sample));
   2655      break;
   2656    case JSMetric::GC_MMU_50:
   2657      glean::javascript_gc::mmu_50.AccumulateSingleSample(sample);
   2658      break;
   2659    case JSMetric::GC_NURSERY_PROMOTION_RATE:
   2660      glean::javascript_gc::nursery_promotion_rate.AccumulateSingleSample(
   2661          sample);
   2662      break;
   2663    case JSMetric::GC_TENURED_SURVIVAL_RATE:
   2664      glean::javascript_gc::tenured_survival_rate.AccumulateSingleSample(
   2665          sample);
   2666      break;
   2667    case JSMetric::GC_PARALLEL_MARK_UTILIZATION:
   2668      glean::javascript_gc::parallel_mark_utilization.AccumulateSingleSample(
   2669          sample);
   2670      break;
   2671    case JSMetric::GC_NURSERY_BYTES_2:
   2672      glean::javascript_gc::nursery_bytes.Accumulate(sample);
   2673      break;
   2674    case JSMetric::GC_EFFECTIVENESS:
   2675      glean::javascript_gc::effectiveness.AccumulateSingleSample(sample);
   2676      break;
   2677    case JSMetric::GC_ZONE_COUNT:
   2678      glean::javascript_gc::zone_count.AccumulateSingleSample(sample);
   2679      break;
   2680    case JSMetric::GC_ZONES_COLLECTED:
   2681      glean::javascript_gc::zones_collected.AccumulateSingleSample(sample);
   2682      break;
   2683    case JSMetric::GC_PRETENURE_COUNT_2:
   2684      glean::javascript_gc::pretenure_count.AccumulateSingleSample(sample);
   2685      break;
   2686    case JSMetric::GC_MARK_RATE_2:
   2687      glean::javascript_gc::mark_rate.AccumulateSingleSample(sample);
   2688      break;
   2689    case JSMetric::GC_SLICE_COUNT:
   2690      glean::javascript_gc::slice_count.AccumulateSingleSample(sample);
   2691      break;
   2692    case JSMetric::GC_PARALLEL_MARK_SPEEDUP:
   2693      glean::javascript_gc::parallel_mark_speedup.AccumulateSingleSample(
   2694          sample);
   2695      break;
   2696    case JSMetric::GC_PARALLEL_MARK_INTERRUPTIONS:
   2697      glean::javascript_gc::parallel_mark_interruptions.AccumulateSingleSample(
   2698          sample);
   2699      break;
   2700    case JSMetric::GC_IS_COMPARTMENTAL:
   2701      if (sample) {
   2702        glean::javascript_gc::is_zone_gc
   2703            .EnumGet(glean::javascript_gc::IsZoneGcLabel::eTrue)
   2704            .Add(1);
   2705      } else {
   2706        glean::javascript_gc::is_zone_gc
   2707            .EnumGet(glean::javascript_gc::IsZoneGcLabel::eFalse)
   2708            .Add(1);
   2709      }
   2710      break;
   2711    case JSMetric::GC_BUDGET_WAS_INCREASED:
   2712      if (sample) {
   2713        glean::javascript_gc::budget_was_increased
   2714            .EnumGet(glean::javascript_gc::BudgetWasIncreasedLabel::eTrue)
   2715            .Add(1);
   2716      } else {
   2717        glean::javascript_gc::budget_was_increased
   2718            .EnumGet(glean::javascript_gc::BudgetWasIncreasedLabel::eFalse)
   2719            .Add(1);
   2720      }
   2721      break;
   2722    case JSMetric::GC_SLICE_WAS_LONG:
   2723      if (sample) {
   2724        glean::javascript_gc::slice_was_long
   2725            .EnumGet(glean::javascript_gc::SliceWasLongLabel::eTrue)
   2726            .Add(1);
   2727      } else {
   2728        glean::javascript_gc::slice_was_long
   2729            .EnumGet(glean::javascript_gc::SliceWasLongLabel::eFalse)
   2730            .Add(1);
   2731      }
   2732      break;
   2733    case JSMetric::GC_RESET:
   2734      if (sample) {
   2735        glean::javascript_gc::reset
   2736            .EnumGet(glean::javascript_gc::ResetLabel::eTrue)
   2737            .Add(1);
   2738      } else {
   2739        glean::javascript_gc::reset
   2740            .EnumGet(glean::javascript_gc::ResetLabel::eFalse)
   2741            .Add(1);
   2742      }
   2743      break;
   2744    case JSMetric::GC_NON_INCREMENTAL:
   2745      if (sample) {
   2746        glean::javascript_gc::non_incremental
   2747            .EnumGet(glean::javascript_gc::NonIncrementalLabel::eTrue)
   2748            .Add(1);
   2749      } else {
   2750        glean::javascript_gc::non_incremental
   2751            .EnumGet(glean::javascript_gc::NonIncrementalLabel::eFalse)
   2752            .Add(1);
   2753      }
   2754      break;
   2755    case JSMetric::GC_PARALLEL_MARK:
   2756      if (sample) {
   2757        glean::javascript_gc::parallel_mark_used
   2758            .EnumGet(glean::javascript_gc::ParallelMarkUsedLabel::eTrue)
   2759            .Add(1);
   2760      } else {
   2761        glean::javascript_gc::parallel_mark_used
   2762            .EnumGet(glean::javascript_gc::ParallelMarkUsedLabel::eFalse)
   2763            .Add(1);
   2764      }
   2765      break;
   2766    case JSMetric::GC_REASON_2: {
   2767      // Assert that every reason has an associated glean label.
   2768      static_assert(
   2769          static_cast<uint8_t>(JS::GCReason::LAST_FIREFOX_REASON) + 1 ==
   2770              static_cast<uint8_t>(
   2771                  glean::javascript_gc::ReasonLabel::e__Other__),
   2772          "GC reason enum and glean::javascript_gc::reason labels do "
   2773          "not match.");
   2774      MOZ_ASSERT(static_cast<JS::GCReason>(sample) <=
   2775                     JS::GCReason::LAST_FIREFOX_REASON,
   2776                 "Invalid GC Reason.");
   2777 
   2778      nsAutoCString reason(
   2779          JS::ExplainGCReason(static_cast<JS::GCReason>(sample)));
   2780      glean::javascript_gc::reason.Get(reason).Add(1);
   2781    } break;
   2782    case JSMetric::GC_RESET_REASON: {
   2783      MOZ_ASSERT(
   2784          sample < static_cast<uint32_t>(
   2785                       glean::javascript_gc::ResetReasonLabel::e__Other__),
   2786          "Reason does not exist in the reset_reason labels list.");
   2787      nsAutoCString reason(JS::ExplainGCAbortReason(sample));
   2788      glean::javascript_gc::reset_reason.Get(reason).Add(1);
   2789    } break;
   2790    case JSMetric::GC_NON_INCREMENTAL_REASON: {
   2791      MOZ_ASSERT(
   2792          sample <
   2793              static_cast<uint32_t>(
   2794                  glean::javascript_gc::NonIncrementalReasonLabel::e__Other__),
   2795          "Reason does not exist in the non_incremental_reason labels list.");
   2796      nsAutoCString reason(JS::ExplainGCAbortReason(sample));
   2797      glean::javascript_gc::non_incremental_reason.Get(reason).Add(1);
   2798    } break;
   2799    case JSMetric::GC_MINOR_REASON: {
   2800      // Assert that every reason has an associated glean label.
   2801      static_assert(
   2802          static_cast<uint8_t>(JS::GCReason::LAST_FIREFOX_REASON) + 1 ==
   2803              static_cast<uint8_t>(
   2804                  glean::javascript_gc::MinorReasonLabel::e__Other__),
   2805          "GC reason enum and glean::javascript_gc::reason labels do not "
   2806          "match.");
   2807      MOZ_ASSERT(static_cast<JS::GCReason>(sample) <=
   2808                     JS::GCReason::LAST_FIREFOX_REASON,
   2809                 "Invalid GC Reason.");
   2810 
   2811      nsAutoCString reason(
   2812          JS::ExplainGCReason(static_cast<JS::GCReason>(sample)));
   2813      glean::javascript_gc::minor_reason.Get(reason).Add(1);
   2814    } break;
   2815    case JSMetric::GC_MINOR_REASON_LONG: {
   2816      // Assert that every reason has an associated glean label.
   2817      static_assert(
   2818          static_cast<uint8_t>(JS::GCReason::LAST_FIREFOX_REASON) + 1 ==
   2819              static_cast<uint8_t>(
   2820                  glean::javascript_gc::MinorReasonLongLabel::e__Other__),
   2821          "GC reason enum and glean::javascript_gc::reason labels do not "
   2822          "match.");
   2823      MOZ_ASSERT(static_cast<JS::GCReason>(sample) <=
   2824                     JS::GCReason::LAST_FIREFOX_REASON,
   2825                 "Invalid GC Reason.");
   2826 
   2827      nsAutoCString reason(
   2828          JS::ExplainGCReason(static_cast<JS::GCReason>(sample)));
   2829      glean::javascript_gc::minor_reason_long.Get(reason).Add(1);
   2830    } break;
   2831    case JSMetric::GC_SLOW_PHASE: {
   2832      MOZ_ASSERT(sample < static_cast<uint32_t>(
   2833                              glean::javascript_gc::SlowPhaseLabel::e__Other__),
   2834                 "Phase does not exist in the slow_phase labels list.");
   2835      nsAutoCString phase(JS::GetGCPhaseName(sample));
   2836      glean::javascript_gc::slow_phase.Get(phase).Add(1);
   2837    } break;
   2838    case JSMetric::GC_SLOW_TASK: {
   2839      MOZ_ASSERT(sample < static_cast<uint32_t>(
   2840                              glean::javascript_gc::SlowTaskLabel::e__Other__),
   2841                 "Phase does not exist in the slow_task labels list.");
   2842      nsAutoCString phase(JS::GetGCPhaseName(sample));
   2843      glean::javascript_gc::slow_task.Get(phase).Add(1);
   2844    } break;
   2845 
   2846    default:
   2847      // The rest aren't relayed to Glean.
   2848      break;
   2849  }
   2850 }
   2851 
   2852 static void SetUseCounterCallback(JSObject* obj, JSUseCounter counter) {
   2853  switch (counter) {
   2854    case JSUseCounter::ASMJS:
   2855      SetUseCounter(obj, eUseCounter_custom_JS_asmjs);
   2856      return;
   2857    case JSUseCounter::WASM:
   2858      SetUseCounter(obj, eUseCounter_custom_JS_wasm);
   2859      return;
   2860    case JSUseCounter::USE_ASM:
   2861      SetUseCounter(obj, eUseCounter_custom_JS_use_asm);
   2862      return;
   2863    case JSUseCounter::WASM_LEGACY_EXCEPTIONS:
   2864      SetUseCounter(obj, eUseCounter_custom_JS_wasm_legacy_exceptions);
   2865      return;
   2866    case JSUseCounter::ISHTMLDDA_FUSE:
   2867      SetUseCounter(obj, eUseCounter_custom_JS_isHTMLDDA_fuse);
   2868      return;
   2869    case JSUseCounter::OPTIMIZE_GET_ITERATOR_FUSE:
   2870      SetUseCounter(obj, eUseCounter_custom_JS_optimizeGetIterator_fuse);
   2871      return;
   2872    case JSUseCounter::OPTIMIZE_ARRAY_SPECIES_FUSE:
   2873      SetUseCounter(obj, eUseCounter_custom_JS_optimizeArraySpecies_fuse);
   2874      return;
   2875    case JSUseCounter::OPTIMIZE_PROMISE_LOOKUP_FUSE:
   2876      SetUseCounter(obj, eUseCounter_custom_JS_optimizePromiseLookup_fuse);
   2877      return;
   2878    case JSUseCounter::THENABLE_USE:
   2879      SetUseCounter(obj, eUseCounter_custom_JS_thenable);
   2880      return;
   2881    case JSUseCounter::THENABLE_USE_PROTO:
   2882      SetUseCounter(obj, eUseCounter_custom_JS_thenable_proto);
   2883      return;
   2884    case JSUseCounter::THENABLE_USE_STANDARD_PROTO:
   2885      SetUseCounter(obj, eUseCounter_custom_JS_thenable_standard_proto);
   2886      return;
   2887    case JSUseCounter::THENABLE_USE_OBJECT_PROTO:
   2888      SetUseCounter(obj, eUseCounter_custom_JS_thenable_object_proto);
   2889      return;
   2890    case JSUseCounter::LEGACY_LANG_SUBTAG:
   2891      SetUseCounter(obj, eUseCounter_custom_JS_legacy_lang_subtag);
   2892      return;
   2893    case JSUseCounter::IC_STUB_TOO_LARGE:
   2894      SetUseCounter(obj, eUseCounter_custom_JS_ic_stub_too_large);
   2895      return;
   2896    case JSUseCounter::IC_STUB_OOM:
   2897      SetUseCounter(obj, eUseCounter_custom_JS_ic_stub_oom);
   2898      return;
   2899    case JSUseCounter::DATEPARSE:
   2900      SetUseCounter(obj, eUseCounter_custom_JS_dateparse);
   2901      return;
   2902    case JSUseCounter::DATEPARSE_IMPL_DEF:
   2903      SetUseCounter(obj, eUseCounter_custom_JS_dateparse_impl_def);
   2904      return;
   2905    case JSUseCounter::COUNT:
   2906      break;
   2907  }
   2908  MOZ_ASSERT_UNREACHABLE("Unexpected JSUseCounter id");
   2909 }
   2910 
   2911 static void GetRealmNameCallback(JSContext* cx, Realm* realm, char* buf,
   2912                                 size_t bufsize,
   2913                                 const JS::AutoRequireNoGC& nogc) {
   2914  nsCString name;
   2915  // This is called via the JSAPI and isn't involved in memory reporting, so
   2916  // we don't need to anonymize realm names.
   2917  int anonymizeID = 0;
   2918  GetRealmName(realm, name, &anonymizeID, /* replaceSlashes = */ false);
   2919  if (name.Length() >= bufsize) {
   2920    name.Truncate(bufsize - 1);
   2921  }
   2922  memcpy(buf, name.get(), name.Length() + 1);
   2923 }
   2924 
   2925 static void DestroyRealm(JS::GCContext* gcx, JS::Realm* realm) {
   2926  // Get the current compartment private into an AutoPtr (which will do the
   2927  // cleanup for us), and null out the private field.
   2928  mozilla::UniquePtr<RealmPrivate> priv(RealmPrivate::Get(realm));
   2929  JS::SetRealmPrivate(realm, nullptr);
   2930 }
   2931 
   2932 static bool PreserveWrapper(JSContext* cx, JS::Handle<JSObject*> obj) {
   2933  MOZ_ASSERT(cx);
   2934  MOZ_ASSERT(obj);
   2935  MOZ_ASSERT(mozilla::dom::IsDOMObject(obj));
   2936 
   2937  if (!mozilla::dom::TryPreserveWrapper(obj)) {
   2938    return false;
   2939  }
   2940 
   2941  MOZ_ASSERT(!mozilla::dom::HasReleasedWrapper(obj),
   2942             "There should be no released wrapper since we just preserved it");
   2943 
   2944  return true;
   2945 }
   2946 
   2947 static nsresult ReadSourceFromFilename(JSContext* cx, const char* filename,
   2948                                       char16_t** twoByteSource,
   2949                                       char** utf8Source, size_t* len) {
   2950  MOZ_ASSERT(*len == 0);
   2951  MOZ_ASSERT((twoByteSource != nullptr) != (utf8Source != nullptr),
   2952             "must be called requesting only one of UTF-8 or UTF-16 source");
   2953  MOZ_ASSERT_IF(twoByteSource, !*twoByteSource);
   2954  MOZ_ASSERT_IF(utf8Source, !*utf8Source);
   2955 
   2956  nsresult rv;
   2957 
   2958  // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with
   2959  // the filename of its caller. Axe that if present.
   2960  const char* arrow;
   2961  while ((arrow = strstr(filename, " -> "))) {
   2962    filename = arrow + strlen(" -> ");
   2963  }
   2964 
   2965  // Get the URI.
   2966  nsCOMPtr<nsIURI> uri;
   2967  rv = NS_NewURI(getter_AddRefs(uri), filename);
   2968  NS_ENSURE_SUCCESS(rv, rv);
   2969 
   2970  nsCOMPtr<nsIChannel> scriptChannel;
   2971  rv = NS_NewChannel(getter_AddRefs(scriptChannel), uri,
   2972                     nsContentUtils::GetSystemPrincipal(),
   2973                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
   2974                     nsIContentPolicy::TYPE_OTHER);
   2975  NS_ENSURE_SUCCESS(rv, rv);
   2976 
   2977  // Only allow local reading.
   2978  nsCOMPtr<nsIURI> actualUri;
   2979  rv = scriptChannel->GetURI(getter_AddRefs(actualUri));
   2980  NS_ENSURE_SUCCESS(rv, rv);
   2981  nsCString scheme;
   2982  rv = actualUri->GetScheme(scheme);
   2983  NS_ENSURE_SUCCESS(rv, rv);
   2984  if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")) {
   2985    return NS_OK;
   2986  }
   2987 
   2988  // Explicitly set the content type so that we don't load the
   2989  // exthandler to guess it.
   2990  scriptChannel->SetContentType("text/plain"_ns);
   2991 
   2992  nsCOMPtr<nsIInputStream> scriptStream;
   2993  rv = scriptChannel->Open(getter_AddRefs(scriptStream));
   2994  NS_ENSURE_SUCCESS(rv, rv);
   2995 
   2996  uint64_t rawLen;
   2997  rv = scriptStream->Available(&rawLen);
   2998  NS_ENSURE_SUCCESS(rv, rv);
   2999  if (!rawLen) {
   3000    return NS_ERROR_FAILURE;
   3001  }
   3002 
   3003  // Technically, this should be SIZE_MAX, but we don't run on machines
   3004  // where that would be less than UINT32_MAX, and the latter is already
   3005  // well beyond a reasonable limit.
   3006  if (rawLen > UINT32_MAX) {
   3007    return NS_ERROR_FILE_TOO_BIG;
   3008  }
   3009 
   3010  // Allocate a buffer the size of the file to initially fill with the UTF-8
   3011  // contents of the file.  Use the JS allocator so that if UTF-8 source was
   3012  // requested, we can return this memory directly.
   3013  JS::UniqueChars buf(js_pod_malloc<char>(rawLen));
   3014  if (!buf) {
   3015    return NS_ERROR_OUT_OF_MEMORY;
   3016  }
   3017 
   3018  char* ptr = buf.get();
   3019  char* end = ptr + rawLen;
   3020  while (ptr < end) {
   3021    uint32_t bytesRead;
   3022    rv = scriptStream->Read(ptr, PointerRangeSize(ptr, end), &bytesRead);
   3023    if (NS_FAILED(rv)) {
   3024      return rv;
   3025    }
   3026    MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF");
   3027    ptr += bytesRead;
   3028  }
   3029 
   3030  if (utf8Source) {
   3031    // |buf| is already UTF-8, so we can directly return it.
   3032    *len = rawLen;
   3033    *utf8Source = buf.release();
   3034  } else {
   3035    MOZ_ASSERT(twoByteSource != nullptr);
   3036 
   3037    // |buf| can't be directly returned -- convert it to UTF-16.
   3038 
   3039    // On success this overwrites |chars| and |*len|.
   3040    JS::UniqueTwoByteChars chars;
   3041    rv = ScriptLoader::ConvertToUTF16(
   3042        scriptChannel, reinterpret_cast<const unsigned char*>(buf.get()),
   3043        rawLen, u"UTF-8"_ns, nullptr, chars, *len);
   3044    NS_ENSURE_SUCCESS(rv, rv);
   3045 
   3046    if (!chars) {
   3047      return NS_ERROR_FAILURE;
   3048    }
   3049 
   3050    *twoByteSource = chars.release();
   3051  }
   3052 
   3053  return NS_OK;
   3054 }
   3055 
   3056 // The JS engine calls this object's 'load' member function when it needs
   3057 // the source for a chrome JS function. See the comment in the XPCJSRuntime
   3058 // constructor.
   3059 class XPCJSSourceHook : public js::SourceHook {
   3060  bool load(JSContext* cx, const char* filename, char16_t** twoByteSource,
   3061            char** utf8Source, size_t* length) override {
   3062    MOZ_ASSERT((twoByteSource != nullptr) != (utf8Source != nullptr),
   3063               "must be called requesting only one of UTF-8 or UTF-16 source");
   3064 
   3065    *length = 0;
   3066    if (twoByteSource) {
   3067      *twoByteSource = nullptr;
   3068    } else {
   3069      *utf8Source = nullptr;
   3070    }
   3071 
   3072    if (!nsContentUtils::IsSystemCaller(cx)) {
   3073      return true;
   3074    }
   3075 
   3076    if (!filename) {
   3077      return true;
   3078    }
   3079 
   3080    nsresult rv =
   3081        ReadSourceFromFilename(cx, filename, twoByteSource, utf8Source, length);
   3082    if (NS_FAILED(rv)) {
   3083      xpc::Throw(cx, rv);
   3084      return false;
   3085    }
   3086 
   3087    return true;
   3088  }
   3089 };
   3090 
   3091 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
   3092    xpc::WrapperFactory::Rewrap, xpc::WrapperFactory::PrepareForWrapping};
   3093 
   3094 XPCJSRuntime::XPCJSRuntime(JSContext* aCx)
   3095    : CycleCollectedJSRuntime(aCx),
   3096      mWrappedJSMap(mozilla::MakeUnique<JSObject2WrappedJSMap>()),
   3097      mIID2NativeInterfaceMap(mozilla::MakeUnique<IID2NativeInterfaceMap>()),
   3098      mClassInfo2NativeSetMap(mozilla::MakeUnique<ClassInfo2NativeSetMap>()),
   3099      mNativeSetMap(mozilla::MakeUnique<NativeSetMap>()),
   3100      mGCIsRunning(false),
   3101      mDoingFinalization(false),
   3102      mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()) {
   3103  MOZ_COUNT_CTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
   3104 }
   3105 
   3106 /* static */
   3107 XPCJSRuntime* XPCJSRuntime::Get() { return nsXPConnect::GetRuntimeInstance(); }
   3108 
   3109 // Subclass of JS::ubi::Base for DOM reflector objects for the JS::ubi::Node
   3110 // memory analysis framework; see js/public/UbiNode.h. In
   3111 // XPCJSRuntime::Initialize, we register the ConstructUbiNode function as a hook
   3112 // with the SpiderMonkey runtime for it to use to construct ubi::Nodes of this
   3113 // class for JSObjects whose class has the JSCLASS_IS_DOMJSCLASS flag set.
   3114 // ReflectorNode specializes Concrete<JSObject> for DOM reflector nodes,
   3115 // reporting the edge from the JSObject to the nsINode it represents, in
   3116 // addition to the usual edges departing any normal JSObject.
   3117 namespace JS {
   3118 namespace ubi {
   3119 class ReflectorNode : public Concrete<JSObject> {
   3120 protected:
   3121  explicit ReflectorNode(JSObject* ptr) : Concrete<JSObject>(ptr) {}
   3122 
   3123 public:
   3124  static void construct(void* storage, JSObject* ptr) {
   3125    new (storage) ReflectorNode(ptr);
   3126  }
   3127  js::UniquePtr<JS::ubi::EdgeRange> edges(JSContext* cx,
   3128                                          bool wantNames) const override;
   3129 };
   3130 
   3131 js::UniquePtr<EdgeRange> ReflectorNode::edges(JSContext* cx,
   3132                                              bool wantNames) const {
   3133  js::UniquePtr<SimpleEdgeRange> range(static_cast<SimpleEdgeRange*>(
   3134      Concrete<JSObject>::edges(cx, wantNames).release()));
   3135  if (!range) {
   3136    return nullptr;
   3137  }
   3138  // UNWRAP_NON_WRAPPER_OBJECT assumes the object is completely initialized,
   3139  // but ours may not be. Luckily, UnwrapDOMObjectToISupports checks for the
   3140  // uninitialized case (and returns null if uninitialized), so we can use that
   3141  // to guard against uninitialized objects.
   3142  nsISupports* supp = UnwrapDOMObjectToISupports(&get());
   3143  if (supp) {
   3144    JS::AutoSuppressGCAnalysis nogc;  // bug 1582326
   3145 
   3146    nsINode* node;
   3147    // UnwrapDOMObjectToISupports can only return non-null if its argument is
   3148    // an actual DOM object, not a cross-compartment wrapper.
   3149    if (NS_SUCCEEDED(UNWRAP_NON_WRAPPER_OBJECT(Node, &get(), node))) {
   3150      char16_t* edgeName = nullptr;
   3151      if (wantNames) {
   3152        edgeName = NS_xstrdup(u"Reflected Node");
   3153      }
   3154      if (!range->addEdge(Edge(edgeName, node))) {
   3155        return nullptr;
   3156      }
   3157    }
   3158  }
   3159  return js::UniquePtr<EdgeRange>(range.release());
   3160 }
   3161 
   3162 }  // Namespace ubi
   3163 }  // Namespace JS
   3164 
   3165 void ConstructUbiNode(void* storage, JSObject* ptr) {
   3166  JS::ubi::ReflectorNode::construct(storage, ptr);
   3167 }
   3168 
   3169 void XPCJSRuntime::Initialize(JSContext* cx) {
   3170  // these jsids filled in later when we have a JSContext to work with.
   3171  mStrIDs[0] = JS::PropertyKey::Void();
   3172 
   3173  nsScriptSecurityManager::GetScriptSecurityManager()->InitJSCallbacks(cx);
   3174 
   3175  // Unconstrain the runtime's threshold on nominal heap size, to avoid
   3176  // triggering GC too often if operating continuously near an arbitrary
   3177  // finite threshold (0xffffffff is infinity for uint32_t parameters).
   3178  // This leaves the maximum-JS_malloc-bytes threshold still in effect
   3179  // to cause period, and we hope hygienic, last-ditch GCs from within
   3180  // the GC's allocator.
   3181  JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff);
   3182 
   3183  JS_SetDestroyCompartmentCallback(cx, CompartmentDestroyedCallback);
   3184  JS_SetSizeOfIncludingThisCompartmentCallback(
   3185      cx, CompartmentSizeOfIncludingThisCallback);
   3186  JS::SetDestroyRealmCallback(cx, DestroyRealm);
   3187  JS::SetRealmNameCallback(cx, GetRealmNameCallback);
   3188  mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
   3189  mPrevDoCycleCollectionCallback =
   3190      JS::SetDoCycleCollectionCallback(cx, DoCycleCollectionCallback);
   3191  JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
   3192  JS_AddWeakPointerZonesCallback(cx, WeakPointerZonesCallback, this);
   3193  JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback,
   3194                                       this);
   3195  JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
   3196  if (XRE_IsE10sParentProcess()) {
   3197    JS::SetFilenameValidationCallback(
   3198        nsContentSecurityUtils::ValidateScriptFilename);
   3199  }
   3200  js::SetPreserveWrapperCallbacks(cx, PreserveWrapper, HasReleasedWrapper);
   3201  JS_InitReadPrincipalsCallback(cx, nsJSPrincipals::ReadPrincipals);
   3202  JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
   3203  JS_SetSetUseCounterCallback(cx, SetUseCounterCallback);
   3204 
   3205  js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
   3206 
   3207  JS::SetXrayJitInfo(&gXrayJitInfo);
   3208  JS::SetProcessLargeAllocationFailureCallback(
   3209      OnLargeAllocationFailureCallback);
   3210 
   3211  // The WasmAltDataType is build by the JS engine from the build id.
   3212  JS::SetProcessBuildIdOp(GetBuildId);
   3213  FetchUtil::InitWasmAltDataType();
   3214 
   3215  // The JS engine needs to keep the source code around in order to implement
   3216  // Function.prototype.toSource(). It'd be nice to not have to do this for
   3217  // chrome code and simply stub out requests for source on it. Life is not so
   3218  // easy, unfortunately. Nobody relies on chrome toSource() working in core
   3219  // browser code, but chrome tests use it. The worst offenders are addons,
   3220  // which like to monkeypatch chrome functions by calling toSource() on them
   3221  // and using regular expressions to modify them. We avoid keeping most browser
   3222  // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when
   3223  // compiling some chrome code. This causes the JS engine not save the source
   3224  // code in memory. When the JS engine is asked to provide the source for a
   3225  // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
   3226  ///
   3227  // Note we do have to retain the source code in memory for scripts compiled in
   3228  // isRunOnce mode and compiled function bodies (from
   3229  // JS::CompileFunction). In practice, this means content scripts and event
   3230  // handlers.
   3231  mozilla::UniquePtr<XPCJSSourceHook> hook(new XPCJSSourceHook);
   3232  js::SetSourceHook(cx, std::move(hook));
   3233 
   3234  // Register memory reporters and distinguished amount functions.
   3235  RegisterStrongMemoryReporter(new JSMainRuntimeRealmsReporter());
   3236  RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
   3237  RegisterJSMainRuntimeGCHeapDistinguishedAmount(
   3238      JSMainRuntimeGCHeapDistinguishedAmount);
   3239  RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(
   3240      JSMainRuntimeTemporaryPeakDistinguishedAmount);
   3241  RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(
   3242      JSMainRuntimeCompartmentsSystemDistinguishedAmount);
   3243  RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(
   3244      JSMainRuntimeCompartmentsUserDistinguishedAmount);
   3245  RegisterJSMainRuntimeRealmsSystemDistinguishedAmount(
   3246      JSMainRuntimeRealmsSystemDistinguishedAmount);
   3247  RegisterJSMainRuntimeRealmsUserDistinguishedAmount(
   3248      JSMainRuntimeRealmsUserDistinguishedAmount);
   3249  mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
   3250 
   3251  // Set the callback for reporting memory to ubi::Node.
   3252  JS::ubi::SetConstructUbiNodeForDOMObjectCallback(cx, &ConstructUbiNode);
   3253 
   3254  xpc_LocalizeRuntime(JS_GetRuntime(cx));
   3255 }
   3256 
   3257 bool XPCJSRuntime::InitializeStrings(JSContext* cx) {
   3258  // if it is our first context then we need to generate our string ids
   3259  if (mStrIDs[0].isVoid()) {
   3260    RootedString str(cx);
   3261    for (unsigned i = 0; i < XPCJSContext::IDX_TOTAL_COUNT; i++) {
   3262      str = JS_AtomizeAndPinString(cx, mStrings[i]);
   3263      if (!str) {
   3264        mStrIDs[0] = JS::PropertyKey::Void();
   3265        return false;
   3266      }
   3267      mStrIDs[i] = PropertyKey::fromPinnedString(str);
   3268    }
   3269 
   3270    if (!mozilla::dom::DefineStaticJSVals(cx)) {
   3271      return false;
   3272    }
   3273  }
   3274 
   3275  return true;
   3276 }
   3277 
   3278 bool XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const JSClass* clasp,
   3279                                         char (&name)[72]) const {
   3280  if (clasp != &XPC_WN_Proto_JSClass) {
   3281    return false;
   3282  }
   3283 
   3284  XPCWrappedNativeProto* p = XPCWrappedNativeProto::Get(obj);
   3285  // Nothing here can GC. The analysis would otherwise think that ~nsCOMPtr
   3286  // could GC, but that's only possible if nsIXPCScriptable::GetJSClass()
   3287  // somehow released a reference to the nsIXPCScriptable, which isn't going to
   3288  // happen.
   3289  JS::AutoSuppressGCAnalysis nogc;
   3290  nsCOMPtr<nsIXPCScriptable> scr = p->GetScriptable();
   3291  if (!scr) {
   3292    return false;
   3293  }
   3294 
   3295  SprintfLiteral(name, "JS Object (%s - %s)", clasp->name,
   3296                 scr->GetJSClass()->name);
   3297  return true;
   3298 }
   3299 
   3300 bool XPCJSRuntime::NoteCustomGCThingXPCOMChildren(
   3301    const JSClass* clasp, JSObject* obj,
   3302    nsCycleCollectionTraversalCallback& cb) const {
   3303  if (clasp != &XPC_WN_Tearoff_JSClass) {
   3304    return false;
   3305  }
   3306 
   3307  // A tearoff holds a strong reference to its native object
   3308  // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
   3309  // will be held alive through tearoff's XPC_WN_TEAROFF_FLAT_OBJECT_SLOT,
   3310  // which points to the XPCWrappedNative's mFlatJSObject.
   3311  XPCWrappedNativeTearOff* to = XPCWrappedNativeTearOff::Get(obj);
   3312  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
   3313      cb, "XPCWrappedNativeTearOff::Get(obj)->mNative");
   3314  cb.NoteXPCOMChild(to->GetNative());
   3315  return true;
   3316 }
   3317 
   3318 /***************************************************************************/
   3319 
   3320 void XPCJSRuntime::DebugDump(int16_t depth) {
   3321 #ifdef DEBUG
   3322  depth--;
   3323  XPC_LOG_ALWAYS(("XPCJSRuntime @ %p", this));
   3324  XPC_LOG_INDENT();
   3325 
   3326  // iterate wrappers...
   3327  XPC_LOG_ALWAYS(("mWrappedJSMap @ %p with %d wrappers(s)", mWrappedJSMap.get(),
   3328                  mWrappedJSMap->Count()));
   3329  if (depth && mWrappedJSMap->Count()) {
   3330    XPC_LOG_INDENT();
   3331    mWrappedJSMap->Dump(depth);
   3332    XPC_LOG_OUTDENT();
   3333  }
   3334 
   3335  XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %p with %d interface(s)",
   3336                  mIID2NativeInterfaceMap.get(),
   3337                  mIID2NativeInterfaceMap->Count()));
   3338 
   3339  XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %p with %d sets(s)",
   3340                  mClassInfo2NativeSetMap.get(),
   3341                  mClassInfo2NativeSetMap->Count()));
   3342 
   3343  XPC_LOG_ALWAYS(("mNativeSetMap @ %p with %d sets(s)", mNativeSetMap.get(),
   3344                  mNativeSetMap->Count()));
   3345 
   3346  // iterate sets...
   3347  if (depth && mNativeSetMap->Count()) {
   3348    XPC_LOG_INDENT();
   3349    for (auto i = mNativeSetMap->Iter(); !i.done(); i.next()) {
   3350      i.get()->DebugDump(depth);
   3351    }
   3352    XPC_LOG_OUTDENT();
   3353  }
   3354 
   3355  XPC_LOG_OUTDENT();
   3356 #endif
   3357 }
   3358 
   3359 /***************************************************************************/
   3360 
   3361 void XPCJSRuntime::AddGCCallback(xpcGCCallback cb) {
   3362  MOZ_ASSERT(cb, "null callback");
   3363  extraGCCallbacks.AppendElement(cb);
   3364 }
   3365 
   3366 void XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb) {
   3367  MOZ_ASSERT(cb, "null callback");
   3368  bool found = extraGCCallbacks.RemoveElement(cb);
   3369  if (!found) {
   3370    NS_ERROR("Removing a callback which was never added.");
   3371  }
   3372 }
   3373 
   3374 JSObject* XPCJSRuntime::GetUAWidgetScope(JSContext* cx,
   3375                                         nsIPrincipal* principal) {
   3376  MOZ_ASSERT(!principal->IsSystemPrincipal(), "Running UA Widget in chrome");
   3377 
   3378  RootedObject scope(cx);
   3379  do {
   3380    RefPtr<BasePrincipal> key = BasePrincipal::Cast(principal);
   3381    if (Principal2JSObjectMap::Ptr p = mUAWidgetScopeMap.lookup(key)) {
   3382      scope = p->value();
   3383      break;  // Need ~RefPtr to run, and potentially GC, before returning.
   3384    }
   3385 
   3386    SandboxOptions options;
   3387    options.sandboxName.AssignLiteral("UA Widget Scope");
   3388    options.wantXrays = false;
   3389    options.wantComponents = false;
   3390    options.isUAWidgetScope = true;
   3391 
   3392    // Use an ExpandedPrincipal to create asymmetric security.
   3393    MOZ_ASSERT(!nsContentUtils::IsExpandedPrincipal(principal));
   3394    nsTArray<nsCOMPtr<nsIPrincipal>> principalAsArray{principal};
   3395    RefPtr<ExpandedPrincipal> ep = ExpandedPrincipal::Create(
   3396        principalAsArray, principal->OriginAttributesRef());
   3397 
   3398    // Create the sandbox.
   3399    RootedValue v(cx);
   3400    nsresult rv = CreateSandboxObject(
   3401        cx, &v, static_cast<nsIExpandedPrincipal*>(ep), options);
   3402    NS_ENSURE_SUCCESS(rv, nullptr);
   3403    scope = &v.toObject();
   3404 
   3405    JSObject* unwrapped = js::UncheckedUnwrap(scope);
   3406    MOZ_ASSERT(xpc::IsInUAWidgetScope(unwrapped));
   3407 
   3408    MOZ_ALWAYS_TRUE(mUAWidgetScopeMap.putNew(key, unwrapped));
   3409  } while (false);
   3410 
   3411  return scope;
   3412 }
   3413 
   3414 JSObject* XPCJSRuntime::UnprivilegedJunkScope(const mozilla::fallible_t&) {
   3415  if (!mUnprivilegedJunkScope) {
   3416    dom::AutoJSAPI jsapi;
   3417    jsapi.Init();
   3418    JSContext* cx = jsapi.cx();
   3419 
   3420    SandboxOptions options;
   3421    options.sandboxName.AssignLiteral("XPConnect Junk Compartment");
   3422    options.invisibleToDebugger = true;
   3423 
   3424    RootedValue sandbox(cx);
   3425    nsresult rv = CreateSandboxObject(cx, &sandbox, nullptr, options);
   3426    NS_ENSURE_SUCCESS(rv, nullptr);
   3427 
   3428    mUnprivilegedJunkScope =
   3429        SandboxPrivate::GetPrivate(sandbox.toObjectOrNull());
   3430  }
   3431  MOZ_ASSERT(mUnprivilegedJunkScope->GetWrapper(),
   3432             "Wrapper should have same lifetime as weak reference");
   3433  return mUnprivilegedJunkScope->GetWrapper();
   3434 }
   3435 
   3436 JSObject* XPCJSRuntime::UnprivilegedJunkScope() {
   3437  JSObject* scope = UnprivilegedJunkScope(fallible);
   3438  MOZ_RELEASE_ASSERT(scope);
   3439  return scope;
   3440 }
   3441 
   3442 bool XPCJSRuntime::IsUnprivilegedJunkScope(JSObject* obj) {
   3443  return mUnprivilegedJunkScope && obj == mUnprivilegedJunkScope->GetWrapper();
   3444 }
   3445 
   3446 void XPCJSRuntime::DeleteSingletonScopes() {
   3447  // We're pretty late in shutdown, so we call ReleaseWrapper on the scopes.
   3448  // This way the GC can collect them immediately, and we don't rely on the CC
   3449  // to clean up.
   3450  if (RefPtr<SandboxPrivate> sandbox = mUnprivilegedJunkScope.get()) {
   3451    sandbox->ReleaseWrapper(sandbox);
   3452    mUnprivilegedJunkScope = nullptr;
   3453  }
   3454 }
   3455 
   3456 uint32_t GetAndClampCPUCount() {
   3457  // See HelperThreads.cpp for why we want between 2-8 threads
   3458  int32_t proc = GetNumberOfProcessors();
   3459  if (proc < 2) {
   3460    return 2;
   3461  }
   3462  return std::min(proc, 8);
   3463 }