Back to index

lightning-sunbird  0.9+nobinonly
nsSoftwareUpdateRun.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim: set ts=4 sw=4 et tw=80: */
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  *   Daniel Veditz <dveditz@netscape.com>
00026  *   Douglas Turner <dougt@netscape.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 #include "nsSoftwareUpdate.h"
00043 #include "nsSoftwareUpdateRun.h"
00044 #include "nsSoftwareUpdateIIDs.h"
00045 
00046 #include "nsInstall.h"
00047 
00048 #include "nsIComponentManager.h"
00049 #include "nsIServiceManager.h"
00050 
00051 #include "nsProxiedService.h"
00052 #include "nsIURI.h"
00053 #include "nsIFileURL.h"
00054 #include "nsNetUtil.h"
00055 
00056 #include "nspr.h"
00057 #include "jsapi.h"
00058 
00059 #include "nsIEventQueueService.h"
00060 #include "nsIEnumerator.h"
00061 #include "nsIZipReader.h"
00062 #include "nsIJSRuntimeService.h"
00063 #include "nsCOMPtr.h"
00064 #include "nsXPIDLString.h"
00065 #include "nsReadableUtils.h"
00066 #include "nsILocalFile.h"
00067 #include "nsIChromeRegistry.h"
00068 #include "nsInstallTrigger.h"
00069 #include "nsIConsoleService.h"
00070 #include "nsIScriptError.h"
00071 
00072 #include "nsIJAR.h"
00073 #include "nsIPrincipal.h"
00074 
00075 #include "nsIExtensionManager.h"
00076 
00077 static NS_DEFINE_CID(kSoftwareUpdateCID,  NS_SoftwareUpdate_CID);
00078 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00079 
00080 extern JSObject *InitXPInstallObjects(JSContext *jscontext, 
00081                                       nsIFile* jarfile, const PRUnichar* url, 
00082                                       const PRUnichar* args, PRUint32 flags, 
00083                                       CHROMEREG_IFACE* registry, 
00084                                       nsIZipReader* hZip);
00085 extern nsresult InitInstallVersionClass(JSContext *jscontext, JSObject *global, void** prototype);
00086 extern nsresult InitInstallTriggerGlobalClass(JSContext *jscontext, JSObject *global, void** prototype);
00087 
00088 // Defined in this file:
00089 PR_STATIC_CALLBACK(void) XPInstallErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
00090 static PRInt32  GetInstallScriptFromJarfile(nsIZipReader* hZip, char** scriptBuffer, PRUint32 *scriptLength);
00091 static PRInt32  OpenAndValidateArchive(nsIZipReader* hZip, nsIFile* jarFile, nsIPrincipal* aPrincipal);
00092 
00093 static nsresult SetupInstallContext(nsIZipReader* hZip, nsIFile* jarFile, const PRUnichar* url, const PRUnichar* args, 
00094                                     PRUint32 flags, CHROMEREG_IFACE* reg, JSRuntime *jsRT, JSContext **jsCX, JSObject **jsGlob);
00095 
00096 extern "C" void RunInstallOnThread(void *data);
00097 
00098 
00099 nsresult VerifySigning(nsIZipReader* hZip, nsIPrincipal* aPrincipal)
00100 {
00101     if (!aPrincipal) 
00102         return NS_OK; // not signed, but not an error
00103 
00104     PRBool hasCert;
00105     aPrincipal->GetHasCertificate(&hasCert);
00106     if (!hasCert)
00107         return NS_ERROR_FAILURE;
00108 
00109     nsCOMPtr<nsIJAR> jar(do_QueryInterface(hZip));
00110     if (!jar)
00111         return NS_ERROR_FAILURE;
00112 
00113     // See if the archive is signed at all first
00114     nsCOMPtr<nsIPrincipal> principal;
00115     nsresult rv = jar->GetCertificatePrincipal(nsnull, getter_AddRefs(principal));
00116     if (NS_FAILED(rv) || !principal)
00117         return NS_ERROR_FAILURE;
00118     
00119     PRUint32 entryCount = 0;
00120 
00121     // first verify all files in the jar are also in the manifest.
00122     nsCOMPtr<nsISimpleEnumerator> entries;
00123     rv = hZip->FindEntries("*", getter_AddRefs(entries));
00124     if (NS_FAILED(rv))
00125         return rv;
00126 
00127     PRBool more;
00128     nsXPIDLCString name;
00129     while (NS_SUCCEEDED(entries->HasMoreElements(&more)) && more)
00130     {
00131         nsCOMPtr<nsIZipEntry> file;
00132         rv = entries->GetNext(getter_AddRefs(file));
00133         if (NS_FAILED(rv)) return rv;
00134         
00135         file->GetName(getter_Copies(name));
00136 
00137         if ( PL_strncasecmp("META-INF/", name.get(), 9) == 0)
00138             continue;
00139 
00140         // we only count the entries not in the meta-inf directory
00141         entryCount++;
00142 
00143         // Each entry must be signed
00144         PRBool equal;
00145         rv = jar->GetCertificatePrincipal(name, getter_AddRefs(principal));
00146         if (NS_FAILED(rv) || !principal) return NS_ERROR_FAILURE;
00147 
00148         rv = principal->Equals(aPrincipal, &equal);
00149         if (NS_FAILED(rv) || !equal) return NS_ERROR_FAILURE;
00150     }
00151 
00152     // next verify all files in the manifest are in the archive.
00153     PRUint32 manifestEntryCount;
00154     rv = jar->GetManifestEntriesCount(&manifestEntryCount);
00155     if (NS_FAILED(rv))
00156         return rv;
00157 
00158     if (entryCount != manifestEntryCount)
00159         return NS_ERROR_FAILURE;  // some files were deleted from archive
00160 
00161     return NS_OK;
00162 }
00163 
00165 // Function name    : XPInstallErrorReporter
00166 // Description      : Prints error message to stdout
00167 // Return type      : void
00168 // Argument         : JSContext *cx
00169 // Argument         : const char *message
00170 // Argument         : JSErrorReport *report
00172 static void
00173 XPInstallErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
00174 {
00175     nsresult rv;
00176 
00177     /* Use the console service to register the error. */
00178     nsCOMPtr<nsIConsoleService> consoleService
00179         (do_GetService("@mozilla.org/consoleservice;1"));
00180 
00181     /*
00182      * Make an nsIScriptError, populate it with information from this
00183      * error, then log it with the console service.
00184      */
00185     nsCOMPtr<nsIScriptError>
00186         errorObject(do_CreateInstance("@mozilla.org/scripterror;1"));
00187 
00188     if (consoleService != nsnull && errorObject != nsnull && report != nsnull) {
00189         /*
00190          * Got an error object; prepare appropriate-width versions of
00191          * various arguments to it.
00192          */
00193 
00194         PRUint32 column = report->uctokenptr - report->uclinebuf;
00195 
00196         rv = errorObject->Init(NS_REINTERPRET_CAST(const PRUnichar*, report->ucmessage),
00197                                NS_ConvertASCIItoUCS2(report->filename).get(),
00198                                NS_REINTERPRET_CAST(const PRUnichar*, report->uclinebuf),
00199                                report->lineno, column, report->flags,
00200                                "XPInstall JavaScript");
00201         if (NS_SUCCEEDED(rv)) {
00202             rv = consoleService->LogMessage(errorObject);
00203             if (NS_SUCCEEDED(rv)) {
00204               // We're done!
00205               // For now, always also print out the error to stderr.
00206               // return;
00207             }
00208         }
00209     }
00210 
00211     // lets set up an eventQ so that our xpcom/proxies will not have to:
00212     nsCOMPtr<nsISoftwareUpdate> softwareUpdate =
00213              do_GetService(kSoftwareUpdateCID, &rv);
00214 
00215     if (NS_FAILED(rv))
00216     {
00217         NS_WARNING("shouldn't have RunInstall() if we can't get SoftwareUpdate");
00218         return;
00219     }
00220 
00221     nsCOMPtr<nsIXPIListener> listener;
00222     softwareUpdate->GetMasterListener(getter_AddRefs(listener));
00223 
00224     if(listener)
00225     {
00226         nsAutoString logMessage;
00227         if (report)
00228         {
00229             logMessage.AssignLiteral("Line: ");
00230             logMessage.AppendInt(report->lineno, 10);
00231             logMessage.AppendLiteral("\t");
00232             if (report->ucmessage)
00233                 logMessage.Append( NS_REINTERPRET_CAST(const PRUnichar*, report->ucmessage) );
00234             else
00235                 logMessage.AppendWithConversion( message );
00236         }
00237         else
00238             logMessage.AssignWithConversion( message );
00239 
00240         listener->OnLogComment( logMessage.get() );
00241     }
00242 }
00243 
00244 
00246 // Function name    : OpenAndValidateArchive
00247 // Description      : Opens install archive and validates contents
00248 // Return type      : PRInt32
00249 // Argument         : nsIZipReader* hZip       - the zip reader
00250 // Argument         : nsIFile* jarFile         - the .xpi file
00251 // Argument         : nsIPrincipal* aPrincipal - a principal, if any, displayed to the user 
00252 //                    regarding the cert used to sign this install
00254 
00255 static PRInt32
00256 OpenAndValidateArchive(nsIZipReader* hZip, nsIFile* jarFile, nsIPrincipal* aPrincipal)
00257 {
00258     if (!jarFile)
00259         return nsInstall::DOWNLOAD_ERROR;
00260 
00261     nsCOMPtr<nsIFile> jFile;
00262     nsresult rv =jarFile->Clone(getter_AddRefs(jFile));
00263     if (NS_SUCCEEDED(rv))
00264         rv = hZip->Init(jFile);
00265 
00266     if (NS_FAILED(rv))
00267         return nsInstall::CANT_READ_ARCHIVE;
00268 
00269     rv = hZip->Open();
00270     if (NS_FAILED(rv))
00271         return nsInstall::CANT_READ_ARCHIVE;
00272  
00273     // CRC check the integrity of all items in this archive
00274     rv = hZip->Test(nsnull);
00275     if (NS_FAILED(rv))
00276     {
00277         NS_WARNING("CRC check of archive failed!");
00278         return nsInstall::CANT_READ_ARCHIVE;
00279     }
00280 
00281     rv = VerifySigning(hZip, aPrincipal);
00282     if (NS_FAILED(rv))
00283     {
00284         NS_WARNING("Signing check of archive failed!");
00285         return nsInstall::INVALID_SIGNATURE;
00286     }
00287  
00288     return nsInstall::SUCCESS;
00289 }
00290 
00292 // Function name    : GetInstallScriptFromJarfile
00293 // Description      : Extracts and reads in a install.js file from a passed jar file.
00294 // Return type      : static PRInt32
00295 // Argument         : char** scriptBuffer     - must be deleted via delete []
00296 // Argument         : PRUint32 *scriptLength
00298 
00299 static PRInt32
00300 GetInstallScriptFromJarfile(nsIZipReader* hZip, char** scriptBuffer, PRUint32 *scriptLength)
00301 {
00302     PRInt32 result = NS_OK;
00303 
00304     *scriptBuffer = nsnull;
00305     *scriptLength = 0;
00306 
00307     // Extract the install.js file.
00308     nsCOMPtr<nsIInputStream> instream;
00309     nsresult rv = hZip->GetInputStream("install.js", getter_AddRefs(instream));
00310     if ( NS_SUCCEEDED(rv) )
00311     {
00312         // Read it into a buffer
00313         char* buffer;
00314         PRUint32 bufferLength;
00315         PRUint32 readLength;
00316         result = nsInstall::CANT_READ_ARCHIVE;
00317 
00318         rv = instream->Available(&bufferLength);
00319         if (NS_SUCCEEDED(rv))
00320         {
00321             buffer = new char[bufferLength + 1];
00322 
00323             if (buffer != nsnull)
00324             {
00325                 rv = instream->Read(buffer, bufferLength, &readLength);
00326 
00327                 if (NS_SUCCEEDED(rv) && readLength > 0)
00328                 {
00329                     *scriptBuffer = buffer;
00330                     *scriptLength = readLength;
00331                     result = NS_OK;
00332                 }
00333                 else
00334                 {
00335                     delete [] buffer;
00336                 }
00337             }
00338         }
00339         instream->Close();
00340     }
00341     else
00342     {
00343         result = nsInstall::NO_INSTALL_SCRIPT;
00344     }
00345 
00346     return result;
00347 }
00348 
00350 // Function name    : SetupInstallContext
00351 // Description      : Creates a Javascript context and adds our xpinstall objects to it.
00352 // Return type      : static nsresult
00353 // Argument         : nsIZipReader hZip - the handle to the open archive file
00354 // Argument         : const char* jarFile - native filepath to where jar exists on disk
00355 // Argument         : const PRUnichar* url  - URL of where this package came from
00356 // Argument         : const PRUnichar* args    - any arguments passed into the javascript context
00357 // Argument         : PRUint32 flags   - bitmask of flags passed in
00358 // Argument         : nsIChromeRegistry* - the chrome registry to run with.
00359 // Argument         : JSRuntime *jsRT    - A valid JS Runtime
00360 // Argument         : JSContext **jsCX   - Created context, destroy via JS_DestroyContext
00361 // Argument         : JSObject **jsGlob  - created global object
00363 static nsresult SetupInstallContext(nsIZipReader* hZip,
00364                                     nsIFile* jarFile,
00365                                     const PRUnichar* url,
00366                                     const PRUnichar* args,
00367                                     PRUint32 flags,
00368                                     CHROMEREG_IFACE* reg,
00369                                     JSRuntime *rt,
00370                                     JSContext **jsCX,
00371                                     JSObject **jsGlob)
00372 {
00373     JSContext   *cx;
00374     JSObject    *glob;
00375 
00376     *jsCX   = nsnull;
00377     *jsGlob = nsnull;
00378 
00379     if (!rt)
00380         return NS_ERROR_OUT_OF_MEMORY;
00381 
00382     cx = JS_NewContext(rt, 8192);
00383     if (!cx)
00384     {
00385         return NS_ERROR_OUT_OF_MEMORY;
00386     }
00387 
00388     JS_SetErrorReporter(cx, XPInstallErrorReporter);
00389 
00390     JS_BeginRequest(cx);
00391     glob = InitXPInstallObjects(cx, jarFile, url, args, flags, reg, hZip);
00392     if (!glob)
00393     {
00394         JS_DestroyContext(cx);
00395         return NS_ERROR_OUT_OF_MEMORY;
00396     }
00397 
00398     // Init standard classes
00399     JS_InitStandardClasses(cx, glob);
00400 
00401     // Add our Install class to this context
00402     InitInstallVersionClass(cx, glob, nsnull);
00403     InitInstallTriggerGlobalClass(cx, glob, nsnull);
00404     JS_EndRequest(cx);
00405     *jsCX   = cx;
00406     *jsGlob = glob;
00407 
00408     return NS_OK;
00409 }
00410 
00411 
00412 
00413 
00415 // Function name    : RunInstall
00416 // Description      : Creates our Install Thread.
00417 // Return type      : PRInt32
00418 // Argument         : nsInstallInfo *installInfo
00420 PRInt32 RunInstall(nsInstallInfo *installInfo)
00421 {
00422     if (installInfo->GetFlags() & XPI_NO_NEW_THREAD)
00423     {
00424         RunInstallOnThread((void *)installInfo);
00425     }
00426     else
00427     {
00428         PR_CreateThread(PR_USER_THREAD,
00429                         RunInstallOnThread,
00430                         (void*)installInfo,
00431                         PR_PRIORITY_NORMAL,
00432                         PR_GLOBAL_THREAD,
00433                         PR_UNJOINABLE_THREAD,
00434                         0);
00435     }
00436     return 0;
00437 }
00438 
00439 
00441 // Function name    : RunInstallOnThread
00442 // Description      : called by starting thread.  It directly calls the C api for xpinstall,
00443 //                  : and once that returns, it calls the completion routine to notify installation
00444 //                  : completion.
00445 // Return type      : extern "C"
00446 // Argument         : void *data
00448 extern "C" void RunInstallOnThread(void *data)
00449 {
00450     nsInstallInfo *installInfo = (nsInstallInfo*)data;
00451 
00452     char        *scriptBuffer = nsnull;
00453     PRUint32    scriptLength;
00454 
00455     JSRuntime   *rt;
00456     JSContext   *cx;
00457     JSObject    *glob;
00458 
00459     static NS_DEFINE_IID(kZipReaderCID,  NS_ZIPREADER_CID);
00460 
00461     nsresult rv;
00462     nsCOMPtr<nsIZipReader> hZip = do_CreateInstance(kZipReaderCID, &rv);
00463     if (NS_FAILED(rv))
00464         return;
00465 
00466     // we will plan on sending a failure status back from here unless we
00467     // find positive acknowledgement that the script sent the status
00468     PRInt32     finalStatus;
00469 
00470     nsCOMPtr<nsIXPIListener> listener;
00471 
00472     // lets set up an eventQ so that our xpcom/proxies will not have to:
00473     nsCOMPtr<nsIEventQueue> eventQ;
00474     nsCOMPtr<nsIEventQueueService> eventQService =
00475              do_GetService(kEventQueueServiceCID, &rv);
00476     if (NS_SUCCEEDED(rv))
00477     {
00478         eventQService->CreateMonitoredThreadEventQueue();
00479         eventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eventQ));
00480     }
00481 
00482     nsCOMPtr<nsISoftwareUpdate> softwareUpdate =
00483              do_GetService(kSoftwareUpdateCID, &rv);
00484 
00485     if (NS_FAILED(rv))
00486     {
00487         NS_WARNING("shouldn't have RunInstall() if we can't get SoftwareUpdate");
00488         return;
00489     }
00490 
00491     softwareUpdate->SetActiveListener( installInfo->GetListener() );
00492     softwareUpdate->GetMasterListener(getter_AddRefs(listener));
00493 
00494     if(listener)
00495         listener->OnInstallStart( installInfo->GetURL() );
00496 
00497     nsCOMPtr<nsIFile> jarpath = installInfo->GetFile();
00498 
00499     finalStatus = OpenAndValidateArchive( hZip,
00500                                           jarpath,
00501                                           installInfo->mPrincipal);
00502 
00503     if (finalStatus == nsInstall::SUCCESS)
00504     {
00505 #ifdef MOZ_XUL_APP
00506         if (NS_SUCCEEDED(hZip->Test("install.rdf")) && !(nsSoftwareUpdate::GetProgramDirectory()))
00507         {
00508             hZip->Close();
00509             // appears to be an Extension Manager install
00510             nsIExtensionManager* em = installInfo->GetExtensionManager();
00511             if (em)
00512             {
00513                 rv = em->InstallItemFromFile(jarpath, 
00514                                              NS_INSTALL_LOCATION_APPPROFILE);
00515                 if (NS_FAILED(rv))
00516                     finalStatus = nsInstall::EXECUTION_ERROR;
00517             } else {
00518                 finalStatus = nsInstall::UNEXPECTED_ERROR;
00519             }
00520             // If install.rdf exists, but the install failed, we don't want
00521             // to try an install.js install.
00522         } else
00523 #endif
00524         {
00525             // If we're the suite, or there is no install.rdf,
00526             // try original XPInstall
00527             finalStatus = GetInstallScriptFromJarfile( hZip,
00528                                                        &scriptBuffer,
00529                                                        &scriptLength);
00530             if ( finalStatus == NS_OK && scriptBuffer )
00531             {
00532                 // create our runtime
00533                 rt = JS_NewRuntime(4L * 1024L * 1024L);
00534 
00535                 rv = SetupInstallContext( hZip, jarpath,
00536                                           installInfo->GetURL(),
00537                                           installInfo->GetArguments(),
00538                                           installInfo->GetFlags(),
00539                                           installInfo->GetChromeRegistry(),
00540                                           rt, &cx, &glob);
00541 
00542                 if (NS_SUCCEEDED(rv))
00543                 {
00544                     // Go ahead and run!!
00545                     jsval rval;
00546                     jsval installedFiles;
00547                     JS_BeginRequest(cx); //Increment JS thread counter associated
00548                                         //with this context
00549                     PRBool ok = JS_EvaluateScript(  cx,
00550                                                     glob,
00551                                                     scriptBuffer,
00552                                                     scriptLength,
00553                                                     nsnull,
00554                                                     0,
00555                                                     &rval);
00556 
00557 
00558                     if(!ok)
00559                     {
00560                         // problem compiling or running script -- a true SCRIPT_ERROR
00561                         if(JS_GetProperty(cx, glob, "_installedFiles", &installedFiles) &&
00562                           JSVAL_TO_BOOLEAN(installedFiles))
00563                         {
00564                             nsInstall *a = (nsInstall*)JS_GetPrivate(cx, glob);
00565                             a->InternalAbort(nsInstall::SCRIPT_ERROR);
00566                         }
00567 
00568                         finalStatus = nsInstall::SCRIPT_ERROR;
00569                     }
00570                     else
00571                     {
00572                         // check to make sure the script sent back a status -- if
00573                         // not the install may have been syntactically correct but
00574                         // left the init/(perform|cancel) transaction open
00575 
00576                         if(JS_GetProperty(cx, glob, "_installedFiles", &installedFiles) &&
00577                           JSVAL_TO_BOOLEAN(installedFiles))
00578                         {
00579                             // install items remain in queue, must clean up!
00580                             nsInstall *a = (nsInstall*)JS_GetPrivate(cx, glob);
00581                             a->InternalAbort(nsInstall::MALFORMED_INSTALL);
00582                         }
00583 
00584                         jsval sent;
00585                         if ( JS_GetProperty( cx, glob, "_finalStatus", &sent ) )
00586                             finalStatus = JSVAL_TO_INT(sent);
00587                         else
00588                             finalStatus = nsInstall::UNEXPECTED_ERROR;
00589                     }
00590                     JS_EndRequest(cx); //Decrement JS thread counter
00591                     JS_DestroyContextMaybeGC(cx);
00592                 }
00593                 else
00594                 {
00595                     // couldn't initialize install context
00596                     finalStatus = nsInstall::UNEXPECTED_ERROR;
00597                 }
00598 
00599                 // Clean up after ourselves.
00600                 JS_DestroyRuntime(rt);
00601             }
00602         }
00603         // force zip archive closed before other cleanup
00604         hZip = 0;
00605     }
00606 
00607     if(listener)
00608         listener->OnInstallDone( installInfo->GetURL(), finalStatus );
00609 
00610     if (scriptBuffer) delete [] scriptBuffer;
00611 
00612     softwareUpdate->SetActiveListener(0);
00613     softwareUpdate->InstallJarCallBack();
00614 }
00615 
00616 
00617 //-----------------------------------------------------------------------------
00618 // RunChromeInstallOnThread
00619 //
00620 // Performs the guts of a chrome install on its own thread
00621 //
00622 // XXX: need to return errors/status somehow. What feedback will a user want?
00623 // How do we get it there? Maybe just alerts on errors, could also dump to
00624 // the new console service.
00625 //-----------------------------------------------------------------------------
00626 extern "C" void RunChromeInstallOnThread(void *data)
00627 {
00628     nsresult rv;
00629 
00630     NS_ASSERTION(data, "No nsInstallInfo passed to Chrome Install");
00631     nsInstallInfo *info = (nsInstallInfo*)data;
00632     nsIXPIListener* listener = info->GetListener();
00633 
00634     if (listener)
00635         listener->OnInstallStart(info->GetURL());
00636 
00637     // make sure we've got a chrome registry -- can't proceed if not
00638     CHROMEREG_IFACE* reg = info->GetChromeRegistry();
00639     NS_ASSERTION(reg, "We shouldn't get here without a chrome registry.");
00640 
00641     if (reg)
00642     {
00643 #ifdef MOZ_XUL_APP
00644         if (info->GetType() == CHROME_SKIN) {
00645             static NS_DEFINE_CID(kZipReaderCID,  NS_ZIPREADER_CID);
00646             nsCOMPtr<nsIZipReader> hZip = do_CreateInstance(kZipReaderCID, &rv);
00647             if (hZip)
00648                 rv = hZip->Init(info->GetFile());
00649             if (NS_SUCCEEDED(rv))
00650                 rv = hZip->Open();
00651 
00652             if (NS_SUCCEEDED(rv))
00653             {
00654                 rv = hZip->Test("install.rdf");
00655                 nsIExtensionManager* em = info->GetExtensionManager();
00656                 if (NS_SUCCEEDED(rv) && em) {
00657                     rv = em->InstallItemFromFile(info->GetFile(), 
00658                                                  NS_INSTALL_LOCATION_APPPROFILE);
00659                 }
00660             }
00661             hZip->Close();
00662             // Extension Manager copies the theme .jar file to 
00663             // a different location, so remove the temporary file.
00664             info->GetFile()->Remove(PR_FALSE);
00665         }
00666 #else
00667         PRBool isSkin    = (info->GetType() & CHROME_SKIN);
00668         PRBool isLocale  = (info->GetType() & CHROME_LOCALE);
00669         PRBool isContent = (info->GetType() & CHROME_CONTENT);
00670         PRBool selected  = (info->GetFlags() != 0);
00671 
00672         const nsCString& spec = info->GetFileJARSpec();
00673 
00674         if ( isContent )
00675             rv = reg->InstallPackage(spec.get(), PR_TRUE);
00676 
00677         if ( isSkin )
00678         {
00679             rv = reg->InstallSkin(spec.get(), PR_TRUE, PR_FALSE);
00680                 
00681             if (NS_SUCCEEDED(rv) && selected)
00682             {
00683                 NS_ConvertUCS2toUTF8 utf8Args(info->GetArguments());
00684                 rv = reg->SelectSkin(utf8Args, PR_TRUE);
00685             }
00686         }
00687 
00688         if ( isLocale )
00689         {
00690             rv = reg->InstallLocale(spec.get(), PR_TRUE);
00691 
00692             if (NS_SUCCEEDED(rv) && selected)
00693             {
00694                 NS_ConvertUCS2toUTF8 utf8Args(info->GetArguments());
00695                 rv = reg->SelectLocale(utf8Args, PR_TRUE);
00696             }
00697         }
00698 
00699         // now that all types are registered try to activate
00700         if ( isSkin && selected )
00701             reg->RefreshSkins();
00702 
00703 #ifdef RELOAD_CHROME_WORKS
00704 // XXX ReloadChrome() crashes right now
00705             if ( isContent || (isLocale && selected) )
00706                 reg->ReloadChrome();
00707 #endif
00708 #endif
00709     }
00710 
00711     if (listener)
00712         listener->OnInstallDone(info->GetURL(), nsInstall::SUCCESS);
00713 
00714     delete info;
00715 }