LCOV - code coverage report
Current view: top level - js/xpconnect/src - XPCJSRuntime.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 322 1232 26.1 %
Date: 2017-07-14 16:53:18 Functions: 47 128 36.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
       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/MemoryReporting.h"
      10             : #include "mozilla/UniquePtr.h"
      11             : 
      12             : #include "xpcprivate.h"
      13             : #include "xpcpublic.h"
      14             : #include "XPCWrapper.h"
      15             : #include "XPCJSMemoryReporter.h"
      16             : #include "WrapperFactory.h"
      17             : #include "mozJSComponentLoader.h"
      18             : #include "nsAutoPtr.h"
      19             : #include "nsNetUtil.h"
      20             : 
      21             : #include "nsIMemoryInfoDumper.h"
      22             : #include "nsIMemoryReporter.h"
      23             : #include "nsIObserverService.h"
      24             : #include "nsIDebug2.h"
      25             : #include "nsIDocShell.h"
      26             : #include "nsIRunnable.h"
      27             : #include "amIAddonManager.h"
      28             : #include "nsPIDOMWindow.h"
      29             : #include "nsPrintfCString.h"
      30             : #include "mozilla/Preferences.h"
      31             : #include "mozilla/Telemetry.h"
      32             : #include "mozilla/Services.h"
      33             : #include "mozilla/dom/ScriptLoader.h"
      34             : #include "mozilla/dom/ScriptSettings.h"
      35             : 
      36             : #include "nsContentUtils.h"
      37             : #include "nsCCUncollectableMarker.h"
      38             : #include "nsCycleCollectionNoteRootCallback.h"
      39             : #include "nsCycleCollector.h"
      40             : #include "jsapi.h"
      41             : #include "jsprf.h"
      42             : #include "js/MemoryMetrics.h"
      43             : #include "mozilla/dom/GeneratedAtomList.h"
      44             : #include "mozilla/dom/BindingUtils.h"
      45             : #include "mozilla/dom/Element.h"
      46             : #include "mozilla/dom/WindowBinding.h"
      47             : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
      48             : #include "mozilla/Atomics.h"
      49             : #include "mozilla/Attributes.h"
      50             : #include "mozilla/ProcessHangMonitor.h"
      51             : #include "mozilla/Sprintf.h"
      52             : #include "mozilla/UniquePtrExtensions.h"
      53             : #include "mozilla/Unused.h"
      54             : #include "AccessCheck.h"
      55             : #include "nsGlobalWindow.h"
      56             : #include "nsAboutProtocolUtils.h"
      57             : 
      58             : #include "GeckoProfiler.h"
      59             : #include "nsIInputStream.h"
      60             : #include "nsIXULRuntime.h"
      61             : #include "nsJSPrincipals.h"
      62             : 
      63             : #ifdef MOZ_CRASHREPORTER
      64             : #include "nsExceptionHandler.h"
      65             : #endif
      66             : 
      67             : #ifdef XP_WIN
      68             : #include <windows.h>
      69             : #endif
      70             : 
      71             : using namespace mozilla;
      72             : using namespace xpc;
      73             : using namespace JS;
      74             : using mozilla::dom::PerThreadAtomCache;
      75             : using mozilla::dom::AutoEntryScript;
      76             : 
      77             : /***************************************************************************/
      78             : 
      79             : const char* const XPCJSRuntime::mStrings[] = {
      80             :     "constructor",          // IDX_CONSTRUCTOR
      81             :     "toString",             // IDX_TO_STRING
      82             :     "toSource",             // IDX_TO_SOURCE
      83             :     "lastResult",           // IDX_LAST_RESULT
      84             :     "returnCode",           // IDX_RETURN_CODE
      85             :     "value",                // IDX_VALUE
      86             :     "QueryInterface",       // IDX_QUERY_INTERFACE
      87             :     "Components",           // IDX_COMPONENTS
      88             :     "wrappedJSObject",      // IDX_WRAPPED_JSOBJECT
      89             :     "Object",               // IDX_OBJECT
      90             :     "Function",             // IDX_FUNCTION
      91             :     "prototype",            // IDX_PROTOTYPE
      92             :     "createInstance",       // IDX_CREATE_INSTANCE
      93             :     "item",                 // IDX_ITEM
      94             :     "__proto__",            // IDX_PROTO
      95             :     "__iterator__",         // IDX_ITERATOR
      96             :     "__exposedProps__",     // IDX_EXPOSEDPROPS
      97             :     "eval",                 // IDX_EVAL
      98             :     "controllers",          // IDX_CONTROLLERS
      99             :     "Controllers",          // IDX_CONTROLLERS_CLASS
     100             :     "realFrameElement",     // IDX_REALFRAMEELEMENT
     101             :     "length",               // IDX_LENGTH
     102             :     "name",                 // IDX_NAME
     103             :     "undefined",            // IDX_UNDEFINED
     104             :     "",                     // IDX_EMPTYSTRING
     105             :     "fileName",             // IDX_FILENAME
     106             :     "lineNumber",           // IDX_LINENUMBER
     107             :     "columnNumber",         // IDX_COLUMNNUMBER
     108             :     "stack",                // IDX_STACK
     109             :     "message",              // IDX_MESSAGE
     110             :     "lastIndex"             // IDX_LASTINDEX
     111             : };
     112             : 
     113             : /***************************************************************************/
     114             : 
     115             : // *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
     116             : // *All* NativeSets are referenced from mNativeSetMap.
     117             : // So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
     118             : // In mNativeSetMap we clear the references to the unmarked *and* delete them.
     119             : 
     120           0 : class AsyncFreeSnowWhite : public Runnable
     121             : {
     122             : public:
     123           1 :   NS_IMETHOD Run() override
     124             :   {
     125           1 :       TimeStamp start = TimeStamp::Now();
     126           1 :       bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
     127           1 :       Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
     128           2 :                             uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
     129           1 :       if (hadSnowWhiteObjects && !mContinuation) {
     130           1 :           mContinuation = true;
     131           2 :           if (NS_FAILED(Dispatch())) {
     132           0 :               mActive = false;
     133             :           }
     134             :       } else {
     135           0 :           mActive = false;
     136             :       }
     137           1 :       return NS_OK;
     138             :   }
     139             : 
     140           2 :   nsresult Dispatch()
     141             :   {
     142           4 :       nsCOMPtr<nsIRunnable> self(this);
     143           4 :       return NS_IdleDispatchToCurrentThread(self.forget(), 1000);
     144             :   }
     145             : 
     146           3 :   void Start(bool aContinuation = false, bool aPurge = false)
     147             :   {
     148           3 :       if (mContinuation) {
     149           0 :           mContinuation = aContinuation;
     150             :       }
     151           3 :       mPurge = aPurge;
     152           3 :       if (!mActive && NS_SUCCEEDED(Dispatch())) {
     153           1 :           mActive = true;
     154             :       }
     155           3 :   }
     156             : 
     157           3 :   AsyncFreeSnowWhite()
     158           3 :     : Runnable("AsyncFreeSnowWhite")
     159             :     , mContinuation(false)
     160             :     , mActive(false)
     161           3 :     , mPurge(false) {}
     162             : 
     163             : public:
     164             :   bool mContinuation;
     165             :   bool mActive;
     166             :   bool mPurge;
     167             : };
     168             : 
     169             : namespace xpc {
     170             : 
     171         293 : CompartmentPrivate::CompartmentPrivate(JSCompartment* c)
     172             :     : wantXrays(false)
     173             :     , allowWaivers(true)
     174             :     , writeToGlobalPrototype(false)
     175             :     , skipWriteToGlobalPrototype(false)
     176             :     , isWebExtensionContentScript(false)
     177             :     , waiveInterposition(false)
     178             :     , allowCPOWs(false)
     179             :     , universalXPConnectEnabled(false)
     180             :     , forcePermissiveCOWs(false)
     181             :     , wasNuked(false)
     182             :     , scriptability(c)
     183             :     , scope(nullptr)
     184         293 :     , mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH))
     185             : {
     186         293 :     MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
     187         293 :     mozilla::PodArrayZero(wrapperDenialWarnings);
     188         293 : }
     189             : 
     190           0 : CompartmentPrivate::~CompartmentPrivate()
     191             : {
     192           0 :     MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
     193           0 :     mWrappedJSMap->ShutdownMarker();
     194           0 :     delete mWrappedJSMap;
     195           0 : }
     196             : 
     197             : static bool
     198           0 : TryParseLocationURICandidate(const nsACString& uristr,
     199             :                              CompartmentPrivate::LocationHint aLocationHint,
     200             :                              nsIURI** aURI)
     201             : {
     202           0 :     static NS_NAMED_LITERAL_CSTRING(kGRE, "resource://gre/");
     203           0 :     static NS_NAMED_LITERAL_CSTRING(kToolkit, "chrome://global/");
     204           0 :     static NS_NAMED_LITERAL_CSTRING(kBrowser, "chrome://browser/");
     205             : 
     206           0 :     if (aLocationHint == CompartmentPrivate::LocationHintAddon) {
     207             :         // Blacklist some known locations which are clearly not add-on related.
     208           0 :         if (StringBeginsWith(uristr, kGRE) ||
     209           0 :             StringBeginsWith(uristr, kToolkit) ||
     210           0 :             StringBeginsWith(uristr, kBrowser))
     211           0 :             return false;
     212             : 
     213             :         // -- GROSS HACK ALERT --
     214             :         // The Yandex Elements 8.10.2 extension implements its own "xb://" URL
     215             :         // scheme. If we call NS_NewURI() on an "xb://..." URL, we'll end up
     216             :         // calling into the extension's own JS-implemented nsIProtocolHandler
     217             :         // object, which we can't allow while we're iterating over the JS heap.
     218             :         // So just skip any such URL.
     219             :         // -- GROSS HACK ALERT --
     220           0 :         if (StringBeginsWith(uristr, NS_LITERAL_CSTRING("xb")))
     221           0 :             return false;
     222             :     }
     223             : 
     224           0 :     nsCOMPtr<nsIURI> uri;
     225           0 :     if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr)))
     226           0 :         return false;
     227             : 
     228           0 :     nsAutoCString scheme;
     229           0 :     if (NS_FAILED(uri->GetScheme(scheme)))
     230           0 :         return false;
     231             : 
     232             :     // Cannot really map data: and blob:.
     233             :     // Also, data: URIs are pretty memory hungry, which is kinda bad
     234             :     // for memory reporter use.
     235           0 :     if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob"))
     236           0 :         return false;
     237             : 
     238           0 :     uri.forget(aURI);
     239           0 :     return true;
     240             : }
     241             : 
     242           0 : bool CompartmentPrivate::TryParseLocationURI(CompartmentPrivate::LocationHint aLocationHint,
     243             :                                              nsIURI** aURI)
     244             : {
     245           0 :     if (!aURI)
     246           0 :         return false;
     247             : 
     248             :     // Need to parse the URI.
     249           0 :     if (location.IsEmpty())
     250           0 :         return false;
     251             : 
     252             :     // Handle Sandbox location strings.
     253             :     // A sandbox string looks like this:
     254             :     // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
     255             :     // where <sandboxName> is user-provided via Cu.Sandbox()
     256             :     // and <js-stack-frame-filename> and <lineno> is the stack frame location
     257             :     // from where Cu.Sandbox was called.
     258             :     // <js-stack-frame-filename> furthermore is "free form", often using a
     259             :     // "uri -> uri -> ..." chain. The following code will and must handle this
     260             :     // common case.
     261             :     // It should be noted that other parts of the code may already rely on the
     262             :     // "format" of these strings, such as the add-on SDK.
     263             : 
     264           0 :     static const nsDependentCString from("(from: ");
     265           0 :     static const nsDependentCString arrow(" -> ");
     266           0 :     static const size_t fromLength = from.Length();
     267           0 :     static const size_t arrowLength = arrow.Length();
     268             : 
     269             :     // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
     270           0 :     int32_t idx = location.Find(from);
     271           0 :     if (idx < 0)
     272           0 :         return TryParseLocationURICandidate(location, aLocationHint, aURI);
     273             : 
     274             : 
     275             :     // When parsing we're looking for the right-most URI. This URI may be in
     276             :     // <sandboxName>, so we try this first.
     277           0 :     if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint,
     278             :                                      aURI))
     279           0 :         return true;
     280             : 
     281             :     // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and
     282             :     // the chain that is potentially contained within and grab the rightmost
     283             :     // item that is actually a URI.
     284             : 
     285             :     // First, hack off the :<lineno>) part as well
     286           0 :     int32_t ridx = location.RFind(NS_LITERAL_CSTRING(":"));
     287           0 :     nsAutoCString chain(Substring(location, idx + fromLength,
     288           0 :                                   ridx - idx - fromLength));
     289             : 
     290             :     // Loop over the "->" chain. This loop also works for non-chains, or more
     291             :     // correctly chains with only one item.
     292             :     for (;;) {
     293           0 :         idx = chain.RFind(arrow);
     294           0 :         if (idx < 0) {
     295             :             // This is the last chain item. Try to parse what is left.
     296           0 :             return TryParseLocationURICandidate(chain, aLocationHint, aURI);
     297             :         }
     298             : 
     299             :         // Try to parse current chain item
     300           0 :         if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength),
     301             :                                          aLocationHint, aURI))
     302           0 :             return true;
     303             : 
     304             :         // Current chain item couldn't be parsed.
     305             :         // Strip current item and continue.
     306           0 :         chain = Substring(chain, 0, idx);
     307             :     }
     308             : 
     309             :     MOZ_CRASH("Chain parser loop does not terminate");
     310             : }
     311             : 
     312             : static bool
     313         293 : PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal)
     314             : {
     315             :     // System principal gets a free pass.
     316         293 :     if (nsXPConnect::SecurityManager()->IsSystemPrincipal(aPrincipal))
     317         282 :         return true;
     318             : 
     319             :     // ExpandedPrincipal gets a free pass.
     320          22 :     nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
     321          11 :     if (ep)
     322           0 :         return true;
     323             : 
     324             :     // Check whether our URI is an "about:" URI that allows scripts.  If it is,
     325             :     // we need to allow JS to run.
     326          22 :     nsCOMPtr<nsIURI> principalURI;
     327          11 :     aPrincipal->GetURI(getter_AddRefs(principalURI));
     328          11 :     MOZ_ASSERT(principalURI);
     329             : 
     330             :     // WebExtension principals gets a free pass.
     331          22 :     nsString addonId;
     332          11 :     aPrincipal->GetAddonId(addonId);
     333          11 :     bool isWebExtension = !addonId.IsEmpty();
     334          11 :     if (isWebExtension) {
     335           0 :         return true;
     336             :     }
     337             : 
     338             :     bool isAbout;
     339          11 :     nsresult rv = principalURI->SchemeIs("about", &isAbout);
     340          11 :     if (NS_SUCCEEDED(rv) && isAbout) {
     341           0 :         nsCOMPtr<nsIAboutModule> module;
     342           0 :         rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
     343           0 :         if (NS_SUCCEEDED(rv)) {
     344             :             uint32_t flags;
     345           0 :             rv = module->GetURIFlags(principalURI, &flags);
     346           0 :             if (NS_SUCCEEDED(rv) &&
     347           0 :                 (flags & nsIAboutModule::ALLOW_SCRIPT)) {
     348           0 :                 return true;
     349             :             }
     350             :         }
     351             :     }
     352             : 
     353          11 :     return false;
     354             : }
     355             : 
     356         293 : Scriptability::Scriptability(JSCompartment* c) : mScriptBlocks(0)
     357             :                                                , mDocShellAllowsScript(true)
     358         293 :                                                , mScriptBlockedByPolicy(false)
     359             : {
     360         293 :     nsIPrincipal* prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
     361         293 :     mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
     362             : 
     363             :     // If we're not immune, we should have a real principal with a codebase URI.
     364             :     // Check the URI against the new-style domain policy.
     365         293 :     if (!mImmuneToScriptPolicy) {
     366          22 :         nsCOMPtr<nsIURI> codebase;
     367          11 :         nsresult rv = prin->GetURI(getter_AddRefs(codebase));
     368             :         bool policyAllows;
     369          22 :         if (NS_SUCCEEDED(rv) && codebase &&
     370          11 :             NS_SUCCEEDED(nsXPConnect::SecurityManager()->PolicyAllowsScript(codebase, &policyAllows)))
     371             :         {
     372          11 :             mScriptBlockedByPolicy = !policyAllows;
     373             :         } else {
     374             :             // Something went wrong - be safe and block script.
     375           0 :             mScriptBlockedByPolicy = true;
     376             :         }
     377             :     }
     378         293 : }
     379             : 
     380             : bool
     381         516 : Scriptability::Allowed()
     382             : {
     383        1032 :     return mDocShellAllowsScript && !mScriptBlockedByPolicy &&
     384        1032 :            mScriptBlocks == 0;
     385             : }
     386             : 
     387             : bool
     388           0 : Scriptability::IsImmuneToScriptPolicy()
     389             : {
     390           0 :     return mImmuneToScriptPolicy;
     391             : }
     392             : 
     393             : void
     394           0 : Scriptability::Block()
     395             : {
     396           0 :     ++mScriptBlocks;
     397           0 : }
     398             : 
     399             : void
     400           0 : Scriptability::Unblock()
     401             : {
     402           0 :     MOZ_ASSERT(mScriptBlocks > 0);
     403           0 :     --mScriptBlocks;
     404           0 : }
     405             : 
     406             : void
     407          11 : Scriptability::SetDocShellAllowsScript(bool aAllowed)
     408             : {
     409          11 :     mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy;
     410          11 : }
     411             : 
     412             : /* static */
     413             : Scriptability&
     414         527 : Scriptability::Get(JSObject* aScope)
     415             : {
     416         527 :     return CompartmentPrivate::Get(aScope)->scriptability;
     417             : }
     418             : 
     419             : bool
     420        3499 : IsContentXBLScope(JSCompartment* compartment)
     421             : {
     422             :     // We always eagerly create compartment privates for XBL scopes.
     423        3499 :     CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
     424        3499 :     if (!priv || !priv->scope)
     425           0 :         return false;
     426        3499 :     return priv->scope->IsContentXBLScope();
     427             : }
     428             : 
     429             : bool
     430        3456 : IsInContentXBLScope(JSObject* obj)
     431             : {
     432        3456 :     return IsContentXBLScope(js::GetObjectCompartment(obj));
     433             : }
     434             : 
     435             : bool
     436        3716 : IsInAddonScope(JSObject* obj)
     437             : {
     438        3716 :     return ObjectScope(obj)->IsAddonScope();
     439             : }
     440             : 
     441             : bool
     442          15 : IsUniversalXPConnectEnabled(JSCompartment* compartment)
     443             : {
     444          15 :     CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
     445          15 :     if (!priv)
     446           0 :         return false;
     447          15 :     return priv->universalXPConnectEnabled;
     448             : }
     449             : 
     450             : bool
     451          57 : IsUniversalXPConnectEnabled(JSContext* cx)
     452             : {
     453          57 :     JSCompartment* compartment = js::GetContextCompartment(cx);
     454          57 :     if (!compartment)
     455          45 :         return false;
     456          12 :     return IsUniversalXPConnectEnabled(compartment);
     457             : }
     458             : 
     459             : bool
     460           3 : EnableUniversalXPConnect(JSContext* cx)
     461             : {
     462           3 :     JSCompartment* compartment = js::GetContextCompartment(cx);
     463           3 :     if (!compartment)
     464           0 :         return true;
     465             :     // Never set universalXPConnectEnabled on a chrome compartment - it confuses
     466             :     // the security wrapping code.
     467           3 :     if (AccessCheck::isChrome(compartment))
     468           0 :         return true;
     469           3 :     CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
     470           3 :     if (!priv)
     471           0 :         return true;
     472           3 :     if (priv->universalXPConnectEnabled)
     473           2 :         return true;
     474           1 :     priv->universalXPConnectEnabled = true;
     475             : 
     476             :     // Recompute all the cross-compartment wrappers leaving the newly-privileged
     477             :     // compartment.
     478           2 :     bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
     479           3 :                                     js::AllCompartments());
     480           1 :     NS_ENSURE_TRUE(ok, false);
     481             : 
     482             :     // The Components object normally isn't defined for unprivileged web content,
     483             :     // but we define it when UniversalXPConnect is enabled to support legacy
     484             :     // tests.
     485           1 :     XPCWrappedNativeScope* scope = priv->scope;
     486           1 :     if (!scope)
     487           0 :         return true;
     488           1 :     scope->ForcePrivilegedComponents();
     489           1 :     return scope->AttachComponentsObject(cx);
     490             : }
     491             : 
     492             : JSObject*
     493         284 : UnprivilegedJunkScope()
     494             : {
     495         284 :     return XPCJSRuntime::Get()->UnprivilegedJunkScope();
     496             : }
     497             : 
     498             : JSObject*
     499          66 : PrivilegedJunkScope()
     500             : {
     501          66 :     return XPCJSRuntime::Get()->PrivilegedJunkScope();
     502             : }
     503             : 
     504             : JSObject*
     505        3774 : CompilationScope()
     506             : {
     507        3774 :     return XPCJSRuntime::Get()->CompilationScope();
     508             : }
     509             : 
     510             : nsGlobalWindow*
     511        1453 : WindowOrNull(JSObject* aObj)
     512             : {
     513        1453 :     MOZ_ASSERT(aObj);
     514        1453 :     MOZ_ASSERT(!js::IsWrapper(aObj));
     515             : 
     516        1453 :     nsGlobalWindow* win = nullptr;
     517        1453 :     UNWRAP_NON_WRAPPER_OBJECT(Window, aObj, win);
     518        1453 :     return win;
     519             : }
     520             : 
     521             : nsGlobalWindow*
     522        1011 : WindowGlobalOrNull(JSObject* aObj)
     523             : {
     524        1011 :     MOZ_ASSERT(aObj);
     525        1011 :     JSObject* glob = js::GetGlobalForObjectCrossCompartment(aObj);
     526             : 
     527        1011 :     return WindowOrNull(glob);
     528             : }
     529             : 
     530             : nsGlobalWindow*
     531           2 : AddonWindowOrNull(JSObject* aObj)
     532             : {
     533           2 :     if (!IsInAddonScope(aObj))
     534           2 :         return nullptr;
     535             : 
     536           0 :     JSObject* global = js::GetGlobalForObjectCrossCompartment(aObj);
     537           0 :     JSObject* proto = js::GetPrototypeNoProxy(global);
     538             : 
     539             :     // Addons could theoretically change the prototype of the addon scope, but
     540             :     // we pretty much just want to crash if that happens so that we find out
     541             :     // about it and get them to change their code.
     542           0 :     MOZ_RELEASE_ASSERT(js::IsCrossCompartmentWrapper(proto) ||
     543             :                        xpc::IsSandboxPrototypeProxy(proto));
     544           0 :     JSObject* mainGlobal = js::UncheckedUnwrap(proto, /* stopAtWindowProxy = */ false);
     545           0 :     MOZ_RELEASE_ASSERT(JS_IsGlobalObject(mainGlobal));
     546             : 
     547           0 :     return WindowOrNull(mainGlobal);
     548             : }
     549             : 
     550             : nsGlobalWindow*
     551           0 : CurrentWindowOrNull(JSContext* cx)
     552             : {
     553           0 :     JSObject* glob = JS::CurrentGlobalOrNull(cx);
     554           0 :     return glob ? WindowOrNull(glob) : nullptr;
     555             : }
     556             : 
     557             : // Nukes all wrappers into or out of the given compartment, and prevents new
     558             : // wrappers from being created. Additionally marks the compartment as
     559             : // unscriptable after wrappers have been nuked.
     560             : //
     561             : // Note: This should *only* be called for browser or extension compartments.
     562             : // Wrappers between web compartments must never be cut in web-observable
     563             : // ways.
     564             : void
     565           0 : NukeAllWrappersForCompartment(JSContext* cx, JSCompartment* compartment,
     566             :                               js::NukeReferencesToWindow nukeReferencesToWindow)
     567             : {
     568             :     // First, nuke all wrappers into or out of the target compartment. Once
     569             :     // the compartment is marked as nuked, WrapperFactory will refuse to
     570             :     // create new live wrappers for it, in either direction. This means that
     571             :     // we need to be sure that we don't have any existing cross-compartment
     572             :     // wrappers which may be replaced with dead wrappers during unrelated
     573             :     // wrapper recomputation *before* we set that bit.
     574           0 :     js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(), compartment,
     575             :                                      nukeReferencesToWindow,
     576           0 :                                      js::NukeAllReferences);
     577             : 
     578             :     // At this point, we should cross-compartment wrappers for the nuked
     579             :     // compartment. Set the wasNuked bit so WrapperFactory will return a
     580             :     // DeadObjectProxy when asked to create a new wrapper for it, and mark as
     581             :     // unscriptable.
     582           0 :     auto compartmentPrivate = xpc::CompartmentPrivate::Get(compartment);
     583           0 :     compartmentPrivate->wasNuked = true;
     584           0 :     compartmentPrivate->scriptability.Block();
     585           0 : }
     586             : 
     587             : } // namespace xpc
     588             : 
     589             : static void
     590           0 : CompartmentDestroyedCallback(JSFreeOp* fop, JSCompartment* compartment)
     591             : {
     592             :     // NB - This callback may be called in JS_DestroyContext, which happens
     593             :     // after the XPCJSRuntime has been torn down.
     594             : 
     595             :     // Get the current compartment private into an AutoPtr (which will do the
     596             :     // cleanup for us), and null out the private (which may already be null).
     597           0 :     nsAutoPtr<CompartmentPrivate> priv(CompartmentPrivate::Get(compartment));
     598           0 :     JS_SetCompartmentPrivate(compartment, nullptr);
     599           0 : }
     600             : 
     601             : static size_t
     602           0 : CompartmentSizeOfIncludingThisCallback(MallocSizeOf mallocSizeOf, JSCompartment* compartment)
     603             : {
     604           0 :     CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
     605           0 :     return priv ? priv->SizeOfIncludingThis(mallocSizeOf) : 0;
     606             : }
     607             : 
     608             : /*
     609             :  * Return true if there exists a non-system inner window which is a current
     610             :  * inner window and whose reflector is gray.  We don't merge system
     611             :  * compartments, so we don't use them to trigger merging CCs.
     612             :  */
     613           0 : bool XPCJSRuntime::UsefulToMergeZones() const
     614             : {
     615           0 :     MOZ_ASSERT(NS_IsMainThread());
     616             : 
     617             :     // Turns out, actually making this return true often enough makes Windows
     618             :     // mochitest-gl OOM a lot.  Need to figure out what's going on there; see
     619             :     // bug 1277036.
     620             : 
     621           0 :     return false;
     622             : }
     623             : 
     624           1 : void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc)
     625             : {
     626           2 :     for (CycleCollectedJSContext* ccx : Contexts()) {
     627           1 :         auto* cx = static_cast<const XPCJSContext*>(ccx);
     628           1 :         if (AutoMarkingPtr* roots = cx->mAutoRoots)
     629           0 :             roots->TraceJSAll(trc);
     630             :     }
     631             : 
     632           1 :     JSContext* cx = XPCJSContext::Get()->Context();
     633           1 :     dom::TraceBlackJS(trc, JS_GetGCParameter(cx, JSGC_NUMBER),
     634           2 :                       nsXPConnect::XPConnect()->IsShuttingDown());
     635           1 : }
     636             : 
     637           1 : void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer* trc)
     638             : {
     639           1 :     XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc);
     640             : 
     641           3 :     for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot())
     642           2 :         static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
     643             : 
     644         480 :     for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot())
     645         479 :         static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
     646           1 : }
     647             : 
     648             : void
     649           0 : XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb)
     650             : {
     651           0 :     XPCWrappedNativeScope::SuspectAllWrappers(cb);
     652             : 
     653           0 :     for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot()) {
     654           0 :         XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
     655           0 :         if (nsCCUncollectableMarker::InGeneration(cb,
     656             :                                                   v->CCGeneration())) {
     657           0 :            JS::Value val = v->GetJSValPreserveColor();
     658           0 :            if (val.isObject() && !JS::ObjectIsMarkedGray(&val.toObject()))
     659           0 :                continue;
     660             :         }
     661           0 :         cb.NoteXPCOMRoot(v);
     662             :     }
     663             : 
     664           0 :     for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
     665           0 :         cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)));
     666             :     }
     667           0 : }
     668             : 
     669             : void
     670           0 : XPCJSRuntime::UnmarkSkippableJSHolders()
     671             : {
     672           0 :     CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
     673           0 : }
     674             : 
     675             : void
     676           0 : XPCJSRuntime::PrepareForForgetSkippable()
     677             : {
     678           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     679           0 :     if (obs) {
     680           0 :         obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr);
     681             :     }
     682           0 : }
     683             : 
     684             : void
     685           0 : XPCJSRuntime::BeginCycleCollectionCallback()
     686             : {
     687           0 :     nsJSContext::BeginCycleCollectionCallback();
     688             : 
     689           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     690           0 :     if (obs) {
     691           0 :         obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
     692             :     }
     693           0 : }
     694             : 
     695             : void
     696           0 : XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults& aResults)
     697             : {
     698           0 :     nsJSContext::EndCycleCollectionCallback(aResults);
     699             : 
     700           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     701           0 :     if (obs) {
     702           0 :         obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
     703             :     }
     704           0 : }
     705             : 
     706             : void
     707           3 : XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation, bool aPurge)
     708             : {
     709           3 :     mAsyncSnowWhiteFreer->Start(aContinuation, aPurge);
     710           3 : }
     711             : 
     712             : void
     713           0 : xpc_UnmarkSkippableJSHolders()
     714             : {
     715           0 :     if (nsXPConnect::GetRuntimeInstance()) {
     716           0 :         nsXPConnect::GetRuntimeInstance()->UnmarkSkippableJSHolders();
     717             :     }
     718           0 : }
     719             : 
     720             : /* static */ void
     721           7 : XPCJSRuntime::GCSliceCallback(JSContext* cx,
     722             :                               JS::GCProgress progress,
     723             :                               const JS::GCDescription& desc)
     724             : {
     725           7 :     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
     726           7 :     if (!self)
     727           0 :         return;
     728             : 
     729             : #ifdef MOZ_CRASHREPORTER
     730           7 :     CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN);
     731             : #endif
     732             : 
     733           7 :     if (self->mPrevGCSliceCallback)
     734           7 :         (*self->mPrevGCSliceCallback)(cx, progress, desc);
     735             : }
     736             : 
     737             : /* static */ void
     738           0 : XPCJSRuntime::DoCycleCollectionCallback(JSContext* cx)
     739             : {
     740             :     // The GC has detected that a CC at this point would collect a tremendous
     741             :     // amount of garbage that is being revivified unnecessarily.
     742           0 :     NS_DispatchToCurrentThread(
     743           0 :       NS_NewRunnableFunction("XPCJSRuntime::DoCycleCollectionCallback",
     744           0 :                              []() { nsJSContext::CycleCollectNow(nullptr); }));
     745             : 
     746           0 :     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
     747           0 :     if (!self)
     748           0 :         return;
     749             : 
     750           0 :     if (self->mPrevDoCycleCollectionCallback)
     751           0 :         (*self->mPrevDoCycleCollectionCallback)(cx);
     752             : }
     753             : 
     754             : void
     755           1 : XPCJSRuntime::CustomGCCallback(JSGCStatus status)
     756             : {
     757           2 :     nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
     758           1 :     for (uint32_t i = 0; i < callbacks.Length(); ++i)
     759           0 :         callbacks[i](status);
     760           1 : }
     761             : 
     762             : /* static */ void
     763           0 : XPCJSRuntime::FinalizeCallback(JSFreeOp* fop,
     764             :                                JSFinalizeStatus status,
     765             :                                bool isZoneGC,
     766             :                                void* data)
     767             : {
     768           0 :     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
     769           0 :     if (!self)
     770           0 :         return;
     771             : 
     772           0 :     switch (status) {
     773             :         case JSFINALIZE_GROUP_PREPARE:
     774             :         {
     775           0 :             MOZ_ASSERT(!self->mDoingFinalization, "bad state");
     776             : 
     777           0 :             MOZ_ASSERT(!self->mGCIsRunning, "bad state");
     778           0 :             self->mGCIsRunning = true;
     779             : 
     780           0 :             self->mDoingFinalization = true;
     781             : 
     782           0 :             break;
     783             :         }
     784             :         case JSFINALIZE_GROUP_START:
     785             :         {
     786           0 :             MOZ_ASSERT(self->mDoingFinalization, "bad state");
     787             : 
     788           0 :             MOZ_ASSERT(self->mGCIsRunning, "bad state");
     789           0 :             self->mGCIsRunning = false;
     790             : 
     791           0 :             break;
     792             :         }
     793             :         case JSFINALIZE_GROUP_END:
     794             :         {
     795             :             // Sweep scopes needing cleanup
     796           0 :             XPCWrappedNativeScope::KillDyingScopes();
     797             : 
     798           0 :             MOZ_ASSERT(self->mDoingFinalization, "bad state");
     799           0 :             self->mDoingFinalization = false;
     800             : 
     801           0 :             break;
     802             :         }
     803             :         case JSFINALIZE_COLLECTION_END:
     804             :         {
     805           0 :             MOZ_ASSERT(!self->mGCIsRunning, "bad state");
     806           0 :             self->mGCIsRunning = true;
     807             : 
     808           0 :             for (CycleCollectedJSContext* ccx : self->Contexts()) {
     809           0 :                 auto* cx = static_cast<const XPCJSContext*>(ccx);
     810           0 :                 if (AutoMarkingPtr* roots = cx->mAutoRoots)
     811           0 :                     roots->MarkAfterJSFinalizeAll();
     812             : 
     813             :                 // Now we are going to recycle any unused WrappedNativeTearoffs.
     814             :                 // We do this by iterating all the live callcontexts
     815             :                 // and marking the tearoffs in use. And then we
     816             :                 // iterate over all the WrappedNative wrappers and sweep their
     817             :                 // tearoffs.
     818             :                 //
     819             :                 // This allows us to perhaps minimize the growth of the
     820             :                 // tearoffs. And also makes us not hold references to interfaces
     821             :                 // on our wrapped natives that we are not actually using.
     822             :                 //
     823             :                 // XXX We may decide to not do this on *every* gc cycle.
     824             : 
     825           0 :                 XPCCallContext* ccxp = cx->GetCallContext();
     826           0 :                 while (ccxp) {
     827             :                     // Deal with the strictness of callcontext that
     828             :                     // complains if you ask for a tearoff when
     829             :                     // it is in a state where the tearoff could not
     830             :                     // possibly be valid.
     831           0 :                     if (ccxp->CanGetTearOff()) {
     832             :                         XPCWrappedNativeTearOff* to =
     833           0 :                             ccxp->GetTearOff();
     834           0 :                         if (to)
     835           0 :                             to->Mark();
     836             :                     }
     837           0 :                     ccxp = ccxp->GetPrevCallContext();
     838             :                 }
     839             :             }
     840             : 
     841           0 :             XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
     842             : 
     843             :             // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
     844             :             // We transfered these native objects to this table when their
     845             :             // JSObject's were finalized. We did not destroy them immediately
     846             :             // at that point because the ordering of JS finalization is not
     847             :             // deterministic and we did not yet know if any wrappers that
     848             :             // might still be referencing the protos where still yet to be
     849             :             // finalized and destroyed. We *do* know that the protos'
     850             :             // JSObjects would not have been finalized if there were any
     851             :             // wrappers that referenced the proto but where not themselves
     852             :             // slated for finalization in this gc cycle. So... at this point
     853             :             // we know that any and all wrappers that might have been
     854             :             // referencing the protos in the dying list are themselves dead.
     855             :             // So, we can safely delete all the protos in the list.
     856             : 
     857           0 :             for (auto i = self->mDyingWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
     858           0 :                 auto entry = static_cast<XPCWrappedNativeProtoMap::Entry*>(i.Get());
     859           0 :                 delete static_cast<const XPCWrappedNativeProto*>(entry->key);
     860           0 :                 i.Remove();
     861             :             }
     862             : 
     863           0 :             MOZ_ASSERT(self->mGCIsRunning, "bad state");
     864           0 :             self->mGCIsRunning = false;
     865             : 
     866           0 :             break;
     867             :         }
     868             :     }
     869             : }
     870             : 
     871             : /* static */ void
     872           0 : XPCJSRuntime::WeakPointerZonesCallback(JSContext* cx, void* data)
     873             : {
     874             :     // Called before each sweeping slice -- after processing any final marking
     875             :     // triggered by barriers -- to clear out any references to things that are
     876             :     // about to be finalized and update any pointers to moved GC things.
     877           0 :     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
     878             : 
     879           0 :     self->mWrappedJSMap->UpdateWeakPointersAfterGC();
     880             : 
     881           0 :     XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC();
     882           0 : }
     883             : 
     884             : /* static */ void
     885           0 : XPCJSRuntime::WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data)
     886             : {
     887             :     // Called immediately after the ZoneGroup weak pointer callback, but only
     888             :     // once for each compartment that is being swept.
     889           0 :     CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
     890           0 :     if (xpcComp)
     891           0 :         xpcComp->UpdateWeakPointersAfterGC();
     892           0 : }
     893             : 
     894             : void
     895           0 : CompartmentPrivate::UpdateWeakPointersAfterGC()
     896             : {
     897           0 :     mWrappedJSMap->UpdateWeakPointersAfterGC();
     898           0 : }
     899             : 
     900             : void
     901           0 : XPCJSRuntime::CustomOutOfMemoryCallback()
     902             : {
     903           0 :     if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
     904           0 :         return;
     905             :     }
     906             : 
     907             :     nsCOMPtr<nsIMemoryInfoDumper> dumper =
     908           0 :         do_GetService("@mozilla.org/memory-info-dumper;1");
     909           0 :     if (!dumper) {
     910           0 :         return;
     911             :     }
     912             : 
     913             :     // If this fails, it fails silently.
     914           0 :     dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
     915             :                                     /* anonymize = */ false,
     916           0 :                                     /* minimizeMemoryUsage = */ false);
     917             : }
     918             : 
     919             : void
     920           0 : XPCJSRuntime::OnLargeAllocationFailure()
     921             : {
     922           0 :     CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState::Reporting);
     923             : 
     924           0 :     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     925           0 :     if (os) {
     926           0 :         os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
     927             :     }
     928             : 
     929           0 :     CycleCollectedJSRuntime::SetLargeAllocationFailure(OOMState::Reported);
     930           0 : }
     931             : 
     932             : class LargeAllocationFailureRunnable final : public Runnable
     933             : {
     934             :     Mutex mMutex;
     935             :     CondVar mCondVar;
     936             :     bool mWaiting;
     937             : 
     938           0 :     virtual ~LargeAllocationFailureRunnable()
     939           0 :     {
     940           0 :         MOZ_ASSERT(!mWaiting);
     941           0 :     }
     942             : 
     943             :   protected:
     944           0 :     NS_IMETHOD Run() override
     945             :     {
     946           0 :         MOZ_ASSERT(NS_IsMainThread());
     947             : 
     948           0 :         XPCJSRuntime::Get()->OnLargeAllocationFailure();
     949             : 
     950           0 :         MutexAutoLock lock(mMutex);
     951           0 :         MOZ_ASSERT(mWaiting);
     952             : 
     953           0 :         mWaiting = false;
     954           0 :         mCondVar.Notify();
     955           0 :         return NS_OK;
     956             :     }
     957             : 
     958             :   public:
     959           0 :     LargeAllocationFailureRunnable()
     960           0 :       : mozilla::Runnable("LargeAllocationFailureRunnable")
     961             :       , mMutex("LargeAllocationFailureRunnable::mMutex")
     962             :       , mCondVar(mMutex, "LargeAllocationFailureRunnable::mCondVar")
     963           0 :       , mWaiting(true)
     964             :     {
     965           0 :         MOZ_ASSERT(!NS_IsMainThread());
     966           0 :     }
     967             : 
     968           0 :     void BlockUntilDone()
     969             :     {
     970           0 :         MOZ_ASSERT(!NS_IsMainThread());
     971             : 
     972           0 :         MutexAutoLock lock(mMutex);
     973           0 :         while (mWaiting) {
     974           0 :             mCondVar.Wait();
     975             :         }
     976           0 :     }
     977             : };
     978             : 
     979             : static void
     980           0 : OnLargeAllocationFailureCallback()
     981             : {
     982             :     // This callback can be called from any thread, including internal JS helper
     983             :     // and DOM worker threads. We need to send the low-memory event via the
     984             :     // observer service which can only be called on the main thread, so proxy to
     985             :     // the main thread if we're not there already. The purpose of this callback
     986             :     // is to synchronously free some memory so the caller can retry a failed
     987             :     // allocation, so block on the completion.
     988             : 
     989           0 :     if (NS_IsMainThread()) {
     990           0 :         XPCJSRuntime::Get()->OnLargeAllocationFailure();
     991           0 :         return;
     992             :     }
     993             : 
     994           0 :     RefPtr<LargeAllocationFailureRunnable> r = new LargeAllocationFailureRunnable;
     995           0 :     if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
     996           0 :         return;
     997             :     }
     998             : 
     999           0 :     r->BlockUntilDone();
    1000             : }
    1001             : 
    1002             : size_t
    1003           0 : XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
    1004             : {
    1005           0 :     size_t n = 0;
    1006           0 :     n += mallocSizeOf(this);
    1007           0 :     n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
    1008           0 :     n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
    1009           0 :     n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
    1010           0 :     n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
    1011             : 
    1012           0 :     n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf);
    1013             : 
    1014             :     // There are other XPCJSRuntime members that could be measured; the above
    1015             :     // ones have been seen by DMD to be worth measuring.  More stuff may be
    1016             :     // added later.
    1017             : 
    1018           0 :     return n;
    1019             : }
    1020             : 
    1021             : size_t
    1022           0 : CompartmentPrivate::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
    1023             : {
    1024           0 :     size_t n = mallocSizeOf(this);
    1025           0 :     n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
    1026           0 :     n += mWrappedJSMap->SizeOfWrappedJS(mallocSizeOf);
    1027           0 :     return n;
    1028             : }
    1029             : 
    1030             : /***************************************************************************/
    1031             : 
    1032             : void
    1033           0 : XPCJSRuntime::Shutdown(JSContext* cx)
    1034             : {
    1035             :     // This destructor runs before ~CycleCollectedJSContext, which does the
    1036             :     // actual JS_DestroyContext() call. But destroying the context triggers
    1037             :     // one final GC, which can call back into the context with various
    1038             :     // callbacks if we aren't careful. Null out the relevant callbacks.
    1039           0 :     JS_RemoveFinalizeCallback(cx, FinalizeCallback);
    1040           0 :     JS_RemoveWeakPointerZonesCallback(cx, WeakPointerZonesCallback);
    1041           0 :     JS_RemoveWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback);
    1042             : 
    1043           0 :     JS::SetGCSliceCallback(cx, mPrevGCSliceCallback);
    1044             : 
    1045             :     // We don't want to track wrapped JS roots after this point since we're
    1046             :     // making them !IsValid anyway through SystemIsBeingShutDown.
    1047           0 :     mWrappedJSRoots = nullptr;
    1048             : 
    1049             :     // clean up and destroy maps...
    1050           0 :     mWrappedJSMap->ShutdownMarker();
    1051           0 :     delete mWrappedJSMap;
    1052           0 :     mWrappedJSMap = nullptr;
    1053             : 
    1054           0 :     delete mWrappedJSClassMap;
    1055           0 :     mWrappedJSClassMap = nullptr;
    1056             : 
    1057           0 :     delete mIID2NativeInterfaceMap;
    1058           0 :     mIID2NativeInterfaceMap = nullptr;
    1059             : 
    1060           0 :     delete mClassInfo2NativeSetMap;
    1061           0 :     mClassInfo2NativeSetMap = nullptr;
    1062             : 
    1063           0 :     delete mNativeSetMap;
    1064           0 :     mNativeSetMap = nullptr;
    1065             : 
    1066           0 :     delete mThisTranslatorMap;
    1067           0 :     mThisTranslatorMap = nullptr;
    1068             : 
    1069           0 :     delete mDyingWrappedNativeProtoMap;
    1070           0 :     mDyingWrappedNativeProtoMap = nullptr;
    1071             : 
    1072           0 :     CycleCollectedJSRuntime::Shutdown(cx);
    1073           0 : }
    1074             : 
    1075           0 : XPCJSRuntime::~XPCJSRuntime()
    1076             : {
    1077           0 :     MOZ_COUNT_DTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
    1078           0 : }
    1079             : 
    1080             : // If |*anonymizeID| is non-zero and this is a user compartment, the name will
    1081             : // be anonymized.
    1082             : static void
    1083           0 : GetCompartmentName(JSCompartment* c, nsCString& name, int* anonymizeID,
    1084             :                    bool replaceSlashes)
    1085             : {
    1086           0 :     if (js::IsAtomsCompartment(c)) {
    1087           0 :         name.AssignLiteral("atoms");
    1088           0 :     } else if (*anonymizeID && !js::IsSystemCompartment(c)) {
    1089           0 :         name.AppendPrintf("<anonymized-%d>", *anonymizeID);
    1090           0 :         *anonymizeID += 1;
    1091           0 :     } else if (JSPrincipals* principals = JS_GetCompartmentPrincipals(c)) {
    1092           0 :         nsresult rv = nsJSPrincipals::get(principals)->GetScriptLocation(name);
    1093           0 :         if (NS_FAILED(rv)) {
    1094           0 :             name.AssignLiteral("(unknown)");
    1095             :         }
    1096             : 
    1097             :         // If the compartment's location (name) differs from the principal's
    1098             :         // script location, append the compartment's location to allow
    1099             :         // differentiation of multiple compartments owned by the same principal
    1100             :         // (e.g. components owned by the system or null principal).
    1101           0 :         CompartmentPrivate* compartmentPrivate = CompartmentPrivate::Get(c);
    1102           0 :         if (compartmentPrivate) {
    1103           0 :             const nsACString& location = compartmentPrivate->GetLocation();
    1104           0 :             if (!location.IsEmpty() && !location.Equals(name)) {
    1105           0 :                 name.AppendLiteral(", ");
    1106           0 :                 name.Append(location);
    1107             :             }
    1108             :         }
    1109             : 
    1110           0 :         if (*anonymizeID) {
    1111             :             // We might have a file:// URL that includes a path from the local
    1112             :             // filesystem, which should be omitted if we're anonymizing.
    1113             :             static const char* filePrefix = "file://";
    1114           0 :             int filePos = name.Find(filePrefix);
    1115           0 :             if (filePos >= 0) {
    1116           0 :                 int pathPos = filePos + strlen(filePrefix);
    1117           0 :                 int lastSlashPos = -1;
    1118           0 :                 for (int i = pathPos; i < int(name.Length()); i++) {
    1119           0 :                     if (name[i] == '/' || name[i] == '\\') {
    1120           0 :                         lastSlashPos = i;
    1121             :                     }
    1122             :                 }
    1123           0 :                 if (lastSlashPos != -1) {
    1124           0 :                     name.ReplaceASCII(pathPos, lastSlashPos - pathPos,
    1125           0 :                                       "<anonymized>");
    1126             :                 } else {
    1127             :                     // Something went wrong. Anonymize the entire path to be
    1128             :                     // safe.
    1129           0 :                     name.Truncate(pathPos);
    1130           0 :                     name += "<anonymized?!>";
    1131             :                 }
    1132             :             }
    1133             : 
    1134             :             // We might have a location like this:
    1135             :             //   inProcessTabChildGlobal?ownedBy=http://www.example.com/
    1136             :             // The owner should be omitted if it's not a chrome: URI and we're
    1137             :             // anonymizing.
    1138             :             static const char* ownedByPrefix =
    1139             :                 "inProcessTabChildGlobal?ownedBy=";
    1140           0 :             int ownedByPos = name.Find(ownedByPrefix);
    1141           0 :             if (ownedByPos >= 0) {
    1142           0 :                 const char* chrome = "chrome:";
    1143           0 :                 int ownerPos = ownedByPos + strlen(ownedByPrefix);
    1144             :                 const nsDependentCSubstring& ownerFirstPart =
    1145           0 :                     Substring(name, ownerPos, strlen(chrome));
    1146           0 :                 if (!ownerFirstPart.EqualsASCII(chrome)) {
    1147           0 :                     name.Truncate(ownerPos);
    1148           0 :                     name += "<anonymized>";
    1149             :                 }
    1150             :             }
    1151             :         }
    1152             : 
    1153             :         // A hack: replace forward slashes with '\\' so they aren't
    1154             :         // treated as path separators.  Users of the reporters
    1155             :         // (such as about:memory) have to undo this change.
    1156           0 :         if (replaceSlashes)
    1157           0 :             name.ReplaceChar('/', '\\');
    1158             :     } else {
    1159           0 :         name.AssignLiteral("null-principal");
    1160             :     }
    1161           0 : }
    1162             : 
    1163             : extern void
    1164           0 : xpc::GetCurrentCompartmentName(JSContext* cx, nsCString& name)
    1165             : {
    1166           0 :     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
    1167           0 :     if (!global) {
    1168           0 :         name.AssignLiteral("no global");
    1169           0 :         return;
    1170             :     }
    1171             : 
    1172           0 :     JSCompartment* compartment = GetObjectCompartment(global);
    1173           0 :     int anonymizeID = 0;
    1174           0 :     GetCompartmentName(compartment, name, &anonymizeID, false);
    1175             : }
    1176             : 
    1177             : void
    1178           0 : xpc::AddGCCallback(xpcGCCallback cb)
    1179             : {
    1180           0 :     XPCJSRuntime::Get()->AddGCCallback(cb);
    1181           0 : }
    1182             : 
    1183             : void
    1184           0 : xpc::RemoveGCCallback(xpcGCCallback cb)
    1185             : {
    1186           0 :     XPCJSRuntime::Get()->RemoveGCCallback(cb);
    1187           0 : }
    1188             : 
    1189             : static int64_t
    1190           0 : JSMainRuntimeGCHeapDistinguishedAmount()
    1191             : {
    1192           0 :     JSContext* cx = danger::GetJSContext();
    1193           0 :     return int64_t(JS_GetGCParameter(cx, JSGC_TOTAL_CHUNKS)) *
    1194           0 :            js::gc::ChunkSize;
    1195             : }
    1196             : 
    1197             : static int64_t
    1198           0 : JSMainRuntimeTemporaryPeakDistinguishedAmount()
    1199             : {
    1200           0 :     JSContext* cx = danger::GetJSContext();
    1201           0 :     return JS::PeakSizeOfTemporary(cx);
    1202             : }
    1203             : 
    1204             : static int64_t
    1205           0 : JSMainRuntimeCompartmentsSystemDistinguishedAmount()
    1206             : {
    1207           0 :     JSContext* cx = danger::GetJSContext();
    1208           0 :     return JS::SystemCompartmentCount(cx);
    1209             : }
    1210             : 
    1211             : static int64_t
    1212           0 : JSMainRuntimeCompartmentsUserDistinguishedAmount()
    1213             : {
    1214           0 :     JSContext* cx = XPCJSContext::Get()->Context();
    1215           0 :     return JS::UserCompartmentCount(cx);
    1216             : }
    1217             : 
    1218           3 : class JSMainRuntimeTemporaryPeakReporter final : public nsIMemoryReporter
    1219             : {
    1220           0 :     ~JSMainRuntimeTemporaryPeakReporter() {}
    1221             : 
    1222             :   public:
    1223             :     NS_DECL_ISUPPORTS
    1224             : 
    1225           0 :     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    1226             :                               nsISupports* aData, bool aAnonymize) override
    1227             :     {
    1228           0 :         MOZ_COLLECT_REPORT(
    1229             :             "js-main-runtime-temporary-peak", KIND_OTHER, UNITS_BYTES,
    1230             :             JSMainRuntimeTemporaryPeakDistinguishedAmount(),
    1231             :             "Peak transient data size in the main JSRuntime (the current size "
    1232             :             "of which is reported as "
    1233           0 :             "'explicit/js-non-window/runtime/temporary').");
    1234             : 
    1235           0 :         return NS_OK;
    1236             :     }
    1237             : };
    1238             : 
    1239          39 : NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
    1240             : 
    1241             : // The REPORT* macros do an unconditional report.  The ZCREPORT* macros are for
    1242             : // compartments and zones; they aggregate any entries smaller than
    1243             : // SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
    1244             : // entries for the compartment.
    1245             : 
    1246             : #define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
    1247             : 
    1248             : #define REPORT(_path, _kind, _units, _amount, _desc) \
    1249             :     handleReport->Callback(EmptyCString(), _path, \
    1250             :                            nsIMemoryReporter::_kind, \
    1251             :                            nsIMemoryReporter::_units, _amount, \
    1252             :                            NS_LITERAL_CSTRING(_desc), data); \
    1253             : 
    1254             : #define REPORT_BYTES(_path, _kind, _amount, _desc) \
    1255             :     REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
    1256             : 
    1257             : #define REPORT_GC_BYTES(_path, _amount, _desc) \
    1258             :     do { \
    1259             :         size_t amount = _amount;  /* evaluate _amount only once */ \
    1260             :         handleReport->Callback(EmptyCString(), _path, \
    1261             :                                nsIMemoryReporter::KIND_NONHEAP, \
    1262             :                                nsIMemoryReporter::UNITS_BYTES, amount, \
    1263             :                                NS_LITERAL_CSTRING(_desc), data); \
    1264             :         gcTotal += amount; \
    1265             :     } while (0)
    1266             : 
    1267             : // Report compartment/zone non-GC (KIND_HEAP) bytes.
    1268             : #define ZCREPORT_BYTES(_path, _amount, _desc) \
    1269             :     do { \
    1270             :         /* Assign _descLiteral plus "" into a char* to prove that it's */ \
    1271             :         /* actually a literal. */ \
    1272             :         size_t amount = _amount;  /* evaluate _amount only once */ \
    1273             :         if (amount >= SUNDRIES_THRESHOLD) { \
    1274             :             handleReport->Callback(EmptyCString(), _path, \
    1275             :                                    nsIMemoryReporter::KIND_HEAP, \
    1276             :                                    nsIMemoryReporter::UNITS_BYTES, amount, \
    1277             :                                    NS_LITERAL_CSTRING(_desc), data); \
    1278             :         } else { \
    1279             :             sundriesMallocHeap += amount; \
    1280             :         } \
    1281             :     } while (0)
    1282             : 
    1283             : // Report compartment/zone GC bytes.
    1284             : #define ZCREPORT_GC_BYTES(_path, _amount, _desc) \
    1285             :     do { \
    1286             :         size_t amount = _amount;  /* evaluate _amount only once */ \
    1287             :         if (amount >= SUNDRIES_THRESHOLD) { \
    1288             :             handleReport->Callback(EmptyCString(), _path, \
    1289             :                                    nsIMemoryReporter::KIND_NONHEAP, \
    1290             :                                    nsIMemoryReporter::UNITS_BYTES, amount, \
    1291             :                                    NS_LITERAL_CSTRING(_desc), data); \
    1292             :             gcTotal += amount; \
    1293             :         } else { \
    1294             :             sundriesGCHeap += amount; \
    1295             :         } \
    1296             :     } while (0)
    1297             : 
    1298             : // Report runtime bytes.
    1299             : #define RREPORT_BYTES(_path, _kind, _amount, _desc) \
    1300             :     do { \
    1301             :         size_t amount = _amount;  /* evaluate _amount only once */ \
    1302             :         handleReport->Callback(EmptyCString(), _path, \
    1303             :                                nsIMemoryReporter::_kind, \
    1304             :                                nsIMemoryReporter::UNITS_BYTES, amount, \
    1305             :                                NS_LITERAL_CSTRING(_desc), data); \
    1306             :         rtTotal += amount; \
    1307             :     } while (0)
    1308             : 
    1309             : // Report GC thing bytes.
    1310             : #define MREPORT_BYTES(_path, _kind, _amount, _desc) \
    1311             :     do { \
    1312             :         size_t amount = _amount;  /* evaluate _amount only once */ \
    1313             :         handleReport->Callback(EmptyCString(), _path, \
    1314             :                                nsIMemoryReporter::_kind, \
    1315             :                                nsIMemoryReporter::UNITS_BYTES, amount, \
    1316             :                                NS_LITERAL_CSTRING(_desc), data); \
    1317             :         gcThingTotal += amount; \
    1318             :     } while (0)
    1319             : 
    1320           0 : MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
    1321             : 
    1322             : namespace xpc {
    1323             : 
    1324             : static void
    1325           0 : ReportZoneStats(const JS::ZoneStats& zStats,
    1326             :                 const xpc::ZoneStatsExtras& extras,
    1327             :                 nsIHandleReportCallback* handleReport,
    1328             :                 nsISupports* data,
    1329             :                 bool anonymize,
    1330             :                 size_t* gcTotalOut = nullptr)
    1331             : {
    1332           0 :     const nsCString& pathPrefix = extras.pathPrefix;
    1333           0 :     size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
    1334             : 
    1335           0 :     MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
    1336             : 
    1337           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("symbols/gc-heap"),
    1338             :         zStats.symbolsGCHeap,
    1339             :         "Symbols.");
    1340             : 
    1341           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
    1342             :         zStats.gcHeapArenaAdmin,
    1343             :         "Bookkeeping information and alignment padding within GC arenas.");
    1344             : 
    1345           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
    1346             :         zStats.unusedGCThings.totalSize(),
    1347             :         "Unused GC thing cells within non-empty arenas.");
    1348             : 
    1349           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("unique-id-map"),
    1350             :         zStats.uniqueIdMap,
    1351             :         "Address-independent cell identities.");
    1352             : 
    1353           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shape-tables"),
    1354             :         zStats.shapeTables,
    1355             :         "Tables storing shape information.");
    1356             : 
    1357           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
    1358             :         zStats.lazyScriptsGCHeap,
    1359             :         "Scripts that haven't executed yet.");
    1360             : 
    1361           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
    1362             :         zStats.lazyScriptsMallocHeap,
    1363             :         "Lazy script tables containing closed-over bindings or inner functions.");
    1364             : 
    1365           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"),
    1366             :         zStats.jitCodesGCHeap,
    1367             :         "References to executable code pools used by the JITs.");
    1368             : 
    1369           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/gc-heap"),
    1370             :         zStats.objectGroupsGCHeap,
    1371             :         "Classification and type inference information about objects.");
    1372             : 
    1373           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/malloc-heap"),
    1374             :         zStats.objectGroupsMallocHeap,
    1375             :         "Object group addenda.");
    1376             : 
    1377           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/gc-heap"),
    1378             :         zStats.scopesGCHeap,
    1379             :         "Scope information for scripts.");
    1380             : 
    1381           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/malloc-heap"),
    1382             :         zStats.scopesMallocHeap,
    1383             :         "Arrays of binding names and other binding-related data.");
    1384             : 
    1385           0 :     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/gc-heap"),
    1386             :         zStats.regExpSharedsGCHeap,
    1387             :         "Shared compiled regexp data.");
    1388             : 
    1389           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/malloc-heap"),
    1390             :         zStats.regExpSharedsMallocHeap,
    1391             :         "Shared compiled regexp data.");
    1392             : 
    1393           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
    1394             :         zStats.typePool,
    1395             :         "Type sets and related data.");
    1396             : 
    1397           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-zone"),
    1398             :         zStats.regexpZone,
    1399             :         "The regexp zone and regexp data.");
    1400             : 
    1401           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-zone"),
    1402             :         zStats.jitZone,
    1403             :         "The JIT zone.");
    1404             : 
    1405           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
    1406             :         zStats.baselineStubsOptimized,
    1407             :         "The Baseline JIT's optimized IC stubs (excluding code).");
    1408             : 
    1409           0 :     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-cached-cfg"),
    1410             :         zStats.cachedCFG,
    1411             :         "The cached CFG to construct Ion code out of it.");
    1412             : 
    1413           0 :     size_t stringsNotableAboutMemoryGCHeap = 0;
    1414           0 :     size_t stringsNotableAboutMemoryMallocHeap = 0;
    1415             : 
    1416             :     #define MAYBE_INLINE \
    1417             :         "The characters may be inline or on the malloc heap."
    1418             :     #define MAYBE_OVERALLOCATED \
    1419             :         "Sometimes over-allocated to simplify string concatenation."
    1420             : 
    1421           0 :     for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
    1422           0 :         const JS::NotableStringInfo& info = zStats.notableStrings[i];
    1423             : 
    1424           0 :         MOZ_ASSERT(!zStats.isTotals);
    1425             : 
    1426             :         // We don't do notable string detection when anonymizing, because
    1427             :         // there's a good chance its for crash submission, and the memory
    1428             :         // required for notable string detection is high.
    1429           0 :         MOZ_ASSERT(!anonymize);
    1430             : 
    1431           0 :         nsDependentCString notableString(info.buffer);
    1432             : 
    1433             :         // Viewing about:memory generates many notable strings which contain
    1434             :         // "string(length=".  If we report these as notable, then we'll create
    1435             :         // even more notable strings the next time we open about:memory (unless
    1436             :         // there's a GC in the meantime), and so on ad infinitum.
    1437             :         //
    1438             :         // To avoid cluttering up about:memory like this, we stick notable
    1439             :         // strings which contain "string(length=" into their own bucket.
    1440             : #       define STRING_LENGTH "string(length="
    1441           0 :         if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
    1442           0 :             stringsNotableAboutMemoryGCHeap += info.gcHeapLatin1;
    1443           0 :             stringsNotableAboutMemoryGCHeap += info.gcHeapTwoByte;
    1444           0 :             stringsNotableAboutMemoryMallocHeap += info.mallocHeapLatin1;
    1445           0 :             stringsNotableAboutMemoryMallocHeap += info.mallocHeapTwoByte;
    1446           0 :             continue;
    1447             :         }
    1448             : 
    1449             :         // Escape / to \ before we put notableString into the memory reporter
    1450             :         // path, because we don't want any forward slashes in the string to
    1451             :         // count as path separators.
    1452           0 :         nsCString escapedString(notableString);
    1453           0 :         escapedString.ReplaceSubstring("/", "\\");
    1454             : 
    1455           0 :         bool truncated = notableString.Length() < info.length;
    1456             : 
    1457           0 :         nsCString path = pathPrefix +
    1458           0 :             nsPrintfCString("strings/" STRING_LENGTH "%" PRIuSIZE ", copies=%d, \"%s\"%s)/",
    1459           0 :                             info.length, info.numCopies, escapedString.get(),
    1460           0 :                             truncated ? " (truncated)" : "");
    1461             : 
    1462           0 :         if (info.gcHeapLatin1 > 0) {
    1463           0 :             REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/latin1"),
    1464             :                 info.gcHeapLatin1,
    1465             :                 "Latin1 strings. " MAYBE_INLINE);
    1466             :         }
    1467             : 
    1468           0 :         if (info.gcHeapTwoByte > 0) {
    1469           0 :             REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/two-byte"),
    1470             :                 info.gcHeapTwoByte,
    1471             :                 "TwoByte strings. " MAYBE_INLINE);
    1472             :         }
    1473             : 
    1474           0 :         if (info.mallocHeapLatin1 > 0) {
    1475           0 :             REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/latin1"),
    1476             :                 KIND_HEAP, info.mallocHeapLatin1,
    1477             :                 "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
    1478             :         }
    1479             : 
    1480           0 :         if (info.mallocHeapTwoByte > 0) {
    1481           0 :             REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
    1482             :                 KIND_HEAP, info.mallocHeapTwoByte,
    1483             :                 "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
    1484             :         }
    1485             :     }
    1486             : 
    1487           0 :     nsCString nonNotablePath = pathPrefix;
    1488           0 :     nonNotablePath += (zStats.isTotals || anonymize)
    1489           0 :                     ? NS_LITERAL_CSTRING("strings/")
    1490           0 :                     : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
    1491             : 
    1492           0 :     if (zStats.stringInfo.gcHeapLatin1 > 0) {
    1493           0 :         REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/latin1"),
    1494             :             zStats.stringInfo.gcHeapLatin1,
    1495             :             "Latin1 strings. " MAYBE_INLINE);
    1496             :     }
    1497             : 
    1498           0 :     if (zStats.stringInfo.gcHeapTwoByte > 0) {
    1499           0 :         REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/two-byte"),
    1500             :             zStats.stringInfo.gcHeapTwoByte,
    1501             :             "TwoByte strings. " MAYBE_INLINE);
    1502             :     }
    1503             : 
    1504           0 :     if (zStats.stringInfo.mallocHeapLatin1 > 0) {
    1505           0 :         REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/latin1"),
    1506             :             KIND_HEAP, zStats.stringInfo.mallocHeapLatin1,
    1507             :             "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
    1508             :     }
    1509             : 
    1510           0 :     if (zStats.stringInfo.mallocHeapTwoByte > 0) {
    1511           0 :         REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
    1512             :             KIND_HEAP, zStats.stringInfo.mallocHeapTwoByte,
    1513             :             "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
    1514             :     }
    1515             : 
    1516           0 :     if (stringsNotableAboutMemoryGCHeap > 0) {
    1517           0 :         MOZ_ASSERT(!zStats.isTotals);
    1518           0 :         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
    1519             :             stringsNotableAboutMemoryGCHeap,
    1520             :             "Strings that contain the characters '" STRING_LENGTH "', which "
    1521             :             "are probably from about:memory itself." MAYBE_INLINE
    1522             :             " We filter them out rather than display them, because displaying "
    1523             :             "them would create even more such strings every time about:memory "
    1524             :             "is refreshed.");
    1525             :     }
    1526             : 
    1527           0 :     if (stringsNotableAboutMemoryMallocHeap > 0) {
    1528           0 :         MOZ_ASSERT(!zStats.isTotals);
    1529           0 :         REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
    1530             :             KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
    1531             :             "Non-inline string characters of strings that contain the "
    1532             :             "characters '" STRING_LENGTH "', which are probably from "
    1533             :             "about:memory itself. " MAYBE_OVERALLOCATED
    1534             :             " We filter them out rather than display them, because displaying "
    1535             :             "them would create even more such strings every time about:memory "
    1536             :             "is refreshed.");
    1537             :     }
    1538             : 
    1539           0 :     const JS::ShapeInfo& shapeInfo = zStats.shapeInfo;
    1540           0 :     if (shapeInfo.shapesGCHeapTree > 0) {
    1541           0 :         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree"),
    1542             :             shapeInfo.shapesGCHeapTree,
    1543             :         "Shapes in a property tree.");
    1544             :     }
    1545             : 
    1546           0 :     if (shapeInfo.shapesGCHeapDict > 0) {
    1547           0 :         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
    1548             :             shapeInfo.shapesGCHeapDict,
    1549             :         "Shapes in dictionary mode.");
    1550             :     }
    1551             : 
    1552           0 :     if (shapeInfo.shapesGCHeapBase > 0) {
    1553           0 :         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
    1554             :             shapeInfo.shapesGCHeapBase,
    1555             :             "Base shapes, which collate data common to many shapes.");
    1556             :     }
    1557             : 
    1558           0 :     if (shapeInfo.shapesMallocHeapTreeTables > 0) {
    1559           0 :         REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
    1560             :             KIND_HEAP, shapeInfo.shapesMallocHeapTreeTables,
    1561             :             "Property tables of shapes in a property tree.");
    1562             :     }
    1563             : 
    1564           0 :     if (shapeInfo.shapesMallocHeapDictTables > 0) {
    1565           0 :         REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
    1566             :             KIND_HEAP, shapeInfo.shapesMallocHeapDictTables,
    1567             :             "Property tables of shapes in dictionary mode.");
    1568             :     }
    1569             : 
    1570           0 :     if (shapeInfo.shapesMallocHeapTreeKids > 0) {
    1571           0 :         REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-kids"),
    1572             :             KIND_HEAP, shapeInfo.shapesMallocHeapTreeKids,
    1573             :             "Kid hashes of shapes in a property tree.");
    1574             :     }
    1575             : 
    1576           0 :     if (sundriesGCHeap > 0) {
    1577             :         // We deliberately don't use ZCREPORT_GC_BYTES here.
    1578           0 :         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
    1579             :             sundriesGCHeap,
    1580             :             "The sum of all 'gc-heap' measurements that are too small to be "
    1581             :             "worth showing individually.");
    1582             :     }
    1583             : 
    1584           0 :     if (sundriesMallocHeap > 0) {
    1585             :         // We deliberately don't use ZCREPORT_BYTES here.
    1586           0 :         REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
    1587             :             KIND_HEAP, sundriesMallocHeap,
    1588             :             "The sum of all 'malloc-heap' measurements that are too small to "
    1589             :             "be worth showing individually.");
    1590             :     }
    1591             : 
    1592           0 :     if (gcTotalOut)
    1593           0 :         *gcTotalOut += gcTotal;
    1594             : 
    1595             : #   undef STRING_LENGTH
    1596           0 : }
    1597             : 
    1598             : static void
    1599           0 : ReportClassStats(const ClassInfo& classInfo, const nsACString& path,
    1600             :                  nsIHandleReportCallback* handleReport,
    1601             :                  nsISupports* data, size_t& gcTotal)
    1602             : {
    1603             :     // We deliberately don't use ZCREPORT_BYTES, so that these per-class values
    1604             :     // don't go into sundries.
    1605             : 
    1606           0 :     if (classInfo.objectsGCHeap > 0) {
    1607           0 :         REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("objects/gc-heap"),
    1608             :             classInfo.objectsGCHeap,
    1609             :             "Objects, including fixed slots.");
    1610             :     }
    1611             : 
    1612           0 :     if (classInfo.objectsMallocHeapSlots > 0) {
    1613           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
    1614             :             KIND_HEAP, classInfo.objectsMallocHeapSlots,
    1615             :             "Non-fixed object slots.");
    1616             :     }
    1617             : 
    1618           0 :     if (classInfo.objectsMallocHeapElementsNormal > 0) {
    1619           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/normal"),
    1620             :             KIND_HEAP, classInfo.objectsMallocHeapElementsNormal,
    1621             :             "Normal (non-wasm) indexed elements.");
    1622             :     }
    1623             : 
    1624           0 :     if (classInfo.objectsMallocHeapElementsAsmJS > 0) {
    1625           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
    1626             :             KIND_HEAP, classInfo.objectsMallocHeapElementsAsmJS,
    1627             :             "asm.js array buffer elements allocated in the malloc heap.");
    1628             :     }
    1629             : 
    1630           0 :     if (classInfo.objectsMallocHeapMisc > 0) {
    1631           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/misc"),
    1632             :             KIND_HEAP, classInfo.objectsMallocHeapMisc,
    1633             :             "Miscellaneous object data.");
    1634             :     }
    1635             : 
    1636           0 :     if (classInfo.objectsNonHeapElementsNormal > 0) {
    1637           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/normal"),
    1638             :             KIND_NONHEAP, classInfo.objectsNonHeapElementsNormal,
    1639             :             "Memory-mapped non-shared array buffer elements.");
    1640             :     }
    1641             : 
    1642           0 :     if (classInfo.objectsNonHeapElementsShared > 0) {
    1643           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/shared"),
    1644             :             KIND_NONHEAP, classInfo.objectsNonHeapElementsShared,
    1645             :             "Memory-mapped shared array buffer elements. These elements are "
    1646             :             "shared between one or more runtimes; the reported size is divided "
    1647             :             "by the buffer's refcount.");
    1648             :     }
    1649             : 
    1650             :     // WebAssembly memories are always non-heap-allocated (mmap). We never put
    1651             :     // these under sundries, because (a) in practice they're almost always
    1652             :     // larger than the sundries threshold, and (b) we'd need a third category of
    1653             :     // sundries ("non-heap"), which would be a pain.
    1654           0 :     if (classInfo.objectsNonHeapElementsWasm > 0) {
    1655           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/wasm"),
    1656             :             KIND_NONHEAP, classInfo.objectsNonHeapElementsWasm,
    1657             :             "wasm/asm.js array buffer elements allocated outside both the "
    1658             :             "malloc heap and the GC heap.");
    1659             :     }
    1660             : 
    1661           0 :     if (classInfo.objectsNonHeapCodeWasm > 0) {
    1662           0 :         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/code/wasm"),
    1663             :             KIND_NONHEAP, classInfo.objectsNonHeapCodeWasm,
    1664             :             "AOT-compiled wasm/asm.js code.");
    1665             :     }
    1666             : 
    1667             :     // Although wasm guard pages aren't committed in memory they can be very
    1668             :     // large and contribute greatly to vsize and so are worth reporting.
    1669           0 :     if (classInfo.wasmGuardPages > 0) {
    1670           0 :         REPORT_BYTES(NS_LITERAL_CSTRING("wasm-guard-pages"),
    1671             :             KIND_OTHER, classInfo.wasmGuardPages,
    1672             :             "Guard pages mapped after the end of wasm memories, reserved for "
    1673             :             "optimization tricks, but not committed and thus never contributing"
    1674             :             " to RSS, only vsize.");
    1675             :     }
    1676           0 : }
    1677             : 
    1678             : static void
    1679           0 : ReportCompartmentStats(const JS::CompartmentStats& cStats,
    1680             :                        const xpc::CompartmentStatsExtras& extras,
    1681             :                        amIAddonManager* addonManager,
    1682             :                        nsIHandleReportCallback* handleReport,
    1683             :                        nsISupports* data, size_t* gcTotalOut = nullptr)
    1684             : {
    1685           0 :     static const nsDependentCString addonPrefix("explicit/add-ons/");
    1686             : 
    1687           0 :     size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
    1688           0 :     nsAutoCString cJSPathPrefix(extras.jsPathPrefix);
    1689           0 :     nsAutoCString cDOMPathPrefix(extras.domPathPrefix);
    1690             : 
    1691           0 :     MOZ_ASSERT(!gcTotalOut == cStats.isTotals);
    1692             : 
    1693             :     // Only attempt to prefix if we got a location and the path wasn't already
    1694             :     // prefixed.
    1695           0 :     if (extras.location && addonManager &&
    1696           0 :         cJSPathPrefix.Find(addonPrefix, false, 0, 0) != 0) {
    1697           0 :         nsAutoCString addonId;
    1698             :         bool ok;
    1699           0 :         if (NS_SUCCEEDED(addonManager->MapURIToAddonID(extras.location,
    1700             :                                                         addonId, &ok))
    1701           0 :             && ok) {
    1702             :             // Insert the add-on id as "add-ons/@id@/" after "explicit/" to
    1703             :             // aggregate add-on compartments.
    1704             :             static const size_t explicitLength = strlen("explicit/");
    1705           0 :             addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
    1706           0 :             addonId += "/";
    1707           0 :             cJSPathPrefix.Insert(addonId, explicitLength);
    1708           0 :             cDOMPathPrefix.Insert(addonId, explicitLength);
    1709             :         }
    1710             :     }
    1711             : 
    1712           0 :     nsCString nonNotablePath = cJSPathPrefix;
    1713           0 :     nonNotablePath += cStats.isTotals
    1714           0 :                     ? NS_LITERAL_CSTRING("classes/")
    1715           0 :                     : NS_LITERAL_CSTRING("classes/class(<non-notable classes>)/");
    1716             : 
    1717           0 :     ReportClassStats(cStats.classInfo, nonNotablePath, handleReport, data,
    1718           0 :                      gcTotal);
    1719             : 
    1720           0 :     for (size_t i = 0; i < cStats.notableClasses.length(); i++) {
    1721           0 :         MOZ_ASSERT(!cStats.isTotals);
    1722           0 :         const JS::NotableClassInfo& classInfo = cStats.notableClasses[i];
    1723             : 
    1724           0 :         nsCString classPath = cJSPathPrefix +
    1725           0 :             nsPrintfCString("classes/class(%s)/", classInfo.className_);
    1726             : 
    1727           0 :         ReportClassStats(classInfo, classPath, handleReport, data, gcTotal);
    1728             :     }
    1729             : 
    1730             :     // Note that we use cDOMPathPrefix here.  This is because we measure orphan
    1731             :     // DOM nodes in the JS reporter, but we want to report them in a "dom"
    1732             :     // sub-tree rather than a "js" sub-tree.
    1733           0 :     ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
    1734             :         cStats.objectsPrivate,
    1735             :         "Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
    1736             :         "objects.");
    1737             : 
    1738           0 :     ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
    1739             :         cStats.scriptsGCHeap,
    1740             :         "JSScript instances. There is one per user-defined function in a "
    1741             :         "script, and one for the top-level code in a script.");
    1742             : 
    1743           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/malloc-heap/data"),
    1744             :         cStats.scriptsMallocHeapData,
    1745             :         "Various variable-length tables in JSScripts.");
    1746             : 
    1747           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/data"),
    1748             :         cStats.baselineData,
    1749             :         "The Baseline JIT's compilation data (BaselineScripts).");
    1750             : 
    1751           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"),
    1752             :         cStats.baselineStubsFallback,
    1753             :         "The Baseline JIT's fallback IC stubs (excluding code).");
    1754             : 
    1755           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("ion-data"),
    1756             :         cStats.ionData,
    1757             :         "The IonMonkey JIT's compilation data (IonScripts).");
    1758             : 
    1759           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
    1760             :         cStats.typeInferenceTypeScripts,
    1761             :         "Type sets associated with scripts.");
    1762             : 
    1763           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
    1764             :         cStats.typeInferenceAllocationSiteTables,
    1765             :         "Tables of type objects associated with allocation sites.");
    1766             : 
    1767           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
    1768             :         cStats.typeInferenceArrayTypeTables,
    1769             :         "Tables of type objects associated with array literals.");
    1770             : 
    1771           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"),
    1772             :         cStats.typeInferenceObjectTypeTables,
    1773             :         "Tables of type objects associated with object literals.");
    1774             : 
    1775           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-object"),
    1776             :         cStats.compartmentObject,
    1777             :         "The JSCompartment object itself.");
    1778             : 
    1779           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-tables"),
    1780             :         cStats.compartmentTables,
    1781             :         "Compartment-wide tables storing object group information and wasm instances.");
    1782             : 
    1783           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("inner-views"),
    1784             :         cStats.innerViewsTable,
    1785             :         "The table for array buffer inner views.");
    1786             : 
    1787           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("lazy-array-buffers"),
    1788             :         cStats.lazyArrayBuffersTable,
    1789             :         "The table for typed object lazy array buffers.");
    1790             : 
    1791           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("object-metadata"),
    1792             :         cStats.objectMetadataTable,
    1793             :         "The table used by debugging tools for tracking object metadata");
    1794             : 
    1795           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
    1796             :         cStats.crossCompartmentWrappersTable,
    1797             :         "The cross-compartment wrapper table.");
    1798             : 
    1799           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("saved-stacks-set"),
    1800             :         cStats.savedStacksSet,
    1801             :         "The saved stacks set.");
    1802             : 
    1803           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("non-syntactic-lexical-scopes-table"),
    1804             :         cStats.nonSyntacticLexicalScopesTable,
    1805             :         "The non-syntactic lexical scopes table.");
    1806             : 
    1807           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("template-literal-map"),
    1808             :         cStats.templateLiteralMap,
    1809             :         "The template literal registry.");
    1810             : 
    1811           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("jit-compartment"),
    1812             :         cStats.jitCompartment,
    1813             :         "The JIT compartment.");
    1814             : 
    1815           0 :     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("private-data"),
    1816             :         cStats.privateData,
    1817             :         "Extra data attached to the compartment by XPConnect, including "
    1818             :         "its wrapped-js.");
    1819             : 
    1820           0 :     if (sundriesGCHeap > 0) {
    1821             :         // We deliberately don't use ZCREPORT_GC_BYTES here.
    1822           0 :         REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
    1823             :             sundriesGCHeap,
    1824             :             "The sum of all 'gc-heap' measurements that are too small to be "
    1825             :             "worth showing individually.");
    1826             :     }
    1827             : 
    1828           0 :     if (sundriesMallocHeap > 0) {
    1829             :         // We deliberately don't use ZCREPORT_BYTES here.
    1830           0 :         REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
    1831             :             KIND_HEAP, sundriesMallocHeap,
    1832             :             "The sum of all 'malloc-heap' measurements that are too small to "
    1833             :             "be worth showing individually.");
    1834             :     }
    1835             : 
    1836           0 :     if (gcTotalOut)
    1837           0 :         *gcTotalOut += gcTotal;
    1838           0 : }
    1839             : 
    1840             : static void
    1841           0 : ReportScriptSourceStats(const ScriptSourceInfo& scriptSourceInfo,
    1842             :                         const nsACString& path,
    1843             :                         nsIHandleReportCallback* handleReport,
    1844             :                         nsISupports* data, size_t& rtTotal)
    1845             : {
    1846           0 :     if (scriptSourceInfo.misc > 0) {
    1847           0 :         RREPORT_BYTES(path + NS_LITERAL_CSTRING("misc"),
    1848             :             KIND_HEAP, scriptSourceInfo.misc,
    1849             :             "Miscellaneous data relating to JavaScript source code.");
    1850             :     }
    1851           0 : }
    1852             : 
    1853             : static void
    1854           0 : ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
    1855             :                                  const nsACString& rtPath,
    1856             :                                  amIAddonManager* addonManager,
    1857             :                                  nsIHandleReportCallback* handleReport,
    1858             :                                  nsISupports* data,
    1859             :                                  bool anonymize,
    1860             :                                  size_t* rtTotalOut)
    1861             : {
    1862           0 :     size_t gcTotal = 0;
    1863             : 
    1864           0 :     for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
    1865           0 :         const JS::ZoneStats& zStats = rtStats.zoneStatsVector[i];
    1866             :         const xpc::ZoneStatsExtras* extras =
    1867           0 :           static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
    1868           0 :         ReportZoneStats(zStats, *extras, handleReport, data, anonymize,
    1869           0 :                         &gcTotal);
    1870             :     }
    1871             : 
    1872           0 :     for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
    1873           0 :         const JS::CompartmentStats& cStats = rtStats.compartmentStatsVector[i];
    1874             :         const xpc::CompartmentStatsExtras* extras =
    1875           0 :             static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra);
    1876             : 
    1877             :         ReportCompartmentStats(cStats, *extras, addonManager, handleReport,
    1878           0 :                                data, &gcTotal);
    1879             :     }
    1880             : 
    1881             :     // Report the rtStats.runtime numbers under "runtime/", and compute their
    1882             :     // total for later.
    1883             : 
    1884           0 :     size_t rtTotal = 0;
    1885             : 
    1886           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"),
    1887             :         KIND_HEAP, rtStats.runtime.object,
    1888             :         "The JSRuntime object.");
    1889             : 
    1890           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"),
    1891             :         KIND_HEAP, rtStats.runtime.atomsTable,
    1892             :         "The atoms table.");
    1893             : 
    1894           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-mark-bitmaps"),
    1895             :         KIND_HEAP, rtStats.runtime.atomsMarkBitmaps,
    1896             :         "Mark bitmaps for atoms held by each zone.");
    1897             : 
    1898           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"),
    1899             :         KIND_HEAP, rtStats.runtime.contexts,
    1900             :         "JSContext objects and structures that belong to them.");
    1901             : 
    1902           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"),
    1903             :         KIND_HEAP, rtStats.runtime.temporary,
    1904             :         "Transient data (mostly parse nodes) held by the JSRuntime during "
    1905             :         "compilation.");
    1906             : 
    1907           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
    1908             :         KIND_HEAP, rtStats.runtime.interpreterStack,
    1909             :         "JS interpreter frames.");
    1910             : 
    1911           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"),
    1912             :         KIND_HEAP, rtStats.runtime.mathCache,
    1913             :         "The math cache.");
    1914             : 
    1915           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-immutable-strings-cache"),
    1916             :         KIND_HEAP, rtStats.runtime.sharedImmutableStringsCache,
    1917             :         "Immutable strings (such as JS scripts' source text) shared across all JSRuntimes.");
    1918             : 
    1919           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-intl-data"),
    1920             :         KIND_HEAP, rtStats.runtime.sharedIntlData,
    1921             :         "Shared internationalization data.");
    1922             : 
    1923           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/uncompressed-source-cache"),
    1924             :         KIND_HEAP, rtStats.runtime.uncompressedSourceCache,
    1925             :         "The uncompressed source code cache.");
    1926             : 
    1927           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"),
    1928             :         KIND_HEAP, rtStats.runtime.scriptData,
    1929             :         "The table holding script data shared in the runtime.");
    1930             : 
    1931             :     nsCString nonNotablePath =
    1932           0 :         rtPath + nsPrintfCString("runtime/script-sources/source(scripts=%d, <non-notable files>)/",
    1933           0 :                                  rtStats.runtime.scriptSourceInfo.numScripts);
    1934             : 
    1935           0 :     ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo,
    1936           0 :                             nonNotablePath, handleReport, data, rtTotal);
    1937             : 
    1938           0 :     for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) {
    1939             :         const JS::NotableScriptSourceInfo& scriptSourceInfo =
    1940           0 :             rtStats.runtime.notableScriptSources[i];
    1941             : 
    1942             :         // Escape / to \ before we put the filename into the memory reporter
    1943             :         // path, because we don't want any forward slashes in the string to
    1944             :         // count as path separators. Consumers of memory reporters (e.g.
    1945             :         // about:memory) will convert them back to / after doing path
    1946             :         // splitting.
    1947           0 :         nsCString escapedFilename;
    1948           0 :         if (anonymize) {
    1949           0 :             escapedFilename.AppendPrintf("<anonymized-source-%d>", int(i));
    1950             :         } else {
    1951           0 :             nsDependentCString filename(scriptSourceInfo.filename_);
    1952           0 :             escapedFilename.Append(filename);
    1953           0 :             escapedFilename.ReplaceSubstring("/", "\\");
    1954             :         }
    1955             : 
    1956           0 :         nsCString notablePath = rtPath +
    1957           0 :             nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
    1958           0 :                             scriptSourceInfo.numScripts, escapedFilename.get());
    1959             : 
    1960           0 :         ReportScriptSourceStats(scriptSourceInfo, notablePath,
    1961           0 :                                 handleReport, data, rtTotal);
    1962             :     }
    1963             : 
    1964           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/ion"),
    1965             :         KIND_NONHEAP, rtStats.runtime.code.ion,
    1966             :         "Code generated by the IonMonkey JIT.");
    1967             : 
    1968           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/baseline"),
    1969             :         KIND_NONHEAP, rtStats.runtime.code.baseline,
    1970             :         "Code generated by the Baseline JIT.");
    1971             : 
    1972           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/regexp"),
    1973             :         KIND_NONHEAP, rtStats.runtime.code.regexp,
    1974             :         "Code generated by the regexp JIT.");
    1975             : 
    1976           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/other"),
    1977             :         KIND_NONHEAP, rtStats.runtime.code.other,
    1978             :         "Code generated by the JITs for wrappers and trampolines.");
    1979             : 
    1980           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/unused"),
    1981             :         KIND_NONHEAP, rtStats.runtime.code.unused,
    1982             :         "Memory allocated by one of the JITs to hold code, but which is "
    1983             :         "currently unused.");
    1984             : 
    1985           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"),
    1986             :         KIND_HEAP, rtStats.runtime.gc.marker,
    1987             :         "The GC mark stack and gray roots.");
    1988             : 
    1989           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"),
    1990             :         KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted,
    1991             :         "Memory being used by the GC's nursery.");
    1992             : 
    1993           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-malloced-buffers"),
    1994             :         KIND_HEAP, rtStats.runtime.gc.nurseryMallocedBuffers,
    1995             :         "Out-of-line slots and elements belonging to objects in the nursery.");
    1996             : 
    1997           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"),
    1998             :         KIND_HEAP, rtStats.runtime.gc.storeBufferVals,
    1999             :         "Values in the store buffer.");
    2000             : 
    2001           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"),
    2002             :         KIND_HEAP, rtStats.runtime.gc.storeBufferCells,
    2003             :         "Cells in the store buffer.");
    2004             : 
    2005           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/slots"),
    2006             :         KIND_HEAP, rtStats.runtime.gc.storeBufferSlots,
    2007             :         "Slots in the store buffer.");
    2008             : 
    2009           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/whole-cells"),
    2010             :         KIND_HEAP, rtStats.runtime.gc.storeBufferWholeCells,
    2011             :         "Whole cells in the store buffer.");
    2012             : 
    2013           0 :     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/generics"),
    2014             :         KIND_HEAP, rtStats.runtime.gc.storeBufferGenerics,
    2015             :         "Generic things in the store buffer.");
    2016             : 
    2017           0 :     if (rtTotalOut)
    2018           0 :         *rtTotalOut = rtTotal;
    2019             : 
    2020             :     // Report GC numbers that don't belong to a compartment.
    2021             : 
    2022             :     // We don't want to report decommitted memory in "explicit", so we just
    2023             :     // change the leading "explicit/" to "decommitted/".
    2024           0 :     nsCString rtPath2(rtPath);
    2025           0 :     rtPath2.Replace(0, strlen("explicit"), NS_LITERAL_CSTRING("decommitted"));
    2026           0 :     REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"),
    2027             :         rtStats.gcHeapDecommittedArenas,
    2028             :         "GC arenas in non-empty chunks that is decommitted, i.e. it takes up "
    2029             :         "address space but no physical memory or swap space.");
    2030             : 
    2031           0 :     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"),
    2032             :         rtStats.gcHeapUnusedChunks,
    2033             :         "Empty GC chunks which will soon be released unless claimed for new "
    2034             :         "allocations.");
    2035             : 
    2036           0 :     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
    2037             :         rtStats.gcHeapUnusedArenas,
    2038             :         "Empty GC arenas within non-empty chunks.");
    2039             : 
    2040           0 :     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"),
    2041             :         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           0 :     MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
    2047           0 : }
    2048             : 
    2049             : void
    2050           0 : ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
    2051             :                                  const nsACString& rtPath,
    2052             :                                  nsIHandleReportCallback* handleReport,
    2053             :                                  nsISupports* data,
    2054             :                                  bool anonymize,
    2055             :                                  size_t* rtTotalOut)
    2056             : {
    2057           0 :     nsCOMPtr<amIAddonManager> am;
    2058           0 :     if (XRE_IsParentProcess()) {
    2059             :         // Only try to access the service from the main process.
    2060           0 :         am = do_GetService("@mozilla.org/addons/integration;1");
    2061             :     }
    2062           0 :     ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), handleReport,
    2063           0 :                                      data, anonymize, rtTotalOut);
    2064           0 : }
    2065             : 
    2066             : 
    2067             : } // namespace xpc
    2068             : 
    2069           3 : class JSMainRuntimeCompartmentsReporter final : public nsIMemoryReporter
    2070             : {
    2071             : 
    2072           0 :     ~JSMainRuntimeCompartmentsReporter() {}
    2073             : 
    2074             :   public:
    2075             :     NS_DECL_ISUPPORTS
    2076             : 
    2077           0 :     struct Data {
    2078             :         int anonymizeID;
    2079             :         js::Vector<nsCString, 0, js::SystemAllocPolicy> paths;
    2080             :     };
    2081             : 
    2082           0 :     static void CompartmentCallback(JSContext* cx, void* vdata, JSCompartment* c) {
    2083             :         // silently ignore OOM errors
    2084           0 :         Data* data = static_cast<Data*>(vdata);
    2085           0 :         nsCString path;
    2086           0 :         GetCompartmentName(c, path, &data->anonymizeID, /* replaceSlashes = */ true);
    2087           0 :         path.Insert(js::IsSystemCompartment(c)
    2088           0 :                     ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/")
    2089           0 :                     : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"),
    2090           0 :                     0);
    2091           0 :         mozilla::Unused << data->paths.append(path);
    2092           0 :     }
    2093             : 
    2094           0 :     NS_IMETHOD CollectReports(nsIHandleReportCallback* handleReport,
    2095             :                               nsISupports* data, bool anonymize) override
    2096             :     {
    2097             :         // First we collect the compartment paths.  Then we report them.  Doing
    2098             :         // the two steps interleaved is a bad idea, because calling
    2099             :         // |handleReport| from within CompartmentCallback() leads to all manner
    2100             :         // of assertions.
    2101             : 
    2102           0 :         Data d;
    2103           0 :         d.anonymizeID = anonymize ? 1 : 0;
    2104           0 :         JS_IterateCompartments(XPCJSContext::Get()->Context(),
    2105           0 :                                &d, CompartmentCallback);
    2106             : 
    2107           0 :         for (size_t i = 0; i < d.paths.length(); i++)
    2108           0 :             REPORT(nsCString(d.paths[i]), KIND_OTHER, UNITS_COUNT, 1,
    2109             :                 "A live compartment in the main JSRuntime.");
    2110             : 
    2111           0 :         return NS_OK;
    2112             :     }
    2113             : };
    2114             : 
    2115          39 : NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
    2116             : 
    2117           0 : MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
    2118             : 
    2119             : namespace xpc {
    2120             : 
    2121             : static size_t
    2122           0 : SizeOfTreeIncludingThis(nsINode* tree)
    2123             : {
    2124           0 :     size_t n = tree->SizeOfIncludingThis(OrphanMallocSizeOf);
    2125           0 :     for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
    2126           0 :         n += child->SizeOfIncludingThis(OrphanMallocSizeOf);
    2127             : 
    2128           0 :     return n;
    2129             : }
    2130             : 
    2131           0 : class OrphanReporter : public JS::ObjectPrivateVisitor
    2132             : {
    2133             :   public:
    2134           0 :     explicit OrphanReporter(GetISupportsFun aGetISupports)
    2135           0 :       : JS::ObjectPrivateVisitor(aGetISupports)
    2136             :     {
    2137           0 :     }
    2138             : 
    2139           0 :     virtual size_t sizeOfIncludingThis(nsISupports* aSupports) override {
    2140           0 :         size_t n = 0;
    2141           0 :         nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
    2142             :         // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
    2143             :         // that we have to skip XBL elements because they violate certain
    2144             :         // assumptions.  Yuk.
    2145           0 :         if (node && !node->IsInUncomposedDoc() &&
    2146           0 :             !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
    2147             :         {
    2148             :             // This is an orphan node.  If we haven't already handled the
    2149             :             // sub-tree that this node belongs to, measure the sub-tree's size
    2150             :             // and then record its root so we don't measure it again.
    2151           0 :             nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
    2152           0 :             if (orphanTree &&
    2153           0 :                 !mAlreadyMeasuredOrphanTrees.Contains(orphanTree)) {
    2154             :                 // If PutEntry() fails we don't measure this tree, which could
    2155             :                 // lead to under-measurement. But that's better than the
    2156             :                 // alternatives, which are over-measurement or an OOM abort.
    2157           0 :                 if (mAlreadyMeasuredOrphanTrees.PutEntry(orphanTree, fallible)) {
    2158           0 :                     n += SizeOfTreeIncludingThis(orphanTree);
    2159             :                 }
    2160             :             }
    2161             :         }
    2162           0 :         return n;
    2163             :     }
    2164             : 
    2165             :   private:
    2166             :     nsTHashtable <nsISupportsHashKey> mAlreadyMeasuredOrphanTrees;
    2167             : };
    2168             : 
    2169             : #ifdef DEBUG
    2170             : static bool
    2171           0 : StartsWithExplicit(nsACString& s)
    2172             : {
    2173           0 :     return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
    2174             : }
    2175             : #endif
    2176             : 
    2177             : class XPCJSRuntimeStats : public JS::RuntimeStats
    2178             : {
    2179             :     WindowPaths* mWindowPaths;
    2180             :     WindowPaths* mTopWindowPaths;
    2181             :     bool mGetLocations;
    2182             :     int mAnonymizeID;
    2183             : 
    2184             :   public:
    2185           0 :     XPCJSRuntimeStats(WindowPaths* windowPaths, WindowPaths* topWindowPaths,
    2186             :                       bool getLocations, bool anonymize)
    2187           0 :       : JS::RuntimeStats(JSMallocSizeOf),
    2188             :         mWindowPaths(windowPaths),
    2189             :         mTopWindowPaths(topWindowPaths),
    2190             :         mGetLocations(getLocations),
    2191           0 :         mAnonymizeID(anonymize ? 1 : 0)
    2192           0 :     {}
    2193             : 
    2194           0 :     ~XPCJSRuntimeStats() {
    2195           0 :         for (size_t i = 0; i != compartmentStatsVector.length(); ++i)
    2196           0 :             delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
    2197             : 
    2198             : 
    2199           0 :         for (size_t i = 0; i != zoneStatsVector.length(); ++i)
    2200           0 :             delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
    2201           0 :     }
    2202             : 
    2203           0 :     virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats) override {
    2204             :         // Get the compartment's global.
    2205           0 :         AutoSafeJSContext cx;
    2206           0 :         JSCompartment* comp = js::GetAnyCompartmentInZone(zone);
    2207           0 :         xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
    2208           0 :         extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
    2209           0 :         RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, comp));
    2210           0 :         if (global) {
    2211           0 :             RefPtr<nsGlobalWindow> window;
    2212           0 :             if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
    2213             :                 // The global is a |window| object.  Use the path prefix that
    2214             :                 // we should have already created for it.
    2215           0 :                 if (mTopWindowPaths->Get(window->WindowID(),
    2216             :                                          &extras->pathPrefix))
    2217           0 :                     extras->pathPrefix.AppendLiteral("/js-");
    2218             :             }
    2219             :         }
    2220             : 
    2221           0 :         extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)zone);
    2222             : 
    2223           0 :         MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
    2224             : 
    2225           0 :         zStats->extra = extras;
    2226           0 :     }
    2227             : 
    2228           0 :     virtual void initExtraCompartmentStats(JSCompartment* c,
    2229             :                                            JS::CompartmentStats* cstats) override
    2230             :     {
    2231           0 :         xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras;
    2232           0 :         nsCString cName;
    2233           0 :         GetCompartmentName(c, cName, &mAnonymizeID, /* replaceSlashes = */ true);
    2234           0 :         CompartmentPrivate* cp = CompartmentPrivate::Get(c);
    2235           0 :         if (cp) {
    2236           0 :             if (mGetLocations) {
    2237           0 :                 cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
    2238           0 :                                    getter_AddRefs(extras->location));
    2239             :             }
    2240             :             // Note: cannot use amIAddonManager implementation at this point,
    2241             :             // as it is a JS service and the JS heap is currently not idle.
    2242             :             // Otherwise, we could have computed the add-on id at this point.
    2243             :         }
    2244             : 
    2245             :         // Get the compartment's global.
    2246           0 :         AutoSafeJSContext cx;
    2247           0 :         bool needZone = true;
    2248           0 :         RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, c));
    2249           0 :         if (global) {
    2250           0 :             RefPtr<nsGlobalWindow> window;
    2251           0 :             if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
    2252             :                 // The global is a |window| object.  Use the path prefix that
    2253             :                 // we should have already created for it.
    2254           0 :                 if (mWindowPaths->Get(window->WindowID(),
    2255             :                                       &extras->jsPathPrefix)) {
    2256           0 :                     extras->domPathPrefix.Assign(extras->jsPathPrefix);
    2257           0 :                     extras->domPathPrefix.AppendLiteral("/dom/");
    2258           0 :                     extras->jsPathPrefix.AppendLiteral("/js-");
    2259           0 :                     needZone = false;
    2260             :                 } else {
    2261           0 :                     extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
    2262           0 :                     extras->domPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/");
    2263             :                 }
    2264             :             } else {
    2265           0 :                 extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
    2266           0 :                 extras->domPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/");
    2267             :             }
    2268             :         } else {
    2269           0 :             extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
    2270           0 :             extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
    2271             :         }
    2272             : 
    2273           0 :         if (needZone)
    2274           0 :             extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void*)js::GetCompartmentZone(c));
    2275             : 
    2276           0 :         extras->jsPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/");
    2277             : 
    2278             :         // extras->jsPathPrefix is used for almost all the compartment-specific
    2279             :         // reports. At this point it has the form
    2280             :         // "<something>compartment(<cname>)/".
    2281             :         //
    2282             :         // extras->domPathPrefix is used for DOM orphan nodes, which are
    2283             :         // counted by the JS reporter but reported as part of the DOM
    2284             :         // measurements. At this point it has the form "<something>/dom/" if
    2285             :         // this compartment belongs to an nsGlobalWindow, and
    2286             :         // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
    2287             :         // be used, because non-nsGlobalWindow compartments shouldn't have
    2288             :         // orphan DOM nodes).
    2289             : 
    2290           0 :         MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
    2291           0 :         MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
    2292             : 
    2293           0 :         cstats->extra = extras;
    2294           0 :     }
    2295             : };
    2296             : 
    2297             : void
    2298           0 : JSReporter::CollectReports(WindowPaths* windowPaths,
    2299             :                            WindowPaths* topWindowPaths,
    2300             :                            nsIHandleReportCallback* handleReport,
    2301             :                            nsISupports* data,
    2302             :                            bool anonymize)
    2303             : {
    2304           0 :     XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
    2305             : 
    2306             :     // In the first step we get all the stats and stash them in a local
    2307             :     // data structure.  In the second step we pass all the stashed stats to
    2308             :     // the callback.  Separating these steps is important because the
    2309             :     // callback may be a JS function, and executing JS while getting these
    2310             :     // stats seems like a bad idea.
    2311             : 
    2312           0 :     nsCOMPtr<amIAddonManager> addonManager;
    2313           0 :     if (XRE_IsParentProcess()) {
    2314             :         // Only try to access the service from the main process.
    2315           0 :         addonManager = do_GetService("@mozilla.org/addons/integration;1");
    2316             :     }
    2317           0 :     bool getLocations = !!addonManager;
    2318             :     XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations,
    2319           0 :                               anonymize);
    2320           0 :     OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
    2321           0 :     JSContext* cx = XPCJSContext::Get()->Context();
    2322           0 :     if (!JS::CollectRuntimeStats(cx, &rtStats, &orphanReporter,
    2323             :                                  anonymize))
    2324             :     {
    2325           0 :         return;
    2326             :     }
    2327             : 
    2328           0 :     JS::CollectTraceLoggerStateStats(&rtStats);
    2329             : 
    2330           0 :     size_t xpcJSRuntimeSize = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
    2331             : 
    2332           0 :     size_t wrappedJSSize = xpcrt->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
    2333             : 
    2334           0 :     XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
    2335           0 :     XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
    2336             : 
    2337           0 :     mozJSComponentLoader* loader = mozJSComponentLoader::Get();
    2338           0 :     size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
    2339             : 
    2340             :     // This is the second step (see above).  First we report stuff in the
    2341             :     // "explicit" tree, then we report other stuff.
    2342             : 
    2343           0 :     size_t rtTotal = 0;
    2344           0 :     xpc::ReportJSRuntimeExplicitTreeStats(rtStats,
    2345           0 :                                           NS_LITERAL_CSTRING("explicit/js-non-window/"),
    2346             :                                           addonManager, handleReport, data,
    2347           0 :                                           anonymize, &rtTotal);
    2348             : 
    2349             :     // Report the sums of the compartment numbers.
    2350           0 :     xpc::CompartmentStatsExtras cExtrasTotal;
    2351           0 :     cExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/compartments/");
    2352           0 :     cExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
    2353           0 :     ReportCompartmentStats(rtStats.cTotals, cExtrasTotal, addonManager,
    2354           0 :                            handleReport, data);
    2355             : 
    2356           0 :     xpc::ZoneStatsExtras zExtrasTotal;
    2357           0 :     zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
    2358           0 :     ReportZoneStats(rtStats.zTotals, zExtrasTotal, handleReport, data,
    2359           0 :                     anonymize);
    2360             : 
    2361             :     // Report the sum of the runtime/ numbers.
    2362           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
    2363             :         KIND_OTHER, rtTotal,
    2364             :         "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
    2365             : 
    2366             :     // Report the numbers for memory used by tracelogger.
    2367           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("tracelogger"),
    2368             :         KIND_OTHER, rtStats.runtime.tracelogger,
    2369             :         "The memory used for the tracelogger, including the graph and events.");
    2370             : 
    2371             :     // Report the numbers for memory outside of compartments.
    2372             : 
    2373           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
    2374             :         KIND_OTHER, rtStats.gcHeapUnusedChunks,
    2375             :         "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
    2376             : 
    2377           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
    2378             :         KIND_OTHER, rtStats.gcHeapUnusedArenas,
    2379             :         "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
    2380             : 
    2381           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
    2382             :         KIND_OTHER, rtStats.gcHeapChunkAdmin,
    2383             :         "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
    2384             : 
    2385             :     // Report a breakdown of the committed GC space.
    2386             : 
    2387           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
    2388             :         KIND_OTHER, rtStats.gcHeapUnusedChunks,
    2389             :         "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
    2390             : 
    2391           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
    2392             :         KIND_OTHER, rtStats.gcHeapUnusedArenas,
    2393             :         "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
    2394             : 
    2395           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/objects"),
    2396             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.object,
    2397             :         "Unused object cells within non-empty arenas.");
    2398             : 
    2399           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/strings"),
    2400             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.string,
    2401             :         "Unused string cells within non-empty arenas.");
    2402             : 
    2403           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/symbols"),
    2404             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.symbol,
    2405             :         "Unused symbol cells within non-empty arenas.");
    2406             : 
    2407           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/shapes"),
    2408             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.shape,
    2409             :         "Unused shape cells within non-empty arenas.");
    2410             : 
    2411           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/base-shapes"),
    2412             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.baseShape,
    2413             :         "Unused base shape cells within non-empty arenas.");
    2414             : 
    2415           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/object-groups"),
    2416             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.objectGroup,
    2417             :         "Unused object group cells within non-empty arenas.");
    2418             : 
    2419           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/scopes"),
    2420             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.scope,
    2421             :         "Unused scope cells within non-empty arenas.");
    2422             : 
    2423           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/scripts"),
    2424             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.script,
    2425             :         "Unused script cells within non-empty arenas.");
    2426             : 
    2427           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/lazy-scripts"),
    2428             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.lazyScript,
    2429             :         "Unused lazy script cells within non-empty arenas.");
    2430             : 
    2431           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/jitcode"),
    2432             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode,
    2433             :         "Unused jitcode cells within non-empty arenas.");
    2434             : 
    2435           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/regexp-shareds"),
    2436             :         KIND_OTHER, rtStats.zTotals.unusedGCThings.regExpShared,
    2437             :         "Unused regexpshared cells within non-empty arenas.");
    2438             : 
    2439           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
    2440             :         KIND_OTHER, rtStats.gcHeapChunkAdmin,
    2441             :         "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
    2442             : 
    2443           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
    2444             :         KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
    2445             :         "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
    2446             : 
    2447           0 :     size_t gcThingTotal = 0;
    2448             : 
    2449           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/objects"),
    2450             :         KIND_OTHER, rtStats.cTotals.classInfo.objectsGCHeap,
    2451             :         "Used object cells.");
    2452             : 
    2453           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/strings"),
    2454             :         KIND_OTHER, rtStats.zTotals.stringInfo.sizeOfLiveGCThings(),
    2455             :         "Used string cells.");
    2456             : 
    2457           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/symbols"),
    2458             :         KIND_OTHER, rtStats.zTotals.symbolsGCHeap,
    2459             :         "Used symbol cells.");
    2460             : 
    2461           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/shapes"),
    2462             :         KIND_OTHER,
    2463             :         rtStats.zTotals.shapeInfo.shapesGCHeapTree + rtStats.zTotals.shapeInfo.shapesGCHeapDict,
    2464             :         "Used shape cells.");
    2465             : 
    2466           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/base-shapes"),
    2467             :         KIND_OTHER, rtStats.zTotals.shapeInfo.shapesGCHeapBase,
    2468             :         "Used base shape cells.");
    2469             : 
    2470           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/object-groups"),
    2471             :         KIND_OTHER, rtStats.zTotals.objectGroupsGCHeap,
    2472             :         "Used object group cells.");
    2473             : 
    2474           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/scopes"),
    2475             :         KIND_OTHER, rtStats.zTotals.scopesGCHeap,
    2476             :         "Used scope cells.");
    2477             : 
    2478           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/scripts"),
    2479             :         KIND_OTHER, rtStats.cTotals.scriptsGCHeap,
    2480             :         "Used script cells.");
    2481             : 
    2482           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/lazy-scripts"),
    2483             :         KIND_OTHER, rtStats.zTotals.lazyScriptsGCHeap,
    2484             :         "Used lazy script cells.");
    2485             : 
    2486           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/jitcode"),
    2487             :         KIND_OTHER, rtStats.zTotals.jitCodesGCHeap,
    2488             :         "Used jitcode cells.");
    2489             : 
    2490           0 :     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/regexp-shareds"),
    2491             :         KIND_OTHER, rtStats.zTotals.regExpSharedsGCHeap,
    2492             :         "Used regexpshared cells.");
    2493             : 
    2494           0 :     MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings);
    2495             : 
    2496             :     // Report xpconnect.
    2497             : 
    2498           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
    2499             :         KIND_HEAP, xpcJSRuntimeSize,
    2500             :         "The XPConnect runtime.");
    2501             : 
    2502           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/wrappedjs"),
    2503             :         KIND_HEAP, wrappedJSSize,
    2504             :         "Wrappers used to implement XPIDL interfaces with JS.");
    2505             : 
    2506           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"),
    2507             :         KIND_HEAP, sizeInfo.mScopeAndMapSize,
    2508             :         "XPConnect scopes.");
    2509             : 
    2510           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"),
    2511             :         KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize,
    2512             :         "Prototype and interface binding caches.");
    2513             : 
    2514           0 :     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/js-component-loader"),
    2515             :         KIND_HEAP, jsComponentLoaderSize,
    2516             :         "XPConnect's JS component loader.");
    2517             : }
    2518             : 
    2519             : static nsresult
    2520           0 : JSSizeOfTab(JSObject* objArg, size_t* jsObjectsSize, size_t* jsStringsSize,
    2521             :             size_t* jsPrivateSize, size_t* jsOtherSize)
    2522             : {
    2523           0 :     JSContext* cx = XPCJSContext::Get()->Context();
    2524           0 :     JS::RootedObject obj(cx, objArg);
    2525             : 
    2526           0 :     TabSizes sizes;
    2527           0 :     OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
    2528           0 :     NS_ENSURE_TRUE(JS::AddSizeOfTab(cx, obj, moz_malloc_size_of,
    2529             :                                     &orphanReporter, &sizes),
    2530             :                    NS_ERROR_OUT_OF_MEMORY);
    2531             : 
    2532           0 :     *jsObjectsSize = sizes.objects;
    2533           0 :     *jsStringsSize = sizes.strings;
    2534           0 :     *jsPrivateSize = sizes.private_;
    2535           0 :     *jsOtherSize   = sizes.other;
    2536           0 :     return NS_OK;
    2537             : }
    2538             : 
    2539             : } // namespace xpc
    2540             : 
    2541             : static void
    2542         767 : AccumulateTelemetryCallback(int id, uint32_t sample, const char* key)
    2543             : {
    2544         767 :     switch (id) {
    2545             :       case JS_TELEMETRY_GC_REASON:
    2546           3 :         Telemetry::Accumulate(Telemetry::GC_REASON_2, sample);
    2547           3 :         break;
    2548             :       case JS_TELEMETRY_GC_IS_ZONE_GC:
    2549           0 :         Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample);
    2550           0 :         break;
    2551             :       case JS_TELEMETRY_GC_MS:
    2552           0 :         Telemetry::Accumulate(Telemetry::GC_MS, sample);
    2553           0 :         break;
    2554             :       case JS_TELEMETRY_GC_BUDGET_MS:
    2555           3 :         Telemetry::Accumulate(Telemetry::GC_BUDGET_MS, sample);
    2556           3 :         break;
    2557             :       case JS_TELEMETRY_GC_ANIMATION_MS:
    2558           0 :         Telemetry::Accumulate(Telemetry::GC_ANIMATION_MS, sample);
    2559           0 :         break;
    2560             :       case JS_TELEMETRY_GC_MAX_PAUSE_MS:
    2561           0 :         Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS, sample);
    2562           0 :         break;
    2563             :       case JS_TELEMETRY_GC_MAX_PAUSE_MS_2:
    2564           0 :         Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS_2, sample);
    2565           0 :         break;
    2566             :       case JS_TELEMETRY_GC_MARK_MS:
    2567           0 :         Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample);
    2568           0 :         break;
    2569             :       case JS_TELEMETRY_GC_SWEEP_MS:
    2570           0 :         Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample);
    2571           0 :         break;
    2572             :       case JS_TELEMETRY_GC_COMPACT_MS:
    2573           0 :         Telemetry::Accumulate(Telemetry::GC_COMPACT_MS, sample);
    2574           0 :         break;
    2575             :       case JS_TELEMETRY_GC_MARK_ROOTS_MS:
    2576           0 :         Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample);
    2577           0 :         break;
    2578             :       case JS_TELEMETRY_GC_MARK_GRAY_MS:
    2579           0 :         Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample);
    2580           0 :         break;
    2581             :       case JS_TELEMETRY_GC_SLICE_MS:
    2582           3 :         Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
    2583           3 :         break;
    2584             :       case JS_TELEMETRY_GC_SLOW_PHASE:
    2585           1 :         Telemetry::Accumulate(Telemetry::GC_SLOW_PHASE, sample);
    2586           1 :         break;
    2587             :       case JS_TELEMETRY_GC_SLOW_TASK:
    2588           0 :         Telemetry::Accumulate(Telemetry::GC_SLOW_TASK, sample);
    2589           0 :         break;
    2590             :       case JS_TELEMETRY_GC_MMU_50:
    2591           0 :         Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
    2592           0 :         break;
    2593             :       case JS_TELEMETRY_GC_RESET:
    2594           3 :         Telemetry::Accumulate(Telemetry::GC_RESET, sample);
    2595           3 :         break;
    2596             :       case JS_TELEMETRY_GC_RESET_REASON:
    2597           0 :         Telemetry::Accumulate(Telemetry::GC_RESET_REASON, sample);
    2598           0 :         break;
    2599             :       case JS_TELEMETRY_GC_INCREMENTAL_DISABLED:
    2600           0 :         Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample);
    2601           0 :         break;
    2602             :       case JS_TELEMETRY_GC_NON_INCREMENTAL:
    2603           0 :         Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample);
    2604           0 :         break;
    2605             :       case JS_TELEMETRY_GC_NON_INCREMENTAL_REASON:
    2606           0 :         Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL_REASON, sample);
    2607           0 :         break;
    2608             :       case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS:
    2609           0 :         Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample);
    2610           0 :         break;
    2611             :       case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS:
    2612           0 :         Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample);
    2613           0 :         break;
    2614             :       case JS_TELEMETRY_GC_MINOR_REASON:
    2615          24 :         Telemetry::Accumulate(Telemetry::GC_MINOR_REASON, sample);
    2616          24 :         break;
    2617             :       case JS_TELEMETRY_GC_MINOR_REASON_LONG:
    2618          21 :         Telemetry::Accumulate(Telemetry::GC_MINOR_REASON_LONG, sample);
    2619          21 :         break;
    2620             :       case JS_TELEMETRY_GC_MINOR_US:
    2621          24 :         Telemetry::Accumulate(Telemetry::GC_MINOR_US, sample);
    2622          24 :         break;
    2623             :       case JS_TELEMETRY_GC_NURSERY_BYTES:
    2624          24 :         Telemetry::Accumulate(Telemetry::GC_NURSERY_BYTES, sample);
    2625          24 :         break;
    2626             :       case JS_TELEMETRY_GC_PRETENURE_COUNT:
    2627          24 :         Telemetry::Accumulate(Telemetry::GC_PRETENURE_COUNT, sample);
    2628          24 :         break;
    2629             :       case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT:
    2630           0 :         Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, sample);
    2631           0 :         break;
    2632             :       case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS:
    2633           0 :         Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, sample);
    2634           0 :         break;
    2635             :       case JS_TELEMETRY_ADDON_EXCEPTIONS:
    2636           0 :         Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample);
    2637           0 :         break;
    2638             :       case JS_TELEMETRY_AOT_USAGE:
    2639           0 :         Telemetry::Accumulate(Telemetry::JS_AOT_USAGE, sample);
    2640           0 :         break;
    2641             :       case JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS:
    2642         273 :         Telemetry::Accumulate(Telemetry::JS_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS, sample);
    2643         273 :         break;
    2644             :       case JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS:
    2645         364 :         Telemetry::Accumulate(Telemetry::JS_WEB_PARSER_COMPILE_LAZY_AFTER_MS, sample);
    2646         364 :         break;
    2647             :       default:
    2648           0 :         MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id");
    2649             :     }
    2650         767 : }
    2651             : 
    2652             : static void
    2653           0 : CompartmentNameCallback(JSContext* cx, JSCompartment* comp,
    2654             :                         char* buf, size_t bufsize)
    2655             : {
    2656           0 :     nsCString name;
    2657             :     // This is called via the JSAPI and isn't involved in memory reporting, so
    2658             :     // we don't need to anonymize compartment names.
    2659           0 :     int anonymizeID = 0;
    2660           0 :     GetCompartmentName(comp, name, &anonymizeID, /* replaceSlashes = */ false);
    2661           0 :     if (name.Length() >= bufsize)
    2662           0 :         name.Truncate(bufsize - 1);
    2663           0 :     memcpy(buf, name.get(), name.Length() + 1);
    2664           0 : }
    2665             : 
    2666             : static bool
    2667          13 : PreserveWrapper(JSContext* cx, JSObject* obj)
    2668             : {
    2669          13 :     MOZ_ASSERT(cx);
    2670          13 :     MOZ_ASSERT(obj);
    2671          13 :     MOZ_ASSERT(IS_WN_REFLECTOR(obj) || mozilla::dom::IsDOMObject(obj));
    2672             : 
    2673          13 :     return mozilla::dom::IsDOMObject(obj) && mozilla::dom::TryPreserveWrapper(obj);
    2674             : }
    2675             : 
    2676             : static nsresult
    2677           2 : ReadSourceFromFilename(JSContext* cx, const char* filename, char16_t** src, size_t* len)
    2678             : {
    2679             :     nsresult rv;
    2680             : 
    2681             :     // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with
    2682             :     // the filename of its caller. Axe that if present.
    2683             :     const char* arrow;
    2684           3 :     while ((arrow = strstr(filename, " -> ")))
    2685           1 :         filename = arrow + strlen(" -> ");
    2686             : 
    2687             :     // Get the URI.
    2688           2 :     nsCOMPtr<nsIURI> uri;
    2689           1 :     rv = NS_NewURI(getter_AddRefs(uri), filename);
    2690           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2691             : 
    2692           2 :     nsCOMPtr<nsIChannel> scriptChannel;
    2693           2 :     rv = NS_NewChannel(getter_AddRefs(scriptChannel),
    2694             :                        uri,
    2695             :                        nsContentUtils::GetSystemPrincipal(),
    2696             :                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
    2697           1 :                        nsIContentPolicy::TYPE_OTHER);
    2698           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2699             : 
    2700             :     // Only allow local reading.
    2701           2 :     nsCOMPtr<nsIURI> actualUri;
    2702           1 :     rv = scriptChannel->GetURI(getter_AddRefs(actualUri));
    2703           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2704           2 :     nsCString scheme;
    2705           1 :     rv = actualUri->GetScheme(scheme);
    2706           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2707           1 :     if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar"))
    2708           0 :         return NS_OK;
    2709             : 
    2710             :     // Explicitly set the content type so that we don't load the
    2711             :     // exthandler to guess it.
    2712           1 :     scriptChannel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
    2713             : 
    2714           2 :     nsCOMPtr<nsIInputStream> scriptStream;
    2715           1 :     rv = scriptChannel->Open2(getter_AddRefs(scriptStream));
    2716           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2717             : 
    2718             :     uint64_t rawLen;
    2719           1 :     rv = scriptStream->Available(&rawLen);
    2720           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2721           1 :     if (!rawLen)
    2722           0 :         return NS_ERROR_FAILURE;
    2723             : 
    2724             :     // Technically, this should be SIZE_MAX, but we don't run on machines
    2725             :     // where that would be less than UINT32_MAX, and the latter is already
    2726             :     // well beyond a reasonable limit.
    2727           1 :     if (rawLen > UINT32_MAX)
    2728           0 :         return NS_ERROR_FILE_TOO_BIG;
    2729             : 
    2730             :     // Allocate an internal buf the size of the file.
    2731           2 :     auto buf = MakeUniqueFallible<unsigned char[]>(rawLen);
    2732           1 :     if (!buf)
    2733           0 :         return NS_ERROR_OUT_OF_MEMORY;
    2734             : 
    2735           1 :     unsigned char* ptr = buf.get();
    2736           1 :     unsigned char* end = ptr + rawLen;
    2737           3 :     while (ptr < end) {
    2738             :         uint32_t bytesRead;
    2739           1 :         rv = scriptStream->Read(reinterpret_cast<char*>(ptr), end - ptr, &bytesRead);
    2740           1 :         if (NS_FAILED(rv))
    2741           0 :             return rv;
    2742           1 :         MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF");
    2743           1 :         ptr += bytesRead;
    2744             :     }
    2745             : 
    2746           1 :     rv = ScriptLoader::ConvertToUTF16(scriptChannel, buf.get(), rawLen,
    2747           2 :                                       EmptyString(), nullptr, *src, *len);
    2748           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2749             : 
    2750           1 :     if (!*src)
    2751           0 :         return NS_ERROR_FAILURE;
    2752             : 
    2753             :     // Historically this method used JS_malloc() which updates the GC memory
    2754             :     // accounting.  Since ConvertToUTF16() now uses js_malloc() instead we
    2755             :     // update the accounting manually after the fact.
    2756           1 :     JS_updateMallocCounter(cx, *len);
    2757             : 
    2758           1 :     return NS_OK;
    2759             : }
    2760             : 
    2761             : // The JS engine calls this object's 'load' member function when it needs
    2762             : // the source for a chrome JS function. See the comment in the XPCJSRuntime
    2763             : // constructor.
    2764           3 : class XPCJSSourceHook: public js::SourceHook {
    2765           1 :     bool load(JSContext* cx, const char* filename, char16_t** src, size_t* length) {
    2766           1 :         *src = nullptr;
    2767           1 :         *length = 0;
    2768             : 
    2769           1 :         if (!nsContentUtils::IsSystemCaller(cx))
    2770           0 :             return true;
    2771             : 
    2772           1 :         if (!filename)
    2773           0 :             return true;
    2774             : 
    2775           1 :         nsresult rv = ReadSourceFromFilename(cx, filename, src, length);
    2776           1 :         if (NS_FAILED(rv)) {
    2777           0 :             xpc::Throw(cx, rv);
    2778           0 :             return false;
    2779             :         }
    2780             : 
    2781           1 :         return true;
    2782             :     }
    2783             : };
    2784             : 
    2785             : static const JSWrapObjectCallbacks WrapObjectCallbacks = {
    2786             :     xpc::WrapperFactory::Rewrap,
    2787             :     xpc::WrapperFactory::PrepareForWrapping
    2788             : };
    2789             : 
    2790           3 : XPCJSRuntime::XPCJSRuntime(JSContext* aCx)
    2791             :  : CycleCollectedJSRuntime(aCx),
    2792           3 :    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH)),
    2793           3 :    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_LENGTH)),
    2794           3 :    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_LENGTH)),
    2795           3 :    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_LENGTH)),
    2796           3 :    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_LENGTH)),
    2797           3 :    mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_LENGTH)),
    2798           3 :    mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_LENGTH)),
    2799             :    mGCIsRunning(false),
    2800             :    mNativesToReleaseArray(),
    2801             :    mDoingFinalization(false),
    2802             :    mVariantRoots(nullptr),
    2803             :    mWrappedJSRoots(nullptr),
    2804          27 :    mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
    2805             : {
    2806           3 :     MOZ_COUNT_CTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
    2807           3 : }
    2808             : 
    2809             : /* static */
    2810             : XPCJSRuntime*
    2811       36382 : XPCJSRuntime::Get()
    2812             : {
    2813       36382 :     return nsXPConnect::GetRuntimeInstance();
    2814             : }
    2815             : 
    2816             : void
    2817           3 : XPCJSRuntime::Initialize(JSContext* cx)
    2818             : {
    2819           3 :     mUnprivilegedJunkScope.init(cx, nullptr);
    2820           3 :     mPrivilegedJunkScope.init(cx, nullptr);
    2821           3 :     mCompilationScope.init(cx, nullptr);
    2822             : 
    2823             :     // these jsids filled in later when we have a JSContext to work with.
    2824           3 :     mStrIDs[0] = JSID_VOID;
    2825             : 
    2826             :     // Unconstrain the runtime's threshold on nominal heap size, to avoid
    2827             :     // triggering GC too often if operating continuously near an arbitrary
    2828             :     // finite threshold (0xffffffff is infinity for uint32_t parameters).
    2829             :     // This leaves the maximum-JS_malloc-bytes threshold still in effect
    2830             :     // to cause period, and we hope hygienic, last-ditch GCs from within
    2831             :     // the GC's allocator.
    2832           3 :     JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff);
    2833             : 
    2834           3 :     JS_SetDestroyCompartmentCallback(cx, CompartmentDestroyedCallback);
    2835           3 :     JS_SetSizeOfIncludingThisCompartmentCallback(cx, CompartmentSizeOfIncludingThisCallback);
    2836           3 :     JS_SetCompartmentNameCallback(cx, CompartmentNameCallback);
    2837           3 :     mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
    2838           3 :     mPrevDoCycleCollectionCallback = JS::SetDoCycleCollectionCallback(cx,
    2839             :             DoCycleCollectionCallback);
    2840           3 :     JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
    2841           3 :     JS_AddWeakPointerZonesCallback(cx, WeakPointerZonesCallback, this);
    2842           3 :     JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this);
    2843           3 :     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
    2844           3 :     js::SetPreserveWrapperCallback(cx, PreserveWrapper);
    2845           3 :     JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
    2846           3 :     js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
    2847           3 :     JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback);
    2848             : 
    2849             :     // The JS engine needs to keep the source code around in order to implement
    2850             :     // Function.prototype.toSource(). It'd be nice to not have to do this for
    2851             :     // chrome code and simply stub out requests for source on it. Life is not so
    2852             :     // easy, unfortunately. Nobody relies on chrome toSource() working in core
    2853             :     // browser code, but chrome tests use it. The worst offenders are addons,
    2854             :     // which like to monkeypatch chrome functions by calling toSource() on them
    2855             :     // and using regular expressions to modify them. We avoid keeping most browser
    2856             :     // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when
    2857             :     // compiling some chrome code. This causes the JS engine not save the source
    2858             :     // code in memory. When the JS engine is asked to provide the source for a
    2859             :     // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
    2860             :     ///
    2861             :     // Note we do have to retain the source code in memory for scripts compiled in
    2862             :     // isRunOnce mode and compiled function bodies (from
    2863             :     // JS::CompileFunction). In practice, this means content scripts and event
    2864             :     // handlers.
    2865           6 :     mozilla::UniquePtr<XPCJSSourceHook> hook(new XPCJSSourceHook);
    2866           3 :     js::SetSourceHook(cx, Move(hook));
    2867             : 
    2868             :     // Register memory reporters and distinguished amount functions.
    2869           3 :     RegisterStrongMemoryReporter(new JSMainRuntimeCompartmentsReporter());
    2870           3 :     RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
    2871           3 :     RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
    2872           3 :     RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
    2873           3 :     RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
    2874           3 :     RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
    2875           3 :     mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
    2876           3 : }
    2877             : 
    2878             : bool
    2879           3 : XPCJSRuntime::InitializeStrings(JSContext* cx)
    2880             : {
    2881           6 :     JSAutoRequest ar(cx);
    2882             : 
    2883             :     // if it is our first context then we need to generate our string ids
    2884           3 :     if (JSID_IS_VOID(mStrIDs[0])) {
    2885           6 :         RootedString str(cx);
    2886          96 :         for (unsigned i = 0; i < XPCJSContext::IDX_TOTAL_COUNT; i++) {
    2887          93 :             str = JS_AtomizeAndPinString(cx, mStrings[i]);
    2888          93 :             if (!str) {
    2889           0 :                 mStrIDs[0] = JSID_VOID;
    2890           0 :                 return false;
    2891             :             }
    2892          93 :             mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str);
    2893          93 :             mStrJSVals[i].setString(str);
    2894             :         }
    2895             : 
    2896           3 :         if (!mozilla::dom::DefineStaticJSVals(cx)) {
    2897           0 :             return false;
    2898             :         }
    2899             :     }
    2900             : 
    2901           3 :     return true;
    2902             : }
    2903             : 
    2904             : bool
    2905           0 : XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
    2906             :                                     char (&name)[72]) const
    2907             : {
    2908             : 
    2909           0 :     if (!IS_PROTO_CLASS(clasp)) {
    2910           0 :         return false;
    2911             :     }
    2912             : 
    2913             :     XPCWrappedNativeProto* p =
    2914           0 :         static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
    2915           0 :     nsCOMPtr<nsIXPCScriptable> scr = p->GetScriptable();
    2916           0 :     if (!scr) {
    2917           0 :         return false;
    2918             :     }
    2919             : 
    2920             :     SprintfLiteral(name, "JS Object (%s - %s)",
    2921           0 :                    clasp->name, scr->GetJSClass()->name);
    2922           0 :     return true;
    2923             : }
    2924             : 
    2925             : bool
    2926           0 : XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
    2927             :                                              nsCycleCollectionTraversalCallback& cb) const
    2928             : {
    2929           0 :     if (clasp != &XPC_WN_Tearoff_JSClass) {
    2930           0 :         return false;
    2931             :     }
    2932             : 
    2933             :     // A tearoff holds a strong reference to its native object
    2934             :     // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
    2935             :     // will be held alive through the parent of the JSObject of the tearoff.
    2936             :     XPCWrappedNativeTearOff* to =
    2937           0 :         static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj));
    2938           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
    2939           0 :     cb.NoteXPCOMChild(to->GetNative());
    2940           0 :     return true;
    2941             : }
    2942             : 
    2943             : /***************************************************************************/
    2944             : 
    2945             : void
    2946           0 : XPCJSRuntime::DebugDump(int16_t depth)
    2947             : {
    2948             : #ifdef DEBUG
    2949           0 :     depth--;
    2950           0 :     XPC_LOG_ALWAYS(("XPCJSRuntime @ %p", this));
    2951           0 :         XPC_LOG_INDENT();
    2952             : 
    2953           0 :         XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %p with %d wrapperclasses(s)",
    2954             :                         mWrappedJSClassMap, mWrappedJSClassMap->Count()));
    2955             :         // iterate wrappersclasses...
    2956           0 :         if (depth && mWrappedJSClassMap->Count()) {
    2957           0 :             XPC_LOG_INDENT();
    2958           0 :             for (auto i = mWrappedJSClassMap->Iter(); !i.Done(); i.Next()) {
    2959           0 :                 auto entry = static_cast<IID2WrappedJSClassMap::Entry*>(i.Get());
    2960           0 :                 entry->value->DebugDump(depth);
    2961             :             }
    2962           0 :             XPC_LOG_OUTDENT();
    2963             :         }
    2964             : 
    2965             :         // iterate wrappers...
    2966           0 :         XPC_LOG_ALWAYS(("mWrappedJSMap @ %p with %d wrappers(s)",
    2967             :                         mWrappedJSMap, mWrappedJSMap->Count()));
    2968           0 :         if (depth && mWrappedJSMap->Count()) {
    2969           0 :             XPC_LOG_INDENT();
    2970           0 :             mWrappedJSMap->Dump(depth);
    2971           0 :             XPC_LOG_OUTDENT();
    2972             :         }
    2973             : 
    2974           0 :         XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %p with %d interface(s)",
    2975             :                         mIID2NativeInterfaceMap,
    2976             :                         mIID2NativeInterfaceMap->Count()));
    2977             : 
    2978           0 :         XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %p with %d sets(s)",
    2979             :                         mClassInfo2NativeSetMap,
    2980             :                         mClassInfo2NativeSetMap->Count()));
    2981             : 
    2982           0 :         XPC_LOG_ALWAYS(("mThisTranslatorMap @ %p with %d translator(s)",
    2983             :                         mThisTranslatorMap, mThisTranslatorMap->Count()));
    2984             : 
    2985           0 :         XPC_LOG_ALWAYS(("mNativeSetMap @ %p with %d sets(s)",
    2986             :                         mNativeSetMap, mNativeSetMap->Count()));
    2987             : 
    2988             :         // iterate sets...
    2989           0 :         if (depth && mNativeSetMap->Count()) {
    2990           0 :             XPC_LOG_INDENT();
    2991           0 :             for (auto i = mNativeSetMap->Iter(); !i.Done(); i.Next()) {
    2992           0 :                 auto entry = static_cast<NativeSetMap::Entry*>(i.Get());
    2993           0 :                 entry->key_value->DebugDump(depth);
    2994             :             }
    2995           0 :             XPC_LOG_OUTDENT();
    2996             :         }
    2997             : 
    2998           0 :         XPC_LOG_OUTDENT();
    2999             : #endif
    3000           0 : }
    3001             : 
    3002             : /***************************************************************************/
    3003             : 
    3004             : void
    3005         978 : XPCRootSetElem::AddToRootSet(XPCRootSetElem** listHead)
    3006             : {
    3007         978 :     MOZ_ASSERT(!mSelfp, "Must be not linked");
    3008             : 
    3009         978 :     mSelfp = listHead;
    3010         978 :     mNext = *listHead;
    3011         978 :     if (mNext) {
    3012         974 :         MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start");
    3013         974 :         mNext->mSelfp = &mNext;
    3014             :     }
    3015         978 :     *listHead = this;
    3016         978 : }
    3017             : 
    3018             : void
    3019         218 : XPCRootSetElem::RemoveFromRootSet()
    3020             : {
    3021         218 :     JS::NotifyGCRootsRemoved(XPCJSContext::Get()->Context());
    3022             : 
    3023         218 :     MOZ_ASSERT(mSelfp, "Must be linked");
    3024             : 
    3025         218 :     MOZ_ASSERT(*mSelfp == this, "Link invariant");
    3026         218 :     *mSelfp = mNext;
    3027         218 :     if (mNext)
    3028         217 :         mNext->mSelfp = mSelfp;
    3029             : #ifdef DEBUG
    3030         218 :     mSelfp = nullptr;
    3031         218 :     mNext = nullptr;
    3032             : #endif
    3033         218 : }
    3034             : 
    3035             : void
    3036           0 : XPCJSRuntime::AddGCCallback(xpcGCCallback cb)
    3037             : {
    3038           0 :     MOZ_ASSERT(cb, "null callback");
    3039           0 :     extraGCCallbacks.AppendElement(cb);
    3040           0 : }
    3041             : 
    3042             : void
    3043           0 : XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
    3044             : {
    3045           0 :     MOZ_ASSERT(cb, "null callback");
    3046           0 :     bool found = extraGCCallbacks.RemoveElement(cb);
    3047           0 :     if (!found) {
    3048           0 :         NS_ERROR("Removing a callback which was never added.");
    3049             :     }
    3050           0 : }
    3051             : 
    3052             : void
    3053           3 : XPCJSRuntime::InitSingletonScopes()
    3054             : {
    3055             :     // This all happens very early, so we don't bother with cx pushing.
    3056           3 :     JSContext* cx = XPCJSContext::Get()->Context();
    3057           6 :     JSAutoRequest ar(cx);
    3058           6 :     RootedValue v(cx);
    3059             :     nsresult rv;
    3060             : 
    3061             :     // Create the Unprivileged Junk Scope.
    3062           6 :     SandboxOptions unprivilegedJunkScopeOptions;
    3063           3 :     unprivilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Junk Compartment");
    3064           3 :     unprivilegedJunkScopeOptions.invisibleToDebugger = true;
    3065           3 :     rv = CreateSandboxObject(cx, &v, nullptr, unprivilegedJunkScopeOptions);
    3066           3 :     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
    3067           3 :     mUnprivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
    3068             : 
    3069             :     // Create the Privileged Junk Scope.
    3070           6 :     SandboxOptions privilegedJunkScopeOptions;
    3071           3 :     privilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Privileged Junk Compartment");
    3072           3 :     privilegedJunkScopeOptions.invisibleToDebugger = true;
    3073           3 :     privilegedJunkScopeOptions.wantComponents = false;
    3074           3 :     rv = CreateSandboxObject(cx, &v, nsXPConnect::SystemPrincipal(), privilegedJunkScopeOptions);
    3075           3 :     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
    3076           3 :     mPrivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
    3077             : 
    3078             :     // Create the Compilation Scope.
    3079           6 :     SandboxOptions compilationScopeOptions;
    3080           3 :     compilationScopeOptions.sandboxName.AssignLiteral("XPConnect Compilation Compartment");
    3081           3 :     compilationScopeOptions.invisibleToDebugger = true;
    3082           3 :     compilationScopeOptions.discardSource = ShouldDiscardSystemSource();
    3083           3 :     rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, compilationScopeOptions);
    3084           3 :     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
    3085           3 :     mCompilationScope = js::UncheckedUnwrap(&v.toObject());
    3086           3 : }
    3087             : 
    3088             : void
    3089           0 : XPCJSRuntime::DeleteSingletonScopes()
    3090             : {
    3091           0 :     mUnprivilegedJunkScope = nullptr;
    3092           0 :     mPrivilegedJunkScope = nullptr;
    3093           0 :     mCompilationScope = nullptr;
    3094           0 : }

Generated by: LCOV version 1.13