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