Back to index

lightning-sunbird  0.9+nobinonly
mozJSComponentLoader.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1999
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributors:
00025  *   Mike Shaver <shaver@zeroknowledge.com>
00026  *   John Bandhauer <jband@netscape.com>
00027  *   IBM Corp.
00028  *   Robert Ginda <rginda@netscape.com>
00029  *
00030  * Alternatively, the contents of this file may be used under the terms of
00031  * either of the GNU General Public License Version 2 or later (the "GPL"),
00032  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00033  * in which case the provisions of the GPL or the LGPL are applicable instead
00034  * of those above. If you wish to allow use of your version of this file only
00035  * under the terms of either the GPL or the LGPL, and not to allow others to
00036  * use your version of this file under the terms of the MPL, indicate your
00037  * decision by deleting the provisions above and replace them with the notice
00038  * and other provisions required by the GPL or the LGPL. If you do not delete
00039  * the provisions above, a recipient may use your version of this file under
00040  * the terms of any one of the MPL, the GPL or the LGPL.
00041  *
00042  * ***** END LICENSE BLOCK ***** */
00043 
00044 #ifdef MOZ_LOGGING
00045 #define FORCE_PR_LOG
00046 #endif
00047 
00048 #include "prlog.h"
00049 
00050 #include "nsCOMPtr.h"
00051 #include "nsICategoryManager.h"
00052 #include "nsIComponentLoader.h"
00053 #include "nsIComponentManager.h"
00054 #include "nsIComponentManagerObsolete.h"
00055 #include "nsIGenericFactory.h"
00056 #include "nsILocalFile.h"
00057 #include "nsIModule.h"
00058 #include "nsIServiceManager.h"
00059 #include "nsISupports.h"
00060 #include "mozJSComponentLoader.h"
00061 #include "nsIJSRuntimeService.h"
00062 #include "nsIJSContextStack.h"
00063 #include "nsIXPConnect.h"
00064 #include "nsCRT.h"
00065 #include "nsMemory.h"
00066 #include "nsXPIDLString.h"
00067 #include "nsIObserverService.h"
00068 #include "nsIXPCScriptable.h"
00069 #include "nsString.h"
00070 #ifndef XPCONNECT_STANDALONE
00071 #include "nsIScriptSecurityManager.h"
00072 #include "nsIURI.h"
00073 #include "nsNetUtil.h"
00074 #endif
00075 #include "nsIComponentLoaderManager.h"
00076 #include "jsxdrapi.h"
00077 #include "nsIFastLoadFileControl.h"
00078 // For reporting errors with the console service
00079 #include "nsIScriptError.h"
00080 #include "nsIConsoleService.h"
00081 
00082 static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1";
00083 static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
00084 static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
00085 
00086 /* Some platforms don't have an implementation of PR_MemMap(). */
00087 /* See bug 318077 for WinCE.                                   */
00088 #if !defined(XP_BEOS) && !defined(XP_OS2) && !defined(WINCE)
00089 #define HAVE_PR_MEMMAP
00090 #endif
00091 
00096 #define XPC_SERIALIZATION_BUFFER_SIZE   (64 * 1024)
00097 #define XPC_DESERIALIZATION_BUFFER_SIZE (8 * 1024)
00098 
00099 // Inactivity delay before closing our fastload file stream.
00100 static const int kFastLoadWriteDelay = 5000;   // 5 seconds
00101 
00102 #ifdef PR_LOGGING
00103 // NSPR_LOG_MODULES=JSComponentLoader:5
00104 static PRLogModuleInfo *gJSCLLog;
00105 #endif
00106 
00107 #define LOG(args) PR_LOG(gJSCLLog, PR_LOG_DEBUG, args)
00108 
00109 void JS_DLL_CALLBACK
00110 mozJSLoaderErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep)
00111 {
00112     nsresult rv;
00113 
00114     /* Use the console service to register the error. */
00115     nsCOMPtr<nsIConsoleService> consoleService =
00116         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
00117 
00118     /*
00119      * Make an nsIScriptError, populate it with information from this
00120      * error, then log it with the console service.  The UI can then
00121      * poll the service to update the Error console.
00122      */
00123     nsCOMPtr<nsIScriptError> errorObject = 
00124         do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
00125     
00126     if (consoleService && errorObject) {
00127         /*
00128          * Got an error object; prepare appropriate-width versions of
00129          * various arguments to it.
00130          */
00131         nsAutoString fileUni;
00132         fileUni.AssignWithConversion(rep->filename);
00133 
00134         PRUint32 column = rep->uctokenptr - rep->uclinebuf;
00135 
00136         rv = errorObject->Init(NS_REINTERPRET_CAST(const PRUnichar*,
00137                                                    rep->ucmessage),
00138                                fileUni.get(),
00139                                NS_REINTERPRET_CAST(const PRUnichar*,
00140                                                    rep->uclinebuf),
00141                                rep->lineno, column, rep->flags,
00142                                "component javascript");
00143         if (NS_SUCCEEDED(rv)) {
00144             rv = consoleService->LogMessage(errorObject);
00145             if (NS_SUCCEEDED(rv)) {
00146                 // We're done!  Skip return to fall thru to stderr
00147                 // printout, for the benefit of those invoking the
00148                 // browser with -console
00149                 // return;
00150             }
00151         }
00152     }
00153 
00154     /*
00155      * If any of the above fails for some reason, fall back to
00156      * printing to stderr.
00157      */
00158 #ifdef DEBUG
00159     fprintf(stderr, "JS Component Loader: %s %s:%d\n"
00160             "                     %s\n",
00161             JSREPORT_IS_WARNING(rep->flags) ? "WARNING" : "ERROR",
00162             rep->filename, rep->lineno,
00163             message ? message : "<no message>");
00164 #endif
00165 }
00166 
00167 JS_STATIC_DLL_CALLBACK(JSBool)
00168 Dump(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00169 {
00170     JSString *str;
00171     if (!argc)
00172         return JS_TRUE;
00173     
00174     str = JS_ValueToString(cx, argv[0]);
00175     if (!str)
00176         return JS_FALSE;
00177 
00178     char *bytes = JS_GetStringBytes(str);
00179     bytes = nsCRT::strdup(bytes);
00180 
00181     fputs(bytes, stderr);
00182     nsMemory::Free(bytes);
00183     return JS_TRUE;
00184 }
00185 
00186 JS_STATIC_DLL_CALLBACK(JSBool)
00187 Debug(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00188 {
00189 #ifdef DEBUG
00190     return Dump(cx, obj, argc, argv, rval);
00191 #else
00192     return JS_TRUE;
00193 #endif
00194 }
00195 
00196 static JSFunctionSpec gGlobalFun[] = {
00197     {"dump", Dump, 1 },
00198     {"debug", Debug, 1 },
00199     {0}
00200 };
00201 
00202 class JSCLContextHelper
00203 {
00204 public:
00205     JSCLContextHelper(JSContext* cx);
00206     ~JSCLContextHelper();
00207 
00208     operator JSContext*() const {return mContext;}
00209 
00210     JSCLContextHelper(); // not implemnted
00211 private:
00212     JSContext* mContext;
00213     intN       mContextThread; 
00214 };
00215 
00216 
00217 class JSCLAutoErrorReporterSetter
00218 {
00219 public:
00220     JSCLAutoErrorReporterSetter(JSContext* cx, JSErrorReporter reporter)
00221         {mContext = cx; mOldReporter = JS_SetErrorReporter(cx, reporter);}
00222     ~JSCLAutoErrorReporterSetter()
00223         {JS_SetErrorReporter(mContext, mOldReporter);} 
00224     JSCLAutoErrorReporterSetter(); // not implemented
00225 private:
00226     JSContext* mContext;
00227     JSErrorReporter mOldReporter;
00228 };
00229 
00230 NS_IMPL_ISUPPORTS1(nsXPCFastLoadIO, nsIFastLoadFileIO)
00231 
00232 NS_IMETHODIMP
00233 nsXPCFastLoadIO::GetInputStream(nsIInputStream **_retval)
00234 {
00235     if (! mInputStream) {
00236         nsCOMPtr<nsIInputStream> fileInput;
00237         nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInput),
00238                                                  mFile);
00239         NS_ENSURE_SUCCESS(rv, rv);
00240 
00241         rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
00242                                        fileInput,
00243                                        XPC_DESERIALIZATION_BUFFER_SIZE);
00244         NS_ENSURE_SUCCESS(rv, rv);
00245     }
00246 
00247     NS_ADDREF(*_retval = mInputStream);
00248     return NS_OK;
00249 }
00250 
00251 NS_IMETHODIMP
00252 nsXPCFastLoadIO::GetOutputStream(nsIOutputStream **_retval)
00253 {
00254     if (! mOutputStream) {
00255         PRInt32 ioFlags = PR_WRONLY;
00256         if (! mInputStream) {
00257             ioFlags |= PR_CREATE_FILE | PR_TRUNCATE;
00258         }
00259 
00260         nsCOMPtr<nsIOutputStream> fileOutput;
00261         nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutput),
00262                                                   mFile, ioFlags, 0644);
00263         NS_ENSURE_SUCCESS(rv, rv);
00264 
00265         rv = NS_NewBufferedOutputStream(getter_AddRefs(mOutputStream),
00266                                         fileOutput,
00267                                         XPC_SERIALIZATION_BUFFER_SIZE);
00268         NS_ENSURE_SUCCESS(rv, rv);
00269     }
00270 
00271     NS_ADDREF(*_retval = mOutputStream);
00272     return NS_OK;
00273 }
00274 
00275 static nsresult
00276 ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
00277                      JSScript **script)
00278 {
00279     *script = nsnull;
00280 
00281     PRUint32 size;
00282     nsresult rv = stream->Read32(&size);
00283     NS_ENSURE_SUCCESS(rv, rv);
00284 
00285     char *data;
00286     rv = stream->ReadBytes(size, &data);
00287     NS_ENSURE_SUCCESS(rv, rv);
00288 
00289     JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
00290     NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
00291 
00292     xdr->userdata = stream;
00293     JS_XDRMemSetData(xdr, data, size);
00294 
00295     if (!JS_XDRScript(xdr, script)) {
00296         rv = NS_ERROR_FAILURE;
00297     }
00298 
00299     // Update data in case ::JS_XDRScript called back into C++ code to
00300     // read an XPCOM object.
00301     //
00302     // In that case, the serialization process must have flushed a run
00303     // of counted bytes containing JS data at the point where the XPCOM
00304     // object starts, after which an encoding C++ callback from the JS
00305     // XDR code must have written the XPCOM object directly into the
00306     // nsIObjectOutputStream.
00307     //
00308     // The deserialization process will XDR-decode counted bytes up to
00309     // but not including the XPCOM object, then call back into C++ to
00310     // read the object, then read more counted bytes and hand them off
00311     // to the JSXDRState, so more JS data can be decoded.
00312     //
00313     // This interleaving of JS XDR data and XPCOM object data may occur
00314     // several times beneath the call to ::JS_XDRScript, above.  At the
00315     // end of the day, we need to free (via nsMemory) the data owned by
00316     // the JSXDRState.  So we steal it back, nulling xdr's buffer so it
00317     // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
00318 
00319     uint32 length;
00320     data = NS_STATIC_CAST(char*, JS_XDRMemGetData(xdr, &length));
00321     if (data) {
00322         JS_XDRMemSetData(xdr, nsnull, 0);
00323     }
00324 
00325     JS_XDRDestroy(xdr);
00326 
00327     // If data is null now, it must have been freed while deserializing an
00328     // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
00329     if (data) {
00330         nsMemory::Free(data);
00331     }
00332 
00333     return rv;
00334 }
00335 
00336 static nsresult
00337 WriteScriptToStream(JSContext *cx, JSScript *script,
00338                     nsIObjectOutputStream *stream)
00339 {
00340     JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
00341     NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
00342 
00343     xdr->userdata = stream;
00344     nsresult rv = NS_OK;
00345 
00346     if (JS_XDRScript(xdr, &script)) {
00347         // Get the encoded JSXDRState data and write it.  The JSXDRState owns
00348         // this buffer memory and will free it beneath ::JS_XDRDestroy.
00349         //
00350         // If an XPCOM object needs to be written in the midst of the JS XDR
00351         // encoding process, the C++ code called back from the JS engine (e.g.,
00352         // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
00353         // from the JSXDRState to aStream, then write the object, then return
00354         // to JS XDR code with xdr reset so new JS data is encoded at the front
00355         // of the xdr's data buffer.
00356         //
00357         // However many XPCOM objects are interleaved with JS XDR data in the
00358         // stream, when control returns here from ::JS_XDRScript, we'll have
00359         // one last buffer of data to write to aStream.
00360 
00361         uint32 size;
00362         const char* data = NS_REINTERPRET_CAST(const char*,
00363                                                JS_XDRMemGetData(xdr, &size));
00364         NS_ASSERTION(data, "no decoded JSXDRState data!");
00365 
00366         rv = stream->Write32(size);
00367         if (NS_SUCCEEDED(rv)) {
00368             rv = stream->WriteBytes(data, size);
00369         }
00370     } else {
00371         rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
00372     }
00373 
00374     JS_XDRDestroy(xdr);
00375     return rv;
00376 }
00377 
00378 mozJSComponentLoader::mozJSComponentLoader()
00379     : mRuntime(nsnull),
00380       mContext(nsnull),
00381       mModules(nsnull),
00382       mGlobals(nsnull),
00383       mInitialized(PR_FALSE)
00384 {
00385 #ifdef PR_LOGGING
00386     if (!gJSCLLog) {
00387         gJSCLLog = PR_NewLogModule("JSComponentLoader");
00388     }
00389 #endif
00390 }
00391 
00392 static PRIntn PR_CALLBACK
00393 UnrootGlobals(PLHashEntry *he, PRIntn i, void *arg)
00394 {
00395     JSContext *cx = (JSContext *)arg;
00396     JSObject *global = (JSObject *)he->value;
00397     JS_ClearScope(cx, global);
00398     JS_RemoveRoot(cx, &he->value);
00399     nsCRT::free((char *)he->key);
00400     return HT_ENUMERATE_REMOVE;
00401 }
00402 
00403 static PRIntn PR_CALLBACK
00404 UnloadAndReleaseModules(PLHashEntry *he, PRIntn i, void *arg)
00405 {
00406     nsIModule *module = NS_STATIC_CAST(nsIModule *, he->value);
00407     nsIComponentManager *mgr = NS_STATIC_CAST(nsIComponentManager *, arg);
00408     PRBool canUnload;
00409     nsresult rv = module->CanUnload(mgr, &canUnload);
00410     NS_ASSERTION(NS_SUCCEEDED(rv), "module CanUnload failed");
00411     if (NS_SUCCEEDED(rv) && canUnload) {
00412         NS_RELEASE(module);
00413         /* XXX need to unroot the global for the module as well */
00414         nsCRT::free((char *)he->key);
00415         return HT_ENUMERATE_REMOVE;
00416     }
00417     return HT_ENUMERATE_NEXT;
00418 }
00419 
00420 mozJSComponentLoader::~mozJSComponentLoader()
00421 {
00422     NS_ASSERTION(!mFastLoadTimer,
00423                  "Fastload file should have been closed via xpcom-shutdown");
00424 }
00425 
00426 NS_IMPL_ISUPPORTS2(mozJSComponentLoader, nsIComponentLoader, nsIObserver)
00427 
00428 NS_IMETHODIMP
00429 mozJSComponentLoader::GetFactory(const nsIID &aCID,
00430                                  const char *aLocation,
00431                                  const char *aType,
00432                                  nsIFactory **_retval)
00433 {
00434     if (!_retval)
00435         return NS_ERROR_NULL_POINTER;
00436 
00437 #ifdef DEBUG_shaver_off
00438     char *cidString = aCID.ToString();
00439     fprintf(stderr, "mJCL::GetFactory(%s,%s,%s)\n", cidString, aLocation, aType);
00440     delete [] cidString;
00441 #endif
00442     
00443     nsresult rv;
00444     nsIModule *module = ModuleForLocation(aLocation, 0, &rv);
00445     if (NS_FAILED(rv)) {
00446 #ifdef DEBUG_shaver_off
00447         fprintf(stderr, "ERROR: couldn't get module for %s\n", aLocation);
00448 #endif
00449         return rv;
00450     }
00451     
00452     rv = module->GetClassObject(mCompMgr, aCID, NS_GET_IID(nsIFactory),
00453                                 (void **)_retval);
00454 #ifdef DEBUG_shaver_off
00455     fprintf(stderr, "GetClassObject %s\n", NS_FAILED(rv) ? "FAILED" : "ok");
00456 #endif
00457     return rv;
00458 }
00459 
00460 NS_IMETHODIMP
00461 mozJSComponentLoader::Init(nsIComponentManager *aCompMgr, nsISupports *aReg)
00462 {
00463     mCompMgr = aCompMgr;
00464 
00465     nsresult rv;
00466     mLoaderManager = do_QueryInterface(mCompMgr, &rv);
00467     if (NS_FAILED(rv))
00468         return rv;
00469 
00470     return NS_OK;
00471 }
00472 
00473 nsresult
00474 mozJSComponentLoader::ReallyInit()
00475 {
00476     nsresult rv;
00477 
00478     /*
00479      * Get the JSRuntime from the runtime svc, if possible.
00480      * We keep a reference around, because it's a Bad Thing if the runtime
00481      * service gets shut down before we're done.  Bad!
00482      */
00483 
00484     mRuntimeService = do_GetService(kJSRuntimeServiceContractID, &rv);
00485     if (NS_FAILED(rv) ||
00486         NS_FAILED(rv = mRuntimeService->GetRuntime(&mRuntime)))
00487         return rv;
00488 
00489     // Create our compilation context.
00490     mContext = JS_NewContext(mRuntime, 256);
00491     if (!mContext)
00492         return NS_ERROR_OUT_OF_MEMORY;
00493 
00494     uint32 options = JS_GetOptions(mContext);
00495     JS_SetOptions(mContext, options | JSOPTION_XML);
00496 
00497     // enable Javascript 1.7 features (let, yield, etc. - see bug#351515)
00498     JS_SetVersion(mContext, JSVERSION_1_7);
00499     
00500 #ifndef XPCONNECT_STANDALONE
00501     nsCOMPtr<nsIScriptSecurityManager> secman = 
00502         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
00503     if (!secman)
00504         return NS_ERROR_FAILURE;
00505 
00506     rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
00507     if (NS_FAILED(rv) || !mSystemPrincipal)
00508         return NS_ERROR_FAILURE;
00509 #endif
00510 
00511     mModules = PL_NewHashTable(16, PL_HashString, PL_CompareStrings,
00512                                PL_CompareValues, 0, 0);
00513     if (!mModules)
00514         return NS_ERROR_OUT_OF_MEMORY;
00515     
00516     mGlobals = PL_NewHashTable(16, PL_HashString, PL_CompareStrings,
00517                                PL_CompareValues, 0, 0);
00518     if (!mGlobals)
00519         return NS_ERROR_OUT_OF_MEMORY;
00520 
00521     // Set up our fastload file
00522     nsCOMPtr<nsIFastLoadService> flSvc = do_GetFastLoadService(&rv);
00523     if (flSvc)
00524     {
00525         rv = flSvc->NewFastLoadFile("XPC", getter_AddRefs(mFastLoadFile));
00526         if (NS_FAILED(rv)) {
00527             LOG(("Could not get fastload file location\n"));
00528         }
00529         // Listen for xpcom-shutdown so that we can close out our fastload file
00530         // at that point (after that we can no longer create an input stream).
00531         nsCOMPtr<nsIObserverService> obsSvc =
00532             do_GetService(kObserverServiceContractID, &rv);
00533         NS_ENSURE_SUCCESS(rv, rv);
00534         
00535         rv = obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
00536         NS_ENSURE_SUCCESS(rv, rv);
00537         
00538 #ifdef DEBUG_shaver_off
00539         fprintf(stderr, "mJCL: ReallyInit success!\n");
00540 #endif
00541     }
00542 
00543     mInitialized = PR_TRUE;
00544 
00545     return NS_OK;
00546 }
00547 
00548 NS_IMETHODIMP
00549 mozJSComponentLoader::AutoRegisterComponents(PRInt32 when,
00550                                              nsIFile *aDirectory)
00551 {
00552     return RegisterComponentsInDir(when, aDirectory);
00553 }
00554 
00555 nsresult
00556 mozJSComponentLoader::RegisterComponentsInDir(PRInt32 when, nsIFile *dir)
00557 {
00558     nsresult rv;
00559     PRBool isDir;
00560     
00561     if (NS_FAILED(rv = dir->IsDirectory(&isDir)))
00562         return rv;
00563     
00564     if (!isDir)
00565         return NS_ERROR_INVALID_ARG;
00566 
00567     // Create a directory iterator
00568     nsCOMPtr<nsISimpleEnumerator> dirIterator;
00569     rv = dir->GetDirectoryEntries(getter_AddRefs(dirIterator));
00570     
00571     if (NS_FAILED(rv)) return rv;
00572     
00573    // whip through the directory to register every file
00574     nsIFile *dirEntry = NULL;
00575     PRBool more = PR_FALSE;
00576 
00577     rv = dirIterator->HasMoreElements(&more);
00578     if (NS_FAILED(rv)) return rv;
00579     while (more == PR_TRUE)
00580     {
00581         rv = dirIterator->GetNext((nsISupports**)&dirEntry);
00582         if (NS_SUCCEEDED(rv))
00583         {
00584             rv = dirEntry->IsDirectory(&isDir);
00585             if (NS_SUCCEEDED(rv))
00586             {
00587                 if (isDir == PR_TRUE)
00588                 {
00589                     // This is a directory. Grovel for components into the directory.
00590                     rv = RegisterComponentsInDir(when, dirEntry);
00591                 }
00592                 else
00593                 {
00594                     PRBool registered;
00595                     // This is a file. Try to register it.
00596                     rv = AutoRegisterComponent(when, dirEntry, &registered);
00597                 }
00598             }
00599             NS_RELEASE(dirEntry);
00600         }
00601         rv = dirIterator->HasMoreElements(&more);
00602         if (NS_FAILED(rv)) return rv;
00603     }
00604 
00605     return NS_OK;
00606 }
00607 
00608 
00609 nsresult
00610 mozJSComponentLoader::SetRegistryInfo(const char *registryLocation,
00611                                       nsIFile *component)
00612 {
00613     nsresult rv;
00614     if (!mLoaderManager)
00615         return NS_ERROR_FAILURE;
00616     
00617     PRInt64 modDate;
00618     rv = component->GetLastModifiedTime(&modDate);
00619     if (NS_FAILED(rv))
00620         return rv;
00621 
00622 #ifdef DEBUG_shaver_off
00623     fprintf(stderr, "SetRegistryInfo(%s) => (%d,%d)\n", registryLocation,
00624             modDate, fileSize);
00625 #endif
00626     return mLoaderManager->SaveFileInfo(component, registryLocation, modDate);                                                          
00627 }
00628 
00629 nsresult
00630 mozJSComponentLoader::RemoveRegistryInfo(nsIFile *component, const char *registryLocation)
00631 {
00632     if (!mLoaderManager)
00633         return NS_ERROR_FAILURE;
00634     
00635     return mLoaderManager->RemoveFileInfo(component, registryLocation);                                                          
00636 }
00637 
00638 
00639 PRBool
00640 mozJSComponentLoader::HasChanged(const char *registryLocation,
00641                                  nsIFile *component)
00642 {
00643     if (!mLoaderManager)
00644         return NS_ERROR_FAILURE;
00645     
00646     PRInt64 lastTime;
00647     component->GetLastModifiedTime(&lastTime);
00648 
00649     PRBool hasChanged = PR_TRUE;
00650     mLoaderManager->HasFileChanged(component, registryLocation, lastTime, &hasChanged);                                                          
00651     return hasChanged;
00652 }
00653 
00654 NS_IMETHODIMP
00655 mozJSComponentLoader::AutoRegisterComponent(PRInt32 when,
00656                                             nsIFile *component,
00657                                             PRBool *registered)
00658 {
00659     nsresult rv;
00660     if (!registered)
00661         return NS_ERROR_NULL_POINTER;
00662 
00663     const char jsExtension[] = ".js";
00664     int jsExtensionLen = 3;
00665     nsCAutoString leafName;
00666 
00667     *registered = PR_FALSE;
00668 
00669     /* we only do files */
00670     PRBool isFile = PR_FALSE;
00671     if (NS_FAILED(rv = component->IsFile(&isFile)) || !isFile)
00672         return rv;
00673 
00674     if (NS_FAILED(rv = component->GetNativeLeafName(leafName)))
00675         return rv;
00676     int len = leafName.Length();
00677     
00678     /* if it's not *.js, return now */
00679     if (len < jsExtensionLen || // too short
00680         PL_strcasecmp(leafName.get() + len - jsExtensionLen, jsExtension))
00681         return NS_OK;
00682 
00683 #ifdef DEBUG_shaver_off
00684     fprintf(stderr, "mJCL: registering JS component %s\n",
00685             leafName.get());
00686 #endif
00687       
00688     rv = AttemptRegistration(component, PR_FALSE);
00689 #ifdef DEBUG_shaver_off
00690     if (NS_SUCCEEDED(rv))
00691         fprintf(stderr, "registered module %s\n", leafName.get());
00692     else if (rv == NS_ERROR_FACTORY_REGISTER_AGAIN) 
00693         fprintf(stderr, "deferred module %s\n", leafName.get());
00694     else
00695         fprintf(stderr, "failed to register %s\n", leafName.get());
00696 #endif    
00697     *registered = (PRBool) NS_SUCCEEDED(rv);
00698     return NS_OK;
00699 }
00700 
00701 NS_IMETHODIMP
00702 mozJSComponentLoader::AutoUnregisterComponent(PRInt32 when,
00703                                               nsIFile *component,
00704                                               PRBool *unregistered)
00705 {
00706     nsresult rv;
00707     if (!unregistered)
00708         return NS_ERROR_NULL_POINTER;
00709 
00710     const char jsExtension[] = ".js";
00711     int jsExtensionLen = 3;
00712     nsCAutoString leafName;
00713 
00714     *unregistered = PR_FALSE;
00715 
00716     /* we only do files */
00717     PRBool isFile = PR_FALSE;
00718     if (NS_FAILED(rv = component->IsFile(&isFile)) || !isFile)
00719         return rv;
00720 
00721     if (NS_FAILED(rv = component->GetNativeLeafName(leafName)))
00722         return rv;
00723     int len = leafName.Length();
00724     
00725     /* if it's not *.js, return now */
00726     if (len < jsExtensionLen || // too short
00727         PL_strcasecmp(leafName.get() + len - jsExtensionLen, jsExtension))
00728         return NS_OK;
00729 
00730     rv = UnregisterComponent(component);
00731 #ifdef DEBUG_dp
00732     if (NS_SUCCEEDED(rv))
00733         fprintf(stderr, "unregistered module %s\n", leafName.get());
00734     else
00735         fprintf(stderr, "failed to unregister %s\n", leafName.get());
00736 #endif    
00737     *unregistered = (PRBool) NS_SUCCEEDED(rv);
00738     return NS_OK;
00739 }
00740 
00741 nsresult
00742 mozJSComponentLoader::AttemptRegistration(nsIFile *component,
00743                                           PRBool deferred)
00744 {
00745     nsXPIDLCString registryLocation;
00746     nsresult rv;
00747     
00748     // what I want to do here is QI for a Component Registration Manager.  Since this 
00749     // has not been invented yet, QI to the obsolete manager.  Kids, don't do this at home.
00750     nsCOMPtr<nsIComponentManagerObsolete> obsoleteManager = do_QueryInterface(mCompMgr, &rv);
00751     if (obsoleteManager)
00752         rv = obsoleteManager->RegistryLocationForSpec(component, 
00753                                                       getter_Copies(registryLocation));
00754     if (NS_FAILED(rv))
00755         return rv;
00756     
00757     /* no need to check registry data on deferred reg */
00758     if (!deferred && !HasChanged(registryLocation, component))
00759         return NS_OK;
00760     
00761     nsIModule *module = ModuleForLocation(registryLocation, component, &rv);
00762     if (NS_FAILED(rv)) {
00763         SetRegistryInfo(registryLocation, component);
00764         return rv;
00765     }
00766 
00767     // Notify observers, if any, of autoregistration work
00768     nsCOMPtr<nsIObserverService> observerService = 
00769         do_GetService(kObserverServiceContractID, &rv);
00770     NS_ENSURE_SUCCESS(rv, rv);
00771 
00772     nsCOMPtr<nsIServiceManager> mgr;
00773     rv = NS_GetServiceManager(getter_AddRefs(mgr));
00774     NS_ENSURE_SUCCESS(rv, rv);
00775 
00776     // this string can't come from a string bundle, because we
00777     // don't have string bundles yet.
00778     NS_ConvertASCIItoUCS2 fileName("(no name)");
00779 
00780     // get the file name
00781     if (component) {
00782         component->GetLeafName(fileName);
00783     }
00784 
00785     // this string can't come from a string bundle, because we
00786     // don't have string bundles yet.
00787     const nsPromiseFlatString &msg =
00788         PromiseFlatString(NS_LITERAL_STRING("Registering JS component ") +
00789                           fileName);
00790 
00791     observerService->NotifyObservers(mgr,
00792                                      NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID,
00793                                      msg.get());
00794     
00795     rv = module->RegisterSelf(mCompMgr, component, registryLocation,
00796                               MOZJSCOMPONENTLOADER_TYPE_NAME);
00797     if (rv == NS_ERROR_FACTORY_REGISTER_AGAIN) {
00798         if (!deferred) {
00799             mDeferredComponents.AppendElement(component);
00800         }
00801         /*
00802          * we don't enter in the registry because we may want to
00803          * try again on a later autoreg, in case a dependency has
00804          * become available. 
00805          */
00806     } else {
00807         SetRegistryInfo(registryLocation, component);
00808     }
00809 
00810     return rv;
00811 }
00812 
00813 nsresult
00814 mozJSComponentLoader::UnregisterComponent(nsIFile *component)
00815 {
00816     nsXPIDLCString registryLocation;
00817     nsresult rv;
00818     
00819     // what I want to do here is QI for a Component Registration Manager.  Since this 
00820     // has not been invented yet, QI to the obsolete manager.  Kids, don't do this at home.
00821     nsCOMPtr<nsIComponentManagerObsolete> obsoleteManager = do_QueryInterface(mCompMgr, &rv);
00822     if (obsoleteManager)
00823         rv = obsoleteManager->RegistryLocationForSpec(component, 
00824                                                       getter_Copies(registryLocation));
00825     if (NS_FAILED(rv))
00826         return rv;
00827     
00828     nsIModule *module = ModuleForLocation(registryLocation, component, &rv);
00829     NS_ENSURE_SUCCESS(rv, rv);
00830     
00831     // Notify observers, if any, of autoregistration work
00832     nsCOMPtr<nsIObserverService> observerService = 
00833         do_GetService(kObserverServiceContractID, &rv);
00834     NS_ENSURE_SUCCESS(rv, rv);
00835 
00836     nsCOMPtr<nsIServiceManager> mgr;
00837     rv = NS_GetServiceManager(getter_AddRefs(mgr));
00838     NS_ENSURE_SUCCESS(rv, rv);
00839 
00840     const nsAFlatString &msg = NS_LITERAL_STRING("Unregistering JS component");
00841     observerService->NotifyObservers(mgr,
00842                                      NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID,
00843                                      msg.get());
00844     
00845     rv = module->UnregisterSelf(mCompMgr, component, registryLocation);
00846     if (NS_SUCCEEDED(rv)) {
00847         // Remove any autoreg specific info. Ignore error.
00848         RemoveRegistryInfo(component, registryLocation);
00849     }
00850         
00851     return rv;
00852 }
00853 
00854 
00855 nsresult
00856 mozJSComponentLoader::RegisterDeferredComponents(PRInt32 aWhen,
00857                                                  PRBool *aRegistered)
00858 {
00859     nsresult rv;
00860     *aRegistered = PR_FALSE;
00861 
00862     PRUint32 count;
00863     rv = mDeferredComponents.Count(&count);
00864 #ifdef DEBUG_shaver_off
00865     fprintf(stderr, "mJCL: registering deferred (%d)\n", count);
00866 #endif
00867     if (NS_FAILED(rv) || !count)
00868         return NS_OK;
00869     
00870     for (PRUint32 i = 0; i < count; i++) {
00871         nsCOMPtr<nsIFile> component;
00872         rv = mDeferredComponents.QueryElementAt(i, NS_GET_IID(nsIFile), getter_AddRefs(component));
00873         if (NS_FAILED(rv))
00874             continue;
00875 
00876         rv = AttemptRegistration(component, PR_TRUE /* deferred */);
00877         if (rv != NS_ERROR_FACTORY_REGISTER_AGAIN) {
00878             if (NS_SUCCEEDED(rv))
00879                 *aRegistered = PR_TRUE;
00880             mDeferredComponents.RemoveElementAt(i);
00881         }
00882     }
00883 
00884 #ifdef DEBUG_shaver_off
00885     rv = mDeferredComponents.Count(&count);
00886     if (NS_SUCCEEDED(rv)) {
00887         if (*aRegistered)
00888             fprintf(stderr, "mJCL: registered deferred, %d left\n",
00889                     count);
00890         else
00891             fprintf(stderr, "mJCL: didn't register any components, %d left\n",
00892                     count);
00893     }
00894 #endif
00895     /* are there any fatal errors? */
00896     return NS_OK;
00897 }
00898 
00899 
00900 nsIModule*
00901 mozJSComponentLoader::ModuleForLocation(const char *registryLocation,
00902                                         nsIFile *component, nsresult *status)
00903 {
00904     nsresult rv;
00905     if (!mInitialized) {
00906         rv = ReallyInit();
00907         if (NS_FAILED(rv)) {
00908             *status = rv;
00909             return nsnull;
00910         }
00911     }
00912 
00913     PLHashNumber hash = PL_HashString(registryLocation);
00914     PLHashEntry **hep = PL_HashTableRawLookup(mModules, hash,
00915                                               registryLocation);
00916     PLHashEntry *he = *hep;
00917     if (he) {
00918         *status = NS_OK;
00919         return NS_STATIC_CAST(nsIModule*, he->value);
00920     }
00921 
00922     JSObject *global;
00923     rv = GlobalForLocation(registryLocation, component, &global);
00924     if (NS_FAILED(rv)) {
00925 #ifdef DEBUG_shaver
00926         fprintf(stderr, "GlobalForLocation failed!\n");
00927 #endif
00928         *status = rv;
00929         return nsnull;
00930     }
00931 
00932     nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
00933                                                &rv);
00934     if (NS_FAILED(rv)) {
00935         *status = rv;
00936         return nsnull;
00937     }
00938 
00939     JSCLContextHelper cx(mContext);
00940 
00941     JSObject* cm_jsobj;
00942     nsCOMPtr<nsIXPConnectJSObjectHolder> cm_holder;
00943     rv = xpc->WrapNative(cx, global, mCompMgr, 
00944                          NS_GET_IID(nsIComponentManager),
00945                          getter_AddRefs(cm_holder));
00946 
00947     if (NS_FAILED(rv)) {
00948 #ifdef DEBUG_shaver
00949         fprintf(stderr, "WrapNative(%p,%p,nsIComponentManager) failed: %x\n",
00950                 (void *)(JSContext*)cx, (void *)mCompMgr, rv);
00951 #endif
00952         *status = rv;
00953         return nsnull;
00954     }
00955 
00956     rv = cm_holder->GetJSObject(&cm_jsobj);
00957     if (NS_FAILED(rv)) {
00958 #ifdef DEBUG_shaver
00959         fprintf(stderr, "GetJSObject of ComponentManager failed\n");
00960 #endif
00961         *status = rv;
00962         return nsnull;
00963     }
00964 
00965     JSCLAutoErrorReporterSetter aers(cx, mozJSLoaderErrorReporter);
00966 
00967     jsval argv[2], retval, NSGetModule_val;
00968 
00969     if (!JS_GetProperty(cx, global, "NSGetModule", &NSGetModule_val) ||
00970         JSVAL_IS_VOID(NSGetModule_val)) {
00971         *status = NS_ERROR_FAILURE;
00972         return nsnull;
00973     }
00974 
00975     if (JS_TypeOfValue(cx, NSGetModule_val) != JSTYPE_FUNCTION) {
00976         JS_ReportError(cx, "%s has NSGetModule property that is not a function",
00977                        registryLocation);
00978         *status = NS_ERROR_FAILURE;
00979         return nsnull;
00980     }
00981     
00982     argv[0] = OBJECT_TO_JSVAL(cm_jsobj);
00983     argv[1] = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, registryLocation));
00984     if (!JS_CallFunctionValue(cx, global, NSGetModule_val, 2, argv, &retval)) {
00985         *status = NS_ERROR_FAILURE;
00986         return nsnull;
00987     }
00988 
00989 #ifdef DEBUG_shaver_off
00990     JSString *s = JS_ValueToString(cx, retval);
00991     fprintf(stderr, "mJCL: %s::NSGetModule returned %s\n",
00992             registryLocation, JS_GetStringBytes(s));
00993 #endif
00994 
00995     JSObject *jsModuleObj;
00996     if (!JS_ValueToObject(cx, retval, &jsModuleObj)) {
00997         /* XXX report error properly */
00998 #ifdef DEBUG
00999         fprintf(stderr, "mJCL: couldn't convert %s's nsIModule to obj\n",
01000                 registryLocation);
01001 #endif
01002         *status = NS_ERROR_FAILURE;
01003         return nsnull;
01004     }
01005 
01006     nsIModule *module;
01007     rv = xpc->WrapJS(cx, jsModuleObj, NS_GET_IID(nsIModule), (void**)&module);
01008     if (NS_FAILED(rv)) {
01009         /* XXX report error properly */
01010 #ifdef DEBUG
01011         fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
01012 #endif
01013         *status = rv;
01014         return nsnull;
01015     }
01016 
01017     /* we hand our reference to the hash table, it'll be released much later */
01018     he = PL_HashTableRawAdd(mModules, hep, hash,
01019                             nsCRT::strdup(registryLocation), module);
01020 
01021     *status = NS_OK;
01022     return module;
01023 }
01024 
01025 // Some stack based classes for cleaning up on early return
01026 #ifdef HAVE_PR_MEMMAP
01027 class FileAutoCloser
01028 {
01029  public:
01030     explicit FileAutoCloser(PRFileDesc *file) : mFile(file) {}
01031     ~FileAutoCloser() { PR_Close(mFile); }
01032  private:
01033     PRFileDesc *mFile;
01034 };
01035 
01036 class FileMapAutoCloser
01037 {
01038  public:
01039     explicit FileMapAutoCloser(PRFileMap *map) : mMap(map) {}
01040     ~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
01041  private:
01042     PRFileMap *mMap;
01043 };
01044 #endif
01045 
01046 class JSPrincipalsHolder
01047 {
01048  public:
01049     JSPrincipalsHolder(JSContext *cx, JSPrincipals *principals)
01050         : mCx(cx), mPrincipals(principals) {}
01051     ~JSPrincipalsHolder() { JSPRINCIPALS_DROP(mCx, mPrincipals); }
01052  private:
01053     JSContext *mCx;
01054     JSPrincipals *mPrincipals;
01055 };
01056 
01057 class JSScriptHolder
01058 {
01059  public:
01060     JSScriptHolder(JSContext *cx, JSScript *script)
01061         : mCx(cx), mScript(script) {}
01062     ~JSScriptHolder() { ::JS_DestroyScript(mCx, mScript); }
01063  private:
01064     JSContext *mCx;
01065     JSScript *mScript;
01066 };
01067 
01068 class FastLoadStateHolder
01069 {
01070  public:
01071     explicit FastLoadStateHolder(nsIFastLoadService *service);
01072     ~FastLoadStateHolder() { pop(); }
01073 
01074     void pop();
01075 
01076  private:
01077     nsCOMPtr<nsIFastLoadService> mService;
01078     nsCOMPtr<nsIFastLoadFileIO> mIO;
01079     nsCOMPtr<nsIObjectInputStream> mInputStream;
01080     nsCOMPtr<nsIObjectOutputStream> mOutputStream;
01081 };
01082 
01083 FastLoadStateHolder::FastLoadStateHolder(nsIFastLoadService *service)
01084 {
01085     if (!service)
01086         return;
01087 
01088     mService = service;
01089     service->GetFileIO(getter_AddRefs(mIO));
01090     service->GetInputStream(getter_AddRefs(mInputStream));
01091     service->GetOutputStream(getter_AddRefs(mOutputStream));
01092 }
01093 
01094 void
01095 FastLoadStateHolder::pop()
01096 {
01097     if (!mService)
01098         return;
01099 
01100     mService->SetFileIO(mIO);
01101     mService->SetInputStream(mInputStream);
01102     mService->SetOutputStream(mOutputStream);
01103 
01104     mService = nsnull;
01105 }
01106 
01107 /* static */
01108 void
01109 mozJSComponentLoader::CloseFastLoad(nsITimer *timer, void *closure)
01110 {
01111     NS_STATIC_CAST(mozJSComponentLoader*, closure)->CloseFastLoad();
01112 }
01113 
01114 void
01115 mozJSComponentLoader::CloseFastLoad()
01116 {
01117     // Close our fastload streams
01118     LOG(("Closing fastload file\n"));
01119     if (mFastLoadOutput) {
01120         nsresult rv = mFastLoadOutput->Close();
01121         if (NS_SUCCEEDED(rv)) {
01122             nsCOMPtr<nsIFastLoadService> flSvc = do_GetFastLoadService(&rv);
01123             if (NS_SUCCEEDED(rv)) {
01124                 flSvc->CacheChecksum(mFastLoadFile, mFastLoadOutput);
01125             }
01126         }
01127         mFastLoadOutput = nsnull;
01128     }
01129     if (mFastLoadInput) {
01130         mFastLoadInput->Close();
01131         mFastLoadInput = nsnull;
01132     }
01133 
01134     mFastLoadIO = nsnull;
01135     mFastLoadTimer = nsnull;
01136 }
01137 
01138 nsresult
01139 mozJSComponentLoader::StartFastLoad(nsIFastLoadService *flSvc)
01140 {
01141     if (!mFastLoadFile || !flSvc) {
01142         return NS_ERROR_NOT_AVAILABLE;
01143     }
01144 
01145     // Now set our IO object as current, and create our streams.
01146     if (!mFastLoadIO) {
01147         mFastLoadIO = new nsXPCFastLoadIO(mFastLoadFile);
01148         NS_ENSURE_TRUE(mFastLoadIO, NS_ERROR_OUT_OF_MEMORY);
01149     }
01150 
01151     nsresult rv = flSvc->SetFileIO(mFastLoadIO);
01152     NS_ENSURE_SUCCESS(rv, rv);
01153 
01154     if (!mFastLoadInput && !mFastLoadOutput) {
01155         // First time accessing the fastload file
01156         PRBool exists;
01157         mFastLoadFile->Exists(&exists);
01158         if (exists) {
01159             LOG(("trying to use existing fastload file\n"));
01160 
01161             nsCOMPtr<nsIInputStream> input;
01162             rv = mFastLoadIO->GetInputStream(getter_AddRefs(input));
01163             NS_ENSURE_SUCCESS(rv, rv);
01164 
01165             rv = flSvc->NewInputStream(input, getter_AddRefs(mFastLoadInput));
01166             if (NS_SUCCEEDED(rv)) {
01167                 LOG(("opened fastload file for reading\n"));
01168 
01169                 nsCOMPtr<nsIFastLoadReadControl>
01170                     readControl(do_QueryInterface(mFastLoadInput));
01171                 if (readControl) {
01172                     // Verify checksum, using the FastLoadService's
01173                     // checksum cache to avoid computing more than once
01174                     // per session.
01175                     PRUint32 checksum;
01176                     rv = readControl->GetChecksum(&checksum);
01177                     if (NS_SUCCEEDED(rv)) {
01178                         PRUint32 verified;
01179                         rv = flSvc->ComputeChecksum(mFastLoadFile,
01180                                                     readControl, &verified);
01181                         if (NS_SUCCEEDED(rv) && verified != checksum) {
01182                             LOG(("Incorrect checksum detected"));
01183                             rv = NS_ERROR_FAILURE;
01184                         }
01185                     }
01186                 }
01187 
01188                 if (NS_SUCCEEDED(rv)) {
01189                     /* Get the JS bytecode version number and validate it. */
01190                     PRUint32 version;
01191                     rv = mFastLoadInput->Read32(&version);
01192                     if (NS_SUCCEEDED(rv) && version != JSXDR_BYTECODE_VERSION) {
01193                         LOG(("Bad JS bytecode version\n"));
01194                         rv = NS_ERROR_UNEXPECTED;
01195                     }
01196                 }
01197             }
01198             if (NS_FAILED(rv)) {
01199                 LOG(("Invalid fastload file detected, removing it\n"));
01200                 if (mFastLoadInput) {
01201                     mFastLoadInput->Close();
01202                     mFastLoadInput = nsnull;
01203                 } else {
01204                     input->Close();
01205                 }
01206                 mFastLoadIO->SetInputStream(nsnull);
01207                 mFastLoadFile->Remove(PR_FALSE);
01208                 exists = PR_FALSE;
01209             }
01210         }
01211 
01212         if (!exists) {
01213             LOG(("Creating new fastload file\n"));
01214 
01215             nsCOMPtr<nsIOutputStream> output;
01216             rv = mFastLoadIO->GetOutputStream(getter_AddRefs(output));
01217             NS_ENSURE_SUCCESS(rv, rv);
01218 
01219             rv = flSvc->NewOutputStream(output,
01220                                         getter_AddRefs(mFastLoadOutput));
01221 
01222             if (NS_SUCCEEDED(rv))
01223                 rv = mFastLoadOutput->Write32(JSXDR_BYTECODE_VERSION);
01224 
01225             if (NS_FAILED(rv)) {
01226                 LOG(("Fatal error, could not create fastload file\n"));
01227 
01228                 if (mFastLoadOutput) {
01229                     mFastLoadOutput->Close();
01230                     mFastLoadOutput = nsnull;
01231                 } else {
01232                     output->Close();
01233                 }
01234                 mFastLoadIO->SetOutputStream(nsnull);
01235                 mFastLoadFile->Remove(PR_FALSE);
01236                 return rv;
01237             }
01238         }
01239     }
01240 
01241     flSvc->SetInputStream(mFastLoadInput);
01242     flSvc->SetOutputStream(mFastLoadOutput);
01243 
01244     // Start our update timer.  This allows us to keep the stream open
01245     // when many components are loaded in succession, but close it once
01246     // there has been a period of inactivity.
01247 
01248     if (!mFastLoadTimer) {
01249         mFastLoadTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
01250         NS_ENSURE_SUCCESS(rv, rv);
01251 
01252         rv = mFastLoadTimer->InitWithFuncCallback(&mozJSComponentLoader::CloseFastLoad,
01253                                                   this,
01254                                                   kFastLoadWriteDelay,
01255                                                   nsITimer::TYPE_ONE_SHOT);
01256     } else {
01257         rv = mFastLoadTimer->SetDelay(kFastLoadWriteDelay);
01258     }
01259 
01260     return rv;
01261 }
01262 
01263 nsresult
01264 mozJSComponentLoader::ReadScript(nsIFastLoadService *flSvc,
01265                                  const char *nativePath, nsIURI *uri,
01266                                  JSContext *cx, JSScript **script)
01267 {
01268     NS_ASSERTION(flSvc, "fastload not initialized");
01269 
01270     nsresult rv = flSvc->StartMuxedDocument(uri, nativePath,
01271                                             nsIFastLoadService::NS_FASTLOAD_READ);
01272     if (NS_FAILED(rv)) {
01273         return rv; // don't warn since NOT_AVAILABLE is an ok error
01274     }
01275 
01276     LOG(("Found %s in fastload file\n", nativePath));
01277 
01278     nsCOMPtr<nsIURI> oldURI;
01279     rv = flSvc->SelectMuxedDocument(uri, getter_AddRefs(oldURI));
01280     NS_ENSURE_SUCCESS(rv, rv);
01281 
01282     NS_ASSERTION(mFastLoadInput,
01283                  "FASTLOAD_READ should only succeed with an input stream");
01284 
01285     rv = ReadScriptFromStream(cx, mFastLoadInput, script);
01286     if (NS_SUCCEEDED(rv)) {
01287         rv = flSvc->EndMuxedDocument(uri);
01288     }
01289 
01290     return rv;
01291 }
01292 
01293 nsresult
01294 mozJSComponentLoader::WriteScript(nsIFastLoadService *flSvc, JSScript *script,
01295                                   nsIFile *component, const char *nativePath,
01296                                   nsIURI *uri, JSContext *cx)
01297 {
01298     NS_ASSERTION(flSvc, "fastload not initialized");
01299     nsresult rv;
01300 
01301     if (!mFastLoadOutput) {
01302         // Trying to read a URI that was not in the fastload file will have
01303         // created an output stream for us.  But, if we haven't tried to
01304         // load anything that was missing, it will still be null.
01305         rv = flSvc->GetOutputStream(getter_AddRefs(mFastLoadOutput));
01306         NS_ENSURE_SUCCESS(rv, rv);
01307     }
01308 
01309     NS_ASSERTION(mFastLoadOutput, "must have an output stream here");
01310 
01311     LOG(("Writing %s to fastload\n", nativePath));
01312     rv = flSvc->AddDependency(component);
01313     NS_ENSURE_SUCCESS(rv, rv);
01314 
01315     rv = flSvc->StartMuxedDocument(uri, nativePath,
01316                                    nsIFastLoadService::NS_FASTLOAD_WRITE);
01317     NS_ENSURE_SUCCESS(rv, rv);
01318 
01319     nsCOMPtr<nsIURI> oldURI;
01320     rv = flSvc->SelectMuxedDocument(uri, getter_AddRefs(oldURI));
01321     NS_ENSURE_SUCCESS(rv, rv);
01322 
01323     rv = WriteScriptToStream(cx, script, mFastLoadOutput);
01324     NS_ENSURE_SUCCESS(rv, rv);
01325 
01326     return flSvc->EndMuxedDocument(uri);
01327 }
01328 
01329 nsresult
01330 mozJSComponentLoader::GlobalForLocation(const char *aLocation,
01331                                         nsIFile *aComponent,
01332                                         JSObject **aGlobal)
01333 {
01334     nsresult rv;
01335     if (!mInitialized) {
01336         rv = ReallyInit();
01337         NS_ENSURE_SUCCESS(rv, rv);
01338     }
01339 
01340     PLHashNumber hash = PL_HashString(aLocation);
01341     PLHashEntry **hep = PL_HashTableRawLookup(mGlobals, hash, aLocation);
01342     PLHashEntry *he = *hep;
01343     if (he) {
01344         *aGlobal = NS_STATIC_CAST(JSObject*, he->value);
01345         return NS_OK;
01346     }
01347 
01348     *aGlobal = nsnull;
01349 
01350     JSPrincipals* jsPrincipals = nsnull;
01351     JSCLContextHelper cx(mContext);
01352 
01353 #ifndef XPCONNECT_STANDALONE
01354     rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals);
01355     NS_ENSURE_SUCCESS(rv, rv);
01356 
01357     JSPrincipalsHolder princHolder(mContext, jsPrincipals);
01358 #endif
01359 
01360     nsCOMPtr<nsIXPCScriptable> backstagePass;
01361     rv = mRuntimeService->GetBackstagePass(getter_AddRefs(backstagePass));
01362     NS_ENSURE_SUCCESS(rv, rv);
01363 
01364     JSCLAutoErrorReporterSetter aers(cx, mozJSLoaderErrorReporter);
01365 
01366     nsCOMPtr<nsIXPConnect> xpc =
01367         do_GetService(kXPConnectServiceContractID, &rv);
01368     NS_ENSURE_SUCCESS(rv, rv);
01369 
01370     // Make sure InitClassesWithNewWrappedGlobal() installs the
01371     // backstage pass as the global in our compilation context.
01372     JS_SetGlobalObject(cx, nsnull);
01373 
01374     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
01375     rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
01376                                               NS_GET_IID(nsISupports),
01377                                               nsIXPConnect::
01378                                                   FLAG_SYSTEM_GLOBAL_OBJECT,
01379                                               getter_AddRefs(holder));
01380     NS_ENSURE_SUCCESS(rv, rv);
01381 
01382     JSObject *global;
01383     rv = holder->GetJSObject(&global);
01384     NS_ENSURE_SUCCESS(rv, rv);
01385 
01386     if (!JS_DefineFunctions(cx, global, gGlobalFun)) {
01387         return NS_ERROR_FAILURE;
01388     }
01389 
01390     nsCOMPtr<nsIFile> component = aComponent;
01391     if (!component) {
01392         // what I want to do here is QI for a Component Registration Manager.  Since this 
01393         // has not been invented yet, QI to the obsolete manager.  Kids, don't do this at home.
01394         nsCOMPtr<nsIComponentManagerObsolete> mgr = do_QueryInterface(mCompMgr,
01395                                                                       &rv);
01396         NS_ENSURE_SUCCESS(rv, rv);
01397 
01398         rv = mgr->SpecForRegistryLocation(aLocation,
01399                                           getter_AddRefs(component));
01400         NS_ENSURE_SUCCESS(rv, rv);
01401     }
01402 
01403     nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(component, &rv);
01404     NS_ENSURE_SUCCESS(rv, rv);
01405 
01406     nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
01407     rv = xpc->WrapNative(cx, global, localFile,
01408                          NS_GET_IID(nsILocalFile),
01409                          getter_AddRefs(locationHolder));
01410     NS_ENSURE_SUCCESS(rv, rv);
01411 
01412     JSObject *locationObj;
01413     rv = locationHolder->GetJSObject(&locationObj);
01414     NS_ENSURE_SUCCESS(rv, rv);
01415 
01416     if (!JS_DefineProperty(cx, global, "__LOCATION__",
01417                            OBJECT_TO_JSVAL(locationObj), nsnull, nsnull, 0)) {
01418         return NS_ERROR_FAILURE;
01419     }
01420 
01421     nsCAutoString nativePath;
01422     // Quick hack to unbust XPCONNECT_STANDALONE.
01423     // This leaves the jsdebugger with a non-URL pathname in the 
01424     // XPCONNECT_STANDALONE case - but at least it builds and runs otherwise.
01425     // See: http://bugzilla.mozilla.org/show_bug.cgi?id=121438
01426 #ifdef XPCONNECT_STANDALONE
01427     localFile->GetNativePath(nativePath);
01428 #else
01429     NS_GetURLSpecFromFile(localFile, nativePath);
01430 #endif
01431 
01432     // Before compiling the script, first check to see if we have it in
01433     // the fastload file.  Note: as a rule, fastload errors are not fatal
01434     // to loading the script, since we can always slow-load.
01435     nsCOMPtr<nsIFastLoadService> flSvc = do_GetFastLoadService(&rv);
01436 
01437     // Save the old state and restore it upon return
01438     FastLoadStateHolder flState(flSvc);
01439     PRBool fastLoading = PR_FALSE;
01440 
01441     if (NS_SUCCEEDED(rv)) {
01442         rv = StartFastLoad(flSvc);
01443         if (NS_SUCCEEDED(rv)) {
01444             fastLoading = PR_TRUE;
01445         }
01446     }
01447 
01448     nsCOMPtr<nsIURI> uri;
01449     rv = NS_NewURI(getter_AddRefs(uri), nativePath);
01450     NS_ENSURE_SUCCESS(rv, rv);
01451 
01452     JSScript *script = nsnull;
01453 
01454     if (fastLoading) {
01455         rv = ReadScript(flSvc, nativePath.get(), uri, cx, &script);
01456         if (NS_SUCCEEDED(rv)) {
01457             LOG(("Successfully loaded %s from fastload\n", nativePath.get()));
01458             fastLoading = PR_FALSE; // no need to write out the script
01459         } else if (rv == NS_ERROR_NOT_AVAILABLE) {
01460             // This is ok, it just means the script is not yet in the
01461             // fastload file.
01462             rv = NS_OK;
01463         } else {
01464             LOG(("Failed to deserialize %s\n", nativePath.get()));
01465 
01466             // Remove the fastload file, it may be corrupted.
01467             LOG(("Invalid fastload file detected, removing it\n"));
01468             nsCOMPtr<nsIObjectOutputStream> objectOutput;
01469             flSvc->GetOutputStream(getter_AddRefs(objectOutput));
01470             if (objectOutput) {
01471                 flSvc->SetOutputStream(nsnull);
01472                 objectOutput->Close();
01473             }
01474             nsCOMPtr<nsIObjectInputStream> objectInput;
01475             flSvc->GetInputStream(getter_AddRefs(objectInput));
01476             if (objectInput) {
01477                 flSvc->SetInputStream(nsnull);
01478                 objectInput->Close();
01479             }
01480             if (mFastLoadFile) {
01481                 mFastLoadFile->Remove(PR_FALSE);
01482             }
01483             fastLoading = PR_FALSE;
01484         }
01485     }
01486 
01487 
01488     if (!script || NS_FAILED(rv)) {
01489         // The script wasn't in the fastload cache, so compile it now.
01490         LOG(("Slow loading %s\n", nativePath.get()));
01491 
01492 #ifdef HAVE_PR_MEMMAP
01493         PRInt64 fileSize;
01494         localFile->GetFileSize(&fileSize);
01495 
01496         PRInt64 maxSize;
01497         LL_UI2L(maxSize, PR_UINT32_MAX);
01498         if (LL_CMP(fileSize, >, maxSize)) {
01499             NS_ERROR("file too large");
01500             return NS_ERROR_FAILURE;
01501         }
01502 
01503         PRFileDesc *fileHandle;
01504         rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
01505         NS_ENSURE_SUCCESS(rv, rv);
01506 
01507         // Make sure the file is closed, no matter how we return.
01508         FileAutoCloser fileCloser(fileHandle);
01509 
01510         PRFileMap *map = PR_CreateFileMap(fileHandle, fileSize,
01511                                           PR_PROT_READONLY);
01512         if (!map) {
01513             NS_ERROR("Failed to create file map");
01514             return NS_ERROR_FAILURE;
01515         }
01516 
01517         // Make sure the file map is closed, no matter how we return.
01518         FileMapAutoCloser mapCloser(map);
01519 
01520         PRUint32 fileSize32;
01521         LL_L2UI(fileSize32, fileSize);
01522 
01523         char *buf = NS_STATIC_CAST(char*, PR_MemMap(map, 0, fileSize32));
01524         if (!buf) {
01525             NS_WARNING("Failed to map file");
01526             return NS_ERROR_FAILURE;
01527         }
01528 
01529         script = JS_CompileScriptForPrincipals(cx, global,
01530                                                jsPrincipals,
01531                                                buf, fileSize32,
01532                                                nativePath.get(), 0);
01533         PR_MemUnmap(buf, fileSize32);
01534 
01535 #else  /* HAVE_PR_MEMMAP */
01536 
01542         FILE *fileHandle;
01543         rv = localFile->OpenANSIFileDesc("r", &fileHandle);
01544         NS_ENSURE_SUCCESS(rv, rv);
01545 
01546         script = JS_CompileFileHandleForPrincipals(cx, global,
01547                                                    nativePath.get(),
01548                                                    fileHandle, jsPrincipals);
01549 
01550         /* JS will close the filehandle after compilation is complete. */
01551 
01552 #endif /* HAVE_PR_MEMMAP */
01553     }
01554 
01555     if (!script) {
01556 #ifdef DEBUG_shaver_off
01557         fprintf(stderr, "mJCL: script compilation of %s FAILED\n",
01558                 nativePath.get());
01559 #endif
01560         return NS_ERROR_FAILURE;
01561     }
01562 
01563     // Ensure that we clean up the script on return.
01564     JSScriptHolder scriptHolder(cx, script);
01565 
01566     // Flag this script as a system script
01567     // FIXME: BUG 346139: We actually want to flag this exact filename, not
01568     // anything that starts with this filename... Maybe we need a way to do
01569     // that?  On the other hand, the fact that this is in our components dir
01570     // means that if someone snuck a malicious file into this dir we're screwed
01571     // anyway...  So maybe flagging as a prefix is fine.
01572     xpc->FlagSystemFilenamePrefix(nativePath.get());
01573 
01574 #ifdef DEBUG_shaver_off
01575     fprintf(stderr, "mJCL: compiled JS component %s\n",
01576             nativePath.get());
01577 #endif
01578 
01579     if (fastLoading) {
01580         // We successfully compiled the script, so cache it in fastload.
01581         rv = WriteScript(flSvc, script, component, nativePath.get(), uri, cx);
01582 
01583         // Don't treat failure to write as fatal, since we might be working
01584         // with a read-only fastload file.
01585         if (NS_SUCCEEDED(rv)) {
01586             LOG(("Successfully wrote to fastload\n"));
01587         } else {
01588             LOG(("Failed to write to fastload\n"));
01589         }
01590     }
01591 
01592     // Restore the old state of the fastload service.
01593     flState.pop();
01594 
01595     jsval retval;
01596     if (!JS_ExecuteScript(cx, global, script, &retval)) {
01597 #ifdef DEBUG_shaver_off
01598         fprintf(stderr, "mJCL: failed to execute %s\n", nativePath.get());
01599 #endif
01600         return NS_ERROR_FAILURE;
01601     }
01602 
01603     *aGlobal = global;
01604 
01605     /* Freed when we remove from the table. */
01606     char *location = nsCRT::strdup(aLocation);
01607     NS_ENSURE_TRUE(location, NS_ERROR_OUT_OF_MEMORY);
01608 
01609     he = PL_HashTableRawAdd(mGlobals, hep, hash, location, global);
01610     JS_AddNamedRoot(cx, &he->value, location);
01611     return NS_OK;
01612 }
01613 
01614 NS_IMETHODIMP
01615 mozJSComponentLoader::OnRegister(const nsIID &aCID, const char *aType,
01616                                  const char *aClassName, const char *aContractID,
01617                                  const char *aLocation,
01618                                  PRBool aReplace, PRBool aPersist)
01619 
01620 {
01621 #ifdef DEBUG_shaver_off
01622     fprintf(stderr, "mJCL: registered %s/%s in %s\n", aClassName, aContractID,
01623             aLocation);
01624 #endif
01625     return NS_OK;
01626 }    
01627 
01628 NS_IMETHODIMP
01629 mozJSComponentLoader::UnloadAll(PRInt32 aWhen)
01630 {
01631     if (mInitialized) {
01632         mInitialized = PR_FALSE;
01633 
01634         // stabilize the component manager, etc.
01635         nsCOMPtr<nsIComponentManager> kungFuDeathGrip = mCompMgr;
01636 
01637         PL_HashTableEnumerateEntries(mModules, 
01638                                      UnloadAndReleaseModules,
01639                                      mCompMgr);
01640         PL_HashTableDestroy(mModules);
01641         mModules = nsnull;
01642 
01643         PL_HashTableEnumerateEntries(mGlobals, UnrootGlobals, mContext);
01644         PL_HashTableDestroy(mGlobals);
01645         mGlobals = nsnull;
01646 
01647         // Destroying our context will force a GC.
01648         JS_DestroyContext(mContext);
01649         mContext = nsnull;
01650 
01651         mRuntimeService = nsnull;
01652     }
01653 
01654 #ifdef DEBUG_shaver_off
01655     fprintf(stderr, "mJCL: UnloadAll(%d)\n", aWhen);
01656 #endif
01657 
01658     return NS_OK;
01659 }
01660 
01661 NS_IMETHODIMP
01662 mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
01663                               const PRUnichar *data)
01664 {
01665     NS_ASSERTION(!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID),
01666                  "Unexpected observer topic");
01667 
01668     if (mFastLoadTimer) {
01669         mFastLoadTimer->Cancel();
01670     }
01671 
01672     CloseFastLoad();
01673     return NS_OK;
01674 }
01675 
01676 //----------------------------------------------------------------------
01677 
01678 JSCLContextHelper::JSCLContextHelper(JSContext *cx)
01679     : mContext(cx), mContextThread(0)
01680 {
01681     mContextThread = JS_GetContextThread(mContext);
01682     if (mContextThread) {
01683         JS_BeginRequest(mContext);
01684     } 
01685 }
01686 
01687 JSCLContextHelper::~JSCLContextHelper()
01688 {
01689     JS_ClearNewbornRoots(mContext);
01690     if (mContextThread)
01691         JS_EndRequest(mContext);
01692 }        
01693 
01694 //----------------------------------------------------------------------
01695 
01696 /* XXX this should all be data-driven, via NS_IMPL_GETMODULE_WITH_CATEGORIES */