js/xpconnect/src/XPCJSRuntime.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/src/XPCJSRuntime.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3524 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/* Per JSRuntime object */
    1.11 +
    1.12 +#include "mozilla/MemoryReporting.h"
    1.13 +
    1.14 +#include "xpcprivate.h"
    1.15 +#include "xpcpublic.h"
    1.16 +#include "XPCWrapper.h"
    1.17 +#include "XPCJSMemoryReporter.h"
    1.18 +#include "WrapperFactory.h"
    1.19 +#include "dom_quickstubs.h"
    1.20 +#include "mozJSComponentLoader.h"
    1.21 +
    1.22 +#include "nsIMemoryInfoDumper.h"
    1.23 +#include "nsIMemoryReporter.h"
    1.24 +#include "nsIObserverService.h"
    1.25 +#include "nsIDebug2.h"
    1.26 +#include "amIAddonManager.h"
    1.27 +#include "nsPIDOMWindow.h"
    1.28 +#include "nsPrintfCString.h"
    1.29 +#include "mozilla/Preferences.h"
    1.30 +#include "mozilla/Telemetry.h"
    1.31 +#include "mozilla/Services.h"
    1.32 +
    1.33 +#include "nsContentUtils.h"
    1.34 +#include "nsCxPusher.h"
    1.35 +#include "nsCCUncollectableMarker.h"
    1.36 +#include "nsCycleCollectionNoteRootCallback.h"
    1.37 +#include "nsScriptLoader.h"
    1.38 +#include "jsfriendapi.h"
    1.39 +#include "jsprf.h"
    1.40 +#include "js/MemoryMetrics.h"
    1.41 +#include "js/OldDebugAPI.h"
    1.42 +#include "mozilla/dom/GeneratedAtomList.h"
    1.43 +#include "mozilla/dom/BindingUtils.h"
    1.44 +#include "mozilla/dom/Element.h"
    1.45 +#include "mozilla/dom/WindowBinding.h"
    1.46 +#include "mozilla/Atomics.h"
    1.47 +#include "mozilla/Attributes.h"
    1.48 +#include "AccessCheck.h"
    1.49 +#include "nsGlobalWindow.h"
    1.50 +#include "nsAboutProtocolUtils.h"
    1.51 +
    1.52 +#include "GeckoProfiler.h"
    1.53 +#include "nsIXULRuntime.h"
    1.54 +#include "nsJSPrincipals.h"
    1.55 +
    1.56 +#ifdef MOZ_CRASHREPORTER
    1.57 +#include "nsExceptionHandler.h"
    1.58 +#endif
    1.59 +
    1.60 +using namespace mozilla;
    1.61 +using namespace xpc;
    1.62 +using namespace JS;
    1.63 +using mozilla::dom::PerThreadAtomCache;
    1.64 +
    1.65 +/***************************************************************************/
    1.66 +
    1.67 +const char* const XPCJSRuntime::mStrings[] = {
    1.68 +    "constructor",          // IDX_CONSTRUCTOR
    1.69 +    "toString",             // IDX_TO_STRING
    1.70 +    "toSource",             // IDX_TO_SOURCE
    1.71 +    "lastResult",           // IDX_LAST_RESULT
    1.72 +    "returnCode",           // IDX_RETURN_CODE
    1.73 +    "value",                // IDX_VALUE
    1.74 +    "QueryInterface",       // IDX_QUERY_INTERFACE
    1.75 +    "Components",           // IDX_COMPONENTS
    1.76 +    "wrappedJSObject",      // IDX_WRAPPED_JSOBJECT
    1.77 +    "Object",               // IDX_OBJECT
    1.78 +    "Function",             // IDX_FUNCTION
    1.79 +    "prototype",            // IDX_PROTOTYPE
    1.80 +    "createInstance",       // IDX_CREATE_INSTANCE
    1.81 +    "item",                 // IDX_ITEM
    1.82 +    "__proto__",            // IDX_PROTO
    1.83 +    "__iterator__",         // IDX_ITERATOR
    1.84 +    "__exposedProps__",     // IDX_EXPOSEDPROPS
    1.85 +    "eval",                 // IDX_EVAL
    1.86 +    "controllers",           // IDX_CONTROLLERS
    1.87 +};
    1.88 +
    1.89 +/***************************************************************************/
    1.90 +
    1.91 +static mozilla::Atomic<bool> sDiscardSystemSource(false);
    1.92 +
    1.93 +bool
    1.94 +xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; }
    1.95 +
    1.96 +static void * const UNMARK_ONLY = nullptr;
    1.97 +static void * const UNMARK_AND_SWEEP = (void *)1;
    1.98 +
    1.99 +static PLDHashOperator
   1.100 +NativeInterfaceSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.101 +                       uint32_t number, void *arg)
   1.102 +{
   1.103 +    XPCNativeInterface* iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value;
   1.104 +    if (iface->IsMarked()) {
   1.105 +        iface->Unmark();
   1.106 +        return PL_DHASH_NEXT;
   1.107 +    }
   1.108 +
   1.109 +    if (arg == UNMARK_ONLY)
   1.110 +        return PL_DHASH_NEXT;
   1.111 +
   1.112 +    XPCNativeInterface::DestroyInstance(iface);
   1.113 +    return PL_DHASH_REMOVE;
   1.114 +}
   1.115 +
   1.116 +// *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
   1.117 +// *All* NativeSets are referenced from mNativeSetMap.
   1.118 +// So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
   1.119 +// In mNativeSetMap we clear the references to the unmarked *and* delete them.
   1.120 +
   1.121 +static PLDHashOperator
   1.122 +NativeUnMarkedSetRemover(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.123 +                         uint32_t number, void *arg)
   1.124 +{
   1.125 +    XPCNativeSet* set = ((ClassInfo2NativeSetMap::Entry*)hdr)->value;
   1.126 +    if (set->IsMarked())
   1.127 +        return PL_DHASH_NEXT;
   1.128 +    return PL_DHASH_REMOVE;
   1.129 +}
   1.130 +
   1.131 +static PLDHashOperator
   1.132 +NativeSetSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.133 +                 uint32_t number, void *arg)
   1.134 +{
   1.135 +    XPCNativeSet* set = ((NativeSetMap::Entry*)hdr)->key_value;
   1.136 +    if (set->IsMarked()) {
   1.137 +        set->Unmark();
   1.138 +        return PL_DHASH_NEXT;
   1.139 +    }
   1.140 +
   1.141 +    if (arg == UNMARK_ONLY)
   1.142 +        return PL_DHASH_NEXT;
   1.143 +
   1.144 +    XPCNativeSet::DestroyInstance(set);
   1.145 +    return PL_DHASH_REMOVE;
   1.146 +}
   1.147 +
   1.148 +static PLDHashOperator
   1.149 +JSClassSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.150 +               uint32_t number, void *arg)
   1.151 +{
   1.152 +    XPCNativeScriptableShared* shared =
   1.153 +        ((XPCNativeScriptableSharedMap::Entry*) hdr)->key;
   1.154 +    if (shared->IsMarked()) {
   1.155 +        shared->Unmark();
   1.156 +        return PL_DHASH_NEXT;
   1.157 +    }
   1.158 +
   1.159 +    if (arg == UNMARK_ONLY)
   1.160 +        return PL_DHASH_NEXT;
   1.161 +
   1.162 +    delete shared;
   1.163 +    return PL_DHASH_REMOVE;
   1.164 +}
   1.165 +
   1.166 +static PLDHashOperator
   1.167 +DyingProtoKiller(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.168 +                 uint32_t number, void *arg)
   1.169 +{
   1.170 +    XPCWrappedNativeProto* proto =
   1.171 +        (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
   1.172 +    delete proto;
   1.173 +    return PL_DHASH_REMOVE;
   1.174 +}
   1.175 +
   1.176 +static PLDHashOperator
   1.177 +DetachedWrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.178 +                                 uint32_t number, void *arg)
   1.179 +{
   1.180 +    XPCWrappedNativeProto* proto =
   1.181 +        (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
   1.182 +
   1.183 +    proto->Mark();
   1.184 +    return PL_DHASH_NEXT;
   1.185 +}
   1.186 +
   1.187 +bool
   1.188 +XPCJSRuntime::CustomContextCallback(JSContext *cx, unsigned operation)
   1.189 +{
   1.190 +    if (operation == JSCONTEXT_NEW) {
   1.191 +        if (!OnJSContextNew(cx)) {
   1.192 +            return false;
   1.193 +        }
   1.194 +    } else if (operation == JSCONTEXT_DESTROY) {
   1.195 +        delete XPCContext::GetXPCContext(cx);
   1.196 +    }
   1.197 +
   1.198 +    nsTArray<xpcContextCallback> callbacks(extraContextCallbacks);
   1.199 +    for (uint32_t i = 0; i < callbacks.Length(); ++i) {
   1.200 +        if (!callbacks[i](cx, operation)) {
   1.201 +            return false;
   1.202 +        }
   1.203 +    }
   1.204 +
   1.205 +    return true;
   1.206 +}
   1.207 +
   1.208 +class AsyncFreeSnowWhite : public nsRunnable
   1.209 +{
   1.210 +public:
   1.211 +  NS_IMETHOD Run()
   1.212 +  {
   1.213 +      TimeStamp start = TimeStamp::Now();
   1.214 +      bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
   1.215 +      Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
   1.216 +                            uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
   1.217 +      if (hadSnowWhiteObjects && !mContinuation) {
   1.218 +          mContinuation = true;
   1.219 +          if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
   1.220 +              mActive = false;
   1.221 +          }
   1.222 +      } else {
   1.223 +          mActive = false;
   1.224 +      }
   1.225 +      return NS_OK;
   1.226 +  }
   1.227 +
   1.228 +  void Dispatch(bool aContinuation = false)
   1.229 +  {
   1.230 +      if (mContinuation) {
   1.231 +          mContinuation = aContinuation;
   1.232 +      }
   1.233 +      if (!mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(this))) {
   1.234 +          mActive = true;
   1.235 +      }
   1.236 +  }
   1.237 +
   1.238 +  AsyncFreeSnowWhite() : mContinuation(false), mActive(false) {}
   1.239 +
   1.240 +public:
   1.241 +  bool mContinuation;
   1.242 +  bool mActive;
   1.243 +};
   1.244 +
   1.245 +namespace xpc {
   1.246 +
   1.247 +static uint32_t kLivingAdopters = 0;
   1.248 +
   1.249 +void
   1.250 +RecordAdoptedNode(JSCompartment *c)
   1.251 +{
   1.252 +    CompartmentPrivate *priv = EnsureCompartmentPrivate(c);
   1.253 +    if (!priv->adoptedNode) {
   1.254 +        priv->adoptedNode = true;
   1.255 +        ++kLivingAdopters;
   1.256 +    }
   1.257 +}
   1.258 +
   1.259 +void
   1.260 +RecordDonatedNode(JSCompartment *c)
   1.261 +{
   1.262 +    EnsureCompartmentPrivate(c)->donatedNode = true;
   1.263 +}
   1.264 +
   1.265 +CompartmentPrivate::~CompartmentPrivate()
   1.266 +{
   1.267 +    MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
   1.268 +
   1.269 +    Telemetry::Accumulate(Telemetry::COMPARTMENT_ADOPTED_NODE, adoptedNode);
   1.270 +    Telemetry::Accumulate(Telemetry::COMPARTMENT_DONATED_NODE, donatedNode);
   1.271 +    if (adoptedNode)
   1.272 +        --kLivingAdopters;
   1.273 +}
   1.274 +
   1.275 +static bool
   1.276 +TryParseLocationURICandidate(const nsACString& uristr,
   1.277 +                             CompartmentPrivate::LocationHint aLocationHint,
   1.278 +                             nsIURI** aURI)
   1.279 +{
   1.280 +    static NS_NAMED_LITERAL_CSTRING(kGRE, "resource://gre/");
   1.281 +    static NS_NAMED_LITERAL_CSTRING(kToolkit, "chrome://global/");
   1.282 +    static NS_NAMED_LITERAL_CSTRING(kBrowser, "chrome://browser/");
   1.283 +
   1.284 +    if (aLocationHint == CompartmentPrivate::LocationHintAddon) {
   1.285 +        // Blacklist some known locations which are clearly not add-on related.
   1.286 +        if (StringBeginsWith(uristr, kGRE) ||
   1.287 +            StringBeginsWith(uristr, kToolkit) ||
   1.288 +            StringBeginsWith(uristr, kBrowser))
   1.289 +            return false;
   1.290 +    }
   1.291 +
   1.292 +    nsCOMPtr<nsIURI> uri;
   1.293 +    if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr)))
   1.294 +        return false;
   1.295 +
   1.296 +    nsAutoCString scheme;
   1.297 +    if (NS_FAILED(uri->GetScheme(scheme)))
   1.298 +        return false;
   1.299 +
   1.300 +    // Cannot really map data: and blob:.
   1.301 +    // Also, data: URIs are pretty memory hungry, which is kinda bad
   1.302 +    // for memory reporter use.
   1.303 +    if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob"))
   1.304 +        return false;
   1.305 +
   1.306 +    uri.forget(aURI);
   1.307 +    return true;
   1.308 +}
   1.309 +
   1.310 +bool CompartmentPrivate::TryParseLocationURI(CompartmentPrivate::LocationHint aLocationHint,
   1.311 +                                             nsIURI **aURI)
   1.312 +{
   1.313 +    if (!aURI)
   1.314 +        return false;
   1.315 +
   1.316 +    // Need to parse the URI.
   1.317 +    if (location.IsEmpty())
   1.318 +        return false;
   1.319 +
   1.320 +    // Handle Sandbox location strings.
   1.321 +    // A sandbox string looks like this:
   1.322 +    // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
   1.323 +    // where <sandboxName> is user-provided via Cu.Sandbox()
   1.324 +    // and <js-stack-frame-filename> and <lineno> is the stack frame location
   1.325 +    // from where Cu.Sandbox was called.
   1.326 +    // <js-stack-frame-filename> furthermore is "free form", often using a
   1.327 +    // "uri -> uri -> ..." chain. The following code will and must handle this
   1.328 +    // common case.
   1.329 +    // It should be noted that other parts of the code may already rely on the
   1.330 +    // "format" of these strings, such as the add-on SDK.
   1.331 +
   1.332 +    static const nsDependentCString from("(from: ");
   1.333 +    static const nsDependentCString arrow(" -> ");
   1.334 +    static const size_t fromLength = from.Length();
   1.335 +    static const size_t arrowLength = arrow.Length();
   1.336 +
   1.337 +    // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
   1.338 +    int32_t idx = location.Find(from);
   1.339 +    if (idx < 0)
   1.340 +        return TryParseLocationURICandidate(location, aLocationHint, aURI);
   1.341 +
   1.342 +
   1.343 +    // When parsing we're looking for the right-most URI. This URI may be in
   1.344 +    // <sandboxName>, so we try this first.
   1.345 +    if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint,
   1.346 +                                     aURI))
   1.347 +        return true;
   1.348 +
   1.349 +    // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and
   1.350 +    // the chain that is potentially contained within and grab the rightmost
   1.351 +    // item that is actually a URI.
   1.352 +
   1.353 +    // First, hack off the :<lineno>) part as well
   1.354 +    int32_t ridx = location.RFind(NS_LITERAL_CSTRING(":"));
   1.355 +    nsAutoCString chain(Substring(location, idx + fromLength,
   1.356 +                                  ridx - idx - fromLength));
   1.357 +
   1.358 +    // Loop over the "->" chain. This loop also works for non-chains, or more
   1.359 +    // correctly chains with only one item.
   1.360 +    for (;;) {
   1.361 +        idx = chain.RFind(arrow);
   1.362 +        if (idx < 0) {
   1.363 +            // This is the last chain item. Try to parse what is left.
   1.364 +            return TryParseLocationURICandidate(chain, aLocationHint, aURI);
   1.365 +        }
   1.366 +
   1.367 +        // Try to parse current chain item
   1.368 +        if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength),
   1.369 +                                         aLocationHint, aURI))
   1.370 +            return true;
   1.371 +
   1.372 +        // Current chain item couldn't be parsed.
   1.373 +        // Strip current item and continue.
   1.374 +        chain = Substring(chain, 0, idx);
   1.375 +    }
   1.376 +    MOZ_ASSUME_UNREACHABLE("Chain parser loop does not terminate");
   1.377 +}
   1.378 +
   1.379 +CompartmentPrivate*
   1.380 +EnsureCompartmentPrivate(JSObject *obj)
   1.381 +{
   1.382 +    return EnsureCompartmentPrivate(js::GetObjectCompartment(obj));
   1.383 +}
   1.384 +
   1.385 +CompartmentPrivate*
   1.386 +EnsureCompartmentPrivate(JSCompartment *c)
   1.387 +{
   1.388 +    CompartmentPrivate *priv = GetCompartmentPrivate(c);
   1.389 +    if (priv)
   1.390 +        return priv;
   1.391 +    priv = new CompartmentPrivate(c);
   1.392 +    JS_SetCompartmentPrivate(c, priv);
   1.393 +    return priv;
   1.394 +}
   1.395 +
   1.396 +XPCWrappedNativeScope*
   1.397 +MaybeGetObjectScope(JSObject *obj)
   1.398 +{
   1.399 +    MOZ_ASSERT(obj);
   1.400 +    JSCompartment *compartment = js::GetObjectCompartment(obj);
   1.401 +
   1.402 +    MOZ_ASSERT(compartment);
   1.403 +    CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   1.404 +    if (!priv)
   1.405 +        return nullptr;
   1.406 +
   1.407 +    return priv->scope;
   1.408 +}
   1.409 +
   1.410 +static bool
   1.411 +PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal)
   1.412 +{
   1.413 +    // System principal gets a free pass.
   1.414 +    if (XPCWrapper::GetSecurityManager()->IsSystemPrincipal(aPrincipal))
   1.415 +        return true;
   1.416 +
   1.417 +    // nsExpandedPrincipal gets a free pass.
   1.418 +    nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
   1.419 +    if (ep)
   1.420 +        return true;
   1.421 +
   1.422 +    // Check whether our URI is an "about:" URI that allows scripts.  If it is,
   1.423 +    // we need to allow JS to run.
   1.424 +    nsCOMPtr<nsIURI> principalURI;
   1.425 +    aPrincipal->GetURI(getter_AddRefs(principalURI));
   1.426 +    MOZ_ASSERT(principalURI);
   1.427 +    bool isAbout;
   1.428 +    nsresult rv = principalURI->SchemeIs("about", &isAbout);
   1.429 +    if (NS_SUCCEEDED(rv) && isAbout) {
   1.430 +        nsCOMPtr<nsIAboutModule> module;
   1.431 +        rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
   1.432 +        if (NS_SUCCEEDED(rv)) {
   1.433 +            uint32_t flags;
   1.434 +            rv = module->GetURIFlags(principalURI, &flags);
   1.435 +            if (NS_SUCCEEDED(rv) &&
   1.436 +                (flags & nsIAboutModule::ALLOW_SCRIPT)) {
   1.437 +                return true;
   1.438 +            }
   1.439 +        }
   1.440 +    }
   1.441 +
   1.442 +    return false;
   1.443 +}
   1.444 +
   1.445 +Scriptability::Scriptability(JSCompartment *c) : mScriptBlocks(0)
   1.446 +                                               , mDocShellAllowsScript(true)
   1.447 +                                               , mScriptBlockedByPolicy(false)
   1.448 +{
   1.449 +    nsIPrincipal *prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
   1.450 +    mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
   1.451 +
   1.452 +    // If we're not immune, we should have a real principal with a codebase URI.
   1.453 +    // Check the URI against the new-style domain policy.
   1.454 +    if (!mImmuneToScriptPolicy) {
   1.455 +        nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
   1.456 +        nsCOMPtr<nsIURI> codebase;
   1.457 +        nsresult rv = prin->GetURI(getter_AddRefs(codebase));
   1.458 +        bool policyAllows;
   1.459 +        if (NS_SUCCEEDED(rv) && codebase &&
   1.460 +            NS_SUCCEEDED(ssm->PolicyAllowsScript(codebase, &policyAllows)))
   1.461 +        {
   1.462 +            mScriptBlockedByPolicy = !policyAllows;
   1.463 +        } else {
   1.464 +            // Something went wrong - be safe and block script.
   1.465 +            mScriptBlockedByPolicy = true;
   1.466 +        }
   1.467 +    }
   1.468 +}
   1.469 +
   1.470 +bool
   1.471 +Scriptability::Allowed()
   1.472 +{
   1.473 +    return mDocShellAllowsScript && !mScriptBlockedByPolicy &&
   1.474 +           mScriptBlocks == 0;
   1.475 +}
   1.476 +
   1.477 +bool
   1.478 +Scriptability::IsImmuneToScriptPolicy()
   1.479 +{
   1.480 +    return mImmuneToScriptPolicy;
   1.481 +}
   1.482 +
   1.483 +void
   1.484 +Scriptability::Block()
   1.485 +{
   1.486 +    ++mScriptBlocks;
   1.487 +}
   1.488 +
   1.489 +void
   1.490 +Scriptability::Unblock()
   1.491 +{
   1.492 +    MOZ_ASSERT(mScriptBlocks > 0);
   1.493 +    --mScriptBlocks;
   1.494 +}
   1.495 +
   1.496 +void
   1.497 +Scriptability::SetDocShellAllowsScript(bool aAllowed)
   1.498 +{
   1.499 +    mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy;
   1.500 +}
   1.501 +
   1.502 +/* static */
   1.503 +Scriptability&
   1.504 +Scriptability::Get(JSObject *aScope)
   1.505 +{
   1.506 +    return EnsureCompartmentPrivate(aScope)->scriptability;
   1.507 +}
   1.508 +
   1.509 +bool
   1.510 +IsXBLScope(JSCompartment *compartment)
   1.511 +{
   1.512 +    // We always eagerly create compartment privates for XBL scopes.
   1.513 +    CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   1.514 +    if (!priv || !priv->scope)
   1.515 +        return false;
   1.516 +    return priv->scope->IsXBLScope();
   1.517 +}
   1.518 +
   1.519 +bool
   1.520 +IsInXBLScope(JSObject *obj)
   1.521 +{
   1.522 +    return IsXBLScope(js::GetObjectCompartment(obj));
   1.523 +}
   1.524 +
   1.525 +bool
   1.526 +IsUniversalXPConnectEnabled(JSCompartment *compartment)
   1.527 +{
   1.528 +    CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   1.529 +    if (!priv)
   1.530 +        return false;
   1.531 +    return priv->universalXPConnectEnabled;
   1.532 +}
   1.533 +
   1.534 +bool
   1.535 +IsUniversalXPConnectEnabled(JSContext *cx)
   1.536 +{
   1.537 +    JSCompartment *compartment = js::GetContextCompartment(cx);
   1.538 +    if (!compartment)
   1.539 +        return false;
   1.540 +    return IsUniversalXPConnectEnabled(compartment);
   1.541 +}
   1.542 +
   1.543 +bool
   1.544 +EnableUniversalXPConnect(JSContext *cx)
   1.545 +{
   1.546 +    JSCompartment *compartment = js::GetContextCompartment(cx);
   1.547 +    if (!compartment)
   1.548 +        return true;
   1.549 +    // Never set universalXPConnectEnabled on a chrome compartment - it confuses
   1.550 +    // the security wrapping code.
   1.551 +    if (AccessCheck::isChrome(compartment))
   1.552 +        return true;
   1.553 +    CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
   1.554 +    if (!priv)
   1.555 +        return true;
   1.556 +    priv->universalXPConnectEnabled = true;
   1.557 +
   1.558 +    // Recompute all the cross-compartment wrappers leaving the newly-privileged
   1.559 +    // compartment.
   1.560 +    bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
   1.561 +                                    js::AllCompartments());
   1.562 +    NS_ENSURE_TRUE(ok, false);
   1.563 +
   1.564 +    // The Components object normally isn't defined for unprivileged web content,
   1.565 +    // but we define it when UniversalXPConnect is enabled to support legacy
   1.566 +    // tests.
   1.567 +    XPCWrappedNativeScope *scope = priv->scope;
   1.568 +    if (!scope)
   1.569 +        return true;
   1.570 +    scope->ForcePrivilegedComponents();
   1.571 +    return scope->AttachComponentsObject(cx);
   1.572 +}
   1.573 +
   1.574 +JSObject *
   1.575 +GetJunkScope()
   1.576 +{
   1.577 +    XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
   1.578 +    NS_ENSURE_TRUE(self, nullptr);
   1.579 +    return self->GetJunkScope();
   1.580 +}
   1.581 +
   1.582 +nsIGlobalObject *
   1.583 +GetJunkScopeGlobal()
   1.584 +{
   1.585 +    JSObject *junkScope = GetJunkScope();
   1.586 +    // GetJunkScope would ideally never fail, currently it is not yet the case
   1.587 +    // unfortunately...(see Bug 874158)
   1.588 +    if (!junkScope)
   1.589 +        return nullptr;
   1.590 +    return GetNativeForGlobal(junkScope);
   1.591 +}
   1.592 +
   1.593 +JSObject *
   1.594 +GetCompilationScope()
   1.595 +{
   1.596 +    XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
   1.597 +    NS_ENSURE_TRUE(self, nullptr);
   1.598 +    return self->GetCompilationScope();
   1.599 +}
   1.600 +
   1.601 +JSObject *
   1.602 +GetSafeJSContextGlobal()
   1.603 +{
   1.604 +    return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal();
   1.605 +}
   1.606 +
   1.607 +nsGlobalWindow*
   1.608 +WindowOrNull(JSObject *aObj)
   1.609 +{
   1.610 +    MOZ_ASSERT(aObj);
   1.611 +    MOZ_ASSERT(!js::IsWrapper(aObj));
   1.612 +
   1.613 +    // This will always return null until we have Window on WebIDL bindings,
   1.614 +    // at which point it will do the right thing.
   1.615 +    if (!IS_WN_CLASS(js::GetObjectClass(aObj))) {
   1.616 +        nsGlobalWindow* win = nullptr;
   1.617 +        UNWRAP_OBJECT(Window, aObj, win);
   1.618 +        return win;
   1.619 +    }
   1.620 +
   1.621 +    nsISupports* supports = XPCWrappedNative::Get(aObj)->GetIdentityObject();
   1.622 +    nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(supports);
   1.623 +    if (!piWin)
   1.624 +        return nullptr;
   1.625 +    return static_cast<nsGlobalWindow*>(piWin.get());
   1.626 +}
   1.627 +
   1.628 +nsGlobalWindow*
   1.629 +WindowGlobalOrNull(JSObject *aObj)
   1.630 +{
   1.631 +    MOZ_ASSERT(aObj);
   1.632 +    JSObject *glob = js::GetGlobalForObjectCrossCompartment(aObj);
   1.633 +
   1.634 +    return WindowOrNull(glob);
   1.635 +}
   1.636 +
   1.637 +}
   1.638 +
   1.639 +static void
   1.640 +CompartmentDestroyedCallback(JSFreeOp *fop, JSCompartment *compartment)
   1.641 +{
   1.642 +    // NB - This callback may be called in JS_DestroyRuntime, which happens
   1.643 +    // after the XPCJSRuntime has been torn down.
   1.644 +
   1.645 +    // Get the current compartment private into an AutoPtr (which will do the
   1.646 +    // cleanup for us), and null out the private (which may already be null).
   1.647 +    nsAutoPtr<CompartmentPrivate> priv(GetCompartmentPrivate(compartment));
   1.648 +    JS_SetCompartmentPrivate(compartment, nullptr);
   1.649 +}
   1.650 +
   1.651 +void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc)
   1.652 +{
   1.653 +    // Skip this part if XPConnect is shutting down. We get into
   1.654 +    // bad locking problems with the thread iteration otherwise.
   1.655 +    if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   1.656 +        // Trace those AutoMarkingPtr lists!
   1.657 +        if (AutoMarkingPtr *roots = Get()->mAutoRoots)
   1.658 +            roots->TraceJSAll(trc);
   1.659 +    }
   1.660 +
   1.661 +    // XPCJSObjectHolders don't participate in cycle collection, so always
   1.662 +    // trace them here.
   1.663 +    XPCRootSetElem *e;
   1.664 +    for (e = mObjectHolderRoots; e; e = e->GetNextRoot())
   1.665 +        static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
   1.666 +
   1.667 +    dom::TraceBlackJS(trc, JS_GetGCParameter(Runtime(), JSGC_NUMBER),
   1.668 +                      nsXPConnect::XPConnect()->IsShuttingDown());
   1.669 +}
   1.670 +
   1.671 +void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer *trc)
   1.672 +{
   1.673 +    XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc, this);
   1.674 +
   1.675 +    for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot())
   1.676 +        static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
   1.677 +
   1.678 +    for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
   1.679 +        static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
   1.680 +}
   1.681 +
   1.682 +// static
   1.683 +void
   1.684 +XPCJSRuntime::SuspectWrappedNative(XPCWrappedNative *wrapper,
   1.685 +                                   nsCycleCollectionNoteRootCallback &cb)
   1.686 +{
   1.687 +    if (!wrapper->IsValid() || wrapper->IsWrapperExpired())
   1.688 +        return;
   1.689 +
   1.690 +    MOZ_ASSERT(NS_IsMainThread(),
   1.691 +               "Suspecting wrapped natives from non-main thread");
   1.692 +
   1.693 +    // Only record objects that might be part of a cycle as roots, unless
   1.694 +    // the callback wants all traces (a debug feature).
   1.695 +    JSObject* obj = wrapper->GetFlatJSObjectPreserveColor();
   1.696 +    if (xpc_IsGrayGCThing(obj) || cb.WantAllTraces())
   1.697 +        cb.NoteJSRoot(obj);
   1.698 +}
   1.699 +
   1.700 +void
   1.701 +XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &cb)
   1.702 +{
   1.703 +    XPCWrappedNativeScope::SuspectAllWrappers(this, cb);
   1.704 +
   1.705 +    for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) {
   1.706 +        XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
   1.707 +        if (nsCCUncollectableMarker::InGeneration(cb,
   1.708 +                                                  v->CCGeneration())) {
   1.709 +           jsval val = v->GetJSValPreserveColor();
   1.710 +           if (val.isObject() && !xpc_IsGrayGCThing(&val.toObject()))
   1.711 +               continue;
   1.712 +        }
   1.713 +        cb.NoteXPCOMRoot(v);
   1.714 +    }
   1.715 +
   1.716 +    for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
   1.717 +        cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)));
   1.718 +    }
   1.719 +}
   1.720 +
   1.721 +void
   1.722 +XPCJSRuntime::UnmarkSkippableJSHolders()
   1.723 +{
   1.724 +    CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
   1.725 +}
   1.726 +
   1.727 +void
   1.728 +XPCJSRuntime::PrepareForForgetSkippable()
   1.729 +{
   1.730 +    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.731 +    if (obs) {
   1.732 +        obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr);
   1.733 +    }
   1.734 +}
   1.735 +
   1.736 +void
   1.737 +XPCJSRuntime::BeginCycleCollectionCallback()
   1.738 +{
   1.739 +    nsJSContext::BeginCycleCollectionCallback();
   1.740 +
   1.741 +    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.742 +    if (obs) {
   1.743 +        obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
   1.744 +    }
   1.745 +}
   1.746 +
   1.747 +void
   1.748 +XPCJSRuntime::EndCycleCollectionCallback(CycleCollectorResults &aResults)
   1.749 +{
   1.750 +    nsJSContext::EndCycleCollectionCallback(aResults);
   1.751 +
   1.752 +    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.753 +    if (obs) {
   1.754 +        obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
   1.755 +    }
   1.756 +}
   1.757 +
   1.758 +void
   1.759 +XPCJSRuntime::DispatchDeferredDeletion(bool aContinuation)
   1.760 +{
   1.761 +    mAsyncSnowWhiteFreer->Dispatch(aContinuation);
   1.762 +}
   1.763 +
   1.764 +void
   1.765 +xpc_UnmarkSkippableJSHolders()
   1.766 +{
   1.767 +    if (nsXPConnect::XPConnect()->GetRuntime()) {
   1.768 +        nsXPConnect::XPConnect()->GetRuntime()->UnmarkSkippableJSHolders();
   1.769 +    }
   1.770 +}
   1.771 +
   1.772 +template<class T> static void
   1.773 +DoDeferredRelease(nsTArray<T> &array)
   1.774 +{
   1.775 +    while (1) {
   1.776 +        uint32_t count = array.Length();
   1.777 +        if (!count) {
   1.778 +            array.Compact();
   1.779 +            break;
   1.780 +        }
   1.781 +        T wrapper = array[count-1];
   1.782 +        array.RemoveElementAt(count-1);
   1.783 +        NS_RELEASE(wrapper);
   1.784 +    }
   1.785 +}
   1.786 +
   1.787 +/* static */ void
   1.788 +XPCJSRuntime::GCSliceCallback(JSRuntime *rt,
   1.789 +                              JS::GCProgress progress,
   1.790 +                              const JS::GCDescription &desc)
   1.791 +{
   1.792 +    XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
   1.793 +    if (!self)
   1.794 +        return;
   1.795 +
   1.796 +#ifdef MOZ_CRASHREPORTER
   1.797 +    CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN ||
   1.798 +                                        progress == JS::GC_SLICE_BEGIN);
   1.799 +#endif
   1.800 +
   1.801 +    if (self->mPrevGCSliceCallback)
   1.802 +        (*self->mPrevGCSliceCallback)(rt, progress, desc);
   1.803 +}
   1.804 +
   1.805 +void
   1.806 +XPCJSRuntime::CustomGCCallback(JSGCStatus status)
   1.807 +{
   1.808 +    // Record kLivingAdopters once per GC to approximate time sampling during
   1.809 +    // periods of activity.
   1.810 +    if (status == JSGC_BEGIN) {
   1.811 +        Telemetry::Accumulate(Telemetry::COMPARTMENT_LIVING_ADOPTERS,
   1.812 +                              kLivingAdopters);
   1.813 +    }
   1.814 +
   1.815 +    nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
   1.816 +    for (uint32_t i = 0; i < callbacks.Length(); ++i)
   1.817 +        callbacks[i](status);
   1.818 +}
   1.819 +
   1.820 +/* static */ void
   1.821 +XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartmentGC)
   1.822 +{
   1.823 +    XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
   1.824 +    if (!self)
   1.825 +        return;
   1.826 +
   1.827 +    switch (status) {
   1.828 +        case JSFINALIZE_GROUP_START:
   1.829 +        {
   1.830 +            MOZ_ASSERT(!self->mDoingFinalization, "bad state");
   1.831 +
   1.832 +            MOZ_ASSERT(!self->mGCIsRunning, "bad state");
   1.833 +            self->mGCIsRunning = true;
   1.834 +
   1.835 +            nsTArray<nsXPCWrappedJS*>* dyingWrappedJSArray =
   1.836 +                &self->mWrappedJSToReleaseArray;
   1.837 +
   1.838 +            // Add any wrappers whose JSObjects are to be finalized to
   1.839 +            // this array. Note that we do not want to be changing the
   1.840 +            // refcount of these wrappers.
   1.841 +            // We add them to the array now and Release the array members
   1.842 +            // later to avoid the posibility of doing any JS GCThing
   1.843 +            // allocations during the gc cycle.
   1.844 +            self->mWrappedJSMap->FindDyingJSObjects(dyingWrappedJSArray);
   1.845 +
   1.846 +            // Find dying scopes.
   1.847 +            XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self);
   1.848 +
   1.849 +            self->mDoingFinalization = true;
   1.850 +            break;
   1.851 +        }
   1.852 +        case JSFINALIZE_GROUP_END:
   1.853 +        {
   1.854 +            MOZ_ASSERT(self->mDoingFinalization, "bad state");
   1.855 +            self->mDoingFinalization = false;
   1.856 +
   1.857 +            // Release all the members whose JSObjects are now known
   1.858 +            // to be dead.
   1.859 +            DoDeferredRelease(self->mWrappedJSToReleaseArray);
   1.860 +
   1.861 +            // Sweep scopes needing cleanup
   1.862 +            XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC();
   1.863 +
   1.864 +            MOZ_ASSERT(self->mGCIsRunning, "bad state");
   1.865 +            self->mGCIsRunning = false;
   1.866 +
   1.867 +            break;
   1.868 +        }
   1.869 +        case JSFINALIZE_COLLECTION_END:
   1.870 +        {
   1.871 +            MOZ_ASSERT(!self->mGCIsRunning, "bad state");
   1.872 +            self->mGCIsRunning = true;
   1.873 +
   1.874 +            // We use this occasion to mark and sweep NativeInterfaces,
   1.875 +            // NativeSets, and the WrappedNativeJSClasses...
   1.876 +
   1.877 +            // Do the marking...
   1.878 +            XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos();
   1.879 +
   1.880 +            self->mDetachedWrappedNativeProtoMap->
   1.881 +                Enumerate(DetachedWrappedNativeProtoMarker, nullptr);
   1.882 +
   1.883 +            DOM_MarkInterfaces();
   1.884 +
   1.885 +            // Mark the sets used in the call contexts. There is a small
   1.886 +            // chance that a wrapper's set will change *while* a call is
   1.887 +            // happening which uses that wrapper's old interfface set. So,
   1.888 +            // we need to do this marking to avoid collecting those sets
   1.889 +            // that might no longer be otherwise reachable from the wrappers
   1.890 +            // or the wrapperprotos.
   1.891 +
   1.892 +            // Skip this part if XPConnect is shutting down. We get into
   1.893 +            // bad locking problems with the thread iteration otherwise.
   1.894 +            if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   1.895 +
   1.896 +                // Mark those AutoMarkingPtr lists!
   1.897 +                if (AutoMarkingPtr *roots = Get()->mAutoRoots)
   1.898 +                    roots->MarkAfterJSFinalizeAll();
   1.899 +
   1.900 +                XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
   1.901 +                while (ccxp) {
   1.902 +                    // Deal with the strictness of callcontext that
   1.903 +                    // complains if you ask for a set when
   1.904 +                    // it is in a state where the set could not
   1.905 +                    // possibly be valid.
   1.906 +                    if (ccxp->CanGetSet()) {
   1.907 +                        XPCNativeSet* set = ccxp->GetSet();
   1.908 +                        if (set)
   1.909 +                            set->Mark();
   1.910 +                    }
   1.911 +                    if (ccxp->CanGetInterface()) {
   1.912 +                        XPCNativeInterface* iface = ccxp->GetInterface();
   1.913 +                        if (iface)
   1.914 +                            iface->Mark();
   1.915 +                    }
   1.916 +                    ccxp = ccxp->GetPrevCallContext();
   1.917 +                }
   1.918 +            }
   1.919 +
   1.920 +            // Do the sweeping. During a compartment GC, only
   1.921 +            // WrappedNativeProtos in collected compartments will be
   1.922 +            // marked. Therefore, some reachable NativeInterfaces will not be
   1.923 +            // marked, so it is not safe to sweep them. We still need to unmark
   1.924 +            // them, since the ones pointed to by WrappedNativeProtos in a
   1.925 +            // compartment being collected will be marked.
   1.926 +            //
   1.927 +            // Ideally, if NativeInterfaces from different compartments were
   1.928 +            // kept separate, we could sweep only the ones belonging to
   1.929 +            // compartments being collected. Currently, though, NativeInterfaces
   1.930 +            // are shared between compartments. This ought to be fixed.
   1.931 +            void *sweepArg = isCompartmentGC ? UNMARK_ONLY : UNMARK_AND_SWEEP;
   1.932 +
   1.933 +            // We don't want to sweep the JSClasses at shutdown time.
   1.934 +            // At this point there may be JSObjects using them that have
   1.935 +            // been removed from the other maps.
   1.936 +            if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   1.937 +                self->mNativeScriptableSharedMap->
   1.938 +                    Enumerate(JSClassSweeper, sweepArg);
   1.939 +            }
   1.940 +
   1.941 +            if (!isCompartmentGC) {
   1.942 +                self->mClassInfo2NativeSetMap->
   1.943 +                    Enumerate(NativeUnMarkedSetRemover, nullptr);
   1.944 +            }
   1.945 +
   1.946 +            self->mNativeSetMap->
   1.947 +                Enumerate(NativeSetSweeper, sweepArg);
   1.948 +
   1.949 +            self->mIID2NativeInterfaceMap->
   1.950 +                Enumerate(NativeInterfaceSweeper, sweepArg);
   1.951 +
   1.952 +#ifdef DEBUG
   1.953 +            XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked();
   1.954 +#endif
   1.955 +
   1.956 +            // Now we are going to recycle any unused WrappedNativeTearoffs.
   1.957 +            // We do this by iterating all the live callcontexts
   1.958 +            // and marking the tearoffs in use. And then we
   1.959 +            // iterate over all the WrappedNative wrappers and sweep their
   1.960 +            // tearoffs.
   1.961 +            //
   1.962 +            // This allows us to perhaps minimize the growth of the
   1.963 +            // tearoffs. And also makes us not hold references to interfaces
   1.964 +            // on our wrapped natives that we are not actually using.
   1.965 +            //
   1.966 +            // XXX We may decide to not do this on *every* gc cycle.
   1.967 +
   1.968 +            // Skip this part if XPConnect is shutting down. We get into
   1.969 +            // bad locking problems with the thread iteration otherwise.
   1.970 +            if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
   1.971 +                // Do the marking...
   1.972 +
   1.973 +                XPCCallContext* ccxp = XPCJSRuntime::Get()->GetCallContext();
   1.974 +                while (ccxp) {
   1.975 +                    // Deal with the strictness of callcontext that
   1.976 +                    // complains if you ask for a tearoff when
   1.977 +                    // it is in a state where the tearoff could not
   1.978 +                    // possibly be valid.
   1.979 +                    if (ccxp->CanGetTearOff()) {
   1.980 +                        XPCWrappedNativeTearOff* to =
   1.981 +                            ccxp->GetTearOff();
   1.982 +                        if (to)
   1.983 +                            to->Mark();
   1.984 +                    }
   1.985 +                    ccxp = ccxp->GetPrevCallContext();
   1.986 +                }
   1.987 +
   1.988 +                // Do the sweeping...
   1.989 +                XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
   1.990 +            }
   1.991 +
   1.992 +            // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
   1.993 +            // We transfered these native objects to this table when their
   1.994 +            // JSObject's were finalized. We did not destroy them immediately
   1.995 +            // at that point because the ordering of JS finalization is not
   1.996 +            // deterministic and we did not yet know if any wrappers that
   1.997 +            // might still be referencing the protos where still yet to be
   1.998 +            // finalized and destroyed. We *do* know that the protos'
   1.999 +            // JSObjects would not have been finalized if there were any
  1.1000 +            // wrappers that referenced the proto but where not themselves
  1.1001 +            // slated for finalization in this gc cycle. So... at this point
  1.1002 +            // we know that any and all wrappers that might have been
  1.1003 +            // referencing the protos in the dying list are themselves dead.
  1.1004 +            // So, we can safely delete all the protos in the list.
  1.1005 +
  1.1006 +            self->mDyingWrappedNativeProtoMap->
  1.1007 +                Enumerate(DyingProtoKiller, nullptr);
  1.1008 +
  1.1009 +            MOZ_ASSERT(self->mGCIsRunning, "bad state");
  1.1010 +            self->mGCIsRunning = false;
  1.1011 +
  1.1012 +            break;
  1.1013 +        }
  1.1014 +    }
  1.1015 +}
  1.1016 +
  1.1017 +static void WatchdogMain(void *arg);
  1.1018 +class Watchdog;
  1.1019 +class WatchdogManager;
  1.1020 +class AutoLockWatchdog {
  1.1021 +    Watchdog* const mWatchdog;
  1.1022 +  public:
  1.1023 +    AutoLockWatchdog(Watchdog* aWatchdog);
  1.1024 +    ~AutoLockWatchdog();
  1.1025 +};
  1.1026 +
  1.1027 +class Watchdog
  1.1028 +{
  1.1029 +  public:
  1.1030 +    Watchdog(WatchdogManager *aManager)
  1.1031 +      : mManager(aManager)
  1.1032 +      , mLock(nullptr)
  1.1033 +      , mWakeup(nullptr)
  1.1034 +      , mThread(nullptr)
  1.1035 +      , mHibernating(false)
  1.1036 +      , mInitialized(false)
  1.1037 +      , mShuttingDown(false)
  1.1038 +    {}
  1.1039 +    ~Watchdog() { MOZ_ASSERT(!Initialized()); }
  1.1040 +
  1.1041 +    WatchdogManager* Manager() { return mManager; }
  1.1042 +    bool Initialized() { return mInitialized; }
  1.1043 +    bool ShuttingDown() { return mShuttingDown; }
  1.1044 +    PRLock *GetLock() { return mLock; }
  1.1045 +    bool Hibernating() { return mHibernating; }
  1.1046 +    void WakeUp()
  1.1047 +    {
  1.1048 +        MOZ_ASSERT(Initialized());
  1.1049 +        MOZ_ASSERT(Hibernating());
  1.1050 +        mHibernating = false;
  1.1051 +        PR_NotifyCondVar(mWakeup);
  1.1052 +    }
  1.1053 +
  1.1054 +    //
  1.1055 +    // Invoked by the main thread only.
  1.1056 +    //
  1.1057 +
  1.1058 +    void Init()
  1.1059 +    {
  1.1060 +        MOZ_ASSERT(NS_IsMainThread());
  1.1061 +        mLock = PR_NewLock();
  1.1062 +        if (!mLock)
  1.1063 +            NS_RUNTIMEABORT("PR_NewLock failed.");
  1.1064 +        mWakeup = PR_NewCondVar(mLock);
  1.1065 +        if (!mWakeup)
  1.1066 +            NS_RUNTIMEABORT("PR_NewCondVar failed.");
  1.1067 +
  1.1068 +        {
  1.1069 +            AutoLockWatchdog lock(this);
  1.1070 +
  1.1071 +            mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
  1.1072 +                                      PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  1.1073 +                                      PR_UNJOINABLE_THREAD, 0);
  1.1074 +            if (!mThread)
  1.1075 +                NS_RUNTIMEABORT("PR_CreateThread failed!");
  1.1076 +
  1.1077 +            // WatchdogMain acquires the lock and then asserts mInitialized. So
  1.1078 +            // make sure to set mInitialized before releasing the lock here so
  1.1079 +            // that it's atomic with the creation of the thread.
  1.1080 +            mInitialized = true;
  1.1081 +        }
  1.1082 +    }
  1.1083 +
  1.1084 +    void Shutdown()
  1.1085 +    {
  1.1086 +        MOZ_ASSERT(NS_IsMainThread());
  1.1087 +        MOZ_ASSERT(Initialized());
  1.1088 +        {   // Scoped lock.
  1.1089 +            AutoLockWatchdog lock(this);
  1.1090 +
  1.1091 +            // Signal to the watchdog thread that it's time to shut down.
  1.1092 +            mShuttingDown = true;
  1.1093 +
  1.1094 +            // Wake up the watchdog, and wait for it to call us back.
  1.1095 +            PR_NotifyCondVar(mWakeup);
  1.1096 +            PR_WaitCondVar(mWakeup, PR_INTERVAL_NO_TIMEOUT);
  1.1097 +            MOZ_ASSERT(!mShuttingDown);
  1.1098 +        }
  1.1099 +
  1.1100 +        // Destroy state.
  1.1101 +        mThread = nullptr;
  1.1102 +        PR_DestroyCondVar(mWakeup);
  1.1103 +        mWakeup = nullptr;
  1.1104 +        PR_DestroyLock(mLock);
  1.1105 +        mLock = nullptr;
  1.1106 +
  1.1107 +        // All done.
  1.1108 +        mInitialized = false;
  1.1109 +    }
  1.1110 +
  1.1111 +    //
  1.1112 +    // Invoked by the watchdog thread only.
  1.1113 +    //
  1.1114 +
  1.1115 +    void Hibernate()
  1.1116 +    {
  1.1117 +        MOZ_ASSERT(!NS_IsMainThread());
  1.1118 +        mHibernating = true;
  1.1119 +        Sleep(PR_INTERVAL_NO_TIMEOUT);
  1.1120 +    }
  1.1121 +    void Sleep(PRIntervalTime timeout)
  1.1122 +    {
  1.1123 +        MOZ_ASSERT(!NS_IsMainThread());
  1.1124 +        MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS);
  1.1125 +    }
  1.1126 +    void Finished()
  1.1127 +    {
  1.1128 +        MOZ_ASSERT(!NS_IsMainThread());
  1.1129 +        mShuttingDown = false;
  1.1130 +        PR_NotifyCondVar(mWakeup);
  1.1131 +    }
  1.1132 +
  1.1133 +  private:
  1.1134 +    WatchdogManager *mManager;
  1.1135 +
  1.1136 +    PRLock *mLock;
  1.1137 +    PRCondVar *mWakeup;
  1.1138 +    PRThread *mThread;
  1.1139 +    bool mHibernating;
  1.1140 +    bool mInitialized;
  1.1141 +    bool mShuttingDown;
  1.1142 +};
  1.1143 +
  1.1144 +#ifdef MOZ_NUWA_PROCESS
  1.1145 +#include "ipc/Nuwa.h"
  1.1146 +#endif
  1.1147 +
  1.1148 +class WatchdogManager : public nsIObserver
  1.1149 +{
  1.1150 +  public:
  1.1151 +
  1.1152 +    NS_DECL_ISUPPORTS
  1.1153 +    WatchdogManager(XPCJSRuntime *aRuntime) : mRuntime(aRuntime)
  1.1154 +                                            , mRuntimeState(RUNTIME_INACTIVE)
  1.1155 +    {
  1.1156 +        // All the timestamps start at zero except for runtime state change.
  1.1157 +        PodArrayZero(mTimestamps);
  1.1158 +        mTimestamps[TimestampRuntimeStateChange] = PR_Now();
  1.1159 +
  1.1160 +        // Enable the watchdog, if appropriate.
  1.1161 +        RefreshWatchdog();
  1.1162 +
  1.1163 +        // Register ourselves as an observer to get updates on the pref.
  1.1164 +        mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
  1.1165 +    }
  1.1166 +    virtual ~WatchdogManager()
  1.1167 +    {
  1.1168 +        // Shutting down the watchdog requires context-switching to the watchdog
  1.1169 +        // thread, which isn't great to do in a destructor. So we require
  1.1170 +        // consumers to shut it down manually before releasing it.
  1.1171 +        MOZ_ASSERT(!mWatchdog);
  1.1172 +        mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
  1.1173 +    }
  1.1174 +
  1.1175 +    NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
  1.1176 +                       const char16_t* aData)
  1.1177 +    {
  1.1178 +        RefreshWatchdog();
  1.1179 +        return NS_OK;
  1.1180 +    }
  1.1181 +
  1.1182 +    // Runtime statistics. These live on the watchdog manager, are written
  1.1183 +    // from the main thread, and are read from the watchdog thread (holding
  1.1184 +    // the lock in each case).
  1.1185 +    void
  1.1186 +    RecordRuntimeActivity(bool active)
  1.1187 +    {
  1.1188 +        // The watchdog reads this state, so acquire the lock before writing it.
  1.1189 +        MOZ_ASSERT(NS_IsMainThread());
  1.1190 +        Maybe<AutoLockWatchdog> lock;
  1.1191 +        if (mWatchdog)
  1.1192 +            lock.construct(mWatchdog);
  1.1193 +
  1.1194 +        // Write state.
  1.1195 +        mTimestamps[TimestampRuntimeStateChange] = PR_Now();
  1.1196 +        mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE;
  1.1197 +
  1.1198 +        // The watchdog may be hibernating, waiting for the runtime to go
  1.1199 +        // active. Wake it up if necessary.
  1.1200 +        if (active && mWatchdog && mWatchdog->Hibernating())
  1.1201 +            mWatchdog->WakeUp();
  1.1202 +    }
  1.1203 +    bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; }
  1.1204 +    PRTime TimeSinceLastRuntimeStateChange()
  1.1205 +    {
  1.1206 +        return PR_Now() - GetTimestamp(TimestampRuntimeStateChange);
  1.1207 +    }
  1.1208 +
  1.1209 +    // Note - Because of the runtime activity timestamp, these are read and
  1.1210 +    // written from both threads.
  1.1211 +    void RecordTimestamp(WatchdogTimestampCategory aCategory)
  1.1212 +    {
  1.1213 +        // The watchdog thread always holds the lock when it runs.
  1.1214 +        Maybe<AutoLockWatchdog> maybeLock;
  1.1215 +        if (NS_IsMainThread() && mWatchdog)
  1.1216 +            maybeLock.construct(mWatchdog);
  1.1217 +        mTimestamps[aCategory] = PR_Now();
  1.1218 +    }
  1.1219 +    PRTime GetTimestamp(WatchdogTimestampCategory aCategory)
  1.1220 +    {
  1.1221 +        // The watchdog thread always holds the lock when it runs.
  1.1222 +        Maybe<AutoLockWatchdog> maybeLock;
  1.1223 +        if (NS_IsMainThread() && mWatchdog)
  1.1224 +            maybeLock.construct(mWatchdog);
  1.1225 +        return mTimestamps[aCategory];
  1.1226 +    }
  1.1227 +
  1.1228 +    XPCJSRuntime* Runtime() { return mRuntime; }
  1.1229 +    Watchdog* GetWatchdog() { return mWatchdog; }
  1.1230 +
  1.1231 +    void RefreshWatchdog()
  1.1232 +    {
  1.1233 +        bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
  1.1234 +        if (wantWatchdog == !!mWatchdog)
  1.1235 +            return;
  1.1236 +        if (wantWatchdog)
  1.1237 +            StartWatchdog();
  1.1238 +        else
  1.1239 +            StopWatchdog();
  1.1240 +    }
  1.1241 +
  1.1242 +    void StartWatchdog()
  1.1243 +    {
  1.1244 +        MOZ_ASSERT(!mWatchdog);
  1.1245 +        mWatchdog = new Watchdog(this);
  1.1246 +        mWatchdog->Init();
  1.1247 +    }
  1.1248 +
  1.1249 +    void StopWatchdog()
  1.1250 +    {
  1.1251 +        MOZ_ASSERT(mWatchdog);
  1.1252 +        mWatchdog->Shutdown();
  1.1253 +        mWatchdog = nullptr;
  1.1254 +    }
  1.1255 +
  1.1256 +  private:
  1.1257 +    XPCJSRuntime *mRuntime;
  1.1258 +    nsAutoPtr<Watchdog> mWatchdog;
  1.1259 +
  1.1260 +    enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState;
  1.1261 +    PRTime mTimestamps[TimestampCount];
  1.1262 +};
  1.1263 +
  1.1264 +NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
  1.1265 +
  1.1266 +AutoLockWatchdog::AutoLockWatchdog(Watchdog *aWatchdog) : mWatchdog(aWatchdog)
  1.1267 +{
  1.1268 +    PR_Lock(mWatchdog->GetLock());
  1.1269 +}
  1.1270 +
  1.1271 +AutoLockWatchdog::~AutoLockWatchdog()
  1.1272 +{
  1.1273 +    PR_Unlock(mWatchdog->GetLock());
  1.1274 +}
  1.1275 +
  1.1276 +static void
  1.1277 +WatchdogMain(void *arg)
  1.1278 +{
  1.1279 +    PR_SetCurrentThreadName("JS Watchdog");
  1.1280 +
  1.1281 +#ifdef MOZ_NUWA_PROCESS
  1.1282 +    if (IsNuwaProcess()) {
  1.1283 +        NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
  1.1284 +                     "NuwaMarkCurrentThread is undefined!");
  1.1285 +        NuwaMarkCurrentThread(nullptr, nullptr);
  1.1286 +        NuwaFreezeCurrentThread();
  1.1287 +    }
  1.1288 +#endif
  1.1289 +
  1.1290 +    Watchdog* self = static_cast<Watchdog*>(arg);
  1.1291 +    WatchdogManager* manager = self->Manager();
  1.1292 +
  1.1293 +    // Lock lasts until we return
  1.1294 +    AutoLockWatchdog lock(self);
  1.1295 +
  1.1296 +    MOZ_ASSERT(self->Initialized());
  1.1297 +    MOZ_ASSERT(!self->ShuttingDown());
  1.1298 +    while (!self->ShuttingDown()) {
  1.1299 +        // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
  1.1300 +        if (manager->IsRuntimeActive() ||
  1.1301 +            manager->TimeSinceLastRuntimeStateChange() <= PRTime(2*PR_USEC_PER_SEC))
  1.1302 +        {
  1.1303 +            self->Sleep(PR_TicksPerSecond());
  1.1304 +        } else {
  1.1305 +            manager->RecordTimestamp(TimestampWatchdogHibernateStart);
  1.1306 +            self->Hibernate();
  1.1307 +            manager->RecordTimestamp(TimestampWatchdogHibernateStop);
  1.1308 +        }
  1.1309 +
  1.1310 +        // Rise and shine.
  1.1311 +        manager->RecordTimestamp(TimestampWatchdogWakeup);
  1.1312 +
  1.1313 +        // Don't request an interrupt callback if activity started less than one second ago.
  1.1314 +        // The callback is only used for detecting long running scripts, and triggering the
  1.1315 +        // callback from off the main thread can be expensive.
  1.1316 +        if (manager->IsRuntimeActive() &&
  1.1317 +            manager->TimeSinceLastRuntimeStateChange() >= PRTime(PR_USEC_PER_SEC))
  1.1318 +        {
  1.1319 +            bool debuggerAttached = false;
  1.1320 +            nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
  1.1321 +            if (dbg)
  1.1322 +                dbg->GetIsDebuggerAttached(&debuggerAttached);
  1.1323 +            if (!debuggerAttached)
  1.1324 +                JS_RequestInterruptCallback(manager->Runtime()->Runtime());
  1.1325 +        }
  1.1326 +    }
  1.1327 +
  1.1328 +    // Tell the manager that we've shut down.
  1.1329 +    self->Finished();
  1.1330 +}
  1.1331 +
  1.1332 +PRTime
  1.1333 +XPCJSRuntime::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
  1.1334 +{
  1.1335 +    return mWatchdogManager->GetTimestamp(aCategory);
  1.1336 +}
  1.1337 +
  1.1338 +void
  1.1339 +xpc::SimulateActivityCallback(bool aActive)
  1.1340 +{
  1.1341 +    XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive);
  1.1342 +}
  1.1343 +
  1.1344 +// static
  1.1345 +JSContext*
  1.1346 +XPCJSRuntime::DefaultJSContextCallback(JSRuntime *rt)
  1.1347 +{
  1.1348 +    MOZ_ASSERT(rt == Get()->Runtime());
  1.1349 +    return Get()->GetJSContextStack()->GetSafeJSContext();
  1.1350 +}
  1.1351 +
  1.1352 +// static
  1.1353 +void
  1.1354 +XPCJSRuntime::ActivityCallback(void *arg, bool active)
  1.1355 +{
  1.1356 +    XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
  1.1357 +    self->mWatchdogManager->RecordRuntimeActivity(active);
  1.1358 +}
  1.1359 +
  1.1360 +// static
  1.1361 +//
  1.1362 +// JS-CTypes creates and caches a JSContext that it uses when executing JS
  1.1363 +// callbacks. When we're notified that ctypes is about to call into some JS,
  1.1364 +// push the cx to maintain the integrity of the context stack.
  1.1365 +void
  1.1366 +XPCJSRuntime::CTypesActivityCallback(JSContext *cx, js::CTypesActivityType type)
  1.1367 +{
  1.1368 +  if (type == js::CTYPES_CALLBACK_BEGIN) {
  1.1369 +    if (!xpc::PushJSContextNoScriptContext(cx))
  1.1370 +      MOZ_CRASH();
  1.1371 +  } else if (type == js::CTYPES_CALLBACK_END) {
  1.1372 +    xpc::PopJSContextNoScriptContext();
  1.1373 +  }
  1.1374 +}
  1.1375 +
  1.1376 +// static
  1.1377 +bool
  1.1378 +XPCJSRuntime::InterruptCallback(JSContext *cx)
  1.1379 +{
  1.1380 +    XPCJSRuntime *self = XPCJSRuntime::Get();
  1.1381 +
  1.1382 +    // If this is the first time the interrupt callback has fired since we last
  1.1383 +    // returned to the event loop, mark the checkpoint.
  1.1384 +    if (self->mSlowScriptCheckpoint.IsNull()) {
  1.1385 +        self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
  1.1386 +        return true;
  1.1387 +    }
  1.1388 +
  1.1389 +    // This is at least the second interrupt callback we've received since
  1.1390 +    // returning to the event loop. See how long it's been, and what the limit
  1.1391 +    // is.
  1.1392 +    TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
  1.1393 +    bool chrome =
  1.1394 +      nsContentUtils::IsSystemPrincipal(nsContentUtils::GetSubjectPrincipal());
  1.1395 +    const char *prefName = chrome ? "dom.max_chrome_script_run_time"
  1.1396 +                                  : "dom.max_script_run_time";
  1.1397 +    int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
  1.1398 +
  1.1399 +    // If there's no limit, or we're within the limit, let it go.
  1.1400 +    if (limit == 0 || duration.ToSeconds() < limit)
  1.1401 +        return true;
  1.1402 +
  1.1403 +    //
  1.1404 +    // This has gone on long enough! Time to take action. ;-)
  1.1405 +    //
  1.1406 +
  1.1407 +    // Get the DOM window associated with the running script. If the script is
  1.1408 +    // running in a non-DOM scope, we have to just let it keep running.
  1.1409 +    RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
  1.1410 +    nsRefPtr<nsGlobalWindow> win = WindowOrNull(global);
  1.1411 +    if (!win && IsSandbox(global)) {
  1.1412 +        // If this is a sandbox associated with a DOMWindow via a
  1.1413 +        // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
  1.1414 +        // and JetPack content scripts.
  1.1415 +        JS::Rooted<JSObject*> proto(cx);
  1.1416 +        if (!JS_GetPrototype(cx, global, &proto))
  1.1417 +            return false;
  1.1418 +        if (proto && IsSandboxPrototypeProxy(proto) &&
  1.1419 +            (proto = js::CheckedUnwrap(proto, /* stopAtOuter = */ false)))
  1.1420 +        {
  1.1421 +            win = WindowGlobalOrNull(proto);
  1.1422 +        }
  1.1423 +    }
  1.1424 +    if (!win)
  1.1425 +        return true;
  1.1426 +
  1.1427 +    // Show the prompt to the user, and kill if requested.
  1.1428 +    nsGlobalWindow::SlowScriptResponse response = win->ShowSlowScriptDialog();
  1.1429 +    if (response == nsGlobalWindow::KillSlowScript)
  1.1430 +        return false;
  1.1431 +
  1.1432 +    // The user chose to continue the script. Reset the timer, and disable this
  1.1433 +    // machinery with a pref of the user opted out of future slow-script dialogs.
  1.1434 +    self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
  1.1435 +    if (response == nsGlobalWindow::AlwaysContinueSlowScript)
  1.1436 +        Preferences::SetInt(prefName, 0);
  1.1437 +
  1.1438 +    return true;
  1.1439 +}
  1.1440 +
  1.1441 +/* static */ void
  1.1442 +XPCJSRuntime::OutOfMemoryCallback(JSContext *cx)
  1.1443 +{
  1.1444 +    if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
  1.1445 +        return;
  1.1446 +    }
  1.1447 +
  1.1448 +    nsCOMPtr<nsIMemoryInfoDumper> dumper =
  1.1449 +        do_GetService("@mozilla.org/memory-info-dumper;1");
  1.1450 +    if (!dumper) {
  1.1451 +        return;
  1.1452 +    }
  1.1453 +
  1.1454 +    // If this fails, it fails silently.
  1.1455 +    dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
  1.1456 +                                    /* minimizeMemoryUsage = */ false);
  1.1457 +}
  1.1458 +
  1.1459 +size_t
  1.1460 +XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
  1.1461 +{
  1.1462 +    size_t n = 0;
  1.1463 +    n += mallocSizeOf(this);
  1.1464 +    n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
  1.1465 +    n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
  1.1466 +    n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
  1.1467 +    n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
  1.1468 +
  1.1469 +    n += CycleCollectedJSRuntime::SizeOfExcludingThis(mallocSizeOf);
  1.1470 +
  1.1471 +    // There are other XPCJSRuntime members that could be measured; the above
  1.1472 +    // ones have been seen by DMD to be worth measuring.  More stuff may be
  1.1473 +    // added later.
  1.1474 +
  1.1475 +    return n;
  1.1476 +}
  1.1477 +
  1.1478 +nsString*
  1.1479 +XPCJSRuntime::NewShortLivedString()
  1.1480 +{
  1.1481 +    for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
  1.1482 +        if (mScratchStrings[i].empty()) {
  1.1483 +            mScratchStrings[i].construct();
  1.1484 +            return mScratchStrings[i].addr();
  1.1485 +        }
  1.1486 +    }
  1.1487 +
  1.1488 +    // All our internal string wrappers are used, allocate a new string.
  1.1489 +    return new nsString();
  1.1490 +}
  1.1491 +
  1.1492 +void
  1.1493 +XPCJSRuntime::DeleteShortLivedString(nsString *string)
  1.1494 +{
  1.1495 +    if (string == &EmptyString() || string == &NullString())
  1.1496 +        return;
  1.1497 +
  1.1498 +    for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
  1.1499 +        if (!mScratchStrings[i].empty() &&
  1.1500 +            mScratchStrings[i].addr() == string) {
  1.1501 +            // One of our internal strings is no longer in use, mark
  1.1502 +            // it as such and free its data.
  1.1503 +            mScratchStrings[i].destroy();
  1.1504 +            return;
  1.1505 +        }
  1.1506 +    }
  1.1507 +
  1.1508 +    // We're done with a string that's not one of our internal
  1.1509 +    // strings, delete it.
  1.1510 +    delete string;
  1.1511 +}
  1.1512 +
  1.1513 +/***************************************************************************/
  1.1514 +
  1.1515 +static PLDHashOperator
  1.1516 +DetachedWrappedNativeProtoShutdownMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
  1.1517 +                                         uint32_t number, void *arg)
  1.1518 +{
  1.1519 +    XPCWrappedNativeProto* proto =
  1.1520 +        (XPCWrappedNativeProto*)((PLDHashEntryStub*)hdr)->key;
  1.1521 +
  1.1522 +    proto->SystemIsBeingShutDown();
  1.1523 +    return PL_DHASH_NEXT;
  1.1524 +}
  1.1525 +
  1.1526 +void XPCJSRuntime::DestroyJSContextStack()
  1.1527 +{
  1.1528 +    delete mJSContextStack;
  1.1529 +    mJSContextStack = nullptr;
  1.1530 +}
  1.1531 +
  1.1532 +void XPCJSRuntime::SystemIsBeingShutDown()
  1.1533 +{
  1.1534 +    DOM_ClearInterfaces();
  1.1535 +
  1.1536 +    if (mDetachedWrappedNativeProtoMap)
  1.1537 +        mDetachedWrappedNativeProtoMap->
  1.1538 +            Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr);
  1.1539 +}
  1.1540 +
  1.1541 +#define JS_OPTIONS_DOT_STR "javascript.options."
  1.1542 +
  1.1543 +static void
  1.1544 +ReloadPrefsCallback(const char *pref, void *data)
  1.1545 +{
  1.1546 +    XPCJSRuntime *runtime = reinterpret_cast<XPCJSRuntime *>(data);
  1.1547 +    JSRuntime *rt = runtime->Runtime();
  1.1548 +
  1.1549 +    bool safeMode = false;
  1.1550 +    nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
  1.1551 +    if (xr) {
  1.1552 +        xr->GetInSafeMode(&safeMode);
  1.1553 +    }
  1.1554 +
  1.1555 +    bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode;
  1.1556 +    bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode;
  1.1557 +    bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode;
  1.1558 +
  1.1559 +    bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing");
  1.1560 +    bool parallelIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR
  1.1561 +                                                       "ion.parallel_compilation");
  1.1562 +    bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
  1.1563 +                                                 "baselinejit.unsafe_eager_compilation");
  1.1564 +    bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
  1.1565 +
  1.1566 +    sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
  1.1567 +
  1.1568 +    JS::RuntimeOptionsRef(rt).setBaseline(useBaseline)
  1.1569 +                             .setIon(useIon)
  1.1570 +                           .  setAsmJS(useAsmJS);
  1.1571 +
  1.1572 +    JS_SetParallelParsingEnabled(rt, parallelParsing);
  1.1573 +    JS_SetParallelIonCompilationEnabled(rt, parallelIonCompilation);
  1.1574 +    JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER,
  1.1575 +                                  useBaselineEager ? 0 : -1);
  1.1576 +    JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_ION_USECOUNT_TRIGGER,
  1.1577 +                                  useIonEager ? 0 : -1);
  1.1578 +}
  1.1579 +
  1.1580 +XPCJSRuntime::~XPCJSRuntime()
  1.1581 +{
  1.1582 +    // This destructor runs before ~CycleCollectedJSRuntime, which does the
  1.1583 +    // actual JS_DestroyRuntime() call. But destroying the runtime triggers
  1.1584 +    // one final GC, which can call back into the runtime with various
  1.1585 +    // callback if we aren't careful. Null out the relevant callbacks.
  1.1586 +    js::SetActivityCallback(Runtime(), nullptr, nullptr);
  1.1587 +    JS_SetFinalizeCallback(Runtime(), nullptr);
  1.1588 +
  1.1589 +    // Clear any pending exception.  It might be an XPCWrappedJS, and if we try
  1.1590 +    // to destroy it later we will crash.
  1.1591 +    SetPendingException(nullptr);
  1.1592 +
  1.1593 +    JS::SetGCSliceCallback(Runtime(), mPrevGCSliceCallback);
  1.1594 +
  1.1595 +    xpc_DelocalizeRuntime(Runtime());
  1.1596 +
  1.1597 +    if (mWatchdogManager->GetWatchdog())
  1.1598 +        mWatchdogManager->StopWatchdog();
  1.1599 +
  1.1600 +    if (mCallContext)
  1.1601 +        mCallContext->SystemIsBeingShutDown();
  1.1602 +
  1.1603 +    auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(Runtime()));
  1.1604 +    delete rtPrivate;
  1.1605 +    JS_SetRuntimePrivate(Runtime(), nullptr);
  1.1606 +
  1.1607 +    // clean up and destroy maps...
  1.1608 +    if (mWrappedJSMap) {
  1.1609 +        mWrappedJSMap->ShutdownMarker();
  1.1610 +        delete mWrappedJSMap;
  1.1611 +        mWrappedJSMap = nullptr;
  1.1612 +    }
  1.1613 +
  1.1614 +    if (mWrappedJSClassMap) {
  1.1615 +        delete mWrappedJSClassMap;
  1.1616 +        mWrappedJSClassMap = nullptr;
  1.1617 +    }
  1.1618 +
  1.1619 +    if (mIID2NativeInterfaceMap) {
  1.1620 +        delete mIID2NativeInterfaceMap;
  1.1621 +        mIID2NativeInterfaceMap = nullptr;
  1.1622 +    }
  1.1623 +
  1.1624 +    if (mClassInfo2NativeSetMap) {
  1.1625 +        delete mClassInfo2NativeSetMap;
  1.1626 +        mClassInfo2NativeSetMap = nullptr;
  1.1627 +    }
  1.1628 +
  1.1629 +    if (mNativeSetMap) {
  1.1630 +        delete mNativeSetMap;
  1.1631 +        mNativeSetMap = nullptr;
  1.1632 +    }
  1.1633 +
  1.1634 +    if (mThisTranslatorMap) {
  1.1635 +        delete mThisTranslatorMap;
  1.1636 +        mThisTranslatorMap = nullptr;
  1.1637 +    }
  1.1638 +
  1.1639 +    if (mNativeScriptableSharedMap) {
  1.1640 +        delete mNativeScriptableSharedMap;
  1.1641 +        mNativeScriptableSharedMap = nullptr;
  1.1642 +    }
  1.1643 +
  1.1644 +    if (mDyingWrappedNativeProtoMap) {
  1.1645 +        delete mDyingWrappedNativeProtoMap;
  1.1646 +        mDyingWrappedNativeProtoMap = nullptr;
  1.1647 +    }
  1.1648 +
  1.1649 +    if (mDetachedWrappedNativeProtoMap) {
  1.1650 +        delete mDetachedWrappedNativeProtoMap;
  1.1651 +        mDetachedWrappedNativeProtoMap = nullptr;
  1.1652 +    }
  1.1653 +
  1.1654 +#ifdef MOZ_ENABLE_PROFILER_SPS
  1.1655 +    // Tell the profiler that the runtime is gone
  1.1656 +    if (PseudoStack *stack = mozilla_get_pseudo_stack())
  1.1657 +        stack->sampleRuntime(nullptr);
  1.1658 +#endif
  1.1659 +
  1.1660 +#ifdef DEBUG
  1.1661 +    for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
  1.1662 +        MOZ_ASSERT(mScratchStrings[i].empty(), "Short lived string still in use");
  1.1663 +    }
  1.1664 +#endif
  1.1665 +
  1.1666 +    Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
  1.1667 +}
  1.1668 +
  1.1669 +static void
  1.1670 +GetCompartmentName(JSCompartment *c, nsCString &name, bool replaceSlashes)
  1.1671 +{
  1.1672 +    if (js::IsAtomsCompartment(c)) {
  1.1673 +        name.AssignLiteral("atoms");
  1.1674 +    } else if (JSPrincipals *principals = JS_GetCompartmentPrincipals(c)) {
  1.1675 +        nsJSPrincipals::get(principals)->GetScriptLocation(name);
  1.1676 +
  1.1677 +        // If the compartment's location (name) differs from the principal's
  1.1678 +        // script location, append the compartment's location to allow
  1.1679 +        // differentiation of multiple compartments owned by the same principal
  1.1680 +        // (e.g. components owned by the system or null principal).
  1.1681 +        CompartmentPrivate *compartmentPrivate = GetCompartmentPrivate(c);
  1.1682 +        if (compartmentPrivate) {
  1.1683 +            const nsACString& location = compartmentPrivate->GetLocation();
  1.1684 +            if (!location.IsEmpty() && !location.Equals(name)) {
  1.1685 +                name.AppendLiteral(", ");
  1.1686 +                name.Append(location);
  1.1687 +            }
  1.1688 +        }
  1.1689 +
  1.1690 +        // A hack: replace forward slashes with '\\' so they aren't
  1.1691 +        // treated as path separators.  Users of the reporters
  1.1692 +        // (such as about:memory) have to undo this change.
  1.1693 +        if (replaceSlashes)
  1.1694 +            name.ReplaceChar('/', '\\');
  1.1695 +    } else {
  1.1696 +        name.AssignLiteral("null-principal");
  1.1697 +    }
  1.1698 +}
  1.1699 +
  1.1700 +static int64_t
  1.1701 +JSMainRuntimeGCHeapDistinguishedAmount()
  1.1702 +{
  1.1703 +    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1.1704 +    return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
  1.1705 +           js::gc::ChunkSize;
  1.1706 +}
  1.1707 +
  1.1708 +static int64_t
  1.1709 +JSMainRuntimeTemporaryPeakDistinguishedAmount()
  1.1710 +{
  1.1711 +    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1.1712 +    return JS::PeakSizeOfTemporary(rt);
  1.1713 +}
  1.1714 +
  1.1715 +static int64_t
  1.1716 +JSMainRuntimeCompartmentsSystemDistinguishedAmount()
  1.1717 +{
  1.1718 +    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1.1719 +    return JS::SystemCompartmentCount(rt);
  1.1720 +}
  1.1721 +
  1.1722 +static int64_t
  1.1723 +JSMainRuntimeCompartmentsUserDistinguishedAmount()
  1.1724 +{
  1.1725 +    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1.1726 +    return JS::UserCompartmentCount(rt);
  1.1727 +}
  1.1728 +
  1.1729 +class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public nsIMemoryReporter
  1.1730 +{
  1.1731 +  public:
  1.1732 +    NS_DECL_ISUPPORTS
  1.1733 +
  1.1734 +    NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
  1.1735 +                              nsISupports* aData)
  1.1736 +    {
  1.1737 +        return MOZ_COLLECT_REPORT("js-main-runtime-temporary-peak",
  1.1738 +            KIND_OTHER, UNITS_BYTES,
  1.1739 +            JSMainRuntimeTemporaryPeakDistinguishedAmount(),
  1.1740 +            "Peak transient data size in the main JSRuntime (the current size "
  1.1741 +            "of which is reported as "
  1.1742 +            "'explicit/js-non-window/runtime/temporary').");
  1.1743 +    }
  1.1744 +};
  1.1745 +
  1.1746 +NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
  1.1747 +
  1.1748 +// The REPORT* macros do an unconditional report.  The ZCREPORT* macros are for
  1.1749 +// compartments and zones; they aggregate any entries smaller than
  1.1750 +// SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
  1.1751 +// entries for the compartment.
  1.1752 +
  1.1753 +#define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
  1.1754 +
  1.1755 +#define REPORT(_path, _kind, _units, _amount, _desc)                          \
  1.1756 +    do {                                                                      \
  1.1757 +        nsresult rv;                                                          \
  1.1758 +        rv = cb->Callback(EmptyCString(), _path,                              \
  1.1759 +                          nsIMemoryReporter::_kind,                           \
  1.1760 +                          nsIMemoryReporter::_units,                          \
  1.1761 +                          _amount,                                            \
  1.1762 +                          NS_LITERAL_CSTRING(_desc),                          \
  1.1763 +                          closure);                                           \
  1.1764 +        NS_ENSURE_SUCCESS(rv, rv);                                            \
  1.1765 +    } while (0)
  1.1766 +
  1.1767 +#define REPORT_BYTES(_path, _kind, _amount, _desc)                            \
  1.1768 +    REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
  1.1769 +
  1.1770 +#define REPORT_GC_BYTES(_path, _amount, _desc)                                \
  1.1771 +    do {                                                                      \
  1.1772 +        size_t amount = _amount;  /* evaluate _amount only once */            \
  1.1773 +        nsresult rv;                                                          \
  1.1774 +        rv = cb->Callback(EmptyCString(), _path,                              \
  1.1775 +                          nsIMemoryReporter::KIND_NONHEAP,                    \
  1.1776 +                          nsIMemoryReporter::UNITS_BYTES, amount,             \
  1.1777 +                          NS_LITERAL_CSTRING(_desc), closure);                \
  1.1778 +        NS_ENSURE_SUCCESS(rv, rv);                                            \
  1.1779 +        gcTotal += amount;                                                    \
  1.1780 +    } while (0)
  1.1781 +
  1.1782 +// Report compartment/zone non-GC (KIND_HEAP) bytes.
  1.1783 +#define ZCREPORT_BYTES(_path, _amount, _desc)                                 \
  1.1784 +    do {                                                                      \
  1.1785 +        /* Assign _descLiteral plus "" into a char* to prove that it's */     \
  1.1786 +        /* actually a literal. */                                             \
  1.1787 +        size_t amount = _amount;  /* evaluate _amount only once */            \
  1.1788 +        if (amount >= SUNDRIES_THRESHOLD) {                                   \
  1.1789 +            nsresult rv;                                                      \
  1.1790 +            rv = cb->Callback(EmptyCString(), _path,                          \
  1.1791 +                              nsIMemoryReporter::KIND_HEAP,                   \
  1.1792 +                              nsIMemoryReporter::UNITS_BYTES, amount,         \
  1.1793 +                              NS_LITERAL_CSTRING(_desc), closure);            \
  1.1794 +            NS_ENSURE_SUCCESS(rv, rv);                                        \
  1.1795 +        } else {                                                              \
  1.1796 +            sundriesMallocHeap += amount;                                     \
  1.1797 +        }                                                                     \
  1.1798 +    } while (0)
  1.1799 +
  1.1800 +// Report compartment/zone GC bytes.
  1.1801 +#define ZCREPORT_GC_BYTES(_path, _amount, _desc)                              \
  1.1802 +    do {                                                                      \
  1.1803 +        size_t amount = _amount;  /* evaluate _amount only once */            \
  1.1804 +        if (amount >= SUNDRIES_THRESHOLD) {                                   \
  1.1805 +            nsresult rv;                                                      \
  1.1806 +            rv = cb->Callback(EmptyCString(), _path,                          \
  1.1807 +                              nsIMemoryReporter::KIND_NONHEAP,                \
  1.1808 +                              nsIMemoryReporter::UNITS_BYTES, amount,         \
  1.1809 +                              NS_LITERAL_CSTRING(_desc), closure);            \
  1.1810 +            NS_ENSURE_SUCCESS(rv, rv);                                        \
  1.1811 +            gcTotal += amount;                                                \
  1.1812 +        } else {                                                              \
  1.1813 +            sundriesGCHeap += amount;                                         \
  1.1814 +        }                                                                     \
  1.1815 +    } while (0)
  1.1816 +
  1.1817 +// Report runtime bytes.
  1.1818 +#define RREPORT_BYTES(_path, _kind, _amount, _desc)                           \
  1.1819 +    do {                                                                      \
  1.1820 +        size_t amount = _amount;  /* evaluate _amount only once */            \
  1.1821 +        nsresult rv;                                                          \
  1.1822 +        rv = cb->Callback(EmptyCString(), _path,                              \
  1.1823 +                          nsIMemoryReporter::_kind,                           \
  1.1824 +                          nsIMemoryReporter::UNITS_BYTES, amount,             \
  1.1825 +                          NS_LITERAL_CSTRING(_desc), closure);                \
  1.1826 +        NS_ENSURE_SUCCESS(rv, rv);                                            \
  1.1827 +        rtTotal += amount;                                                    \
  1.1828 +    } while (0)
  1.1829 +
  1.1830 +MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
  1.1831 +
  1.1832 +namespace xpc {
  1.1833 +
  1.1834 +static nsresult
  1.1835 +ReportZoneStats(const JS::ZoneStats &zStats,
  1.1836 +                const xpc::ZoneStatsExtras &extras,
  1.1837 +                nsIMemoryReporterCallback *cb,
  1.1838 +                nsISupports *closure, size_t *gcTotalOut = nullptr)
  1.1839 +{
  1.1840 +    const nsAutoCString& pathPrefix = extras.pathPrefix;
  1.1841 +    size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
  1.1842 +
  1.1843 +    MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
  1.1844 +
  1.1845 +    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
  1.1846 +        zStats.gcHeapArenaAdmin,
  1.1847 +        "Bookkeeping information and alignment padding within GC arenas.");
  1.1848 +
  1.1849 +    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
  1.1850 +        zStats.unusedGCThings,
  1.1851 +        "Empty GC thing cells within non-empty arenas.");
  1.1852 +
  1.1853 +    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
  1.1854 +        zStats.lazyScriptsGCHeap,
  1.1855 +        "Scripts that haven't executed yet.");
  1.1856 +
  1.1857 +    ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
  1.1858 +        zStats.lazyScriptsMallocHeap,
  1.1859 +        "Lazy script tables containing free variables or inner functions.");
  1.1860 +
  1.1861 +    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"),
  1.1862 +        zStats.jitCodesGCHeap,
  1.1863 +        "References to executable code pools used by the JITs.");
  1.1864 +
  1.1865 +    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/gc-heap"),
  1.1866 +        zStats.typeObjectsGCHeap,
  1.1867 +        "Type inference information about objects.");
  1.1868 +
  1.1869 +    ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects/malloc-heap"),
  1.1870 +        zStats.typeObjectsMallocHeap,
  1.1871 +        "Type object addenda.");
  1.1872 +
  1.1873 +    ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
  1.1874 +        zStats.typePool,
  1.1875 +        "Type sets and related data.");
  1.1876 +
  1.1877 +    ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
  1.1878 +        zStats.baselineStubsOptimized,
  1.1879 +        "The Baseline JIT's optimized IC stubs (excluding code).");
  1.1880 +
  1.1881 +    size_t stringsNotableAboutMemoryGCHeap = 0;
  1.1882 +    size_t stringsNotableAboutMemoryMallocHeap = 0;
  1.1883 +
  1.1884 +    #define MAYBE_INLINE \
  1.1885 +        "The characters may be inline or on the malloc heap."
  1.1886 +    #define MAYBE_OVERALLOCATED \
  1.1887 +        "Sometimes over-allocated to simplify string concatenation."
  1.1888 +
  1.1889 +    for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
  1.1890 +        const JS::NotableStringInfo& info = zStats.notableStrings[i];
  1.1891 +
  1.1892 +        MOZ_ASSERT(!zStats.isTotals);
  1.1893 +
  1.1894 +        nsDependentCString notableString(info.buffer);
  1.1895 +
  1.1896 +        // Viewing about:memory generates many notable strings which contain
  1.1897 +        // "string(length=".  If we report these as notable, then we'll create
  1.1898 +        // even more notable strings the next time we open about:memory (unless
  1.1899 +        // there's a GC in the meantime), and so on ad infinitum.
  1.1900 +        //
  1.1901 +        // To avoid cluttering up about:memory like this, we stick notable
  1.1902 +        // strings which contain "string(length=" into their own bucket.
  1.1903 +#       define STRING_LENGTH "string(length="
  1.1904 +        if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
  1.1905 +            stringsNotableAboutMemoryGCHeap += info.gcHeap;
  1.1906 +            stringsNotableAboutMemoryMallocHeap += info.mallocHeap;
  1.1907 +            continue;
  1.1908 +        }
  1.1909 +
  1.1910 +        // Escape / to \ before we put notableString into the memory reporter
  1.1911 +        // path, because we don't want any forward slashes in the string to
  1.1912 +        // count as path separators.
  1.1913 +        nsCString escapedString(notableString);
  1.1914 +        escapedString.ReplaceSubstring("/", "\\");
  1.1915 +
  1.1916 +        bool truncated = notableString.Length() < info.length;
  1.1917 +
  1.1918 +        nsCString path = pathPrefix +
  1.1919 +            nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
  1.1920 +                            info.length, info.numCopies, escapedString.get(),
  1.1921 +                            truncated ? " (truncated)" : "");
  1.1922 +
  1.1923 +        if (info.gcHeap > 0) {
  1.1924 +            REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap"),
  1.1925 +                info.gcHeap,
  1.1926 +                "Strings. " MAYBE_INLINE);
  1.1927 +        }
  1.1928 +
  1.1929 +        if (info.mallocHeap > 0) {
  1.1930 +            REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"),
  1.1931 +                KIND_HEAP, info.mallocHeap,
  1.1932 +                "Non-inline string characters. " MAYBE_OVERALLOCATED);
  1.1933 +        }
  1.1934 +    }
  1.1935 +
  1.1936 +    nsCString nonNotablePath = pathPrefix;
  1.1937 +    nonNotablePath += zStats.isTotals
  1.1938 +                    ? NS_LITERAL_CSTRING("strings/")
  1.1939 +                    : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
  1.1940 +
  1.1941 +    if (zStats.stringInfo.gcHeap > 0) {
  1.1942 +        REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"),
  1.1943 +            zStats.stringInfo.gcHeap,
  1.1944 +            "Strings. " MAYBE_INLINE);
  1.1945 +    }
  1.1946 +
  1.1947 +    if (zStats.stringInfo.mallocHeap > 0) {
  1.1948 +        REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap"),
  1.1949 +            KIND_HEAP, zStats.stringInfo.mallocHeap,
  1.1950 +            "Non-inline string characters. " MAYBE_OVERALLOCATED);
  1.1951 +    }
  1.1952 +
  1.1953 +    if (stringsNotableAboutMemoryGCHeap > 0) {
  1.1954 +        MOZ_ASSERT(!zStats.isTotals);
  1.1955 +        REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
  1.1956 +            stringsNotableAboutMemoryGCHeap,
  1.1957 +            "Strings that contain the characters '" STRING_LENGTH "', which "
  1.1958 +            "are probably from about:memory itself." MAYBE_INLINE
  1.1959 +            " We filter them out rather than display them, because displaying "
  1.1960 +            "them would create even more such strings every time about:memory "
  1.1961 +            "is refreshed.");
  1.1962 +    }
  1.1963 +
  1.1964 +    if (stringsNotableAboutMemoryMallocHeap > 0) {
  1.1965 +        MOZ_ASSERT(!zStats.isTotals);
  1.1966 +        REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
  1.1967 +            KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
  1.1968 +            "Non-inline string characters of strings that contain the "
  1.1969 +            "characters '" STRING_LENGTH "', which are probably from "
  1.1970 +            "about:memory itself. " MAYBE_OVERALLOCATED
  1.1971 +            " We filter them out rather than display them, because displaying "
  1.1972 +            "them would create even more such strings every time about:memory "
  1.1973 +            "is refreshed.");
  1.1974 +    }
  1.1975 +
  1.1976 +    if (sundriesGCHeap > 0) {
  1.1977 +        // We deliberately don't use ZCREPORT_GC_BYTES here.
  1.1978 +        REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
  1.1979 +            sundriesGCHeap,
  1.1980 +            "The sum of all 'gc-heap' measurements that are too small to be "
  1.1981 +            "worth showing individually.");
  1.1982 +    }
  1.1983 +
  1.1984 +    if (sundriesMallocHeap > 0) {
  1.1985 +        // We deliberately don't use ZCREPORT_BYTES here.
  1.1986 +        REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
  1.1987 +            KIND_HEAP, sundriesMallocHeap,
  1.1988 +            "The sum of all 'malloc-heap' measurements that are too small to "
  1.1989 +            "be worth showing individually.");
  1.1990 +    }
  1.1991 +
  1.1992 +    if (gcTotalOut)
  1.1993 +        *gcTotalOut += gcTotal;
  1.1994 +
  1.1995 +    return NS_OK;
  1.1996 +
  1.1997 +#   undef STRING_LENGTH
  1.1998 +}
  1.1999 +
  1.2000 +static nsresult
  1.2001 +ReportCompartmentStats(const JS::CompartmentStats &cStats,
  1.2002 +                       const xpc::CompartmentStatsExtras &extras,
  1.2003 +                       amIAddonManager *addonManager,
  1.2004 +                       nsIMemoryReporterCallback *cb,
  1.2005 +                       nsISupports *closure, size_t *gcTotalOut = nullptr)
  1.2006 +{
  1.2007 +    static const nsDependentCString addonPrefix("explicit/add-ons/");
  1.2008 +
  1.2009 +    size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
  1.2010 +    nsAutoCString cJSPathPrefix = extras.jsPathPrefix;
  1.2011 +    nsAutoCString cDOMPathPrefix = extras.domPathPrefix;
  1.2012 +
  1.2013 +    // Only attempt to prefix if we got a location and the path wasn't already
  1.2014 +    // prefixed.
  1.2015 +    if (extras.location && addonManager &&
  1.2016 +        cJSPathPrefix.Find(addonPrefix, false, 0, 0) != 0) {
  1.2017 +        nsAutoCString addonId;
  1.2018 +        bool ok;
  1.2019 +        if (NS_SUCCEEDED(addonManager->MapURIToAddonID(extras.location,
  1.2020 +                                                        addonId, &ok))
  1.2021 +            && ok) {
  1.2022 +            // Insert the add-on id as "add-ons/@id@/" after "explicit/" to
  1.2023 +            // aggregate add-on compartments.
  1.2024 +            static const size_t explicitLength = strlen("explicit/");
  1.2025 +            addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
  1.2026 +            addonId += "/";
  1.2027 +            cJSPathPrefix.Insert(addonId, explicitLength);
  1.2028 +            cDOMPathPrefix.Insert(addonId, explicitLength);
  1.2029 +        }
  1.2030 +    }
  1.2031 +
  1.2032 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/ordinary"),
  1.2033 +        cStats.objectsGCHeapOrdinary,
  1.2034 +        "Ordinary objects, i.e. not otherwise distinguished by memory "
  1.2035 +        "reporters.");
  1.2036 +
  1.2037 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/function"),
  1.2038 +        cStats.objectsGCHeapFunction,
  1.2039 +        "Function objects.");
  1.2040 +
  1.2041 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/dense-array"),
  1.2042 +        cStats.objectsGCHeapDenseArray,
  1.2043 +        "Dense array objects.");
  1.2044 +
  1.2045 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/slow-array"),
  1.2046 +        cStats.objectsGCHeapSlowArray,
  1.2047 +        "Slow array objects.");
  1.2048 +
  1.2049 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/gc-heap/cross-compartment-wrapper"),
  1.2050 +        cStats.objectsGCHeapCrossCompartmentWrapper,
  1.2051 +        "Cross-compartment wrapper objects.");
  1.2052 +
  1.2053 +    // Note that we use cDOMPathPrefix here.  This is because we measure orphan
  1.2054 +    // DOM nodes in the JS reporter, but we want to report them in a "dom"
  1.2055 +    // sub-tree rather than a "js" sub-tree.
  1.2056 +    ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
  1.2057 +        cStats.objectsPrivate,
  1.2058 +        "Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
  1.2059 +        "objects.");
  1.2060 +
  1.2061 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/global-parented"),
  1.2062 +        cStats.shapesGCHeapTreeGlobalParented,
  1.2063 +        "Shapes that (a) are in a property tree, and (b) represent an object "
  1.2064 +        "whose parent is the global object.");
  1.2065 +
  1.2066 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree/non-global-parented"),
  1.2067 +        cStats.shapesGCHeapTreeNonGlobalParented,
  1.2068 +        "Shapes that (a) are in a property tree, and (b) represent an object "
  1.2069 +        "whose parent is not the global object.");
  1.2070 +
  1.2071 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
  1.2072 +        cStats.shapesGCHeapDict,
  1.2073 +        "Shapes that are in dictionary mode.");
  1.2074 +
  1.2075 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
  1.2076 +        cStats.shapesGCHeapBase,
  1.2077 +        "Base shapes, which collate data common to many shapes.");
  1.2078 +
  1.2079 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
  1.2080 +        cStats.shapesMallocHeapTreeTables,
  1.2081 +        "Property tables belonging to shapes that are in a property tree.");
  1.2082 +
  1.2083 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
  1.2084 +        cStats.shapesMallocHeapDictTables,
  1.2085 +        "Property tables that belong to shapes that are in dictionary mode.");
  1.2086 +
  1.2087 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-shape-kids"),
  1.2088 +        cStats.shapesMallocHeapTreeShapeKids,
  1.2089 +        "Kid hashes that belong to shapes that are in a property tree.");
  1.2090 +
  1.2091 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/compartment-tables"),
  1.2092 +        cStats.shapesMallocHeapCompartmentTables,
  1.2093 +        "Compartment-wide tables storing shape information used during object "
  1.2094 +        "construction.");
  1.2095 +
  1.2096 +    ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
  1.2097 +        cStats.scriptsGCHeap,
  1.2098 +        "JSScript instances. There is one per user-defined function in a "
  1.2099 +        "script, and one for the top-level code in a script.");
  1.2100 +
  1.2101 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/malloc-heap/data"),
  1.2102 +        cStats.scriptsMallocHeapData,
  1.2103 +        "Various variable-length tables in JSScripts.");
  1.2104 +
  1.2105 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/data"),
  1.2106 +        cStats.baselineData,
  1.2107 +        "The Baseline JIT's compilation data (BaselineScripts).");
  1.2108 +
  1.2109 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"),
  1.2110 +        cStats.baselineStubsFallback,
  1.2111 +        "The Baseline JIT's fallback IC stubs (excluding code).");
  1.2112 +
  1.2113 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("ion-data"),
  1.2114 +        cStats.ionData,
  1.2115 +        "The IonMonkey JIT's compilation data (IonScripts).");
  1.2116 +
  1.2117 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
  1.2118 +        cStats.typeInferenceTypeScripts,
  1.2119 +        "Type sets associated with scripts.");
  1.2120 +
  1.2121 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
  1.2122 +        cStats.typeInferenceAllocationSiteTables,
  1.2123 +        "Tables of type objects associated with allocation sites.");
  1.2124 +
  1.2125 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
  1.2126 +        cStats.typeInferenceArrayTypeTables,
  1.2127 +        "Tables of type objects associated with array literals.");
  1.2128 +
  1.2129 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"),
  1.2130 +        cStats.typeInferenceObjectTypeTables,
  1.2131 +        "Tables of type objects associated with object literals.");
  1.2132 +
  1.2133 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-object"),
  1.2134 +        cStats.compartmentObject,
  1.2135 +        "The JSCompartment object itself.");
  1.2136 +
  1.2137 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
  1.2138 +        cStats.crossCompartmentWrappersTable,
  1.2139 +        "The cross-compartment wrapper table.");
  1.2140 +
  1.2141 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("regexp-compartment"),
  1.2142 +        cStats.regexpCompartment,
  1.2143 +        "The regexp compartment.");
  1.2144 +
  1.2145 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("debuggees-set"),
  1.2146 +        cStats.debuggeesSet,
  1.2147 +        "The debuggees set.");
  1.2148 +
  1.2149 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
  1.2150 +        cStats.objectsExtra.mallocHeapSlots,
  1.2151 +        "Non-fixed object slot arrays, which represent object properties.");
  1.2152 +
  1.2153 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/non-asm.js"),
  1.2154 +        cStats.objectsExtra.mallocHeapElementsNonAsmJS,
  1.2155 +        "Non-asm.js indexed elements.");
  1.2156 +
  1.2157 +    // asm.js arrays are heap-allocated on some platforms and
  1.2158 +    // non-heap-allocated on others.  We never put them under sundries,
  1.2159 +    // because (a) in practice they're almost always larger than the sundries
  1.2160 +    // threshold, and (b) we'd need a third category of sundries ("non-heap"),
  1.2161 +    // which would be a pain.
  1.2162 +    size_t mallocHeapElementsAsmJS = cStats.objectsExtra.mallocHeapElementsAsmJS;
  1.2163 +    size_t nonHeapElementsAsmJS    = cStats.objectsExtra.nonHeapElementsAsmJS;
  1.2164 +    MOZ_ASSERT(mallocHeapElementsAsmJS == 0 || nonHeapElementsAsmJS == 0);
  1.2165 +    if (mallocHeapElementsAsmJS > 0) {
  1.2166 +        REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
  1.2167 +            KIND_HEAP, mallocHeapElementsAsmJS,
  1.2168 +            "asm.js array buffer elements on the malloc heap.");
  1.2169 +    }
  1.2170 +    if (nonHeapElementsAsmJS > 0) {
  1.2171 +        REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"),
  1.2172 +            KIND_NONHEAP, nonHeapElementsAsmJS,
  1.2173 +            "asm.js array buffer elements outside both the malloc heap and "
  1.2174 +            "the GC heap.");
  1.2175 +    }
  1.2176 +
  1.2177 +    if (cStats.objectsExtra.nonHeapElementsMapped > 0) {
  1.2178 +        REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"),
  1.2179 +            KIND_NONHEAP, cStats.objectsExtra.nonHeapElementsMapped,
  1.2180 +            "Memory-mapped array buffer elements.");
  1.2181 +    }
  1.2182 +
  1.2183 +    REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"),
  1.2184 +        KIND_NONHEAP, cStats.objectsExtra.nonHeapCodeAsmJS,
  1.2185 +        "AOT-compiled asm.js code.");
  1.2186 +
  1.2187 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/asm.js-module-data"),
  1.2188 +        cStats.objectsExtra.mallocHeapAsmJSModuleData,
  1.2189 +        "asm.js module data.");
  1.2190 +
  1.2191 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/arguments-data"),
  1.2192 +        cStats.objectsExtra.mallocHeapArgumentsData,
  1.2193 +        "Data belonging to Arguments objects.");
  1.2194 +
  1.2195 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/regexp-statics"),
  1.2196 +        cStats.objectsExtra.mallocHeapRegExpStatics,
  1.2197 +        "Data belonging to the RegExpStatics object.");
  1.2198 +
  1.2199 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/property-iterator-data"),
  1.2200 +        cStats.objectsExtra.mallocHeapPropertyIteratorData,
  1.2201 +        "Data belonging to property iterator objects.");
  1.2202 +
  1.2203 +    ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/ctypes-data"),
  1.2204 +        cStats.objectsExtra.mallocHeapCtypesData,
  1.2205 +        "Data belonging to ctypes objects.");
  1.2206 +
  1.2207 +    if (sundriesGCHeap > 0) {
  1.2208 +        // We deliberately don't use ZCREPORT_GC_BYTES here.
  1.2209 +        REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
  1.2210 +            sundriesGCHeap,
  1.2211 +            "The sum of all 'gc-heap' measurements that are too small to be "
  1.2212 +            "worth showing individually.");
  1.2213 +    }
  1.2214 +
  1.2215 +    if (sundriesMallocHeap > 0) {
  1.2216 +        // We deliberately don't use ZCREPORT_BYTES here.
  1.2217 +        REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
  1.2218 +            KIND_HEAP, sundriesMallocHeap,
  1.2219 +            "The sum of all 'malloc-heap' measurements that are too small to "
  1.2220 +            "be worth showing individually.");
  1.2221 +    }
  1.2222 +
  1.2223 +    if (gcTotalOut)
  1.2224 +        *gcTotalOut += gcTotal;
  1.2225 +
  1.2226 +    return NS_OK;
  1.2227 +}
  1.2228 +
  1.2229 +static nsresult
  1.2230 +ReportScriptSourceStats(const ScriptSourceInfo &scriptSourceInfo,
  1.2231 +                        const nsACString &path,
  1.2232 +                        nsIHandleReportCallback *cb, nsISupports *closure,
  1.2233 +                        size_t &rtTotal)
  1.2234 +{
  1.2235 +    if (scriptSourceInfo.compressed > 0) {
  1.2236 +        RREPORT_BYTES(path + NS_LITERAL_CSTRING("compressed"),
  1.2237 +            KIND_HEAP, scriptSourceInfo.compressed,
  1.2238 +            "Compressed JavaScript source code.");
  1.2239 +    }
  1.2240 +
  1.2241 +    if (scriptSourceInfo.uncompressed > 0) {
  1.2242 +        RREPORT_BYTES(path + NS_LITERAL_CSTRING("uncompressed"),
  1.2243 +            KIND_HEAP, scriptSourceInfo.uncompressed,
  1.2244 +            "Uncompressed JavaScript source code.");
  1.2245 +    }
  1.2246 +
  1.2247 +    if (scriptSourceInfo.misc > 0) {
  1.2248 +        RREPORT_BYTES(path + NS_LITERAL_CSTRING("misc"),
  1.2249 +            KIND_HEAP, scriptSourceInfo.misc,
  1.2250 +            "Miscellaneous data relating to JavaScript source code.");
  1.2251 +    }
  1.2252 +
  1.2253 +    return NS_OK;
  1.2254 +}
  1.2255 +
  1.2256 +static nsresult
  1.2257 +ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
  1.2258 +                                 const nsACString &rtPath,
  1.2259 +                                 amIAddonManager* addonManager,
  1.2260 +                                 nsIMemoryReporterCallback *cb,
  1.2261 +                                 nsISupports *closure, size_t *rtTotalOut)
  1.2262 +{
  1.2263 +    nsresult rv;
  1.2264 +
  1.2265 +    size_t gcTotal = 0;
  1.2266 +
  1.2267 +    for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
  1.2268 +        const JS::ZoneStats &zStats = rtStats.zoneStatsVector[i];
  1.2269 +        const xpc::ZoneStatsExtras *extras =
  1.2270 +          static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
  1.2271 +        rv = ReportZoneStats(zStats, *extras, cb, closure, &gcTotal);
  1.2272 +        NS_ENSURE_SUCCESS(rv, rv);
  1.2273 +    }
  1.2274 +
  1.2275 +    for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
  1.2276 +        JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i];
  1.2277 +        const xpc::CompartmentStatsExtras *extras =
  1.2278 +            static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra);
  1.2279 +        rv = ReportCompartmentStats(cStats, *extras, addonManager, cb, closure,
  1.2280 +                                    &gcTotal);
  1.2281 +        NS_ENSURE_SUCCESS(rv, rv);
  1.2282 +    }
  1.2283 +
  1.2284 +    // Report the rtStats.runtime numbers under "runtime/", and compute their
  1.2285 +    // total for later.
  1.2286 +
  1.2287 +    size_t rtTotal = 0;
  1.2288 +
  1.2289 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"),
  1.2290 +        KIND_HEAP, rtStats.runtime.object,
  1.2291 +        "The JSRuntime object.");
  1.2292 +
  1.2293 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"),
  1.2294 +        KIND_HEAP, rtStats.runtime.atomsTable,
  1.2295 +        "The atoms table.");
  1.2296 +
  1.2297 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"),
  1.2298 +        KIND_HEAP, rtStats.runtime.contexts,
  1.2299 +        "JSContext objects and structures that belong to them.");
  1.2300 +
  1.2301 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/dtoa"),
  1.2302 +        KIND_HEAP, rtStats.runtime.dtoa,
  1.2303 +        "The DtoaState object, which is used for converting strings to "
  1.2304 +        "numbers and vice versa.");
  1.2305 +
  1.2306 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"),
  1.2307 +        KIND_HEAP, rtStats.runtime.temporary,
  1.2308 +        "Transient data (mostly parse nodes) held by the JSRuntime during "
  1.2309 +        "compilation.");
  1.2310 +
  1.2311 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/regexp-data"),
  1.2312 +        KIND_NONHEAP, rtStats.runtime.regexpData,
  1.2313 +        "Regexp JIT data.");
  1.2314 +
  1.2315 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
  1.2316 +        KIND_HEAP, rtStats.runtime.interpreterStack,
  1.2317 +        "JS interpreter frames.");
  1.2318 +
  1.2319 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"),
  1.2320 +        KIND_HEAP, rtStats.runtime.mathCache,
  1.2321 +        "The math cache.");
  1.2322 +
  1.2323 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/source-data-cache"),
  1.2324 +        KIND_HEAP, rtStats.runtime.sourceDataCache,
  1.2325 +        "The source data cache, which holds decompressed script source code.");
  1.2326 +
  1.2327 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"),
  1.2328 +        KIND_HEAP, rtStats.runtime.scriptData,
  1.2329 +        "The table holding script data shared in the runtime.");
  1.2330 +
  1.2331 +    nsCString nonNotablePath =
  1.2332 +        rtPath + nsPrintfCString("runtime/script-sources/source(scripts=%d, <non-notable files>)/",
  1.2333 +                                 rtStats.runtime.scriptSourceInfo.numScripts);
  1.2334 +
  1.2335 +    rv = ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo,
  1.2336 +                                 nonNotablePath, cb, closure, rtTotal);
  1.2337 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2338 +
  1.2339 +    for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) {
  1.2340 +        const JS::NotableScriptSourceInfo& scriptSourceInfo =
  1.2341 +            rtStats.runtime.notableScriptSources[i];
  1.2342 +
  1.2343 +        // Escape / to \ before we put the filename into the memory reporter
  1.2344 +        // path, because we don't want any forward slashes in the string to
  1.2345 +        // count as path separators. Consumers of memory reporters (e.g.
  1.2346 +        // about:memory) will convert them back to / after doing path
  1.2347 +        // splitting.
  1.2348 +        nsDependentCString filename(scriptSourceInfo.filename_);
  1.2349 +        nsCString escapedFilename(filename);
  1.2350 +        escapedFilename.ReplaceSubstring("/", "\\");
  1.2351 +
  1.2352 +        nsCString notablePath = rtPath +
  1.2353 +            nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
  1.2354 +                            scriptSourceInfo.numScripts, escapedFilename.get());
  1.2355 +
  1.2356 +        rv = ReportScriptSourceStats(scriptSourceInfo, notablePath,
  1.2357 +                                     cb, closure, rtTotal);
  1.2358 +        NS_ENSURE_SUCCESS(rv, rv);
  1.2359 +    }
  1.2360 +
  1.2361 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/ion"),
  1.2362 +        KIND_NONHEAP, rtStats.runtime.code.ion,
  1.2363 +        "Code generated by the IonMonkey JIT.");
  1.2364 +
  1.2365 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/baseline"),
  1.2366 +        KIND_NONHEAP, rtStats.runtime.code.baseline,
  1.2367 +        "Code generated by the Baseline JIT.");
  1.2368 +
  1.2369 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/regexp"),
  1.2370 +        KIND_NONHEAP, rtStats.runtime.code.regexp,
  1.2371 +        "Code generated by the regexp JIT.");
  1.2372 +
  1.2373 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/other"),
  1.2374 +        KIND_NONHEAP, rtStats.runtime.code.other,
  1.2375 +        "Code generated by the JITs for wrappers and trampolines.");
  1.2376 +
  1.2377 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/unused"),
  1.2378 +        KIND_NONHEAP, rtStats.runtime.code.unused,
  1.2379 +        "Memory allocated by one of the JITs to hold code, but which is "
  1.2380 +        "currently unused.");
  1.2381 +
  1.2382 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"),
  1.2383 +        KIND_HEAP, rtStats.runtime.gc.marker,
  1.2384 +        "The GC mark stack and gray roots.");
  1.2385 +
  1.2386 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"),
  1.2387 +        KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted,
  1.2388 +        "Memory being used by the GC's nursery.");
  1.2389 +
  1.2390 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-huge-slots"),
  1.2391 +        KIND_NONHEAP, rtStats.runtime.gc.nurseryHugeSlots,
  1.2392 +        "Out-of-line slots and elements belonging to objects in the "
  1.2393 +        "nursery.");
  1.2394 +
  1.2395 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"),
  1.2396 +        KIND_HEAP, rtStats.runtime.gc.storeBufferVals,
  1.2397 +        "Values in the store buffer.");
  1.2398 +
  1.2399 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"),
  1.2400 +        KIND_HEAP, rtStats.runtime.gc.storeBufferCells,
  1.2401 +        "Cells in the store buffer.");
  1.2402 +
  1.2403 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/slots"),
  1.2404 +        KIND_HEAP, rtStats.runtime.gc.storeBufferSlots,
  1.2405 +        "Slots in the store buffer.");
  1.2406 +
  1.2407 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/whole-cells"),
  1.2408 +        KIND_HEAP, rtStats.runtime.gc.storeBufferWholeCells,
  1.2409 +        "Whole cells in the store buffer.");
  1.2410 +
  1.2411 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-vals"),
  1.2412 +        KIND_HEAP, rtStats.runtime.gc.storeBufferRelocVals,
  1.2413 +        "Relocatable values in the store buffer.");
  1.2414 +
  1.2415 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/reloc-cells"),
  1.2416 +        KIND_HEAP, rtStats.runtime.gc.storeBufferRelocCells,
  1.2417 +        "Relocatable cells in the store buffer.");
  1.2418 +
  1.2419 +    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/generics"),
  1.2420 +        KIND_HEAP, rtStats.runtime.gc.storeBufferGenerics,
  1.2421 +        "Generic things in the store buffer.");
  1.2422 +
  1.2423 +    if (rtTotalOut)
  1.2424 +        *rtTotalOut = rtTotal;
  1.2425 +
  1.2426 +    // Report GC numbers that don't belong to a compartment.
  1.2427 +
  1.2428 +    // We don't want to report decommitted memory in "explicit", so we just
  1.2429 +    // change the leading "explicit/" to "decommitted/".
  1.2430 +    nsCString rtPath2(rtPath);
  1.2431 +    rtPath2.Replace(0, strlen("explicit"), NS_LITERAL_CSTRING("decommitted"));
  1.2432 +    REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"),
  1.2433 +        rtStats.gcHeapDecommittedArenas,
  1.2434 +        "GC arenas in non-empty chunks that is decommitted, i.e. it takes up "
  1.2435 +        "address space but no physical memory or swap space.");
  1.2436 +
  1.2437 +    REPORT_BYTES(rtPath2 + NS_LITERAL_CSTRING("runtime/gc/nursery-decommitted"),
  1.2438 +        KIND_NONHEAP, rtStats.runtime.gc.nurseryDecommitted,
  1.2439 +        "Memory allocated to the GC's nursery this is decommitted, i.e. it takes up "
  1.2440 +        "address space but no physical memory or swap space.");
  1.2441 +
  1.2442 +    REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"),
  1.2443 +        rtStats.gcHeapUnusedChunks,
  1.2444 +        "Empty GC chunks which will soon be released unless claimed for new "
  1.2445 +        "allocations.");
  1.2446 +
  1.2447 +    REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
  1.2448 +        rtStats.gcHeapUnusedArenas,
  1.2449 +        "Empty GC arenas within non-empty chunks.");
  1.2450 +
  1.2451 +    REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"),
  1.2452 +        rtStats.gcHeapChunkAdmin,
  1.2453 +        "Bookkeeping information within GC chunks.");
  1.2454 +
  1.2455 +    // gcTotal is the sum of everything we've reported for the GC heap.  It
  1.2456 +    // should equal rtStats.gcHeapChunkTotal.
  1.2457 +    MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
  1.2458 +
  1.2459 +    return NS_OK;
  1.2460 +}
  1.2461 +
  1.2462 +nsresult
  1.2463 +ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
  1.2464 +                                 const nsACString &rtPath,
  1.2465 +                                 nsIMemoryReporterCallback *cb,
  1.2466 +                                 nsISupports *closure, size_t *rtTotalOut)
  1.2467 +{
  1.2468 +    nsCOMPtr<amIAddonManager> am;
  1.2469 +    if (XRE_GetProcessType() == GeckoProcessType_Default) {
  1.2470 +        // Only try to access the service from the main process.
  1.2471 +        am = do_GetService("@mozilla.org/addons/integration;1");
  1.2472 +    }
  1.2473 +    return ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), cb,
  1.2474 +                                            closure, rtTotalOut);
  1.2475 +}
  1.2476 +
  1.2477 +
  1.2478 +} // namespace xpc
  1.2479 +
  1.2480 +class JSMainRuntimeCompartmentsReporter MOZ_FINAL : public nsIMemoryReporter
  1.2481 +{
  1.2482 +  public:
  1.2483 +    NS_DECL_ISUPPORTS
  1.2484 +
  1.2485 +    typedef js::Vector<nsCString, 0, js::SystemAllocPolicy> Paths;
  1.2486 +
  1.2487 +    static void CompartmentCallback(JSRuntime *rt, void* data, JSCompartment *c) {
  1.2488 +        // silently ignore OOM errors
  1.2489 +        Paths *paths = static_cast<Paths *>(data);
  1.2490 +        nsCString path;
  1.2491 +        GetCompartmentName(c, path, true);
  1.2492 +        path.Insert(js::IsSystemCompartment(c)
  1.2493 +                    ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/")
  1.2494 +                    : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"),
  1.2495 +                    0);
  1.2496 +        paths->append(path);
  1.2497 +    }
  1.2498 +
  1.2499 +    NS_IMETHOD CollectReports(nsIMemoryReporterCallback *cb,
  1.2500 +                              nsISupports *closure)
  1.2501 +    {
  1.2502 +        // First we collect the compartment paths.  Then we report them.  Doing
  1.2503 +        // the two steps interleaved is a bad idea, because calling |cb|
  1.2504 +        // from within CompartmentCallback() leads to all manner of assertions.
  1.2505 +
  1.2506 +        // Collect.
  1.2507 +
  1.2508 +        Paths paths;
  1.2509 +        JS_IterateCompartments(nsXPConnect::GetRuntimeInstance()->Runtime(),
  1.2510 +                               &paths, CompartmentCallback);
  1.2511 +
  1.2512 +        // Report.
  1.2513 +        for (size_t i = 0; i < paths.length(); i++)
  1.2514 +            // These ones don't need a description, hence the "".
  1.2515 +            REPORT(nsCString(paths[i]), KIND_OTHER, UNITS_COUNT, 1,
  1.2516 +                "A live compartment in the main JSRuntime.");
  1.2517 +
  1.2518 +        return NS_OK;
  1.2519 +    }
  1.2520 +};
  1.2521 +
  1.2522 +NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
  1.2523 +
  1.2524 +MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
  1.2525 +
  1.2526 +namespace xpc {
  1.2527 +
  1.2528 +static size_t
  1.2529 +SizeOfTreeIncludingThis(nsINode *tree)
  1.2530 +{
  1.2531 +    size_t n = tree->SizeOfIncludingThis(OrphanMallocSizeOf);
  1.2532 +    for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
  1.2533 +        n += child->SizeOfIncludingThis(OrphanMallocSizeOf);
  1.2534 +
  1.2535 +    return n;
  1.2536 +}
  1.2537 +
  1.2538 +class OrphanReporter : public JS::ObjectPrivateVisitor
  1.2539 +{
  1.2540 +  public:
  1.2541 +    OrphanReporter(GetISupportsFun aGetISupports)
  1.2542 +      : JS::ObjectPrivateVisitor(aGetISupports)
  1.2543 +    {
  1.2544 +    }
  1.2545 +
  1.2546 +    virtual size_t sizeOfIncludingThis(nsISupports *aSupports) MOZ_OVERRIDE {
  1.2547 +        size_t n = 0;
  1.2548 +        nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
  1.2549 +        // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
  1.2550 +        // that we have to skip XBL elements because they violate certain
  1.2551 +        // assumptions.  Yuk.
  1.2552 +        if (node && !node->IsInDoc() &&
  1.2553 +            !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
  1.2554 +        {
  1.2555 +            // This is an orphan node.  If we haven't already handled the
  1.2556 +            // sub-tree that this node belongs to, measure the sub-tree's size
  1.2557 +            // and then record its root so we don't measure it again.
  1.2558 +            nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
  1.2559 +            if (!mAlreadyMeasuredOrphanTrees.Contains(orphanTree)) {
  1.2560 +                n += SizeOfTreeIncludingThis(orphanTree);
  1.2561 +                mAlreadyMeasuredOrphanTrees.PutEntry(orphanTree);
  1.2562 +            }
  1.2563 +        }
  1.2564 +        return n;
  1.2565 +    }
  1.2566 +
  1.2567 +  private:
  1.2568 +    nsTHashtable <nsISupportsHashKey> mAlreadyMeasuredOrphanTrees;
  1.2569 +};
  1.2570 +
  1.2571 +#ifdef DEBUG
  1.2572 +static bool
  1.2573 +StartsWithExplicit(nsACString& s)
  1.2574 +{
  1.2575 +    const char* e = "explicit/";
  1.2576 +    return Substring(s, 0, strlen(e)).Equals(e);
  1.2577 +}
  1.2578 +#endif
  1.2579 +
  1.2580 +class XPCJSRuntimeStats : public JS::RuntimeStats
  1.2581 +{
  1.2582 +    WindowPaths *mWindowPaths;
  1.2583 +    WindowPaths *mTopWindowPaths;
  1.2584 +    bool mGetLocations;
  1.2585 +
  1.2586 +  public:
  1.2587 +    XPCJSRuntimeStats(WindowPaths *windowPaths, WindowPaths *topWindowPaths,
  1.2588 +                      bool getLocations)
  1.2589 +      : JS::RuntimeStats(JSMallocSizeOf),
  1.2590 +        mWindowPaths(windowPaths),
  1.2591 +        mTopWindowPaths(topWindowPaths),
  1.2592 +        mGetLocations(getLocations)
  1.2593 +    {}
  1.2594 +
  1.2595 +    ~XPCJSRuntimeStats() {
  1.2596 +        for (size_t i = 0; i != compartmentStatsVector.length(); ++i)
  1.2597 +            delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
  1.2598 +
  1.2599 +
  1.2600 +        for (size_t i = 0; i != zoneStatsVector.length(); ++i)
  1.2601 +            delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
  1.2602 +    }
  1.2603 +
  1.2604 +    virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats) MOZ_OVERRIDE {
  1.2605 +        // Get the compartment's global.
  1.2606 +        nsXPConnect *xpc = nsXPConnect::XPConnect();
  1.2607 +        AutoSafeJSContext cx;
  1.2608 +        JSCompartment *comp = js::GetAnyCompartmentInZone(zone);
  1.2609 +        xpc::ZoneStatsExtras *extras = new xpc::ZoneStatsExtras;
  1.2610 +        extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  1.2611 +        RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, comp));
  1.2612 +        if (global) {
  1.2613 +            // Need to enter the compartment, otherwise GetNativeOfWrapper()
  1.2614 +            // might crash.
  1.2615 +            JSAutoCompartment ac(cx, global);
  1.2616 +            nsISupports *native = xpc->GetNativeOfWrapper(cx, global);
  1.2617 +            if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) {
  1.2618 +                // The global is a |window| object.  Use the path prefix that
  1.2619 +                // we should have already created for it.
  1.2620 +                if (mTopWindowPaths->Get(piwindow->WindowID(),
  1.2621 +                                         &extras->pathPrefix))
  1.2622 +                    extras->pathPrefix.AppendLiteral("/js-");
  1.2623 +            }
  1.2624 +        }
  1.2625 +
  1.2626 +        extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)zone);
  1.2627 +
  1.2628 +        MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
  1.2629 +
  1.2630 +        zStats->extra = extras;
  1.2631 +    }
  1.2632 +
  1.2633 +    virtual void initExtraCompartmentStats(JSCompartment *c,
  1.2634 +                                           JS::CompartmentStats *cstats) MOZ_OVERRIDE
  1.2635 +    {
  1.2636 +        xpc::CompartmentStatsExtras *extras = new xpc::CompartmentStatsExtras;
  1.2637 +        nsCString cName;
  1.2638 +        GetCompartmentName(c, cName, true);
  1.2639 +        if (mGetLocations) {
  1.2640 +            CompartmentPrivate *cp = GetCompartmentPrivate(c);
  1.2641 +            if (cp)
  1.2642 +              cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
  1.2643 +                                 getter_AddRefs(extras->location));
  1.2644 +            // Note: cannot use amIAddonManager implementation at this point,
  1.2645 +            // as it is a JS service and the JS heap is currently not idle.
  1.2646 +            // Otherwise, we could have computed the add-on id at this point.
  1.2647 +        }
  1.2648 +
  1.2649 +        // Get the compartment's global.
  1.2650 +        nsXPConnect *xpc = nsXPConnect::XPConnect();
  1.2651 +        AutoSafeJSContext cx;
  1.2652 +        bool needZone = true;
  1.2653 +        RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, c));
  1.2654 +        if (global) {
  1.2655 +            // Need to enter the compartment, otherwise GetNativeOfWrapper()
  1.2656 +            // might crash.
  1.2657 +            JSAutoCompartment ac(cx, global);
  1.2658 +            nsISupports *native = xpc->GetNativeOfWrapper(cx, global);
  1.2659 +            if (nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(native)) {
  1.2660 +                // The global is a |window| object.  Use the path prefix that
  1.2661 +                // we should have already created for it.
  1.2662 +                if (mWindowPaths->Get(piwindow->WindowID(),
  1.2663 +                                      &extras->jsPathPrefix)) {
  1.2664 +                    extras->domPathPrefix.Assign(extras->jsPathPrefix);
  1.2665 +                    extras->domPathPrefix.AppendLiteral("/dom/");
  1.2666 +                    extras->jsPathPrefix.AppendLiteral("/js-");
  1.2667 +                    needZone = false;
  1.2668 +                } else {
  1.2669 +                    extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  1.2670 +                    extras->domPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/");
  1.2671 +                }
  1.2672 +            } else {
  1.2673 +                extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  1.2674 +                extras->domPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/");
  1.2675 +            }
  1.2676 +        } else {
  1.2677 +            extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
  1.2678 +            extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
  1.2679 +        }
  1.2680 +
  1.2681 +        if (needZone)
  1.2682 +            extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void *)js::GetCompartmentZone(c));
  1.2683 +
  1.2684 +        extras->jsPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/");
  1.2685 +
  1.2686 +        // extras->jsPathPrefix is used for almost all the compartment-specific
  1.2687 +        // reports. At this point it has the form
  1.2688 +        // "<something>compartment(<cname>)/".
  1.2689 +        //
  1.2690 +        // extras->domPathPrefix is used for DOM orphan nodes, which are
  1.2691 +        // counted by the JS reporter but reported as part of the DOM
  1.2692 +        // measurements. At this point it has the form "<something>/dom/" if
  1.2693 +        // this compartment belongs to an nsGlobalWindow, and
  1.2694 +        // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
  1.2695 +        // be used, because non-nsGlobalWindow compartments shouldn't have
  1.2696 +        // orphan DOM nodes).
  1.2697 +
  1.2698 +        MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
  1.2699 +        MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
  1.2700 +
  1.2701 +        cstats->extra = extras;
  1.2702 +    }
  1.2703 +};
  1.2704 +
  1.2705 +nsresult
  1.2706 +JSReporter::CollectReports(WindowPaths *windowPaths,
  1.2707 +                           WindowPaths *topWindowPaths,
  1.2708 +                           nsIMemoryReporterCallback *cb,
  1.2709 +                           nsISupports *closure)
  1.2710 +{
  1.2711 +    XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
  1.2712 +
  1.2713 +    // In the first step we get all the stats and stash them in a local
  1.2714 +    // data structure.  In the second step we pass all the stashed stats to
  1.2715 +    // the callback.  Separating these steps is important because the
  1.2716 +    // callback may be a JS function, and executing JS while getting these
  1.2717 +    // stats seems like a bad idea.
  1.2718 +
  1.2719 +    nsCOMPtr<amIAddonManager> addonManager;
  1.2720 +    if (XRE_GetProcessType() == GeckoProcessType_Default) {
  1.2721 +        // Only try to access the service from the main process.
  1.2722 +        addonManager = do_GetService("@mozilla.org/addons/integration;1");
  1.2723 +    }
  1.2724 +    bool getLocations = !!addonManager;
  1.2725 +    XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations);
  1.2726 +    OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
  1.2727 +    if (!JS::CollectRuntimeStats(xpcrt->Runtime(), &rtStats, &orphanReporter))
  1.2728 +        return NS_ERROR_FAILURE;
  1.2729 +
  1.2730 +    size_t xpconnect = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
  1.2731 +
  1.2732 +    XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
  1.2733 +    XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
  1.2734 +
  1.2735 +    mozJSComponentLoader* loader = mozJSComponentLoader::Get();
  1.2736 +    size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
  1.2737 +
  1.2738 +    // This is the second step (see above).  First we report stuff in the
  1.2739 +    // "explicit" tree, then we report other stuff.
  1.2740 +
  1.2741 +    nsresult rv;
  1.2742 +    size_t rtTotal = 0;
  1.2743 +    rv = xpc::ReportJSRuntimeExplicitTreeStats(rtStats,
  1.2744 +                                               NS_LITERAL_CSTRING("explicit/js-non-window/"),
  1.2745 +                                               addonManager, cb, closure,
  1.2746 +                                               &rtTotal);
  1.2747 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2748 +
  1.2749 +    // Report the sums of the compartment numbers.
  1.2750 +    xpc::CompartmentStatsExtras cExtrasTotal;
  1.2751 +    cExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/compartments/");
  1.2752 +    cExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
  1.2753 +    rv = ReportCompartmentStats(rtStats.cTotals, cExtrasTotal, addonManager,
  1.2754 +                                cb, closure);
  1.2755 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2756 +
  1.2757 +    xpc::ZoneStatsExtras zExtrasTotal;
  1.2758 +    zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
  1.2759 +    rv = ReportZoneStats(rtStats.zTotals, zExtrasTotal, cb, closure);
  1.2760 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2761 +
  1.2762 +    // Report the sum of the runtime/ numbers.
  1.2763 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
  1.2764 +        KIND_OTHER, rtTotal,
  1.2765 +        "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
  1.2766 +
  1.2767 +    // Report the numbers for memory outside of compartments.
  1.2768 +
  1.2769 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
  1.2770 +        KIND_OTHER, rtStats.gcHeapUnusedChunks,
  1.2771 +        "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
  1.2772 +
  1.2773 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
  1.2774 +        KIND_OTHER, rtStats.gcHeapUnusedArenas,
  1.2775 +        "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
  1.2776 +
  1.2777 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
  1.2778 +        KIND_OTHER, rtStats.gcHeapChunkAdmin,
  1.2779 +        "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
  1.2780 +
  1.2781 +    // Report a breakdown of the committed GC space.
  1.2782 +
  1.2783 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
  1.2784 +        KIND_OTHER, rtStats.gcHeapUnusedChunks,
  1.2785 +        "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
  1.2786 +
  1.2787 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
  1.2788 +        KIND_OTHER, rtStats.gcHeapUnusedArenas,
  1.2789 +        "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
  1.2790 +
  1.2791 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things"),
  1.2792 +        KIND_OTHER, rtStats.zTotals.unusedGCThings,
  1.2793 +        "The same as 'js-main-runtime/zones/unused-gc-things'.");
  1.2794 +
  1.2795 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
  1.2796 +        KIND_OTHER, rtStats.gcHeapChunkAdmin,
  1.2797 +        "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
  1.2798 +
  1.2799 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
  1.2800 +        KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
  1.2801 +        "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
  1.2802 +
  1.2803 +    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things"),
  1.2804 +        KIND_OTHER, rtStats.gcHeapGCThings,
  1.2805 +        "GC things: objects, strings, scripts, etc.");
  1.2806 +
  1.2807 +    // Report xpconnect.
  1.2808 +
  1.2809 +    REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
  1.2810 +        KIND_HEAP, xpconnect,
  1.2811 +        "The XPConnect runtime.");
  1.2812 +
  1.2813 +    REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"),
  1.2814 +        KIND_HEAP, sizeInfo.mScopeAndMapSize,
  1.2815 +        "XPConnect scopes.");
  1.2816 +
  1.2817 +    REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"),
  1.2818 +        KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize,
  1.2819 +        "Prototype and interface binding caches.");
  1.2820 +
  1.2821 +    REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/js-component-loader"),
  1.2822 +        KIND_HEAP, jsComponentLoaderSize,
  1.2823 +        "XPConnect's JS component loader.");
  1.2824 +
  1.2825 +    return NS_OK;
  1.2826 +}
  1.2827 +
  1.2828 +static nsresult
  1.2829 +JSSizeOfTab(JSObject *objArg, size_t *jsObjectsSize, size_t *jsStringsSize,
  1.2830 +            size_t *jsPrivateSize, size_t *jsOtherSize)
  1.2831 +{
  1.2832 +    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
  1.2833 +    JS::RootedObject obj(rt, objArg);
  1.2834 +
  1.2835 +    TabSizes sizes;
  1.2836 +    OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
  1.2837 +    NS_ENSURE_TRUE(JS::AddSizeOfTab(rt, obj, moz_malloc_size_of,
  1.2838 +                                    &orphanReporter, &sizes),
  1.2839 +                   NS_ERROR_OUT_OF_MEMORY);
  1.2840 +
  1.2841 +    *jsObjectsSize = sizes.objects;
  1.2842 +    *jsStringsSize = sizes.strings;
  1.2843 +    *jsPrivateSize = sizes.private_;
  1.2844 +    *jsOtherSize   = sizes.other;
  1.2845 +    return NS_OK;
  1.2846 +}
  1.2847 +
  1.2848 +} // namespace xpc
  1.2849 +
  1.2850 +#ifdef MOZ_CRASHREPORTER
  1.2851 +static bool
  1.2852 +DiagnosticMemoryCallback(void *ptr, size_t size)
  1.2853 +{
  1.2854 +    return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
  1.2855 +}
  1.2856 +#endif
  1.2857 +
  1.2858 +static void
  1.2859 +AccumulateTelemetryCallback(int id, uint32_t sample)
  1.2860 +{
  1.2861 +    switch (id) {
  1.2862 +      case JS_TELEMETRY_GC_REASON:
  1.2863 +        Telemetry::Accumulate(Telemetry::GC_REASON_2, sample);
  1.2864 +        break;
  1.2865 +      case JS_TELEMETRY_GC_IS_COMPARTMENTAL:
  1.2866 +        Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample);
  1.2867 +        break;
  1.2868 +      case JS_TELEMETRY_GC_MS:
  1.2869 +        Telemetry::Accumulate(Telemetry::GC_MS, sample);
  1.2870 +        break;
  1.2871 +      case JS_TELEMETRY_GC_MAX_PAUSE_MS:
  1.2872 +        Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS, sample);
  1.2873 +        break;
  1.2874 +      case JS_TELEMETRY_GC_MARK_MS:
  1.2875 +        Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample);
  1.2876 +        break;
  1.2877 +      case JS_TELEMETRY_GC_SWEEP_MS:
  1.2878 +        Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample);
  1.2879 +        break;
  1.2880 +      case JS_TELEMETRY_GC_MARK_ROOTS_MS:
  1.2881 +        Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample);
  1.2882 +        break;
  1.2883 +      case JS_TELEMETRY_GC_MARK_GRAY_MS:
  1.2884 +        Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample);
  1.2885 +        break;
  1.2886 +      case JS_TELEMETRY_GC_SLICE_MS:
  1.2887 +        Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
  1.2888 +        break;
  1.2889 +      case JS_TELEMETRY_GC_MMU_50:
  1.2890 +        Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
  1.2891 +        break;
  1.2892 +      case JS_TELEMETRY_GC_RESET:
  1.2893 +        Telemetry::Accumulate(Telemetry::GC_RESET, sample);
  1.2894 +        break;
  1.2895 +      case JS_TELEMETRY_GC_INCREMENTAL_DISABLED:
  1.2896 +        Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample);
  1.2897 +        break;
  1.2898 +      case JS_TELEMETRY_GC_NON_INCREMENTAL:
  1.2899 +        Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample);
  1.2900 +        break;
  1.2901 +      case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS:
  1.2902 +        Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample);
  1.2903 +        break;
  1.2904 +      case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS:
  1.2905 +        Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample);
  1.2906 +        break;
  1.2907 +    }
  1.2908 +}
  1.2909 +
  1.2910 +static void
  1.2911 +CompartmentNameCallback(JSRuntime *rt, JSCompartment *comp,
  1.2912 +                        char *buf, size_t bufsize)
  1.2913 +{
  1.2914 +    nsCString name;
  1.2915 +    GetCompartmentName(comp, name, false);
  1.2916 +    if (name.Length() >= bufsize)
  1.2917 +        name.Truncate(bufsize - 1);
  1.2918 +    memcpy(buf, name.get(), name.Length() + 1);
  1.2919 +}
  1.2920 +
  1.2921 +static bool
  1.2922 +PreserveWrapper(JSContext *cx, JSObject *obj)
  1.2923 +{
  1.2924 +    MOZ_ASSERT(cx);
  1.2925 +    MOZ_ASSERT(obj);
  1.2926 +    MOZ_ASSERT(IS_WN_REFLECTOR(obj) || mozilla::dom::IsDOMObject(obj));
  1.2927 +
  1.2928 +    return mozilla::dom::IsDOMObject(obj) && mozilla::dom::TryPreserveWrapper(obj);
  1.2929 +}
  1.2930 +
  1.2931 +static nsresult
  1.2932 +ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, size_t *len)
  1.2933 +{
  1.2934 +    nsresult rv;
  1.2935 +
  1.2936 +    // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with
  1.2937 +    // the filename of its caller. Axe that if present.
  1.2938 +    const char *arrow;
  1.2939 +    while ((arrow = strstr(filename, " -> ")))
  1.2940 +        filename = arrow + strlen(" -> ");
  1.2941 +
  1.2942 +    // Get the URI.
  1.2943 +    nsCOMPtr<nsIURI> uri;
  1.2944 +    rv = NS_NewURI(getter_AddRefs(uri), filename);
  1.2945 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2946 +
  1.2947 +    nsCOMPtr<nsIChannel> scriptChannel;
  1.2948 +    rv = NS_NewChannel(getter_AddRefs(scriptChannel), uri);
  1.2949 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2950 +
  1.2951 +    // Only allow local reading.
  1.2952 +    nsCOMPtr<nsIURI> actualUri;
  1.2953 +    rv = scriptChannel->GetURI(getter_AddRefs(actualUri));
  1.2954 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2955 +    nsCString scheme;
  1.2956 +    rv = actualUri->GetScheme(scheme);
  1.2957 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2958 +    if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar"))
  1.2959 +        return NS_OK;
  1.2960 +
  1.2961 +    nsCOMPtr<nsIInputStream> scriptStream;
  1.2962 +    rv = scriptChannel->Open(getter_AddRefs(scriptStream));
  1.2963 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2964 +
  1.2965 +    uint64_t rawLen;
  1.2966 +    rv = scriptStream->Available(&rawLen);
  1.2967 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2968 +    if (!rawLen)
  1.2969 +        return NS_ERROR_FAILURE;
  1.2970 +
  1.2971 +    // Technically, this should be SIZE_MAX, but we don't run on machines
  1.2972 +    // where that would be less than UINT32_MAX, and the latter is already
  1.2973 +    // well beyond a reasonable limit.
  1.2974 +    if (rawLen > UINT32_MAX)
  1.2975 +        return NS_ERROR_FILE_TOO_BIG;
  1.2976 +
  1.2977 +    // Allocate an internal buf the size of the file.
  1.2978 +    nsAutoArrayPtr<unsigned char> buf(new unsigned char[rawLen]);
  1.2979 +    if (!buf)
  1.2980 +        return NS_ERROR_OUT_OF_MEMORY;
  1.2981 +
  1.2982 +    unsigned char *ptr = buf, *end = ptr + rawLen;
  1.2983 +    while (ptr < end) {
  1.2984 +        uint32_t bytesRead;
  1.2985 +        rv = scriptStream->Read(reinterpret_cast<char *>(ptr), end - ptr, &bytesRead);
  1.2986 +        if (NS_FAILED(rv))
  1.2987 +            return rv;
  1.2988 +        MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF");
  1.2989 +        ptr += bytesRead;
  1.2990 +    }
  1.2991 +
  1.2992 +    rv = nsScriptLoader::ConvertToUTF16(scriptChannel, buf, rawLen, EmptyString(),
  1.2993 +                                        nullptr, *src, *len);
  1.2994 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2995 +
  1.2996 +    if (!*src)
  1.2997 +        return NS_ERROR_FAILURE;
  1.2998 +
  1.2999 +    // Historically this method used JS_malloc() which updates the GC memory
  1.3000 +    // accounting.  Since ConvertToUTF16() now uses js_malloc() instead we
  1.3001 +    // update the accounting manually after the fact.
  1.3002 +    JS_updateMallocCounter(cx, *len);
  1.3003 +
  1.3004 +    return NS_OK;
  1.3005 +}
  1.3006 +
  1.3007 +// The JS engine calls this object's 'load' member function when it needs
  1.3008 +// the source for a chrome JS function. See the comment in the XPCJSRuntime
  1.3009 +// constructor.
  1.3010 +class XPCJSSourceHook: public js::SourceHook {
  1.3011 +    bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) {
  1.3012 +        *src = nullptr;
  1.3013 +        *length = 0;
  1.3014 +
  1.3015 +        if (!nsContentUtils::IsCallerChrome())
  1.3016 +            return true;
  1.3017 +
  1.3018 +        if (!filename)
  1.3019 +            return true;
  1.3020 +
  1.3021 +        nsresult rv = ReadSourceFromFilename(cx, filename, src, length);
  1.3022 +        if (NS_FAILED(rv)) {
  1.3023 +            xpc::Throw(cx, rv);
  1.3024 +            return false;
  1.3025 +        }
  1.3026 +
  1.3027 +        return true;
  1.3028 +    }
  1.3029 +};
  1.3030 +
  1.3031 +static const JSWrapObjectCallbacks WrapObjectCallbacks = {
  1.3032 +    xpc::WrapperFactory::Rewrap,
  1.3033 +    xpc::WrapperFactory::PrepareForWrapping
  1.3034 +};
  1.3035 +
  1.3036 +XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
  1.3037 +   : CycleCollectedJSRuntime(nullptr, 32L * 1024L * 1024L, JS_USE_HELPER_THREADS),
  1.3038 +   mJSContextStack(new XPCJSContextStack(MOZ_THIS_IN_INITIALIZER_LIST())),
  1.3039 +   mCallContext(nullptr),
  1.3040 +   mAutoRoots(nullptr),
  1.3041 +   mResolveName(JSID_VOID),
  1.3042 +   mResolvingWrapper(nullptr),
  1.3043 +   mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
  1.3044 +   mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
  1.3045 +   mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
  1.3046 +   mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
  1.3047 +   mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
  1.3048 +   mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
  1.3049 +   mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
  1.3050 +   mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
  1.3051 +   mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)),
  1.3052 +   mGCIsRunning(false),
  1.3053 +   mWrappedJSToReleaseArray(),
  1.3054 +   mNativesToReleaseArray(),
  1.3055 +   mDoingFinalization(false),
  1.3056 +   mVariantRoots(nullptr),
  1.3057 +   mWrappedJSRoots(nullptr),
  1.3058 +   mObjectHolderRoots(nullptr),
  1.3059 +   mWatchdogManager(new WatchdogManager(MOZ_THIS_IN_INITIALIZER_LIST())),
  1.3060 +   mJunkScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
  1.3061 +   mCompilationScope(MOZ_THIS_IN_INITIALIZER_LIST()->Runtime(), nullptr),
  1.3062 +   mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
  1.3063 +{
  1.3064 +    DOM_InitInterfaces();
  1.3065 +
  1.3066 +    // these jsids filled in later when we have a JSContext to work with.
  1.3067 +    mStrIDs[0] = JSID_VOID;
  1.3068 +
  1.3069 +    MOZ_ASSERT(Runtime());
  1.3070 +    JSRuntime* runtime = Runtime();
  1.3071 +
  1.3072 +    auto rtPrivate = new PerThreadAtomCache();
  1.3073 +    memset(rtPrivate, 0, sizeof(PerThreadAtomCache));
  1.3074 +    JS_SetRuntimePrivate(runtime, rtPrivate);
  1.3075 +
  1.3076 +    // Unconstrain the runtime's threshold on nominal heap size, to avoid
  1.3077 +    // triggering GC too often if operating continuously near an arbitrary
  1.3078 +    // finite threshold (0xffffffff is infinity for uint32_t parameters).
  1.3079 +    // This leaves the maximum-JS_malloc-bytes threshold still in effect
  1.3080 +    // to cause period, and we hope hygienic, last-ditch GCs from within
  1.3081 +    // the GC's allocator.
  1.3082 +    JS_SetGCParameter(runtime, JSGC_MAX_BYTES, 0xffffffff);
  1.3083 +
  1.3084 +    // The JS engine permits us to set different stack limits for system code,
  1.3085 +    // trusted script, and untrusted script. We have tests that ensure that
  1.3086 +    // we can always execute 10 "heavy" (eval+with) stack frames deeper in
  1.3087 +    // privileged code. Our stack sizes vary greatly in different configurations,
  1.3088 +    // so satisfying those tests requires some care. Manual measurements of the
  1.3089 +    // number of heavy stack frames achievable gives us the following rough data,
  1.3090 +    // ordered by the effective categories in which they are grouped in the
  1.3091 +    // JS_SetNativeStackQuota call (which predates this analysis).
  1.3092 +    //
  1.3093 +    // (NB: These numbers may have drifted recently - see bug 938429)
  1.3094 +    // OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame
  1.3095 +    // OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame
  1.3096 +    //
  1.3097 +    // Linux 32-bit Debug: 2MB stack, 447 stack frames => ~4.6k per stack frame
  1.3098 +    // Linux 64-bit Debug: 4MB stack, 501 stack frames => ~8.2k per stack frame
  1.3099 +    //
  1.3100 +    // Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame
  1.3101 +    //
  1.3102 +    // Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
  1.3103 +    // Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
  1.3104 +    //
  1.3105 +    // We tune the trusted/untrusted quotas for each configuration to achieve our
  1.3106 +    // invariants while attempting to minimize overhead. In contrast, our buffer
  1.3107 +    // between system code and trusted script is a very unscientific 10k.
  1.3108 +    const size_t kSystemCodeBuffer = 10 * 1024;
  1.3109 +
  1.3110 +    // Our "default" stack is what we use in configurations where we don't have
  1.3111 +    // a compelling reason to do things differently. This is effectively 1MB on
  1.3112 +    // 32-bit platforms and 2MB on 64-bit platforms.
  1.3113 +    const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
  1.3114 +
  1.3115 +    // Set stack sizes for different configurations. It's probably not great for
  1.3116 +    // the web to base this decision primarily on the default stack size that the
  1.3117 +    // underlying platform makes available, but that seems to be what we do. :-(
  1.3118 +
  1.3119 +#if defined(XP_MACOSX) || defined(DARWIN)
  1.3120 +    // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
  1.3121 +    // and give trusted script 140k extra. The stack is huge on mac anyway.
  1.3122 +    const size_t kStackQuota = 7 * 1024 * 1024;
  1.3123 +    const size_t kTrustedScriptBuffer = 140 * 1024;
  1.3124 +#elif defined(MOZ_ASAN)
  1.3125 +    // ASan requires more stack space due to red-zones, so give it double the
  1.3126 +    // default (2MB on 32-bit, 4MB on 64-bit). ASAN stack frame measurements
  1.3127 +    // were not taken at the time of this writing, so we hazard a guess that
  1.3128 +    // ASAN builds have roughly thrice the stack overhead as normal builds.
  1.3129 +    // On normal builds, the largest stack frame size we might encounter is
  1.3130 +    // 8.2k, so let's use a buffer of 8.2 * 3 * 10 = 246k.
  1.3131 +    const size_t kStackQuota =  2 * kDefaultStackQuota;
  1.3132 +    const size_t kTrustedScriptBuffer = 246 * 1024;
  1.3133 +#elif defined(XP_WIN)
  1.3134 +    // 1MB is the default stack size on Windows, so the default 1MB stack quota
  1.3135 +    // we'd get on win32 is slightly too large. Use 900k instead. And since
  1.3136 +    // windows stack frames are 3.4k each, let's use a buffer of 50k.
  1.3137 +    const size_t kStackQuota = 900 * 1024;
  1.3138 +    const size_t kTrustedScriptBuffer = 50 * 1024;
  1.3139 +    // The following two configurations are linux-only. Given the numbers above,
  1.3140 +    // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively.
  1.3141 +#elif defined(DEBUG)
  1.3142 +    // Bug 803182: account for the 4x difference in the size of js::Interpret
  1.3143 +    // between optimized and debug builds.
  1.3144 +    // XXXbholley - Then why do we only account for 2x of difference?
  1.3145 +    const size_t kStackQuota = 2 * kDefaultStackQuota;
  1.3146 +    const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
  1.3147 +#else
  1.3148 +    const size_t kStackQuota = kDefaultStackQuota;
  1.3149 +    const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
  1.3150 +#endif
  1.3151 +
  1.3152 +    // Avoid an unused variable warning on platforms where we don't use the
  1.3153 +    // default.
  1.3154 +    (void) kDefaultStackQuota;
  1.3155 +
  1.3156 +    JS_SetNativeStackQuota(runtime,
  1.3157 +                           kStackQuota,
  1.3158 +                           kStackQuota - kSystemCodeBuffer,
  1.3159 +                           kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer);
  1.3160 +
  1.3161 +    JS_SetDestroyCompartmentCallback(runtime, CompartmentDestroyedCallback);
  1.3162 +    JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
  1.3163 +    mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
  1.3164 +    JS_SetFinalizeCallback(runtime, FinalizeCallback);
  1.3165 +    JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks);
  1.3166 +    js::SetPreserveWrapperCallback(runtime, PreserveWrapper);
  1.3167 +#ifdef MOZ_CRASHREPORTER
  1.3168 +    JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
  1.3169 +#endif
  1.3170 +#ifdef MOZ_ENABLE_PROFILER_SPS
  1.3171 +    if (PseudoStack *stack = mozilla_get_pseudo_stack())
  1.3172 +        stack->sampleRuntime(runtime);
  1.3173 +#endif
  1.3174 +    JS_SetAccumulateTelemetryCallback(runtime, AccumulateTelemetryCallback);
  1.3175 +    js::SetDefaultJSContextCallback(runtime, DefaultJSContextCallback);
  1.3176 +    js::SetActivityCallback(runtime, ActivityCallback, this);
  1.3177 +    js::SetCTypesActivityCallback(runtime, CTypesActivityCallback);
  1.3178 +    JS_SetInterruptCallback(runtime, InterruptCallback);
  1.3179 +    JS::SetOutOfMemoryCallback(runtime, OutOfMemoryCallback);
  1.3180 +
  1.3181 +    // The JS engine needs to keep the source code around in order to implement
  1.3182 +    // Function.prototype.toSource(). It'd be nice to not have to do this for
  1.3183 +    // chrome code and simply stub out requests for source on it. Life is not so
  1.3184 +    // easy, unfortunately. Nobody relies on chrome toSource() working in core
  1.3185 +    // browser code, but chrome tests use it. The worst offenders are addons,
  1.3186 +    // which like to monkeypatch chrome functions by calling toSource() on them
  1.3187 +    // and using regular expressions to modify them. We avoid keeping most browser
  1.3188 +    // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when
  1.3189 +    // compiling some chrome code. This causes the JS engine not save the source
  1.3190 +    // code in memory. When the JS engine is asked to provide the source for a
  1.3191 +    // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
  1.3192 +    ///
  1.3193 +    // Note we do have to retain the source code in memory for scripts compiled in
  1.3194 +    // compileAndGo mode and compiled function bodies (from
  1.3195 +    // JS_CompileFunction*). In practice, this means content scripts and event
  1.3196 +    // handlers.
  1.3197 +    js::SetSourceHook(runtime, new XPCJSSourceHook);
  1.3198 +
  1.3199 +    // Set up locale information and callbacks for the newly-created runtime so
  1.3200 +    // that the various toLocaleString() methods, localeCompare(), and other
  1.3201 +    // internationalization APIs work as desired.
  1.3202 +    if (!xpc_LocalizeRuntime(runtime))
  1.3203 +        NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
  1.3204 +
  1.3205 +    // Register memory reporters and distinguished amount functions.
  1.3206 +    RegisterStrongMemoryReporter(new JSMainRuntimeCompartmentsReporter());
  1.3207 +    RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
  1.3208 +    RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
  1.3209 +    RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
  1.3210 +    RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
  1.3211 +    RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
  1.3212 +    mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
  1.3213 +
  1.3214 +    // Install a JavaScript 'debugger' keyword handler in debug builds only
  1.3215 +#ifdef DEBUG
  1.3216 +    if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
  1.3217 +        xpc_InstallJSDebuggerKeywordHandler(runtime);
  1.3218 +#endif
  1.3219 +
  1.3220 +    // Watch for the JS boolean options.
  1.3221 +    ReloadPrefsCallback(nullptr, this);
  1.3222 +    Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
  1.3223 +}
  1.3224 +
  1.3225 +// static
  1.3226 +XPCJSRuntime*
  1.3227 +XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
  1.3228 +{
  1.3229 +    NS_PRECONDITION(aXPConnect,"bad param");
  1.3230 +
  1.3231 +    XPCJSRuntime* self = new XPCJSRuntime(aXPConnect);
  1.3232 +
  1.3233 +    if (self                                    &&
  1.3234 +        self->Runtime()                         &&
  1.3235 +        self->GetWrappedJSMap()                 &&
  1.3236 +        self->GetWrappedJSClassMap()            &&
  1.3237 +        self->GetIID2NativeInterfaceMap()       &&
  1.3238 +        self->GetClassInfo2NativeSetMap()       &&
  1.3239 +        self->GetNativeSetMap()                 &&
  1.3240 +        self->GetThisTranslatorMap()            &&
  1.3241 +        self->GetNativeScriptableSharedMap()    &&
  1.3242 +        self->GetDyingWrappedNativeProtoMap()   &&
  1.3243 +        self->mWatchdogManager) {
  1.3244 +        return self;
  1.3245 +    }
  1.3246 +
  1.3247 +    NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize.");
  1.3248 +
  1.3249 +    delete self;
  1.3250 +    return nullptr;
  1.3251 +}
  1.3252 +
  1.3253 +bool
  1.3254 +XPCJSRuntime::OnJSContextNew(JSContext *cx)
  1.3255 +{
  1.3256 +    // If we were the first cx ever created (like the SafeJSContext), the caller
  1.3257 +    // would have had no way to enter a request. Enter one now before doing the
  1.3258 +    // rest of the cx setup.
  1.3259 +    JSAutoRequest ar(cx);
  1.3260 +
  1.3261 +    // if it is our first context then we need to generate our string ids
  1.3262 +    if (JSID_IS_VOID(mStrIDs[0])) {
  1.3263 +        RootedString str(cx);
  1.3264 +        for (unsigned i = 0; i < IDX_TOTAL_COUNT; i++) {
  1.3265 +            str = JS_InternString(cx, mStrings[i]);
  1.3266 +            if (!str) {
  1.3267 +                mStrIDs[0] = JSID_VOID;
  1.3268 +                return false;
  1.3269 +            }
  1.3270 +            mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str);
  1.3271 +            mStrJSVals[i] = STRING_TO_JSVAL(str);
  1.3272 +        }
  1.3273 +
  1.3274 +        if (!mozilla::dom::DefineStaticJSVals(cx)) {
  1.3275 +            return false;
  1.3276 +        }
  1.3277 +    }
  1.3278 +
  1.3279 +    XPCContext* xpc = new XPCContext(this, cx);
  1.3280 +    if (!xpc)
  1.3281 +        return false;
  1.3282 +
  1.3283 +    return true;
  1.3284 +}
  1.3285 +
  1.3286 +bool
  1.3287 +XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
  1.3288 +                                    char (&name)[72]) const
  1.3289 +{
  1.3290 +    XPCNativeScriptableInfo *si = nullptr;
  1.3291 +
  1.3292 +    if (!IS_PROTO_CLASS(clasp)) {
  1.3293 +        return false;
  1.3294 +    }
  1.3295 +
  1.3296 +    XPCWrappedNativeProto *p =
  1.3297 +        static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
  1.3298 +    si = p->GetScriptableInfo();
  1.3299 +    
  1.3300 +    if (!si) {
  1.3301 +        return false;
  1.3302 +    }
  1.3303 +
  1.3304 +    JS_snprintf(name, sizeof(name), "JS Object (%s - %s)",
  1.3305 +                clasp->name, si->GetJSClass()->name);
  1.3306 +    return true;
  1.3307 +}
  1.3308 +
  1.3309 +bool
  1.3310 +XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
  1.3311 +                                             nsCycleCollectionTraversalCallback& cb) const
  1.3312 +{
  1.3313 +    if (clasp != &XPC_WN_Tearoff_JSClass) {
  1.3314 +        return false;
  1.3315 +    }
  1.3316 +
  1.3317 +    // A tearoff holds a strong reference to its native object
  1.3318 +    // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
  1.3319 +    // will be held alive through the parent of the JSObject of the tearoff.
  1.3320 +    XPCWrappedNativeTearOff *to =
  1.3321 +        static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj));
  1.3322 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
  1.3323 +    cb.NoteXPCOMChild(to->GetNative());
  1.3324 +    return true;
  1.3325 +}
  1.3326 +
  1.3327 +/***************************************************************************/
  1.3328 +
  1.3329 +#ifdef DEBUG
  1.3330 +static PLDHashOperator
  1.3331 +WrappedJSClassMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
  1.3332 +                                uint32_t number, void *arg)
  1.3333 +{
  1.3334 +    ((IID2WrappedJSClassMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg);
  1.3335 +    return PL_DHASH_NEXT;
  1.3336 +}
  1.3337 +static PLDHashOperator
  1.3338 +NativeSetDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
  1.3339 +                        uint32_t number, void *arg)
  1.3340 +{
  1.3341 +    ((NativeSetMap::Entry*)hdr)->key_value->DebugDump(*(int16_t*)arg);
  1.3342 +    return PL_DHASH_NEXT;
  1.3343 +}
  1.3344 +#endif
  1.3345 +
  1.3346 +void
  1.3347 +XPCJSRuntime::DebugDump(int16_t depth)
  1.3348 +{
  1.3349 +#ifdef DEBUG
  1.3350 +    depth--;
  1.3351 +    XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", this));
  1.3352 +        XPC_LOG_INDENT();
  1.3353 +        XPC_LOG_ALWAYS(("mJSRuntime @ %x", Runtime()));
  1.3354 +
  1.3355 +        XPC_LOG_ALWAYS(("mWrappedJSToReleaseArray @ %x with %d wrappers(s)", \
  1.3356 +                        &mWrappedJSToReleaseArray,
  1.3357 +                        mWrappedJSToReleaseArray.Length()));
  1.3358 +
  1.3359 +        int cxCount = 0;
  1.3360 +        JSContext* iter = nullptr;
  1.3361 +        while (JS_ContextIterator(Runtime(), &iter))
  1.3362 +            ++cxCount;
  1.3363 +        XPC_LOG_ALWAYS(("%d JS context(s)", cxCount));
  1.3364 +
  1.3365 +        iter = nullptr;
  1.3366 +        while (JS_ContextIterator(Runtime(), &iter)) {
  1.3367 +            XPCContext *xpc = XPCContext::GetXPCContext(iter);
  1.3368 +            XPC_LOG_INDENT();
  1.3369 +            xpc->DebugDump(depth);
  1.3370 +            XPC_LOG_OUTDENT();
  1.3371 +        }
  1.3372 +
  1.3373 +        XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)",  \
  1.3374 +                        mWrappedJSClassMap, mWrappedJSClassMap ?              \
  1.3375 +                        mWrappedJSClassMap->Count() : 0));
  1.3376 +        // iterate wrappersclasses...
  1.3377 +        if (depth && mWrappedJSClassMap && mWrappedJSClassMap->Count()) {
  1.3378 +            XPC_LOG_INDENT();
  1.3379 +            mWrappedJSClassMap->Enumerate(WrappedJSClassMapDumpEnumerator, &depth);
  1.3380 +            XPC_LOG_OUTDENT();
  1.3381 +        }
  1.3382 +        XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)",             \
  1.3383 +                        mWrappedJSMap, mWrappedJSMap ?                        \
  1.3384 +                        mWrappedJSMap->Count() : 0));
  1.3385 +        // iterate wrappers...
  1.3386 +        if (depth && mWrappedJSMap && mWrappedJSMap->Count()) {
  1.3387 +            XPC_LOG_INDENT();
  1.3388 +            mWrappedJSMap->Dump(depth);
  1.3389 +            XPC_LOG_OUTDENT();
  1.3390 +        }
  1.3391 +
  1.3392 +        XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)",  \
  1.3393 +                        mIID2NativeInterfaceMap, mIID2NativeInterfaceMap ?    \
  1.3394 +                        mIID2NativeInterfaceMap->Count() : 0));
  1.3395 +
  1.3396 +        XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)",       \
  1.3397 +                        mClassInfo2NativeSetMap, mClassInfo2NativeSetMap ?    \
  1.3398 +                        mClassInfo2NativeSetMap->Count() : 0));
  1.3399 +
  1.3400 +        XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)",      \
  1.3401 +                        mThisTranslatorMap, mThisTranslatorMap ?              \
  1.3402 +                        mThisTranslatorMap->Count() : 0));
  1.3403 +
  1.3404 +        XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)",                 \
  1.3405 +                        mNativeSetMap, mNativeSetMap ?                        \
  1.3406 +                        mNativeSetMap->Count() : 0));
  1.3407 +
  1.3408 +        // iterate sets...
  1.3409 +        if (depth && mNativeSetMap && mNativeSetMap->Count()) {
  1.3410 +            XPC_LOG_INDENT();
  1.3411 +            mNativeSetMap->Enumerate(NativeSetDumpEnumerator, &depth);
  1.3412 +            XPC_LOG_OUTDENT();
  1.3413 +        }
  1.3414 +
  1.3415 +        XPC_LOG_OUTDENT();
  1.3416 +#endif
  1.3417 +}
  1.3418 +
  1.3419 +/***************************************************************************/
  1.3420 +
  1.3421 +void
  1.3422 +XPCRootSetElem::AddToRootSet(XPCRootSetElem **listHead)
  1.3423 +{
  1.3424 +    MOZ_ASSERT(!mSelfp, "Must be not linked");
  1.3425 +
  1.3426 +    mSelfp = listHead;
  1.3427 +    mNext = *listHead;
  1.3428 +    if (mNext) {
  1.3429 +        MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start");
  1.3430 +        mNext->mSelfp = &mNext;
  1.3431 +    }
  1.3432 +    *listHead = this;
  1.3433 +}
  1.3434 +
  1.3435 +void
  1.3436 +XPCRootSetElem::RemoveFromRootSet()
  1.3437 +{
  1.3438 +    nsXPConnect *xpc = nsXPConnect::XPConnect();
  1.3439 +    JS::PokeGC(xpc->GetRuntime()->Runtime());
  1.3440 +
  1.3441 +    MOZ_ASSERT(mSelfp, "Must be linked");
  1.3442 +
  1.3443 +    MOZ_ASSERT(*mSelfp == this, "Link invariant");
  1.3444 +    *mSelfp = mNext;
  1.3445 +    if (mNext)
  1.3446 +        mNext->mSelfp = mSelfp;
  1.3447 +#ifdef DEBUG
  1.3448 +    mSelfp = nullptr;
  1.3449 +    mNext = nullptr;
  1.3450 +#endif
  1.3451 +}
  1.3452 +
  1.3453 +void
  1.3454 +XPCJSRuntime::AddGCCallback(xpcGCCallback cb)
  1.3455 +{
  1.3456 +    MOZ_ASSERT(cb, "null callback");
  1.3457 +    extraGCCallbacks.AppendElement(cb);
  1.3458 +}
  1.3459 +
  1.3460 +void
  1.3461 +XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
  1.3462 +{
  1.3463 +    MOZ_ASSERT(cb, "null callback");
  1.3464 +    bool found = extraGCCallbacks.RemoveElement(cb);
  1.3465 +    if (!found) {
  1.3466 +        NS_ERROR("Removing a callback which was never added.");
  1.3467 +    }
  1.3468 +}
  1.3469 +
  1.3470 +void
  1.3471 +XPCJSRuntime::AddContextCallback(xpcContextCallback cb)
  1.3472 +{
  1.3473 +    MOZ_ASSERT(cb, "null callback");
  1.3474 +    extraContextCallbacks.AppendElement(cb);
  1.3475 +}
  1.3476 +
  1.3477 +void
  1.3478 +XPCJSRuntime::RemoveContextCallback(xpcContextCallback cb)
  1.3479 +{
  1.3480 +    MOZ_ASSERT(cb, "null callback");
  1.3481 +    bool found = extraContextCallbacks.RemoveElement(cb);
  1.3482 +    if (!found) {
  1.3483 +        NS_ERROR("Removing a callback which was never added.");
  1.3484 +    }
  1.3485 +}
  1.3486 +
  1.3487 +JSObject *
  1.3488 +XPCJSRuntime::GetJunkScope()
  1.3489 +{
  1.3490 +    if (!mJunkScope) {
  1.3491 +        AutoSafeJSContext cx;
  1.3492 +        SandboxOptions options;
  1.3493 +        options.sandboxName.AssignLiteral("XPConnect Junk Compartment");
  1.3494 +        RootedValue v(cx);
  1.3495 +        nsresult rv = CreateSandboxObject(cx, &v, nsContentUtils::GetSystemPrincipal(), options);
  1.3496 +        NS_ENSURE_SUCCESS(rv, nullptr);
  1.3497 +
  1.3498 +        mJunkScope = js::UncheckedUnwrap(&v.toObject());
  1.3499 +    }
  1.3500 +    return mJunkScope;
  1.3501 +}
  1.3502 +
  1.3503 +JSObject *
  1.3504 +XPCJSRuntime::GetCompilationScope()
  1.3505 +{
  1.3506 +    if (!mCompilationScope) {
  1.3507 +        AutoSafeJSContext cx;
  1.3508 +        SandboxOptions options;
  1.3509 +        options.sandboxName.AssignLiteral("XPConnect Compilation Compartment");
  1.3510 +        options.invisibleToDebugger = true;
  1.3511 +        options.discardSource = ShouldDiscardSystemSource();
  1.3512 +        RootedValue v(cx);
  1.3513 +        nsresult rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, options);
  1.3514 +        NS_ENSURE_SUCCESS(rv, nullptr);
  1.3515 +
  1.3516 +        mCompilationScope = js::UncheckedUnwrap(&v.toObject());
  1.3517 +    }
  1.3518 +    return mCompilationScope;
  1.3519 +}
  1.3520 +
  1.3521 +
  1.3522 +void
  1.3523 +XPCJSRuntime::DeleteSingletonScopes()
  1.3524 +{
  1.3525 +    mJunkScope = nullptr;
  1.3526 +    mCompilationScope = nullptr;
  1.3527 +}

mercurial