XPCWrappedJS.cpp (22804B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* Class that wraps JS objects to appear as XPCOM objects. */ 8 9 #include "xpcprivate.h" 10 #include "XPCMaps.h" 11 #include "mozilla/DeferredFinalize.h" 12 #include "mozilla/HoldDropJSObjects.h" 13 #include "mozilla/Sprintf.h" 14 #include "js/Object.h" // JS::GetCompartment 15 #include "js/RealmIterators.h" 16 #include "nsCCUncollectableMarker.h" 17 #include "nsContentUtils.h" 18 #include "nsThreadUtils.h" 19 20 using namespace mozilla; 21 22 // NOTE: much of the fancy footwork is done in xpcstubs.cpp 23 24 // nsXPCWrappedJS lifetime. 25 // 26 // An nsXPCWrappedJS is either rooting its JS object or is subject to 27 // finalization. The subject-to-finalization state lets wrappers support 28 // nsSupportsWeakReference in the case where the underlying JS object 29 // is strongly owned, but the wrapper itself is only weakly owned. 30 // 31 // A wrapper is rooting its JS object whenever its refcount is greater than 1. 32 // In this state, root wrappers are always added to the cycle collector graph. 33 // The wrapper keeps around an extra refcount, added in the constructor, to 34 // support the possibility of an eventual transition to the 35 // subject-to-finalization state. This extra refcount is ignored by the cycle 36 // collector, which traverses the "self" edge for this refcount. 37 // 38 // When the refcount of a rooting wrapper drops to 1, if there is no weak 39 // reference to the wrapper (which can only happen for the root wrapper), it is 40 // immediately Destroy()'d. Otherwise, it becomes subject to finalization. 41 // 42 // When a wrapper is subject to finalization, the wrapper has a refcount of 1. 43 // It is now owned exclusively by its JS object. Either a weak reference will be 44 // turned into a strong ref which will bring its refcount up to 2 and change the 45 // wrapper back to the rooting state, or it will stay alive until the JS object 46 // dies. If the JS object dies, then when 47 // JSObject2WrappedJSMap::UpdateWeakPointersAfterGC is called (via the JS 48 // engine's weak pointer zone or compartment callbacks) it will find the wrapper 49 // and call Release() on it, destroying the wrapper. Otherwise, the wrapper will 50 // stay alive, even if it no longer has a weak reference to it. 51 // 52 // When the wrapper is subject to finalization, it is kept alive by an implicit 53 // reference from the JS object which is invisible to the cycle collector, so 54 // the cycle collector does not traverse any children of wrappers that are 55 // subject to finalization. This will result in a leak if a wrapper in the 56 // non-rooting state has an aggregated native that keeps alive the wrapper's JS 57 // object. See bug 947049. 58 59 // If traversing wrappedJS wouldn't release it, nor cause any other objects to 60 // be added to the graph, there is no need to add it to the graph at all. 61 bool nsXPCWrappedJS::CanSkip() { 62 if (!nsCCUncollectableMarker::sGeneration) { 63 return false; 64 } 65 66 // If this wrapper holds a gray object, need to trace it. 67 // We can't skip it even if it is subject to finalization, because we want to 68 // be able to collect it if the JS object is gray. 69 JSObject* obj = GetJSObjectPreserveColor(); 70 if (obj && JS::ObjectIsMarkedGray(obj)) { 71 return false; 72 } 73 74 // For non-root wrappers, check if the root wrapper will be 75 // added to the CC graph. 76 if (!IsRootWrapper()) { 77 // mRoot points to null after unlinking. 78 NS_ENSURE_TRUE(mRoot, false); 79 return mRoot->CanSkip(); 80 } 81 82 // At this point, the WJS must be a root wrapper with a black JS object, so 83 // if it is subject to finalization, the JS object will be holding it alive 84 // so it will be okay to skip it. 85 86 // For the root wrapper, check if there is an aggregated 87 // native object that will be added to the CC graph. 88 if (!IsAggregatedToNative()) { 89 return true; 90 } 91 92 nsISupports* agg = GetAggregatedNativeObject(); 93 nsXPCOMCycleCollectionParticipant* cp = nullptr; 94 CallQueryInterface(agg, &cp); 95 nsISupports* canonical = nullptr; 96 agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), 97 reinterpret_cast<void**>(&canonical)); 98 return cp && canonical && cp->CanSkipThis(canonical); 99 } 100 101 NS_IMETHODIMP 102 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::TraverseNative( 103 void* p, nsCycleCollectionTraversalCallback& cb) { 104 nsISupports* s = static_cast<nsISupports*>(p); 105 MOZ_ASSERT(CheckForRightISupports(s), 106 "not the nsISupports pointer we expect"); 107 nsXPCWrappedJS* tmp = Downcast(s); 108 109 nsrefcnt refcnt = tmp->mRefCnt.get(); 110 if (cb.WantDebugInfo()) { 111 char name[72]; 112 SprintfLiteral(name, "nsXPCWrappedJS (%s)", tmp->mInfo->Name()); 113 cb.DescribeRefCountedNode(refcnt, name); 114 } else { 115 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt) 116 } 117 118 if (tmp->IsSubjectToFinalization()) { 119 // If the WJS is subject to finalization, then it can be held alive by its 120 // JS object. We represent this edge by using NoteWeakMapping. The linked 121 // list of subject-to-finalization WJS acts like a known-black weak map. 122 cb.NoteWeakMapping(tmp->GetJSObjectPreserveColor(), s, 123 NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS)); 124 } 125 126 // Don't let the extra reference for nsSupportsWeakReference keep a WJS alive. 127 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self"); 128 cb.NoteXPCOMChild(s); 129 130 if (tmp->IsRootWrapper()) { 131 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native"); 132 cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject()); 133 } else { 134 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root"); 135 cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper())); 136 } 137 138 return NS_OK; 139 } 140 141 NS_IMPL_CYCLE_COLLECTION_SINGLE_ZONE_SCRIPT_HOLDER_CLASS(nsXPCWrappedJS) 142 143 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS) 144 tmp->Unlink(); 145 // Note: Unlink already calls ClearWeakReferences, so no need for 146 // NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE here. 147 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 148 149 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXPCWrappedJS) 150 // See the comment at the top of this file for the explanation of 151 // the weird tracing condition. 152 if (!tmp->IsSubjectToFinalization()) { 153 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSObj) 154 } 155 NS_IMPL_CYCLE_COLLECTION_TRACE_END 156 157 // WJS are JS holders, so we'll always add them as roots in CCs and we can 158 // remove them from the purple buffer in between CCs. 159 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS) 160 return true; 161 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 162 163 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS) 164 return tmp->CanSkip(); 165 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 166 167 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS) 168 return tmp->CanSkip(); 169 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 170 171 nsXPCWrappedJS* nsIXPConnectWrappedJS::AsXPCWrappedJS() { 172 return static_cast<nsXPCWrappedJS*>(this); 173 } 174 175 nsresult nsIXPConnectWrappedJS::AggregatedQueryInterface(REFNSIID aIID, 176 void** aInstancePtr) { 177 MOZ_ASSERT(AsXPCWrappedJS()->IsAggregatedToNative(), 178 "bad AggregatedQueryInterface call"); 179 *aInstancePtr = nullptr; 180 181 if (!AsXPCWrappedJS()->IsValid()) { 182 return NS_ERROR_UNEXPECTED; 183 } 184 185 // Put this here rather that in DelegatedQueryInterface because it needs 186 // to be in QueryInterface before the possible delegation to 'outer', but 187 // we don't want to do this check twice in one call in the normal case: 188 // once in QueryInterface and once in DelegatedQueryInterface. 189 if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) { 190 NS_ADDREF(this); 191 *aInstancePtr = (void*)this; 192 return NS_OK; 193 } 194 195 return AsXPCWrappedJS()->DelegatedQueryInterface(aIID, aInstancePtr); 196 } 197 198 NS_IMETHODIMP 199 nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr) { 200 if (nullptr == aInstancePtr) { 201 MOZ_ASSERT(false, "null pointer"); 202 return NS_ERROR_NULL_POINTER; 203 } 204 205 *aInstancePtr = nullptr; 206 207 if (aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { 208 *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS); 209 return NS_OK; 210 } 211 212 if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) { 213 *aInstancePtr = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); 214 return NS_OK; 215 } 216 217 if (!IsValid()) { 218 return NS_ERROR_UNEXPECTED; 219 } 220 221 if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJSUnmarkGray))) { 222 *aInstancePtr = nullptr; 223 224 mJSObj.exposeToActiveJS(); 225 226 // Just return some error value since one isn't supposed to use 227 // nsIXPConnectWrappedJSUnmarkGray objects for anything. 228 return NS_ERROR_FAILURE; 229 } 230 231 // Always check for this first so that our 'outer' can get this interface 232 // from us without recurring into a call to the outer's QI! 233 if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) { 234 NS_ADDREF(this); 235 *aInstancePtr = (void*)static_cast<nsIXPConnectWrappedJS*>(this); 236 return NS_OK; 237 } 238 239 nsISupports* outer = GetAggregatedNativeObject(); 240 if (outer) { 241 return outer->QueryInterface(aIID, aInstancePtr); 242 } 243 244 // else... 245 246 return DelegatedQueryInterface(aIID, aInstancePtr); 247 } 248 249 // For a description of nsXPCWrappedJS lifetime and reference counting, see 250 // the comment at the top of this file. 251 252 MozExternalRefCountType nsXPCWrappedJS::AddRef(void) { 253 MOZ_RELEASE_ASSERT(NS_IsMainThread(), 254 "nsXPCWrappedJS::AddRef called off main thread"); 255 256 MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); 257 nsISupports* base = 258 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); 259 nsrefcnt cnt = mRefCnt.incr(base); 260 NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this)); 261 262 if (2 == cnt && IsValid()) { 263 GetJSObject(); // Unmark gray JSObject. 264 265 // This WJS is no longer subject to finalization. 266 if (isInList()) { 267 remove(); 268 } 269 } 270 271 return cnt; 272 } 273 274 MozExternalRefCountType nsXPCWrappedJS::Release(void) { 275 MOZ_RELEASE_ASSERT(NS_IsMainThread(), 276 "nsXPCWrappedJS::Release called off main thread"); 277 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); 278 NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS); 279 280 bool shouldDelete = false; 281 nsISupports* base = 282 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); 283 nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete); 284 NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS"); 285 286 if (0 == cnt) { 287 if (MOZ_UNLIKELY(shouldDelete)) { 288 mRefCnt.stabilizeForDeletion(); 289 DeleteCycleCollectable(); 290 } else { 291 mRefCnt.incr(base); 292 Destroy(); 293 mRefCnt.decr(base); 294 } 295 } else if (1 == cnt) { 296 // If we are not a root wrapper being used from a weak reference, 297 // then the extra ref is not needed and we can let ourselves be 298 // deleted. 299 if (!HasWeakReferences()) { 300 return Release(); 301 } 302 303 if (IsValid()) { 304 XPCJSRuntime::Get()->AddSubjectToFinalizationWJS(this); 305 } 306 307 MOZ_ASSERT(IsRootWrapper(), 308 "Only root wrappers should have weak references"); 309 } 310 return cnt; 311 } 312 313 NS_IMETHODIMP_(void) 314 nsXPCWrappedJS::DeleteCycleCollectable(void) { delete this; } 315 316 NS_IMETHODIMP 317 nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr) { 318 if (!IsRootWrapper()) { 319 return mRoot->GetWeakReference(aInstancePtr); 320 } 321 322 return nsSupportsWeakReference::GetWeakReference(aInstancePtr); 323 } 324 325 JSObject* nsXPCWrappedJS::GetJSObject() { return mJSObj; } 326 327 JSObject* nsIXPConnectWrappedJS::GetJSObjectGlobal() { 328 JSObject* obj = AsXPCWrappedJS()->mJSObj; 329 if (js::IsCrossCompartmentWrapper(obj)) { 330 JS::Compartment* comp = JS::GetCompartment(obj); 331 return js::GetFirstGlobalInCompartment(comp); 332 } 333 return JS::GetNonCCWObjectGlobal(obj); 334 } 335 336 // static 337 nsresult nsXPCWrappedJS::GetNewOrUsed(JSContext* cx, JS::HandleObject jsObj, 338 REFNSIID aIID, 339 nsXPCWrappedJS** wrapperResult) { 340 // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread. 341 MOZ_RELEASE_ASSERT(NS_IsMainThread(), 342 "nsXPCWrappedJS::GetNewOrUsed called off main thread"); 343 344 MOZ_RELEASE_ASSERT(js::GetContextCompartment(cx) == 345 JS::GetCompartment(jsObj)); 346 347 const nsXPTInterfaceInfo* info = GetInterfaceInfo(aIID); 348 if (!info) { 349 return NS_ERROR_FAILURE; 350 } 351 352 JS::RootedObject rootJSObj(cx, GetRootJSObject(cx, jsObj)); 353 if (!rootJSObj) { 354 return NS_ERROR_FAILURE; 355 } 356 357 xpc::CompartmentPrivate* rootComp = xpc::CompartmentPrivate::Get(rootJSObj); 358 MOZ_ASSERT(rootComp); 359 360 // Find any existing wrapper. 361 RefPtr<nsXPCWrappedJS> root = rootComp->GetWrappedJSMap()->Find(rootJSObj); 362 MOZ_ASSERT_IF(root, !nsXPConnect::GetRuntimeInstance() 363 ->GetMultiCompartmentWrappedJSMap() 364 ->Find(rootJSObj)); 365 if (!root) { 366 root = nsXPConnect::GetRuntimeInstance() 367 ->GetMultiCompartmentWrappedJSMap() 368 ->Find(rootJSObj); 369 } 370 371 nsresult rv = NS_ERROR_FAILURE; 372 if (root) { 373 RefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID); 374 if (wrapper) { 375 wrapper.forget(wrapperResult); 376 return NS_OK; 377 } 378 } else if (rootJSObj != jsObj) { 379 // Make a new root wrapper, because there is no existing 380 // root wrapper, and the wrapper we are trying to make isn't 381 // a root. 382 const nsXPTInterfaceInfo* rootInfo = 383 GetInterfaceInfo(NS_GET_IID(nsISupports)); 384 if (!rootInfo) { 385 return NS_ERROR_FAILURE; 386 } 387 388 root = new nsXPCWrappedJS(cx, rootJSObj, rootInfo, nullptr, &rv); 389 if (NS_FAILED(rv)) { 390 return rv; 391 } 392 } 393 394 RefPtr<nsXPCWrappedJS> wrapper = 395 new nsXPCWrappedJS(cx, jsObj, info, root, &rv); 396 if (NS_FAILED(rv)) { 397 return rv; 398 } 399 wrapper.forget(wrapperResult); 400 return NS_OK; 401 } 402 403 nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj, 404 const nsXPTInterfaceInfo* aInfo, 405 nsXPCWrappedJS* root, nsresult* rv) 406 : mJSObj(aJSObj), mInfo(aInfo), mRoot(root ? root : this), mNext(nullptr) { 407 *rv = InitStub(mInfo->IID()); 408 // Continue even in the failure case, so that our refcounting/Destroy 409 // behavior works correctly. 410 411 // There is an extra AddRef to support weak references to wrappers 412 // that are subject to finalization. See the top of the file for more 413 // details. 414 NS_ADDREF_THIS(); 415 416 if (IsRootWrapper()) { 417 MOZ_ASSERT(!IsMultiCompartment(), "mNext is always nullptr here"); 418 if (!xpc::CompartmentPrivate::Get(mJSObj)->GetWrappedJSMap()->Add(cx, 419 this)) { 420 *rv = NS_ERROR_OUT_OF_MEMORY; 421 } 422 } else { 423 NS_ADDREF(mRoot); 424 mNext = mRoot->mNext; 425 mRoot->mNext = this; 426 427 // We always start wrappers in the per-compartment table. If adding 428 // this wrapper to the chain causes it to cross compartments, we need 429 // to migrate the chain to the global table on the XPCJSContext. 430 if (mRoot->IsMultiCompartment()) { 431 xpc::CompartmentPrivate::Get(mRoot->mJSObj) 432 ->GetWrappedJSMap() 433 ->Remove(mRoot); 434 auto destMap = 435 nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap(); 436 if (!destMap->Add(cx, mRoot)) { 437 *rv = NS_ERROR_OUT_OF_MEMORY; 438 } 439 } 440 } 441 442 mozilla::HoldJSObjects(this); 443 } 444 445 nsXPCWrappedJS::~nsXPCWrappedJS() { Destroy(); } 446 447 void XPCJSRuntime::RemoveWrappedJS(nsXPCWrappedJS* wrapper) { 448 AssertInvalidWrappedJSNotInTable(wrapper); 449 if (!wrapper->IsValid()) { 450 return; 451 } 452 453 // It is possible for the same JS XPCOM implementation object to be wrapped 454 // with a different interface in multiple JS::Compartments. In this case, the 455 // wrapper chain will contain references to multiple compartments. While we 456 // always store single-compartment chains in the per-compartment wrapped-js 457 // table, chains in the multi-compartment wrapped-js table may contain 458 // single-compartment chains, if they have ever contained a wrapper in a 459 // different compartment. Since removal requires a lookup anyway, we just do 460 // the remove on both tables unconditionally. 461 MOZ_ASSERT_IF( 462 wrapper->IsMultiCompartment(), 463 !xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor()) 464 ->GetWrappedJSMap() 465 ->HasWrapper(wrapper)); 466 GetMultiCompartmentWrappedJSMap()->Remove(wrapper); 467 xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor()) 468 ->GetWrappedJSMap() 469 ->Remove(wrapper); 470 } 471 472 #ifdef DEBUG 473 static JS::CompartmentIterResult NotHasWrapperAssertionCallback( 474 JSContext* cx, void* data, JS::Compartment* comp) { 475 auto wrapper = static_cast<nsXPCWrappedJS*>(data); 476 auto xpcComp = xpc::CompartmentPrivate::Get(comp); 477 MOZ_ASSERT_IF(xpcComp, !xpcComp->GetWrappedJSMap()->HasWrapper(wrapper)); 478 return JS::CompartmentIterResult::KeepGoing; 479 } 480 #endif 481 482 void XPCJSRuntime::AssertInvalidWrappedJSNotInTable( 483 nsXPCWrappedJS* wrapper) const { 484 #ifdef DEBUG 485 if (!wrapper->IsValid()) { 486 MOZ_ASSERT(!GetMultiCompartmentWrappedJSMap()->HasWrapper(wrapper)); 487 if (!mGCIsRunning) { 488 JSContext* cx = XPCJSContext::Get()->Context(); 489 JS_IterateCompartments(cx, wrapper, NotHasWrapperAssertionCallback); 490 } 491 } 492 #endif 493 } 494 495 void nsXPCWrappedJS::Destroy() { 496 MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion"); 497 498 if (IsRootWrapper()) { 499 nsXPConnect::GetRuntimeInstance()->RemoveWrappedJS(this); 500 } 501 Unlink(); 502 } 503 504 void nsXPCWrappedJS::Unlink() { 505 nsXPConnect::GetRuntimeInstance()->AssertInvalidWrappedJSNotInTable(this); 506 507 if (IsValid()) { 508 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); 509 if (rt) { 510 if (IsRootWrapper()) { 511 rt->RemoveWrappedJS(this); 512 } 513 } 514 515 mJSObj = nullptr; 516 } 517 518 if (IsRootWrapper()) { 519 if (isInList()) { 520 remove(); 521 } 522 ClearWeakReferences(); 523 } else if (mRoot) { 524 // unlink this wrapper 525 nsXPCWrappedJS* cur = mRoot; 526 while (1) { 527 if (cur->mNext == this) { 528 cur->mNext = mNext; 529 break; 530 } 531 cur = cur->mNext; 532 MOZ_ASSERT(cur, "failed to find wrapper in its own chain"); 533 } 534 535 // Note: unlinking this wrapper may have changed us from a multi- 536 // compartment wrapper chain to a single-compartment wrapper chain. We 537 // leave the wrapper in the multi-compartment table as it is likely to 538 // need to be multi-compartment again in the future and, moreover, we 539 // cannot get a JSContext here. 540 541 // let the root go 542 NS_RELEASE(mRoot); 543 } 544 545 if (mOuter) { 546 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); 547 if (rt->GCIsRunning()) { 548 DeferredFinalize(mOuter.forget().take()); 549 } else { 550 mOuter = nullptr; 551 } 552 } 553 554 mozilla::DropJSObjects(this); 555 } 556 557 bool nsXPCWrappedJS::IsMultiCompartment() const { 558 MOZ_ASSERT(IsRootWrapper()); 559 JS::Compartment* compartment = Compartment(); 560 nsXPCWrappedJS* next = mNext; 561 while (next) { 562 if (next->Compartment() != compartment) { 563 return true; 564 } 565 next = next->mNext; 566 } 567 return false; 568 } 569 570 nsXPCWrappedJS* nsXPCWrappedJS::Find(REFNSIID aIID) { 571 if (aIID.Equals(NS_GET_IID(nsISupports))) { 572 return mRoot; 573 } 574 575 for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) { 576 if (aIID.Equals(cur->GetIID())) { 577 return cur; 578 } 579 } 580 581 return nullptr; 582 } 583 584 // check if asking for an interface that some wrapper in the chain inherits from 585 nsXPCWrappedJS* nsXPCWrappedJS::FindInherited(REFNSIID aIID) { 586 MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence"); 587 588 for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) { 589 if (cur->mInfo->HasAncestor(aIID)) { 590 return cur; 591 } 592 } 593 594 return nullptr; 595 } 596 597 nsresult nsIXPConnectWrappedJS::GetInterfaceIID(nsIID** iid) { 598 MOZ_ASSERT(iid, "bad param"); 599 600 *iid = AsXPCWrappedJS()->GetIID().Clone(); 601 return NS_OK; 602 } 603 604 void nsXPCWrappedJS::SystemIsBeingShutDown() { 605 // XXX It turns out that it is better to leak here then to do any Releases 606 // and have them propagate into all sorts of mischief as the system is being 607 // shutdown. This was learned the hard way :( 608 609 // mJSObj == nullptr is used to indicate that the wrapper is no longer valid 610 // and that calls should fail without trying to use any of the 611 // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer. 612 613 // Clear the contents of the pointer using unsafeGet() to avoid 614 // triggering post barriers in shutdown, as this will access the chunk 615 // containing mJSObj, which may have been freed at this point. This is safe 616 // if we are not currently running an incremental GC. 617 MOZ_ASSERT(!JS::IsIncrementalGCInProgress(xpc_GetSafeJSContext())); 618 mJSObj.unbarrieredSet(nullptr); 619 if (isInList()) { 620 remove(); 621 } 622 623 // Notify other wrappers in the chain. 624 if (mNext) { 625 mNext->SystemIsBeingShutDown(); 626 } 627 } 628 629 size_t nsXPCWrappedJS::SizeOfIncludingThis( 630 mozilla::MallocSizeOf mallocSizeOf) const { 631 // mJSObject is a JS pointer, so don't measure the object. mInfo is 632 // not dynamically allocated. mRoot is not measured because it is 633 // either |this| or we have already measured it. mOuter is rare and 634 // probably not uniquely owned by this. 635 size_t n = mallocSizeOf(this); 636 n += nsAutoXPTCStub::SizeOfExcludingThis(mallocSizeOf); 637 638 // Wrappers form a linked list via the mNext field, so include them all 639 // in the measurement. Only root wrappers are stored in the map, so 640 // everything will be measured exactly once. 641 if (mNext) { 642 n += mNext->SizeOfIncludingThis(mallocSizeOf); 643 } 644 645 return n; 646 } 647 648 /***************************************************************************/ 649 650 nsresult nsIXPConnectWrappedJS::DebugDump(int16_t depth) { 651 return AsXPCWrappedJS()->DebugDump(depth); 652 } 653 654 nsresult nsXPCWrappedJS::DebugDump(int16_t depth) { 655 #ifdef DEBUG 656 XPC_LOG_ALWAYS( 657 ("nsXPCWrappedJS @ %p with mRefCnt = %" PRIuPTR, this, mRefCnt.get())); 658 XPC_LOG_INDENT(); 659 660 XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %p", 661 IsRootWrapper() ? "ROOT" : "non-root", mJSObj.get())); 662 const char* name = mInfo->Name(); 663 XPC_LOG_ALWAYS(("interface name is %s", name)); 664 auto iid = mInfo->IID().ToString(); 665 XPC_LOG_ALWAYS(("IID number is %s", iid.get())); 666 XPC_LOG_ALWAYS(("nsXPTInterfaceInfo @ %p", mInfo)); 667 668 if (!IsRootWrapper()) { 669 XPC_LOG_OUTDENT(); 670 } 671 if (mNext) { 672 if (IsRootWrapper()) { 673 XPC_LOG_ALWAYS(("Additional wrappers for this object...")); 674 XPC_LOG_INDENT(); 675 } 676 mNext->DebugDump(depth); 677 if (IsRootWrapper()) { 678 XPC_LOG_OUTDENT(); 679 } 680 } 681 if (IsRootWrapper()) { 682 XPC_LOG_OUTDENT(); 683 } 684 #endif 685 return NS_OK; 686 }