Back to index

lightning-sunbird  0.9+nobinonly
xpcthrower.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  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /* Code for throwing errors into JavaScript. */
00042 
00043 #include "xpcprivate.h"
00044 
00045 JSBool XPCThrower::sVerbose = JS_TRUE;
00046 
00047 // static
00048 void
00049 XPCThrower::Throw(nsresult rv, JSContext* cx)
00050 {
00051     const char* format;
00052     if(JS_IsExceptionPending(cx))
00053         return;
00054     if(!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
00055         format = "";
00056     BuildAndThrowException(cx, rv, format);
00057 }
00058 
00059 /*
00060  * If there has already been an exception thrown, see if we're throwing the
00061  * same sort of exception, and if we are, don't clobber the old one. ccx
00062  * should be the current call context.
00063  */
00064 // static
00065 JSBool
00066 XPCThrower::CheckForPendingException(nsresult result, XPCCallContext &ccx)
00067 {
00068     nsXPConnect* xpc = nsXPConnect::GetXPConnect();
00069     if(!xpc)
00070         return JS_FALSE;
00071 
00072     nsCOMPtr<nsIException> e;
00073     xpc->GetPendingException(getter_AddRefs(e));
00074     if(!e)
00075         return JS_FALSE;
00076     xpc->SetPendingException(nsnull);
00077 
00078     nsresult e_result;
00079     if(NS_FAILED(e->GetResult(&e_result)) || e_result != result)
00080         return JS_FALSE;
00081 
00082     if(!ThrowExceptionObject(ccx, e))
00083         JS_ReportOutOfMemory(ccx);
00084     return JS_TRUE;
00085 }
00086 
00087 // static
00088 void
00089 XPCThrower::Throw(nsresult rv, XPCCallContext& ccx)
00090 {
00091     char* sz;
00092     const char* format;
00093 
00094     if(CheckForPendingException(rv, ccx))
00095         return;
00096 
00097     if(!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
00098         format = "";
00099 
00100     sz = (char*) format;
00101 
00102     if(sz && sVerbose)
00103         Verbosify(ccx, &sz, PR_FALSE);
00104 
00105     BuildAndThrowException(ccx, rv, sz);
00106 
00107     if(sz && sz != format)
00108         JS_smprintf_free(sz);
00109 }
00110 
00111 
00112 // static
00113 void
00114 XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx)
00115 {
00116     char* sz;
00117     const char* format;
00118     const char* name;
00119 
00120     /*
00121     *  If there is a pending exception when the native call returns and
00122     *  it has the same error result as returned by the native call, then
00123     *  the native call may be passing through an error from a previous JS
00124     *  call. So we'll just throw that exception into our JS.
00125     */
00126 
00127     if(CheckForPendingException(result, ccx))
00128         return;
00129 
00130     // else...
00131 
00132     if(!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format) || !format)
00133         format = "";
00134 
00135     if(nsXPCException::NameAndFormatForNSResult(result, &name, nsnull) && name)
00136         sz = JS_smprintf("%s 0x%x (%s)", format, result, name);
00137     else
00138         sz = JS_smprintf("%s 0x%x", format, result);
00139 
00140     if(sz && sVerbose)
00141         Verbosify(ccx, &sz, PR_TRUE);
00142 
00143     BuildAndThrowException(ccx, result, sz);
00144 
00145     if(sz)
00146         JS_smprintf_free(sz);
00147 }
00148 
00149 // static
00150 void
00151 XPCThrower::ThrowBadParam(nsresult rv, uintN paramNum, XPCCallContext& ccx)
00152 {
00153     char* sz;
00154     const char* format;
00155 
00156     if(!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
00157         format = "";
00158 
00159     sz = JS_smprintf("%s arg %d", format, paramNum);
00160 
00161     if(sz && sVerbose)
00162         Verbosify(ccx, &sz, PR_TRUE);
00163 
00164     BuildAndThrowException(ccx, rv, sz);
00165 
00166     if(sz)
00167         JS_smprintf_free(sz);
00168 }
00169 
00170 
00171 // static
00172 void
00173 XPCThrower::Verbosify(XPCCallContext& ccx,
00174                       char** psz, PRBool own)
00175 {
00176     char* sz = nsnull;
00177 
00178     if(ccx.HasInterfaceAndMember())
00179     {
00180         XPCNativeInterface* iface = ccx.GetInterface();
00181 #ifdef XPC_IDISPATCH_SUPPORT
00182         NS_ASSERTION(ccx.GetIDispatchMember() == nsnull || 
00183                         ccx.GetMember() == nsnull,
00184                      "Both IDispatch member and regular XPCOM member "
00185                      "were set in XPCCallContext");
00186         char const * name;
00187         if(ccx.GetIDispatchMember())
00188         {
00189             XPCDispInterface::Member * member = 
00190                 NS_REINTERPRET_CAST(XPCDispInterface::Member*, 
00191                                     ccx.GetIDispatchMember());
00192             if(member && JSVAL_IS_STRING(member->GetName()))
00193             {
00194                 name = JS_GetStringBytes(JSVAL_TO_STRING(member->GetName()));
00195             }
00196             else
00197                 name = "Unknown";
00198         }
00199         else
00200             name = iface->GetMemberName(ccx, ccx.GetMember());
00201         sz = JS_smprintf("%s [%s.%s]",
00202                          *psz,
00203                          iface->GetNameString(),
00204                          name);
00205 #else
00206         sz = JS_smprintf("%s [%s.%s]",
00207                          *psz,
00208                          iface->GetNameString(),
00209                          iface->GetMemberName(ccx, ccx.GetMember()));
00210 #endif
00211     }
00212 
00213     if(sz)
00214     {
00215         if(own)
00216             JS_smprintf_free(*psz);
00217         *psz = sz;
00218     }
00219 }
00220 
00221 // static
00222 void
00223 XPCThrower::BuildAndThrowException(JSContext* cx, nsresult rv, const char* sz)
00224 {
00225     JSBool success = JS_FALSE;
00226 
00227     /* no need to set an expection if the security manager already has */
00228     if(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO && JS_IsExceptionPending(cx))
00229         return;
00230     nsCOMPtr<nsIException> finalException;
00231     nsCOMPtr<nsIException> defaultException;
00232     nsXPCException::NewException(sz, rv, nsnull, nsnull, getter_AddRefs(defaultException));
00233     XPCPerThreadData* tls = XPCPerThreadData::GetData();
00234     if(tls)
00235     {
00236         nsIExceptionManager * exceptionManager = tls->GetExceptionManager();
00237         if(exceptionManager)
00238         {
00239            // Ask the provider for the exception, if there is no provider
00240            // we expect it to set e to null
00241             exceptionManager->GetExceptionFromProvider(
00242                rv,
00243                defaultException,
00244                getter_AddRefs(finalException));
00245             // We should get at least the defaultException back, 
00246             // but just in case
00247             if(finalException == nsnull)
00248             {
00249                 finalException = defaultException;
00250             }
00251         }
00252     }
00253     // XXX Should we put the following test and call to JS_ReportOutOfMemory
00254     // inside this test?
00255     if(finalException)
00256         success = ThrowExceptionObject(cx, finalException);
00257     // If we weren't able to build or throw an exception we're
00258     // most likely out of memory 
00259     if(!success)
00260         JS_ReportOutOfMemory(cx);
00261 }
00262 
00263 static JSObject*
00264 GetGlobalObject(JSContext* cx, JSObject* start)
00265 {
00266     JSObject* parent;
00267 
00268     while((parent = JS_GetParent(cx, start)) != nsnull)
00269         start = parent;
00270     return start;
00271 }
00272 
00273 // static
00274 JSBool
00275 XPCThrower::ThrowExceptionObject(JSContext* cx, nsIException* e)
00276 {
00277     JSBool success = JS_FALSE;
00278     if(e)
00279     {
00280         nsXPConnect* xpc = nsXPConnect::GetXPConnect();
00281         if(xpc)
00282         {
00283             JSObject* glob = JS_GetScopeChain(cx);
00284             if(!glob)
00285                 return JS_FALSE;
00286             glob = GetGlobalObject(cx, glob);
00287 
00288             nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
00289             nsresult rv = xpc->WrapNative(cx, glob, e,
00290                                           NS_GET_IID(nsIException),
00291                                           getter_AddRefs(holder));
00292             if(NS_SUCCEEDED(rv) && holder)
00293             {
00294                 JSObject* obj;
00295                 if(NS_SUCCEEDED(holder->GetJSObject(&obj)))
00296                 {
00297                     JS_SetPendingException(cx, OBJECT_TO_JSVAL(obj));
00298                     success = JS_TRUE;
00299                 }
00300             }
00301         }
00302     }
00303     return success;
00304 }
00305 
00306 #ifdef XPC_IDISPATCH_SUPPORT
00307 // static
00308 void
00309 XPCThrower::ThrowCOMError(JSContext* cx, unsigned long COMErrorCode,
00310                           nsresult rv, const EXCEPINFO * exception)
00311 {
00312     nsCAutoString msg;
00313     IErrorInfo * pError;
00314     const char * format;
00315     if(!nsXPCException::NameAndFormatForNSResult(rv, nsnull, &format))
00316         format = "";
00317     msg = format;
00318     if(exception)
00319     {
00320         msg += NS_STATIC_CAST(const char *,
00321                               _bstr_t(exception->bstrSource, false));
00322         msg += " : ";
00323         msg.AppendInt(NS_STATIC_CAST(PRUint32, COMErrorCode));
00324         msg += " - ";
00325         msg += NS_STATIC_CAST(const char *,
00326                               _bstr_t(exception->bstrDescription, false));
00327     }
00328     else
00329     {
00330         // Get the current COM error object
00331         unsigned long result = GetErrorInfo(0, &pError);
00332         if(SUCCEEDED(result) && pError)
00333         {
00334             // Build an error message from the COM error object
00335             BSTR bstrSource = NULL;
00336             if(SUCCEEDED(pError->GetSource(&bstrSource)) && bstrSource)
00337             {
00338                 _bstr_t src(bstrSource, false);
00339                 msg += NS_STATIC_CAST(const char *,src);
00340                 msg += " : ";
00341             }
00342             msg.AppendInt(NS_STATIC_CAST(PRUint32, COMErrorCode), 16);
00343             BSTR bstrDesc = NULL;
00344             if(SUCCEEDED(pError->GetDescription(&bstrDesc)) && bstrDesc)
00345             {
00346                 msg += " - ";
00347                 _bstr_t desc(bstrDesc, false);
00348                 msg += NS_STATIC_CAST(const char *,desc);
00349             }
00350         }
00351         else
00352         {
00353             // No error object, so just report the result
00354             msg += "COM Error Result = ";
00355             msg.AppendInt(NS_STATIC_CAST(PRUint32, COMErrorCode), 16);
00356         }
00357     }
00358     
00359     XPCThrower::BuildAndThrowException(cx, rv, msg.get());
00360 }
00361 
00362 #endif