Back to index

lightning-sunbird  0.9+nobinonly
xpcwrappedjs.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   John Bandhauer <jband@netscape.com> (original author)
00026  *   Pierre Phaneuf <pp@ludusdesign.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 /* Class that wraps JS objects to appear as XPCOM objects. */
00043 
00044 #include "xpcprivate.h"
00045 
00046 // NOTE: much of the fancy footwork is done in xpcstubs.cpp
00047 
00048 NS_IMETHODIMP
00049 nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
00050 {
00051     NS_ASSERTION(IsAggregatedToNative(), "bad AggregatedQueryInterface call");
00052 
00053     if(!IsValid())
00054         return NS_ERROR_UNEXPECTED;
00055 
00056     // Put this here rather that in DelegatedQueryInterface because it needs
00057     // to be in QueryInterface before the possible delegation to 'outer', but
00058     // we don't want to do this check twice in one call in the normal case:
00059     // once in QueryInterface and once in DelegatedQueryInterface.
00060     if(aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS)) ||
00061        aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS_MOZILLA_1_8_BRANCH)))
00062     {
00063         NS_ADDREF(this);
00064         *aInstancePtr = (void*) NS_STATIC_CAST(nsIXPConnectWrappedJS_MOZILLA_1_8_BRANCH*,this);
00065         return NS_OK;
00066     }
00067 
00068     return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
00069 }
00070 
00071 NS_IMETHODIMP
00072 nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00073 {
00074     if(!IsValid())
00075         return NS_ERROR_UNEXPECTED;
00076 
00077     if(nsnull == aInstancePtr)
00078     {
00079         NS_PRECONDITION(0, "null pointer");
00080         return NS_ERROR_NULL_POINTER;
00081     }
00082 
00083     // Always check for this first so that our 'outer' can get this interface
00084     // from us without recurring into a call to the outer's QI!
00085     if(aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS)) ||
00086        aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS_MOZILLA_1_8_BRANCH)))
00087     {
00088         NS_ADDREF(this);
00089         *aInstancePtr = (void*) NS_STATIC_CAST(nsIXPConnectWrappedJS_MOZILLA_1_8_BRANCH*,this);
00090         return NS_OK;
00091     }
00092 
00093     // This interface is a hack that says "don't AddRef me".
00094     if(aIID.Equals(NS_GET_IID(nsWeakRefToIXPConnectWrappedJS)))
00095     {
00096         *aInstancePtr = NS_STATIC_CAST(nsWeakRefToIXPConnectWrappedJS*, this);
00097         return NS_OK;
00098     }
00099 
00100     nsISupports* outer = GetAggregatedNativeObject();
00101     if(outer)
00102         return outer->QueryInterface(aIID, aInstancePtr);
00103 
00104     // else...
00105 
00106     return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
00107 }
00108 
00109 
00110 // Refcounting is now similar to that used in the chained (pre-flattening)
00111 // wrappednative system.
00112 //
00113 // We are now holding an extra refcount for nsISupportsWeakReference support.
00114 //
00115 // Non-root wrappers remove themselves from the chain in their destructors.
00116 // We root the JSObject as the refcount transitions from 1->2. And we unroot
00117 // the JSObject when the refcount transitions from 2->1.
00118 //
00119 // When the transition from 2->1 is made and no one holds a weak ref to the
00120 // (aggregated) object then we decrement the refcount again to 0 (and
00121 // destruct) . However, if a weak ref is held at the 2->1 transition, then we
00122 // leave the refcount at 1 to indicate that state. This leaves the JSObject
00123 // no longer rooted by us and (as far as we know) subject to possible
00124 // collection. Code in XPCJSRuntime watches for JS gc to happen and will do
00125 // the final release on wrappers whose JSObjects get finalized. Note that
00126 // even after tranistioning to this refcount-of-one state callers might do
00127 // an addref and cause us to re-root the JSObject and continue on more normally.
00128 
00129 nsrefcnt
00130 nsXPCWrappedJS::AddRef(void)
00131 {
00132     NS_PRECONDITION(mRoot, "bad root");
00133     nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt);
00134     NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
00135 
00136     if(2 == cnt && IsValid())
00137     {
00138         XPCCallContext ccx(NATIVE_CALLER);
00139         if(ccx.IsValid()) {
00140 #ifdef GC_MARK_DEBUG
00141             mGCRootName = JS_smprintf("nsXPCWrappedJS::mJSObj[%s,0x%p,0x%p]",
00142                                       GetClass()->GetInterfaceName(),
00143                                       this, mJSObj);
00144             JS_AddNamedRoot(ccx.GetJSContext(), &mJSObj, mGCRootName);
00145 #else
00146             JS_AddNamedRoot(ccx.GetJSContext(), &mJSObj, "nsXPCWrappedJS::mJSObj");
00147 #endif
00148         }
00149     }
00150 
00151     return cnt;
00152 }
00153 
00154 nsrefcnt
00155 nsXPCWrappedJS::Release(void)
00156 {
00157     NS_PRECONDITION(mRoot, "bad root");
00158     NS_PRECONDITION(0 != mRefCnt, "dup release");
00159 
00160 #ifdef DEBUG_jband
00161     NS_ASSERTION(IsValid(), "post xpconnect shutdown call of nsXPCWrappedJS::Release");
00162 #endif
00163 
00164 do_decrement:
00165 
00166     nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt);
00167     NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
00168 
00169     if(0 == cnt)
00170     {
00171         NS_DELETEXPCOM(this);   // also unlinks us from chain
00172         return 0;
00173     }
00174     if(1 == cnt)
00175     {
00176         if(IsValid())
00177         {
00178             XPCJSRuntime* rt = mClass->GetRuntime();
00179             if(rt) {
00180                 JS_RemoveRootRT(rt->GetJSRuntime(), &mJSObj);
00181 #ifdef GC_MARK_DEBUG
00182                 JS_smprintf_free(mGCRootName);
00183                 mGCRootName = nsnull;
00184 #endif
00185             }
00186         }
00187 
00188         // If we are not the root wrapper or if we are not being used from a
00189         // weak reference, then this extra ref is not needed and we can let
00190         // ourself be deleted.
00191         // Note: HasWeakReferences() could only return true for the root.
00192         if(!HasWeakReferences())
00193             goto do_decrement;
00194     }
00195     return cnt;
00196 }
00197 
00198 NS_IMETHODIMP
00199 nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
00200 {
00201     return mRoot->nsSupportsWeakReference::GetWeakReference(aInstancePtr);
00202 }
00203 
00204 NS_IMETHODIMP
00205 nsXPCWrappedJS::GetJSObject(JSObject** aJSObj)
00206 {
00207     NS_PRECONDITION(aJSObj, "bad param");
00208     NS_PRECONDITION(mJSObj, "bad wrapper");
00209     if(!(*aJSObj = mJSObj))
00210         return NS_ERROR_OUT_OF_MEMORY;
00211     return NS_OK;
00212 }
00213 
00214 // static
00215 nsresult
00216 nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx,
00217                              JSObject* aJSObj,
00218                              REFNSIID aIID,
00219                              nsISupports* aOuter,
00220                              nsXPCWrappedJS** wrapperResult)
00221 {
00222     JSObject2WrappedJSMap* map;
00223     JSObject* rootJSObj;
00224     nsXPCWrappedJS* root;
00225     nsXPCWrappedJS* wrapper = nsnull;
00226     nsXPCWrappedJSClass* clazz = nsnull;
00227     XPCJSRuntime* rt = ccx.GetRuntime();
00228 
00229     map = rt->GetWrappedJSMap();
00230     if(!map)
00231     {
00232         NS_ASSERTION(map,"bad map");
00233         return NS_ERROR_FAILURE;
00234     }
00235 
00236     nsXPCWrappedJSClass::GetNewOrUsed(ccx, aIID, &clazz);
00237     if(!clazz)
00238         return NS_ERROR_FAILURE;
00239     // from here on we need to return through 'return_wrapper'
00240 
00241     // always find the root JSObject
00242     rootJSObj = clazz->GetRootJSObject(ccx, aJSObj);
00243     if(!rootJSObj)
00244         goto return_wrapper;
00245 
00246     // look for the root wrapper
00247     {   // scoped lock
00248         XPCAutoLock lock(rt->GetMapLock());
00249         root = map->Find(rootJSObj);
00250     }
00251     if(root)
00252     {
00253         if((nsnull != (wrapper = root->Find(aIID))) ||
00254            (nsnull != (wrapper = root->FindInherited(aIID))))
00255         {
00256             NS_ADDREF(wrapper);
00257             goto return_wrapper;
00258         }
00259     }
00260     else
00261     {
00262         // build the root wrapper
00263         if(rootJSObj == aJSObj)
00264         {
00265             // the root will do double duty as the interface wrapper
00266             wrapper = root = new nsXPCWrappedJS(ccx, aJSObj, clazz, nsnull,
00267                                                 aOuter);
00268             if(root)
00269             {   // scoped lock
00270                 XPCAutoLock lock(rt->GetMapLock());
00271                 map->Add(root);
00272             }
00273             goto return_wrapper;
00274         }
00275         else
00276         {
00277             // just a root wrapper
00278             nsXPCWrappedJSClass* rootClazz = nsnull;
00279             nsXPCWrappedJSClass::GetNewOrUsed(ccx, NS_GET_IID(nsISupports),
00280                                               &rootClazz);
00281             if(!rootClazz)
00282                 goto return_wrapper;
00283 
00284             root = new nsXPCWrappedJS(ccx, rootJSObj, rootClazz, nsnull, aOuter);
00285             NS_RELEASE(rootClazz);
00286 
00287             if(!root)
00288                 goto return_wrapper;
00289             {   // scoped lock
00290                 XPCAutoLock lock(rt->GetMapLock());
00291                 map->Add(root);
00292             }
00293         }
00294     }
00295 
00296     // at this point we have a root and may need to build the specific wrapper
00297     NS_ASSERTION(root,"bad root");
00298     NS_ASSERTION(clazz,"bad clazz");
00299 
00300     if(!wrapper)
00301     {
00302         wrapper = new nsXPCWrappedJS(ccx, aJSObj, clazz, root, aOuter);
00303         if(!wrapper)
00304             goto return_wrapper;
00305     }
00306 
00307     wrapper->mNext = root->mNext;
00308     root->mNext = wrapper;
00309 
00310 return_wrapper:
00311     if(clazz)
00312         NS_RELEASE(clazz);
00313 
00314     if(!wrapper)
00315         return NS_ERROR_FAILURE;
00316 
00317     *wrapperResult = wrapper;
00318     return NS_OK;
00319 }
00320 
00321 nsXPCWrappedJS::nsXPCWrappedJS(XPCCallContext& ccx,
00322                                JSObject* aJSObj,
00323                                nsXPCWrappedJSClass* aClass,
00324                                nsXPCWrappedJS* root,
00325                                nsISupports* aOuter)
00326     : mJSObj(aJSObj),
00327       mClass(aClass),
00328       mRoot(root ? root : this),
00329       mNext(nsnull),
00330       mOuter(root ? nsnull : aOuter)
00331 #ifdef GC_MARK_DEBUG
00332       , mGCRootName(nsnull)
00333 #endif
00334 {
00335 #ifdef DEBUG_stats_jband
00336     static int count = 0;
00337     static const int interval = 10;
00338     if(0 == (++count % interval))
00339         printf("//////// %d instances of nsXPCWrappedJS created\n", count);
00340 #endif
00341 
00342     // intensionally do double addref - see Release().
00343     NS_ADDREF_THIS();
00344     NS_ADDREF_THIS();
00345     NS_ADDREF(aClass);
00346     NS_IF_ADDREF(mOuter);
00347 
00348     if(mRoot != this)
00349         NS_ADDREF(mRoot);
00350 
00351 }
00352 
00353 nsXPCWrappedJS::~nsXPCWrappedJS()
00354 {
00355     NS_PRECONDITION(0 == mRefCnt, "refcounting error");
00356 
00357     XPCJSRuntime* rt = nsXPConnect::GetRuntime();
00358 
00359     if(mRoot != this)
00360     {
00361         // unlink this wrapper
00362         nsXPCWrappedJS* cur = mRoot;
00363         while(1)
00364         {
00365             if(cur->mNext == this)
00366             {
00367                 cur->mNext = mNext;
00368                 break;
00369             }
00370             cur = cur->mNext;
00371             NS_ASSERTION(cur, "failed to find wrapper in its own chain");
00372         }
00373         // let the root go
00374         NS_RELEASE(mRoot);
00375     }
00376     else
00377     {
00378         NS_ASSERTION(!mNext, "root wrapper with non-empty chain being deleted");
00379 
00380         // Let the nsWeakReference object (if present) know of our demise.
00381         ClearWeakReferences();
00382 
00383         // remove this root wrapper from the map
00384         if(rt)
00385         {
00386             JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
00387             if(map)
00388             {
00389                 XPCAutoLock lock(rt->GetMapLock());
00390                 map->Remove(this);
00391             }
00392         }
00393     }
00394 
00395     if(IsValid())
00396     {
00397         NS_IF_RELEASE(mClass);
00398         if (mOuter)
00399         {
00400             if (rt && rt->GetThreadRunningGC())
00401             {
00402                 rt->DeferredRelease(mOuter);
00403                 mOuter = nsnull;
00404             }
00405             else
00406             {
00407                 NS_RELEASE(mOuter);
00408             }
00409         }
00410     }
00411 }
00412 
00413 nsXPCWrappedJS*
00414 nsXPCWrappedJS::Find(REFNSIID aIID)
00415 {
00416     if(aIID.Equals(NS_GET_IID(nsISupports)))
00417         return mRoot;
00418 
00419     for(nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext)
00420     {
00421         if(aIID.Equals(cur->GetIID()))
00422             return cur;
00423     }
00424 
00425     return nsnull;
00426 }
00427 
00428 // check if asking for an interface that some wrapper in the chain inherits from
00429 nsXPCWrappedJS*
00430 nsXPCWrappedJS::FindInherited(REFNSIID aIID)
00431 {
00432     NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence");
00433 
00434     for(nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext)
00435     {
00436         PRBool found;
00437         if(NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()->
00438                                 HasAncestor(&aIID, &found)) && found)
00439             return cur;
00440     }
00441 
00442     return nsnull;
00443 }
00444 
00445 NS_IMETHODIMP
00446 nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** info)
00447 {
00448     NS_ASSERTION(GetClass(), "wrapper without class");
00449     NS_ASSERTION(GetClass()->GetInterfaceInfo(), "wrapper class without interface");
00450 
00451     // Since failing to get this info will crash some platforms(!), we keep
00452     // mClass valid at shutdown time.
00453 
00454     if(!(*info = GetClass()->GetInterfaceInfo()))
00455         return NS_ERROR_UNEXPECTED;
00456     NS_ADDREF(*info);
00457     return NS_OK;
00458 }
00459 
00460 NS_IMETHODIMP
00461 nsXPCWrappedJS::CallMethod(PRUint16 methodIndex,
00462                            const nsXPTMethodInfo* info,
00463                            nsXPTCMiniVariant* params)
00464 {
00465     if(!IsValid())
00466         return NS_ERROR_UNEXPECTED;
00467     return GetClass()->CallMethod(this, methodIndex, info, params);
00468 }
00469 
00470 NS_IMETHODIMP
00471 nsXPCWrappedJS::GetInterfaceIID(nsIID** iid)
00472 {
00473     NS_PRECONDITION(iid, "bad param");
00474 
00475     *iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID));
00476     return *iid ? NS_OK : NS_ERROR_UNEXPECTED;
00477 }
00478 
00479 void
00480 nsXPCWrappedJS::SystemIsBeingShutDown(JSRuntime* rt)
00481 {
00482     // XXX It turns out that it is better to leak here then to do any Releases
00483     // and have them propagate into all sorts of mischief as the system is being
00484     // shutdown. This was learned the hard way :(
00485 
00486     // mJSObj == nsnull is used to indicate that the wrapper is no longer valid
00487     // and that calls should fail without trying to use any of the
00488     // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
00489 
00490     // NOTE: that mClass is retained so that GetInterfaceInfo can continue to
00491     // work (and avoid crashing some platforms).
00492     mJSObj = nsnull;
00493 
00494     // There is no reason to keep this root any longer. Since we've cleared
00495     // mJSObj our dtor will not remove the root later. So, we do it now.
00496     JS_RemoveRootRT(rt, &mJSObj);
00497 
00498     // Notify other wrappers in the chain.
00499     if(mNext)
00500         mNext->SystemIsBeingShutDown(rt);
00501 }
00502 
00503 /***************************************************************************/
00504 
00505 /* readonly attribute nsISimpleEnumerator enumerator; */
00506 NS_IMETHODIMP 
00507 nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate)
00508 {
00509     XPCCallContext ccx(NATIVE_CALLER);
00510     if(!ccx.IsValid())
00511         return NS_ERROR_UNEXPECTED;
00512 
00513     return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, mJSObj, aEnumerate);
00514 }
00515 
00516 /* nsIVariant getProperty (in AString name); */
00517 NS_IMETHODIMP 
00518 nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant **_retval)
00519 {
00520     XPCCallContext ccx(NATIVE_CALLER);
00521     if(!ccx.IsValid())
00522         return NS_ERROR_UNEXPECTED;
00523 
00524     JSString* jsstr = XPCStringConvert::ReadableToJSString(ccx, name);
00525     if(!jsstr)
00526         return NS_ERROR_OUT_OF_MEMORY;
00527 
00528     return nsXPCWrappedJSClass::
00529         GetNamedPropertyAsVariant(ccx, mJSObj, STRING_TO_JSVAL(jsstr), _retval);
00530 }
00531 
00532 /***************************************************************************/
00533 
00534 NS_IMETHODIMP
00535 nsXPCWrappedJS::DebugDump(PRInt16 depth)
00536 {
00537 #ifdef DEBUG
00538     XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get()));
00539         XPC_LOG_INDENT();
00540 
00541         PRBool isRoot = mRoot == this;
00542         XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \
00543                          isRoot ? "ROOT":"non-root", mJSObj));
00544         char* name;
00545         GetClass()->GetInterfaceInfo()->GetName(&name);
00546         XPC_LOG_ALWAYS(("interface name is %s", name));
00547         if(name)
00548             nsMemory::Free(name);
00549         char * iid = GetClass()->GetIID().ToString();
00550         XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
00551         if(iid)
00552             PR_Free(iid);
00553         XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x", mClass));
00554 
00555         if(!isRoot)
00556             XPC_LOG_OUTDENT();
00557         if(mNext)
00558         {
00559             if(isRoot)
00560             {
00561                 XPC_LOG_ALWAYS(("Additional wrappers for this object..."));
00562                 XPC_LOG_INDENT();
00563             }
00564             mNext->DebugDump(depth);
00565             if(isRoot)
00566                 XPC_LOG_OUTDENT();
00567         }
00568         if(isRoot)
00569             XPC_LOG_OUTDENT();
00570 #endif
00571     return NS_OK;
00572 }