js/xpconnect/src/XPCWrappedJS.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* Class that wraps JS objects to appear as XPCOM objects. */
michael@0 8
michael@0 9 #include "xpcprivate.h"
michael@0 10 #include "jsprf.h"
michael@0 11 #include "nsCCUncollectableMarker.h"
michael@0 12 #include "nsCxPusher.h"
michael@0 13 #include "nsContentUtils.h"
michael@0 14 #include "nsThreadUtils.h"
michael@0 15
michael@0 16 using namespace mozilla;
michael@0 17
michael@0 18 // NOTE: much of the fancy footwork is done in xpcstubs.cpp
michael@0 19
michael@0 20
michael@0 21 // nsXPCWrappedJS lifetime.
michael@0 22 //
michael@0 23 // An nsXPCWrappedJS is either rooting its JS object or is subject to finalization.
michael@0 24 // The subject-to-finalization state lets wrappers support
michael@0 25 // nsSupportsWeakReference in the case where the underlying JS object
michael@0 26 // is strongly owned, but the wrapper itself is only weakly owned.
michael@0 27 //
michael@0 28 // A wrapper is rooting its JS object whenever its refcount is greater than 1. In
michael@0 29 // this state, root wrappers are always added to the cycle collector graph. The
michael@0 30 // wrapper keeps around an extra refcount, added in the constructor, to support
michael@0 31 // the possibility of an eventual transition to the subject-to-finalization state.
michael@0 32 // This extra refcount is ignored by the cycle collector, which traverses the "self"
michael@0 33 // edge for this refcount.
michael@0 34 //
michael@0 35 // When the refcount of a rooting wrapper drops to 1, if there is no weak reference
michael@0 36 // to the wrapper (which can only happen for the root wrapper), it is immediately
michael@0 37 // Destroy()'d. Otherwise, it becomes subject to finalization.
michael@0 38 //
michael@0 39 // When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is
michael@0 40 // now owned exclusively by its JS object. Either a weak reference will be turned into
michael@0 41 // a strong ref which will bring its refcount up to 2 and change the wrapper back to
michael@0 42 // the rooting state, or it will stay alive until the JS object dies. If the JS object
michael@0 43 // dies, then when XPCJSRuntime::FinalizeCallback calls FindDyingJSObjects
michael@0 44 // it will find the wrapper and call Release() in it, destroying the wrapper.
michael@0 45 // Otherwise, the wrapper will stay alive, even if it no longer has a weak reference
michael@0 46 // to it.
michael@0 47 //
michael@0 48 // When the wrapper is subject to finalization, it is kept alive by an implicit reference
michael@0 49 // from the JS object which is invisible to the cycle collector, so the cycle collector
michael@0 50 // does not traverse any children of wrappers that are subject to finalization. This will
michael@0 51 // result in a leak if a wrapper in the non-rooting state has an aggregated native that
michael@0 52 // keeps alive the wrapper's JS object. See bug 947049.
michael@0 53
michael@0 54
michael@0 55 // If traversing wrappedJS wouldn't release it, nor cause any other objects to be
michael@0 56 // added to the graph, there is no need to add it to the graph at all.
michael@0 57 bool
michael@0 58 nsXPCWrappedJS::CanSkip()
michael@0 59 {
michael@0 60 if (!nsCCUncollectableMarker::sGeneration)
michael@0 61 return false;
michael@0 62
michael@0 63 if (IsSubjectToFinalization())
michael@0 64 return true;
michael@0 65
michael@0 66 // If this wrapper holds a gray object, need to trace it.
michael@0 67 JSObject *obj = GetJSObjectPreserveColor();
michael@0 68 if (obj && xpc_IsGrayGCThing(obj))
michael@0 69 return false;
michael@0 70
michael@0 71 // For non-root wrappers, check if the root wrapper will be
michael@0 72 // added to the CC graph.
michael@0 73 if (!IsRootWrapper())
michael@0 74 return mRoot->CanSkip();
michael@0 75
michael@0 76 // For the root wrapper, check if there is an aggregated
michael@0 77 // native object that will be added to the CC graph.
michael@0 78 if (!IsAggregatedToNative())
michael@0 79 return true;
michael@0 80
michael@0 81 nsISupports* agg = GetAggregatedNativeObject();
michael@0 82 nsXPCOMCycleCollectionParticipant* cp = nullptr;
michael@0 83 CallQueryInterface(agg, &cp);
michael@0 84 nsISupports* canonical = nullptr;
michael@0 85 agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
michael@0 86 reinterpret_cast<void**>(&canonical));
michael@0 87 return cp && canonical && cp->CanSkipThis(canonical);
michael@0 88 }
michael@0 89
michael@0 90 NS_IMETHODIMP
michael@0 91 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
michael@0 92 (void *p, nsCycleCollectionTraversalCallback &cb)
michael@0 93 {
michael@0 94 nsISupports *s = static_cast<nsISupports*>(p);
michael@0 95 MOZ_ASSERT(CheckForRightISupports(s), "not the nsISupports pointer we expect");
michael@0 96 nsXPCWrappedJS *tmp = Downcast(s);
michael@0 97
michael@0 98 nsrefcnt refcnt = tmp->mRefCnt.get();
michael@0 99 if (cb.WantDebugInfo()) {
michael@0 100 char name[72];
michael@0 101 if (tmp->GetClass())
michael@0 102 JS_snprintf(name, sizeof(name), "nsXPCWrappedJS (%s)",
michael@0 103 tmp->GetClass()->GetInterfaceName());
michael@0 104 else
michael@0 105 JS_snprintf(name, sizeof(name), "nsXPCWrappedJS");
michael@0 106 cb.DescribeRefCountedNode(refcnt, name);
michael@0 107 } else {
michael@0 108 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt)
michael@0 109 }
michael@0 110
michael@0 111 // A wrapper that is subject to finalization will only die when its JS object dies.
michael@0 112 if (tmp->IsSubjectToFinalization())
michael@0 113 return NS_OK;
michael@0 114
michael@0 115 // Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is
michael@0 116 // not subject to finalization alive.
michael@0 117 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
michael@0 118 cb.NoteXPCOMChild(s);
michael@0 119
michael@0 120 if (tmp->IsValid()) {
michael@0 121 MOZ_ASSERT(refcnt > 1);
michael@0 122 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
michael@0 123 cb.NoteJSChild(tmp->GetJSObjectPreserveColor());
michael@0 124 }
michael@0 125
michael@0 126 if (tmp->IsRootWrapper()) {
michael@0 127 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
michael@0 128 cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
michael@0 129 } else {
michael@0 130 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
michael@0 131 cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper()));
michael@0 132 }
michael@0 133
michael@0 134 return NS_OK;
michael@0 135 }
michael@0 136
michael@0 137 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
michael@0 138
michael@0 139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
michael@0 140 tmp->Unlink();
michael@0 141 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 142
michael@0 143 // XPCJSRuntime keeps a table of WJS, so we can remove them from
michael@0 144 // the purple buffer in between CCs.
michael@0 145 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS)
michael@0 146 return true;
michael@0 147 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
michael@0 148
michael@0 149 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS)
michael@0 150 return tmp->CanSkip();
michael@0 151 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
michael@0 152
michael@0 153 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS)
michael@0 154 return tmp->CanSkip();
michael@0 155 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
michael@0 156
michael@0 157 NS_IMETHODIMP
michael@0 158 nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
michael@0 159 {
michael@0 160 MOZ_ASSERT(IsAggregatedToNative(), "bad AggregatedQueryInterface call");
michael@0 161
michael@0 162 if (!IsValid())
michael@0 163 return NS_ERROR_UNEXPECTED;
michael@0 164
michael@0 165 // Put this here rather that in DelegatedQueryInterface because it needs
michael@0 166 // to be in QueryInterface before the possible delegation to 'outer', but
michael@0 167 // we don't want to do this check twice in one call in the normal case:
michael@0 168 // once in QueryInterface and once in DelegatedQueryInterface.
michael@0 169 if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
michael@0 170 NS_ADDREF(this);
michael@0 171 *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
michael@0 172 return NS_OK;
michael@0 173 }
michael@0 174
michael@0 175 return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
michael@0 176 }
michael@0 177
michael@0 178 NS_IMETHODIMP
michael@0 179 nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
michael@0 180 {
michael@0 181 if (nullptr == aInstancePtr) {
michael@0 182 NS_PRECONDITION(0, "null pointer");
michael@0 183 return NS_ERROR_NULL_POINTER;
michael@0 184 }
michael@0 185
michael@0 186 if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
michael@0 187 *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS);
michael@0 188 return NS_OK;
michael@0 189 }
michael@0 190
michael@0 191 if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) {
michael@0 192 *aInstancePtr =
michael@0 193 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
michael@0 194 return NS_OK;
michael@0 195 }
michael@0 196
michael@0 197 if (!IsValid())
michael@0 198 return NS_ERROR_UNEXPECTED;
michael@0 199
michael@0 200 // Always check for this first so that our 'outer' can get this interface
michael@0 201 // from us without recurring into a call to the outer's QI!
michael@0 202 if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
michael@0 203 NS_ADDREF(this);
michael@0 204 *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
michael@0 205 return NS_OK;
michael@0 206 }
michael@0 207
michael@0 208 nsISupports* outer = GetAggregatedNativeObject();
michael@0 209 if (outer)
michael@0 210 return outer->QueryInterface(aIID, aInstancePtr);
michael@0 211
michael@0 212 // else...
michael@0 213
michael@0 214 return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
michael@0 215 }
michael@0 216
michael@0 217
michael@0 218 // For a description of nsXPCWrappedJS lifetime and reference counting, see
michael@0 219 // the comment at the top of this file.
michael@0 220
michael@0 221 MozExternalRefCountType
michael@0 222 nsXPCWrappedJS::AddRef(void)
michael@0 223 {
michael@0 224 if (!MOZ_LIKELY(NS_IsMainThread()))
michael@0 225 MOZ_CRASH();
michael@0 226
michael@0 227 MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
michael@0 228 nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
michael@0 229 nsrefcnt cnt = mRefCnt.incr(base);
michael@0 230 NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
michael@0 231
michael@0 232 if (2 == cnt && IsValid()) {
michael@0 233 GetJSObject(); // Unmark gray JSObject.
michael@0 234 mClass->GetRuntime()->AddWrappedJSRoot(this);
michael@0 235 }
michael@0 236
michael@0 237 return cnt;
michael@0 238 }
michael@0 239
michael@0 240 MozExternalRefCountType
michael@0 241 nsXPCWrappedJS::Release(void)
michael@0 242 {
michael@0 243 if (!MOZ_LIKELY(NS_IsMainThread()))
michael@0 244 MOZ_CRASH();
michael@0 245 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
michael@0 246 NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS);
michael@0 247
michael@0 248 bool shouldDelete = false;
michael@0 249 nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
michael@0 250 nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete);
michael@0 251 NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
michael@0 252
michael@0 253 if (0 == cnt) {
michael@0 254 if (MOZ_UNLIKELY(shouldDelete)) {
michael@0 255 mRefCnt.stabilizeForDeletion();
michael@0 256 DeleteCycleCollectable();
michael@0 257 } else {
michael@0 258 mRefCnt.incr(base);
michael@0 259 Destroy();
michael@0 260 mRefCnt.decr(base);
michael@0 261 }
michael@0 262 } else if (1 == cnt) {
michael@0 263 if (IsValid())
michael@0 264 RemoveFromRootSet();
michael@0 265
michael@0 266 // If we are not a root wrapper being used from a weak reference,
michael@0 267 // then the extra ref is not needed and we can let outselves be
michael@0 268 // deleted.
michael@0 269 if (!HasWeakReferences())
michael@0 270 return Release();
michael@0 271
michael@0 272 MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references");
michael@0 273 }
michael@0 274 return cnt;
michael@0 275 }
michael@0 276
michael@0 277 NS_IMETHODIMP_(void)
michael@0 278 nsXPCWrappedJS::DeleteCycleCollectable(void)
michael@0 279 {
michael@0 280 delete this;
michael@0 281 }
michael@0 282
michael@0 283 void
michael@0 284 nsXPCWrappedJS::TraceJS(JSTracer* trc)
michael@0 285 {
michael@0 286 MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
michael@0 287 trc->setTracingDetails(GetTraceName, this, 0);
michael@0 288 JS_CallHeapObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
michael@0 289 }
michael@0 290
michael@0 291 // static
michael@0 292 void
michael@0 293 nsXPCWrappedJS::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
michael@0 294 {
michael@0 295 const nsXPCWrappedJS* self = static_cast<const nsXPCWrappedJS*>
michael@0 296 (trc->debugPrintArg());
michael@0 297 JS_snprintf(buf, bufsize, "nsXPCWrappedJS[%s,0x%p:0x%p].mJSObj",
michael@0 298 self->GetClass()->GetInterfaceName(), self, self->mXPTCStub);
michael@0 299 }
michael@0 300
michael@0 301 NS_IMETHODIMP
michael@0 302 nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
michael@0 303 {
michael@0 304 if (!IsRootWrapper())
michael@0 305 return mRoot->GetWeakReference(aInstancePtr);
michael@0 306
michael@0 307 return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
michael@0 308 }
michael@0 309
michael@0 310 JSObject*
michael@0 311 nsXPCWrappedJS::GetJSObject()
michael@0 312 {
michael@0 313 if (mJSObj) {
michael@0 314 JS::ExposeObjectToActiveJS(mJSObj);
michael@0 315 }
michael@0 316 return mJSObj;
michael@0 317 }
michael@0 318
michael@0 319 // static
michael@0 320 nsresult
michael@0 321 nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj,
michael@0 322 REFNSIID aIID,
michael@0 323 nsXPCWrappedJS** wrapperResult)
michael@0 324 {
michael@0 325 // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
michael@0 326 if (!MOZ_LIKELY(NS_IsMainThread()))
michael@0 327 MOZ_CRASH();
michael@0 328
michael@0 329 AutoJSContext cx;
michael@0 330 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
michael@0 331 JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
michael@0 332 if (!map) {
michael@0 333 MOZ_ASSERT(map,"bad map");
michael@0 334 return NS_ERROR_FAILURE;
michael@0 335 }
michael@0 336
michael@0 337 nsRefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID);
michael@0 338 if (!clasp)
michael@0 339 return NS_ERROR_FAILURE;
michael@0 340
michael@0 341 JS::RootedObject rootJSObj(cx, clasp->GetRootJSObject(cx, jsObj));
michael@0 342 if (!rootJSObj)
michael@0 343 return NS_ERROR_FAILURE;
michael@0 344
michael@0 345 nsRefPtr<nsXPCWrappedJS> root = map->Find(rootJSObj);
michael@0 346 if (root) {
michael@0 347 nsRefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID);
michael@0 348 if (wrapper) {
michael@0 349 wrapper.forget(wrapperResult);
michael@0 350 return NS_OK;
michael@0 351 }
michael@0 352 } else if (rootJSObj != jsObj) {
michael@0 353
michael@0 354 // Make a new root wrapper, because there is no existing
michael@0 355 // root wrapper, and the wrapper we are trying to make isn't
michael@0 356 // a root.
michael@0 357 nsRefPtr<nsXPCWrappedJSClass> rootClasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports));
michael@0 358 if (!rootClasp)
michael@0 359 return NS_ERROR_FAILURE;
michael@0 360
michael@0 361 root = new nsXPCWrappedJS(cx, rootJSObj, rootClasp, nullptr);
michael@0 362 }
michael@0 363
michael@0 364 nsRefPtr<nsXPCWrappedJS> wrapper = new nsXPCWrappedJS(cx, jsObj, clasp, root);
michael@0 365 wrapper.forget(wrapperResult);
michael@0 366 return NS_OK;
michael@0 367 }
michael@0 368
michael@0 369 nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx,
michael@0 370 JSObject* aJSObj,
michael@0 371 nsXPCWrappedJSClass* aClass,
michael@0 372 nsXPCWrappedJS* root)
michael@0 373 : mJSObj(aJSObj),
michael@0 374 mClass(aClass),
michael@0 375 mRoot(root ? root : MOZ_THIS_IN_INITIALIZER_LIST()),
michael@0 376 mNext(nullptr)
michael@0 377 {
michael@0 378 InitStub(GetClass()->GetIID());
michael@0 379
michael@0 380 // There is an extra AddRef to support weak references to wrappers
michael@0 381 // that are subject to finalization. See the top of the file for more
michael@0 382 // details.
michael@0 383 NS_ADDREF_THIS();
michael@0 384
michael@0 385 if (IsRootWrapper()) {
michael@0 386 nsXPConnect::GetRuntimeInstance()->GetWrappedJSMap()->Add(cx, this);
michael@0 387 } else {
michael@0 388 NS_ADDREF(mRoot);
michael@0 389 mNext = mRoot->mNext;
michael@0 390 mRoot->mNext = this;
michael@0 391 }
michael@0 392 }
michael@0 393
michael@0 394 nsXPCWrappedJS::~nsXPCWrappedJS()
michael@0 395 {
michael@0 396 Destroy();
michael@0 397 }
michael@0 398
michael@0 399 void
michael@0 400 nsXPCWrappedJS::Destroy()
michael@0 401 {
michael@0 402 MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion");
michael@0 403
michael@0 404 if (IsRootWrapper()) {
michael@0 405 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
michael@0 406 JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
michael@0 407 if (map)
michael@0 408 map->Remove(this);
michael@0 409 }
michael@0 410 Unlink();
michael@0 411 }
michael@0 412
michael@0 413 void
michael@0 414 nsXPCWrappedJS::Unlink()
michael@0 415 {
michael@0 416 if (IsValid()) {
michael@0 417 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
michael@0 418 if (rt) {
michael@0 419 if (IsRootWrapper()) {
michael@0 420 JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
michael@0 421 if (map)
michael@0 422 map->Remove(this);
michael@0 423 }
michael@0 424
michael@0 425 if (mRefCnt > 1)
michael@0 426 RemoveFromRootSet();
michael@0 427 }
michael@0 428
michael@0 429 mJSObj = nullptr;
michael@0 430 }
michael@0 431
michael@0 432 if (IsRootWrapper()) {
michael@0 433 ClearWeakReferences();
michael@0 434 } else if (mRoot) {
michael@0 435 // unlink this wrapper
michael@0 436 nsXPCWrappedJS* cur = mRoot;
michael@0 437 while (1) {
michael@0 438 if (cur->mNext == this) {
michael@0 439 cur->mNext = mNext;
michael@0 440 break;
michael@0 441 }
michael@0 442 cur = cur->mNext;
michael@0 443 MOZ_ASSERT(cur, "failed to find wrapper in its own chain");
michael@0 444 }
michael@0 445 // let the root go
michael@0 446 NS_RELEASE(mRoot);
michael@0 447 }
michael@0 448
michael@0 449 mClass = nullptr;
michael@0 450 if (mOuter) {
michael@0 451 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
michael@0 452 if (rt->GCIsRunning()) {
michael@0 453 nsContentUtils::DeferredFinalize(mOuter.forget().take());
michael@0 454 } else {
michael@0 455 mOuter = nullptr;
michael@0 456 }
michael@0 457 }
michael@0 458 }
michael@0 459
michael@0 460 nsXPCWrappedJS*
michael@0 461 nsXPCWrappedJS::Find(REFNSIID aIID)
michael@0 462 {
michael@0 463 if (aIID.Equals(NS_GET_IID(nsISupports)))
michael@0 464 return mRoot;
michael@0 465
michael@0 466 for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
michael@0 467 if (aIID.Equals(cur->GetIID()))
michael@0 468 return cur;
michael@0 469 }
michael@0 470
michael@0 471 return nullptr;
michael@0 472 }
michael@0 473
michael@0 474 // check if asking for an interface that some wrapper in the chain inherits from
michael@0 475 nsXPCWrappedJS*
michael@0 476 nsXPCWrappedJS::FindInherited(REFNSIID aIID)
michael@0 477 {
michael@0 478 MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence");
michael@0 479
michael@0 480 for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
michael@0 481 bool found;
michael@0 482 if (NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()->
michael@0 483 HasAncestor(&aIID, &found)) && found)
michael@0 484 return cur;
michael@0 485 }
michael@0 486
michael@0 487 return nullptr;
michael@0 488 }
michael@0 489
michael@0 490 NS_IMETHODIMP
michael@0 491 nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** infoResult)
michael@0 492 {
michael@0 493 MOZ_ASSERT(GetClass(), "wrapper without class");
michael@0 494 MOZ_ASSERT(GetClass()->GetInterfaceInfo(), "wrapper class without interface");
michael@0 495
michael@0 496 // Since failing to get this info will crash some platforms(!), we keep
michael@0 497 // mClass valid at shutdown time.
michael@0 498
michael@0 499 nsCOMPtr<nsIInterfaceInfo> info = GetClass()->GetInterfaceInfo();
michael@0 500 if (!info)
michael@0 501 return NS_ERROR_UNEXPECTED;
michael@0 502 info.forget(infoResult);
michael@0 503 return NS_OK;
michael@0 504 }
michael@0 505
michael@0 506 NS_IMETHODIMP
michael@0 507 nsXPCWrappedJS::CallMethod(uint16_t methodIndex,
michael@0 508 const XPTMethodDescriptor* info,
michael@0 509 nsXPTCMiniVariant* params)
michael@0 510 {
michael@0 511 // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
michael@0 512 if (!MOZ_LIKELY(NS_IsMainThread()))
michael@0 513 MOZ_CRASH();
michael@0 514
michael@0 515 if (!IsValid())
michael@0 516 return NS_ERROR_UNEXPECTED;
michael@0 517 return GetClass()->CallMethod(this, methodIndex, info, params);
michael@0 518 }
michael@0 519
michael@0 520 NS_IMETHODIMP
michael@0 521 nsXPCWrappedJS::GetInterfaceIID(nsIID** iid)
michael@0 522 {
michael@0 523 NS_PRECONDITION(iid, "bad param");
michael@0 524
michael@0 525 *iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID));
michael@0 526 return *iid ? NS_OK : NS_ERROR_UNEXPECTED;
michael@0 527 }
michael@0 528
michael@0 529 void
michael@0 530 nsXPCWrappedJS::SystemIsBeingShutDown()
michael@0 531 {
michael@0 532 // XXX It turns out that it is better to leak here then to do any Releases
michael@0 533 // and have them propagate into all sorts of mischief as the system is being
michael@0 534 // shutdown. This was learned the hard way :(
michael@0 535
michael@0 536 // mJSObj == nullptr is used to indicate that the wrapper is no longer valid
michael@0 537 // and that calls should fail without trying to use any of the
michael@0 538 // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
michael@0 539
michael@0 540 // NOTE: that mClass is retained so that GetInterfaceInfo can continue to
michael@0 541 // work (and avoid crashing some platforms).
michael@0 542
michael@0 543 // Use of unsafeGet() is to avoid triggering post barriers in shutdown, as
michael@0 544 // this will access the chunk containing mJSObj, which may have been freed
michael@0 545 // at this point.
michael@0 546 *mJSObj.unsafeGet() = nullptr;
michael@0 547
michael@0 548 // Notify other wrappers in the chain.
michael@0 549 if (mNext)
michael@0 550 mNext->SystemIsBeingShutDown();
michael@0 551 }
michael@0 552
michael@0 553 /***************************************************************************/
michael@0 554
michael@0 555 /* readonly attribute nsISimpleEnumerator enumerator; */
michael@0 556 NS_IMETHODIMP
michael@0 557 nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate)
michael@0 558 {
michael@0 559 AutoJSContext cx;
michael@0 560 XPCCallContext ccx(NATIVE_CALLER, cx);
michael@0 561 if (!ccx.IsValid())
michael@0 562 return NS_ERROR_UNEXPECTED;
michael@0 563
michael@0 564 return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(),
michael@0 565 aEnumerate);
michael@0 566 }
michael@0 567
michael@0 568 /* nsIVariant getProperty (in AString name); */
michael@0 569 NS_IMETHODIMP
michael@0 570 nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant **_retval)
michael@0 571 {
michael@0 572 AutoJSContext cx;
michael@0 573 XPCCallContext ccx(NATIVE_CALLER, cx);
michael@0 574 if (!ccx.IsValid())
michael@0 575 return NS_ERROR_UNEXPECTED;
michael@0 576
michael@0 577 return nsXPCWrappedJSClass::
michael@0 578 GetNamedPropertyAsVariant(ccx, GetJSObject(), name, _retval);
michael@0 579 }
michael@0 580
michael@0 581 /***************************************************************************/
michael@0 582
michael@0 583 NS_IMETHODIMP
michael@0 584 nsXPCWrappedJS::DebugDump(int16_t depth)
michael@0 585 {
michael@0 586 #ifdef DEBUG
michael@0 587 XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get()));
michael@0 588 XPC_LOG_INDENT();
michael@0 589
michael@0 590 XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \
michael@0 591 IsRootWrapper() ? "ROOT":"non-root", mJSObj.get()));
michael@0 592 char* name;
michael@0 593 GetClass()->GetInterfaceInfo()->GetName(&name);
michael@0 594 XPC_LOG_ALWAYS(("interface name is %s", name));
michael@0 595 if (name)
michael@0 596 nsMemory::Free(name);
michael@0 597 char * iid = GetClass()->GetIID().ToString();
michael@0 598 XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
michael@0 599 if (iid)
michael@0 600 NS_Free(iid);
michael@0 601 XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x", mClass.get()));
michael@0 602
michael@0 603 if (!IsRootWrapper())
michael@0 604 XPC_LOG_OUTDENT();
michael@0 605 if (mNext) {
michael@0 606 if (IsRootWrapper()) {
michael@0 607 XPC_LOG_ALWAYS(("Additional wrappers for this object..."));
michael@0 608 XPC_LOG_INDENT();
michael@0 609 }
michael@0 610 mNext->DebugDump(depth);
michael@0 611 if (IsRootWrapper())
michael@0 612 XPC_LOG_OUTDENT();
michael@0 613 }
michael@0 614 if (IsRootWrapper())
michael@0 615 XPC_LOG_OUTDENT();
michael@0 616 #endif
michael@0 617 return NS_OK;
michael@0 618 }

mercurial