1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/XPCWrappedJS.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,618 @@ 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 that wraps JS objects to appear as XPCOM objects. */ 1.11 + 1.12 +#include "xpcprivate.h" 1.13 +#include "jsprf.h" 1.14 +#include "nsCCUncollectableMarker.h" 1.15 +#include "nsCxPusher.h" 1.16 +#include "nsContentUtils.h" 1.17 +#include "nsThreadUtils.h" 1.18 + 1.19 +using namespace mozilla; 1.20 + 1.21 +// NOTE: much of the fancy footwork is done in xpcstubs.cpp 1.22 + 1.23 + 1.24 +// nsXPCWrappedJS lifetime. 1.25 +// 1.26 +// An nsXPCWrappedJS is either rooting its JS object or is subject to finalization. 1.27 +// The subject-to-finalization state lets wrappers support 1.28 +// nsSupportsWeakReference in the case where the underlying JS object 1.29 +// is strongly owned, but the wrapper itself is only weakly owned. 1.30 +// 1.31 +// A wrapper is rooting its JS object whenever its refcount is greater than 1. In 1.32 +// this state, root wrappers are always added to the cycle collector graph. The 1.33 +// wrapper keeps around an extra refcount, added in the constructor, to support 1.34 +// the possibility of an eventual transition to the subject-to-finalization state. 1.35 +// This extra refcount is ignored by the cycle collector, which traverses the "self" 1.36 +// edge for this refcount. 1.37 +// 1.38 +// When the refcount of a rooting wrapper drops to 1, if there is no weak reference 1.39 +// to the wrapper (which can only happen for the root wrapper), it is immediately 1.40 +// Destroy()'d. Otherwise, it becomes subject to finalization. 1.41 +// 1.42 +// When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is 1.43 +// now owned exclusively by its JS object. Either a weak reference will be turned into 1.44 +// a strong ref which will bring its refcount up to 2 and change the wrapper back to 1.45 +// the rooting state, or it will stay alive until the JS object dies. If the JS object 1.46 +// dies, then when XPCJSRuntime::FinalizeCallback calls FindDyingJSObjects 1.47 +// it will find the wrapper and call Release() in it, destroying the wrapper. 1.48 +// Otherwise, the wrapper will stay alive, even if it no longer has a weak reference 1.49 +// to it. 1.50 +// 1.51 +// When the wrapper is subject to finalization, it is kept alive by an implicit reference 1.52 +// from the JS object which is invisible to the cycle collector, so the cycle collector 1.53 +// does not traverse any children of wrappers that are subject to finalization. This will 1.54 +// result in a leak if a wrapper in the non-rooting state has an aggregated native that 1.55 +// keeps alive the wrapper's JS object. See bug 947049. 1.56 + 1.57 + 1.58 +// If traversing wrappedJS wouldn't release it, nor cause any other objects to be 1.59 +// added to the graph, there is no need to add it to the graph at all. 1.60 +bool 1.61 +nsXPCWrappedJS::CanSkip() 1.62 +{ 1.63 + if (!nsCCUncollectableMarker::sGeneration) 1.64 + return false; 1.65 + 1.66 + if (IsSubjectToFinalization()) 1.67 + return true; 1.68 + 1.69 + // If this wrapper holds a gray object, need to trace it. 1.70 + JSObject *obj = GetJSObjectPreserveColor(); 1.71 + if (obj && xpc_IsGrayGCThing(obj)) 1.72 + return false; 1.73 + 1.74 + // For non-root wrappers, check if the root wrapper will be 1.75 + // added to the CC graph. 1.76 + if (!IsRootWrapper()) 1.77 + return mRoot->CanSkip(); 1.78 + 1.79 + // For the root wrapper, check if there is an aggregated 1.80 + // native object that will be added to the CC graph. 1.81 + if (!IsAggregatedToNative()) 1.82 + return true; 1.83 + 1.84 + nsISupports* agg = GetAggregatedNativeObject(); 1.85 + nsXPCOMCycleCollectionParticipant* cp = nullptr; 1.86 + CallQueryInterface(agg, &cp); 1.87 + nsISupports* canonical = nullptr; 1.88 + agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), 1.89 + reinterpret_cast<void**>(&canonical)); 1.90 + return cp && canonical && cp->CanSkipThis(canonical); 1.91 +} 1.92 + 1.93 +NS_IMETHODIMP 1.94 +NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse 1.95 + (void *p, nsCycleCollectionTraversalCallback &cb) 1.96 +{ 1.97 + nsISupports *s = static_cast<nsISupports*>(p); 1.98 + MOZ_ASSERT(CheckForRightISupports(s), "not the nsISupports pointer we expect"); 1.99 + nsXPCWrappedJS *tmp = Downcast(s); 1.100 + 1.101 + nsrefcnt refcnt = tmp->mRefCnt.get(); 1.102 + if (cb.WantDebugInfo()) { 1.103 + char name[72]; 1.104 + if (tmp->GetClass()) 1.105 + JS_snprintf(name, sizeof(name), "nsXPCWrappedJS (%s)", 1.106 + tmp->GetClass()->GetInterfaceName()); 1.107 + else 1.108 + JS_snprintf(name, sizeof(name), "nsXPCWrappedJS"); 1.109 + cb.DescribeRefCountedNode(refcnt, name); 1.110 + } else { 1.111 + NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt) 1.112 + } 1.113 + 1.114 + // A wrapper that is subject to finalization will only die when its JS object dies. 1.115 + if (tmp->IsSubjectToFinalization()) 1.116 + return NS_OK; 1.117 + 1.118 + // Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is 1.119 + // not subject to finalization alive. 1.120 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self"); 1.121 + cb.NoteXPCOMChild(s); 1.122 + 1.123 + if (tmp->IsValid()) { 1.124 + MOZ_ASSERT(refcnt > 1); 1.125 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj"); 1.126 + cb.NoteJSChild(tmp->GetJSObjectPreserveColor()); 1.127 + } 1.128 + 1.129 + if (tmp->IsRootWrapper()) { 1.130 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native"); 1.131 + cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject()); 1.132 + } else { 1.133 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root"); 1.134 + cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper())); 1.135 + } 1.136 + 1.137 + return NS_OK; 1.138 +} 1.139 + 1.140 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS) 1.141 + 1.142 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS) 1.143 + tmp->Unlink(); 1.144 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.145 + 1.146 +// XPCJSRuntime keeps a table of WJS, so we can remove them from 1.147 +// the purple buffer in between CCs. 1.148 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS) 1.149 + return true; 1.150 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 1.151 + 1.152 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS) 1.153 + return tmp->CanSkip(); 1.154 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 1.155 + 1.156 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS) 1.157 + return tmp->CanSkip(); 1.158 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 1.159 + 1.160 +NS_IMETHODIMP 1.161 +nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) 1.162 +{ 1.163 + MOZ_ASSERT(IsAggregatedToNative(), "bad AggregatedQueryInterface call"); 1.164 + 1.165 + if (!IsValid()) 1.166 + return NS_ERROR_UNEXPECTED; 1.167 + 1.168 + // Put this here rather that in DelegatedQueryInterface because it needs 1.169 + // to be in QueryInterface before the possible delegation to 'outer', but 1.170 + // we don't want to do this check twice in one call in the normal case: 1.171 + // once in QueryInterface and once in DelegatedQueryInterface. 1.172 + if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) { 1.173 + NS_ADDREF(this); 1.174 + *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this); 1.175 + return NS_OK; 1.176 + } 1.177 + 1.178 + return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr); 1.179 +} 1.180 + 1.181 +NS_IMETHODIMP 1.182 +nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr) 1.183 +{ 1.184 + if (nullptr == aInstancePtr) { 1.185 + NS_PRECONDITION(0, "null pointer"); 1.186 + return NS_ERROR_NULL_POINTER; 1.187 + } 1.188 + 1.189 + if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) { 1.190 + *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS); 1.191 + return NS_OK; 1.192 + } 1.193 + 1.194 + if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) { 1.195 + *aInstancePtr = 1.196 + NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); 1.197 + return NS_OK; 1.198 + } 1.199 + 1.200 + if (!IsValid()) 1.201 + return NS_ERROR_UNEXPECTED; 1.202 + 1.203 + // Always check for this first so that our 'outer' can get this interface 1.204 + // from us without recurring into a call to the outer's QI! 1.205 + if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) { 1.206 + NS_ADDREF(this); 1.207 + *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this); 1.208 + return NS_OK; 1.209 + } 1.210 + 1.211 + nsISupports* outer = GetAggregatedNativeObject(); 1.212 + if (outer) 1.213 + return outer->QueryInterface(aIID, aInstancePtr); 1.214 + 1.215 + // else... 1.216 + 1.217 + return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr); 1.218 +} 1.219 + 1.220 + 1.221 +// For a description of nsXPCWrappedJS lifetime and reference counting, see 1.222 +// the comment at the top of this file. 1.223 + 1.224 +MozExternalRefCountType 1.225 +nsXPCWrappedJS::AddRef(void) 1.226 +{ 1.227 + if (!MOZ_LIKELY(NS_IsMainThread())) 1.228 + MOZ_CRASH(); 1.229 + 1.230 + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); 1.231 + nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); 1.232 + nsrefcnt cnt = mRefCnt.incr(base); 1.233 + NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this)); 1.234 + 1.235 + if (2 == cnt && IsValid()) { 1.236 + GetJSObject(); // Unmark gray JSObject. 1.237 + mClass->GetRuntime()->AddWrappedJSRoot(this); 1.238 + } 1.239 + 1.240 + return cnt; 1.241 +} 1.242 + 1.243 +MozExternalRefCountType 1.244 +nsXPCWrappedJS::Release(void) 1.245 +{ 1.246 + if (!MOZ_LIKELY(NS_IsMainThread())) 1.247 + MOZ_CRASH(); 1.248 + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); 1.249 + NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS); 1.250 + 1.251 + bool shouldDelete = false; 1.252 + nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); 1.253 + nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete); 1.254 + NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS"); 1.255 + 1.256 + if (0 == cnt) { 1.257 + if (MOZ_UNLIKELY(shouldDelete)) { 1.258 + mRefCnt.stabilizeForDeletion(); 1.259 + DeleteCycleCollectable(); 1.260 + } else { 1.261 + mRefCnt.incr(base); 1.262 + Destroy(); 1.263 + mRefCnt.decr(base); 1.264 + } 1.265 + } else if (1 == cnt) { 1.266 + if (IsValid()) 1.267 + RemoveFromRootSet(); 1.268 + 1.269 + // If we are not a root wrapper being used from a weak reference, 1.270 + // then the extra ref is not needed and we can let outselves be 1.271 + // deleted. 1.272 + if (!HasWeakReferences()) 1.273 + return Release(); 1.274 + 1.275 + MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references"); 1.276 + } 1.277 + return cnt; 1.278 +} 1.279 + 1.280 +NS_IMETHODIMP_(void) 1.281 +nsXPCWrappedJS::DeleteCycleCollectable(void) 1.282 +{ 1.283 + delete this; 1.284 +} 1.285 + 1.286 +void 1.287 +nsXPCWrappedJS::TraceJS(JSTracer* trc) 1.288 +{ 1.289 + MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced"); 1.290 + trc->setTracingDetails(GetTraceName, this, 0); 1.291 + JS_CallHeapObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj"); 1.292 +} 1.293 + 1.294 +// static 1.295 +void 1.296 +nsXPCWrappedJS::GetTraceName(JSTracer* trc, char *buf, size_t bufsize) 1.297 +{ 1.298 + const nsXPCWrappedJS* self = static_cast<const nsXPCWrappedJS*> 1.299 + (trc->debugPrintArg()); 1.300 + JS_snprintf(buf, bufsize, "nsXPCWrappedJS[%s,0x%p:0x%p].mJSObj", 1.301 + self->GetClass()->GetInterfaceName(), self, self->mXPTCStub); 1.302 +} 1.303 + 1.304 +NS_IMETHODIMP 1.305 +nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr) 1.306 +{ 1.307 + if (!IsRootWrapper()) 1.308 + return mRoot->GetWeakReference(aInstancePtr); 1.309 + 1.310 + return nsSupportsWeakReference::GetWeakReference(aInstancePtr); 1.311 +} 1.312 + 1.313 +JSObject* 1.314 +nsXPCWrappedJS::GetJSObject() 1.315 +{ 1.316 + if (mJSObj) { 1.317 + JS::ExposeObjectToActiveJS(mJSObj); 1.318 + } 1.319 + return mJSObj; 1.320 +} 1.321 + 1.322 +// static 1.323 +nsresult 1.324 +nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj, 1.325 + REFNSIID aIID, 1.326 + nsXPCWrappedJS** wrapperResult) 1.327 +{ 1.328 + // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread. 1.329 + if (!MOZ_LIKELY(NS_IsMainThread())) 1.330 + MOZ_CRASH(); 1.331 + 1.332 + AutoJSContext cx; 1.333 + XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); 1.334 + JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); 1.335 + if (!map) { 1.336 + MOZ_ASSERT(map,"bad map"); 1.337 + return NS_ERROR_FAILURE; 1.338 + } 1.339 + 1.340 + nsRefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID); 1.341 + if (!clasp) 1.342 + return NS_ERROR_FAILURE; 1.343 + 1.344 + JS::RootedObject rootJSObj(cx, clasp->GetRootJSObject(cx, jsObj)); 1.345 + if (!rootJSObj) 1.346 + return NS_ERROR_FAILURE; 1.347 + 1.348 + nsRefPtr<nsXPCWrappedJS> root = map->Find(rootJSObj); 1.349 + if (root) { 1.350 + nsRefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID); 1.351 + if (wrapper) { 1.352 + wrapper.forget(wrapperResult); 1.353 + return NS_OK; 1.354 + } 1.355 + } else if (rootJSObj != jsObj) { 1.356 + 1.357 + // Make a new root wrapper, because there is no existing 1.358 + // root wrapper, and the wrapper we are trying to make isn't 1.359 + // a root. 1.360 + nsRefPtr<nsXPCWrappedJSClass> rootClasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports)); 1.361 + if (!rootClasp) 1.362 + return NS_ERROR_FAILURE; 1.363 + 1.364 + root = new nsXPCWrappedJS(cx, rootJSObj, rootClasp, nullptr); 1.365 + } 1.366 + 1.367 + nsRefPtr<nsXPCWrappedJS> wrapper = new nsXPCWrappedJS(cx, jsObj, clasp, root); 1.368 + wrapper.forget(wrapperResult); 1.369 + return NS_OK; 1.370 +} 1.371 + 1.372 +nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, 1.373 + JSObject* aJSObj, 1.374 + nsXPCWrappedJSClass* aClass, 1.375 + nsXPCWrappedJS* root) 1.376 + : mJSObj(aJSObj), 1.377 + mClass(aClass), 1.378 + mRoot(root ? root : MOZ_THIS_IN_INITIALIZER_LIST()), 1.379 + mNext(nullptr) 1.380 +{ 1.381 + InitStub(GetClass()->GetIID()); 1.382 + 1.383 + // There is an extra AddRef to support weak references to wrappers 1.384 + // that are subject to finalization. See the top of the file for more 1.385 + // details. 1.386 + NS_ADDREF_THIS(); 1.387 + 1.388 + if (IsRootWrapper()) { 1.389 + nsXPConnect::GetRuntimeInstance()->GetWrappedJSMap()->Add(cx, this); 1.390 + } else { 1.391 + NS_ADDREF(mRoot); 1.392 + mNext = mRoot->mNext; 1.393 + mRoot->mNext = this; 1.394 + } 1.395 +} 1.396 + 1.397 +nsXPCWrappedJS::~nsXPCWrappedJS() 1.398 +{ 1.399 + Destroy(); 1.400 +} 1.401 + 1.402 +void 1.403 +nsXPCWrappedJS::Destroy() 1.404 +{ 1.405 + MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion"); 1.406 + 1.407 + if (IsRootWrapper()) { 1.408 + XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); 1.409 + JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); 1.410 + if (map) 1.411 + map->Remove(this); 1.412 + } 1.413 + Unlink(); 1.414 +} 1.415 + 1.416 +void 1.417 +nsXPCWrappedJS::Unlink() 1.418 +{ 1.419 + if (IsValid()) { 1.420 + XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); 1.421 + if (rt) { 1.422 + if (IsRootWrapper()) { 1.423 + JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); 1.424 + if (map) 1.425 + map->Remove(this); 1.426 + } 1.427 + 1.428 + if (mRefCnt > 1) 1.429 + RemoveFromRootSet(); 1.430 + } 1.431 + 1.432 + mJSObj = nullptr; 1.433 + } 1.434 + 1.435 + if (IsRootWrapper()) { 1.436 + ClearWeakReferences(); 1.437 + } else if (mRoot) { 1.438 + // unlink this wrapper 1.439 + nsXPCWrappedJS* cur = mRoot; 1.440 + while (1) { 1.441 + if (cur->mNext == this) { 1.442 + cur->mNext = mNext; 1.443 + break; 1.444 + } 1.445 + cur = cur->mNext; 1.446 + MOZ_ASSERT(cur, "failed to find wrapper in its own chain"); 1.447 + } 1.448 + // let the root go 1.449 + NS_RELEASE(mRoot); 1.450 + } 1.451 + 1.452 + mClass = nullptr; 1.453 + if (mOuter) { 1.454 + XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); 1.455 + if (rt->GCIsRunning()) { 1.456 + nsContentUtils::DeferredFinalize(mOuter.forget().take()); 1.457 + } else { 1.458 + mOuter = nullptr; 1.459 + } 1.460 + } 1.461 +} 1.462 + 1.463 +nsXPCWrappedJS* 1.464 +nsXPCWrappedJS::Find(REFNSIID aIID) 1.465 +{ 1.466 + if (aIID.Equals(NS_GET_IID(nsISupports))) 1.467 + return mRoot; 1.468 + 1.469 + for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) { 1.470 + if (aIID.Equals(cur->GetIID())) 1.471 + return cur; 1.472 + } 1.473 + 1.474 + return nullptr; 1.475 +} 1.476 + 1.477 +// check if asking for an interface that some wrapper in the chain inherits from 1.478 +nsXPCWrappedJS* 1.479 +nsXPCWrappedJS::FindInherited(REFNSIID aIID) 1.480 +{ 1.481 + MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence"); 1.482 + 1.483 + for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) { 1.484 + bool found; 1.485 + if (NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()-> 1.486 + HasAncestor(&aIID, &found)) && found) 1.487 + return cur; 1.488 + } 1.489 + 1.490 + return nullptr; 1.491 +} 1.492 + 1.493 +NS_IMETHODIMP 1.494 +nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** infoResult) 1.495 +{ 1.496 + MOZ_ASSERT(GetClass(), "wrapper without class"); 1.497 + MOZ_ASSERT(GetClass()->GetInterfaceInfo(), "wrapper class without interface"); 1.498 + 1.499 + // Since failing to get this info will crash some platforms(!), we keep 1.500 + // mClass valid at shutdown time. 1.501 + 1.502 + nsCOMPtr<nsIInterfaceInfo> info = GetClass()->GetInterfaceInfo(); 1.503 + if (!info) 1.504 + return NS_ERROR_UNEXPECTED; 1.505 + info.forget(infoResult); 1.506 + return NS_OK; 1.507 +} 1.508 + 1.509 +NS_IMETHODIMP 1.510 +nsXPCWrappedJS::CallMethod(uint16_t methodIndex, 1.511 + const XPTMethodDescriptor* info, 1.512 + nsXPTCMiniVariant* params) 1.513 +{ 1.514 + // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread. 1.515 + if (!MOZ_LIKELY(NS_IsMainThread())) 1.516 + MOZ_CRASH(); 1.517 + 1.518 + if (!IsValid()) 1.519 + return NS_ERROR_UNEXPECTED; 1.520 + return GetClass()->CallMethod(this, methodIndex, info, params); 1.521 +} 1.522 + 1.523 +NS_IMETHODIMP 1.524 +nsXPCWrappedJS::GetInterfaceIID(nsIID** iid) 1.525 +{ 1.526 + NS_PRECONDITION(iid, "bad param"); 1.527 + 1.528 + *iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID)); 1.529 + return *iid ? NS_OK : NS_ERROR_UNEXPECTED; 1.530 +} 1.531 + 1.532 +void 1.533 +nsXPCWrappedJS::SystemIsBeingShutDown() 1.534 +{ 1.535 + // XXX It turns out that it is better to leak here then to do any Releases 1.536 + // and have them propagate into all sorts of mischief as the system is being 1.537 + // shutdown. This was learned the hard way :( 1.538 + 1.539 + // mJSObj == nullptr is used to indicate that the wrapper is no longer valid 1.540 + // and that calls should fail without trying to use any of the 1.541 + // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer. 1.542 + 1.543 + // NOTE: that mClass is retained so that GetInterfaceInfo can continue to 1.544 + // work (and avoid crashing some platforms). 1.545 + 1.546 + // Use of unsafeGet() is to avoid triggering post barriers in shutdown, as 1.547 + // this will access the chunk containing mJSObj, which may have been freed 1.548 + // at this point. 1.549 + *mJSObj.unsafeGet() = nullptr; 1.550 + 1.551 + // Notify other wrappers in the chain. 1.552 + if (mNext) 1.553 + mNext->SystemIsBeingShutDown(); 1.554 +} 1.555 + 1.556 +/***************************************************************************/ 1.557 + 1.558 +/* readonly attribute nsISimpleEnumerator enumerator; */ 1.559 +NS_IMETHODIMP 1.560 +nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate) 1.561 +{ 1.562 + AutoJSContext cx; 1.563 + XPCCallContext ccx(NATIVE_CALLER, cx); 1.564 + if (!ccx.IsValid()) 1.565 + return NS_ERROR_UNEXPECTED; 1.566 + 1.567 + return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(), 1.568 + aEnumerate); 1.569 +} 1.570 + 1.571 +/* nsIVariant getProperty (in AString name); */ 1.572 +NS_IMETHODIMP 1.573 +nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant **_retval) 1.574 +{ 1.575 + AutoJSContext cx; 1.576 + XPCCallContext ccx(NATIVE_CALLER, cx); 1.577 + if (!ccx.IsValid()) 1.578 + return NS_ERROR_UNEXPECTED; 1.579 + 1.580 + return nsXPCWrappedJSClass:: 1.581 + GetNamedPropertyAsVariant(ccx, GetJSObject(), name, _retval); 1.582 +} 1.583 + 1.584 +/***************************************************************************/ 1.585 + 1.586 +NS_IMETHODIMP 1.587 +nsXPCWrappedJS::DebugDump(int16_t depth) 1.588 +{ 1.589 +#ifdef DEBUG 1.590 + XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get())); 1.591 + XPC_LOG_INDENT(); 1.592 + 1.593 + XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \ 1.594 + IsRootWrapper() ? "ROOT":"non-root", mJSObj.get())); 1.595 + char* name; 1.596 + GetClass()->GetInterfaceInfo()->GetName(&name); 1.597 + XPC_LOG_ALWAYS(("interface name is %s", name)); 1.598 + if (name) 1.599 + nsMemory::Free(name); 1.600 + char * iid = GetClass()->GetIID().ToString(); 1.601 + XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid")); 1.602 + if (iid) 1.603 + NS_Free(iid); 1.604 + XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x", mClass.get())); 1.605 + 1.606 + if (!IsRootWrapper()) 1.607 + XPC_LOG_OUTDENT(); 1.608 + if (mNext) { 1.609 + if (IsRootWrapper()) { 1.610 + XPC_LOG_ALWAYS(("Additional wrappers for this object...")); 1.611 + XPC_LOG_INDENT(); 1.612 + } 1.613 + mNext->DebugDump(depth); 1.614 + if (IsRootWrapper()) 1.615 + XPC_LOG_OUTDENT(); 1.616 + } 1.617 + if (IsRootWrapper()) 1.618 + XPC_LOG_OUTDENT(); 1.619 +#endif 1.620 + return NS_OK; 1.621 +}