Back to index

lightning-sunbird  0.9+nobinonly
ipcDConnectService.cpp
Go to the documentation of this file.
00001 /* vim:set ts=2 sw=2 et cindent: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla IPC.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2004
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@meer.net>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <stdio.h>
00039 
00040 #include "ipcDConnectService.h"
00041 #include "ipcMessageWriter.h"
00042 #include "ipcMessageReader.h"
00043 #include "ipcLog.h"
00044 
00045 #include "nsServiceManagerUtils.h"
00046 #include "nsIInterfaceInfo.h"
00047 #include "nsIInterfaceInfoManager.h"
00048 #include "nsAutoPtr.h"
00049 #include "nsString.h"
00050 #include "nsVoidArray.h"
00051 #include "nsCRT.h"
00052 #include "xptcall.h"
00053 
00054 // XXX TODO:
00055 //  1. add a client observer and prune mInstances when a peer disconnects
00056 //  2. add thread affinity field to SETUP messages
00057 //  3. support array parameters
00058 
00059 //-----------------------------------------------------------------------------
00060 
00061 #define DCONNECT_IPC_TARGETID                      \
00062 { /* 43ca47ef-ebc8-47a2-9679-a4703218089f */       \
00063   0x43ca47ef,                                      \
00064   0xebc8,                                          \
00065   0x47a2,                                          \
00066   {0x96, 0x79, 0xa4, 0x70, 0x32, 0x18, 0x08, 0x9f} \
00067 }
00068 static const nsID kDConnectTargetID = DCONNECT_IPC_TARGETID;
00069 
00070 //-----------------------------------------------------------------------------
00071 
00072 #define DCON_WAIT_TIMEOUT PR_INTERVAL_NO_TIMEOUT
00073 
00074 // used elsewhere like nsAtomTable to safely represent the integral value
00075 // of an address.
00076 typedef unsigned long PtrBits;
00077 
00078 //-----------------------------------------------------------------------------
00079 
00080 //
00081 // +--------------------------------+
00082 // | opcode : 1 byte                |
00083 // +--------------------------------+
00084 // | flags  : 1 byte                |
00085 // +--------------------------------+
00086 // .                                .
00087 // . variable payload               .
00088 // .                                .
00089 // +--------------------------------+
00090 //
00091 
00092 // dconnect major opcodes
00093 #define DCON_OP_SETUP   1
00094 #define DCON_OP_RELEASE 2
00095 #define DCON_OP_INVOKE  3
00096 
00097 #define DCON_OP_SETUP_REPLY  4
00098 #define DCON_OP_INVOKE_REPLY 5
00099 
00100 // dconnect minor opcodes for DCON_OP_SETUP
00101 #define DCON_OP_SETUP_NEW_INST_CLASSID    1
00102 #define DCON_OP_SETUP_NEW_INST_CONTRACTID 2
00103 #define DCON_OP_SETUP_GET_SERV_CLASSID    3
00104 #define DCON_OP_SETUP_GET_SERV_CONTRACTID 4
00105 #define DCON_OP_SETUP_QUERY_INTERFACE     5
00106 
00107 // dconnect minor opcodes for RELEASE
00108 // dconnect minor opcodes for INVOKE
00109 
00110 struct DConnectOp
00111 {
00112   PRUint8  opcode_major;
00113   PRUint8  opcode_minor;
00114   PRUint32 request_index; // initialized with NewRequestIndex
00115 };
00116 
00117 typedef class DConnectInstance* DConAddr;
00118 
00119 // SETUP structs
00120 
00121 struct DConnectSetup : DConnectOp
00122 {
00123   nsID iid;
00124 };
00125 
00126 struct DConnectSetupClassID : DConnectSetup
00127 {
00128   nsID classid;
00129 };
00130 
00131 struct DConnectSetupContractID : DConnectSetup
00132 {
00133   char contractid[1]; // variable length
00134 };
00135 
00136 struct DConnectSetupQueryInterface : DConnectSetup
00137 {
00138   DConAddr instance;
00139 };
00140 
00141 // SETUP_REPLY struct
00142 
00143 struct DConnectSetupReply : DConnectOp
00144 {
00145   DConAddr instance;
00146   nsresult status;
00147 };
00148 
00149 // RELEASE struct
00150 
00151 struct DConnectRelease : DConnectOp
00152 {
00153   DConAddr instance;
00154 };
00155 
00156 // INVOKE struct
00157 
00158 struct DConnectInvoke : DConnectOp
00159 {
00160   DConAddr instance;
00161   PRUint16 method_index;
00162   // followed by an array of in-param blobs
00163 };
00164 
00165 // INVOKE_REPLY struct
00166 
00167 struct DConnectInvokeReply : DConnectOp
00168 {
00169   nsresult result;
00170   // followed by an array of out-param blobs
00171 };
00172 
00173 //-----------------------------------------------------------------------------
00174 
00175 static ipcDConnectService *gDConnect;
00176 
00177 //-----------------------------------------------------------------------------
00178 
00179 static nsresult
00180 SetupPeerInstance(PRUint32 aPeerID, DConnectSetup *aMsg, PRUint32 aMsgLen,
00181                   void **aInstancePtr);
00182 
00183 //-----------------------------------------------------------------------------
00184 
00185 // A wrapper class holding an instance to an in-process XPCOM object.
00186 
00187 class DConnectInstance
00188 {
00189 public:
00190   DConnectInstance(PRUint32 peer, nsIInterfaceInfo *iinfo, nsISupports *instance)
00191     : mPeer(peer)
00192     , mIInfo(iinfo)
00193     , mInstance(instance)
00194   {}
00195 
00196   nsISupports      *RealInstance()  { return mInstance; }
00197   nsIInterfaceInfo *InterfaceInfo() { return mIInfo; }
00198   
00199 private:
00200   PRUint32                   mPeer;  // peer process "owning" this instance
00201   nsCOMPtr<nsIInterfaceInfo> mIInfo;
00202   nsCOMPtr<nsISupports>      mInstance;
00203 };
00204 
00205 static void
00206 DeleteWrappers(nsVoidArray &wrappers)
00207 {
00208   for (PRInt32 i=0; i<wrappers.Count(); ++i)
00209     gDConnect->DeleteInstance((DConnectInstance *) wrappers[i]);
00210 }
00211 
00212 //-----------------------------------------------------------------------------
00213 
00214 static nsresult
00215 SerializeParam(ipcMessageWriter &writer, const nsXPTType &t, const nsXPTCMiniVariant &v)
00216 {
00217   switch (t.TagPart())
00218   {
00219     case nsXPTType::T_I8:
00220     case nsXPTType::T_U8:
00221       writer.PutInt8(v.val.u8);
00222       break;
00223 
00224     case nsXPTType::T_I16:
00225     case nsXPTType::T_U16:
00226       writer.PutInt16(v.val.u16);
00227       break;
00228 
00229     case nsXPTType::T_I32:
00230     case nsXPTType::T_U32:
00231       writer.PutInt32(v.val.u32);
00232       break;
00233 
00234     case nsXPTType::T_I64:
00235     case nsXPTType::T_U64:
00236       writer.PutBytes(&v.val.u64, sizeof(PRUint64));
00237       break;
00238 
00239     case nsXPTType::T_FLOAT:
00240       writer.PutBytes(&v.val.f, sizeof(float));
00241       break;
00242 
00243     case nsXPTType::T_DOUBLE:
00244       writer.PutBytes(&v.val.d, sizeof(double));
00245       break;
00246 
00247     case nsXPTType::T_BOOL:
00248       writer.PutBytes(&v.val.b, sizeof(PRBool));
00249       break;
00250 
00251     case nsXPTType::T_CHAR:
00252       writer.PutBytes(&v.val.c, sizeof(char));
00253       break;
00254 
00255     case nsXPTType::T_WCHAR:
00256       writer.PutBytes(&v.val.wc, sizeof(PRUnichar));
00257       break;
00258 
00259     case nsXPTType::T_IID:
00260       writer.PutBytes(v.val.p, sizeof(nsID));
00261       break;
00262 
00263     case nsXPTType::T_CHAR_STR:
00264       {
00265         int len = strlen((const char *) v.val.p);
00266         writer.PutInt32(len);
00267         writer.PutBytes(v.val.p, len);
00268       }
00269       break;
00270 
00271     case nsXPTType::T_WCHAR_STR:
00272       {
00273         int len = 2 * nsCRT::strlen((const PRUnichar *) v.val.p);
00274         writer.PutInt32(len);
00275         writer.PutBytes(v.val.p, len);
00276       }
00277       break;
00278 
00279     case nsXPTType::T_INTERFACE:
00280     case nsXPTType::T_INTERFACE_IS:
00281       NS_NOTREACHED("this should be handled elsewhere");
00282       return NS_ERROR_UNEXPECTED;
00283 
00284     case nsXPTType::T_ASTRING:
00285     case nsXPTType::T_DOMSTRING:
00286       {
00287         const nsAString *str = (const nsAString *) v.val.p;
00288 
00289         PRUint32 len = 2 * str->Length();
00290         nsAString::const_iterator begin;
00291         const PRUnichar *data = str->BeginReading(begin).get();
00292 
00293         writer.PutInt32(len);
00294         writer.PutBytes(data, len);
00295       }
00296       break;
00297 
00298     case nsXPTType::T_UTF8STRING:
00299     case nsXPTType::T_CSTRING:
00300       {
00301         const nsACString *str = (const nsACString *) v.val.p;
00302 
00303         PRUint32 len = str->Length();
00304         nsACString::const_iterator begin;
00305         const char *data = str->BeginReading(begin).get();
00306 
00307         writer.PutInt32(len);
00308         writer.PutBytes(data, len);
00309       }
00310       break;
00311 
00312     case nsXPTType::T_ARRAY:
00313       LOG(("array types are not yet supported\n"));
00314       return NS_ERROR_NOT_IMPLEMENTED;
00315 
00316     case nsXPTType::T_VOID:
00317     case nsXPTType::T_PSTRING_SIZE_IS:
00318     case nsXPTType::T_PWSTRING_SIZE_IS:
00319     default:
00320       LOG(("unexpected parameter type\n"));
00321       return NS_ERROR_UNEXPECTED;
00322   }
00323   return NS_OK;
00324 }
00325 
00326 static nsresult
00327 DeserializeParam(ipcMessageReader &reader, const nsXPTType &t, nsXPTCVariant &v)
00328 {
00329   // defaults
00330   v.ptr = nsnull;
00331   v.type = t;
00332   v.flags = 0;
00333 
00334   switch (t.TagPart())
00335   {
00336     case nsXPTType::T_I8:
00337     case nsXPTType::T_U8:
00338       v.val.u8 = reader.GetInt8();
00339       break;
00340 
00341     case nsXPTType::T_I16:
00342     case nsXPTType::T_U16:
00343       v.val.u16 = reader.GetInt16();
00344       break;
00345 
00346     case nsXPTType::T_I32:
00347     case nsXPTType::T_U32:
00348       v.val.u32 = reader.GetInt32();
00349       break;
00350 
00351     case nsXPTType::T_I64:
00352     case nsXPTType::T_U64:
00353       reader.GetBytes(&v.val.u64, sizeof(v.val.u64));
00354       break;
00355 
00356     case nsXPTType::T_FLOAT:
00357       reader.GetBytes(&v.val.f, sizeof(v.val.f));
00358       break;
00359 
00360     case nsXPTType::T_DOUBLE:
00361       reader.GetBytes(&v.val.d, sizeof(v.val.d));
00362       break;
00363 
00364     case nsXPTType::T_BOOL:
00365       reader.GetBytes(&v.val.b, sizeof(v.val.b));
00366       break;
00367 
00368     case nsXPTType::T_CHAR:
00369       reader.GetBytes(&v.val.c, sizeof(v.val.c));
00370       break;
00371 
00372     case nsXPTType::T_WCHAR:
00373       reader.GetBytes(&v.val.wc, sizeof(v.val.wc));
00374       break;
00375 
00376     case nsXPTType::T_IID:
00377       {
00378         nsID *buf = (nsID *) malloc(sizeof(nsID));
00379         NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
00380         reader.GetBytes(buf, sizeof(nsID));
00381         v.val.p = v.ptr = buf;
00382         v.flags = nsXPTCVariant::PTR_IS_DATA | nsXPTCVariant::VAL_IS_ALLOCD;
00383       }
00384       break;
00385 
00386     case nsXPTType::T_CHAR_STR:
00387       {
00388         PRUint32 len = reader.GetInt32();
00389         char *buf = (char *) malloc(len + 1);
00390         NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
00391         reader.GetBytes(buf, len);
00392         buf[len] = char(0);
00393 
00394         v.val.p = v.ptr = buf;
00395         v.flags = nsXPTCVariant::PTR_IS_DATA | nsXPTCVariant::VAL_IS_ALLOCD;
00396       }
00397       break;
00398 
00399     case nsXPTType::T_WCHAR_STR:
00400       {
00401         PRUint32 len = reader.GetInt32();
00402         PRUnichar *buf = (PRUnichar *) malloc(len + 2);
00403         NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
00404         reader.GetBytes(buf, len);
00405         buf[len] = PRUnichar(0);
00406 
00407         v.val.p = v.ptr = buf;
00408         v.flags = nsXPTCVariant::PTR_IS_DATA | nsXPTCVariant::VAL_IS_ALLOCD;
00409       }
00410       break;
00411 
00412     case nsXPTType::T_INTERFACE:
00413     case nsXPTType::T_INTERFACE_IS:
00414       {
00415         reader.GetBytes(&v.ptr, sizeof(void *));
00416         v.val.p = nsnull;
00417         v.flags = nsXPTCVariant::PTR_IS_DATA;
00418       }
00419       break;
00420 
00421     case nsXPTType::T_ASTRING:
00422     case nsXPTType::T_DOMSTRING:
00423       {
00424         PRUint32 len = reader.GetInt32();
00425 
00426         nsString *str = new nsString();
00427         if (!str || !(EnsureStringLength(*str, len/2)))
00428           return NS_ERROR_OUT_OF_MEMORY;
00429         PRUnichar *buf = str->BeginWriting();
00430         reader.GetBytes(buf, len);
00431 
00432         v.val.p = v.ptr = str;
00433         v.flags = nsXPTCVariant::PTR_IS_DATA | nsXPTCVariant::VAL_IS_DOMSTR;
00434       }
00435       break;
00436 
00437     case nsXPTType::T_UTF8STRING:
00438     case nsXPTType::T_CSTRING:
00439       {
00440         PRUint32 len = reader.GetInt32();
00441 
00442         nsCString *str = new nsCString();
00443         if (!str || !(EnsureStringLength(*str, len)))
00444           return NS_ERROR_OUT_OF_MEMORY;
00445         char *buf = str->BeginWriting();
00446         reader.GetBytes(buf, len);
00447 
00448         v.val.p = v.ptr = str;
00449         v.flags = nsXPTCVariant::PTR_IS_DATA;
00450 
00451         // this distinction here is pretty pointless
00452         if (t.TagPart() == nsXPTType::T_CSTRING)
00453           v.flags |= nsXPTCVariant::VAL_IS_CSTR;
00454         else
00455           v.flags |= nsXPTCVariant::VAL_IS_UTF8STR;
00456       }
00457       break;
00458 
00459     case nsXPTType::T_ARRAY:
00460       LOG(("array types are not yet supported\n"));
00461       return NS_ERROR_NOT_IMPLEMENTED;
00462 
00463     case nsXPTType::T_VOID:
00464     case nsXPTType::T_PSTRING_SIZE_IS:
00465     case nsXPTType::T_PWSTRING_SIZE_IS:
00466     default:
00467       LOG(("unexpected parameter type\n"));
00468       return NS_ERROR_UNEXPECTED;
00469   }
00470   return NS_OK;
00471 }
00472 
00473 static nsresult
00474 SetupParam(const nsXPTParamInfo &p, nsXPTCVariant &v)
00475 {
00476   const nsXPTType &t = p.GetType();
00477 
00478   if (p.IsIn() && p.IsDipper())
00479   {
00480     v.ptr = nsnull;
00481 
00482     switch (t.TagPart())
00483     {
00484       case nsXPTType::T_ASTRING:
00485       case nsXPTType::T_DOMSTRING:
00486         v.ptr = new nsString();
00487         if (!v.ptr)
00488           return NS_ERROR_OUT_OF_MEMORY;
00489         v.val.p = v.ptr;
00490         v.type = t;
00491         v.flags = nsXPTCVariant::PTR_IS_DATA | nsXPTCVariant::VAL_IS_DOMSTR;
00492         break;
00493 
00494       case nsXPTType::T_UTF8STRING:
00495       case nsXPTType::T_CSTRING:
00496         v.ptr = new nsCString();
00497         if (!v.ptr)
00498           return NS_ERROR_OUT_OF_MEMORY;
00499         v.val.p = v.ptr;
00500         v.type = t;
00501         v.flags = nsXPTCVariant::PTR_IS_DATA | nsXPTCVariant::VAL_IS_CSTR;
00502         break;
00503 
00504       default:
00505         LOG(("unhandled dipper: type=%d\n", t.TagPart()));
00506         return NS_ERROR_UNEXPECTED;
00507     }
00508   }
00509   else if (p.IsOut())
00510   {
00511     v.ptr = &v.val;
00512     v.type = t;
00513     v.flags = nsXPTCVariant::PTR_IS_DATA;
00514   }
00515 
00516   return NS_OK;
00517 }
00518 
00519 static void
00520 FinishParam(nsXPTCVariant &v)
00521 {
00522   if (!v.val.p)
00523     return;
00524 
00525   if (v.IsValAllocated())
00526     free(v.val.p);
00527   else if (v.IsValInterface())
00528     ((nsISupports *) v.val.p)->Release();
00529   else if (v.IsValDOMString())
00530     delete (nsAString *) v.val.p;
00531   else if (v.IsValUTF8String() || v.IsValCString())
00532     delete (nsACString *) v.val.p;
00533 }
00534 
00535 static nsresult
00536 DeserializeResult(ipcMessageReader &reader, const nsXPTType &t, nsXPTCMiniVariant &v)
00537 {
00538   if (v.val.p == nsnull)
00539     return NS_OK;
00540 
00541   switch (t.TagPart())
00542   {
00543     case nsXPTType::T_I8:
00544     case nsXPTType::T_U8:
00545       *((PRUint8 *) v.val.p) = reader.GetInt8();
00546       break;
00547 
00548     case nsXPTType::T_I16:
00549     case nsXPTType::T_U16:
00550       *((PRUint16 *) v.val.p) = reader.GetInt16();
00551       break;
00552 
00553     case nsXPTType::T_I32:
00554     case nsXPTType::T_U32:
00555       *((PRUint32 *) v.val.p) = reader.GetInt32();
00556       break;
00557 
00558     case nsXPTType::T_I64:
00559     case nsXPTType::T_U64:
00560       reader.GetBytes(v.val.p, sizeof(PRUint64));
00561       break;
00562 
00563     case nsXPTType::T_FLOAT:
00564       reader.GetBytes(v.val.p, sizeof(float));
00565       break;
00566 
00567     case nsXPTType::T_DOUBLE:
00568       reader.GetBytes(v.val.p, sizeof(double));
00569       break;
00570 
00571     case nsXPTType::T_BOOL:
00572       reader.GetBytes(v.val.p, sizeof(PRBool));
00573       break;
00574 
00575     case nsXPTType::T_CHAR:
00576       reader.GetBytes(v.val.p, sizeof(char));
00577       break;
00578 
00579     case nsXPTType::T_WCHAR:
00580       reader.GetBytes(v.val.p, sizeof(PRUnichar));
00581       break;
00582 
00583     case nsXPTType::T_IID:
00584       {
00585         nsID *buf = (nsID *) nsMemory::Alloc(sizeof(nsID));
00586         reader.GetBytes(buf, sizeof(nsID));
00587         *((nsID **) v.val.p) = buf;
00588       }
00589       break;
00590 
00591     case nsXPTType::T_CHAR_STR:
00592       {
00593         PRUint32 len = reader.GetInt32();
00594         char *buf = (char *) nsMemory::Alloc(len + 1);
00595         reader.GetBytes(buf, len);
00596         buf[len] = char(0);
00597 
00598         *((char **) v.val.p) = buf;
00599       }
00600       break;
00601 
00602     case nsXPTType::T_WCHAR_STR:
00603       {
00604         PRUint32 len = reader.GetInt32();
00605         PRUnichar *buf = (PRUnichar *) nsMemory::Alloc(len + 2);
00606         reader.GetBytes(buf, len);
00607         buf[len] = PRUnichar(0);
00608 
00609         *((PRUnichar **) v.val.p) = buf;
00610       }
00611       break;
00612 
00613     case nsXPTType::T_INTERFACE:
00614     case nsXPTType::T_INTERFACE_IS:
00615       {
00616         // stub creation will be handled outside this routine.  we only
00617         // deserialize the DConAddr into v.val.p temporarily.
00618         void *ptr;
00619         reader.GetBytes(&ptr, sizeof(void *));
00620         *((void **) v.val.p) = ptr;
00621       }
00622       break;
00623 
00624     case nsXPTType::T_ASTRING:
00625     case nsXPTType::T_DOMSTRING:
00626       {
00627         PRUint32 len = reader.GetInt32();
00628 
00629         nsAString *str = (nsAString *) v.val.p;
00630 
00631         if (!str || !(EnsureStringLength(*str, len/2)))
00632           return NS_ERROR_OUT_OF_MEMORY;
00633         nsAString::iterator begin;
00634         str->BeginWriting(begin);
00635 
00636         reader.GetBytes(begin.get(), len);
00637       }
00638       break;
00639 
00640     case nsXPTType::T_UTF8STRING:
00641     case nsXPTType::T_CSTRING:
00642       {
00643         PRUint32 len = reader.GetInt32();
00644 
00645         nsACString *str = (nsACString *) v.val.p;
00646 
00647         if (!str || !(EnsureStringLength(*str, len)))
00648           return NS_ERROR_OUT_OF_MEMORY;
00649         nsACString::iterator begin;
00650         str->BeginWriting(begin);
00651 
00652         reader.GetBytes(begin.get(), len);
00653       }
00654       break;
00655 
00656     case nsXPTType::T_ARRAY:
00657       LOG(("array types are not yet supported\n"));
00658       return NS_ERROR_NOT_IMPLEMENTED;
00659 
00660     case nsXPTType::T_VOID:
00661     case nsXPTType::T_PSTRING_SIZE_IS:
00662     case nsXPTType::T_PWSTRING_SIZE_IS:
00663     default:
00664       LOG(("unexpected parameter type\n"));
00665       return NS_ERROR_UNEXPECTED;
00666   }
00667   return NS_OK;
00668 }
00669 
00670 //-----------------------------------------------------------------------------
00671 
00672 static PRUint32
00673 NewRequestIndex()
00674 {
00675   static PRUint32 sRequestIndex;
00676   return ++sRequestIndex;
00677 }
00678 
00679 //-----------------------------------------------------------------------------
00680 
00681 class DConnectCompletion : public ipcIMessageObserver
00682 {
00683 public:
00684   DConnectCompletion(PRUint32 requestIndex)
00685     : mRequestIndex(requestIndex)
00686   {}
00687 
00688   // stack based only
00689   NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
00690   NS_IMETHOD_(nsrefcnt) Release() { return 1; }
00691 
00692   NS_IMETHOD QueryInterface(const nsIID &aIID, void **aInstancePtr);
00693 
00694   NS_IMETHOD OnMessageAvailable(PRUint32 aSenderID, const nsID &aTarget,
00695                                 const PRUint8 *aData, PRUint32 aDataLen)
00696   {
00697     const DConnectOp *op = (const DConnectOp *) aData;
00698     if ((aDataLen >= sizeof(DConnectOp)) && (op->request_index == mRequestIndex))
00699       OnResponseAvailable(aSenderID, op, aDataLen);
00700     else
00701       gDConnect->OnMessageAvailable(aSenderID, aTarget, aData, aDataLen);
00702     return NS_OK;
00703   }
00704 
00705   virtual void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen) = 0;
00706 
00707 protected:
00708   PRUint32 mRequestIndex;
00709 };
00710 NS_IMPL_QUERY_INTERFACE1(DConnectCompletion, ipcIMessageObserver)
00711 
00712 //-----------------------------------------------------------------------------
00713 
00714 class DConnectInvokeCompletion : public DConnectCompletion
00715 {
00716 public:
00717   DConnectInvokeCompletion(const DConnectInvoke *invoke)
00718     : DConnectCompletion(invoke->request_index)
00719     , mReply(nsnull)
00720     , mParamsLen(0)
00721   {}
00722 
00723   ~DConnectInvokeCompletion() { if (mReply) free(mReply); }
00724 
00725   void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen)
00726   {
00727     mReply = (DConnectInvokeReply *) malloc(opLen);
00728     if (!mReply)
00729       return; // out of memory
00730     memcpy(mReply, op, opLen);
00731 
00732     // the length in bytes of the parameter blob
00733     mParamsLen = opLen - sizeof(*mReply);
00734   }
00735 
00736   PRBool IsPending() const { return mReply == nsnull; }
00737   nsresult GetResult() const { return mReply->result; }
00738 
00739   const PRUint8 *Params() const { return (const PRUint8 *) (mReply + 1); }
00740   PRUint32 ParamsLen() const { return mParamsLen; }
00741 
00742   const DConnectInvokeReply *Reply() const { return mReply; }
00743 
00744 private:
00745   DConnectInvokeReply *mReply;
00746   PRUint32             mParamsLen;
00747 };
00748 
00749 //-----------------------------------------------------------------------------
00750 
00751 #define DCONNECT_STUB_ID                           \
00752 { /* 132c1f14-5442-49cb-8fe6-e60214bbf1db */       \
00753   0x132c1f14,                                      \
00754   0x5442,                                          \
00755   0x49cb,                                          \
00756   {0x8f, 0xe6, 0xe6, 0x02, 0x14, 0xbb, 0xf1, 0xdb} \
00757 }
00758 static NS_DEFINE_IID(kDConnectStubID, DCONNECT_STUB_ID);
00759 
00760 // this class represents the non-local object instance.
00761 
00762 class DConnectStub : public nsXPTCStubBase
00763 {
00764 public:
00765   NS_DECL_ISUPPORTS
00766 
00767   DConnectStub(nsIInterfaceInfo *aIInfo,
00768                DConAddr aInstance,
00769                PRUint32 aPeerID)
00770     : mIInfo(aIInfo)
00771     , mMaster(nsnull)
00772     , mInstance(aInstance)
00773     , mPeerID(aPeerID)
00774     {}
00775 
00776   // return a refcounted pointer to the InterfaceInfo for this object
00777   // NOTE: on some platforms this MUST not fail or we crash!
00778   NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo **aInfo);
00779 
00780   // call this method and return result
00781   NS_IMETHOD CallMethod(PRUint16 aMethodIndex,
00782                         const nsXPTMethodInfo *aInfo,
00783                         nsXPTCMiniVariant *aParams);
00784 
00785   DConAddr Instance() { return mInstance; }
00786   PRUint32 PeerID()   { return mPeerID; }
00787 
00788 private:
00789   NS_HIDDEN ~DConnectStub();
00790 
00791   // returns a weak reference to a child supporting the specified interface
00792   NS_HIDDEN_(DConnectStub *) FindStubSupportingIID(const nsID &aIID);
00793 
00794   // returns true if this stub supports the specified interface
00795   NS_HIDDEN_(PRBool) SupportsIID(const nsID &aIID);
00796 
00797 private:
00798   nsCOMPtr<nsIInterfaceInfo> mIInfo;
00799 
00800   nsVoidArray   mChildren; // weak references (cleared by the children)
00801   DConnectStub *mMaster;   // strong reference
00802 
00803   // uniquely identifies this object instance between peers.
00804   DConAddr mInstance;
00805 
00806   // the "client id" of our IPC peer.  this guy owns the real object.
00807   PRUint32 mPeerID;
00808 };
00809 
00810 static nsresult
00811 CreateStub(const nsID &iid, PRUint32 peer, DConAddr instance, DConnectStub **result)
00812 {
00813   nsresult rv;
00814 
00815   nsCOMPtr<nsIInterfaceInfo> iinfo;
00816   rv = gDConnect->GetInterfaceInfo(iid, getter_AddRefs(iinfo));
00817   if (NS_FAILED(rv))
00818     return rv;
00819 
00820   DConnectStub *stub = new DConnectStub(iinfo,
00821                                         instance,
00822                                         peer);
00823   if (NS_UNLIKELY(!stub))
00824     rv = NS_ERROR_OUT_OF_MEMORY;
00825   else
00826   {
00827     NS_ADDREF(stub);
00828     *result = stub;
00829   }
00830 
00831   return rv;
00832 }
00833 
00834 static nsresult
00835 SerializeInterfaceParam(ipcMessageWriter &writer,
00836                         PRUint32 peer, const nsID &iid,
00837                         const nsXPTType &type, const nsXPTCMiniVariant &v,
00838                         nsVoidArray &wrappers)
00839 {
00840   // we create an instance wrapper, and assume that the other side will send a
00841   // RELEASE message when it no longer needs the instance wrapper.  that will
00842   // usually happen after the call returns.
00843   //
00844   // XXX a lazy scheme might be better, but for now simplicity wins.
00845 
00846   // if the interface pointer references a DConnectStub corresponding
00847   // to an object in the address space of the peer, then no need to
00848   // create a new wrapper.
00849 
00850   nsISupports *obj = (nsISupports *) v.val.p;
00851   if (!obj)
00852   {
00853     // write null address
00854     writer.PutBytes(&obj, sizeof(obj));
00855   }
00856   else
00857   {
00858     DConnectStub *stub = nsnull;
00859     nsresult rv = obj->QueryInterface(kDConnectStubID, (void **) &stub);
00860     if (NS_SUCCEEDED(rv) && (stub->PeerID() == peer))
00861     {
00862       void *p = stub->Instance();
00863       writer.PutBytes(&p, sizeof(p));
00864     }
00865     else
00866     {
00867       // create instance wrapper
00868 
00869       nsCOMPtr<nsIInterfaceInfo> iinfo;
00870       rv = gDConnect->GetInterfaceInfo(iid, getter_AddRefs(iinfo));
00871       if (NS_FAILED(rv))
00872         return rv;
00873 
00874       DConnectInstance *wrapper = nsnull;
00875 
00876       wrapper = new DConnectInstance(peer, iinfo, obj);
00877       if (!wrapper)
00878         return NS_ERROR_OUT_OF_MEMORY;
00879 
00880       if (!wrappers.AppendElement(wrapper))
00881       {
00882         delete wrapper;
00883         return NS_ERROR_OUT_OF_MEMORY;
00884       }
00885 
00886       rv = gDConnect->StoreInstance(wrapper);
00887       if (NS_FAILED(rv))
00888       {
00889         wrappers.RemoveElement(wrapper);
00890         delete wrapper;
00891         return rv;
00892       }
00893 
00894       // send address of the instance wrapper, and set the low bit
00895       // to indicate that this is an instance wrapper.
00896       PtrBits bits = ((PtrBits) wrapper) | 0x1;
00897       writer.PutBytes(&bits, sizeof(bits));
00898     }
00899     NS_IF_RELEASE(stub);
00900   }
00901   return NS_OK;
00902 }
00903 
00904 DConnectStub::~DConnectStub()
00905 {
00906   // destroying stub, notify peer that we have released our reference
00907 
00908   nsresult rv;
00909 
00910   DConnectRelease msg;
00911   msg.opcode_major = DCON_OP_RELEASE;
00912   msg.opcode_minor = 0;
00913   msg.instance = mInstance;
00914 
00915   // fire off asynchronously... we don't expect any response to this message.
00916   rv = IPC_SendMessage(mPeerID, kDConnectTargetID,
00917                        (const PRUint8 *) &msg, sizeof(msg));
00918   if (NS_FAILED(rv))
00919     NS_WARNING("failed to send RELEASE event");
00920 
00921   if (!mMaster)
00922   {
00923     // delete each child stub
00924     for (PRInt32 i=0; i<mChildren.Count(); ++i)
00925       delete (DConnectStub *) mChildren[i];
00926   }
00927 }
00928 
00929 PRBool
00930 DConnectStub::SupportsIID(const nsID &iid)
00931 {
00932   PRBool match;
00933   nsCOMPtr<nsIInterfaceInfo> iter = mIInfo;
00934   do
00935   {
00936     if (NS_SUCCEEDED(iter->IsIID(&iid, &match)) && match)
00937       return PR_TRUE;
00938 
00939     nsCOMPtr<nsIInterfaceInfo> parent;
00940     iter->GetParent(getter_AddRefs(parent));
00941     iter = parent;
00942   }
00943   while (iter != nsnull);
00944 
00945   return PR_FALSE;
00946 }
00947 
00948 DConnectStub *
00949 DConnectStub::FindStubSupportingIID(const nsID &iid)
00950 {
00951   NS_ASSERTION(mMaster == nsnull, "this is not a master stub");
00952 
00953   if (SupportsIID(iid))
00954     return this;
00955 
00956   for (PRInt32 i=0; i<mChildren.Count(); ++i)
00957   {
00958     DConnectStub *child = (DConnectStub *) mChildren[i];
00959     if (child->SupportsIID(iid))
00960       return child;
00961   }
00962   return nsnull;
00963 }
00964 
00965 NS_IMETHODIMP_(nsrefcnt)
00966 DConnectStub::AddRef()
00967 {
00968   if (mMaster)
00969     return mMaster->AddRef();
00970 
00971   NS_ASSERT_OWNINGTHREAD("DConnectStub");
00972   ++mRefCnt;
00973   NS_LOG_ADDREF(this, mRefCnt, "DConnectStub", sizeof(*this));
00974   return mRefCnt;
00975 }
00976 
00977 NS_IMETHODIMP_(nsrefcnt)
00978 DConnectStub::Release()
00979 {
00980   if (mMaster)
00981     return mMaster->Release();
00982 
00983   NS_ASSERT_OWNINGTHREAD("DConnectStub");
00984   --mRefCnt;
00985   NS_LOG_RELEASE(this, mRefCnt, "DConnectStub");
00986   if (mRefCnt == 0)
00987   {
00988     mRefCnt = 1; /* stabilize */
00989     delete this;
00990     return 0;
00991   }                                                                           \
00992   return mRefCnt;        
00993 }
00994 
00995 NS_IMETHODIMP
00996 DConnectStub::QueryInterface(const nsID &aIID, void **aInstancePtr)
00997 {
00998   DConnectStub *master = mMaster ? mMaster : this;
00999 
01000   // always return the master stub for nsISupports
01001   if (aIID.Equals(NS_GET_IID(nsISupports)))
01002   {
01003     *aInstancePtr = master;
01004     NS_ADDREF(master);
01005     return NS_OK;
01006   }
01007 
01008   // used to discover if this is a DConnectStub instance.
01009   if (aIID.Equals(kDConnectStubID))
01010   {
01011     *aInstancePtr = this;
01012     NS_ADDREF_THIS();
01013     return NS_OK;
01014   }
01015 
01016   // it might seem attractive to check this stub first for parent
01017   // interfaces, but we should instead always defer to the master
01018   // since that ensures that the result of QI is independent of
01019   // the stub on which it is called.
01020 #if 0
01021   if (SupportsIID(aIID))
01022   {
01023     *aInstancePtr = this;
01024     NS_ADDREF(this);
01025     return NS_OK;
01026   }
01027 #endif
01028 
01029   // does any existing stub support the requested IID?
01030   DConnectStub *stub = master->FindStubSupportingIID(aIID);
01031   if (stub)
01032   {
01033     *aInstancePtr = stub;
01034     NS_ADDREF(stub);
01035     return NS_OK;
01036   }
01037 
01038   // else, we need to query the peer object
01039   LOG(("calling QueryInterface on peer object\n"));
01040 
01041   DConnectSetupQueryInterface msg;
01042   msg.opcode_minor = DCON_OP_SETUP_QUERY_INTERFACE;
01043   msg.iid = aIID;
01044   msg.instance = mInstance;
01045 
01046   void *result;
01047 
01048   nsresult rv = SetupPeerInstance(mPeerID, &msg, sizeof(msg), &result);
01049   if (NS_FAILED(rv))
01050     return rv;
01051 
01052   stub = (DConnectStub *) result;
01053 
01054   // add stub to the master's list of children, so we can preserve
01055   // symmetry in future QI calls.  the master will delete each child
01056   // when it is destroyed.  the refcount of each child is bound to
01057   // the refcount of the master.  this is done to deal with code
01058   // like this:
01059   //
01060   //   nsCOMPtr<nsIBar> bar = ...;
01061   //   nsIFoo *foo;
01062   //   {
01063   //     nsCOMPtr<nsIFoo> temp = do_QueryInterface(bar);
01064   //     foo = temp;
01065   //   }
01066   //   foo->DoStuff();
01067   //
01068   // while this code is not valid XPCOM (since it is using |foo|
01069   // after having called Release on it), such code is unfortunately
01070   // very common in the mozilla codebase.  the assumption this code
01071   // is making is that so long as |bar| is alive, it should be valid
01072   // to access |foo| even if the code doesn't own a strong reference
01073   // to |foo|!  clearly wrong, but we need to support it anyways.
01074 
01075   stub->mMaster = master;
01076   master->mChildren.AppendElement(stub);
01077 
01078   *aInstancePtr = stub;
01079   NS_ADDREF(stub);
01080   return NS_OK;
01081 }
01082 
01083 NS_IMETHODIMP
01084 DConnectStub::GetInterfaceInfo(nsIInterfaceInfo **aInfo)
01085 {
01086   NS_ADDREF(*aInfo = mIInfo);
01087   return NS_OK;
01088 }
01089 
01090 NS_IMETHODIMP
01091 DConnectStub::CallMethod(PRUint16 aMethodIndex,
01092                          const nsXPTMethodInfo *aInfo,
01093                          nsXPTCMiniVariant *aParams)
01094 {
01095   nsresult rv;
01096 
01097   LOG(("DConnectStub::CallMethod [methodIndex=%hu]\n", aMethodIndex));
01098 
01099   // dump arguments
01100 
01101   PRUint8 i, paramCount = aInfo->GetParamCount();
01102 
01103   LOG(("  name=%s\n", aInfo->GetName()));
01104   LOG(("  param-count=%u\n", (PRUint32) paramCount));
01105 
01106   ipcMessageWriter writer(16 * paramCount);
01107 
01108   // INVOKE message header
01109   DConnectInvoke invoke;
01110   invoke.opcode_major = DCON_OP_INVOKE;
01111   invoke.opcode_minor = 0;
01112   invoke.request_index = NewRequestIndex();
01113   invoke.instance = mInstance;
01114   invoke.method_index = aMethodIndex;
01115 
01116   writer.PutBytes(&invoke, sizeof(invoke));
01117 
01118   // list of wrappers that get created during parameter serialization.  if we
01119   // are unable to send the INVOKE message, then we'll clean these up.
01120   nsVoidArray wrappers;
01121 
01122   for (i=0; i<paramCount; ++i)
01123   {
01124     const nsXPTParamInfo &paramInfo = aInfo->GetParam(i);
01125 
01126     if (paramInfo.IsIn() && !paramInfo.IsDipper())
01127     {
01128       const nsXPTType &type = paramInfo.GetType();
01129 
01130       if (type.IsInterfacePointer())
01131       {
01132         nsID iid;
01133         rv = gDConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type,
01134                                              aMethodIndex, i, aParams, PR_FALSE, iid);
01135         if (NS_SUCCEEDED(rv))
01136           rv = SerializeInterfaceParam(writer, mPeerID, iid, type, aParams[i], wrappers);
01137       }
01138       else
01139         rv = SerializeParam(writer, type, aParams[i]);
01140 
01141       if (NS_FAILED(rv))
01142         return rv;
01143     }
01144   }
01145 
01146   rv = IPC_SendMessage(mPeerID, kDConnectTargetID,
01147                        writer.GetBuffer(),
01148                        writer.GetSize());
01149   if (NS_FAILED(rv))
01150   {
01151     // INVOKE message wasn't delivered; clean up wrappers
01152     DeleteWrappers(wrappers);
01153     return rv;
01154   }
01155 
01156   // now, we wait for the method call to complete.  during that time, it's
01157   // possible that we'll receive other method call requests.  we'll process
01158   // those while waiting for out method call to complete.  it's critical that
01159   // we do so since those other method calls might need to complete before
01160   // out method call can complete!
01161 
01162   DConnectInvokeCompletion completion(&invoke);
01163 
01164   do
01165   {
01166     rv = IPC_WaitMessage(mPeerID, kDConnectTargetID, &completion,
01167                          DCON_WAIT_TIMEOUT);
01168     if (NS_FAILED(rv))
01169     {
01170       // INVOKE message wasn't received; clean up wrappers
01171       DeleteWrappers(wrappers);
01172       return rv;
01173     }
01174   }
01175   while (completion.IsPending());
01176 
01177   rv = completion.GetResult();
01178   if (NS_SUCCEEDED(rv) && completion.ParamsLen() > 0)
01179   {
01180     ipcMessageReader reader(completion.Params(), completion.ParamsLen());
01181 
01182     PRUint8 i;
01183 
01184     // handle out-params and retvals: DCON_OP_INVOKE_REPLY has the data
01185     for (i=0; i<paramCount; ++i)
01186     {
01187       const nsXPTParamInfo &paramInfo = aInfo->GetParam(i);
01188 
01189       if (paramInfo.IsOut() || paramInfo.IsRetval())
01190         DeserializeResult(reader, paramInfo.GetType(), aParams[i]);
01191     }
01192 
01193     // fixup any interface pointers using a second pass so we can properly
01194     // handle INTERFACE_IS referencing an IID that is an out param!
01195     for (i=0; i<paramCount; ++i)
01196     {
01197       const nsXPTParamInfo &paramInfo = aInfo->GetParam(i);
01198       if (aParams[i].val.p && (paramInfo.IsOut() || paramInfo.IsRetval()))
01199       {
01200         const nsXPTType &type = paramInfo.GetType();
01201         if (type.IsInterfacePointer())
01202         {
01203           PtrBits bits = (PtrBits) *((void **) aParams[i].val.p);
01204           if (bits & 0x1)
01205           {
01206             *((void **) aParams[i].val.p) = (void *) (bits & ~0x1);
01207 
01208             nsID iid;
01209             rv = gDConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type,
01210                                                  aMethodIndex, i, aParams, PR_FALSE, iid);
01211             if (NS_SUCCEEDED(rv))
01212             {
01213               DConnectStub *stub;
01214               void **pptr = (void **) aParams[i].val.p;
01215               rv = CreateStub(iid, mPeerID, (DConAddr) *pptr, &stub);
01216               if (NS_SUCCEEDED(rv))
01217                 *((nsISupports **) aParams[i].val.p) = stub;
01218             }
01219           }
01220           else if (bits)
01221           {
01222             // pointer is to one of our instance wrappers.
01223 
01224             DConnectInstance *wrapper = (DConnectInstance *) aParams[i].val.p;
01225             *((void **) aParams[i].val.p) = wrapper->RealInstance();
01226           }
01227           else
01228           {
01229             *((void **) aParams[i].val.p) = nsnull;
01230           }
01231         }
01232       }
01233     }
01234   }
01235 
01236   return rv;
01237 }
01238 
01239 //-----------------------------------------------------------------------------
01240 
01241 class DConnectSetupCompletion : public DConnectCompletion
01242 {
01243 public:
01244   DConnectSetupCompletion(const DConnectSetup *setup)
01245     : DConnectCompletion(setup->request_index)
01246     , mSetup(setup)
01247     , mStatus(NS_OK)
01248   {}
01249 
01250   void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen)
01251   {
01252     if (op->opcode_major != DCON_OP_SETUP_REPLY)
01253     {
01254       NS_NOTREACHED("unexpected response");
01255       mStatus = NS_ERROR_UNEXPECTED;
01256       return;
01257     }
01258 
01259     const DConnectSetupReply *reply = (const DConnectSetupReply *) op;
01260 
01261     LOG(("got SETUP_REPLY: status=%x instance=%p\n", reply->status, reply->instance));
01262 
01263     if (NS_FAILED(reply->status))
01264     {
01265       NS_ASSERTION(!reply->instance, "non-null instance on failure");
01266       mStatus = reply->status;
01267     }
01268     else
01269     {
01270       nsresult rv = CreateStub(mSetup->iid, sender, reply->instance,
01271                                getter_AddRefs(mStub));
01272       if (NS_FAILED(rv))
01273         mStatus = rv;
01274     }
01275   }
01276 
01277   nsresult GetStub(void **aInstancePtr)
01278   {
01279     if (NS_FAILED(mStatus))
01280       return mStatus;
01281 
01282     DConnectStub *stub = mStub;
01283     NS_IF_ADDREF(stub);
01284     *aInstancePtr = stub;
01285     return NS_OK;
01286   }
01287 
01288 private:
01289   const DConnectSetup    *mSetup;
01290   nsresult                mStatus;
01291   nsRefPtr<DConnectStub>  mStub;
01292 };
01293 
01294 // static
01295 nsresult
01296 SetupPeerInstance(PRUint32 aPeerID, DConnectSetup *aMsg, PRUint32 aMsgLen,
01297                   void **aInstancePtr)
01298 {
01299   *aInstancePtr = nsnull;
01300 
01301   aMsg->opcode_major = DCON_OP_SETUP;
01302   aMsg->request_index = NewRequestIndex();
01303 
01304   // send SETUP message, expect SETUP_REPLY
01305 
01306   nsresult rv = IPC_SendMessage(aPeerID, kDConnectTargetID,
01307                                 (const PRUint8 *) aMsg, aMsgLen);
01308   if (NS_FAILED(rv))
01309     return rv;
01310 
01311   DConnectSetupCompletion completion(aMsg);
01312 
01313   // need to allow messages from other clients to be processed immediately 
01314   // to avoid distributed dead locks.  the completion's OnMessageAvailable
01315   // will call our default OnMessageAvailable if it receives any message
01316   // other than the one for which it is waiting.
01317 
01318   do
01319   {
01320     rv = IPC_WaitMessage(aPeerID, kDConnectTargetID, &completion, DCON_WAIT_TIMEOUT);
01321     if (NS_FAILED(rv))
01322       break;
01323 
01324     rv = completion.GetStub(aInstancePtr);
01325   }
01326   while (NS_SUCCEEDED(rv) && *aInstancePtr == nsnull);
01327 
01328   return rv;
01329 } 
01330 
01331 //-----------------------------------------------------------------------------
01332 
01333 PR_STATIC_CALLBACK(PLDHashOperator)
01334 DestroyDConnectInstance(const void *key,
01335                         DConnectInstance *wrapper,
01336                         void *userArg)
01337 {
01338   delete wrapper;
01339   return PL_DHASH_NEXT;
01340 }
01341 
01342 //-----------------------------------------------------------------------------
01343 
01344 ipcDConnectService::~ipcDConnectService()
01345 {
01346   // make sure we have released all instances
01347   mInstances.EnumerateRead(DestroyDConnectInstance, nsnull);
01348   mInstances.Clear();
01349 
01350   gDConnect = nsnull;
01351 }
01352 
01353 nsresult
01354 ipcDConnectService::Init()
01355 {
01356   nsresult rv;
01357 
01358   rv = IPC_DefineTarget(kDConnectTargetID, this);
01359   if (NS_FAILED(rv))
01360     return rv;
01361 
01362   if (!mInstances.Init())
01363     return NS_ERROR_OUT_OF_MEMORY;
01364 
01365   mIIM = do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID, &rv);
01366   if (NS_FAILED(rv))
01367     return rv;
01368 
01369   gDConnect = this;
01370   return NS_OK;
01371 }
01372 
01373 // this should be inlined
01374 nsresult
01375 ipcDConnectService::GetInterfaceInfo(const nsID &iid, nsIInterfaceInfo **result)
01376 {
01377   return mIIM->GetInfoForIID(&iid, result);
01378 }
01379 
01380 // this is adapted from the version in xpcwrappednative.cpp
01381 nsresult
01382 ipcDConnectService::GetIIDForMethodParam(nsIInterfaceInfo *iinfo,
01383                                          const nsXPTMethodInfo *methodInfo,
01384                                          const nsXPTParamInfo &paramInfo,
01385                                          const nsXPTType &type,
01386                                          PRUint16 methodIndex,
01387                                          PRUint8 paramIndex,
01388                                          nsXPTCMiniVariant *dispatchParams,
01389                                          PRBool isFullVariantArray,
01390                                          nsID &result)
01391 {
01392   PRUint8 argnum, tag = type.TagPart();
01393   nsresult rv;
01394 
01395   if (tag == nsXPTType::T_INTERFACE)
01396   {
01397     rv = iinfo->GetIIDForParamNoAlloc(methodIndex, &paramInfo, &result);
01398   }
01399   else if (tag == nsXPTType::T_INTERFACE_IS)
01400   {
01401     rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, &paramInfo, &argnum);
01402     if (NS_FAILED(rv))
01403       return rv;
01404 
01405     const nsXPTParamInfo& arg_param = methodInfo->GetParam(argnum);
01406     const nsXPTType& arg_type = arg_param.GetType();
01407 
01408     // The xpidl compiler ensures this. We reaffirm it for safety.
01409     if (!arg_type.IsPointer() || arg_type.TagPart() != nsXPTType::T_IID)
01410       return NS_ERROR_UNEXPECTED;
01411 
01412     nsID *p;
01413     if (isFullVariantArray)
01414       p = (nsID *) ((nsXPTCVariant *) dispatchParams)[argnum].val.p;
01415     else
01416       p = (nsID *) dispatchParams[argnum].val.p;
01417     if (!p)
01418       return NS_ERROR_UNEXPECTED;
01419 
01420     result = *p;
01421   }
01422   else
01423     rv = NS_ERROR_UNEXPECTED;
01424   return rv;
01425 }
01426 
01427 nsresult
01428 ipcDConnectService::StoreInstance(DConnectInstance *wrapper)
01429 {
01430   return mInstances.Put(nsVoidPtrHashKey(wrapper).GetKey(), wrapper)
01431       ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
01432 }
01433 
01434 void
01435 ipcDConnectService::DeleteInstance(DConnectInstance *wrapper)
01436 {
01437   // this calls |operator delete|
01438   mInstances.Remove(nsVoidPtrHashKey(wrapper).GetKey());
01439 }
01440 
01441 NS_IMPL_ISUPPORTS2(ipcDConnectService, ipcIDConnectService, ipcIMessageObserver)
01442 
01443 NS_IMETHODIMP
01444 ipcDConnectService::CreateInstance(PRUint32 aPeerID,
01445                                    const nsID &aCID,
01446                                    const nsID &aIID,
01447                                    void **aInstancePtr)
01448 {
01449   DConnectSetupClassID msg;
01450   msg.opcode_minor = DCON_OP_SETUP_NEW_INST_CLASSID;
01451   msg.iid = aIID;
01452   msg.classid = aCID;
01453 
01454   return SetupPeerInstance(aPeerID, &msg, sizeof(msg), aInstancePtr);
01455 }
01456 
01457 NS_IMETHODIMP
01458 ipcDConnectService::CreateInstanceByContractID(PRUint32 aPeerID,
01459                                                const char *aContractID,
01460                                                const nsID &aIID,
01461                                                void **aInstancePtr)
01462 {
01463   size_t slen = strlen(aContractID);
01464   size_t size = sizeof(DConnectSetupContractID) + slen;
01465 
01466   DConnectSetupContractID *msg =
01467       (DConnectSetupContractID *) malloc(size);
01468   NS_ENSURE_TRUE(msg, NS_ERROR_OUT_OF_MEMORY);
01469 
01470   msg->opcode_minor = DCON_OP_SETUP_NEW_INST_CONTRACTID;
01471   msg->iid = aIID;
01472   memcpy(&msg->contractid, aContractID, slen + 1);
01473 
01474   nsresult rv = SetupPeerInstance(aPeerID, msg, size, aInstancePtr);
01475 
01476   free(msg);
01477   return rv;
01478 }
01479 
01480 NS_IMETHODIMP
01481 ipcDConnectService::GetService(PRUint32 aPeerID,
01482                                const nsID &aCID,
01483                                const nsID &aIID,
01484                                void **aInstancePtr)
01485 {
01486   DConnectSetupClassID msg;
01487   msg.opcode_minor = DCON_OP_SETUP_GET_SERV_CLASSID;
01488   msg.iid = aIID;
01489   msg.classid = aCID;
01490 
01491   return SetupPeerInstance(aPeerID, &msg, sizeof(msg), aInstancePtr);
01492 }
01493 
01494 NS_IMETHODIMP
01495 ipcDConnectService::GetServiceByContractID(PRUint32 aPeerID,
01496                                            const char *aContractID,
01497                                            const nsID &aIID,
01498                                            void **aInstancePtr)
01499 {
01500   size_t slen = strlen(aContractID);
01501   size_t size = sizeof(DConnectSetupContractID) + slen;
01502 
01503   DConnectSetupContractID *msg =
01504       (DConnectSetupContractID *) malloc(size);
01505   NS_ENSURE_TRUE(msg, NS_ERROR_OUT_OF_MEMORY);
01506 
01507   msg->opcode_minor = DCON_OP_SETUP_GET_SERV_CONTRACTID;
01508   msg->iid = aIID;
01509   memcpy(&msg->contractid, aContractID, slen + 1);
01510 
01511   nsresult rv = SetupPeerInstance(aPeerID, msg, size, aInstancePtr);
01512 
01513   free(msg);
01514   return rv;
01515 }
01516 
01517 //-----------------------------------------------------------------------------
01518 
01519 NS_IMETHODIMP
01520 ipcDConnectService::OnMessageAvailable(PRUint32 aSenderID,
01521                                        const nsID &aTarget,
01522                                        const PRUint8 *aData,
01523                                        PRUint32 aDataLen)
01524 {
01525   const DConnectOp *op = (const DConnectOp *) aData;
01526   switch (op->opcode_major)
01527   {
01528     case DCON_OP_SETUP:
01529       OnSetup(aSenderID, (const DConnectSetup *) aData, aDataLen);
01530       break;
01531     case DCON_OP_RELEASE:
01532       OnRelease(aSenderID, (const DConnectRelease *) aData);
01533       break;
01534     case DCON_OP_INVOKE:
01535       OnInvoke(aSenderID, (const DConnectInvoke *) aData, aDataLen);
01536       break;
01537     default:
01538       NS_NOTREACHED("unknown opcode major");
01539   }
01540 
01541   return NS_OK;
01542 }
01543 
01544 //-----------------------------------------------------------------------------
01545 
01546 void
01547 ipcDConnectService::OnSetup(PRUint32 peer, const DConnectSetup *setup, PRUint32 opLen)
01548 {
01549   nsISupports *instance = nsnull;
01550   nsresult rv = NS_ERROR_FAILURE;
01551 
01552   switch (setup->opcode_minor)
01553   {
01554     // CreateInstance
01555     case DCON_OP_SETUP_NEW_INST_CLASSID:
01556     {
01557       const DConnectSetupClassID *setupCI = (const DConnectSetupClassID *) setup;
01558 
01559       nsCOMPtr<nsIComponentManager> compMgr;
01560       rv = NS_GetComponentManager(getter_AddRefs(compMgr));
01561       if (NS_SUCCEEDED(rv))
01562         rv = compMgr->CreateInstance(setupCI->classid, nsnull, setupCI->iid, (void **) &instance);
01563 
01564       break;
01565     }
01566 
01567     // CreateInstanceByContractID
01568     case DCON_OP_SETUP_NEW_INST_CONTRACTID:
01569     {
01570       const DConnectSetupContractID *setupCI = (const DConnectSetupContractID *) setup;
01571 
01572       nsCOMPtr<nsIComponentManager> compMgr;
01573       rv = NS_GetComponentManager(getter_AddRefs(compMgr));
01574       if (NS_SUCCEEDED(rv))
01575         rv = compMgr->CreateInstanceByContractID(setupCI->contractid, nsnull, setupCI->iid, (void **) &instance);
01576 
01577       break;
01578     }
01579 
01580     // GetService
01581     case DCON_OP_SETUP_GET_SERV_CLASSID:
01582     {
01583       const DConnectSetupClassID *setupCI = (const DConnectSetupClassID *) setup;
01584 
01585       nsCOMPtr<nsIServiceManager> svcMgr;
01586       rv = NS_GetServiceManager(getter_AddRefs(svcMgr));
01587       if (NS_SUCCEEDED(rv))
01588         rv = svcMgr->GetService(setupCI->classid, setupCI->iid, (void **) &instance);
01589       break;
01590     }
01591 
01592     // GetServiceByContractID
01593     case DCON_OP_SETUP_GET_SERV_CONTRACTID:
01594     {
01595       const DConnectSetupContractID *setupCI = (const DConnectSetupContractID *) setup;
01596 
01597       nsCOMPtr<nsIServiceManager> svcMgr;
01598       rv = NS_GetServiceManager(getter_AddRefs(svcMgr));
01599       if (NS_SUCCEEDED(rv))
01600         rv = svcMgr->GetServiceByContractID(setupCI->contractid, setupCI->iid, (void **) &instance);
01601 
01602       break;
01603     }
01604 
01605     // QueryInterface
01606     case DCON_OP_SETUP_QUERY_INTERFACE:
01607     {
01608       const DConnectSetupQueryInterface *setupQI = (const DConnectSetupQueryInterface *) setup;
01609   
01610       // make sure we've been sent a valid wrapper
01611       if (!mInstances.Get(nsVoidPtrHashKey(setupQI->instance).GetKey(), nsnull))
01612       {
01613         NS_NOTREACHED("instance wrapper not found");
01614         rv = NS_ERROR_INVALID_ARG;
01615       }
01616       else
01617         rv = setupQI->instance->RealInstance()->QueryInterface(setupQI->iid, (void **) &instance);
01618       break;
01619     }
01620 
01621     default:
01622       NS_NOTREACHED("unexpected minor opcode");
01623       rv = NS_ERROR_UNEXPECTED;
01624       break;
01625   }
01626 
01627   // now, create instance wrapper, and store it in our instances set.
01628   // this allows us to keep track of object references held on behalf of a
01629   // particular peer.  we can use this information to cleanup after a peer
01630   // that disconnects without sending RELEASE messages for its objects.
01631   DConnectInstance *wrapper = nsnull;
01632   if (NS_SUCCEEDED(rv))
01633   {
01634     nsCOMPtr<nsIInterfaceInfo> iinfo;
01635     rv = gDConnect->GetInterfaceInfo(setup->iid, getter_AddRefs(iinfo));
01636     if (NS_SUCCEEDED(rv))
01637     {
01638       wrapper = new DConnectInstance(peer, iinfo, instance);
01639       if (!wrapper)
01640         rv = NS_ERROR_OUT_OF_MEMORY;
01641       else
01642         rv = StoreInstance(wrapper);
01643     }
01644   }
01645 
01646   if (NS_FAILED(rv) && instance)
01647     NS_RELEASE(instance);
01648 
01649   DConnectSetupReply msg;
01650   msg.opcode_major = DCON_OP_SETUP_REPLY;
01651   msg.opcode_minor = 0;
01652   msg.request_index = setup->request_index;
01653   msg.instance = wrapper;
01654   msg.status = rv;
01655 
01656   // fire off SETUP_REPLY, don't wait for a response
01657   IPC_SendMessage(peer, kDConnectTargetID,
01658                   (const PRUint8 *) &msg, sizeof(msg));
01659 }
01660 
01661 void
01662 ipcDConnectService::OnRelease(PRUint32 peer, const DConnectRelease *release)
01663 {
01664   LOG(("ipcDConnectService::OnRelease [peer=%u instance=%p]\n", peer, release->instance));
01665 
01666 #ifdef DEBUG
01667   // make sure we've been sent a valid wrapper
01668   if (!mInstances.Get(nsVoidPtrHashKey(release->instance).GetKey(), nsnull))
01669     NS_NOTREACHED("instance wrapper not found");
01670 #endif
01671 
01672   DeleteInstance(release->instance);
01673 }
01674 
01675 void
01676 ipcDConnectService::OnInvoke(PRUint32 peer, const DConnectInvoke *invoke, PRUint32 opLen)
01677 {
01678   LOG(("ipcDConnectService::OnInvoke [peer=%u instance=%p method=%u]\n",
01679       peer, invoke->instance, invoke->method_index));
01680 
01681   DConnectInstance *wrapper = invoke->instance;
01682 
01683   ipcMessageReader reader((const PRUint8 *) (invoke + 1), opLen - sizeof(*invoke));
01684 
01685   const nsXPTMethodInfo *methodInfo;
01686   nsXPTCVariant *params = nsnull;
01687   nsIInterfaceInfo *iinfo = nsnull;
01688   PRUint8 i, paramCount = 0, paramUsed = 0;
01689   nsresult rv;
01690   
01691   // make sure we've been sent a valid wrapper
01692   if (!mInstances.Get(nsVoidPtrHashKey(wrapper).GetKey(), nsnull))
01693   {
01694     NS_NOTREACHED("instance wrapper not found");
01695     rv = NS_ERROR_INVALID_ARG;
01696     goto end;
01697   }
01698 
01699   iinfo = wrapper->InterfaceInfo();
01700 
01701   rv = iinfo->GetMethodInfo(invoke->method_index, &methodInfo);
01702   if (NS_FAILED(rv))
01703     goto end;
01704 
01705   paramCount = methodInfo->GetParamCount();
01706 
01707   params = new nsXPTCVariant[paramCount];
01708   if (!params)
01709     goto end;
01710 
01711   // setup |params| for xptcall
01712 
01713   for (i=0; i<paramCount; ++i, ++paramUsed)
01714   {
01715     const nsXPTParamInfo &paramInfo = methodInfo->GetParam(i);
01716 
01717     // XXX are inout params an issue?
01718 
01719     if (paramInfo.IsIn() && !paramInfo.IsDipper())
01720       rv = DeserializeParam(reader, paramInfo.GetType(), params[i]);
01721     else
01722       rv = SetupParam(paramInfo, params[i]);
01723 
01724     if (NS_FAILED(rv))
01725       goto end;
01726   }
01727 
01728   // fixup any interface pointers.  we do this with a second pass so that
01729   // we can properly handle INTERFACE_IS.
01730   for (i=0; i<paramCount; ++i)
01731   {
01732     const nsXPTParamInfo &paramInfo = methodInfo->GetParam(i);
01733     const nsXPTType &type = paramInfo.GetType();
01734 
01735     if (paramInfo.IsIn() && type.IsInterfacePointer())
01736     {
01737       PtrBits bits = (PtrBits) params[i].ptr;
01738       if (bits & 0x1)
01739       {
01740         // pointer is to a remote object.  we need to build a stub.
01741         params[i].ptr = (void *) (bits & ~0x1);
01742 
01743         nsID iid;
01744         rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type,
01745                                   invoke->method_index, i, params, PR_TRUE, iid);
01746         if (NS_SUCCEEDED(rv))
01747         {
01748           DConnectStub *stub;
01749           rv = CreateStub(iid, peer, (DConAddr) params[i].ptr, &stub);
01750           if (NS_SUCCEEDED(rv))
01751           {
01752             params[i].val.p = params[i].ptr = stub;
01753             params[i].SetValIsInterface();
01754           }
01755         }
01756       }
01757       else if (bits)
01758       {
01759         // pointer is to one of our instance wrappers.
01760 
01761         DConnectInstance *wrapper = (DConnectInstance *) params[i].ptr;
01762         params[i].val.p = params[i].ptr = wrapper->RealInstance();
01763         params[i].SetValIsInterface();
01764       }
01765       else
01766       {
01767         params[i].val.p = params[i].ptr = nsnull;
01768         params[i].SetValIsInterface();
01769       }
01770     }
01771   }
01772 
01773   rv = XPTC_InvokeByIndex(wrapper->RealInstance(),
01774                           invoke->method_index, 
01775                           paramCount,
01776                           params);
01777 
01778 end:
01779   LOG(("sending INVOKE_REPLY: rv=%x\n", rv));
01780 
01781   ipcMessageWriter writer(64);
01782 
01783   DConnectInvokeReply reply;
01784   reply.opcode_major = DCON_OP_INVOKE_REPLY;
01785   reply.opcode_minor = 0;
01786   reply.request_index = invoke->request_index;
01787   reply.result = rv;
01788 
01789   writer.PutBytes(&reply, sizeof(reply));
01790 
01791   nsVoidArray wrappers;
01792 
01793   if (NS_SUCCEEDED(rv) && params)
01794   {
01795     // serialize out-params and retvals
01796     for (i=0; i<paramCount; ++i)
01797     {
01798       const nsXPTParamInfo paramInfo = methodInfo->GetParam(i);
01799 
01800       if (paramInfo.IsRetval() || paramInfo.IsOut())
01801       {
01802         const nsXPTType &type = paramInfo.GetType();
01803 
01804         if (type.IsInterfacePointer())
01805         {
01806           nsID iid;
01807           rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type,
01808                                     invoke->method_index, i, params, PR_TRUE, iid);
01809           if (NS_SUCCEEDED(rv))
01810             rv = SerializeInterfaceParam(writer, peer, iid, type, params[i], wrappers);
01811         }
01812         else
01813           rv = SerializeParam(writer, type, params[i]);
01814 
01815         if (NS_FAILED(rv))
01816         {
01817           reply.result = rv;
01818           break;
01819         }
01820       }
01821     }
01822   }
01823 
01824   if (NS_FAILED(rv))
01825     rv = IPC_SendMessage(peer, kDConnectTargetID, (const PRUint8 *) &reply, sizeof(reply));
01826   else
01827     rv = IPC_SendMessage(peer, kDConnectTargetID, writer.GetBuffer(), writer.GetSize());
01828   if (NS_FAILED(rv))
01829   {
01830     LOG(("unable to send INVOKE_REPLY: rv=%x\n", rv));
01831     DeleteWrappers(wrappers);
01832   }
01833 
01834   if (params)
01835   {
01836     for (i=0; i<paramUsed; ++i)
01837       FinishParam(params[i]);
01838     delete[] params;
01839   }
01840 }