Back to index

lightning-sunbird  0.9+nobinonly
xpcwrappedjsclass.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) 1999
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 /* Sharable code and data for wrapper around JSObjects. */
00043 
00044 #include "xpcprivate.h"
00045 
00046 NS_IMPL_THREADSAFE_ISUPPORTS1(nsXPCWrappedJSClass, nsIXPCWrappedJSClass)
00047 
00048 // the value of this variable is never used - we use its address as a sentinel
00049 static uint32 zero_methods_descriptor;
00050 
00051 void AutoScriptEvaluate::StartEvaluating(JSErrorReporter errorReporter)
00052 {
00053     NS_PRECONDITION(!mEvaluated, "AutoScriptEvaluate::Evaluate should only be called once");
00054 
00055     if(!mJSContext)
00056         return;
00057     mEvaluated = PR_TRUE;
00058     mOldErrorReporter = JS_SetErrorReporter(mJSContext, errorReporter);
00059     mContextHasThread = JS_GetContextThread(mJSContext);
00060     if (mContextHasThread)
00061         JS_BeginRequest(mJSContext);
00062 
00063     // Saving the exception state keeps us from interfering with another script
00064     // that may also be running on this context.  This occurred first with the
00065     // js debugger, as described in
00066     // http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could
00067     // show up in any situation where a script calls into a wrapped js component
00068     // on the same context, while the context has a nonzero exception state.
00069     // Because JS_SaveExceptionState/JS_RestoreExceptionState use malloc
00070     // and addroot, we avoid them if possible by returning null (as opposed to
00071     // a JSExceptionState with no information) when there is no pending
00072     // exception.
00073     if(JS_IsExceptionPending(mJSContext))
00074     {
00075         mState = JS_SaveExceptionState(mJSContext);
00076         JS_ClearPendingException(mJSContext);
00077     }
00078 }
00079 
00080 AutoScriptEvaluate::~AutoScriptEvaluate()
00081 {
00082     if(!mJSContext || !mEvaluated)
00083         return;
00084     if(mState)
00085         JS_RestoreExceptionState(mJSContext, mState);
00086     else
00087         JS_ClearPendingException(mJSContext);
00088 
00089     if(mContextHasThread)
00090         JS_EndRequest(mJSContext);
00091 
00092     // If this is a JSContext that has a private context that provides a
00093     // nsIXPCScriptNotify interface, then notify the object the script has
00094     // been executed.
00095     //
00096     // Note: We rely on the rule that if any JSContext in our JSRuntime has
00097     // private data that points to an nsISupports subclass, it has also set
00098     // the JSOPTION_PRIVATE_IS_NSISUPPORTS option.
00099 
00100     if (JS_GetOptions(mJSContext) & JSOPTION_PRIVATE_IS_NSISUPPORTS)
00101     {
00102         nsCOMPtr<nsIXPCScriptNotify> scriptNotify = 
00103             do_QueryInterface(NS_STATIC_CAST(nsISupports*,
00104                                              JS_GetContextPrivate(mJSContext)));
00105         if(scriptNotify)
00106             scriptNotify->ScriptExecuted();
00107     }
00108     JS_SetErrorReporter(mJSContext, mOldErrorReporter);
00109 }
00110 
00111 // It turns out that some errors may be not worth reporting. So, this
00112 // function is factored out to manage that.
00113 JSBool xpc_IsReportableErrorCode(nsresult code)
00114 {
00115     if(NS_SUCCEEDED(code))
00116         return JS_FALSE;
00117 
00118     switch(code)
00119     {
00120         // Error codes that we don't want to report as errors...
00121         // These generally indicate bad interface design AFAIC. 
00122         case NS_ERROR_FACTORY_REGISTER_AGAIN:
00123         case NS_BASE_STREAM_WOULD_BLOCK:
00124             return JS_FALSE;
00125     }
00126     return JS_TRUE;
00127 }
00128 
00129 // static
00130 nsresult
00131 nsXPCWrappedJSClass::GetNewOrUsed(XPCCallContext& ccx, REFNSIID aIID,
00132                                   nsXPCWrappedJSClass** resultClazz)
00133 {
00134     nsXPCWrappedJSClass* clazz = nsnull;
00135     XPCJSRuntime* rt = ccx.GetRuntime();
00136 
00137     {   // scoped lock
00138         XPCAutoLock lock(rt->GetMapLock());
00139         IID2WrappedJSClassMap* map = rt->GetWrappedJSClassMap();
00140         clazz = map->Find(aIID);
00141         NS_IF_ADDREF(clazz);
00142     }
00143 
00144     if(!clazz)
00145     {
00146         nsCOMPtr<nsIInterfaceInfo> info;
00147         ccx.GetXPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
00148         if(info)
00149         {
00150             PRBool canScript;
00151             if(NS_SUCCEEDED(info->IsScriptable(&canScript)) && canScript &&
00152                nsXPConnect::IsISupportsDescendant(info))
00153             {
00154                 clazz = new nsXPCWrappedJSClass(ccx, aIID, info);
00155                 if(clazz && !clazz->mDescriptors)
00156                     NS_RELEASE(clazz);  // sets clazz to nsnull
00157             }
00158         }
00159     }
00160     *resultClazz = clazz;
00161     return NS_OK;
00162 }
00163 
00164 nsXPCWrappedJSClass::nsXPCWrappedJSClass(XPCCallContext& ccx, REFNSIID aIID,
00165                                          nsIInterfaceInfo* aInfo)
00166     : mRuntime(ccx.GetRuntime()),
00167       mInfo(aInfo),
00168       mName(nsnull),
00169       mIID(aIID),
00170       mDescriptors(nsnull)
00171 {
00172     NS_ADDREF(mInfo);
00173     NS_ADDREF_THIS();
00174 
00175     {   // scoped lock
00176         XPCAutoLock lock(mRuntime->GetMapLock());
00177         mRuntime->GetWrappedJSClassMap()->Add(this);
00178     }
00179 
00180     uint16 methodCount;
00181     if(NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount)))
00182     {
00183         if(methodCount)
00184         {
00185             int wordCount = (methodCount/32)+1;
00186             if(nsnull != (mDescriptors = new uint32[wordCount]))
00187             {
00188                 int i;
00189                 // init flags to 0;
00190                 for(i = wordCount-1; i >= 0; i--)
00191                     mDescriptors[i] = 0;
00192 
00193                 for(i = 0; i < methodCount; i++)
00194                 {
00195                     const nsXPTMethodInfo* info;
00196                     if(NS_SUCCEEDED(mInfo->GetMethodInfo(i, &info)))
00197                         SetReflectable(i, XPCConvert::IsMethodReflectable(*info));
00198                     else
00199                     {
00200                         delete [] mDescriptors;
00201                         mDescriptors = nsnull;
00202                         break;
00203                     }
00204                 }
00205             }
00206         }
00207         else
00208         {
00209             mDescriptors = &zero_methods_descriptor;
00210         }
00211     }
00212 }
00213 
00214 nsXPCWrappedJSClass::~nsXPCWrappedJSClass()
00215 {
00216     if(mDescriptors && mDescriptors != &zero_methods_descriptor)
00217         delete [] mDescriptors;
00218     if(mRuntime)
00219     {   // scoped lock
00220         XPCAutoLock lock(mRuntime->GetMapLock());
00221         mRuntime->GetWrappedJSClassMap()->Remove(this);
00222     }
00223     if(mName)
00224         nsMemory::Free(mName);
00225     NS_IF_RELEASE(mInfo);
00226 }
00227 
00228 JSObject*
00229 nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(XPCCallContext& ccx,
00230                                                   JSObject* jsobj,
00231                                                   REFNSIID aIID)
00232 {
00233     JSContext* cx = ccx.GetJSContext();
00234     JSObject* id;
00235     jsval retval;
00236     JSObject* retObj;
00237     JSBool success = JS_FALSE;
00238     jsid funid;
00239     jsval fun;
00240 
00241     // check upfront for the existence of the function property
00242     funid = mRuntime->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE);
00243     if(!OBJ_GET_PROPERTY(cx, jsobj, funid, &fun) || JSVAL_IS_PRIMITIVE(fun))
00244         return nsnull;
00245 
00246     // protect fun so that we're sure it's alive when we call it
00247     AUTO_MARK_JSVAL(ccx, fun);
00248 
00249     // Ensure that we are asking for a scriptable interface.
00250     // NB:  It's important for security that this check is here rather
00251     // than later, since it prevents untrusted objects from implementing
00252     // some interfaces in JS and aggregating a trusted object to
00253     // implement intentionally (for security) unscriptable interfaces.
00254     // We so often ask for nsISupports that we can short-circuit the test...
00255     if(!aIID.Equals(NS_GET_IID(nsISupports)))
00256     {
00257         nsCOMPtr<nsIInterfaceInfo> info;
00258         ccx.GetXPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
00259         if(!info)
00260             return nsnull;
00261         PRBool canScript;
00262         if(NS_FAILED(info->IsScriptable(&canScript)) || !canScript)
00263             return nsnull;
00264     }
00265 
00266     // OK, it looks like we'll be calling into JS code.
00267 
00268     AutoScriptEvaluate scriptEval(cx);
00269 
00270     // XXX we should install an error reporter that will send reports to
00271     // the JS error console service.
00272     scriptEval.StartEvaluating();
00273 
00274     id = xpc_NewIDObject(cx, jsobj, aIID);
00275     if(id)
00276     {
00277         jsval args[1] = {OBJECT_TO_JSVAL(id)};
00278         success = JS_CallFunctionValue(cx, jsobj, fun, 1, args, &retval);
00279     }
00280 
00281     if(success)
00282         success = JS_ValueToObject(cx, retval, &retObj);
00283 
00284     return success ? retObj : nsnull;
00285 }
00286 
00287 /***************************************************************************/
00288 
00289 static JSBool 
00290 GetNamedPropertyAsVariantRaw(XPCCallContext& ccx, 
00291                              JSObject* aJSObj,
00292                              jsid aName, 
00293                              nsIVariant** aResult,
00294                              nsresult* pErr)
00295 {
00296     nsXPTType type = nsXPTType((uint8)(TD_INTERFACE_TYPE | XPT_TDP_POINTER));
00297     jsval val;
00298 
00299     return OBJ_GET_PROPERTY(ccx, aJSObj, aName, &val) &&
00300            XPCConvert::JSData2Native(ccx, aResult, val, type, JS_FALSE, 
00301                                      &NS_GET_IID(nsIVariant), pErr);
00302 }
00303 
00304 // static
00305 nsresult
00306 nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx, 
00307                                                JSObject* aJSObj,
00308                                                jsval aName, 
00309                                                nsIVariant** aResult)
00310 {
00311     JSContext* cx = ccx.GetJSContext();
00312     JSBool ok;
00313     jsid id;
00314     nsresult rv;
00315 
00316     AutoScriptEvaluate scriptEval(cx);
00317     scriptEval.StartEvaluating();
00318 
00319     ok = JS_ValueToId(cx, aName, &id) && 
00320          GetNamedPropertyAsVariantRaw(ccx, aJSObj, id, aResult, &rv);
00321 
00322     return ok ? NS_OK : NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
00323 }
00324 
00325 /***************************************************************************/
00326 
00327 // static 
00328 nsresult 
00329 nsXPCWrappedJSClass::BuildPropertyEnumerator(XPCCallContext& ccx,
00330                                              JSObject* aJSObj,
00331                                              nsISimpleEnumerator** aEnumerate)
00332 {
00333     JSContext* cx = ccx.GetJSContext();
00334     nsresult retval = NS_ERROR_FAILURE;
00335     JSIdArray* idArray = nsnull;
00336     xpcPropertyBagEnumerator* enumerator = nsnull;
00337     int i;
00338 
00339     // Saved state must be restored, all exits through 'out'...
00340     AutoScriptEvaluate scriptEval(cx);
00341     scriptEval.StartEvaluating();
00342 
00343     idArray = JS_Enumerate(cx, aJSObj);
00344     if(!idArray)
00345         goto out;
00346     
00347     enumerator = new xpcPropertyBagEnumerator(idArray->length);
00348     if(!enumerator)
00349         goto out;
00350     NS_ADDREF(enumerator);
00351         
00352     for(i = 0; i < idArray->length; i++)
00353     {
00354         nsCOMPtr<nsIVariant> value;
00355         jsid idName = idArray->vector[i];
00356         nsresult rv;
00357 
00358         if(!GetNamedPropertyAsVariantRaw(ccx, aJSObj, idName, 
00359                                          getter_AddRefs(value), &rv))
00360         {
00361             if(NS_FAILED(rv))
00362                 retval = rv;                                        
00363             goto out;
00364         }
00365 
00366         jsval jsvalName;
00367         if(!JS_IdToValue(cx, idName, &jsvalName))
00368             goto out;
00369 
00370         JSString* name = JS_ValueToString(cx, jsvalName);
00371         if(!name)
00372             goto out;
00373 
00374         nsCOMPtr<nsIProperty> property = 
00375             new xpcProperty((const PRUnichar*) JS_GetStringChars(name), 
00376                             (PRUint32) JS_GetStringLength(name),
00377                             value);
00378         if(!property)
00379             goto out;
00380 
00381         if(!enumerator->AppendElement(property))
00382             goto out;
00383     }
00384 
00385     NS_ADDREF(*aEnumerate = enumerator);
00386     retval = NS_OK;
00387 
00388 out:
00389     NS_IF_RELEASE(enumerator);
00390     if(idArray)
00391         JS_DestroyIdArray(cx, idArray);
00392 
00393     return retval;
00394 }
00395 
00396 /***************************************************************************/
00397 
00398 NS_IMPL_ISUPPORTS1(xpcProperty, nsIProperty)
00399 
00400 xpcProperty::xpcProperty(const PRUnichar* aName, PRUint32 aNameLen, 
00401                          nsIVariant* aValue)
00402     : mName(aName, aNameLen), mValue(aValue)
00403 {
00404 }
00405 
00406 /* readonly attribute AString name; */
00407 NS_IMETHODIMP xpcProperty::GetName(nsAString & aName)
00408 {
00409     aName.Assign(mName);
00410     return NS_OK;
00411 }
00412 
00413 /* readonly attribute nsIVariant value; */
00414 NS_IMETHODIMP xpcProperty::GetValue(nsIVariant * *aValue)
00415 {
00416     NS_ADDREF(*aValue = mValue);
00417     return NS_OK;
00418 }
00419 
00420 /***************************************************************************/
00421 
00422 NS_IMPL_ISUPPORTS1(xpcPropertyBagEnumerator, nsISimpleEnumerator)
00423 
00424 xpcPropertyBagEnumerator::xpcPropertyBagEnumerator(PRUint32 count)
00425     : mIndex(0), mCount(0)
00426 {
00427     mArray.SizeTo(count);
00428 }
00429 
00430 JSBool xpcPropertyBagEnumerator::AppendElement(nsISupports* element)
00431 {
00432     if(!mArray.AppendElement(element))
00433         return JS_FALSE;
00434     mCount++;
00435     return JS_TRUE;
00436 }
00437 
00438 /* boolean hasMoreElements (); */
00439 NS_IMETHODIMP xpcPropertyBagEnumerator::HasMoreElements(PRBool *_retval)
00440 {
00441     *_retval = mIndex < mCount;
00442     return NS_OK;
00443 }
00444 
00445 /* nsISupports getNext (); */
00446 NS_IMETHODIMP xpcPropertyBagEnumerator::GetNext(nsISupports **_retval)
00447 {
00448     if(!(mIndex < mCount))
00449     {
00450         NS_ERROR("Bad nsISimpleEnumerator caller!");
00451         return NS_ERROR_FAILURE;    
00452     }
00453     
00454     *_retval = mArray.ElementAt(mIndex++);
00455     return *_retval ? NS_OK : NS_ERROR_FAILURE;
00456 }
00457 
00458 /***************************************************************************/
00459 // This 'WrappedJSIdentity' class and singleton allow us to figure out if
00460 // any given nsISupports* is implemented by a WrappedJS object. This is done
00461 // using a QueryInterface call on the interface pointer with our ID. If
00462 // that call returns NS_OK and the pointer is to our singleton, then the
00463 // interface must be implemented by a WrappedJS object. NOTE: the
00464 // 'WrappedJSIdentity' object is not a real XPCOM object and should not be
00465 // used for anything else (hence it is declared in this implementation file).
00466 
00467 // {5C5C3BB0-A9BA-11d2-BA64-00805F8A5DD7}
00468 #define NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID \
00469 { 0x5c5c3bb0, 0xa9ba, 0x11d2,                       \
00470   { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
00471 
00472 class WrappedJSIdentity
00473 {
00474     // no instance methods...
00475 public:
00476     NS_DEFINE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
00477 
00478     static void* GetSingleton()
00479     {
00480         static WrappedJSIdentity* singleton = nsnull;
00481         if(!singleton)
00482             singleton = new WrappedJSIdentity();
00483         return (void*) singleton;
00484     }
00485 };
00486 
00487 /***************************************************************************/
00488 
00489 // static
00490 JSBool
00491 nsXPCWrappedJSClass::IsWrappedJS(nsISupports* aPtr)
00492 {
00493     void* result;
00494     NS_PRECONDITION(aPtr, "null pointer");
00495     return aPtr &&
00496            NS_OK == aPtr->QueryInterface(NS_GET_IID(WrappedJSIdentity), &result) &&
00497            result == WrappedJSIdentity::GetSingleton();
00498 }
00499 
00500 NS_IMETHODIMP
00501 nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
00502                                              REFNSIID aIID,
00503                                              void** aInstancePtr)
00504 {
00505     if(aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder)))
00506     {
00507         NS_ADDREF(self);
00508         *aInstancePtr = (void*) NS_STATIC_CAST(nsIXPConnectJSObjectHolder*,self);
00509         return NS_OK;
00510     }
00511 
00512     // Objects internal to xpconnect are the only objects that even know *how*
00513     // to ask for this iid. And none of them bother refcoutning the thing.
00514     if(aIID.Equals(NS_GET_IID(WrappedJSIdentity)))
00515     {
00516         // asking to find out if this is a wrapper object
00517         *aInstancePtr = WrappedJSIdentity::GetSingleton();
00518         return NS_OK;
00519     }
00520 
00521 #ifdef XPC_IDISPATCH_SUPPORT
00522     // If IDispatch is enabled and we're QI'ing to IDispatch
00523     if(nsXPConnect::IsIDispatchEnabled() && aIID.Equals(NSID_IDISPATCH))
00524     {
00525         return XPCIDispatchExtension::IDispatchQIWrappedJS(self, aInstancePtr);
00526     }
00527 #endif
00528     if(aIID.Equals(NS_GET_IID(nsIPropertyBag)))
00529     {
00530         // We only want to expose one implementation from our aggregate.
00531         nsXPCWrappedJS* root = self->GetRootWrapper();
00532 
00533         if(!root->IsValid())
00534         {
00535             *aInstancePtr = nsnull;
00536             return NS_NOINTERFACE;
00537         }
00538 
00539         NS_ADDREF(root);
00540         *aInstancePtr = (void*) NS_STATIC_CAST(nsIPropertyBag*,root);
00541         return NS_OK;
00542     }
00543 
00544     XPCCallContext ccx(NATIVE_CALLER);
00545     if(!ccx.IsValid())
00546     {
00547         *aInstancePtr = nsnull;
00548         return NS_NOINTERFACE;
00549     }
00550 
00551     // We support nsISupportsWeakReference iff the root wrapped JSObject
00552     // claims to support it in its QueryInterface implementation.
00553     if(aIID.Equals(NS_GET_IID(nsISupportsWeakReference)))
00554     {
00555         // We only want to expose one implementation from our aggregate.
00556         nsXPCWrappedJS* root = self->GetRootWrapper();
00557 
00558         // Fail if JSObject doesn't claim support for nsISupportsWeakReference
00559         if(!root->IsValid() ||
00560            !CallQueryInterfaceOnJSObject(ccx, root->GetJSObject(), aIID))
00561         {
00562             *aInstancePtr = nsnull;
00563             return NS_NOINTERFACE;
00564         }
00565 
00566         NS_ADDREF(root);
00567         *aInstancePtr = (void*) NS_STATIC_CAST(nsISupportsWeakReference*,root);
00568         return NS_OK;
00569     }
00570 
00571     nsXPCWrappedJS* sibling;
00572 
00573     // Checks for any existing wrapper explicitly constructed for this iid.
00574     // This includes the current 'self' wrapper. This also deals with the
00575     // nsISupports case (for which it returns mRoot).
00576     if(nsnull != (sibling = self->Find(aIID)))
00577     {
00578         NS_ADDREF(sibling);
00579         *aInstancePtr = (void*) sibling;
00580         return NS_OK;
00581     }
00582 
00583     // Check if asking for an interface from which one of our wrappers inherits.
00584     if(nsnull != (sibling = self->FindInherited(aIID)))
00585     {
00586         NS_ADDREF(sibling);
00587         *aInstancePtr = (void*) sibling;
00588         return NS_OK;
00589     }
00590 
00591     // else we do the more expensive stuff...
00592 
00593 #ifndef XPCONNECT_STANDALONE
00594     // Before calling out, ensure that we're not about to claim to implement
00595     // nsISecurityCheckedComponent for an untrusted object. Doing so causes
00596     // problems. See bug 352882.
00597 
00598     if(aIID.Equals(NS_GET_IID(nsISecurityCheckedComponent)))
00599     {
00600         // XXX This code checks to see if the given object has chrome (also
00601         // known as system) principals. It really wants to do a
00602         // UniversalXPConnect type check.
00603 
00604         nsXPConnect *xpc = nsXPConnect::GetXPConnect();
00605         nsCOMPtr<nsIScriptSecurityManager> secMan =
00606             do_QueryInterface(xpc->GetDefaultSecurityManager());
00607         if(!secMan)
00608         {
00609             *aInstancePtr = nsnull;
00610             return NS_NOINTERFACE;
00611         }
00612         nsCOMPtr<nsIPrincipal> objPrin;
00613         nsresult rv = secMan->GetObjectPrincipal(ccx, self->GetJSObject(),
00614                                                  getter_AddRefs(objPrin));
00615         if(NS_SUCCEEDED(rv))
00616         {
00617             nsCOMPtr<nsIPrincipal> systemPrin;
00618             rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrin));
00619             if(systemPrin != objPrin)
00620                 rv = NS_NOINTERFACE;
00621         }
00622 
00623         if(NS_FAILED(rv))
00624         {
00625             *aInstancePtr = nsnull;
00626             return rv;
00627         }
00628     }
00629 #endif
00630 
00631     // check if the JSObject claims to implement this interface
00632     JSObject* jsobj = CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(),
00633                                                    aIID);
00634     if(jsobj)
00635     {
00636         // protect jsobj until it is actually attached
00637         AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(jsobj));
00638 
00639         // We can't use XPConvert::JSObject2NativeInterface() here
00640         // since that can find a XPCWrappedNative directly on the
00641         // proto chain, and we don't want that here. We need to find
00642         // the actual JS object that claimed it supports the interface
00643         // we're looking for or we'll potentially bypass security
00644         // checks etc by calling directly through to a native found on
00645         // the prototype chain.
00646         //
00647         // Instead, simply do the nsXPCWrappedJS part of
00648         // XPConvert::JSObject2NativeInterface() here to make sure we
00649         // get a new (or used) nsXPCWrappedJS.
00650         nsXPCWrappedJS* wrapper;
00651         nsresult rv = nsXPCWrappedJS::GetNewOrUsed(ccx, jsobj, aIID, nsnull,
00652                                                    &wrapper);
00653         if(NS_SUCCEEDED(rv) && wrapper)
00654         {
00655             // We need to go through the QueryInterface logic to make
00656             // this return the right thing for the various 'special'
00657             // interfaces; e.g.  nsIPropertyBag.
00658             rv = wrapper->QueryInterface(aIID, aInstancePtr);
00659             NS_RELEASE(wrapper);
00660             return rv;
00661         }
00662     }
00663 
00664     // else...
00665     // no can do
00666     *aInstancePtr = nsnull;
00667     return NS_NOINTERFACE;
00668 }
00669 
00670 JSObject*
00671 nsXPCWrappedJSClass::GetRootJSObject(XPCCallContext& ccx, JSObject* aJSObj)
00672 {
00673     JSObject* result = CallQueryInterfaceOnJSObject(ccx, aJSObj,
00674                                                     NS_GET_IID(nsISupports));
00675     return result ? result : aJSObj;
00676 }
00677 
00678 void JS_DLL_CALLBACK
00679 xpcWrappedJSErrorReporter(JSContext *cx, const char *message,
00680                           JSErrorReport *report)
00681 {
00682     if(report)
00683     {
00684         // If it is an exception report, then we can just deal with the
00685         // exception later (if not caught in the JS code).
00686         if(JSREPORT_IS_EXCEPTION(report->flags))
00687         {
00688             // XXX We have a problem with error reports from uncaught exceptions.
00689             //
00690             // http://bugzilla.mozilla.org/show_bug.cgi?id=66453
00691             //
00692             // The issue is...
00693             //
00694             // We can't assume that the exception will *stay* uncaught. So, if
00695             // we build an nsIXPCException here and the underlying exception
00696             // really is caught before our script is done running then we blow
00697             // it by returning failure to our caller when the script didn't
00698             // really fail. However, This report contains error location info
00699             // that is no longer available after the script is done. So, if the
00700             // exception really is not caught (and is a non-engine exception)
00701             // then we've lost the oportunity to capture the script location
00702             // info that we *could* have captured here.
00703             //
00704             // This is expecially an issue with nested evaluations.
00705             //
00706             // Perhaps we could capture an expception here and store it as
00707             // 'provisional' and then later if there is a pending exception
00708             // when the script is done then we could maybe compare that in some
00709             // way with the 'provisional' one in which we captured location info.
00710             // We would not want to assume that the one discovered here is the
00711             // same one that is later detected. This could cause us to lie.
00712             //
00713             // The thing is. we do not currently store the right stuff to compare
00714             // these two nsIXPCExceptions (triggered by the same exception jsval
00715             // in the engine). Maybe we should store the jsval and compare that?
00716             // Maybe without even rooting it since we will not dereference it.
00717             // This is inexact, but maybe the right thing to do?
00718             //
00719             // if(report->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)) ...
00720             //
00721 
00722             return;
00723         }
00724 
00725         if(JSREPORT_IS_WARNING(report->flags))
00726         {
00727             // XXX printf the warning (#ifdef DEBUG only!).
00728             // XXX send the warning to the console service.
00729             return;
00730         }
00731     }
00732 
00733     XPCCallContext ccx(NATIVE_CALLER, cx);
00734     if(!ccx.IsValid())
00735         return;
00736 
00737     nsCOMPtr<nsIException> e;
00738     XPCConvert::JSErrorToXPCException(ccx, message, nsnull, nsnull, report,
00739                                       getter_AddRefs(e));
00740     if(e)
00741         ccx.GetXPCContext()->SetException(e);
00742 }
00743 
00744 JSBool
00745 nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx,
00746                                            const nsXPTMethodInfo* method,
00747                                            const nsXPTParamInfo& param,
00748                                            uint16 methodIndex,
00749                                            uint8 paramIndex,
00750                                            SizeMode mode,
00751                                            nsXPTCMiniVariant* nativeParams,
00752                                            JSUint32* result)
00753 {
00754     uint8 argnum;
00755     nsresult rv;
00756 
00757     if(mode == GET_SIZE)
00758         rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, &param, 0, &argnum);
00759     else
00760         rv = mInfo->GetLengthIsArgNumberForParam(methodIndex, &param, 0, &argnum);
00761     if(NS_FAILED(rv))
00762         return JS_FALSE;
00763 
00764     const nsXPTParamInfo& arg_param = method->GetParam(argnum);
00765     const nsXPTType& arg_type = arg_param.GetType();
00766 
00767     // The xpidl compiler ensures this. We reaffirm it for safety.
00768     if(arg_type.IsPointer() || arg_type.TagPart() != nsXPTType::T_U32)
00769         return JS_FALSE;
00770 
00771     if(arg_param.IsOut())
00772         *result = *(JSUint32*)nativeParams[argnum].val.p;
00773     else
00774         *result = nativeParams[argnum].val.u32;
00775 
00776     return JS_TRUE;
00777 }
00778 
00779 JSBool
00780 nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx,
00781                                                const nsXPTMethodInfo* method,
00782                                                const nsXPTParamInfo& param,
00783                                                uint16 methodIndex,
00784                                                const nsXPTType& type,
00785                                                nsXPTCMiniVariant* nativeParams,
00786                                                nsID* result)
00787 {
00788     uint8 type_tag = type.TagPart();
00789 
00790     if(type_tag == nsXPTType::T_INTERFACE)
00791     {
00792         if(NS_SUCCEEDED(GetInterfaceInfo()->
00793                 GetIIDForParamNoAlloc(methodIndex, &param, result)))
00794         {
00795             return JS_TRUE;
00796         }
00797     }
00798     else if(type_tag == nsXPTType::T_INTERFACE_IS)
00799     {
00800         uint8 argnum;
00801         nsresult rv;
00802         rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex,
00803                                                     &param, &argnum);
00804         if(NS_FAILED(rv))
00805             return JS_FALSE;
00806 
00807         const nsXPTParamInfo& arg_param = method->GetParam(argnum);
00808         const nsXPTType& arg_type = arg_param.GetType();
00809         if(arg_type.IsPointer() &&
00810            arg_type.TagPart() == nsXPTType::T_IID)
00811         {
00812             if(arg_param.IsOut())
00813             {
00814                 nsID** p = (nsID**) nativeParams[argnum].val.p;
00815                 if(!p || !*p)
00816                     return JS_FALSE;
00817                 *result = **p;
00818             }
00819             else
00820             {
00821                 nsID* p = (nsID*) nativeParams[argnum].val.p;
00822                 if(!p)
00823                     return JS_FALSE;
00824                 *result = *p;
00825             }
00826             return JS_TRUE;
00827         }
00828     }
00829     return JS_FALSE;
00830 }
00831 
00832 void
00833 nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type,
00834                                          JSUint32 array_count,
00835                                          void** arrayp)
00836 {
00837     if(datum_type.IsInterfacePointer())
00838     {
00839         nsISupports** pp = (nsISupports**) arrayp;
00840         for(JSUint32 k = 0; k < array_count; k++)
00841         {
00842             nsISupports* p = pp[k];
00843             NS_IF_RELEASE(p);
00844         }
00845     }
00846     else
00847     {
00848         void** pp = (void**) arrayp;
00849         for(JSUint32 k = 0; k < array_count; k++)
00850         {
00851             void* p = pp[k];
00852             if(p) nsMemory::Free(p);
00853         }
00854     }
00855 }
00856 
00857 void
00858 nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type,
00859                                               void** pp)
00860 {
00861     NS_ASSERTION(pp,"null pointer");
00862     if(type.IsInterfacePointer())
00863     {
00864         nsISupports* p = *((nsISupports**)pp);
00865         if(p) p->Release();
00866     }
00867     else
00868     {
00869         void* p = *((void**)pp);
00870         if(p) nsMemory::Free(p);
00871     }
00872 }
00873 
00874 nsresult
00875 nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
00876                                        const char * aPropertyName,
00877                                        const char * anInterfaceName)
00878 {
00879     XPCContext * xpcc = ccx.GetXPCContext();
00880     JSContext * cx = ccx.GetJSContext();
00881     nsCOMPtr<nsIException> xpc_exception;
00882     /* this one would be set by our error reporter */
00883 
00884     xpcc->GetException(getter_AddRefs(xpc_exception));
00885     if(xpc_exception)
00886         xpcc->SetException(nsnull);
00887 
00888     // get this right away in case we do something below to cause JS code
00889     // to run on this JSContext
00890     nsresult pending_result = xpcc->GetPendingResult();
00891 
00892     jsval js_exception;
00893     /* JS might throw an expection whether the reporter was called or not */
00894     if(JS_GetPendingException(cx, &js_exception))
00895     {
00896         if(!xpc_exception)
00897             XPCConvert::JSValToXPCException(ccx, js_exception, anInterfaceName,
00898                                             aPropertyName, getter_AddRefs(xpc_exception));
00899 
00900         /* cleanup and set failed even if we can't build an exception */
00901         if(!xpc_exception)
00902         {
00903             ccx.GetThreadData()->SetException(nsnull); // XXX necessary?
00904         }
00905         JS_ClearPendingException(cx);
00906     }
00907 
00908     if(xpc_exception)
00909     {
00910         nsresult e_result;
00911         if(NS_SUCCEEDED(xpc_exception->GetResult(&e_result)))
00912         {
00913             if(xpc_IsReportableErrorCode(e_result))
00914             {
00915 #ifdef DEBUG
00916                 static const char line[] =
00917                     "************************************************************\n";
00918                 static const char preamble[] =
00919                     "* Call to xpconnect wrapped JSObject produced this error:  *\n";
00920                 static const char cant_get_text[] =
00921                     "FAILED TO GET TEXT FROM EXCEPTION\n";
00922 
00923                 fputs(line, stdout);
00924                 fputs(preamble, stdout);
00925                 char* text;
00926                 if(NS_SUCCEEDED(xpc_exception->ToString(&text)) && text)
00927                 {
00928                     fputs(text, stdout);
00929                     fputs("\n", stdout);
00930                     nsMemory::Free(text);
00931                 }
00932                 else
00933                     fputs(cant_get_text, stdout);
00934                 fputs(line, stdout);
00935 #endif
00936 
00937                 // Log the exception to the JS Console, so that users can do
00938                 // something with it.
00939                 nsCOMPtr<nsIConsoleService> consoleService
00940                     (do_GetService(XPC_CONSOLE_CONTRACTID));
00941                 if(nsnull != consoleService)
00942                 {
00943                     nsresult rv;
00944                     nsCOMPtr<nsIScriptError> scriptError;
00945                     nsCOMPtr<nsISupports> errorData;
00946                     rv = xpc_exception->GetData(getter_AddRefs(errorData));
00947                     if(NS_SUCCEEDED(rv))
00948                         scriptError = do_QueryInterface(errorData);
00949 
00950                     if(nsnull == scriptError)
00951                     {
00952                         // No luck getting one from the exception, so
00953                         // try to cook one up.
00954                         scriptError = do_CreateInstance(XPC_SCRIPT_ERROR_CONTRACTID);
00955                         if(nsnull != scriptError)
00956                         {
00957                             char* exn_string;
00958                             rv = xpc_exception->ToString(&exn_string);
00959                             if(NS_SUCCEEDED(rv))
00960                             {
00961                                 // use toString on the exception as the message
00962                                 nsAutoString newMessage;
00963                                 newMessage.AssignWithConversion(exn_string);
00964                                 nsMemory::Free((void *) exn_string);
00965 
00966                                 // try to get filename, lineno from the first
00967                                 // stack frame location.
00968                                 PRInt32 lineNumber = 0;
00969                                 nsXPIDLCString sourceName;
00970 
00971                                 nsCOMPtr<nsIStackFrame> location;
00972                                 xpc_exception->
00973                                     GetLocation(getter_AddRefs(location));
00974                                 if(location)
00975                                 {
00976                                     // Get line number w/o checking; 0 is ok.
00977                                     location->GetLineNumber(&lineNumber);
00978 
00979                                     // get a filename.
00980                                     rv = location->GetFilename(getter_Copies(sourceName));
00981                                 }
00982 
00983                                 rv = scriptError->Init(newMessage.get(),
00984                                                        NS_ConvertASCIItoUCS2(sourceName).get(),
00985                                                        nsnull,
00986                                                        lineNumber, 0, 0,
00987                                                        "XPConnect JavaScript");
00988                                 if(NS_FAILED(rv))
00989                                     scriptError = nsnull;
00990                             }
00991                         }
00992                     }
00993                     if(nsnull != scriptError)
00994                         consoleService->LogMessage(scriptError);
00995                 }
00996             }
00997             // Whether or not it passes the 'reportable' test, it might
00998             // still be an error and we have to do the right thing here...
00999             if(NS_FAILED(e_result))
01000             {
01001                 ccx.GetThreadData()->SetException(xpc_exception);
01002                 return e_result;
01003             }
01004         }
01005     }
01006     else
01007     {
01008         // see if JS code signaled failure result without throwing exception
01009         if(NS_FAILED(pending_result))
01010         {
01011             return pending_result;
01012         }
01013     }
01014     return NS_ERROR_FAILURE;
01015 }
01016 
01017 NS_IMETHODIMP
01018 nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16 methodIndex,
01019                                 const nsXPTMethodInfo* info,
01020                                 nsXPTCMiniVariant* nativeParams)
01021 {
01022     jsval* stackbase;
01023     jsval* sp = nsnull;
01024     uint8 i;
01025     uint8 argc=0;
01026     uint8 stack_size;
01027     jsval result;
01028     uint8 paramCount=0;
01029     nsresult retval = NS_ERROR_FAILURE;
01030     nsresult pending_result = NS_OK;
01031     JSBool success;
01032     JSBool readyToDoTheCall = JS_FALSE;
01033     nsID  param_iid;
01034     uint8 outConversionFailedIndex;
01035     JSObject* obj;
01036     const char* name = info->GetName();
01037     jsval fval;
01038     void* mark;
01039     JSBool foundDependentParam;
01040     XPCContext* xpcc;
01041     JSContext* cx;
01042     JSObject* thisObj;
01043 
01044     // Make sure not to set the callee on ccx until after we've gone through
01045     // the whole nsIXPCFunctionThisTranslator bit.  That code uses ccx to
01046     // convert natives to JSObjects, but we do NOT plan to pass those JSObjects
01047     // to our real callee.
01048     XPCCallContext ccx(NATIVE_CALLER);
01049     if(ccx.IsValid())
01050     {
01051         xpcc = ccx.GetXPCContext();
01052         cx = ccx.GetJSContext();
01053     }
01054     else
01055     {
01056         xpcc = nsnull;
01057         cx = nsnull;
01058     }
01059 
01060     AutoScriptEvaluate scriptEval(cx);
01061 #ifdef DEBUG_stats_jband
01062     PRIntervalTime startTime = PR_IntervalNow();
01063     PRIntervalTime endTime = 0;
01064     static int totalTime = 0;
01065 
01066 
01067     static int count = 0;
01068     static const int interval = 10;
01069     if(0 == (++count % interval))
01070         printf("<<<<<<<< %d calls on nsXPCWrappedJSs made.  (%d)\n", count, PR_IntervalToMilliseconds(totalTime));
01071 #endif
01072 
01073     obj = thisObj = wrapper->GetJSObject();
01074 
01075     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
01076     paramCount = info->GetParamCount();
01077     argc = paramCount -
01078             (paramCount && info->GetParam(paramCount-1).IsRetval() ? 1 : 0);
01079 
01080     if(!cx || !xpcc || !IsReflectable(methodIndex))
01081         goto pre_call_clean_up;
01082 
01083     scriptEval.StartEvaluating(xpcWrappedJSErrorReporter);
01084 
01085     xpcc->SetPendingResult(pending_result);
01086     xpcc->SetException(nsnull);
01087     ccx.GetThreadData()->SetException(nsnull);
01088 
01089     // We use js_AllocStack, js_Invoke, and js_FreeStack so that the gcthings
01090     // we use as args will be rooted by the engine as we do conversions and
01091     // prepare to do the function call. This adds a fair amount of complexity,
01092     // but is a good optimization compared to calling JS_AddRoot for each item.
01093 
01094     // setup stack
01095 
01096     // if this isn't a function call then we don't need to push extra stuff
01097     if(info->IsGetter() || info->IsSetter())
01098     {
01099         stack_size = argc;
01100     }
01101     else
01102     {
01103         // allocate extra space for function and 'this'
01104         stack_size = argc + 2;
01105 
01106         // We get fval before allocating the stack to avoid gc badness that can
01107         // happen if the GetProperty call leaves our request and the gc runs
01108         // while the stack we allocate contains garbage.
01109 
01110         // If the interface is marked as a [function] then we will assume that
01111         // our JSObject is a function and not an object with a named method.
01112 
01113         PRBool isFunction;
01114         if(NS_FAILED(mInfo->IsFunction(&isFunction)))
01115             goto pre_call_clean_up;
01116 
01117         // In the xpidl [function] case we are making sure now that the 
01118         // JSObject is callable. If it is *not* callable then we silently 
01119         // fallback to looking up the named property...
01120         // (because jst says he thinks this fallback is 'The Right Thing'.)
01121         //
01122         // In the normal (non-function) case we just lookup the property by 
01123         // name and as long as the object has such a named property we go ahead
01124         // and try to make the call. If it turns out the named property is not
01125         // a callable object then the JS engine will throw an error and we'll
01126         // pass this along to the caller as an exception/result code.
01127 
01128         if(isFunction && 
01129            JS_TypeOfValue(ccx, OBJECT_TO_JSVAL(obj)) == JSTYPE_FUNCTION)
01130         {
01131             fval = OBJECT_TO_JSVAL(obj);
01132 
01133             // We may need to translate the 'this' for the function object.
01134 
01135             if(paramCount)
01136             {
01137                 const nsXPTParamInfo& firstParam = info->GetParam(0);
01138                 if(firstParam.IsIn())
01139                 {
01140                     const nsXPTType& firstType = firstParam.GetType();
01141                     if(firstType.IsPointer() && firstType.IsInterfacePointer())
01142                     {
01143                         nsIXPCFunctionThisTranslator* translator;
01144 
01145                         IID2ThisTranslatorMap* map =
01146                             mRuntime->GetThisTranslatorMap();
01147 
01148                         {
01149                             XPCAutoLock lock(mRuntime->GetMapLock()); // scoped lock
01150                             translator = map->Find(mIID);
01151                         }
01152 
01153                         if(translator)
01154                         {
01155                             PRBool hideFirstParamFromJS = PR_FALSE;
01156                             nsIID* newWrapperIID = nsnull;
01157                             nsCOMPtr<nsISupports> newThis;
01158 
01159                             if(NS_FAILED(translator->
01160                               TranslateThis((nsISupports*)nativeParams[0].val.p,
01161                                             mInfo, methodIndex,
01162                                             &hideFirstParamFromJS,
01163                                             &newWrapperIID,
01164                                             getter_AddRefs(newThis))))
01165                             {
01166                                 goto pre_call_clean_up;
01167                             }
01168                             if(hideFirstParamFromJS)
01169                             {
01170                                 NS_ERROR("HideFirstParamFromJS not supported");
01171                                 goto pre_call_clean_up;
01172                             }
01173                             if(newThis)
01174                             {
01175                                 if(!newWrapperIID)
01176                                     newWrapperIID =
01177                                         NS_CONST_CAST(nsIID*,
01178                                                       &NS_GET_IID(nsISupports));
01179                                 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
01180                                 JSBool ok =
01181                                   XPCConvert::NativeInterface2JSObject(ccx,
01182                                         getter_AddRefs(holder), newThis,
01183                                         newWrapperIID, obj, PR_FALSE, PR_FALSE,
01184                                         nsnull);
01185                                 if(newWrapperIID != &NS_GET_IID(nsISupports))
01186                                     nsMemory::Free(newWrapperIID);
01187                                 if(!ok ||
01188                                     NS_FAILED(holder->GetJSObject(&thisObj)))
01189                                 {
01190                                     goto pre_call_clean_up;
01191                                 }
01192                             }
01193                         }
01194                     }
01195                 }
01196             }
01197         }
01198         else if(!JS_GetMethod(cx, obj, name, &thisObj, &fval))
01199         {
01200             // XXX We really want to factor out the error reporting better and
01201             // specifically report the failure to find a function with this name.
01202             // This is what we do below if the property is found but is not a
01203             // function. We just need to factor better so we can get to that
01204             // reporting path from here.
01205             goto pre_call_clean_up;
01206         }
01207     }
01208 
01209     // if stack_size is zero then we won't be needing a stack
01210     if(stack_size && !(stackbase = sp = js_AllocStack(cx, stack_size, &mark)))
01211     {
01212         retval = NS_ERROR_OUT_OF_MEMORY;
01213         goto pre_call_clean_up;
01214     }
01215 
01216     NS_ASSERTION(info->IsGetter() || sp, "Only a getter needs no stack.");
01217 
01218     // this is a function call, so push function and 'this'
01219     if(stack_size != argc)
01220     {
01221         *sp++ = fval;
01222         *sp++ = OBJECT_TO_JSVAL(thisObj);
01223     }
01224 
01225     // make certain we leave no garbage in the stack
01226     for(i = 0; i < argc; i++)
01227     {
01228         sp[i] = JSVAL_VOID;
01229     }
01230 
01231     // build the args
01232     for(i = 0; i < argc; i++)
01233     {
01234         const nsXPTParamInfo& param = info->GetParam(i);
01235         const nsXPTType& type = param.GetType();
01236         nsXPTType datum_type;
01237         JSUint32 array_count;
01238         PRBool isArray = type.IsArray();
01239         jsval val = JSVAL_NULL;
01240         AUTO_MARK_JSVAL(ccx, &val);
01241         PRBool isSizedString = isArray ?
01242                 JS_FALSE :
01243                 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
01244                 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
01245 
01246 
01247         // verify that null was not passed for 'out' param
01248         if(param.IsOut() && !nativeParams[i].val.p)
01249         {
01250             retval = NS_ERROR_INVALID_ARG;
01251             goto pre_call_clean_up;
01252         }
01253 
01254         if(isArray)
01255         {
01256             if(NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
01257                                                 &datum_type)))
01258                 goto pre_call_clean_up;
01259         }
01260         else
01261             datum_type = type;
01262 
01263         if(param.IsIn())
01264         {
01265             nsXPTCMiniVariant* pv;
01266 
01267             if(param.IsOut())
01268                 pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
01269             else
01270                 pv = &nativeParams[i];
01271 
01272             if(datum_type.IsInterfacePointer() &&
01273                !GetInterfaceTypeFromParam(cx, info, param, methodIndex,
01274                                           datum_type, nativeParams,
01275                                           &param_iid))
01276                 goto pre_call_clean_up;
01277 
01278             if(isArray || isSizedString)
01279             {
01280                 if(!GetArraySizeFromParam(cx, info, param, methodIndex,
01281                                           i, GET_LENGTH, nativeParams,
01282                                           &array_count))
01283                     goto pre_call_clean_up;
01284             }
01285 
01286             // Figure out what our callee is
01287             if(info->IsGetter() || info->IsSetter())
01288             {
01289                 // Pull the getter or setter off of |obj|
01290                 uintN attrs;
01291                 JSBool found;
01292                 JSPropertyOp getter;
01293                 JSPropertyOp setter;
01294                 JSBool ok =
01295                     JS_GetPropertyAttrsGetterAndSetter(cx, obj, name,
01296                                                        &attrs, &found,
01297                                                        &getter, &setter);
01298                 if(ok)
01299                 {
01300                     if(info->IsGetter() && (attrs & JSPROP_GETTER))
01301                     {
01302                         // JSPROP_GETTER means the getter is actually a
01303                         // function object.
01304                         ccx.SetCallee((JSObject*)getter);
01305                     }
01306                     else if(info->IsSetter() && (attrs & JSPROP_SETTER))
01307                     {
01308                         // JSPROP_SETTER means the setter is actually a
01309                         // function object.
01310                         ccx.SetCallee((JSObject*)setter);
01311                     }
01312                 }
01313             }
01314             else if(JSVAL_IS_OBJECT(fval))
01315             {
01316                 ccx.SetCallee(JSVAL_TO_OBJECT(fval));
01317             }
01318 
01319             if(isArray)
01320             {
01321 
01322                 if(!XPCConvert::NativeArray2JS(ccx, &val, (const void**)&pv->val,
01323                                                datum_type, &param_iid,
01324                                                array_count, obj, nsnull))
01325                     goto pre_call_clean_up;
01326             }
01327             else if(isSizedString)
01328             {
01329                 if(!XPCConvert::NativeStringWithSize2JS(ccx, &val,
01330                                                (const void*)&pv->val,
01331                                                datum_type,
01332                                                array_count, nsnull))
01333                     goto pre_call_clean_up;
01334             }
01335             else
01336             {
01337                 if(!XPCConvert::NativeData2JS(ccx, &val, &pv->val, type,
01338                                               &param_iid, obj, nsnull))
01339                     goto pre_call_clean_up;
01340             }
01341         }
01342 
01343         if(param.IsOut())
01344         {
01345             // create an 'out' object
01346             JSObject* out_obj = NewOutObject(cx);
01347             if(!out_obj)
01348             {
01349                 retval = NS_ERROR_OUT_OF_MEMORY;
01350                 goto pre_call_clean_up;
01351             }
01352 
01353             if(param.IsIn())
01354             {
01355                 if(!OBJ_SET_PROPERTY(cx, out_obj,
01356                         mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
01357                         &val))
01358                 {
01359                     goto pre_call_clean_up;
01360                 }
01361             }
01362             *sp++ = OBJECT_TO_JSVAL(out_obj);
01363         }
01364         else
01365             *sp++ = val;
01366     }
01367 
01368 
01369 
01370     readyToDoTheCall = JS_TRUE;
01371 
01372 pre_call_clean_up:
01373     // clean up any 'out' params handed in
01374     for(i = 0; i < paramCount; i++)
01375     {
01376         const nsXPTParamInfo& param = info->GetParam(i);
01377         if(!param.IsOut())
01378             continue;
01379 
01380         const nsXPTType& type = param.GetType();
01381         if(!type.IsPointer())
01382             continue;
01383         void* p;
01384         if(!(p = nativeParams[i].val.p))
01385             continue;
01386 
01387         if(param.IsIn())
01388         {
01389             if(type.IsArray())
01390             {
01391                 void** pp;
01392                 if(nsnull != (pp = *((void***)p)))
01393                 {
01394 
01395                     // we need to get the array length and iterate the items
01396                     JSUint32 array_count;
01397                     nsXPTType datum_type;
01398 
01399                     if(NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, &param,
01400                                                            1, &datum_type)) &&
01401                        datum_type.IsPointer() &&
01402                        GetArraySizeFromParam(cx, info, param, methodIndex,
01403                                              i, GET_LENGTH, nativeParams,
01404                                              &array_count) && array_count)
01405                     {
01406                         CleanupPointerArray(datum_type, array_count, pp);
01407                     }
01408                     // always release the array if it is inout
01409                     nsMemory::Free(pp);
01410                 }
01411             }
01412             else
01413                 CleanupPointerTypeObject(type, (void**)p);
01414         }
01415         *((void**)p) = nsnull;
01416     }
01417 
01418     // Make sure "this" doesn't get deleted during this call.
01419     nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this);
01420 
01421     result = JSVAL_NULL;
01422     AUTO_MARK_JSVAL(ccx, &result);
01423 
01424     if(!readyToDoTheCall)
01425         goto done;
01426 
01427     // do the deed - note exceptions
01428 
01429     JS_ClearPendingException(cx);
01430 
01431     if(info->IsGetter())
01432         success = JS_GetProperty(cx, obj, name, &result);
01433     else if(info->IsSetter())
01434         success = JS_SetProperty(cx, obj, name, sp-1);
01435     else
01436     {
01437         if(!JSVAL_IS_PRIMITIVE(fval))
01438         {
01439             // Lift current frame (or make new one) to include the args
01440             // and do the call.
01441             JSStackFrame *fp, *oldfp, frame;
01442             jsval *oldsp;
01443 
01444             fp = oldfp = cx->fp;
01445             if(!fp)
01446             {
01447                 memset(&frame, 0, sizeof frame);
01448                 cx->fp = fp = &frame;
01449             }
01450             oldsp = fp->sp;
01451             fp->sp = sp;
01452 
01453             success = js_Invoke(cx, argc, JSINVOKE_INTERNAL);
01454 
01455             result = fp->sp[-1];
01456             fp->sp = oldsp;
01457             if(oldfp != fp)
01458                 cx->fp = oldfp;
01459         }
01460         else
01461         {
01462             // The property was not an object so can't be a function.
01463             // Let's build and 'throw' an exception.
01464 
01465             static const nsresult code =
01466                     NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
01467             static const char format[] = "%s \"%s\"";
01468             const char * msg;
01469             char* sz = nsnull;
01470 
01471             if(nsXPCException::NameAndFormatForNSResult(code, nsnull, &msg) && msg)
01472                 sz = JS_smprintf(format, msg, name);
01473 
01474             nsCOMPtr<nsIException> e;
01475 
01476             XPCConvert::ConstructException(code, sz, GetInterfaceName(), name,
01477                                            nsnull, getter_AddRefs(e));
01478             xpcc->SetException(e);
01479             if(sz)
01480                 JS_smprintf_free(sz);
01481             success = JS_FALSE;
01482         }
01483     }
01484 
01485     if (!success)
01486     {
01487         retval = CheckForException(ccx, name, GetInterfaceName());
01488         goto done;
01489     }
01490 
01491     ccx.GetThreadData()->SetException(nsnull); // XXX necessary?
01492 
01493 #define HANDLE_OUT_CONVERSION_FAILURE       \
01494         {outConversionFailedIndex = i; break;}
01495 
01496     // convert out args and result
01497     // NOTE: this is the total number of native params, not just the args
01498     // Convert independent params only.
01499     // When we later convert the dependent params (if any) we will know that
01500     // the params upon which they depend will have already been converted -
01501     // regardless of ordering.
01502 
01503     outConversionFailedIndex = paramCount;
01504     foundDependentParam = JS_FALSE;
01505     for(i = 0; i < paramCount; i++)
01506     {
01507         const nsXPTParamInfo& param = info->GetParam(i);
01508         if(!param.IsOut() && !param.IsDipper())
01509             continue;
01510 
01511         const nsXPTType& type = param.GetType();
01512         if(type.IsDependent())
01513         {
01514             foundDependentParam = JS_TRUE;
01515             continue;
01516         }
01517 
01518         jsval val;
01519         uint8 type_tag = type.TagPart();
01520         JSBool useAllocator = JS_FALSE;
01521         nsXPTCMiniVariant* pv;
01522 
01523         if(param.IsDipper())
01524             pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p;
01525         else
01526             pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
01527 
01528         if(param.IsRetval())
01529             val = result;
01530         else if(JSVAL_IS_PRIMITIVE(stackbase[i+2]) ||
01531                 !OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(stackbase[i+2]),
01532                     mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
01533                     &val))
01534             HANDLE_OUT_CONVERSION_FAILURE
01535 
01536         // setup allocator and/or iid
01537 
01538         if(type_tag == nsXPTType::T_INTERFACE)
01539         {
01540             if(NS_FAILED(GetInterfaceInfo()->
01541                             GetIIDForParamNoAlloc(methodIndex, &param,
01542                                                   &param_iid)))
01543                 HANDLE_OUT_CONVERSION_FAILURE
01544         }
01545         else if(type.IsPointer() && !param.IsShared() && !param.IsDipper())
01546             useAllocator = JS_TRUE;
01547 
01548         if(!XPCConvert::JSData2Native(ccx, &pv->val, val, type,
01549                                       useAllocator, &param_iid, nsnull))
01550             HANDLE_OUT_CONVERSION_FAILURE
01551     }
01552 
01553     // if any params were dependent, then we must iterate again to convert them.
01554     if(foundDependentParam && outConversionFailedIndex == paramCount)
01555     {
01556         for(i = 0; i < paramCount; i++)
01557         {
01558             const nsXPTParamInfo& param = info->GetParam(i);
01559             if(!param.IsOut())
01560                 continue;
01561 
01562             const nsXPTType& type = param.GetType();
01563             if(!type.IsDependent())
01564                 continue;
01565 
01566             jsval val;
01567             nsXPTCMiniVariant* pv;
01568             nsXPTType datum_type;
01569             JSBool useAllocator = JS_FALSE;
01570             JSUint32 array_count;
01571             PRBool isArray = type.IsArray();
01572             PRBool isSizedString = isArray ?
01573                     JS_FALSE :
01574                     type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
01575                     type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
01576 
01577             pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
01578 
01579             if(param.IsRetval())
01580                 val = result;
01581             else if(!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(stackbase[i+2]),
01582                         mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
01583                         &val))
01584                 HANDLE_OUT_CONVERSION_FAILURE
01585 
01586             // setup allocator and/or iid
01587 
01588             if(isArray)
01589             {
01590                 if(NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
01591                                                     &datum_type)))
01592                     HANDLE_OUT_CONVERSION_FAILURE
01593             }
01594             else
01595                 datum_type = type;
01596 
01597             if(datum_type.IsInterfacePointer())
01598             {
01599                if(!GetInterfaceTypeFromParam(cx, info, param, methodIndex,
01600                                              datum_type, nativeParams,
01601                                              &param_iid))
01602                     HANDLE_OUT_CONVERSION_FAILURE
01603             }
01604             else if(type.IsPointer() && !param.IsShared())
01605                 useAllocator = JS_TRUE;
01606 
01607             if(isArray || isSizedString)
01608             {
01609                 if(!GetArraySizeFromParam(cx, info, param, methodIndex,
01610                                           i, GET_LENGTH, nativeParams,
01611                                           &array_count))
01612                     HANDLE_OUT_CONVERSION_FAILURE
01613             }
01614 
01615             if(isArray)
01616             {
01617                 if(array_count &&
01618                    !XPCConvert::JSArray2Native(ccx, (void**)&pv->val, val,
01619                                                array_count, array_count,
01620                                                datum_type,
01621                                                useAllocator, &param_iid,
01622                                                nsnull))
01623                     HANDLE_OUT_CONVERSION_FAILURE
01624             }
01625             else if(isSizedString)
01626             {
01627                 if(!XPCConvert::JSStringWithSize2Native(ccx,
01628                                                    (void*)&pv->val, val,
01629                                                    array_count, array_count,
01630                                                    datum_type, useAllocator,
01631                                                    nsnull))
01632                     HANDLE_OUT_CONVERSION_FAILURE
01633             }
01634             else
01635             {
01636                 if(!XPCConvert::JSData2Native(ccx, &pv->val, val, type,
01637                                               useAllocator, &param_iid,
01638                                               nsnull))
01639                     HANDLE_OUT_CONVERSION_FAILURE
01640             }
01641         }
01642     }
01643 
01644     if(outConversionFailedIndex != paramCount)
01645     {
01646         // We didn't manage all the result conversions!
01647         // We have to cleanup any junk that *did* get converted.
01648 
01649         for(uint8 k = 0; k < i; k++)
01650         {
01651             const nsXPTParamInfo& param = info->GetParam(k);
01652             if(!param.IsOut())
01653                 continue;
01654             const nsXPTType& type = param.GetType();
01655             if(!type.IsPointer())
01656                 continue;
01657             void* p;
01658             if(!(p = nativeParams[k].val.p))
01659                 continue;
01660 
01661             if(type.IsArray())
01662             {
01663                 void** pp;
01664                 if(nsnull != (pp = *((void***)p)))
01665                 {
01666                     // we need to get the array length and iterate the items
01667                     JSUint32 array_count;
01668                     nsXPTType datum_type;
01669 
01670                     if(NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, &param,
01671                                                            1, &datum_type)) &&
01672                        datum_type.IsPointer() &&
01673                        GetArraySizeFromParam(cx, info, param, methodIndex,
01674                                              k, GET_LENGTH, nativeParams,
01675                                              &array_count) && array_count)
01676                     {
01677                         CleanupPointerArray(datum_type, array_count, pp);
01678                     }
01679                     nsMemory::Free(pp);
01680                 }
01681             }
01682             else
01683                 CleanupPointerTypeObject(type, (void**)p);
01684             *((void**)p) = nsnull;
01685         }
01686     }
01687     else
01688     {
01689         // set to whatever the JS code might have set as the result
01690         retval = pending_result;
01691     }
01692 
01693 done:
01694     if(sp)
01695         js_FreeStack(cx, mark);
01696 
01697 #ifdef DEBUG_stats_jband
01698     endTime = PR_IntervalNow();
01699     printf("%s::%s %d ( c->js ) \n", GetInterfaceName(), info->GetName(), PR_IntervalToMilliseconds(endTime-startTime));
01700     totalTime += endTime-startTime;
01701 #endif
01702     return retval;
01703 }
01704 
01705 const char*
01706 nsXPCWrappedJSClass::GetInterfaceName()
01707 {
01708     if(!mName)
01709         mInfo->GetName(&mName);
01710     return mName;
01711 }
01712 
01713 JSObject*
01714 nsXPCWrappedJSClass::NewOutObject(JSContext* cx)
01715 {
01716     return JS_NewObject(cx, nsnull, nsnull, nsnull);
01717 }
01718 
01719 
01720 NS_IMETHODIMP
01721 nsXPCWrappedJSClass::DebugDump(PRInt16 depth)
01722 {
01723 #ifdef DEBUG
01724     depth-- ;
01725     XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x with mRefCnt = %d", this, mRefCnt.get()));
01726     XPC_LOG_INDENT();
01727         char* name;
01728         mInfo->GetName(&name);
01729         XPC_LOG_ALWAYS(("interface name is %s", name));
01730         if(name)
01731             nsMemory::Free(name);
01732         char * iid = mIID.ToString();
01733         XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
01734         if(iid)
01735             PR_Free(iid);
01736         XPC_LOG_ALWAYS(("InterfaceInfo @ %x", mInfo));
01737         uint16 methodCount = 0;
01738         if(depth)
01739         {
01740             uint16 i;
01741             nsIInterfaceInfo* parent;
01742             XPC_LOG_INDENT();
01743             mInfo->GetParent(&parent);
01744             XPC_LOG_ALWAYS(("parent @ %x", parent));
01745             mInfo->GetMethodCount(&methodCount);
01746             XPC_LOG_ALWAYS(("MethodCount = %d", methodCount));
01747             mInfo->GetConstantCount(&i);
01748             XPC_LOG_ALWAYS(("ConstantCount = %d", i));
01749             XPC_LOG_OUTDENT();
01750         }
01751         XPC_LOG_ALWAYS(("mRuntime @ %x", mRuntime));
01752         XPC_LOG_ALWAYS(("mDescriptors @ %x count = %d", mDescriptors, methodCount));
01753         if(depth && mDescriptors && methodCount)
01754         {
01755             depth--;
01756             XPC_LOG_INDENT();
01757             for(uint16 i = 0; i < methodCount; i++)
01758             {
01759                 XPC_LOG_ALWAYS(("Method %d is %s%s", \
01760                         i, IsReflectable(i) ? "":" NOT ","reflectable"));
01761             }
01762             XPC_LOG_OUTDENT();
01763             depth++;
01764         }
01765     XPC_LOG_OUTDENT();
01766 #endif
01767     return NS_OK;
01768 }