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 that wraps JS objects to appear as XPCOM objects. */ michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "jsprf.h" michael@0: #include "nsCCUncollectableMarker.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // NOTE: much of the fancy footwork is done in xpcstubs.cpp michael@0: michael@0: michael@0: // nsXPCWrappedJS lifetime. michael@0: // michael@0: // An nsXPCWrappedJS is either rooting its JS object or is subject to finalization. michael@0: // The subject-to-finalization state lets wrappers support michael@0: // nsSupportsWeakReference in the case where the underlying JS object michael@0: // is strongly owned, but the wrapper itself is only weakly owned. michael@0: // michael@0: // A wrapper is rooting its JS object whenever its refcount is greater than 1. In michael@0: // this state, root wrappers are always added to the cycle collector graph. The michael@0: // wrapper keeps around an extra refcount, added in the constructor, to support michael@0: // the possibility of an eventual transition to the subject-to-finalization state. michael@0: // This extra refcount is ignored by the cycle collector, which traverses the "self" michael@0: // edge for this refcount. michael@0: // michael@0: // When the refcount of a rooting wrapper drops to 1, if there is no weak reference michael@0: // to the wrapper (which can only happen for the root wrapper), it is immediately michael@0: // Destroy()'d. Otherwise, it becomes subject to finalization. michael@0: // michael@0: // When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is michael@0: // now owned exclusively by its JS object. Either a weak reference will be turned into michael@0: // a strong ref which will bring its refcount up to 2 and change the wrapper back to michael@0: // the rooting state, or it will stay alive until the JS object dies. If the JS object michael@0: // dies, then when XPCJSRuntime::FinalizeCallback calls FindDyingJSObjects michael@0: // it will find the wrapper and call Release() in it, destroying the wrapper. michael@0: // Otherwise, the wrapper will stay alive, even if it no longer has a weak reference michael@0: // to it. michael@0: // michael@0: // When the wrapper is subject to finalization, it is kept alive by an implicit reference michael@0: // from the JS object which is invisible to the cycle collector, so the cycle collector michael@0: // does not traverse any children of wrappers that are subject to finalization. This will michael@0: // result in a leak if a wrapper in the non-rooting state has an aggregated native that michael@0: // keeps alive the wrapper's JS object. See bug 947049. michael@0: michael@0: michael@0: // If traversing wrappedJS wouldn't release it, nor cause any other objects to be michael@0: // added to the graph, there is no need to add it to the graph at all. michael@0: bool michael@0: nsXPCWrappedJS::CanSkip() michael@0: { michael@0: if (!nsCCUncollectableMarker::sGeneration) michael@0: return false; michael@0: michael@0: if (IsSubjectToFinalization()) michael@0: return true; michael@0: michael@0: // If this wrapper holds a gray object, need to trace it. michael@0: JSObject *obj = GetJSObjectPreserveColor(); michael@0: if (obj && xpc_IsGrayGCThing(obj)) michael@0: return false; michael@0: michael@0: // For non-root wrappers, check if the root wrapper will be michael@0: // added to the CC graph. michael@0: if (!IsRootWrapper()) michael@0: return mRoot->CanSkip(); michael@0: michael@0: // For the root wrapper, check if there is an aggregated michael@0: // native object that will be added to the CC graph. michael@0: if (!IsAggregatedToNative()) michael@0: return true; michael@0: michael@0: nsISupports* agg = GetAggregatedNativeObject(); michael@0: nsXPCOMCycleCollectionParticipant* cp = nullptr; michael@0: CallQueryInterface(agg, &cp); michael@0: nsISupports* canonical = nullptr; michael@0: agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), michael@0: reinterpret_cast(&canonical)); michael@0: return cp && canonical && cp->CanSkipThis(canonical); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse michael@0: (void *p, nsCycleCollectionTraversalCallback &cb) michael@0: { michael@0: nsISupports *s = static_cast(p); michael@0: MOZ_ASSERT(CheckForRightISupports(s), "not the nsISupports pointer we expect"); michael@0: nsXPCWrappedJS *tmp = Downcast(s); michael@0: michael@0: nsrefcnt refcnt = tmp->mRefCnt.get(); michael@0: if (cb.WantDebugInfo()) { michael@0: char name[72]; michael@0: if (tmp->GetClass()) michael@0: JS_snprintf(name, sizeof(name), "nsXPCWrappedJS (%s)", michael@0: tmp->GetClass()->GetInterfaceName()); michael@0: else michael@0: JS_snprintf(name, sizeof(name), "nsXPCWrappedJS"); michael@0: cb.DescribeRefCountedNode(refcnt, name); michael@0: } else { michael@0: NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt) michael@0: } michael@0: michael@0: // A wrapper that is subject to finalization will only die when its JS object dies. michael@0: if (tmp->IsSubjectToFinalization()) michael@0: return NS_OK; michael@0: michael@0: // Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is michael@0: // not subject to finalization alive. michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self"); michael@0: cb.NoteXPCOMChild(s); michael@0: michael@0: if (tmp->IsValid()) { michael@0: MOZ_ASSERT(refcnt > 1); michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj"); michael@0: cb.NoteJSChild(tmp->GetJSObjectPreserveColor()); michael@0: } michael@0: michael@0: if (tmp->IsRootWrapper()) { michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native"); michael@0: cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject()); michael@0: } else { michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root"); michael@0: cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper())); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS) michael@0: tmp->Unlink(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: // XPCJSRuntime keeps a table of WJS, so we can remove them from michael@0: // the purple buffer in between CCs. michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS) michael@0: return true; michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS) michael@0: return tmp->CanSkip(); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS) michael@0: return tmp->CanSkip(); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) michael@0: { michael@0: MOZ_ASSERT(IsAggregatedToNative(), "bad AggregatedQueryInterface call"); michael@0: michael@0: if (!IsValid()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Put this here rather that in DelegatedQueryInterface because it needs michael@0: // to be in QueryInterface before the possible delegation to 'outer', but michael@0: // we don't want to do this check twice in one call in the normal case: michael@0: // once in QueryInterface and once in DelegatedQueryInterface. michael@0: if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) { michael@0: NS_ADDREF(this); michael@0: *aInstancePtr = (void*) static_cast(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr) michael@0: { michael@0: if (nullptr == aInstancePtr) { michael@0: NS_PRECONDITION(0, "null pointer"); michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: michael@0: if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) { michael@0: *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) { michael@0: *aInstancePtr = michael@0: NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!IsValid()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Always check for this first so that our 'outer' can get this interface michael@0: // from us without recurring into a call to the outer's QI! michael@0: if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) { michael@0: NS_ADDREF(this); michael@0: *aInstancePtr = (void*) static_cast(this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsISupports* outer = GetAggregatedNativeObject(); michael@0: if (outer) michael@0: return outer->QueryInterface(aIID, aInstancePtr); michael@0: michael@0: // else... michael@0: michael@0: return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr); michael@0: } michael@0: michael@0: michael@0: // For a description of nsXPCWrappedJS lifetime and reference counting, see michael@0: // the comment at the top of this file. michael@0: michael@0: MozExternalRefCountType michael@0: nsXPCWrappedJS::AddRef(void) michael@0: { michael@0: if (!MOZ_LIKELY(NS_IsMainThread())) michael@0: MOZ_CRASH(); michael@0: michael@0: MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); michael@0: nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); michael@0: nsrefcnt cnt = mRefCnt.incr(base); michael@0: NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this)); michael@0: michael@0: if (2 == cnt && IsValid()) { michael@0: GetJSObject(); // Unmark gray JSObject. michael@0: mClass->GetRuntime()->AddWrappedJSRoot(this); michael@0: } michael@0: michael@0: return cnt; michael@0: } michael@0: michael@0: MozExternalRefCountType michael@0: nsXPCWrappedJS::Release(void) michael@0: { michael@0: if (!MOZ_LIKELY(NS_IsMainThread())) michael@0: MOZ_CRASH(); michael@0: MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); michael@0: NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS); michael@0: michael@0: bool shouldDelete = false; michael@0: nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); michael@0: nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete); michael@0: NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS"); michael@0: michael@0: if (0 == cnt) { michael@0: if (MOZ_UNLIKELY(shouldDelete)) { michael@0: mRefCnt.stabilizeForDeletion(); michael@0: DeleteCycleCollectable(); michael@0: } else { michael@0: mRefCnt.incr(base); michael@0: Destroy(); michael@0: mRefCnt.decr(base); michael@0: } michael@0: } else if (1 == cnt) { michael@0: if (IsValid()) michael@0: RemoveFromRootSet(); michael@0: michael@0: // If we are not a root wrapper being used from a weak reference, michael@0: // then the extra ref is not needed and we can let outselves be michael@0: // deleted. michael@0: if (!HasWeakReferences()) michael@0: return Release(); michael@0: michael@0: MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references"); michael@0: } michael@0: return cnt; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsXPCWrappedJS::DeleteCycleCollectable(void) michael@0: { michael@0: delete this; michael@0: } michael@0: michael@0: void michael@0: nsXPCWrappedJS::TraceJS(JSTracer* trc) michael@0: { michael@0: MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced"); michael@0: trc->setTracingDetails(GetTraceName, this, 0); michael@0: JS_CallHeapObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj"); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsXPCWrappedJS::GetTraceName(JSTracer* trc, char *buf, size_t bufsize) michael@0: { michael@0: const nsXPCWrappedJS* self = static_cast michael@0: (trc->debugPrintArg()); michael@0: JS_snprintf(buf, bufsize, "nsXPCWrappedJS[%s,0x%p:0x%p].mJSObj", michael@0: self->GetClass()->GetInterfaceName(), self, self->mXPTCStub); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr) michael@0: { michael@0: if (!IsRootWrapper()) michael@0: return mRoot->GetWeakReference(aInstancePtr); michael@0: michael@0: return nsSupportsWeakReference::GetWeakReference(aInstancePtr); michael@0: } michael@0: michael@0: JSObject* michael@0: nsXPCWrappedJS::GetJSObject() michael@0: { michael@0: if (mJSObj) { michael@0: JS::ExposeObjectToActiveJS(mJSObj); michael@0: } michael@0: return mJSObj; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj, michael@0: REFNSIID aIID, michael@0: nsXPCWrappedJS** wrapperResult) michael@0: { michael@0: // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread. michael@0: if (!MOZ_LIKELY(NS_IsMainThread())) michael@0: MOZ_CRASH(); michael@0: michael@0: AutoJSContext cx; michael@0: XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); michael@0: JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); michael@0: if (!map) { michael@0: MOZ_ASSERT(map,"bad map"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID); michael@0: if (!clasp) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: JS::RootedObject rootJSObj(cx, clasp->GetRootJSObject(cx, jsObj)); michael@0: if (!rootJSObj) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRefPtr root = map->Find(rootJSObj); michael@0: if (root) { michael@0: nsRefPtr wrapper = root->FindOrFindInherited(aIID); michael@0: if (wrapper) { michael@0: wrapper.forget(wrapperResult); michael@0: return NS_OK; michael@0: } michael@0: } else if (rootJSObj != jsObj) { michael@0: michael@0: // Make a new root wrapper, because there is no existing michael@0: // root wrapper, and the wrapper we are trying to make isn't michael@0: // a root. michael@0: nsRefPtr rootClasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports)); michael@0: if (!rootClasp) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: root = new nsXPCWrappedJS(cx, rootJSObj, rootClasp, nullptr); michael@0: } michael@0: michael@0: nsRefPtr wrapper = new nsXPCWrappedJS(cx, jsObj, clasp, root); michael@0: wrapper.forget(wrapperResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, michael@0: JSObject* aJSObj, michael@0: nsXPCWrappedJSClass* aClass, michael@0: nsXPCWrappedJS* root) michael@0: : mJSObj(aJSObj), michael@0: mClass(aClass), michael@0: mRoot(root ? root : MOZ_THIS_IN_INITIALIZER_LIST()), michael@0: mNext(nullptr) michael@0: { michael@0: InitStub(GetClass()->GetIID()); michael@0: michael@0: // There is an extra AddRef to support weak references to wrappers michael@0: // that are subject to finalization. See the top of the file for more michael@0: // details. michael@0: NS_ADDREF_THIS(); michael@0: michael@0: if (IsRootWrapper()) { michael@0: nsXPConnect::GetRuntimeInstance()->GetWrappedJSMap()->Add(cx, this); michael@0: } else { michael@0: NS_ADDREF(mRoot); michael@0: mNext = mRoot->mNext; michael@0: mRoot->mNext = this; michael@0: } michael@0: } michael@0: michael@0: nsXPCWrappedJS::~nsXPCWrappedJS() michael@0: { michael@0: Destroy(); michael@0: } michael@0: michael@0: void michael@0: nsXPCWrappedJS::Destroy() michael@0: { michael@0: MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion"); michael@0: michael@0: if (IsRootWrapper()) { michael@0: XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); michael@0: JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); michael@0: if (map) michael@0: map->Remove(this); michael@0: } michael@0: Unlink(); michael@0: } michael@0: michael@0: void michael@0: nsXPCWrappedJS::Unlink() michael@0: { michael@0: if (IsValid()) { michael@0: XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); michael@0: if (rt) { michael@0: if (IsRootWrapper()) { michael@0: JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); michael@0: if (map) michael@0: map->Remove(this); michael@0: } michael@0: michael@0: if (mRefCnt > 1) michael@0: RemoveFromRootSet(); michael@0: } michael@0: michael@0: mJSObj = nullptr; michael@0: } michael@0: michael@0: if (IsRootWrapper()) { michael@0: ClearWeakReferences(); michael@0: } else if (mRoot) { michael@0: // unlink this wrapper michael@0: nsXPCWrappedJS* cur = mRoot; michael@0: while (1) { michael@0: if (cur->mNext == this) { michael@0: cur->mNext = mNext; michael@0: break; michael@0: } michael@0: cur = cur->mNext; michael@0: MOZ_ASSERT(cur, "failed to find wrapper in its own chain"); michael@0: } michael@0: // let the root go michael@0: NS_RELEASE(mRoot); michael@0: } michael@0: michael@0: mClass = nullptr; michael@0: if (mOuter) { michael@0: XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); michael@0: if (rt->GCIsRunning()) { michael@0: nsContentUtils::DeferredFinalize(mOuter.forget().take()); michael@0: } else { michael@0: mOuter = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsXPCWrappedJS* michael@0: nsXPCWrappedJS::Find(REFNSIID aIID) michael@0: { michael@0: if (aIID.Equals(NS_GET_IID(nsISupports))) michael@0: return mRoot; michael@0: michael@0: for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) { michael@0: if (aIID.Equals(cur->GetIID())) michael@0: return cur; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // check if asking for an interface that some wrapper in the chain inherits from michael@0: nsXPCWrappedJS* michael@0: nsXPCWrappedJS::FindInherited(REFNSIID aIID) michael@0: { michael@0: MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence"); michael@0: michael@0: for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) { michael@0: bool found; michael@0: if (NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()-> michael@0: HasAncestor(&aIID, &found)) && found) michael@0: return cur; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** infoResult) michael@0: { michael@0: MOZ_ASSERT(GetClass(), "wrapper without class"); michael@0: MOZ_ASSERT(GetClass()->GetInterfaceInfo(), "wrapper class without interface"); michael@0: michael@0: // Since failing to get this info will crash some platforms(!), we keep michael@0: // mClass valid at shutdown time. michael@0: michael@0: nsCOMPtr info = GetClass()->GetInterfaceInfo(); michael@0: if (!info) michael@0: return NS_ERROR_UNEXPECTED; michael@0: info.forget(infoResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::CallMethod(uint16_t methodIndex, michael@0: const XPTMethodDescriptor* info, michael@0: nsXPTCMiniVariant* params) michael@0: { michael@0: // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread. michael@0: if (!MOZ_LIKELY(NS_IsMainThread())) michael@0: MOZ_CRASH(); michael@0: michael@0: if (!IsValid()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: return GetClass()->CallMethod(this, methodIndex, info, params); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::GetInterfaceIID(nsIID** iid) michael@0: { michael@0: NS_PRECONDITION(iid, "bad param"); michael@0: michael@0: *iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID)); michael@0: return *iid ? NS_OK : NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: void michael@0: nsXPCWrappedJS::SystemIsBeingShutDown() michael@0: { michael@0: // XXX It turns out that it is better to leak here then to do any Releases michael@0: // and have them propagate into all sorts of mischief as the system is being michael@0: // shutdown. This was learned the hard way :( michael@0: michael@0: // mJSObj == nullptr is used to indicate that the wrapper is no longer valid michael@0: // and that calls should fail without trying to use any of the michael@0: // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer. michael@0: michael@0: // NOTE: that mClass is retained so that GetInterfaceInfo can continue to michael@0: // work (and avoid crashing some platforms). michael@0: michael@0: // Use of unsafeGet() is to avoid triggering post barriers in shutdown, as michael@0: // this will access the chunk containing mJSObj, which may have been freed michael@0: // at this point. michael@0: *mJSObj.unsafeGet() = nullptr; michael@0: michael@0: // Notify other wrappers in the chain. michael@0: if (mNext) michael@0: mNext->SystemIsBeingShutDown(); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: /* readonly attribute nsISimpleEnumerator enumerator; */ michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate) michael@0: { michael@0: AutoJSContext cx; michael@0: XPCCallContext ccx(NATIVE_CALLER, cx); michael@0: if (!ccx.IsValid()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(), michael@0: aEnumerate); michael@0: } michael@0: michael@0: /* nsIVariant getProperty (in AString name); */ michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant **_retval) michael@0: { michael@0: AutoJSContext cx; michael@0: XPCCallContext ccx(NATIVE_CALLER, cx); michael@0: if (!ccx.IsValid()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return nsXPCWrappedJSClass:: michael@0: GetNamedPropertyAsVariant(ccx, GetJSObject(), name, _retval); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPCWrappedJS::DebugDump(int16_t depth) michael@0: { michael@0: #ifdef DEBUG michael@0: XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get())); michael@0: XPC_LOG_INDENT(); michael@0: michael@0: XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \ michael@0: IsRootWrapper() ? "ROOT":"non-root", mJSObj.get())); michael@0: char* name; michael@0: GetClass()->GetInterfaceInfo()->GetName(&name); michael@0: XPC_LOG_ALWAYS(("interface name is %s", name)); michael@0: if (name) michael@0: nsMemory::Free(name); michael@0: char * iid = GetClass()->GetIID().ToString(); michael@0: XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid")); michael@0: if (iid) michael@0: NS_Free(iid); michael@0: XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x", mClass.get())); michael@0: michael@0: if (!IsRootWrapper()) michael@0: XPC_LOG_OUTDENT(); michael@0: if (mNext) { michael@0: if (IsRootWrapper()) { michael@0: XPC_LOG_ALWAYS(("Additional wrappers for this object...")); michael@0: XPC_LOG_INDENT(); michael@0: } michael@0: mNext->DebugDump(depth); michael@0: if (IsRootWrapper()) michael@0: XPC_LOG_OUTDENT(); michael@0: } michael@0: if (IsRootWrapper()) michael@0: XPC_LOG_OUTDENT(); michael@0: #endif michael@0: return NS_OK; michael@0: }