1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * John Bandhauer <jband@netscape.com> (original author)
26 : * Pierre Phaneuf <pp@ludusdesign.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : /* Class that wraps JS objects to appear as XPCOM objects. */
43 :
44 : #include "xpcprivate.h"
45 : #include "nsAtomicRefcnt.h"
46 : #include "nsProxyRelease.h"
47 : #include "nsThreadUtils.h"
48 : #include "nsTextFormatter.h"
49 :
50 : // NOTE: much of the fancy footwork is done in xpcstubs.cpp
51 :
52 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
53 :
54 : NS_IMETHODIMP
55 7425 : NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
56 : (void *p, nsCycleCollectionTraversalCallback &cb)
57 : {
58 7425 : nsISupports *s = static_cast<nsISupports*>(p);
59 7425 : NS_ASSERTION(CheckForRightISupports(s),
60 : "not the nsISupports pointer we expect");
61 7425 : nsXPCWrappedJS *tmp = Downcast(s);
62 :
63 7425 : nsrefcnt refcnt = tmp->mRefCnt.get();
64 7425 : if (cb.WantDebugInfo()) {
65 : char name[72];
66 0 : if (tmp->GetClass())
67 : JS_snprintf(name, sizeof(name), "nsXPCWrappedJS (%s)",
68 0 : tmp->GetClass()->GetInterfaceName());
69 : else
70 0 : JS_snprintf(name, sizeof(name), "nsXPCWrappedJS");
71 0 : cb.DescribeRefCountedNode(refcnt, sizeof(nsXPCWrappedJS), name);
72 : } else {
73 7425 : NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt)
74 : }
75 :
76 : // nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the
77 : // comment above nsXPCWrappedJS::AddRef.
78 7425 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
79 7425 : cb.NoteXPCOMChild(s);
80 :
81 7425 : if (refcnt > 1) {
82 : // nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see
83 : // the comment above nsXPCWrappedJS::AddRef.
84 7425 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
85 : cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
86 7425 : tmp->GetJSObjectPreserveColor());
87 : }
88 :
89 7425 : nsXPCWrappedJS* root = tmp->GetRootWrapper();
90 7425 : if (root == tmp) {
91 : // The root wrapper keeps the aggregated native object alive.
92 7145 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
93 7145 : cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
94 : } else {
95 : // Non-root wrappers keep their root alive.
96 280 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
97 280 : cb.NoteXPCOMChild(static_cast<nsIXPConnectWrappedJS*>(root));
98 : }
99 :
100 7425 : return NS_OK;
101 : }
102 :
103 289 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
104 289 : tmp->Unlink();
105 289 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
106 :
107 : NS_IMETHODIMP
108 0 : nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
109 : {
110 0 : NS_ASSERTION(IsAggregatedToNative(), "bad AggregatedQueryInterface call");
111 :
112 0 : if (!IsValid())
113 0 : return NS_ERROR_UNEXPECTED;
114 :
115 : // Put this here rather that in DelegatedQueryInterface because it needs
116 : // to be in QueryInterface before the possible delegation to 'outer', but
117 : // we don't want to do this check twice in one call in the normal case:
118 : // once in QueryInterface and once in DelegatedQueryInterface.
119 0 : if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
120 0 : NS_ADDREF(this);
121 0 : *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
122 0 : return NS_OK;
123 : }
124 :
125 0 : return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
126 : }
127 :
128 : NS_IMETHODIMP
129 1046122 : nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
130 : {
131 1046122 : if (nsnull == aInstancePtr) {
132 0 : NS_PRECONDITION(0, "null pointer");
133 0 : return NS_ERROR_NULL_POINTER;
134 : }
135 :
136 1046122 : if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
137 15771 : *aInstancePtr = & NS_CYCLE_COLLECTION_NAME(nsXPCWrappedJS);
138 15771 : return NS_OK;
139 : }
140 :
141 1030351 : if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) {
142 : *aInstancePtr =
143 23447 : NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
144 23447 : return NS_OK;
145 : }
146 :
147 1006904 : if (!IsValid())
148 24 : return NS_ERROR_UNEXPECTED;
149 :
150 : // Always check for this first so that our 'outer' can get this interface
151 : // from us without recurring into a call to the outer's QI!
152 1006880 : if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
153 64913 : NS_ADDREF(this);
154 64913 : *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
155 64913 : return NS_OK;
156 : }
157 :
158 941967 : nsISupports* outer = GetAggregatedNativeObject();
159 941967 : if (outer)
160 0 : return outer->QueryInterface(aIID, aInstancePtr);
161 :
162 : // else...
163 :
164 941967 : return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
165 : }
166 :
167 :
168 : // Refcounting is now similar to that used in the chained (pre-flattening)
169 : // wrappednative system.
170 : //
171 : // We are now holding an extra refcount for nsISupportsWeakReference support.
172 : //
173 : // Non-root wrappers remove themselves from the chain in their destructors.
174 : // We root the JSObject as the refcount transitions from 1->2. And we unroot
175 : // the JSObject when the refcount transitions from 2->1.
176 : //
177 : // When the transition from 2->1 is made and no one holds a weak ref to the
178 : // (aggregated) object then we decrement the refcount again to 0 (and
179 : // destruct) . However, if a weak ref is held at the 2->1 transition, then we
180 : // leave the refcount at 1 to indicate that state. This leaves the JSObject
181 : // no longer rooted by us and (as far as we know) subject to possible
182 : // collection. Code in XPCJSRuntime watches for JS gc to happen and will do
183 : // the final release on wrappers whose JSObjects get finalized. Note that
184 : // even after tranistioning to this refcount-of-one state callers might do
185 : // an addref and cause us to re-root the JSObject and continue on more normally.
186 :
187 : nsrefcnt
188 1475063 : nsXPCWrappedJS::AddRef(void)
189 : {
190 1475063 : nsrefcnt cnt = NS_AtomicIncrementRefcnt(mRefCnt);
191 1475063 : NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
192 :
193 1475063 : if (2 == cnt && IsValid()) {
194 114050 : XPCJSRuntime* rt = mClass->GetRuntime();
195 114050 : rt->AddWrappedJSRoot(this);
196 : }
197 :
198 1475063 : return cnt;
199 : }
200 :
201 : nsrefcnt
202 1376901 : nsXPCWrappedJS::Release(void)
203 : {
204 1376901 : NS_PRECONDITION(0 != mRefCnt, "dup release");
205 :
206 1376901 : if (mMainThreadOnly && !NS_IsMainThread()) {
207 : // We'd like to abort here, but this can happen if someone uses a proxy
208 : // for the nsXPCWrappedJS.
209 6832 : nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
210 : // If we can't get the main thread anymore we just leak, but this really
211 : // shouldn't happen.
212 3416 : NS_ASSERTION(mainThread,
213 : "Can't get main thread, leaking nsXPCWrappedJS!");
214 3416 : if (mainThread) {
215 : NS_ProxyRelease(mainThread,
216 3416 : static_cast<nsIXPConnectWrappedJS*>(this));
217 : }
218 3416 : return mRefCnt;
219 : }
220 :
221 : // need to take the map lock here to prevent GetNewOrUsed from trying
222 : // to reuse a wrapper on one thread while it's being destroyed on another
223 1373485 : XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
224 2746970 : XPCAutoLock lock(rt->GetMapLock());
225 :
226 : do_decrement:
227 :
228 1474501 : nsrefcnt cnt = NS_AtomicDecrementRefcnt(mRefCnt);
229 1474501 : NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
230 :
231 1474501 : if (0 == cnt) {
232 106519 : delete this; // also unlinks us from chain
233 106519 : return 0;
234 : }
235 1367982 : if (1 == cnt) {
236 113769 : if (IsValid())
237 113480 : RemoveFromRootSet(rt->GetMapLock());
238 :
239 : // If we are not the root wrapper or if we are not being used from a
240 : // weak reference, then this extra ref is not needed and we can let
241 : // ourself be deleted.
242 : // Note: HasWeakReferences() could only return true for the root.
243 113769 : if (!HasWeakReferences())
244 101016 : goto do_decrement;
245 : }
246 1266966 : return cnt;
247 : }
248 :
249 : void
250 152113 : nsXPCWrappedJS::TraceJS(JSTracer* trc)
251 : {
252 152113 : NS_ASSERTION(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
253 152113 : JS_SET_TRACING_DETAILS(trc, PrintTraceName, this, 0);
254 152113 : JS_CallTracer(trc, GetJSObjectPreserveColor(), JSTRACE_OBJECT);
255 152113 : }
256 :
257 : #ifdef DEBUG
258 : // static
259 : void
260 0 : nsXPCWrappedJS::PrintTraceName(JSTracer* trc, char *buf, size_t bufsize)
261 : {
262 : const nsXPCWrappedJS* self = static_cast<const nsXPCWrappedJS*>
263 0 : (trc->debugPrintArg);
264 : JS_snprintf(buf, bufsize, "nsXPCWrappedJS[%s,0x%p:0x%p].mJSObj",
265 0 : self->GetClass()->GetInterfaceName(), self, self->mXPTCStub);
266 0 : }
267 : #endif
268 :
269 : NS_IMETHODIMP
270 8576 : nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
271 : {
272 8576 : if (mRoot != this)
273 0 : return mRoot->GetWeakReference(aInstancePtr);
274 :
275 8576 : return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
276 : }
277 :
278 : NS_IMETHODIMP
279 58319 : nsXPCWrappedJS::GetJSObject(JSObject** aJSObj)
280 : {
281 58319 : NS_PRECONDITION(aJSObj, "bad param");
282 58319 : NS_PRECONDITION(IsValid(), "bad wrapper");
283 :
284 58319 : if (!(*aJSObj = GetJSObject()))
285 0 : return NS_ERROR_OUT_OF_MEMORY;
286 58319 : return NS_OK;
287 : }
288 :
289 : static bool
290 88800 : CheckMainThreadOnly(nsXPCWrappedJS *aWrapper)
291 : {
292 88800 : if(aWrapper->IsMainThreadOnly())
293 0 : return NS_IsMainThread();
294 :
295 177600 : nsCOMPtr<nsIClassInfo> ci;
296 88800 : CallQueryInterface(aWrapper, getter_AddRefs(ci));
297 88800 : if (ci) {
298 : PRUint32 flags;
299 86 : if (NS_SUCCEEDED(ci->GetFlags(&flags)) && !(flags & nsIClassInfo::MAIN_THREAD_ONLY))
300 86 : return true;
301 :
302 0 : if (!NS_IsMainThread())
303 0 : return false;
304 : }
305 :
306 88714 : aWrapper->SetIsMainThreadOnly();
307 :
308 88714 : return true;
309 : }
310 :
311 : // static
312 : nsresult
313 156775 : nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx,
314 : JSObject* aJSObj,
315 : REFNSIID aIID,
316 : nsISupports* aOuter,
317 : nsXPCWrappedJS** wrapperResult)
318 : {
319 : JSObject2WrappedJSMap* map;
320 : JSObject* rootJSObj;
321 156775 : nsXPCWrappedJS* root = nsnull;
322 156775 : nsXPCWrappedJS* wrapper = nsnull;
323 156775 : nsXPCWrappedJSClass* clazz = nsnull;
324 156775 : XPCJSRuntime* rt = ccx.GetRuntime();
325 156775 : JSBool release_root = false;
326 :
327 156775 : map = rt->GetWrappedJSMap();
328 156775 : if (!map) {
329 0 : NS_ASSERTION(map,"bad map");
330 0 : return NS_ERROR_FAILURE;
331 : }
332 :
333 156775 : nsXPCWrappedJSClass::GetNewOrUsed(ccx, aIID, &clazz);
334 156775 : if (!clazz)
335 0 : return NS_ERROR_FAILURE;
336 : // from here on we need to return through 'return_wrapper'
337 :
338 : // always find the root JSObject
339 156775 : rootJSObj = clazz->GetRootJSObject(ccx, aJSObj);
340 156775 : if (!rootJSObj)
341 0 : goto return_wrapper;
342 :
343 : // look for the root wrapper, and if found, hold the map lock until
344 : // we've added our ref to prevent another thread from destroying it
345 : // under us
346 : { // scoped lock
347 313550 : XPCAutoLock lock(rt->GetMapLock());
348 156775 : root = map->Find(rootJSObj);
349 156775 : if (root) {
350 67975 : if ((nsnull != (wrapper = root->Find(aIID))) ||
351 : (nsnull != (wrapper = root->FindInherited(aIID)))) {
352 49975 : NS_ADDREF(wrapper);
353 : goto return_wrapper;
354 : }
355 : }
356 : }
357 :
358 106800 : if (!root) {
359 : // build the root wrapper
360 88800 : if (rootJSObj == aJSObj) {
361 : // the root will do double duty as the interface wrapper
362 : wrapper = root = new nsXPCWrappedJS(ccx, aJSObj, clazz, nsnull,
363 88800 : aOuter);
364 88800 : if (!root)
365 0 : goto return_wrapper;
366 :
367 : { // scoped lock
368 : #if DEBUG_xpc_leaks
369 : printf("Created nsXPCWrappedJS %p, JSObject is %p\n",
370 : (void*)wrapper, (void*)aJSObj);
371 : #endif
372 177600 : XPCAutoLock lock(rt->GetMapLock());
373 88800 : map->Add(root);
374 : }
375 :
376 88800 : if (!CheckMainThreadOnly(root)) {
377 0 : XPCAutoLock lock(rt->GetMapLock());
378 0 : map->Remove(root);
379 :
380 0 : wrapper = NULL;
381 : }
382 :
383 88800 : goto return_wrapper;
384 : } else {
385 : // just a root wrapper
386 0 : nsXPCWrappedJSClass* rootClazz = nsnull;
387 : nsXPCWrappedJSClass::GetNewOrUsed(ccx, NS_GET_IID(nsISupports),
388 0 : &rootClazz);
389 0 : if (!rootClazz)
390 0 : goto return_wrapper;
391 :
392 0 : root = new nsXPCWrappedJS(ccx, rootJSObj, rootClazz, nsnull, aOuter);
393 0 : NS_RELEASE(rootClazz);
394 :
395 0 : if (!root)
396 0 : goto return_wrapper;
397 :
398 0 : release_root = true;
399 :
400 : { // scoped lock
401 : #if DEBUG_xpc_leaks
402 : printf("Created nsXPCWrappedJS %p, JSObject is %p\n",
403 : (void*)root, (void*)rootJSObj);
404 : #endif
405 0 : XPCAutoLock lock(rt->GetMapLock());
406 0 : map->Add(root);
407 : }
408 :
409 0 : if (!CheckMainThreadOnly(root)) {
410 0 : XPCAutoLock lock(rt->GetMapLock());
411 0 : map->Remove(root);
412 :
413 : goto return_wrapper;
414 : }
415 : }
416 : }
417 :
418 : // at this point we have a root and may need to build the specific wrapper
419 18000 : NS_ASSERTION(root,"bad root");
420 18000 : NS_ASSERTION(clazz,"bad clazz");
421 :
422 18000 : if (!wrapper) {
423 18000 : wrapper = new nsXPCWrappedJS(ccx, aJSObj, clazz, root, aOuter);
424 18000 : if (!wrapper)
425 0 : goto return_wrapper;
426 : #if DEBUG_xpc_leaks
427 : printf("Created nsXPCWrappedJS %p, JSObject is %p\n",
428 : (void*)wrapper, (void*)aJSObj);
429 : #endif
430 : }
431 :
432 18000 : wrapper->mNext = root->mNext;
433 18000 : root->mNext = wrapper;
434 :
435 : return_wrapper:
436 156775 : if (clazz)
437 156775 : NS_RELEASE(clazz);
438 :
439 156775 : if (release_root)
440 0 : NS_RELEASE(root);
441 :
442 156775 : if (!wrapper)
443 0 : return NS_ERROR_FAILURE;
444 :
445 156775 : *wrapperResult = wrapper;
446 156775 : return NS_OK;
447 : }
448 :
449 106800 : nsXPCWrappedJS::nsXPCWrappedJS(XPCCallContext& ccx,
450 : JSObject* aJSObj,
451 : nsXPCWrappedJSClass* aClass,
452 : nsXPCWrappedJS* root,
453 : nsISupports* aOuter)
454 : : mJSObj(aJSObj),
455 : mClass(aClass),
456 : mRoot(root ? root : this),
457 : mNext(nsnull),
458 : mOuter(root ? nsnull : aOuter),
459 106800 : mMainThread(NS_IsMainThread()),
460 213600 : mMainThreadOnly(root && root->mMainThreadOnly)
461 : {
462 : #ifdef DEBUG_stats_jband
463 : static int count = 0;
464 : static const int interval = 10;
465 : if (0 == (++count % interval))
466 : printf("//////// %d instances of nsXPCWrappedJS created\n", count);
467 : #endif
468 :
469 106800 : JS_ASSERT_IF(mMainThreadOnly, mMainThread);
470 :
471 106800 : InitStub(GetClass()->GetIID());
472 :
473 : // intentionally do double addref - see Release().
474 106800 : NS_ADDREF_THIS();
475 106800 : NS_ADDREF_THIS();
476 106800 : NS_ADDREF(aClass);
477 106800 : NS_IF_ADDREF(mOuter);
478 :
479 106800 : if (mRoot != this)
480 18000 : NS_ADDREF(mRoot);
481 :
482 106800 : }
483 :
484 319557 : nsXPCWrappedJS::~nsXPCWrappedJS()
485 : {
486 106519 : NS_PRECONDITION(0 == mRefCnt, "refcounting error");
487 :
488 106519 : if (mRoot == this) {
489 : // Remove this root wrapper from the map
490 88520 : XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
491 88520 : JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
492 88520 : if (map) {
493 177040 : XPCAutoLock lock(rt->GetMapLock());
494 88520 : map->Remove(this);
495 : }
496 : }
497 106519 : Unlink();
498 426076 : }
499 :
500 : void
501 106808 : nsXPCWrappedJS::Unlink()
502 : {
503 106808 : if (IsValid()) {
504 106519 : XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
505 106519 : if (rt) {
506 106519 : if (mRoot == this) {
507 : // remove this root wrapper from the map
508 88520 : JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
509 88520 : if (map) {
510 177040 : XPCAutoLock lock(rt->GetMapLock());
511 88520 : map->Remove(this);
512 : }
513 : }
514 :
515 106519 : if (mRefCnt > 1)
516 289 : RemoveFromRootSet(rt->GetMapLock());
517 : }
518 :
519 106519 : mJSObj = nsnull;
520 : }
521 :
522 106808 : if (mRoot == this) {
523 88744 : ClearWeakReferences();
524 18064 : } else if (mRoot) {
525 : // unlink this wrapper
526 17999 : nsXPCWrappedJS* cur = mRoot;
527 1145 : while (1) {
528 19144 : if (cur->mNext == this) {
529 17999 : cur->mNext = mNext;
530 : break;
531 : }
532 1145 : cur = cur->mNext;
533 1145 : NS_ASSERTION(cur, "failed to find wrapper in its own chain");
534 : }
535 : // let the root go
536 17999 : NS_RELEASE(mRoot);
537 : }
538 :
539 106808 : NS_IF_RELEASE(mClass);
540 106808 : if (mOuter) {
541 0 : XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
542 0 : if (rt->GetThreadRunningGC()) {
543 0 : rt->DeferredRelease(mOuter);
544 0 : mOuter = nsnull;
545 : } else {
546 0 : NS_RELEASE(mOuter);
547 : }
548 : }
549 106808 : }
550 :
551 : nsXPCWrappedJS*
552 686895 : nsXPCWrappedJS::Find(REFNSIID aIID)
553 : {
554 686895 : if (aIID.Equals(NS_GET_IID(nsISupports)))
555 311259 : return mRoot;
556 :
557 631296 : for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
558 477652 : if (aIID.Equals(cur->GetIID()))
559 221992 : return cur;
560 : }
561 :
562 153644 : return nsnull;
563 : }
564 :
565 : // check if asking for an interface that some wrapper in the chain inherits from
566 : nsXPCWrappedJS*
567 153644 : nsXPCWrappedJS::FindInherited(REFNSIID aIID)
568 : {
569 153644 : NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence");
570 :
571 327784 : for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
572 : bool found;
573 176381 : if (NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()->
574 : HasAncestor(&aIID, &found)) && found)
575 2241 : return cur;
576 : }
577 :
578 151403 : return nsnull;
579 : }
580 :
581 : NS_IMETHODIMP
582 0 : nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** info)
583 : {
584 0 : NS_ASSERTION(GetClass(), "wrapper without class");
585 0 : NS_ASSERTION(GetClass()->GetInterfaceInfo(), "wrapper class without interface");
586 :
587 : // Since failing to get this info will crash some platforms(!), we keep
588 : // mClass valid at shutdown time.
589 :
590 0 : if (!(*info = GetClass()->GetInterfaceInfo()))
591 0 : return NS_ERROR_UNEXPECTED;
592 0 : NS_ADDREF(*info);
593 0 : return NS_OK;
594 : }
595 :
596 : NS_IMETHODIMP
597 1225634 : nsXPCWrappedJS::CallMethod(PRUint16 methodIndex,
598 : const XPTMethodDescriptor* info,
599 : nsXPTCMiniVariant* params)
600 : {
601 1225634 : if (!IsValid())
602 64 : return NS_ERROR_UNEXPECTED;
603 1225570 : if (NS_IsMainThread() != mMainThread) {
604 2 : NS_NAMED_LITERAL_STRING(kFmt, "Attempt to use JS function on a different thread calling %s.%s. JS objects may not be shared across threads.");
605 : PRUnichar* msg =
606 : nsTextFormatter::smprintf(kFmt.get(),
607 : GetClass()->GetInterfaceName(),
608 1 : info->name);
609 : nsCOMPtr<nsIConsoleService> cs =
610 2 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
611 1 : if (cs)
612 1 : cs->LogStringMessage(msg);
613 1 : NS_Free(msg);
614 :
615 1 : return NS_ERROR_NOT_SAME_THREAD;
616 : }
617 1225569 : return GetClass()->CallMethod(this, methodIndex, info, params);
618 : }
619 :
620 : NS_IMETHODIMP
621 0 : nsXPCWrappedJS::GetInterfaceIID(nsIID** iid)
622 : {
623 0 : NS_PRECONDITION(iid, "bad param");
624 :
625 0 : *iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID));
626 0 : return *iid ? NS_OK : NS_ERROR_UNEXPECTED;
627 : }
628 :
629 : void
630 276 : nsXPCWrappedJS::SystemIsBeingShutDown(JSRuntime* rt)
631 : {
632 : // XXX It turns out that it is better to leak here then to do any Releases
633 : // and have them propagate into all sorts of mischief as the system is being
634 : // shutdown. This was learned the hard way :(
635 :
636 : // mJSObj == nsnull is used to indicate that the wrapper is no longer valid
637 : // and that calls should fail without trying to use any of the
638 : // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
639 :
640 : // NOTE: that mClass is retained so that GetInterfaceInfo can continue to
641 : // work (and avoid crashing some platforms).
642 276 : mJSObj = nsnull;
643 :
644 : // Notify other wrappers in the chain.
645 276 : if (mNext)
646 1 : mNext->SystemIsBeingShutDown(rt);
647 276 : }
648 :
649 : /***************************************************************************/
650 :
651 : /* readonly attribute nsISimpleEnumerator enumerator; */
652 : NS_IMETHODIMP
653 0 : nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate)
654 : {
655 0 : XPCCallContext ccx(NATIVE_CALLER);
656 0 : if (!ccx.IsValid())
657 0 : return NS_ERROR_UNEXPECTED;
658 :
659 : return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(),
660 0 : aEnumerate);
661 : }
662 :
663 : /* nsIVariant getProperty (in AString name); */
664 : NS_IMETHODIMP
665 6 : nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant **_retval)
666 : {
667 12 : XPCCallContext ccx(NATIVE_CALLER);
668 6 : if (!ccx.IsValid())
669 0 : return NS_ERROR_UNEXPECTED;
670 :
671 : return nsXPCWrappedJSClass::
672 6 : GetNamedPropertyAsVariant(ccx, GetJSObject(), name, _retval);
673 : }
674 :
675 : /***************************************************************************/
676 :
677 : NS_IMETHODIMP
678 0 : nsXPCWrappedJS::DebugDump(PRInt16 depth)
679 : {
680 : #ifdef DEBUG
681 0 : XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get()));
682 0 : XPC_LOG_INDENT();
683 :
684 0 : bool isRoot = mRoot == this;
685 0 : XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \
686 : isRoot ? "ROOT":"non-root", mJSObj));
687 : char* name;
688 0 : GetClass()->GetInterfaceInfo()->GetName(&name);
689 0 : XPC_LOG_ALWAYS(("interface name is %s", name));
690 0 : if (name)
691 0 : nsMemory::Free(name);
692 0 : char * iid = GetClass()->GetIID().ToString();
693 0 : XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
694 0 : if (iid)
695 0 : NS_Free(iid);
696 0 : XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x", mClass));
697 :
698 0 : if (!isRoot)
699 0 : XPC_LOG_OUTDENT();
700 0 : if (mNext) {
701 0 : if (isRoot) {
702 0 : XPC_LOG_ALWAYS(("Additional wrappers for this object..."));
703 0 : XPC_LOG_INDENT();
704 : }
705 0 : mNext->DebugDump(depth);
706 0 : if (isRoot)
707 0 : XPC_LOG_OUTDENT();
708 : }
709 0 : if (isRoot)
710 0 : XPC_LOG_OUTDENT();
711 : #endif
712 0 : return NS_OK;
713 4392 : }
|