Bug 552121 - Load js modules and components from the omnijar [7/7], r=bsmedberg
authorMichael Wu <mwu@mozilla.com>
Tue, 15 Jun 2010 12:38:46 -0700
changeset 43649 c666507bf280d537606d3d5a408cfe23b94b832e
parent 43648 2a8e821c0e802a1be9382d2d64db39c62214c7f3
child 43650 86a1dfc93d39d2be25d7ca3064c085b5876a522a
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs552121
milestone1.9.3a6pre
Bug 552121 - Load js modules and components from the omnijar [7/7], r=bsmedberg
js/src/xpconnect/loader/mozJSComponentLoader.cpp
js/src/xpconnect/loader/mozJSComponentLoader.h
xpcom/components/nsComponentManager.cpp
xpcom/components/nsComponentManager.h
xpcom/components/nsIModuleLoader.idl
xpcom/components/nsNativeComponentLoader.cpp
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -67,26 +67,30 @@
 #include "nsMemory.h"
 #include "nsIObserverService.h"
 #include "nsIXPCScriptable.h"
 #include "nsString.h"
 #ifndef XPCONNECT_STANDALONE
 #include "nsIScriptSecurityManager.h"
 #include "nsIURI.h"
 #include "nsIFileURL.h"
+#include "nsIJARURI.h"
 #include "nsNetUtil.h"
 #endif
 #include "jsxdrapi.h"
 #include "jsprf.h"
 #include "nsIFastLoadFileControl.h"
 // For reporting errors with the console service
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "prmem.h"
 #include "plbase64.h"
+#if defined(XP_WIN)
+#include "nsILocalFileWin.h"
+#endif
 
 #if defined(MOZ_SHARK) || defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE) || defined(MOZ_TRACEVIS)
 #include "jsdbgapi.h"
 #endif
 
 #include "mozilla/FunctionTimer.h"
 
 static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1";
@@ -655,59 +659,154 @@ mozJSComponentLoader::ReallyInit()
 #ifdef DEBUG_shaver_off
     fprintf(stderr, "mJCL: ReallyInit success!\n");
 #endif
     mInitialized = PR_TRUE;
 
     return NS_OK;
 }
 
+nsresult
+mozJSComponentLoader::FileKey(nsILocalFile* aFile, nsAString &aResult)
+{
+    nsresult rv = NS_OK;
+    nsAutoString canonicalPath;
+
+#if defined(XP_WIN)
+    nsCOMPtr<nsILocalFileWin> winFile = do_QueryInterface(aFile, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    winFile->GetCanonicalPath(canonicalPath);
+#else
+    aFile->GetPath(canonicalPath);
+#endif
+
+    aResult = NS_LITERAL_STRING("f");
+    aResult += canonicalPath;
+
+    return rv;
+}
+
+nsresult
+mozJSComponentLoader::JarKey(nsILocalFile* aFile,
+                             const nsACString &aComponentPath,
+                             nsAString &aResult)
+{
+    nsresult rv = NS_OK;
+    nsAutoString canonicalPath;
+
+#if defined(XP_WIN)
+    nsCOMPtr<nsILocalFileWin> winFile = do_QueryInterface(aFile, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    winFile->GetCanonicalPath(canonicalPath);
+#else
+    aFile->GetPath(canonicalPath);
+#endif
+
+    aResult = NS_LITERAL_STRING("j");
+    aResult += canonicalPath;
+    AppendUTF8toUTF16(aComponentPath, aResult);
+
+    return rv;
+}
+
 NS_IMETHODIMP
 mozJSComponentLoader::LoadModule(nsILocalFile* aComponentFile,
                                  nsIModule* *aResult)
 {
+    nsCOMPtr<nsIURI> uri;
+    nsCAutoString spec;
+    NS_GetURLSpecFromActualFile(aComponentFile, spec);
+
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString hashstring;
+    rv = FileKey(aComponentFile, hashstring);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return LoadModuleImpl(aComponentFile,
+                          hashstring,
+                          uri, aResult);
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::LoadModuleFromJAR(nsILocalFile *aJarFile,
+                                        const nsACString &aComponentPath,
+                                        nsIModule* *aResult)
+{
+#if !defined(XPCONNECT_STANDALONE)
+    nsresult rv;
+
+    nsCAutoString fileSpec;
+    NS_GetURLSpecFromActualFile(aJarFile, fileSpec);
+
+    nsCAutoString jarSpec("jar:");
+    jarSpec += fileSpec;
+    jarSpec += "!/";
+    jarSpec += aComponentPath;
+
+    nsCOMPtr<nsIURI> uri;
+    rv = NS_NewURI(getter_AddRefs(uri), jarSpec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString hashstring;
+    rv = JarKey(aJarFile, aComponentPath, hashstring);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return LoadModuleImpl(aJarFile,
+                          hashstring,
+                          uri, aResult);
+#else
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult
+mozJSComponentLoader::LoadModuleImpl(nsILocalFile* aSourceFile,
+                                     nsAString &aKey,
+                                     nsIURI* aComponentURI,
+                                     nsIModule* *aResult)
+{
     nsresult rv;
 
 #ifdef NS_FUNCTION_TIMER
     nsAutoString path__(NS_LITERAL_STRING("N/A"));
     aComponentFile->GetPath(path__);
     NS_TIME_FUNCTION_FMT("%s (line %d) (file: %s)", MOZ_FUNCTION_NAME,
                          __LINE__, nsPromiseFlatCString(NS_LossyConvertUTF16toASCII(path__)).BeginReading());
 #endif
 
-    nsCAutoString leafName;
-    aComponentFile->GetNativeLeafName(leafName);
-    if (!StringTail(leafName, 3).LowerCaseEqualsLiteral(".js"))
-        return NS_ERROR_INVALID_ARG;
-
     if (!mInitialized) {
         rv = ReallyInit();
         if (NS_FAILED(rv))
             return rv;
     }
 
-    nsCOMPtr<nsIHashable> lfhash(do_QueryInterface(aComponentFile));
-    if (!lfhash) {
-        NS_ERROR("nsLocalFile not implementing nsIHashable");
-        return NS_NOINTERFACE;
-    }
+    nsCAutoString uriStr;
+    rv = aComponentURI->GetSpec(uriStr);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!StringTail(uriStr, 3).LowerCaseEqualsLiteral(".js"))
+        return NS_ERROR_INVALID_ARG;
 
     ModuleEntry* mod;
-    if (mModules.Get(lfhash, &mod)) {
+    if (mModules.Get(aKey, &mod)) {
         NS_ASSERTION(mod->module, "Bad hashtable data!");
         NS_ADDREF(*aResult = mod->module);
         return NS_OK;
     }
 
     nsAutoPtr<ModuleEntry> entry(new ModuleEntry);
     if (!entry)
         return NS_ERROR_OUT_OF_MEMORY;
 
-    rv = GlobalForLocation(aComponentFile, &entry->global, &entry->location,
-                           nsnull);
+    rv = GlobalForLocation(aSourceFile, aComponentURI, &entry->global,
+                           &entry->location, nsnull);
     if (NS_FAILED(rv)) {
 #ifdef DEBUG_shaver
         fprintf(stderr, "GlobalForLocation failed!\n");
 #endif
         return rv;
     }
 
     nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
@@ -741,17 +840,17 @@ mozJSComponentLoader::LoadModule(nsILoca
 #ifdef DEBUG_shaver
         fprintf(stderr, "GetJSObject of ComponentManager failed\n");
 #endif
         return rv;
     }
 
     JSObject* file_jsobj;
     nsCOMPtr<nsIXPConnectJSObjectHolder> file_holder;
-    rv = xpc->WrapNative(cx, entry->global, aComponentFile, 
+    rv = xpc->WrapNative(cx, entry->global, aSourceFile,
                          NS_GET_IID(nsIFile),
                          getter_AddRefs(file_holder));
 
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     rv = file_holder->GetJSObject(&file_jsobj);
@@ -764,21 +863,18 @@ mozJSComponentLoader::LoadModule(nsILoca
     jsval argv[2], retval, NSGetModule_val;
 
     if (!JS_GetProperty(cx, entry->global, "NSGetModule", &NSGetModule_val) ||
         JSVAL_IS_VOID(NSGetModule_val)) {
         return NS_ERROR_FAILURE;
     }
 
     if (JS_TypeOfValue(cx, NSGetModule_val) != JSTYPE_FUNCTION) {
-        nsCAutoString path;
-        aComponentFile->GetNativePath(path);
-
         JS_ReportError(cx, "%s has NSGetModule property that is not a function",
-                       path.get());
+                       uriStr.get());
         return NS_ERROR_FAILURE;
     }
     
     argv[0] = OBJECT_TO_JSVAL(cm_jsobj);
     argv[1] = OBJECT_TO_JSVAL(file_jsobj);
     if (!JS_CallFunctionValue(cx, entry->global, NSGetModule_val,
                               2, argv, &retval)) {
         return NS_ERROR_FAILURE;
@@ -803,17 +899,17 @@ mozJSComponentLoader::LoadModule(nsILoca
         /* XXX report error properly */
 #ifdef DEBUG
         fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
 #endif
         return rv;
     }
 
     // Cache this module for later
-    if (!mModules.Put(lfhash, entry))
+    if (!mModules.Put(aKey, entry))
         return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aResult = entry->module);
 
     // The hash owns the ModuleEntry now, forget about it
     entry.forget();
 
     return NS_OK;
@@ -1098,17 +1194,18 @@ mozJSComponentLoader::WriteScript(nsIFas
 
     rv = WriteScriptToStream(cx, script, mFastLoadOutput);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return flSvc->EndMuxedDocument(uri);
 }
 
 nsresult
-mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponent,
+mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
+                                        nsIURI *aURI,
                                         JSObject **aGlobal,
                                         char **aLocation,
                                         jsval *exception)
 {
     nsresult rv;
 
     JSPrincipals* jsPrincipals = nsnull;
     JSCLContextHelper cx(this);
@@ -1145,40 +1242,54 @@ mozJSComponentLoader::GlobalForLocation(
     JSObject *global;
     rv = holder->GetJSObject(&global);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!JS_DefineFunctions(cx, global, gGlobalFun)) {
         return NS_ERROR_FAILURE;
     }
 
-    nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
-    rv = xpc->WrapNative(cx, global, aComponent,
-                         NS_GET_IID(nsILocalFile),
-                         getter_AddRefs(locationHolder));
-    NS_ENSURE_SUCCESS(rv, rv);
+    bool realFile = false;
+    // need to be extra careful checking for URIs pointing to files
+    // EnsureFile may not always get called, especially on resource URIs
+    // so we need to call GetFile to make sure this is a valid file
+    nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
+    nsCOMPtr<nsIFile> testFile;
+    if (NS_SUCCEEDED(rv)) {
+        fileURL->GetFile(getter_AddRefs(testFile));
+    }
+    
+    if (testFile) {
+        realFile = true;
 
-    JSObject *locationObj;
-    rv = locationHolder->GetJSObject(&locationObj);
-    NS_ENSURE_SUCCESS(rv, rv);
+        nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
+        rv = xpc->WrapNative(cx, global, aComponentFile,
+                             NS_GET_IID(nsILocalFile),
+                             getter_AddRefs(locationHolder));
+        NS_ENSURE_SUCCESS(rv, rv);
 
-    if (!JS_DefineProperty(cx, global, "__LOCATION__",
-                           OBJECT_TO_JSVAL(locationObj), nsnull, nsnull, 0)) {
-        return NS_ERROR_FAILURE;
+        JSObject *locationObj;
+        rv = locationHolder->GetJSObject(&locationObj);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!JS_DefineProperty(cx, global, "__LOCATION__",
+                               OBJECT_TO_JSVAL(locationObj), nsnull, nsnull, 0))
+            return NS_ERROR_FAILURE;
     }
 
     nsCAutoString nativePath;
     // Quick hack to unbust XPCONNECT_STANDALONE.
     // This leaves the jsdebugger with a non-URL pathname in the 
     // XPCONNECT_STANDALONE case - but at least it builds and runs otherwise.
     // See: http://bugzilla.mozilla.org/show_bug.cgi?id=121438
 #ifdef XPCONNECT_STANDALONE
     localFile->GetNativePath(nativePath);
 #else
-    NS_GetURLSpecFromActualFile(aComponent, nativePath);
+    rv = aURI->GetSpec(nativePath);
+    NS_ENSURE_SUCCESS(rv, rv);
 #endif
 
     // Before compiling the script, first check to see if we have it in
     // the fastload file.  Note: as a rule, fastload errors are not fatal
     // to loading the script, since we can always slow-load.
     nsCOMPtr<nsIFastLoadService> flSvc = do_GetFastLoadService(&rv);
 
     // Save the old state and restore it upon return
@@ -1187,24 +1298,20 @@ mozJSComponentLoader::GlobalForLocation(
 
     if (NS_SUCCEEDED(rv)) {
         rv = StartFastLoad(flSvc);
         if (NS_SUCCEEDED(rv)) {
             fastLoading = PR_TRUE;
         }
     }
 
-    nsCOMPtr<nsIURI> uri;
-    rv = NS_NewURI(getter_AddRefs(uri), nativePath);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     JSScript *script = nsnull;
 
     if (fastLoading) {
-        rv = ReadScript(flSvc, nativePath.get(), uri, cx, &script);
+        rv = ReadScript(flSvc, nativePath.get(), aURI, cx, &script);
         if (NS_SUCCEEDED(rv)) {
             LOG(("Successfully loaded %s from fastload\n", nativePath.get()));
             fastLoading = PR_FALSE; // no need to write out the script
         } else if (rv == NS_ERROR_NOT_AVAILABLE) {
             // This is ok, it just means the script is not yet in the
             // fastload file.
             rv = NS_OK;
         } else {
@@ -1240,90 +1347,126 @@ mozJSComponentLoader::GlobalForLocation(
         // any exceptions out to our caller. Ensure that the engine doesn't
         // eagerly report the exception.
         uint32 oldopts = 0;
         if (exception) {
             oldopts = JS_GetOptions(cx);
             JS_SetOptions(cx, oldopts | JSOPTION_DONT_REPORT_UNCAUGHT);
         }
 
+        if (realFile) {
 #ifdef HAVE_PR_MEMMAP
-        PRInt64 fileSize;
-        rv = aComponent->GetFileSize(&fileSize);
-        if (NS_FAILED(rv)) {
-            JS_SetOptions(cx, oldopts);
-            return rv;
-        }
+            PRInt64 fileSize;
+            rv = aComponentFile->GetFileSize(&fileSize);
+            if (NS_FAILED(rv)) {
+                JS_SetOptions(cx, oldopts);
+                return rv;
+            }
 
-        PRInt64 maxSize;
-        LL_UI2L(maxSize, PR_UINT32_MAX);
-        if (LL_CMP(fileSize, >, maxSize)) {
-            NS_ERROR("file too large");
-            JS_SetOptions(cx, oldopts);
-            return NS_ERROR_FAILURE;
-        }
+            PRInt64 maxSize;
+            LL_UI2L(maxSize, PR_UINT32_MAX);
+            if (LL_CMP(fileSize, >, maxSize)) {
+                NS_ERROR("file too large");
+                JS_SetOptions(cx, oldopts);
+                return NS_ERROR_FAILURE;
+            }
 
-        PRFileDesc *fileHandle;
-        rv = aComponent->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
-        if (NS_FAILED(rv)) {
-            JS_SetOptions(cx, oldopts);
-            return NS_ERROR_FILE_NOT_FOUND;
-        }
+            PRFileDesc *fileHandle;
+            rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
+            if (NS_FAILED(rv)) {
+                JS_SetOptions(cx, oldopts);
+                return NS_ERROR_FILE_NOT_FOUND;
+            }
 
-        // Make sure the file is closed, no matter how we return.
-        FileAutoCloser fileCloser(fileHandle);
+            // Make sure the file is closed, no matter how we return.
+            FileAutoCloser fileCloser(fileHandle);
 
-        PRFileMap *map = PR_CreateFileMap(fileHandle, fileSize,
-                                          PR_PROT_READONLY);
-        if (!map) {
-            NS_ERROR("Failed to create file map");
-            JS_SetOptions(cx, oldopts);
-            return NS_ERROR_FAILURE;
-        }
+            PRFileMap *map = PR_CreateFileMap(fileHandle, fileSize,
+                                              PR_PROT_READONLY);
+            if (!map) {
+                NS_ERROR("Failed to create file map");
+                JS_SetOptions(cx, oldopts);
+                return NS_ERROR_FAILURE;
+            }
 
-        // Make sure the file map is closed, no matter how we return.
-        FileMapAutoCloser mapCloser(map);
+            // Make sure the file map is closed, no matter how we return.
+            FileMapAutoCloser mapCloser(map);
 
-        PRUint32 fileSize32;
-        LL_L2UI(fileSize32, fileSize);
+            PRUint32 fileSize32;
+            LL_L2UI(fileSize32, fileSize);
 
-        char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
-        if (!buf) {
-            NS_WARNING("Failed to map file");
-            JS_SetOptions(cx, oldopts);
-            return NS_ERROR_FAILURE;
-        }
+            char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
+            if (!buf) {
+                NS_WARNING("Failed to map file");
+                JS_SetOptions(cx, oldopts);
+                return NS_ERROR_FAILURE;
+            }
 
-        script = JS_CompileScriptForPrincipals(cx, global,
-                                               jsPrincipals,
-                                               buf, fileSize32,
-                                               nativePath.get(), 1);
-        PR_MemUnmap(buf, fileSize32);
+            script = JS_CompileScriptForPrincipals(cx, global,
+                                                   jsPrincipals,
+                                                   buf, fileSize32,
+                                                   nativePath.get(), 1);
+            PR_MemUnmap(buf, fileSize32);
 
 #else  /* HAVE_PR_MEMMAP */
 
-        /**
-         * No memmap implementation, so fall back to using
-         * JS_CompileFileHandleForPrincipals().
-         */
+            /**
+             * No memmap implementation, so fall back to using
+             * JS_CompileFileHandleForPrincipals().
+             */
+
+            FILE *fileHandle;
+            rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle);
+            if (NS_FAILED(rv)) {
+                JS_SetOptions(cx, oldopts);
+                return NS_ERROR_FILE_NOT_FOUND;
+            }
+
+            script = JS_CompileFileHandleForPrincipals(cx, global,
+                                                       nativePath.get(),
+                                                       fileHandle, jsPrincipals);
+
+            /* JS will close the filehandle after compilation is complete. */
+#endif /* HAVE_PR_MEMMAP */
+        } else {
+            nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            nsCOMPtr<nsIChannel> scriptChannel;
+            rv = ioService->NewChannelFromURI(aURI, getter_AddRefs(scriptChannel));
+            NS_ENSURE_SUCCESS(rv, rv);
 
-        FILE *fileHandle;
-        rv = aComponent->OpenANSIFileDesc("r", &fileHandle);
-        if (NS_FAILED(rv)) {
-            JS_SetOptions(cx, oldopts);
-            return NS_ERROR_FILE_NOT_FOUND;
-        }
+            nsCOMPtr<nsIInputStream> scriptStream;
+            rv = scriptChannel->Open(getter_AddRefs(scriptStream));
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            PRUint32 len, bytesRead;
+
+            rv = scriptStream->Available(&len);
+            NS_ENSURE_SUCCESS(rv, rv);
+            if (!len)
+                return NS_ERROR_FAILURE;
 
-        script = JS_CompileFileHandleForPrincipals(cx, global,
-                                                   nativePath.get(),
-                                                   fileHandle, jsPrincipals);
+            /* malloc an internal buf the size of the file */
+            nsAutoArrayPtr<char> buf(new char[len + 1]);
+            if (!buf)
+                return NS_ERROR_OUT_OF_MEMORY;
 
-        /* JS will close the filehandle after compilation is complete. */
-#endif /* HAVE_PR_MEMMAP */
+            /* read the file in one swoop */
+            rv = scriptStream->Read(buf, len, &bytesRead);
+            if (bytesRead != len)
+                return NS_BASE_STREAM_OSERROR;
 
+            buf[len] = '\0';
+
+            script = JS_CompileScriptForPrincipals(cx, global,
+                                                   jsPrincipals,
+                                                   buf, bytesRead,
+                                                   nativePath.get(), 1);
+        }
         // Propagate the exception, if one exists. Also, don't leave the stale
         // exception on this context.
         // NB: The caller must stick exception into a rooted slot (probably on
         // its context) as soon as possible to avoid GC hazards.
         if (exception) {
             JS_SetOptions(cx, oldopts);
             if (!script) {
                 JS_GetPendingException(cx, exception);
@@ -1353,17 +1496,17 @@ mozJSComponentLoader::GlobalForLocation(
 
 #ifdef DEBUG_shaver_off
     fprintf(stderr, "mJCL: compiled JS component %s\n",
             nativePath.get());
 #endif
 
     if (fastLoading) {
         // We successfully compiled the script, so cache it in fastload.
-        rv = WriteScript(flSvc, script, aComponent, nativePath.get(), uri, cx);
+        rv = WriteScript(flSvc, script, aComponentFile, nativePath.get(), aURI, cx);
 
         // Don't treat failure to write as fatal, since we might be working
         // with a read-only fastload file.
         if (NS_SUCCEEDED(rv)) {
             LOG(("Successfully wrote to fastload\n"));
         } else {
             LOG(("Failed to write to fastload\n"));
         }
@@ -1381,19 +1524,17 @@ mozJSComponentLoader::GlobalForLocation(
 #ifdef DEBUG_shaver_off
         fprintf(stderr, "mJCL: failed to execute %s\n", nativePath.get());
 #endif
         *aGlobal = nsnull;
         return NS_ERROR_FAILURE;
     }
 
     /* Freed when we remove from the table. */
-    nsCAutoString path;
-    aComponent->GetNativePath(path);
-    *aLocation = ToNewCString(path);
+    *aLocation = ToNewCString(nativePath);
     if (!*aLocation) {
         *aGlobal = nsnull;
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     JS_AddNamedRoot(cx, aGlobal, *aLocation);
     return NS_OK;
 }
@@ -1525,45 +1666,75 @@ mozJSComponentLoader::ImportInto(const n
     }
     
     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Get the URI.
     nsCOMPtr<nsIURI> resURI;
     rv = ioService->NewURI(aLocation, nsnull, nsnull, getter_AddRefs(resURI));
-    nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(resURI, &rv);
-    // If we don't have a file URL, then the location passed in is invalid.
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    // Get the file belonging to it.
-    nsCOMPtr<nsIFile> file;
-    rv = fileURL->GetFile(getter_AddRefs(file));
+    // figure out the resolved URI
+    nsCOMPtr<nsIChannel> scriptChannel;
+    rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
     NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsILocalFile> componentFile = do_QueryInterface(file, &rv);
+
+    nsCOMPtr<nsIURI> resolvedURI;
+    rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIHashable> lfhash(do_QueryInterface(componentFile));
-    if (!lfhash) {
-        NS_ERROR("nsLocalFile not implementing nsIHashable");
-        return NS_NOINTERFACE;
+    // get the JAR if there is one
+    nsCOMPtr<nsIJARURI> jarURI;
+    jarURI = do_QueryInterface(resolvedURI, &rv);
+    nsCOMPtr<nsIFileURL> baseFileURL;
+    nsCAutoString jarEntry;
+    if (NS_SUCCEEDED(rv)) {
+        nsCOMPtr<nsIURI> baseURI;
+        rv = jarURI->GetJARFile(getter_AddRefs(baseURI));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        baseFileURL = do_QueryInterface(baseURI, &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        jarURI->GetJAREntry(jarEntry);
+        NS_ENSURE_SUCCESS(rv, rv);
+    } else {
+        baseFileURL = do_QueryInterface(resolvedURI, &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
     }
 
+    nsCOMPtr<nsIFile> sourceFile;
+    rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsILocalFile> sourceLocalFile;
+    sourceLocalFile = do_QueryInterface(sourceFile, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString key;
+    if (jarEntry.IsEmpty()) {
+        rv = FileKey(sourceLocalFile, key);
+    } else {
+        rv = JarKey(sourceLocalFile, jarEntry, key);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
     ModuleEntry* mod;
     nsAutoPtr<ModuleEntry> newEntry;
-    if (!mImports.Get(lfhash, &mod) && !mInProgressImports.Get(lfhash, &mod)) {
+    if (!mImports.Get(key, &mod) && !mInProgressImports.Get(key, &mod)) {
         newEntry = new ModuleEntry;
-        if (!newEntry || !mInProgressImports.Put(lfhash, newEntry))
+        if (!newEntry || !mInProgressImports.Put(key, newEntry))
             return NS_ERROR_OUT_OF_MEMORY;
 
         jsval exception = JSVAL_VOID;
-        rv = GlobalForLocation(componentFile, &newEntry->global,
+        rv = GlobalForLocation(sourceLocalFile, resURI, &newEntry->global,
                                &newEntry->location, &exception);
 
-        mInProgressImports.Remove(lfhash);
+        mInProgressImports.Remove(key);
 
         if (NS_FAILED(rv)) {
             *_retval = nsnull;
 
             if (!JSVAL_IS_VOID(exception)) {
                 // An exception was thrown during compilation. Propagate it
                 // out to our caller so they can report it.
                 JSContext *callercx;
@@ -1647,17 +1818,17 @@ mozJSComponentLoader::ImportInto(const n
                                       PromiseFlatCString(aLocation).get()));
             }
 #endif
         }
     }
 
     // Cache this module for later
     if (newEntry) {
-        if (!mImports.Put(lfhash, newEntry))
+        if (!mImports.Put(key, newEntry))
             return NS_ERROR_OUT_OF_MEMORY;
         newEntry.forget();
     }
     
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/js/src/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.h
@@ -104,17 +104,28 @@ class mozJSComponentLoader : public nsIM
     virtual ~mozJSComponentLoader();
 
  protected:
     static mozJSComponentLoader* sSelf;
 
     nsresult ReallyInit();
     void UnloadModules();
 
-    nsresult GlobalForLocation(nsILocalFile *aComponent,
+    nsresult FileKey(nsILocalFile* aFile, nsAString &aResult);
+    nsresult JarKey(nsILocalFile* aFile,
+                    const nsACString& aComponentPath,
+                    nsAString &aResult);
+
+    nsresult LoadModuleImpl(nsILocalFile* aSourceFile,
+                            nsAString &aKey,
+                            nsIURI* aComponentURI,
+                            nsIModule* *aResult);
+
+    nsresult GlobalForLocation(nsILocalFile* aComponentFile,
+                               nsIURI *aComponent,
                                JSObject **aGlobal,
                                char **location,
                                jsval *exception);
 
     nsresult StartFastLoad(nsIFastLoadService *flSvc);
     nsresult ReadScript(nsIFastLoadService *flSvc, const char *nativePath,
                         nsIURI *uri, JSContext *cx, JSScript **script);
     nsresult WriteScript(nsIFastLoadService *flSvc, JSScript *script,
@@ -160,14 +171,14 @@ class mozJSComponentLoader : public nsIM
 
         nsCOMPtr<nsIModule>  module;
         JSObject            *global;
         char                *location;
     };
 
     friend class ModuleEntry;
 
-    nsClassHashtable<nsHashableHashKey, ModuleEntry> mModules;
-    nsClassHashtable<nsHashableHashKey, ModuleEntry> mImports;
-    nsDataHashtable<nsHashableHashKey, ModuleEntry*> mInProgressImports;
+    nsClassHashtable<nsStringHashKey, ModuleEntry> mModules;
+    nsClassHashtable<nsStringHashKey, ModuleEntry> mImports;
+    nsDataHashtable<nsStringHashKey, ModuleEntry*> mInProgressImports;
 
     PRBool mInitialized;
 };
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -95,16 +95,22 @@
 #include NEW_H     // for placement new
 
 
 #ifdef XP_BEOS
 #include <FindDirectory.h>
 #include <Path.h>
 #endif
 
+#ifdef MOZ_OMNIJAR
+#include "nsIZipReader.h"
+#include "mozilla/Omnijar.h"
+static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
+#endif
+
 #include "prlog.h"
 
 NS_COM PRLogModuleInfo* nsComponentManagerLog = nsnull;
 
 #if 0 || defined (DEBUG_timeless)
  #define SHOW_DENIED_ON_SHUTDOWN
  #define SHOW_CI_ON_EXISTING_SERVICE
 #endif
@@ -127,16 +133,17 @@ const char classNameValueName[]="ClassNa
 const char componentCountValueName[]="ComponentsCount";
 const char componentTypeValueName[]="ComponentType";
 const char contractIDValueName[]="ContractID";
 const char fileSizeValueName[]="FileSize";
 const char inprocServerValueName[]="InprocServer";
 const char lastModValueName[]="LastModTimeStamp";
 const char nativeComponentType[]="application/x-mozilla-native";
 const char staticComponentType[]="application/x-mozilla-static";
+const char jarComponentType[]="application/x-mozilla-jarjs";
 const char versionValueName[]="VersionString";
 
 const static char XPCOM_ABSCOMPONENT_PREFIX[] = "abs:";
 const static char XPCOM_RELCOMPONENT_PREFIX[] = "rel:";
 const static char XPCOM_GRECOMPONENT_PREFIX[] = "gre:";
 
 static const char gIDFormat[] =
   "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}";
@@ -174,16 +181,18 @@ static void GetIDString(const nsID& aCID
 #define COMPMGR_TIME_FUNCTION_CONTRACTID(cid)                                  \
   NS_TIME_FUNCTION_MIN_FMT(5, "%s (line %d) (contractid: %s)", MOZ_FUNCTION_NAME, \
                            __LINE__, (cid))
 #else
 #define COMPMGR_TIME_FUNCTION_CID(cid) do {} while (0)
 #define COMPMGR_TIME_FUNCTION_CONTRACTID(cid) do {} while (0)
 #endif
 
+#define kOMNIJAR_PREFIX  NS_LITERAL_CSTRING("resource:///")
+
 nsresult
 nsGetServiceFromCategory::operator()(const nsIID& aIID, void** aInstancePtr) const
 {
     nsresult rv;
     nsXPIDLCString value;
     nsCOMPtr<nsICategoryManager> catman;
     nsComponentManagerImpl *compMgr = nsComponentManagerImpl::gComponentManager;
     if (!compMgr) {
@@ -1164,16 +1173,20 @@ ClassIDWriter(PLDHashTable *table,
     case NS_LOADER_TYPE_STATIC:
         loaderName = staticComponentType;
         break;
 
     case NS_LOADER_TYPE_NATIVE:
         loaderName = nativeComponentType;
         break;
 
+    case NS_LOADER_TYPE_JAR:
+        loaderName = jarComponentType;
+        break;
+
     default:
         loaderName = loaderData->ElementAt(factoryEntry->mLoaderType).type.get();
     }
 
     const char* location = factoryEntry->mLocationKey;
 
     // cid,contract_id,type,class_name,inproc_server
     PR_fprintf(fd,
@@ -2747,16 +2760,19 @@ nsComponentManagerImpl::LoaderForType(Lo
                  "Static component loader is special");
 
     if (aType == NS_LOADER_TYPE_INVALID)
         return nsnull;
 
     if (aType == NS_LOADER_TYPE_NATIVE)
         return &mNativeModuleLoader;
 
+    if (aType == NS_LOADER_TYPE_JAR)
+        return nsnull;
+
     NS_ASSERTION(aType >= 0 && PRUint32(aType) < mLoaderData.Length(),
                  "LoaderType out of range");
 
     if (!mLoaderData[aType].loader) {
         nsCOMPtr<nsIModuleLoader> loader;
         loader = do_GetServiceFromCategory("module-loader",
                                            mLoaderData[aType].type.get());
         if (!loader)
@@ -2805,16 +2821,19 @@ nsComponentManagerImpl::GetLoaderType(co
     }
 
     if (!strcmp(typeStr, staticComponentType))
         return NS_LOADER_TYPE_STATIC;
 
     if (!strcmp(typeStr, nativeComponentType))
         return NS_LOADER_TYPE_NATIVE;
 
+    if (!strcmp(typeStr, jarComponentType))
+        return NS_LOADER_TYPE_JAR;
+
     const nsDependentCString type(typeStr);
 
     for (unsigned int i = 0; i < mLoaderData.Length(); ++i) {
         if (mLoaderData[i].type == type)
             return i;
     }
 
     return NS_LOADER_TYPE_INVALID;
@@ -3388,16 +3407,116 @@ nsComponentManagerImpl::EnumerateContrac
         return rv;
 
     *aEnumerator = static_cast<nsIEnumerator*>(aEnum);
     return NS_OK;
 }
 
 // nsIComponentRegistrar
 
+#ifdef MOZ_OMNIJAR
+void
+nsComponentManagerImpl::AutoRegisterJar()
+{
+    nsresult rv;
+    nsCOMPtr<nsIZipReader> jarReader = do_CreateInstance(kZipReaderCID, &rv);
+    if (NS_FAILED(rv)) {
+        NS_ERROR("could not get jar reader");
+        return;
+    }
+
+    nsIModuleLoader *jsLoader = LoaderForType(GetLoaderType("text/javascript"));
+    if (!jsLoader) {
+        NS_ERROR("could not get JS loader");
+        return;
+    }
+
+    nsCOMPtr<nsILocalFile> omniJar(mozilla::OmnijarPath());
+    if (!omniJar) {
+        return;
+    }
+
+    rv = jarReader->Open(omniJar);
+    if (NS_FAILED(rv)) {
+        NS_ERROR("Failed to open omniJar");
+        return;
+    }
+
+    nsCOMPtr<nsIUTF8StringEnumerator> compEnum;
+    rv = jarReader->FindEntries("components/*.js$",
+                                getter_AddRefs(compEnum));
+    if (NS_FAILED(rv)) {
+        NS_ERROR("Failed to get js enumerator for omniJar");
+        return;
+    }
+
+    nsTArray<DeferredModule> deferred;
+
+    nsCAutoString compName;
+    PRBool more;
+    while (NS_SUCCEEDED((rv = compEnum->HasMore(&more))) && more) {
+        rv = compEnum->GetNext(compName);
+        if (NS_FAILED(rv)) {
+            NS_ERROR("Failed to get name from js enumerator");
+            continue;
+        }
+
+        nsCAutoString entryspec(kOMNIJAR_PREFIX);
+        entryspec += compName;
+
+        nsCOMPtr<nsIModule> module;
+        rv = jsLoader->LoadModuleFromJAR(omniJar, compName, getter_AddRefs(module));
+        if (NS_FAILED(rv)) {
+            NS_ERROR("Failed to load JS component from JAR");
+            continue;
+        }
+
+        nsCOMPtr<nsIFile> fakeFile;
+        rv = omniJar->Clone(getter_AddRefs(fakeFile));
+        if (NS_FAILED(rv)) {
+            NS_ERROR("Failed to clone fake local file for JS component");
+            continue;
+        }
+
+        nsCOMPtr<nsILocalFile> fakeNativeFile = do_QueryInterface(fakeFile);
+
+        PRInt32 startIdx = compName.RFindChar('/') + 1;
+        rv = fakeNativeFile->AppendNative(Substring(compName, startIdx));
+        if (NS_FAILED(rv)) {
+            NS_ERROR("Failed to create fake local file for JS component");
+            continue;
+        }
+
+        rv = module->RegisterSelf(this, fakeNativeFile,
+                                  entryspec.get(),
+                                  jarComponentType);
+        if (NS_ERROR_FACTORY_REGISTER_AGAIN == rv) {
+            DeferredModule *d = deferred.AppendElement();
+            if (!d) {
+                NS_ERROR("Failed to allocate DeferredModule");
+                continue;
+            }
+
+            d->file = fakeNativeFile;
+            d->location = entryspec;
+            d->module = module;
+        }
+    }
+
+    for (PRInt32 i = 0; i < deferred.Length(); i++) {
+        DeferredModule &d = deferred[i];
+        rv = d.module->RegisterSelf(this, d.file,
+                                    d.location.get(),
+                                    jarComponentType);
+        if (NS_FAILED(rv))
+            NS_ERROR("js component failed to load on reregistration");
+    }
+}
+#endif /* MOZ_OMNIJAR */
+
 static void
 RegisterStaticModule(const char *key, nsIModule* module,
                      nsTArray<DeferredModule> &deferred)
 {
     nsresult rv = module->
         RegisterSelf(nsComponentManagerImpl::gComponentManager,
                      nsnull, key, staticComponentType);
 
@@ -3445,16 +3564,20 @@ nsComponentManagerImpl::AutoRegister(nsI
     if (!aSpec) {
         mStaticModuleLoader.EnumerateModules(RegisterStaticModule,
                                              deferred);
 
         // Builtin component loaders (xpconnect!) can be static modules.
         // Set them up now, so that JS components don't go into
         // the leftovers list.
         GetAllLoaders();
+
+#ifdef MOZ_OMNIJAR
+        AutoRegisterJar();
+#endif
     }
 
     LoaderType curLoader = GetLoaderCount();
 
     if (aSpec) {
         rv = AutoRegisterImpl(aSpec, leftovers, deferred);
     }
     else {
@@ -3731,16 +3854,53 @@ nsFactoryEntry::GetFactory(nsIFactory **
         nsCOMPtr<nsIModule> module;
 
         if (mLoaderType == NS_LOADER_TYPE_STATIC) {
             rv = nsComponentManagerImpl::gComponentManager->
                 mStaticModuleLoader.
                 GetModuleFor(mLocationKey,
                              getter_AddRefs(module));
         }
+        else if (mLoaderType == NS_LOADER_TYPE_JAR) {
+            nsComponentManagerImpl::gComponentManager->GetAllLoaders();
+
+            nsIModuleLoader *jsLoader =
+                nsComponentManagerImpl::gComponentManager->
+                    LoaderForType(nsComponentManagerImpl::gComponentManager->
+                        GetLoaderType("text/javascript"));
+            if (!jsLoader) {
+                NS_ERROR("could not get JS loader");
+                NS_ERROR(mLocationKey);
+                return NS_ERROR_FAILURE;
+            }
+
+            // currently we assume all NS_LOADER_TYPE_JAR cases point to 
+            // entries in the omnijar.
+#ifdef MOZ_OMNIJAR
+            nsCOMPtr<nsILocalFile> omniJar(mozilla::OmnijarPath());
+            if (!omniJar) {
+                NS_ERROR("could not get omnijar");
+                return NS_ERROR_FAILURE;
+            }
+
+            if (strlen(mLocationKey) < kOMNIJAR_PREFIX.Length()) {
+                NS_ERROR("invalid mLocationKey");
+                return NS_ERROR_FAILURE;
+            }
+
+            rv = jsLoader->LoadModuleFromJAR(omniJar,
+                                             nsDependentCSubstring(nsDependentCString(mLocationKey), kOMNIJAR_PREFIX.Length()),
+                                             getter_AddRefs(module));
+            if (NS_FAILED(rv))
+                NS_ERROR("Failed to load JS component from JAR");
+#else
+            NS_ERROR("Omnijar not enabled");
+            return NS_ERROR_FAILURE;
+#endif
+        }
         else {
             nsCOMPtr<nsILocalFile> moduleFile;
             rv = nsComponentManagerImpl::gComponentManager->
                 FileForRegistryLocation(nsDependentCString(mLocationKey),
                                         getter_AddRefs(moduleFile));
             NS_ENSURE_SUCCESS(rv, rv);
 
             nsIModuleLoader* loader =
--- a/xpcom/components/nsComponentManager.h
+++ b/xpcom/components/nsComponentManager.h
@@ -85,17 +85,18 @@ extern const char fileSizeValueName[];
 extern const char nativeComponentType[];
 extern const char staticComponentType[];
 
 typedef int LoaderType;
 
 // Predefined loader types.
 #define NS_LOADER_TYPE_NATIVE  -1
 #define NS_LOADER_TYPE_STATIC  -2
-#define NS_LOADER_TYPE_INVALID -3
+#define NS_LOADER_TYPE_JAR     -3
+#define NS_LOADER_TYPE_INVALID -4
 
 #ifdef DEBUG
 #define XPCOM_CHECK_PENDING_CIDS
 #endif
 ////////////////////////////////////////////////////////////////////////////////
 
 // Array of Loaders and their type strings
 struct nsLoaderdata {
@@ -233,16 +234,19 @@ public:
     nsresult AutoRegisterComponent(nsILocalFile*             aComponentFile,
                                    nsTArray<DeferredModule> &aDeferred,
                                    LoaderType                minLoader = NS_LOADER_TYPE_NATIVE);
     void LoadLeftoverComponents(nsCOMArray<nsILocalFile> &aLeftovers,
                                 nsTArray<DeferredModule> &aDeferred,
                                 LoaderType                minLoader);
 
     void LoadDeferredModules(nsTArray<DeferredModule> &aDeferred);
+#ifdef MOZ_OMNIJAR
+    void AutoRegisterJar();
+#endif
 
     PLDHashTable        mFactories;
     PLDHashTable        mContractIDs;
     PRMonitor*          mMon;
 
     nsNativeModuleLoader mNativeModuleLoader;
     nsStaticModuleLoader mStaticModuleLoader;
     nsCOMPtr<nsIFile>   mComponentsDir;
--- a/xpcom/components/nsIModuleLoader.idl
+++ b/xpcom/components/nsIModuleLoader.idl
@@ -42,23 +42,28 @@ interface nsIModule;
 
 /**
  * Module loaders are responsible for loading a component file. The static
  * component loader is special and does not use this interface.
  *
  * @note Implementations of this interface should be threadsafe,
  *       methods may be called from any thread.
  */
-[scriptable, uuid(9b328e24-4d85-4a9c-9cb7-f25e66f430c3)]
+[scriptable, uuid(F8A0C31B-8DB0-433B-AF7A-8D494799A229)]
 interface nsIModuleLoader : nsISupports
 {
   /**
    * Return the module for a specified file. The module loader should
    * cache the module and return the same module for subsequent
    * requests for the same physical file. The module loader is
    * responsible for unloading the module during shutdown.
    *
    * @throws NS_ERROR_INVALID_ARG If the filename is not recognized.
    * @throws NS_ERROR_FACTORY_NOT_LOADED If the filename is recognized but
    *           the module fails to load, e.g.: missing symbols, syntax errors
    */
   nsIModule loadModule(in nsILocalFile aFile);
+
+  /**
+   * nsIModuleLoader::LoadModule that can load from JARs
+   */
+  [noscript] nsIModule loadModuleFromJAR(in nsILocalFile aJAR, in ACString aPath);
 };
--- a/xpcom/components/nsNativeComponentLoader.cpp
+++ b/xpcom/components/nsNativeComponentLoader.cpp
@@ -268,16 +268,22 @@ nsNativeModuleLoader::UnloaderFunc(nsIHa
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
 #endif
 
     return PL_DHASH_REMOVE;
 }
 
+NS_IMETHODIMP
+nsNativeModuleLoader::LoadModuleFromJAR(nsILocalFile* aJAR, const nsACString &aPath, nsIModule* *aResult)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 void
 nsNativeModuleLoader::UnloadLibraries()
 {
     NS_ASSERTION(NS_IsMainThread(), "Shutdown not on main thread?");
 
     mLibraries.Enumerate(ReleaserFunc, nsnull);
     mLibraries.Enumerate(UnloaderFunc, nsnull);
 }