michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Class used to manage the wrapped native objects within a JS scope. */ michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "XPCWrapper.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCycleCollectionNoteRootCallback.h" michael@0: #include "nsPrincipal.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace xpc; michael@0: using namespace JS; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nullptr; michael@0: XPCWrappedNativeScope* XPCWrappedNativeScope::gDyingScopes = nullptr; michael@0: michael@0: // static michael@0: XPCWrappedNativeScope* michael@0: XPCWrappedNativeScope::GetNewOrUsed(JSContext *cx, JS::HandleObject aGlobal) michael@0: { michael@0: XPCWrappedNativeScope* scope = GetObjectScope(aGlobal); michael@0: if (!scope) { michael@0: scope = new XPCWrappedNativeScope(cx, aGlobal); michael@0: } michael@0: return scope; michael@0: } michael@0: michael@0: static bool michael@0: RemoteXULForbidsXBLScope(nsIPrincipal *aPrincipal, HandleObject aGlobal) michael@0: { michael@0: // Check for random JSD scopes that don't have a principal. michael@0: if (!aPrincipal) michael@0: return false; michael@0: michael@0: // The SafeJSContext is lazily created, and tends to be created at really michael@0: // weird times, at least for xpcshell (often very early in startup or late michael@0: // in shutdown). Its scope isn't system principal, so if we proceeded we'd michael@0: // end up calling into AllowXULXBLForPrincipal, which depends on all kinds michael@0: // of persistent storage and permission machinery that may or not be running. michael@0: // We know the answer to the question here, so just short-circuit. michael@0: if (JS_GetClass(aGlobal) == &SafeJSContextGlobalClass) michael@0: return false; michael@0: michael@0: // AllowXULXBLForPrincipal will return true for system principal, but we michael@0: // don't want that here. michael@0: MOZ_ASSERT(nsContentUtils::IsInitialized()); michael@0: if (nsContentUtils::IsSystemPrincipal(aPrincipal)) michael@0: return false; michael@0: michael@0: // If this domain isn't whitelisted, we're done. michael@0: if (!nsContentUtils::AllowXULXBLForPrincipal(aPrincipal)) michael@0: return false; michael@0: michael@0: // Check the pref to determine how we should behave. michael@0: return !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false); michael@0: } michael@0: michael@0: XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx, michael@0: JS::HandleObject aGlobal) michael@0: : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)), michael@0: mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)), michael@0: mComponents(nullptr), michael@0: mNext(nullptr), michael@0: mGlobalJSObject(aGlobal), michael@0: mIsXBLScope(false) michael@0: { michael@0: // add ourselves to the scopes list michael@0: { michael@0: MOZ_ASSERT(aGlobal); michael@0: MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS | michael@0: JSCLASS_HAS_PRIVATE)); michael@0: #ifdef DEBUG michael@0: for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) michael@0: MOZ_ASSERT(aGlobal != cur->GetGlobalJSObjectPreserveColor(), "dup object"); michael@0: #endif michael@0: michael@0: mNext = gScopes; michael@0: gScopes = this; michael@0: michael@0: // Grab the XPCContext associated with our context. michael@0: mContext = XPCContext::GetXPCContext(cx); michael@0: mContext->AddScope(this); michael@0: } michael@0: michael@0: MOZ_COUNT_CTOR(XPCWrappedNativeScope); michael@0: michael@0: // Attach ourselves to the compartment private. michael@0: CompartmentPrivate *priv = EnsureCompartmentPrivate(aGlobal); michael@0: priv->scope = this; michael@0: michael@0: // Determine whether we would allow an XBL scope in this situation. michael@0: // In addition to being pref-controlled, we also disable XBL scopes for michael@0: // remote XUL domains, _except_ if we have an additional pref override set. michael@0: nsIPrincipal *principal = GetPrincipal(); michael@0: mAllowXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal); michael@0: michael@0: // Determine whether to use an XBL scope. michael@0: mUseXBLScope = mAllowXBLScope; michael@0: if (mUseXBLScope) { michael@0: const js::Class *clasp = js::GetObjectClass(mGlobalJSObject); michael@0: mUseXBLScope = !strcmp(clasp->name, "Window") || michael@0: !strcmp(clasp->name, "ChromeWindow") || michael@0: !strcmp(clasp->name, "ModalContentWindow"); michael@0: } michael@0: if (mUseXBLScope) { michael@0: mUseXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope *scope) michael@0: { michael@0: for (XPCWrappedNativeScope *cur = gDyingScopes; cur; cur = cur->mNext) { michael@0: if (scope == cur) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeScope::GetComponentsJSObject(JS::MutableHandleObject obj) michael@0: { michael@0: AutoJSContext cx; michael@0: if (!mComponents) { michael@0: nsIPrincipal *p = GetPrincipal(); michael@0: bool system = XPCWrapper::GetSecurityManager()->IsSystemPrincipal(p); michael@0: mComponents = system ? new nsXPCComponents(this) michael@0: : new nsXPCComponentsBase(this); michael@0: } michael@0: michael@0: RootedValue val(cx); michael@0: xpcObjectHelper helper(mComponents); michael@0: bool ok = XPCConvert::NativeInterface2JSObject(&val, nullptr, helper, michael@0: nullptr, nullptr, false, michael@0: nullptr); michael@0: if (NS_WARN_IF(!ok)) michael@0: return false; michael@0: michael@0: if (NS_WARN_IF(!val.isObject())) michael@0: return false; michael@0: michael@0: // The call to wrap() here is necessary even though the object is same- michael@0: // compartment, because it applies our security wrapper. michael@0: obj.set(&val.toObject()); michael@0: if (NS_WARN_IF(!JS_WrapObject(cx, obj))) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: XPCWrappedNativeScope::ForcePrivilegedComponents() michael@0: { michael@0: // This may only be called on unprivileged scopes during automation where michael@0: // we allow insecure things. michael@0: MOZ_RELEASE_ASSERT(Preferences::GetBool("security.turn_off_all_security_so_" michael@0: "that_viruses_can_take_over_this_" michael@0: "computer")); michael@0: nsCOMPtr c = do_QueryInterface(mComponents); michael@0: if (!c) michael@0: mComponents = new nsXPCComponents(this); michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx) michael@0: { michael@0: RootedObject components(aCx); michael@0: if (!GetComponentsJSObject(&components)) michael@0: return false; michael@0: michael@0: RootedObject global(aCx, GetGlobalJSObject()); michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx)); michael@0: michael@0: RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)); michael@0: return JS_DefinePropertyById(aCx, global, id, ObjectValue(*components), michael@0: nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY); michael@0: } michael@0: michael@0: JSObject* michael@0: XPCWrappedNativeScope::EnsureXBLScope(JSContext *cx) michael@0: { michael@0: JS::RootedObject global(cx, GetGlobalJSObject()); michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx)); michael@0: MOZ_ASSERT(!mIsXBLScope); michael@0: MOZ_ASSERT(strcmp(js::GetObjectClass(global)->name, michael@0: "nsXBLPrototypeScript compilation scope")); michael@0: michael@0: // If we already have a special XBL scope object, we know what to use. michael@0: if (mXBLScope) michael@0: return mXBLScope; michael@0: michael@0: // If this scope doesn't need an XBL scope, just return the global. michael@0: if (!mUseXBLScope) michael@0: return global; michael@0: michael@0: // Set up the sandbox options. Note that we use the DOM global as the michael@0: // sandboxPrototype so that the XBL scope can access all the DOM objects michael@0: // it's accustomed to accessing. michael@0: // michael@0: // NB: One would think that wantXrays wouldn't make a difference here. michael@0: // However, wantXrays lives a secret double life, and one of its other michael@0: // hobbies is to waive Xray on the returned sandbox when set to false. michael@0: // So make sure to keep this set to true, here. michael@0: SandboxOptions options; michael@0: options.wantXrays = true; michael@0: options.wantComponents = true; michael@0: options.proto = global; michael@0: options.sameZoneAs = global; michael@0: michael@0: // Use an nsExpandedPrincipal to create asymmetric security. michael@0: nsIPrincipal *principal = GetPrincipal(); michael@0: nsCOMPtr ep; michael@0: MOZ_ASSERT(!(ep = do_QueryInterface(principal))); michael@0: nsTArray< nsCOMPtr > principalAsArray(1); michael@0: principalAsArray.AppendElement(principal); michael@0: ep = new nsExpandedPrincipal(principalAsArray); michael@0: michael@0: // Create the sandbox. michael@0: RootedValue v(cx); michael@0: nsresult rv = CreateSandboxObject(cx, &v, ep, options); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: mXBLScope = &v.toObject(); michael@0: michael@0: // Tag it. michael@0: EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true; michael@0: michael@0: // Good to go! michael@0: return mXBLScope; michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeScope::AllowXBLScope() michael@0: { michael@0: // We only disallow XBL scopes in remote XUL situations. michael@0: MOZ_ASSERT_IF(!mAllowXBLScope, michael@0: nsContentUtils::AllowXULXBLForPrincipal(GetPrincipal())); michael@0: return mAllowXBLScope; michael@0: } michael@0: michael@0: namespace xpc { michael@0: JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg) michael@0: { michael@0: JS::RootedObject contentScope(cx, contentScopeArg); michael@0: JSAutoCompartment ac(cx, contentScope); michael@0: JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx); michael@0: NS_ENSURE_TRUE(scope, nullptr); // See bug 858642. michael@0: scope = js::UncheckedUnwrap(scope); michael@0: JS::ExposeObjectToActiveJS(scope); michael@0: return scope; michael@0: } michael@0: michael@0: bool AllowXBLScope(JSCompartment *c) michael@0: { michael@0: XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope; michael@0: return scope && scope->AllowXBLScope(); michael@0: } michael@0: michael@0: bool UseXBLScope(JSCompartment *c) michael@0: { michael@0: XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope; michael@0: return scope && scope->UseXBLScope(); michael@0: } michael@0: michael@0: } /* namespace xpc */ michael@0: michael@0: XPCWrappedNativeScope::~XPCWrappedNativeScope() michael@0: { michael@0: MOZ_COUNT_DTOR(XPCWrappedNativeScope); michael@0: michael@0: // We can do additional cleanup assertions here... michael@0: michael@0: if (mWrappedNativeMap) { michael@0: MOZ_ASSERT(0 == mWrappedNativeMap->Count(), "scope has non-empty map"); michael@0: delete mWrappedNativeMap; michael@0: } michael@0: michael@0: if (mWrappedNativeProtoMap) { michael@0: MOZ_ASSERT(0 == mWrappedNativeProtoMap->Count(), "scope has non-empty map"); michael@0: delete mWrappedNativeProtoMap; michael@0: } michael@0: michael@0: if (mContext) michael@0: mContext->RemoveScope(this); michael@0: michael@0: // This should not be necessary, since the Components object should die michael@0: // with the scope but just in case. michael@0: if (mComponents) michael@0: mComponents->mScope = nullptr; michael@0: michael@0: // XXX we should assert that we are dead or that xpconnect has shutdown michael@0: // XXX might not want to do this at xpconnect shutdown time??? michael@0: mComponents = nullptr; michael@0: michael@0: if (mXrayExpandos.initialized()) michael@0: mXrayExpandos.destroy(); michael@0: michael@0: JSRuntime *rt = XPCJSRuntime::Get()->Runtime(); michael@0: mXBLScope.finalize(rt); michael@0: mGlobalJSObject.finalize(rt); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: WrappedNativeJSGCThingTracer(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value; michael@0: if (wrapper->HasExternalReference() && !wrapper->IsWrapperExpired()) michael@0: wrapper->TraceSelf((JSTracer *)arg); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt) michael@0: { michael@0: // Do JS_CallTracer for all wrapped natives with external references, as michael@0: // well as any DOM expando objects. michael@0: for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) { michael@0: cur->mWrappedNativeMap->Enumerate(WrappedNativeJSGCThingTracer, trc); michael@0: if (cur->mDOMExpandoSet) { michael@0: for (DOMExpandoSet::Enum e(*cur->mDOMExpandoSet); !e.empty(); e.popFront()) michael@0: JS_CallHashSetObjectTracer(trc, e, e.front(), "DOM expando object"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: WrappedNativeSuspecter(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value; michael@0: michael@0: if (wrapper->HasExternalReference()) { michael@0: nsCycleCollectionNoteRootCallback *cb = michael@0: static_cast(arg); michael@0: XPCJSRuntime::SuspectWrappedNative(wrapper, *cb); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: static void michael@0: SuspectDOMExpandos(JSObject *obj, nsCycleCollectionNoteRootCallback &cb) michael@0: { michael@0: MOZ_ASSERT(dom::GetDOMClass(obj) && dom::GetDOMClass(obj)->mDOMObjectIsISupports); michael@0: nsISupports* native = dom::UnwrapDOMObject(obj); michael@0: cb.NoteXPCOMRoot(native); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt, michael@0: nsCycleCollectionNoteRootCallback& cb) michael@0: { michael@0: for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) { michael@0: cur->mWrappedNativeMap->Enumerate(WrappedNativeSuspecter, &cb); michael@0: if (cur->mDOMExpandoSet) { michael@0: for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront()) michael@0: SuspectDOMExpandos(r.front(), cb); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt) michael@0: { michael@0: // We are in JSGC_MARK_END and JSGC_FINALIZE_END must always follow it michael@0: // calling FinishedFinalizationPhaseOfGC and clearing gDyingScopes in michael@0: // KillDyingScopes. michael@0: MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END"); michael@0: michael@0: XPCWrappedNativeScope* prev = nullptr; michael@0: XPCWrappedNativeScope* cur = gScopes; michael@0: michael@0: while (cur) { michael@0: // Sweep waivers. michael@0: if (cur->mWaiverWrapperMap) michael@0: cur->mWaiverWrapperMap->Sweep(); michael@0: michael@0: XPCWrappedNativeScope* next = cur->mNext; michael@0: michael@0: if (cur->mGlobalJSObject && cur->mGlobalJSObject.isAboutToBeFinalized()) { michael@0: cur->mGlobalJSObject.finalize(fop->runtime()); michael@0: // Move this scope from the live list to the dying list. michael@0: if (prev) michael@0: prev->mNext = next; michael@0: else michael@0: gScopes = next; michael@0: cur->mNext = gDyingScopes; michael@0: gDyingScopes = cur; michael@0: cur = nullptr; michael@0: } michael@0: if (cur) michael@0: prev = cur; michael@0: cur = next; michael@0: } michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC() michael@0: { michael@0: KillDyingScopes(); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: WrappedNativeMarker(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number_t, void *arg) michael@0: { michael@0: ((Native2WrappedNativeMap::Entry*)hdr)->value->Mark(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // We need to explicitly mark all the protos too because some protos may be michael@0: // alive in the hashtable but not currently in use by any wrapper michael@0: static PLDHashOperator michael@0: WrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->Mark(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos() michael@0: { michael@0: for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) { michael@0: cur->mWrappedNativeMap->Enumerate(WrappedNativeMarker, nullptr); michael@0: cur->mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMarker, nullptr); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static PLDHashOperator michael@0: ASSERT_WrappedNativeSetNotMarked(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ((Native2WrappedNativeMap::Entry*)hdr)->value->ASSERT_SetsNotMarked(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: ASSERT_WrappedNativeProtoSetNotMarked(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->ASSERT_SetNotMarked(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked() michael@0: { michael@0: for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) { michael@0: cur->mWrappedNativeMap->Enumerate(ASSERT_WrappedNativeSetNotMarked, nullptr); michael@0: cur->mWrappedNativeProtoMap->Enumerate(ASSERT_WrappedNativeProtoSetNotMarked, nullptr); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: static PLDHashOperator michael@0: WrappedNativeTearoffSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ((Native2WrappedNativeMap::Entry*)hdr)->value->SweepTearOffs(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs() michael@0: { michael@0: for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) michael@0: cur->mWrappedNativeMap->Enumerate(WrappedNativeTearoffSweeper, nullptr); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::KillDyingScopes() michael@0: { michael@0: XPCWrappedNativeScope* cur = gDyingScopes; michael@0: while (cur) { michael@0: XPCWrappedNativeScope* next = cur->mNext; michael@0: if (cur->mGlobalJSObject) michael@0: GetCompartmentPrivate(cur->mGlobalJSObject)->scope = nullptr; michael@0: delete cur; michael@0: cur = next; michael@0: } michael@0: gDyingScopes = nullptr; michael@0: } michael@0: michael@0: struct ShutdownData michael@0: { michael@0: ShutdownData() michael@0: : wrapperCount(0), michael@0: protoCount(0) {} michael@0: int wrapperCount; michael@0: int protoCount; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: WrappedNativeShutdownEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ShutdownData* data = (ShutdownData*) arg; michael@0: XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value; michael@0: michael@0: if (wrapper->IsValid()) { michael@0: wrapper->SystemIsBeingShutDown(); michael@0: data->wrapperCount++; michael@0: } michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: WrappedNativeProtoShutdownEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ShutdownData* data = (ShutdownData*) arg; michael@0: ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value-> michael@0: SystemIsBeingShutDown(); michael@0: data->protoCount++; michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: //static michael@0: void michael@0: XPCWrappedNativeScope::SystemIsBeingShutDown() michael@0: { michael@0: int liveScopeCount = 0; michael@0: michael@0: ShutdownData data; michael@0: michael@0: XPCWrappedNativeScope* cur; michael@0: michael@0: // First move all the scopes to the dying list. michael@0: michael@0: cur = gScopes; michael@0: while (cur) { michael@0: XPCWrappedNativeScope* next = cur->mNext; michael@0: cur->mNext = gDyingScopes; michael@0: gDyingScopes = cur; michael@0: cur = next; michael@0: liveScopeCount++; michael@0: } michael@0: gScopes = nullptr; michael@0: michael@0: // We're forcibly killing scopes, rather than allowing them to go away michael@0: // when they're ready. As such, we need to do some cleanup before they michael@0: // can safely be destroyed. michael@0: michael@0: for (cur = gDyingScopes; cur; cur = cur->mNext) { michael@0: // Give the Components object a chance to try to clean up. michael@0: if (cur->mComponents) michael@0: cur->mComponents->SystemIsBeingShutDown(); michael@0: michael@0: // Walk the protos first. Wrapper shutdown can leave dangling michael@0: // proto pointers in the proto map. michael@0: cur->mWrappedNativeProtoMap-> michael@0: Enumerate(WrappedNativeProtoShutdownEnumerator, &data); michael@0: cur->mWrappedNativeMap-> michael@0: Enumerate(WrappedNativeShutdownEnumerator, &data); michael@0: } michael@0: michael@0: // Now it is safe to kill all the scopes. michael@0: KillDyingScopes(); michael@0: } michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static PLDHashOperator michael@0: WNProtoRemover(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: XPCWrappedNativeProtoMap* detachedMap = (XPCWrappedNativeProtoMap*)arg; michael@0: michael@0: XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*) michael@0: ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value; michael@0: michael@0: detachedMap->Add(proto); michael@0: michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: void michael@0: XPCWrappedNativeScope::RemoveWrappedNativeProtos() michael@0: { michael@0: mWrappedNativeProtoMap->Enumerate(WNProtoRemover, michael@0: GetRuntime()->GetDetachedWrappedNativeProtoMap()); michael@0: } michael@0: michael@0: JSObject * michael@0: XPCWrappedNativeScope::GetExpandoChain(HandleObject target) michael@0: { michael@0: MOZ_ASSERT(GetObjectScope(target) == this); michael@0: if (!mXrayExpandos.initialized()) michael@0: return nullptr; michael@0: return mXrayExpandos.lookup(target); michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeScope::SetExpandoChain(JSContext *cx, HandleObject target, michael@0: HandleObject chain) michael@0: { michael@0: MOZ_ASSERT(GetObjectScope(target) == this); michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx)); michael@0: MOZ_ASSERT_IF(chain, GetObjectScope(chain) == this); michael@0: if (!mXrayExpandos.initialized() && !mXrayExpandos.init(cx)) michael@0: return false; michael@0: return mXrayExpandos.put(cx, target, chain); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // static michael@0: void michael@0: XPCWrappedNativeScope::DebugDumpAllScopes(int16_t depth) michael@0: { michael@0: #ifdef DEBUG michael@0: depth-- ; michael@0: michael@0: // get scope count. michael@0: int count = 0; michael@0: XPCWrappedNativeScope* cur; michael@0: for (cur = gScopes; cur; cur = cur->mNext) michael@0: count++ ; michael@0: michael@0: XPC_LOG_ALWAYS(("chain of %d XPCWrappedNativeScope(s)", count)); michael@0: XPC_LOG_INDENT(); michael@0: XPC_LOG_ALWAYS(("gDyingScopes @ %x", gDyingScopes)); michael@0: if (depth) michael@0: for (cur = gScopes; cur; cur = cur->mNext) michael@0: cur->DebugDump(depth); michael@0: XPC_LOG_OUTDENT(); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static PLDHashOperator michael@0: WrappedNativeMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ((Native2WrappedNativeMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: static PLDHashOperator michael@0: WrappedNativeProtoMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: XPCWrappedNativeScope::DebugDump(int16_t depth) michael@0: { michael@0: #ifdef DEBUG michael@0: depth-- ; michael@0: XPC_LOG_ALWAYS(("XPCWrappedNativeScope @ %x", this)); michael@0: XPC_LOG_INDENT(); michael@0: XPC_LOG_ALWAYS(("mNext @ %x", mNext)); michael@0: XPC_LOG_ALWAYS(("mComponents @ %x", mComponents.get())); michael@0: XPC_LOG_ALWAYS(("mGlobalJSObject @ %x", mGlobalJSObject.get())); michael@0: michael@0: XPC_LOG_ALWAYS(("mWrappedNativeMap @ %x with %d wrappers(s)", \ michael@0: mWrappedNativeMap, \ michael@0: mWrappedNativeMap ? mWrappedNativeMap->Count() : 0)); michael@0: // iterate contexts... michael@0: if (depth && mWrappedNativeMap && mWrappedNativeMap->Count()) { michael@0: XPC_LOG_INDENT(); michael@0: mWrappedNativeMap->Enumerate(WrappedNativeMapDumpEnumerator, &depth); michael@0: XPC_LOG_OUTDENT(); michael@0: } michael@0: michael@0: XPC_LOG_ALWAYS(("mWrappedNativeProtoMap @ %x with %d protos(s)", \ michael@0: mWrappedNativeProtoMap, \ michael@0: mWrappedNativeProtoMap ? mWrappedNativeProtoMap->Count() : 0)); michael@0: // iterate contexts... michael@0: if (depth && mWrappedNativeProtoMap && mWrappedNativeProtoMap->Count()) { michael@0: XPC_LOG_INDENT(); michael@0: mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth); michael@0: XPC_LOG_OUTDENT(); michael@0: } michael@0: XPC_LOG_OUTDENT(); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo) michael@0: { michael@0: for (XPCWrappedNativeScope *cur = gScopes; cur; cur = cur->mNext) michael@0: cur->AddSizeOfIncludingThis(scopeSizeInfo); michael@0: } michael@0: michael@0: void michael@0: XPCWrappedNativeScope::AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo) michael@0: { michael@0: scopeSizeInfo->mScopeAndMapSize += scopeSizeInfo->mMallocSizeOf(this); michael@0: scopeSizeInfo->mScopeAndMapSize += michael@0: mWrappedNativeMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf); michael@0: scopeSizeInfo->mScopeAndMapSize += michael@0: mWrappedNativeProtoMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf); michael@0: michael@0: if (dom::HasProtoAndIfaceCache(mGlobalJSObject)) { michael@0: dom::ProtoAndIfaceCache* cache = dom::GetProtoAndIfaceCache(mGlobalJSObject); michael@0: scopeSizeInfo->mProtoAndIfaceCacheSize += michael@0: cache->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf); michael@0: } michael@0: michael@0: // There are other XPCWrappedNativeScope members that could be measured; michael@0: // the above ones have been seen by DMD to be worth measuring. More stuff michael@0: // may be added later. michael@0: }