js/xpconnect/src/XPCWrappedNativeScope.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,729 @@
     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 +/* Class used to manage the wrapped native objects within a JS scope. */
    1.11 +
    1.12 +#include "xpcprivate.h"
    1.13 +#include "XPCWrapper.h"
    1.14 +#include "nsContentUtils.h"
    1.15 +#include "nsCycleCollectionNoteRootCallback.h"
    1.16 +#include "nsPrincipal.h"
    1.17 +#include "mozilla/MemoryReporting.h"
    1.18 +#include "mozilla/Preferences.h"
    1.19 +
    1.20 +#include "mozilla/dom/BindingUtils.h"
    1.21 +
    1.22 +using namespace mozilla;
    1.23 +using namespace xpc;
    1.24 +using namespace JS;
    1.25 +
    1.26 +/***************************************************************************/
    1.27 +
    1.28 +XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nullptr;
    1.29 +XPCWrappedNativeScope* XPCWrappedNativeScope::gDyingScopes = nullptr;
    1.30 +
    1.31 +// static
    1.32 +XPCWrappedNativeScope*
    1.33 +XPCWrappedNativeScope::GetNewOrUsed(JSContext *cx, JS::HandleObject aGlobal)
    1.34 +{
    1.35 +    XPCWrappedNativeScope* scope = GetObjectScope(aGlobal);
    1.36 +    if (!scope) {
    1.37 +        scope = new XPCWrappedNativeScope(cx, aGlobal);
    1.38 +    }
    1.39 +    return scope;
    1.40 +}
    1.41 +
    1.42 +static bool
    1.43 +RemoteXULForbidsXBLScope(nsIPrincipal *aPrincipal, HandleObject aGlobal)
    1.44 +{
    1.45 +  // Check for random JSD scopes that don't have a principal.
    1.46 +  if (!aPrincipal)
    1.47 +      return false;
    1.48 +
    1.49 +  // The SafeJSContext is lazily created, and tends to be created at really
    1.50 +  // weird times, at least for xpcshell (often very early in startup or late
    1.51 +  // in shutdown). Its scope isn't system principal, so if we proceeded we'd
    1.52 +  // end up calling into AllowXULXBLForPrincipal, which depends on all kinds
    1.53 +  // of persistent storage and permission machinery that may or not be running.
    1.54 +  // We know the answer to the question here, so just short-circuit.
    1.55 +  if (JS_GetClass(aGlobal) == &SafeJSContextGlobalClass)
    1.56 +      return false;
    1.57 +
    1.58 +  // AllowXULXBLForPrincipal will return true for system principal, but we
    1.59 +  // don't want that here.
    1.60 +  MOZ_ASSERT(nsContentUtils::IsInitialized());
    1.61 +  if (nsContentUtils::IsSystemPrincipal(aPrincipal))
    1.62 +      return false;
    1.63 +
    1.64 +  // If this domain isn't whitelisted, we're done.
    1.65 +  if (!nsContentUtils::AllowXULXBLForPrincipal(aPrincipal))
    1.66 +      return false;
    1.67 +
    1.68 +  // Check the pref to determine how we should behave.
    1.69 +  return !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false);
    1.70 +}
    1.71 +
    1.72 +XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx,
    1.73 +                                             JS::HandleObject aGlobal)
    1.74 +      : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)),
    1.75 +        mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
    1.76 +        mComponents(nullptr),
    1.77 +        mNext(nullptr),
    1.78 +        mGlobalJSObject(aGlobal),
    1.79 +        mIsXBLScope(false)
    1.80 +{
    1.81 +    // add ourselves to the scopes list
    1.82 +    {
    1.83 +        MOZ_ASSERT(aGlobal);
    1.84 +        MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
    1.85 +                                                         JSCLASS_HAS_PRIVATE)); 
    1.86 +#ifdef DEBUG
    1.87 +        for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
    1.88 +            MOZ_ASSERT(aGlobal != cur->GetGlobalJSObjectPreserveColor(), "dup object");
    1.89 +#endif
    1.90 +
    1.91 +        mNext = gScopes;
    1.92 +        gScopes = this;
    1.93 +
    1.94 +        // Grab the XPCContext associated with our context.
    1.95 +        mContext = XPCContext::GetXPCContext(cx);
    1.96 +        mContext->AddScope(this);
    1.97 +    }
    1.98 +
    1.99 +    MOZ_COUNT_CTOR(XPCWrappedNativeScope);
   1.100 +
   1.101 +    // Attach ourselves to the compartment private.
   1.102 +    CompartmentPrivate *priv = EnsureCompartmentPrivate(aGlobal);
   1.103 +    priv->scope = this;
   1.104 +
   1.105 +    // Determine whether we would allow an XBL scope in this situation.
   1.106 +    // In addition to being pref-controlled, we also disable XBL scopes for
   1.107 +    // remote XUL domains, _except_ if we have an additional pref override set.
   1.108 +    nsIPrincipal *principal = GetPrincipal();
   1.109 +    mAllowXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal);
   1.110 +
   1.111 +    // Determine whether to use an XBL scope.
   1.112 +    mUseXBLScope = mAllowXBLScope;
   1.113 +    if (mUseXBLScope) {
   1.114 +      const js::Class *clasp = js::GetObjectClass(mGlobalJSObject);
   1.115 +      mUseXBLScope = !strcmp(clasp->name, "Window") ||
   1.116 +                     !strcmp(clasp->name, "ChromeWindow") ||
   1.117 +                     !strcmp(clasp->name, "ModalContentWindow");
   1.118 +    }
   1.119 +    if (mUseXBLScope) {
   1.120 +      mUseXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
   1.121 +    }
   1.122 +}
   1.123 +
   1.124 +// static
   1.125 +bool
   1.126 +XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope *scope)
   1.127 +{
   1.128 +    for (XPCWrappedNativeScope *cur = gDyingScopes; cur; cur = cur->mNext) {
   1.129 +        if (scope == cur)
   1.130 +            return true;
   1.131 +    }
   1.132 +    return false;
   1.133 +}
   1.134 +
   1.135 +bool
   1.136 +XPCWrappedNativeScope::GetComponentsJSObject(JS::MutableHandleObject obj)
   1.137 +{
   1.138 +    AutoJSContext cx;
   1.139 +    if (!mComponents) {
   1.140 +        nsIPrincipal *p = GetPrincipal();
   1.141 +        bool system = XPCWrapper::GetSecurityManager()->IsSystemPrincipal(p);
   1.142 +        mComponents = system ? new nsXPCComponents(this)
   1.143 +                             : new nsXPCComponentsBase(this);
   1.144 +    }
   1.145 +
   1.146 +    RootedValue val(cx);
   1.147 +    xpcObjectHelper helper(mComponents);
   1.148 +    bool ok = XPCConvert::NativeInterface2JSObject(&val, nullptr, helper,
   1.149 +                                                   nullptr, nullptr, false,
   1.150 +                                                   nullptr);
   1.151 +    if (NS_WARN_IF(!ok))
   1.152 +        return false;
   1.153 +
   1.154 +    if (NS_WARN_IF(!val.isObject()))
   1.155 +        return false;
   1.156 +
   1.157 +    // The call to wrap() here is necessary even though the object is same-
   1.158 +    // compartment, because it applies our security wrapper.
   1.159 +    obj.set(&val.toObject());
   1.160 +    if (NS_WARN_IF(!JS_WrapObject(cx, obj)))
   1.161 +        return false;
   1.162 +    return true;
   1.163 +}
   1.164 +
   1.165 +void
   1.166 +XPCWrappedNativeScope::ForcePrivilegedComponents()
   1.167 +{
   1.168 +    // This may only be called on unprivileged scopes during automation where
   1.169 +    // we allow insecure things.
   1.170 +    MOZ_RELEASE_ASSERT(Preferences::GetBool("security.turn_off_all_security_so_"
   1.171 +                                            "that_viruses_can_take_over_this_"
   1.172 +                                            "computer"));
   1.173 +    nsCOMPtr<nsIXPCComponents> c = do_QueryInterface(mComponents);
   1.174 +    if (!c)
   1.175 +        mComponents = new nsXPCComponents(this);
   1.176 +}
   1.177 +
   1.178 +bool
   1.179 +XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx)
   1.180 +{
   1.181 +    RootedObject components(aCx);
   1.182 +    if (!GetComponentsJSObject(&components))
   1.183 +        return false;
   1.184 +
   1.185 +    RootedObject global(aCx, GetGlobalJSObject());
   1.186 +    MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx));
   1.187 +
   1.188 +    RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS));
   1.189 +    return JS_DefinePropertyById(aCx, global, id, ObjectValue(*components),
   1.190 +                                 nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY);
   1.191 +}
   1.192 +
   1.193 +JSObject*
   1.194 +XPCWrappedNativeScope::EnsureXBLScope(JSContext *cx)
   1.195 +{
   1.196 +    JS::RootedObject global(cx, GetGlobalJSObject());
   1.197 +    MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
   1.198 +    MOZ_ASSERT(!mIsXBLScope);
   1.199 +    MOZ_ASSERT(strcmp(js::GetObjectClass(global)->name,
   1.200 +                      "nsXBLPrototypeScript compilation scope"));
   1.201 +
   1.202 +    // If we already have a special XBL scope object, we know what to use.
   1.203 +    if (mXBLScope)
   1.204 +        return mXBLScope;
   1.205 +
   1.206 +    // If this scope doesn't need an XBL scope, just return the global.
   1.207 +    if (!mUseXBLScope)
   1.208 +        return global;
   1.209 +
   1.210 +    // Set up the sandbox options. Note that we use the DOM global as the
   1.211 +    // sandboxPrototype so that the XBL scope can access all the DOM objects
   1.212 +    // it's accustomed to accessing.
   1.213 +    //
   1.214 +    // NB: One would think that wantXrays wouldn't make a difference here.
   1.215 +    // However, wantXrays lives a secret double life, and one of its other
   1.216 +    // hobbies is to waive Xray on the returned sandbox when set to false.
   1.217 +    // So make sure to keep this set to true, here.
   1.218 +    SandboxOptions options;
   1.219 +    options.wantXrays = true;
   1.220 +    options.wantComponents = true;
   1.221 +    options.proto = global;
   1.222 +    options.sameZoneAs = global;
   1.223 +
   1.224 +    // Use an nsExpandedPrincipal to create asymmetric security.
   1.225 +    nsIPrincipal *principal = GetPrincipal();
   1.226 +    nsCOMPtr<nsIExpandedPrincipal> ep;
   1.227 +    MOZ_ASSERT(!(ep = do_QueryInterface(principal)));
   1.228 +    nsTArray< nsCOMPtr<nsIPrincipal> > principalAsArray(1);
   1.229 +    principalAsArray.AppendElement(principal);
   1.230 +    ep = new nsExpandedPrincipal(principalAsArray);
   1.231 +
   1.232 +    // Create the sandbox.
   1.233 +    RootedValue v(cx);
   1.234 +    nsresult rv = CreateSandboxObject(cx, &v, ep, options);
   1.235 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.236 +    mXBLScope = &v.toObject();
   1.237 +
   1.238 +    // Tag it.
   1.239 +    EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true;
   1.240 +
   1.241 +    // Good to go!
   1.242 +    return mXBLScope;
   1.243 +}
   1.244 +
   1.245 +bool
   1.246 +XPCWrappedNativeScope::AllowXBLScope()
   1.247 +{
   1.248 +    // We only disallow XBL scopes in remote XUL situations.
   1.249 +    MOZ_ASSERT_IF(!mAllowXBLScope,
   1.250 +                  nsContentUtils::AllowXULXBLForPrincipal(GetPrincipal()));
   1.251 +    return mAllowXBLScope;
   1.252 +}
   1.253 +
   1.254 +namespace xpc {
   1.255 +JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
   1.256 +{
   1.257 +    JS::RootedObject contentScope(cx, contentScopeArg);
   1.258 +    JSAutoCompartment ac(cx, contentScope);
   1.259 +    JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx);
   1.260 +    NS_ENSURE_TRUE(scope, nullptr); // See bug 858642.
   1.261 +    scope = js::UncheckedUnwrap(scope);
   1.262 +    JS::ExposeObjectToActiveJS(scope);
   1.263 +    return scope;
   1.264 +}
   1.265 +
   1.266 +bool AllowXBLScope(JSCompartment *c)
   1.267 +{
   1.268 +  XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
   1.269 +  return scope && scope->AllowXBLScope();
   1.270 +}
   1.271 +
   1.272 +bool UseXBLScope(JSCompartment *c)
   1.273 +{
   1.274 +  XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
   1.275 +  return scope && scope->UseXBLScope();
   1.276 +}
   1.277 +
   1.278 +} /* namespace xpc */
   1.279 +
   1.280 +XPCWrappedNativeScope::~XPCWrappedNativeScope()
   1.281 +{
   1.282 +    MOZ_COUNT_DTOR(XPCWrappedNativeScope);
   1.283 +
   1.284 +    // We can do additional cleanup assertions here...
   1.285 +
   1.286 +    if (mWrappedNativeMap) {
   1.287 +        MOZ_ASSERT(0 == mWrappedNativeMap->Count(), "scope has non-empty map");
   1.288 +        delete mWrappedNativeMap;
   1.289 +    }
   1.290 +
   1.291 +    if (mWrappedNativeProtoMap) {
   1.292 +        MOZ_ASSERT(0 == mWrappedNativeProtoMap->Count(), "scope has non-empty map");
   1.293 +        delete mWrappedNativeProtoMap;
   1.294 +    }
   1.295 +
   1.296 +    if (mContext)
   1.297 +        mContext->RemoveScope(this);
   1.298 +
   1.299 +    // This should not be necessary, since the Components object should die
   1.300 +    // with the scope but just in case.
   1.301 +    if (mComponents)
   1.302 +        mComponents->mScope = nullptr;
   1.303 +
   1.304 +    // XXX we should assert that we are dead or that xpconnect has shutdown
   1.305 +    // XXX might not want to do this at xpconnect shutdown time???
   1.306 +    mComponents = nullptr;
   1.307 +
   1.308 +    if (mXrayExpandos.initialized())
   1.309 +        mXrayExpandos.destroy();
   1.310 +
   1.311 +    JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
   1.312 +    mXBLScope.finalize(rt);
   1.313 +    mGlobalJSObject.finalize(rt);
   1.314 +}
   1.315 +
   1.316 +static PLDHashOperator
   1.317 +WrappedNativeJSGCThingTracer(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.318 +                             uint32_t number, void *arg)
   1.319 +{
   1.320 +    XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
   1.321 +    if (wrapper->HasExternalReference() && !wrapper->IsWrapperExpired())
   1.322 +        wrapper->TraceSelf((JSTracer *)arg);
   1.323 +
   1.324 +    return PL_DHASH_NEXT;
   1.325 +}
   1.326 +
   1.327 +// static
   1.328 +void
   1.329 +XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt)
   1.330 +{
   1.331 +    // Do JS_CallTracer for all wrapped natives with external references, as
   1.332 +    // well as any DOM expando objects.
   1.333 +    for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
   1.334 +        cur->mWrappedNativeMap->Enumerate(WrappedNativeJSGCThingTracer, trc);
   1.335 +        if (cur->mDOMExpandoSet) {
   1.336 +            for (DOMExpandoSet::Enum e(*cur->mDOMExpandoSet); !e.empty(); e.popFront())
   1.337 +                JS_CallHashSetObjectTracer(trc, e, e.front(), "DOM expando object");
   1.338 +        }
   1.339 +    }
   1.340 +}
   1.341 +
   1.342 +static PLDHashOperator
   1.343 +WrappedNativeSuspecter(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.344 +                       uint32_t number, void *arg)
   1.345 +{
   1.346 +    XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
   1.347 +
   1.348 +    if (wrapper->HasExternalReference()) {
   1.349 +        nsCycleCollectionNoteRootCallback *cb =
   1.350 +            static_cast<nsCycleCollectionNoteRootCallback *>(arg);
   1.351 +        XPCJSRuntime::SuspectWrappedNative(wrapper, *cb);
   1.352 +    }
   1.353 +
   1.354 +    return PL_DHASH_NEXT;
   1.355 +}
   1.356 +
   1.357 +static void
   1.358 +SuspectDOMExpandos(JSObject *obj, nsCycleCollectionNoteRootCallback &cb)
   1.359 +{
   1.360 +    MOZ_ASSERT(dom::GetDOMClass(obj) && dom::GetDOMClass(obj)->mDOMObjectIsISupports);
   1.361 +    nsISupports* native = dom::UnwrapDOMObject<nsISupports>(obj);
   1.362 +    cb.NoteXPCOMRoot(native);
   1.363 +}
   1.364 +
   1.365 +// static
   1.366 +void
   1.367 +XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt,
   1.368 +                                          nsCycleCollectionNoteRootCallback& cb)
   1.369 +{
   1.370 +    for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
   1.371 +        cur->mWrappedNativeMap->Enumerate(WrappedNativeSuspecter, &cb);
   1.372 +        if (cur->mDOMExpandoSet) {
   1.373 +            for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront())
   1.374 +                SuspectDOMExpandos(r.front(), cb);
   1.375 +        }
   1.376 +    }
   1.377 +}
   1.378 +
   1.379 +// static
   1.380 +void
   1.381 +XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt)
   1.382 +{
   1.383 +    // We are in JSGC_MARK_END and JSGC_FINALIZE_END must always follow it
   1.384 +    // calling FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
   1.385 +    // KillDyingScopes.
   1.386 +    MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END");
   1.387 +
   1.388 +    XPCWrappedNativeScope* prev = nullptr;
   1.389 +    XPCWrappedNativeScope* cur = gScopes;
   1.390 +
   1.391 +    while (cur) {
   1.392 +        // Sweep waivers.
   1.393 +        if (cur->mWaiverWrapperMap)
   1.394 +            cur->mWaiverWrapperMap->Sweep();
   1.395 +
   1.396 +        XPCWrappedNativeScope* next = cur->mNext;
   1.397 +
   1.398 +        if (cur->mGlobalJSObject && cur->mGlobalJSObject.isAboutToBeFinalized()) {
   1.399 +            cur->mGlobalJSObject.finalize(fop->runtime());
   1.400 +            // Move this scope from the live list to the dying list.
   1.401 +            if (prev)
   1.402 +                prev->mNext = next;
   1.403 +            else
   1.404 +                gScopes = next;
   1.405 +            cur->mNext = gDyingScopes;
   1.406 +            gDyingScopes = cur;
   1.407 +            cur = nullptr;
   1.408 +        }
   1.409 +        if (cur)
   1.410 +            prev = cur;
   1.411 +        cur = next;
   1.412 +    }
   1.413 +}
   1.414 +
   1.415 +// static
   1.416 +void
   1.417 +XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC()
   1.418 +{
   1.419 +    KillDyingScopes();
   1.420 +}
   1.421 +
   1.422 +static PLDHashOperator
   1.423 +WrappedNativeMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.424 +                    uint32_t number_t, void *arg)
   1.425 +{
   1.426 +    ((Native2WrappedNativeMap::Entry*)hdr)->value->Mark();
   1.427 +    return PL_DHASH_NEXT;
   1.428 +}
   1.429 +
   1.430 +// We need to explicitly mark all the protos too because some protos may be
   1.431 +// alive in the hashtable but not currently in use by any wrapper
   1.432 +static PLDHashOperator
   1.433 +WrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.434 +                         uint32_t number, void *arg)
   1.435 +{
   1.436 +    ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->Mark();
   1.437 +    return PL_DHASH_NEXT;
   1.438 +}
   1.439 +
   1.440 +// static
   1.441 +void
   1.442 +XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos()
   1.443 +{
   1.444 +    for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
   1.445 +        cur->mWrappedNativeMap->Enumerate(WrappedNativeMarker, nullptr);
   1.446 +        cur->mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMarker, nullptr);
   1.447 +    }
   1.448 +}
   1.449 +
   1.450 +#ifdef DEBUG
   1.451 +static PLDHashOperator
   1.452 +ASSERT_WrappedNativeSetNotMarked(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.453 +                                 uint32_t number, void *arg)
   1.454 +{
   1.455 +    ((Native2WrappedNativeMap::Entry*)hdr)->value->ASSERT_SetsNotMarked();
   1.456 +    return PL_DHASH_NEXT;
   1.457 +}
   1.458 +
   1.459 +static PLDHashOperator
   1.460 +ASSERT_WrappedNativeProtoSetNotMarked(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.461 +                                      uint32_t number, void *arg)
   1.462 +{
   1.463 +    ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->ASSERT_SetNotMarked();
   1.464 +    return PL_DHASH_NEXT;
   1.465 +}
   1.466 +
   1.467 +// static
   1.468 +void
   1.469 +XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked()
   1.470 +{
   1.471 +    for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
   1.472 +        cur->mWrappedNativeMap->Enumerate(ASSERT_WrappedNativeSetNotMarked, nullptr);
   1.473 +        cur->mWrappedNativeProtoMap->Enumerate(ASSERT_WrappedNativeProtoSetNotMarked, nullptr);
   1.474 +    }
   1.475 +}
   1.476 +#endif
   1.477 +
   1.478 +static PLDHashOperator
   1.479 +WrappedNativeTearoffSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.480 +                            uint32_t number, void *arg)
   1.481 +{
   1.482 +    ((Native2WrappedNativeMap::Entry*)hdr)->value->SweepTearOffs();
   1.483 +    return PL_DHASH_NEXT;
   1.484 +}
   1.485 +
   1.486 +// static
   1.487 +void
   1.488 +XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs()
   1.489 +{
   1.490 +    for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
   1.491 +        cur->mWrappedNativeMap->Enumerate(WrappedNativeTearoffSweeper, nullptr);
   1.492 +}
   1.493 +
   1.494 +// static
   1.495 +void
   1.496 +XPCWrappedNativeScope::KillDyingScopes()
   1.497 +{
   1.498 +    XPCWrappedNativeScope* cur = gDyingScopes;
   1.499 +    while (cur) {
   1.500 +        XPCWrappedNativeScope* next = cur->mNext;
   1.501 +        if (cur->mGlobalJSObject)
   1.502 +            GetCompartmentPrivate(cur->mGlobalJSObject)->scope = nullptr;
   1.503 +        delete cur;
   1.504 +        cur = next;
   1.505 +    }
   1.506 +    gDyingScopes = nullptr;
   1.507 +}
   1.508 +
   1.509 +struct ShutdownData
   1.510 +{
   1.511 +    ShutdownData()
   1.512 +        : wrapperCount(0),
   1.513 +          protoCount(0) {}
   1.514 +    int wrapperCount;
   1.515 +    int protoCount;
   1.516 +};
   1.517 +
   1.518 +static PLDHashOperator
   1.519 +WrappedNativeShutdownEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.520 +                                uint32_t number, void *arg)
   1.521 +{
   1.522 +    ShutdownData* data = (ShutdownData*) arg;
   1.523 +    XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
   1.524 +
   1.525 +    if (wrapper->IsValid()) {
   1.526 +        wrapper->SystemIsBeingShutDown();
   1.527 +        data->wrapperCount++;
   1.528 +    }
   1.529 +    return PL_DHASH_REMOVE;
   1.530 +}
   1.531 +
   1.532 +static PLDHashOperator
   1.533 +WrappedNativeProtoShutdownEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.534 +                                     uint32_t number, void *arg)
   1.535 +{
   1.536 +    ShutdownData* data = (ShutdownData*) arg;
   1.537 +    ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->
   1.538 +        SystemIsBeingShutDown();
   1.539 +    data->protoCount++;
   1.540 +    return PL_DHASH_REMOVE;
   1.541 +}
   1.542 +
   1.543 +//static
   1.544 +void
   1.545 +XPCWrappedNativeScope::SystemIsBeingShutDown()
   1.546 +{
   1.547 +    int liveScopeCount = 0;
   1.548 +
   1.549 +    ShutdownData data;
   1.550 +
   1.551 +    XPCWrappedNativeScope* cur;
   1.552 +
   1.553 +    // First move all the scopes to the dying list.
   1.554 +
   1.555 +    cur = gScopes;
   1.556 +    while (cur) {
   1.557 +        XPCWrappedNativeScope* next = cur->mNext;
   1.558 +        cur->mNext = gDyingScopes;
   1.559 +        gDyingScopes = cur;
   1.560 +        cur = next;
   1.561 +        liveScopeCount++;
   1.562 +    }
   1.563 +    gScopes = nullptr;
   1.564 +
   1.565 +    // We're forcibly killing scopes, rather than allowing them to go away
   1.566 +    // when they're ready. As such, we need to do some cleanup before they
   1.567 +    // can safely be destroyed.
   1.568 +
   1.569 +    for (cur = gDyingScopes; cur; cur = cur->mNext) {
   1.570 +        // Give the Components object a chance to try to clean up.
   1.571 +        if (cur->mComponents)
   1.572 +            cur->mComponents->SystemIsBeingShutDown();
   1.573 +
   1.574 +        // Walk the protos first. Wrapper shutdown can leave dangling
   1.575 +        // proto pointers in the proto map.
   1.576 +        cur->mWrappedNativeProtoMap->
   1.577 +                Enumerate(WrappedNativeProtoShutdownEnumerator,  &data);
   1.578 +        cur->mWrappedNativeMap->
   1.579 +                Enumerate(WrappedNativeShutdownEnumerator,  &data);
   1.580 +    }
   1.581 +
   1.582 +    // Now it is safe to kill all the scopes.
   1.583 +    KillDyingScopes();
   1.584 +}
   1.585 +
   1.586 +
   1.587 +/***************************************************************************/
   1.588 +
   1.589 +static PLDHashOperator
   1.590 +WNProtoRemover(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.591 +               uint32_t number, void *arg)
   1.592 +{
   1.593 +    XPCWrappedNativeProtoMap* detachedMap = (XPCWrappedNativeProtoMap*)arg;
   1.594 +
   1.595 +    XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)
   1.596 +        ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value;
   1.597 +
   1.598 +    detachedMap->Add(proto);
   1.599 +
   1.600 +    return PL_DHASH_REMOVE;
   1.601 +}
   1.602 +
   1.603 +void
   1.604 +XPCWrappedNativeScope::RemoveWrappedNativeProtos()
   1.605 +{
   1.606 +    mWrappedNativeProtoMap->Enumerate(WNProtoRemover,
   1.607 +                                      GetRuntime()->GetDetachedWrappedNativeProtoMap());
   1.608 +}
   1.609 +
   1.610 +JSObject *
   1.611 +XPCWrappedNativeScope::GetExpandoChain(HandleObject target)
   1.612 +{
   1.613 +    MOZ_ASSERT(GetObjectScope(target) == this);
   1.614 +    if (!mXrayExpandos.initialized())
   1.615 +        return nullptr;
   1.616 +    return mXrayExpandos.lookup(target);
   1.617 +}
   1.618 +
   1.619 +bool
   1.620 +XPCWrappedNativeScope::SetExpandoChain(JSContext *cx, HandleObject target,
   1.621 +                                       HandleObject chain)
   1.622 +{
   1.623 +    MOZ_ASSERT(GetObjectScope(target) == this);
   1.624 +    MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
   1.625 +    MOZ_ASSERT_IF(chain, GetObjectScope(chain) == this);
   1.626 +    if (!mXrayExpandos.initialized() && !mXrayExpandos.init(cx))
   1.627 +        return false;
   1.628 +    return mXrayExpandos.put(cx, target, chain);
   1.629 +}
   1.630 +
   1.631 +/***************************************************************************/
   1.632 +
   1.633 +// static
   1.634 +void
   1.635 +XPCWrappedNativeScope::DebugDumpAllScopes(int16_t depth)
   1.636 +{
   1.637 +#ifdef DEBUG
   1.638 +    depth-- ;
   1.639 +
   1.640 +    // get scope count.
   1.641 +    int count = 0;
   1.642 +    XPCWrappedNativeScope* cur;
   1.643 +    for (cur = gScopes; cur; cur = cur->mNext)
   1.644 +        count++ ;
   1.645 +
   1.646 +    XPC_LOG_ALWAYS(("chain of %d XPCWrappedNativeScope(s)", count));
   1.647 +    XPC_LOG_INDENT();
   1.648 +        XPC_LOG_ALWAYS(("gDyingScopes @ %x", gDyingScopes));
   1.649 +        if (depth)
   1.650 +            for (cur = gScopes; cur; cur = cur->mNext)
   1.651 +                cur->DebugDump(depth);
   1.652 +    XPC_LOG_OUTDENT();
   1.653 +#endif
   1.654 +}
   1.655 +
   1.656 +#ifdef DEBUG
   1.657 +static PLDHashOperator
   1.658 +WrappedNativeMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.659 +                               uint32_t number, void *arg)
   1.660 +{
   1.661 +    ((Native2WrappedNativeMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg);
   1.662 +    return PL_DHASH_NEXT;
   1.663 +}
   1.664 +static PLDHashOperator
   1.665 +WrappedNativeProtoMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.666 +                                    uint32_t number, void *arg)
   1.667 +{
   1.668 +    ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg);
   1.669 +    return PL_DHASH_NEXT;
   1.670 +}
   1.671 +#endif
   1.672 +
   1.673 +void
   1.674 +XPCWrappedNativeScope::DebugDump(int16_t depth)
   1.675 +{
   1.676 +#ifdef DEBUG
   1.677 +    depth-- ;
   1.678 +    XPC_LOG_ALWAYS(("XPCWrappedNativeScope @ %x", this));
   1.679 +    XPC_LOG_INDENT();
   1.680 +        XPC_LOG_ALWAYS(("mNext @ %x", mNext));
   1.681 +        XPC_LOG_ALWAYS(("mComponents @ %x", mComponents.get()));
   1.682 +        XPC_LOG_ALWAYS(("mGlobalJSObject @ %x", mGlobalJSObject.get()));
   1.683 +
   1.684 +        XPC_LOG_ALWAYS(("mWrappedNativeMap @ %x with %d wrappers(s)",         \
   1.685 +                        mWrappedNativeMap,                                    \
   1.686 +                        mWrappedNativeMap ? mWrappedNativeMap->Count() : 0));
   1.687 +        // iterate contexts...
   1.688 +        if (depth && mWrappedNativeMap && mWrappedNativeMap->Count()) {
   1.689 +            XPC_LOG_INDENT();
   1.690 +            mWrappedNativeMap->Enumerate(WrappedNativeMapDumpEnumerator, &depth);
   1.691 +            XPC_LOG_OUTDENT();
   1.692 +        }
   1.693 +
   1.694 +        XPC_LOG_ALWAYS(("mWrappedNativeProtoMap @ %x with %d protos(s)",      \
   1.695 +                        mWrappedNativeProtoMap,                               \
   1.696 +                        mWrappedNativeProtoMap ? mWrappedNativeProtoMap->Count() : 0));
   1.697 +        // iterate contexts...
   1.698 +        if (depth && mWrappedNativeProtoMap && mWrappedNativeProtoMap->Count()) {
   1.699 +            XPC_LOG_INDENT();
   1.700 +            mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth);
   1.701 +            XPC_LOG_OUTDENT();
   1.702 +        }
   1.703 +    XPC_LOG_OUTDENT();
   1.704 +#endif
   1.705 +}
   1.706 +
   1.707 +void
   1.708 +XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo)
   1.709 +{
   1.710 +    for (XPCWrappedNativeScope *cur = gScopes; cur; cur = cur->mNext)
   1.711 +        cur->AddSizeOfIncludingThis(scopeSizeInfo);
   1.712 +}
   1.713 +
   1.714 +void
   1.715 +XPCWrappedNativeScope::AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo)
   1.716 +{
   1.717 +    scopeSizeInfo->mScopeAndMapSize += scopeSizeInfo->mMallocSizeOf(this);
   1.718 +    scopeSizeInfo->mScopeAndMapSize +=
   1.719 +        mWrappedNativeMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
   1.720 +    scopeSizeInfo->mScopeAndMapSize +=
   1.721 +        mWrappedNativeProtoMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
   1.722 +
   1.723 +    if (dom::HasProtoAndIfaceCache(mGlobalJSObject)) {
   1.724 +        dom::ProtoAndIfaceCache* cache = dom::GetProtoAndIfaceCache(mGlobalJSObject);
   1.725 +        scopeSizeInfo->mProtoAndIfaceCacheSize +=
   1.726 +            cache->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
   1.727 +    }
   1.728 +
   1.729 +    // There are other XPCWrappedNativeScope members that could be measured;
   1.730 +    // the above ones have been seen by DMD to be worth measuring.  More stuff
   1.731 +    // may be added later.
   1.732 +}

mercurial