Back to index

lightning-sunbird  0.9+nobinonly
nsXPITriggerInfo.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 Communicator client code, released
00016  * March 31, 1998.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Daniel Veditz <dveditz@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nscore.h"
00041 #include "nsXPITriggerInfo.h"
00042 #include "nsNetUtil.h"
00043 #include "nsDebug.h"
00044 #include "nsIServiceManager.h"
00045 #include "nsIEventQueueService.h"
00046 #include "nsIJSContextStack.h"
00047 #include "nsIScriptSecurityManager.h"
00048 #include "nsICryptoHash.h"
00049 
00050 static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00051 
00052 //
00053 // nsXPITriggerItem
00054 //
00055 MOZ_DECL_CTOR_COUNTER(nsXPITriggerItem)
00056 
00057 nsXPITriggerItem::nsXPITriggerItem( const PRUnichar* aName,
00058                                     const PRUnichar* aURL,
00059                                     const PRUnichar* aIconURL,
00060                                     const char* aHash,
00061                                     PRInt32 aFlags)
00062     : mName(aName), mURL(aURL), mIconURL(aIconURL), mFlags(aFlags), mHashFound(PR_FALSE)
00063 {
00064     MOZ_COUNT_CTOR(nsXPITriggerItem);
00065 
00066     // check for arguments
00067     PRInt32 qmark = mURL.FindChar('?');
00068     if ( qmark != kNotFound )
00069     {
00070         mArguments = Substring( mURL, qmark+1, mURL.Length() );
00071     }
00072 
00073     // construct name if not passed in
00074     if ( mName.IsEmpty() )
00075     {
00076         // Use the filename as the display name by starting after the last
00077         // slash in the URL, looking backwards from the arguments delimiter if
00078         // we found one. By good fortune using kNotFound as the offset for
00079         // RFindChar() starts at the end, so we can use qmark in all cases.
00080 
00081         PRInt32 namestart = mURL.RFindChar( '/', qmark );
00082 
00083         // the real start is after the slash (or 0 if not found)
00084         namestart = ( namestart==kNotFound ) ? 0 : namestart + 1;
00085 
00086         PRInt32 length;
00087         if (qmark == kNotFound)
00088             length =  mURL.Length();      // no '?', slurp up rest of URL
00089         else
00090             length = (qmark - namestart); // filename stops at the '?'
00091 
00092         mName = Substring( mURL, namestart, length );
00093     }
00094 
00095     // parse optional hash into its parts
00096     if (aHash)
00097     {
00098         mHashFound = PR_TRUE;
00099 
00100         PRUint32 htype = 1;
00101         char * colon = PL_strchr(aHash, ':');
00102         if (colon)
00103         {
00104             mHasher = do_CreateInstance("@mozilla.org/security/hash;1");
00105             if (!mHasher) return;
00106             
00107             *colon = '\0'; // null the colon so that aHash is just the type.
00108             nsresult rv = mHasher->InitWithString(nsDependentCString(aHash));
00109             *colon = ':';  // restore the colon
00110 
00111             if (NS_SUCCEEDED(rv))
00112                 mHash = colon+1;
00113         }
00114     }
00115 }
00116 
00117 nsXPITriggerItem::~nsXPITriggerItem()
00118 {
00119     MOZ_COUNT_DTOR(nsXPITriggerItem);
00120 }
00121 
00122 PRBool nsXPITriggerItem::IsRelativeURL()
00123 {
00124     PRInt32 cpos = mURL.FindChar(':');
00125     if (cpos == kNotFound)
00126         return PR_TRUE;
00127 
00128     PRInt32 spos = mURL.FindChar('/');
00129     return (cpos > spos);
00130 }
00131 
00132 const PRUnichar*
00133 nsXPITriggerItem::GetSafeURLString()
00134 {
00135     // create the safe url string the first time
00136     if (mSafeURL.IsEmpty() && !mURL.IsEmpty())
00137     {
00138         nsCOMPtr<nsIURI> uri;
00139         NS_NewURI(getter_AddRefs(uri), mURL);
00140         if (uri)
00141         {
00142             nsCAutoString spec;
00143             uri->SetUserPass(EmptyCString());
00144             uri->GetSpec(spec);
00145             mSafeURL = NS_ConvertUTF8toUTF16(spec);
00146         }
00147     }
00148 
00149     return mSafeURL.get();
00150 }
00151 
00152 void
00153 nsXPITriggerItem::SetPrincipal(nsIPrincipal* aPrincipal)
00154 {
00155     mPrincipal = aPrincipal;
00156 
00157     // aPrincipal can be null for various failure cases.
00158     // see bug 213894 for an example.
00159     // nsXPInstallManager::OnCertAvailable can be called with a null principal
00160     // and it can also force a null principal.
00161     if (!aPrincipal)
00162         return;
00163 
00164     PRBool hasCert;
00165     aPrincipal->GetHasCertificate(&hasCert);
00166     if (hasCert) {
00167         nsCAutoString prettyName;
00168         // XXXbz should this really be using the prettyName?  Perhaps
00169         // it wants to get the subjectName or nsIX509Cert and display
00170         // it sanely?
00171         aPrincipal->GetPrettyName(prettyName);
00172         CopyUTF8toUTF16(prettyName, mCertName);
00173     }
00174 }
00175 
00176 //
00177 // nsXPITriggerInfo
00178 //
00179 
00180 MOZ_DECL_CTOR_COUNTER(nsXPITriggerInfo)
00181 
00182 nsXPITriggerInfo::nsXPITriggerInfo()
00183   : mCx(0), mCbval(JSVAL_NULL)
00184 {
00185     MOZ_COUNT_CTOR(nsXPITriggerInfo);
00186 }
00187 
00188 nsXPITriggerInfo::~nsXPITriggerInfo()
00189 {
00190     nsXPITriggerItem* item;
00191 
00192     for(PRUint32 i=0; i < Size(); i++)
00193     {
00194         item = Get(i);
00195         if (item)
00196             delete item;
00197     }
00198     mItems.Clear();
00199 
00200     if ( mCx && !JSVAL_IS_NULL(mCbval) ) {
00201         JS_BeginRequest(mCx);
00202         JS_RemoveRoot( mCx, &mCbval );
00203         JS_EndRequest(mCx);
00204     }
00205 
00206     MOZ_COUNT_DTOR(nsXPITriggerInfo);
00207 }
00208 
00209 void nsXPITriggerInfo::SaveCallback( JSContext *aCx, jsval aVal )
00210 {
00211     NS_ASSERTION( mCx == 0, "callback set twice, memory leak" );
00212     mCx = aCx;
00213     JSObject *obj = JS_GetGlobalObject( mCx );
00214 
00215     JSClass* clazz;
00216 
00217 #ifdef JS_THREADSAFE
00218     clazz = ::JS_GetClass(aCx, obj);
00219 #else
00220     clazz = ::JS_GetClass(obj);
00221 #endif
00222 
00223     if (clazz &&
00224         (clazz->flags & JSCLASS_HAS_PRIVATE) &&
00225         (clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
00226       mGlobalWrapper =
00227         do_QueryInterface((nsISupports*)::JS_GetPrivate(aCx, obj));
00228     }
00229 
00230     mCbval = aVal;
00231     mThread = PR_GetCurrentThread();
00232 
00233     if ( !JSVAL_IS_NULL(mCbval) ) {
00234         JS_BeginRequest(mCx);
00235         JS_AddRoot( mCx, &mCbval );
00236         JS_EndRequest(mCx);
00237     }
00238 }
00239 
00240 static void  destroyTriggerEvent(XPITriggerEvent* event)
00241 {
00242     JS_BeginRequest(event->cx);
00243     JS_RemoveRoot( event->cx, &event->cbval );
00244     JS_EndRequest(event->cx);
00245     delete event;
00246 }
00247 
00248 static void* handleTriggerEvent(XPITriggerEvent* event)
00249 {
00250     jsval  ret;
00251     void*  mark;
00252     jsval* args;
00253 
00254     JS_BeginRequest(event->cx);
00255     args = JS_PushArguments( event->cx, &mark, "Wi",
00256                              event->URL.get(),
00257                              event->status );
00258     if ( args )
00259     {
00260         // This code is all in a JS request, and here we're about to
00261         // push the context onto the context stack and also push
00262         // arguments. Be very very sure that no early returns creep in
00263         // here w/o doing the proper cleanup!
00264 
00265         const char *errorStr = nsnull;
00266 
00267         nsCOMPtr<nsIJSContextStack> stack =
00268             do_GetService("@mozilla.org/js/xpc/ContextStack;1");
00269         if (stack)
00270             stack->Push(event->cx);
00271         
00272         nsCOMPtr<nsIScriptSecurityManager> secman = 
00273             do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
00274         
00275         if (!secman)
00276         {
00277             errorStr = "Could not get script security manager service";
00278         }
00279 
00280         nsCOMPtr<nsIPrincipal> principal;
00281         if (!errorStr)
00282         {
00283             secman->GetSubjectPrincipal(getter_AddRefs(principal));
00284             if (!principal)
00285             {
00286                 errorStr = "Could not get principal from script security manager";
00287             }
00288         }
00289 
00290         if (!errorStr)
00291         {
00292             PRBool equals = PR_FALSE;
00293             principal->Equals(event->princ, &equals);
00294 
00295             if (!equals)
00296             {
00297                 errorStr = "Principal of callback context is different than InstallTriggers";
00298             }
00299         }
00300 
00301         if (errorStr)
00302         {
00303             JS_ReportError(event->cx, errorStr);
00304         }
00305         else
00306         {
00307             JS_CallFunctionValue(event->cx,
00308                                  JSVAL_TO_OBJECT(event->global),
00309                                  event->cbval,
00310                                  2,
00311                                  args,
00312                                  &ret);
00313         }
00314 
00315         if (stack)
00316             stack->Pop(nsnull);
00317 
00318         JS_PopArguments( event->cx, mark );
00319     }
00320     JS_EndRequest(event->cx);
00321 
00322     return 0;
00323 }
00324 
00325 
00326 void nsXPITriggerInfo::SendStatus(const PRUnichar* URL, PRInt32 status)
00327 {
00328     nsCOMPtr<nsIEventQueue> eq;
00329     nsresult rv;
00330 
00331     if ( mCx && mGlobalWrapper && !JSVAL_IS_NULL(mCbval) )
00332     {
00333         nsCOMPtr<nsIEventQueueService> EQService =
00334                  do_GetService(kEventQueueServiceCID, &rv);
00335         if ( NS_SUCCEEDED( rv ) )
00336         {
00337             rv = EQService->GetThreadEventQueue(mThread, getter_AddRefs(eq));
00338             if ( NS_SUCCEEDED(rv) )
00339             {
00340                 // create event and post it
00341                 XPITriggerEvent* event = new XPITriggerEvent();
00342                 if (event)
00343                 {
00344                     PL_InitEvent(&event->e, 0,
00345                         (PLHandleEventProc)handleTriggerEvent,
00346                         (PLDestroyEventProc)destroyTriggerEvent);
00347 
00348                     event->URL      = URL;
00349                     event->status   = status;
00350                     event->cx       = mCx;
00351                     event->princ    = mPrincipal;
00352 
00353                     JSObject *obj = nsnull;
00354 
00355                     mGlobalWrapper->GetJSObject(&obj);
00356 
00357                     event->global   = OBJECT_TO_JSVAL(obj);
00358 
00359                     event->cbval    = mCbval;
00360                     JS_BeginRequest(event->cx);
00361                     JS_AddNamedRoot( event->cx, &event->cbval,
00362                                      "XPITriggerEvent::cbval" );
00363                     JS_EndRequest(event->cx);
00364 
00365                     // Hold a strong reference to keep the underlying
00366                     // JSContext from dying before we handle this event.
00367                     event->ref      = mGlobalWrapper;
00368 
00369                     eq->PostEvent(&event->e);
00370                 }
00371                 else
00372                     rv = NS_ERROR_OUT_OF_MEMORY;
00373             }
00374         }
00375 
00376         if ( NS_FAILED( rv ) )
00377         {
00378             // couldn't get event queue -- maybe window is gone or
00379             // some similarly catastrophic occurrance
00380         }
00381     }
00382 }
00383