Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / xpconnect / loader / mozJSComponentLoader.cpp
blob122373a5fe84db2715c076408671cd218c6ac4d5
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1999
22 * the Initial Developer. All Rights Reserved.
24 * Contributors:
25 * Mike Shaver <shaver@zeroknowledge.com>
26 * John Bandhauer <jband@netscape.com>
27 * IBM Corp.
28 * Robert Ginda <rginda@netscape.com>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
44 #ifdef MOZ_LOGGING
45 #define FORCE_PR_LOG
46 #endif
48 #include <stdarg.h>
50 #include "prlog.h"
52 #include "nsCOMPtr.h"
53 #include "nsAutoPtr.h"
54 #include "nsICategoryManager.h"
55 #include "nsIComponentManager.h"
56 #include "mozilla/Module.h"
57 #include "nsILocalFile.h"
58 #include "nsIServiceManager.h"
59 #include "nsISupports.h"
60 #include "mozJSComponentLoader.h"
61 #include "nsIJSRuntimeService.h"
62 #include "nsIJSContextStack.h"
63 #include "nsIXPConnect.h"
64 #include "nsCRT.h"
65 #include "nsMemory.h"
66 #include "nsIObserverService.h"
67 #include "nsIXPCScriptable.h"
68 #include "nsString.h"
69 #ifndef XPCONNECT_STANDALONE
70 #include "nsIScriptSecurityManager.h"
71 #include "nsIURI.h"
72 #include "nsIFileURL.h"
73 #include "nsIJARURI.h"
74 #include "nsNetUtil.h"
75 #endif
76 #include "jsxdrapi.h"
77 #include "jscompartment.h"
78 #include "jsprf.h"
79 // For reporting errors with the console service
80 #include "nsIScriptError.h"
81 #include "nsIConsoleService.h"
82 #include "nsIStorageStream.h"
83 #include "nsIStringStream.h"
84 #include "prmem.h"
85 #if defined(XP_WIN)
86 #include "nsILocalFileWin.h"
87 #endif
88 #include "xpcprivate.h"
90 #ifdef MOZ_ENABLE_LIBXUL
91 #include "mozilla/scache/StartupCache.h"
92 #include "mozilla/scache/StartupCacheUtils.h"
93 #endif
94 #include "mozilla/Omnijar.h"
96 #include "jsdbgapi.h"
98 #include "mozilla/FunctionTimer.h"
100 static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1";
101 static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
102 static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
104 /* Some platforms don't have an implementation of PR_MemMap(). */
105 #if !defined(XP_BEOS) && !defined(XP_OS2)
106 #define HAVE_PR_MEMMAP
107 #endif
110 * Buffer sizes for serialization and deserialization of scripts.
111 * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
113 #define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
114 #define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
116 #ifdef PR_LOGGING
117 // NSPR_LOG_MODULES=JSComponentLoader:5
118 static PRLogModuleInfo *gJSCLLog;
119 #endif
121 #define LOG(args) PR_LOG(gJSCLLog, PR_LOG_DEBUG, args)
123 // Components.utils.import error messages
124 #define ERROR_SCOPE_OBJ "%s - Second argument must be an object."
125 #define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present."
126 #define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array."
127 #define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS."
128 #define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string."
129 #define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'."
130 #define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object."
132 void
133 mozJSLoaderErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep)
135 nsresult rv;
137 /* Use the console service to register the error. */
138 nsCOMPtr<nsIConsoleService> consoleService =
139 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
142 * Make an nsIScriptError, populate it with information from this
143 * error, then log it with the console service. The UI can then
144 * poll the service to update the Error console.
146 nsCOMPtr<nsIScriptError> errorObject =
147 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
149 if (consoleService && errorObject) {
151 * Got an error object; prepare appropriate-width versions of
152 * various arguments to it.
154 nsAutoString fileUni;
155 fileUni.AssignWithConversion(rep->filename);
157 PRUint32 column = rep->uctokenptr - rep->uclinebuf;
159 rv = errorObject->Init(reinterpret_cast<const PRUnichar*>
160 (rep->ucmessage),
161 fileUni.get(),
162 reinterpret_cast<const PRUnichar*>
163 (rep->uclinebuf),
164 rep->lineno, column, rep->flags,
165 "component javascript");
166 if (NS_SUCCEEDED(rv)) {
167 rv = consoleService->LogMessage(errorObject);
168 if (NS_SUCCEEDED(rv)) {
169 // We're done! Skip return to fall thru to stderr
170 // printout, for the benefit of those invoking the
171 // browser with -console
172 // return;
178 * If any of the above fails for some reason, fall back to
179 * printing to stderr.
181 #ifdef DEBUG
182 fprintf(stderr, "JS Component Loader: %s %s:%d\n"
183 " %s\n",
184 JSREPORT_IS_WARNING(rep->flags) ? "WARNING" : "ERROR",
185 rep->filename, rep->lineno,
186 message ? message : "<no message>");
187 #endif
190 static JSBool
191 Dump(JSContext *cx, uintN argc, jsval *vp)
193 JSString *str;
194 if (!argc)
195 return JS_TRUE;
197 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
198 if (!str)
199 return JS_FALSE;
201 size_t length;
202 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
203 if (!chars)
204 return JS_FALSE;
206 fputs(NS_ConvertUTF16toUTF8(reinterpret_cast<const PRUnichar*>(chars)).get(), stderr);
207 return JS_TRUE;
210 static JSBool
211 Debug(JSContext *cx, uintN argc, jsval *vp)
213 #ifdef DEBUG
214 return Dump(cx, argc, vp);
215 #else
216 return JS_TRUE;
217 #endif
220 static JSBool
221 Atob(JSContext *cx, uintN argc, jsval *vp)
223 if (!argc)
224 return JS_TRUE;
226 return nsXPConnect::Base64Decode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
229 static JSBool
230 Btoa(JSContext *cx, uintN argc, jsval *vp)
232 if (!argc)
233 return JS_TRUE;
235 return nsXPConnect::Base64Encode(cx, JS_ARGV(cx, vp)[0], &JS_RVAL(cx, vp));
238 static JSFunctionSpec gGlobalFun[] = {
239 {"dump", Dump, 1,0},
240 {"debug", Debug, 1,0},
241 {"atob", Atob, 1,0},
242 {"btoa", Btoa, 1,0},
243 #ifdef MOZ_CALLGRIND
244 {"startCallgrind", js_StartCallgrind, 0,0},
245 {"stopCallgrind", js_StopCallgrind, 0,0},
246 {"dumpCallgrind", js_DumpCallgrind, 1,0},
247 #endif
248 #ifdef MOZ_VTUNE
249 {"startVtune", js_StartVtune, 1,0},
250 {"stopVtune", js_StopVtune, 0,0},
251 {"pauseVtune", js_PauseVtune, 0,0},
252 {"resumeVtune", js_ResumeVtune, 0,0},
253 #endif
254 #ifdef MOZ_TRACEVIS
255 {"initEthogram", js_InitEthogram, 0,0},
256 {"shutdownEthogram", js_ShutdownEthogram, 0,0},
257 #endif
258 {nsnull,nsnull,0,0}
261 class JSCLContextHelper
263 public:
264 JSCLContextHelper(mozJSComponentLoader* loader);
265 ~JSCLContextHelper() { Pop(); }
267 JSContext* Pop();
269 operator JSContext*() const {return mContext;}
271 private:
272 JSContext* mContext;
273 intN mContextThread;
274 nsIThreadJSContextStack* mContextStack;
276 // prevent copying and assignment
277 JSCLContextHelper(const JSCLContextHelper &); // not implemented
278 const JSCLContextHelper& operator=(const JSCLContextHelper &); // not implemented
282 class JSCLAutoErrorReporterSetter
284 public:
285 JSCLAutoErrorReporterSetter(JSContext* cx, JSErrorReporter reporter)
286 {mContext = cx; mOldReporter = JS_SetErrorReporter(cx, reporter);}
287 ~JSCLAutoErrorReporterSetter()
288 {JS_SetErrorReporter(mContext, mOldReporter);}
289 private:
290 JSContext* mContext;
291 JSErrorReporter mOldReporter;
292 // prevent copying and assignment
293 JSCLAutoErrorReporterSetter(const JSCLAutoErrorReporterSetter &); // not implemented
294 const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &); // not implemented
297 static nsresult
298 OutputError(JSContext *cx,
299 const char *format,
300 va_list ap)
302 char *buf = JS_vsmprintf(format, ap);
303 if (!buf) {
304 return NS_ERROR_OUT_OF_MEMORY;
307 JS_ReportError(cx, buf);
308 JS_smprintf_free(buf);
310 return NS_OK;
313 static nsresult
314 ReportOnCaller(nsAXPCNativeCallContext *cc,
315 const char *format, ...) {
316 if (!cc) {
317 return NS_ERROR_FAILURE;
320 va_list ap;
321 va_start(ap, format);
323 nsresult rv;
324 JSContext *callerContext;
325 rv = cc->GetJSContext(&callerContext);
326 NS_ENSURE_SUCCESS(rv, rv);
328 return OutputError(callerContext, format, ap);
331 static nsresult
332 ReportOnCaller(JSCLContextHelper &helper,
333 const char *format, ...)
335 va_list ap;
336 va_start(ap, format);
338 JSContext *cx = helper.Pop();
339 if (!cx) {
340 return NS_ERROR_FAILURE;
343 return OutputError(cx, format, ap);
346 #ifdef MOZ_ENABLE_LIBXUL
347 static nsresult
348 ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
349 JSScript **script)
351 *script = nsnull;
353 PRUint32 size;
354 nsresult rv = stream->Read32(&size);
355 NS_ENSURE_SUCCESS(rv, rv);
357 char *data;
358 rv = stream->ReadBytes(size, &data);
359 NS_ENSURE_SUCCESS(rv, rv);
361 JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
362 NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
364 xdr->userdata = stream;
365 JS_XDRMemSetData(xdr, data, size);
367 if (!JS_XDRScript(xdr, script)) {
368 rv = NS_ERROR_FAILURE;
371 // Update data in case ::JS_XDRScript called back into C++ code to
372 // read an XPCOM object.
374 // In that case, the serialization process must have flushed a run
375 // of counted bytes containing JS data at the point where the XPCOM
376 // object starts, after which an encoding C++ callback from the JS
377 // XDR code must have written the XPCOM object directly into the
378 // nsIObjectOutputStream.
380 // The deserialization process will XDR-decode counted bytes up to
381 // but not including the XPCOM object, then call back into C++ to
382 // read the object, then read more counted bytes and hand them off
383 // to the JSXDRState, so more JS data can be decoded.
385 // This interleaving of JS XDR data and XPCOM object data may occur
386 // several times beneath the call to ::JS_XDRScript, above. At the
387 // end of the day, we need to free (via nsMemory) the data owned by
388 // the JSXDRState. So we steal it back, nulling xdr's buffer so it
389 // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
391 uint32 length;
392 data = static_cast<char*>(JS_XDRMemGetData(xdr, &length));
393 if (data) {
394 JS_XDRMemSetData(xdr, nsnull, 0);
396 JS_XDRDestroy(xdr);
398 // If data is null now, it must have been freed while deserializing an
399 // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
400 if (data) {
401 nsMemory::Free(data);
404 return rv;
407 static nsresult
408 WriteScriptToStream(JSContext *cx, JSScript *script,
409 nsIObjectOutputStream *stream)
411 JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
412 NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
414 xdr->userdata = stream;
415 nsresult rv = NS_OK;
417 if (JS_XDRScript(xdr, &script)) {
418 // Get the encoded JSXDRState data and write it. The JSXDRState owns
419 // this buffer memory and will free it beneath ::JS_XDRDestroy.
421 // If an XPCOM object needs to be written in the midst of the JS XDR
422 // encoding process, the C++ code called back from the JS engine (e.g.,
423 // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
424 // from the JSXDRState to aStream, then write the object, then return
425 // to JS XDR code with xdr reset so new JS data is encoded at the front
426 // of the xdr's data buffer.
428 // However many XPCOM objects are interleaved with JS XDR data in the
429 // stream, when control returns here from ::JS_XDRScript, we'll have
430 // one last buffer of data to write to aStream.
432 uint32 size;
433 const char* data = reinterpret_cast<const char*>
434 (JS_XDRMemGetData(xdr, &size));
435 NS_ASSERTION(data, "no decoded JSXDRState data!");
437 rv = stream->Write32(size);
438 if (NS_SUCCEEDED(rv)) {
439 rv = stream->WriteBytes(data, size);
441 } else {
442 rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
445 JS_XDRDestroy(xdr);
446 return rv;
448 #endif // MOZ_ENABLE_LIBXUL
450 mozJSComponentLoader::mozJSComponentLoader()
451 : mRuntime(nsnull),
452 mContext(nsnull),
453 mInitialized(PR_FALSE)
455 NS_ASSERTION(!sSelf, "mozJSComponentLoader should be a singleton");
457 #ifdef PR_LOGGING
458 if (!gJSCLLog) {
459 gJSCLLog = PR_NewLogModule("JSComponentLoader");
461 #endif
463 sSelf = this;
466 mozJSComponentLoader::~mozJSComponentLoader()
468 if (mInitialized) {
469 NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader");
470 UnloadModules();
473 sSelf = nsnull;
476 mozJSComponentLoader*
477 mozJSComponentLoader::sSelf;
479 NS_IMPL_ISUPPORTS3(mozJSComponentLoader,
480 mozilla::ModuleLoader,
481 xpcIJSModuleLoader,
482 nsIObserver)
484 nsresult
485 mozJSComponentLoader::ReallyInit()
487 NS_TIME_FUNCTION;
488 nsresult rv;
492 * Get the JSRuntime from the runtime svc, if possible.
493 * We keep a reference around, because it's a Bad Thing if the runtime
494 * service gets shut down before we're done. Bad!
497 mRuntimeService = do_GetService(kJSRuntimeServiceContractID, &rv);
498 if (NS_FAILED(rv) ||
499 NS_FAILED(rv = mRuntimeService->GetRuntime(&mRuntime)))
500 return rv;
502 mContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
503 if (NS_FAILED(rv))
504 return rv;
506 // Create our compilation context.
507 mContext = JS_NewContext(mRuntime, 256);
508 if (!mContext)
509 return NS_ERROR_OUT_OF_MEMORY;
511 uint32 options = JS_GetOptions(mContext);
512 JS_SetOptions(mContext, options | JSOPTION_XML);
514 // Always use the latest js version
515 JS_SetVersion(mContext, JSVERSION_LATEST);
517 // Limit C stack consumption to a reasonable 512K
518 JS_SetNativeStackQuota(mContext, 512 * 1024);
520 #ifndef XPCONNECT_STANDALONE
521 nsCOMPtr<nsIScriptSecurityManager> secman =
522 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
523 if (!secman)
524 return NS_ERROR_FAILURE;
526 rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
527 if (NS_FAILED(rv) || !mSystemPrincipal)
528 return NS_ERROR_FAILURE;
529 #endif
531 if (!mModules.Init(32))
532 return NS_ERROR_OUT_OF_MEMORY;
533 if (!mImports.Init(32))
534 return NS_ERROR_OUT_OF_MEMORY;
535 if (!mInProgressImports.Init(32))
536 return NS_ERROR_OUT_OF_MEMORY;
538 nsCOMPtr<nsIObserverService> obsSvc =
539 do_GetService(kObserverServiceContractID, &rv);
540 NS_ENSURE_SUCCESS(rv, rv);
542 rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", PR_FALSE);
543 NS_ENSURE_SUCCESS(rv, rv);
545 // Set up localized comparison and string conversion
546 xpc_LocalizeContext(mContext);
548 #ifdef DEBUG_shaver_off
549 fprintf(stderr, "mJCL: ReallyInit success!\n");
550 #endif
551 mInitialized = PR_TRUE;
553 return NS_OK;
556 nsresult
557 mozJSComponentLoader::FileKey(nsILocalFile* aFile, nsAString &aResult)
559 nsresult rv = NS_OK;
560 nsAutoString canonicalPath;
562 #if defined(XP_WIN)
563 nsCOMPtr<nsILocalFileWin> winFile = do_QueryInterface(aFile, &rv);
564 NS_ENSURE_SUCCESS(rv, rv);
566 winFile->GetCanonicalPath(canonicalPath);
567 #else
568 aFile->GetPath(canonicalPath);
569 #endif
571 aResult = NS_LITERAL_STRING("f");
572 aResult += canonicalPath;
574 return rv;
577 nsresult
578 mozJSComponentLoader::JarKey(nsILocalFile* aFile,
579 const nsACString &aComponentPath,
580 nsAString &aResult)
582 nsresult rv = NS_OK;
583 nsAutoString canonicalPath;
585 #if defined(XP_WIN)
586 nsCOMPtr<nsILocalFileWin> winFile = do_QueryInterface(aFile, &rv);
587 NS_ENSURE_SUCCESS(rv, rv);
589 winFile->GetCanonicalPath(canonicalPath);
590 #else
591 aFile->GetPath(canonicalPath);
592 #endif
594 aResult = NS_LITERAL_STRING("j");
595 aResult += canonicalPath;
596 AppendUTF8toUTF16(aComponentPath, aResult);
598 return rv;
601 const mozilla::Module*
602 mozJSComponentLoader::LoadModule(nsILocalFile* aComponentFile)
604 nsCOMPtr<nsIURI> uri;
605 nsCAutoString spec;
606 NS_GetURLSpecFromActualFile(aComponentFile, spec);
608 nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
609 if (NS_FAILED(rv))
610 return NULL;
612 nsAutoString hashstring;
613 rv = FileKey(aComponentFile, hashstring);
614 if (NS_FAILED(rv))
615 return NULL;
617 return LoadModuleImpl(aComponentFile,
618 hashstring,
619 uri);
622 const mozilla::Module*
623 mozJSComponentLoader::LoadModuleFromJAR(nsILocalFile *aJarFile,
624 const nsACString &aComponentPath)
626 #if !defined(XPCONNECT_STANDALONE)
627 nsresult rv;
629 nsCAutoString fullSpec;
631 #ifdef MOZ_OMNIJAR
632 PRBool equal;
633 rv = aJarFile->Equals(mozilla::OmnijarPath(), &equal);
634 if (NS_SUCCEEDED(rv) && equal) {
635 fullSpec = "resource://gre/";
636 } else {
637 #endif
638 nsCAutoString fileSpec;
639 NS_GetURLSpecFromActualFile(aJarFile, fileSpec);
640 fullSpec = "jar:";
641 fullSpec += fileSpec;
642 fullSpec += "!/";
643 #ifdef MOZ_OMNIJAR
645 #endif
647 fullSpec += aComponentPath;
649 nsCOMPtr<nsIURI> uri;
650 rv = NS_NewURI(getter_AddRefs(uri), fullSpec);
651 if (NS_FAILED(rv))
652 return NULL;
654 nsAutoString hashstring;
655 rv = JarKey(aJarFile, aComponentPath, hashstring);
656 if (NS_FAILED(rv))
657 return NULL;
659 return LoadModuleImpl(aJarFile,
660 hashstring,
661 uri);
662 #else
663 return NS_ERROR_NOT_IMPLEMENTED;
664 #endif
667 const mozilla::Module*
668 mozJSComponentLoader::LoadModuleImpl(nsILocalFile* aSourceFile,
669 nsAString &aKey,
670 nsIURI* aComponentURI)
672 nsresult rv;
674 #ifdef NS_FUNCTION_TIMER
675 nsCAutoString spec__("N/A");
676 aComponentURI->GetSpec(spec__);
677 NS_TIME_FUNCTION_FMT("%s (line %d) (file: %s)", MOZ_FUNCTION_NAME,
678 __LINE__, spec__.get());
679 #endif
681 if (!mInitialized) {
682 rv = ReallyInit();
683 if (NS_FAILED(rv))
684 return NULL;
687 ModuleEntry* mod;
688 if (mModules.Get(aKey, &mod))
689 return mod;
691 nsAutoPtr<ModuleEntry> entry(new ModuleEntry);
692 if (!entry)
693 return NULL;
695 rv = GlobalForLocation(aSourceFile, aComponentURI, &entry->global,
696 &entry->location, nsnull);
697 if (NS_FAILED(rv)) {
698 #ifdef DEBUG_shaver
699 fprintf(stderr, "GlobalForLocation failed!\n");
700 #endif
701 return NULL;
704 nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
705 &rv);
706 if (NS_FAILED(rv))
707 return NULL;
709 nsCOMPtr<nsIComponentManager> cm;
710 rv = NS_GetComponentManager(getter_AddRefs(cm));
711 if (NS_FAILED(rv))
712 return NULL;
714 JSCLContextHelper cx(this);
715 JSAutoEnterCompartment ac;
716 if (!ac.enter(cx, entry->global))
717 return NULL;
719 JSObject* cm_jsobj;
720 nsCOMPtr<nsIXPConnectJSObjectHolder> cm_holder;
721 rv = xpc->WrapNative(cx, entry->global, cm,
722 NS_GET_IID(nsIComponentManager),
723 getter_AddRefs(cm_holder));
725 if (NS_FAILED(rv)) {
726 #ifdef DEBUG_shaver
727 fprintf(stderr, "WrapNative(%p,%p,nsIComponentManager) failed: %x\n",
728 (void *)(JSContext*)cx, (void *)mCompMgr, rv);
729 #endif
730 return NULL;
733 rv = cm_holder->GetJSObject(&cm_jsobj);
734 if (NS_FAILED(rv)) {
735 #ifdef DEBUG_shaver
736 fprintf(stderr, "GetJSObject of ComponentManager failed\n");
737 #endif
738 return NULL;
741 JSObject* file_jsobj;
742 nsCOMPtr<nsIXPConnectJSObjectHolder> file_holder;
743 rv = xpc->WrapNative(cx, entry->global, aSourceFile,
744 NS_GET_IID(nsIFile),
745 getter_AddRefs(file_holder));
747 if (NS_FAILED(rv)) {
748 return NULL;
751 rv = file_holder->GetJSObject(&file_jsobj);
752 if (NS_FAILED(rv)) {
753 return NULL;
756 JSCLAutoErrorReporterSetter aers(cx, mozJSLoaderErrorReporter);
758 jsval NSGetFactory_val;
760 if (!JS_GetProperty(cx, entry->global, "NSGetFactory", &NSGetFactory_val) ||
761 JSVAL_IS_VOID(NSGetFactory_val)) {
762 return NULL;
765 if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
766 nsCAutoString spec;
767 aComponentURI->GetSpec(spec);
768 JS_ReportError(cx, "%s has NSGetFactory property that is not a function",
769 spec.get());
770 return NULL;
773 JSObject *jsGetFactoryObj;
774 if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
775 !jsGetFactoryObj) {
776 /* XXX report error properly */
777 return NULL;
780 rv = xpc->WrapJS(cx, jsGetFactoryObj,
781 NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj));
782 if (NS_FAILED(rv)) {
783 /* XXX report error properly */
784 #ifdef DEBUG
785 fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
786 #endif
787 return NULL;
790 // Cache this module for later
791 if (!mModules.Put(aKey, entry))
792 return NULL;
794 // The hash owns the ModuleEntry now, forget about it
795 return entry.forget();
798 // Some stack based classes for cleaning up on early return
799 #ifdef HAVE_PR_MEMMAP
800 class FileAutoCloser
802 public:
803 explicit FileAutoCloser(PRFileDesc *file) : mFile(file) {}
804 ~FileAutoCloser() { PR_Close(mFile); }
805 private:
806 PRFileDesc *mFile;
809 class FileMapAutoCloser
811 public:
812 explicit FileMapAutoCloser(PRFileMap *map) : mMap(map) {}
813 ~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
814 private:
815 PRFileMap *mMap;
817 #endif
819 class JSPrincipalsHolder
821 public:
822 JSPrincipalsHolder(JSContext *cx, JSPrincipals *principals)
823 : mCx(cx), mPrincipals(principals) {}
824 ~JSPrincipalsHolder() { JSPRINCIPALS_DROP(mCx, mPrincipals); }
825 private:
826 JSContext *mCx;
827 JSPrincipals *mPrincipals;
830 class JSScriptHolder
832 public:
833 JSScriptHolder(JSContext *cx, JSScript *script)
834 : mCx(cx), mScript(script) {}
835 ~JSScriptHolder() { ::JS_DestroyScript(mCx, mScript); }
836 private:
837 JSContext *mCx;
838 JSScript *mScript;
842 * PathifyURI transforms mozilla .js uris into useful zip paths
843 * to make it makes it easier to manipulate startup cache entries
844 * using standard zip tools.
845 * Transformations applied:
846 * * jsloader/<scheme> prefix is used to group mozJSComponentLoader cache entries in
847 * a top-level zip directory.
848 * * In MOZ_OMNIJAR case resource:/// and resource://gre/ URIs refer to the same path
849 * so treat both of them as resource://gre/
850 * * .bin suffix is added to the end of the path to indicate that jsloader/ entries
851 * are binary representations of JS source.
852 * For example:
853 * resource://gre/modules/XPCOMUtils.jsm becomes
854 * jsloader/resource/gre/modules/XPCOMUtils.jsm.bin
856 static nsresult
857 PathifyURI(nsIURI *in, nsACString &out)
859 out = "jsloader/";
860 nsCAutoString scheme;
861 nsresult rv = in->GetScheme(scheme);
862 NS_ENSURE_SUCCESS(rv, rv);
863 out.Append(scheme);
864 nsCAutoString host;
865 rv = in->GetHost(host);
866 NS_ENSURE_SUCCESS(rv, rv);
867 #ifdef MOZ_OMNIJAR
868 if (scheme.Equals("resource") && host.Length() == 0){
869 host = "gre";
871 #endif
872 if (host.Length()) {
873 out.Append("/");
874 out.Append(host);
876 nsCAutoString path;
877 rv = in->GetPath(path);
878 NS_ENSURE_SUCCESS(rv, rv);
879 out.Append(path);
880 out.Append(".bin");
881 return NS_OK;
884 /* static */
885 #ifdef MOZ_ENABLE_LIBXUL
886 nsresult
887 mozJSComponentLoader::ReadScript(StartupCache* cache, nsIURI *uri,
888 JSContext *cx, JSScript **script)
890 nsresult rv;
892 nsCAutoString spec;
893 rv = PathifyURI(uri, spec);
894 NS_ENSURE_SUCCESS(rv, rv);
896 nsAutoArrayPtr<char> buf;
897 PRUint32 len;
898 rv = cache->GetBuffer(spec.get(), getter_Transfers(buf),
899 &len);
900 if (NS_FAILED(rv)) {
901 return rv; // don't warn since NOT_AVAILABLE is an ok error
904 LOG(("Found %s in startupcache\n", spec.get()));
905 nsCOMPtr<nsIObjectInputStream> ois;
906 rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
907 NS_ENSURE_SUCCESS(rv, rv);
908 buf.forget();
910 return ReadScriptFromStream(cx, ois, script);
913 nsresult
914 mozJSComponentLoader::WriteScript(StartupCache* cache, JSScript *script,
915 nsIFile *component, nsIURI *uri, JSContext *cx)
917 nsresult rv;
919 nsCAutoString spec;
920 rv = PathifyURI(uri, spec);
921 NS_ENSURE_SUCCESS(rv, rv);
923 LOG(("Writing %s to startupcache\n", spec.get()));
924 nsCOMPtr<nsIObjectOutputStream> oos;
925 nsCOMPtr<nsIStorageStream> storageStream;
926 rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
927 getter_AddRefs(storageStream));
928 NS_ENSURE_SUCCESS(rv, rv);
930 rv = WriteScriptToStream(cx, script, oos);
931 oos->Close();
932 NS_ENSURE_SUCCESS(rv, rv);
934 nsAutoArrayPtr<char> buf;
935 PRUint32 len;
936 rv = NS_NewBufferFromStorageStream(storageStream, getter_Transfers(buf),
937 &len);
938 NS_ENSURE_SUCCESS(rv, rv);
940 rv = cache->PutBuffer(spec.get(), buf, len);
941 return rv;
943 #endif //MOZ_ENABLE_LIBXUL
945 nsresult
946 mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
947 nsIURI *aURI,
948 JSObject **aGlobal,
949 char **aLocation,
950 jsval *exception)
952 nsresult rv;
954 JSPrincipals* jsPrincipals = nsnull;
955 JSCLContextHelper cx(this);
957 // preserve caller's compartment
958 js::PreserveCompartment pc(cx);
960 #ifndef XPCONNECT_STANDALONE
961 rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals);
962 NS_ENSURE_SUCCESS(rv, rv);
964 JSPrincipalsHolder princHolder(mContext, jsPrincipals);
965 #endif
967 nsCOMPtr<nsIXPCScriptable> backstagePass;
968 rv = mRuntimeService->GetBackstagePass(getter_AddRefs(backstagePass));
969 NS_ENSURE_SUCCESS(rv, rv);
971 JSCLAutoErrorReporterSetter aers(cx, mozJSLoaderErrorReporter);
973 nsCOMPtr<nsIXPConnect> xpc =
974 do_GetService(kXPConnectServiceContractID, &rv);
975 NS_ENSURE_SUCCESS(rv, rv);
977 // Make sure InitClassesWithNewWrappedGlobal() installs the
978 // backstage pass as the global in our compilation context.
979 JS_SetGlobalObject(cx, nsnull);
981 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
982 rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
983 NS_GET_IID(nsISupports),
984 mSystemPrincipal,
985 nsnull,
986 nsIXPConnect::
987 FLAG_SYSTEM_GLOBAL_OBJECT,
988 getter_AddRefs(holder));
989 NS_ENSURE_SUCCESS(rv, rv);
991 JSObject *global;
992 rv = holder->GetJSObject(&global);
993 NS_ENSURE_SUCCESS(rv, rv);
995 JSAutoEnterCompartment ac;
996 if (!ac.enter(cx, global))
997 return NS_ERROR_FAILURE;
999 if (!JS_DefineFunctions(cx, global, gGlobalFun) ||
1000 !JS_DefineProfilingFunctions(cx, global)) {
1001 return NS_ERROR_FAILURE;
1004 bool realFile = false;
1005 // need to be extra careful checking for URIs pointing to files
1006 // EnsureFile may not always get called, especially on resource URIs
1007 // so we need to call GetFile to make sure this is a valid file
1008 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
1009 nsCOMPtr<nsIFile> testFile;
1010 if (NS_SUCCEEDED(rv)) {
1011 fileURL->GetFile(getter_AddRefs(testFile));
1014 if (testFile) {
1015 realFile = true;
1017 nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
1018 rv = xpc->WrapNative(cx, global, aComponentFile,
1019 NS_GET_IID(nsILocalFile),
1020 getter_AddRefs(locationHolder));
1021 NS_ENSURE_SUCCESS(rv, rv);
1023 JSObject *locationObj;
1024 rv = locationHolder->GetJSObject(&locationObj);
1025 NS_ENSURE_SUCCESS(rv, rv);
1027 if (!JS_DefineProperty(cx, global, "__LOCATION__",
1028 OBJECT_TO_JSVAL(locationObj), nsnull, nsnull, 0))
1029 return NS_ERROR_FAILURE;
1032 nsCAutoString nativePath;
1033 // Quick hack to unbust XPCONNECT_STANDALONE.
1034 // This leaves the jsdebugger with a non-URL pathname in the
1035 // XPCONNECT_STANDALONE case - but at least it builds and runs otherwise.
1036 // See: http://bugzilla.mozilla.org/show_bug.cgi?id=121438
1037 #ifdef XPCONNECT_STANDALONE
1038 localFile->GetNativePath(nativePath);
1039 #else
1040 rv = aURI->GetSpec(nativePath);
1041 NS_ENSURE_SUCCESS(rv, rv);
1042 #endif
1044 JSScript *script = nsnull;
1046 #ifdef MOZ_ENABLE_LIBXUL
1047 // Before compiling the script, first check to see if we have it in
1048 // the startupcache. Note: as a rule, startupcache errors are not fatal
1049 // to loading the script, since we can always slow-load.
1051 PRBool writeToCache = PR_FALSE;
1052 StartupCache* cache = StartupCache::GetSingleton();
1054 if (cache) {
1055 rv = ReadScript(cache, aURI, cx, &script);
1056 if (NS_SUCCEEDED(rv)) {
1057 LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
1058 } else {
1059 // This is ok, it just means the script is not yet in the
1060 // cache. Could mean that the cache was corrupted and got removed,
1061 // but either way we're going to write this out.
1062 writeToCache = PR_TRUE;
1065 #endif
1067 if (!script) {
1068 // The script wasn't in the cache , so compile it now.
1069 LOG(("Slow loading %s\n", nativePath.get()));
1071 // If |exception| is non-null, then our caller wants us to propagate
1072 // any exceptions out to our caller. Ensure that the engine doesn't
1073 // eagerly report the exception.
1074 uint32 oldopts = 0;
1075 if (exception) {
1076 oldopts = JS_GetOptions(cx);
1077 JS_SetOptions(cx, oldopts | JSOPTION_DONT_REPORT_UNCAUGHT);
1080 if (realFile) {
1081 #ifdef HAVE_PR_MEMMAP
1082 PRInt64 fileSize;
1083 rv = aComponentFile->GetFileSize(&fileSize);
1084 if (NS_FAILED(rv)) {
1085 JS_SetOptions(cx, oldopts);
1086 return rv;
1089 PRInt64 maxSize;
1090 LL_UI2L(maxSize, PR_UINT32_MAX);
1091 if (LL_CMP(fileSize, >, maxSize)) {
1092 NS_ERROR("file too large");
1093 JS_SetOptions(cx, oldopts);
1094 return NS_ERROR_FAILURE;
1097 PRFileDesc *fileHandle;
1098 rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
1099 if (NS_FAILED(rv)) {
1100 JS_SetOptions(cx, oldopts);
1101 return NS_ERROR_FILE_NOT_FOUND;
1104 // Make sure the file is closed, no matter how we return.
1105 FileAutoCloser fileCloser(fileHandle);
1107 PRFileMap *map = PR_CreateFileMap(fileHandle, fileSize,
1108 PR_PROT_READONLY);
1109 if (!map) {
1110 NS_ERROR("Failed to create file map");
1111 JS_SetOptions(cx, oldopts);
1112 return NS_ERROR_FAILURE;
1115 // Make sure the file map is closed, no matter how we return.
1116 FileMapAutoCloser mapCloser(map);
1118 PRUint32 fileSize32;
1119 LL_L2UI(fileSize32, fileSize);
1121 char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
1122 if (!buf) {
1123 NS_WARNING("Failed to map file");
1124 JS_SetOptions(cx, oldopts);
1125 return NS_ERROR_FAILURE;
1128 script = JS_CompileScriptForPrincipalsVersion(
1129 cx, global, jsPrincipals, buf, fileSize32, nativePath.get(), 1,
1130 JSVERSION_LATEST);
1132 PR_MemUnmap(buf, fileSize32);
1134 #else /* HAVE_PR_MEMMAP */
1137 * No memmap implementation, so fall back to using
1138 * JS_CompileFileHandleForPrincipals().
1141 FILE *fileHandle;
1142 rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle);
1143 if (NS_FAILED(rv)) {
1144 JS_SetOptions(cx, oldopts);
1145 return NS_ERROR_FILE_NOT_FOUND;
1148 script = JS_CompileFileHandleForPrincipalsVersion(
1149 cx, global, nativePath.get(), fileHandle, jsPrincipals, JSVERSION_LATEST);
1151 /* JS will close the filehandle after compilation is complete. */
1152 #endif /* HAVE_PR_MEMMAP */
1153 } else {
1154 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
1155 NS_ENSURE_SUCCESS(rv, rv);
1157 nsCOMPtr<nsIChannel> scriptChannel;
1158 rv = ioService->NewChannelFromURI(aURI, getter_AddRefs(scriptChannel));
1159 NS_ENSURE_SUCCESS(rv, rv);
1161 nsCOMPtr<nsIInputStream> scriptStream;
1162 rv = scriptChannel->Open(getter_AddRefs(scriptStream));
1163 NS_ENSURE_SUCCESS(rv, rv);
1165 PRUint32 len, bytesRead;
1167 rv = scriptStream->Available(&len);
1168 NS_ENSURE_SUCCESS(rv, rv);
1169 if (!len)
1170 return NS_ERROR_FAILURE;
1172 /* malloc an internal buf the size of the file */
1173 nsAutoArrayPtr<char> buf(new char[len + 1]);
1174 if (!buf)
1175 return NS_ERROR_OUT_OF_MEMORY;
1177 /* read the file in one swoop */
1178 rv = scriptStream->Read(buf, len, &bytesRead);
1179 if (bytesRead != len)
1180 return NS_BASE_STREAM_OSERROR;
1182 buf[len] = '\0';
1184 script = JS_CompileScriptForPrincipalsVersion(
1185 cx, global, jsPrincipals, buf, bytesRead, nativePath.get(), 1,
1186 JSVERSION_LATEST);
1188 // Propagate the exception, if one exists. Also, don't leave the stale
1189 // exception on this context.
1190 // NB: The caller must stick exception into a rooted slot (probably on
1191 // its context) as soon as possible to avoid GC hazards.
1192 if (exception) {
1193 JS_SetOptions(cx, oldopts);
1194 if (!script) {
1195 JS_GetPendingException(cx, exception);
1196 JS_ClearPendingException(cx);
1201 if (!script) {
1202 #ifdef DEBUG_shaver_off
1203 fprintf(stderr, "mJCL: script compilation of %s FAILED\n",
1204 nativePath.get());
1205 #endif
1206 return NS_ERROR_FAILURE;
1209 // Ensure that we clean up the script on return.
1210 JSScriptHolder scriptHolder(cx, script);
1212 // Flag this script as a system script
1213 // FIXME: BUG 346139: We actually want to flag this exact filename, not
1214 // anything that starts with this filename... Maybe we need a way to do
1215 // that? On the other hand, the fact that this is in our components dir
1216 // means that if someone snuck a malicious file into this dir we're screwed
1217 // anyway... So maybe flagging as a prefix is fine.
1218 xpc->FlagSystemFilenamePrefix(nativePath.get(), PR_TRUE);
1220 #ifdef DEBUG_shaver_off
1221 fprintf(stderr, "mJCL: compiled JS component %s\n",
1222 nativePath.get());
1223 #endif
1225 #ifdef MOZ_ENABLE_LIBXUL
1226 if (writeToCache) {
1227 // We successfully compiled the script, so cache it.
1228 rv = WriteScript(cache, script, aComponentFile, aURI, cx);
1230 // Don't treat failure to write as fatal, since we might be working
1231 // with a read-only cache.
1232 if (NS_SUCCEEDED(rv)) {
1233 LOG(("Successfully wrote to cache\n"));
1234 } else {
1235 LOG(("Failed to write to cache\n"));
1238 #endif
1240 // Assign aGlobal here so that it's available to recursive imports.
1241 // See bug 384168.
1242 *aGlobal = global;
1244 jsval retval;
1245 if (!JS_ExecuteScriptVersion(cx, global, script, &retval, JSVERSION_LATEST)) {
1246 #ifdef DEBUG_shaver_off
1247 fprintf(stderr, "mJCL: failed to execute %s\n", nativePath.get());
1248 #endif
1249 *aGlobal = nsnull;
1250 return NS_ERROR_FAILURE;
1253 /* Freed when we remove from the table. */
1254 *aLocation = ToNewCString(nativePath);
1255 if (!*aLocation) {
1256 *aGlobal = nsnull;
1257 return NS_ERROR_OUT_OF_MEMORY;
1260 JS_AddNamedObjectRoot(cx, aGlobal, *aLocation);
1261 return NS_OK;
1264 /* static */ PLDHashOperator
1265 mozJSComponentLoader::ClearModules(const nsAString& key, ModuleEntry*& entry, void* cx)
1267 entry->Clear();
1268 return PL_DHASH_REMOVE;
1271 void
1272 mozJSComponentLoader::UnloadModules()
1274 mInitialized = PR_FALSE;
1276 mInProgressImports.Clear();
1277 mImports.Clear();
1279 mModules.Enumerate(ClearModules, NULL);
1281 // Destroying our context will force a GC.
1282 JS_DestroyContext(mContext);
1283 mContext = nsnull;
1285 mRuntimeService = nsnull;
1286 mContextStack = nsnull;
1287 #ifdef DEBUG_shaver_off
1288 fprintf(stderr, "mJCL: UnloadAll(%d)\n", aWhen);
1289 #endif
1292 /* [JSObject] import (in AUTF8String registryLocation,
1293 [optional] in JSObject targetObj ); */
1294 NS_IMETHODIMP
1295 mozJSComponentLoader::Import(const nsACString & registryLocation)
1297 // This function should only be called from JS.
1298 nsresult rv;
1300 NS_TIME_FUNCTION_FMT("%s (line %d) (file: %s)", MOZ_FUNCTION_NAME,
1301 __LINE__, registryLocation.BeginReading());
1303 nsCOMPtr<nsIXPConnect> xpc =
1304 do_GetService(kXPConnectServiceContractID, &rv);
1305 NS_ENSURE_SUCCESS(rv, rv);
1307 nsAXPCNativeCallContext *cc = nsnull;
1308 rv = xpc->GetCurrentNativeCallContext(&cc);
1309 NS_ENSURE_SUCCESS(rv, rv);
1311 #ifdef DEBUG
1313 // ensure that we are being call from JS, from this method
1314 nsCOMPtr<nsIInterfaceInfo> info;
1315 rv = cc->GetCalleeInterface(getter_AddRefs(info));
1316 NS_ENSURE_SUCCESS(rv, rv);
1317 nsXPIDLCString name;
1318 info->GetName(getter_Copies(name));
1319 NS_ASSERTION(nsCRT::strcmp("nsIXPCComponents_Utils", name.get()) == 0,
1320 "Components.utils.import must only be called from JS.");
1321 PRUint16 methodIndex;
1322 const nsXPTMethodInfo *methodInfo;
1323 rv = info->GetMethodInfoForName("import", &methodIndex, &methodInfo);
1324 NS_ENSURE_SUCCESS(rv, rv);
1325 PRUint16 calleeIndex;
1326 rv = cc->GetCalleeMethodIndex(&calleeIndex);
1327 NS_ASSERTION(calleeIndex == methodIndex,
1328 "Components.utils.import called from another utils method.");
1330 #endif
1332 JSContext *cx = nsnull;
1333 rv = cc->GetJSContext(&cx);
1334 NS_ENSURE_SUCCESS(rv, rv);
1336 JSAutoRequest ar(cx);
1338 JSObject *targetObject = nsnull;
1340 PRUint32 argc = 0;
1341 rv = cc->GetArgc(&argc);
1342 NS_ENSURE_SUCCESS(rv, rv);
1344 if (argc > 1) {
1345 // The caller passed in the optional second argument. Get it.
1346 jsval *argv = nsnull;
1347 rv = cc->GetArgvPtr(&argv);
1348 NS_ENSURE_SUCCESS(rv, rv);
1349 if (!JSVAL_IS_OBJECT(argv[1])) {
1350 return ReportOnCaller(cc, ERROR_SCOPE_OBJ,
1351 PromiseFlatCString(registryLocation).get());
1353 targetObject = JSVAL_TO_OBJECT(argv[1]);
1354 } else {
1355 // Our targetObject is the caller's global object. Find it by
1356 // walking the calling object's parent chain.
1358 nsCOMPtr<nsIXPConnectWrappedNative> wn;
1359 rv = cc->GetCalleeWrapper(getter_AddRefs(wn));
1360 NS_ENSURE_SUCCESS(rv, rv);
1362 wn->GetJSObject(&targetObject);
1363 if (!targetObject) {
1364 NS_ERROR("null calling object");
1365 return NS_ERROR_FAILURE;
1368 targetObject = JS_GetGlobalForObject(cx, targetObject);
1371 JSAutoEnterCompartment ac;
1372 if (targetObject && !ac.enter(cx, targetObject)) {
1373 NS_ERROR("can't enter compartment");
1374 return NS_ERROR_FAILURE;
1377 JSObject *globalObj = nsnull;
1378 rv = ImportInto(registryLocation, targetObject, cc, &globalObj);
1380 if (globalObj && !JS_WrapObject(cx, &globalObj)) {
1381 NS_ERROR("can't wrap return value");
1382 return NS_ERROR_FAILURE;
1385 jsval *retval = nsnull;
1386 cc->GetRetValPtr(&retval);
1387 if (retval)
1388 *retval = OBJECT_TO_JSVAL(globalObj);
1390 return rv;
1393 /* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation,
1394 in JSObjectPtr targetObj); */
1395 NS_IMETHODIMP
1396 mozJSComponentLoader::ImportInto(const nsACString & aLocation,
1397 JSObject * targetObj,
1398 nsAXPCNativeCallContext * cc,
1399 JSObject * *_retval)
1401 nsresult rv;
1402 *_retval = nsnull;
1404 if (!mInitialized) {
1405 rv = ReallyInit();
1406 NS_ENSURE_SUCCESS(rv, rv);
1409 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
1410 NS_ENSURE_SUCCESS(rv, rv);
1412 // Get the URI.
1413 nsCOMPtr<nsIURI> resURI;
1414 rv = ioService->NewURI(aLocation, nsnull, nsnull, getter_AddRefs(resURI));
1415 NS_ENSURE_SUCCESS(rv, rv);
1417 // figure out the resolved URI
1418 nsCOMPtr<nsIChannel> scriptChannel;
1419 rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
1420 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
1422 nsCOMPtr<nsIURI> resolvedURI;
1423 rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
1424 NS_ENSURE_SUCCESS(rv, rv);
1426 // get the JAR if there is one
1427 nsCOMPtr<nsIJARURI> jarURI;
1428 jarURI = do_QueryInterface(resolvedURI, &rv);
1429 nsCOMPtr<nsIFileURL> baseFileURL;
1430 nsCAutoString jarEntry;
1431 if (NS_SUCCEEDED(rv)) {
1432 nsCOMPtr<nsIURI> baseURI;
1433 rv = jarURI->GetJARFile(getter_AddRefs(baseURI));
1434 NS_ENSURE_SUCCESS(rv, rv);
1436 baseFileURL = do_QueryInterface(baseURI, &rv);
1437 NS_ENSURE_SUCCESS(rv, rv);
1439 jarURI->GetJAREntry(jarEntry);
1440 NS_ENSURE_SUCCESS(rv, rv);
1441 } else {
1442 baseFileURL = do_QueryInterface(resolvedURI, &rv);
1443 NS_ENSURE_SUCCESS(rv, rv);
1446 nsCOMPtr<nsIFile> sourceFile;
1447 rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
1448 NS_ENSURE_SUCCESS(rv, rv);
1450 nsCOMPtr<nsILocalFile> sourceLocalFile;
1451 sourceLocalFile = do_QueryInterface(sourceFile, &rv);
1452 NS_ENSURE_SUCCESS(rv, rv);
1454 nsAutoString key;
1455 if (jarEntry.IsEmpty()) {
1456 rv = FileKey(sourceLocalFile, key);
1457 } else {
1458 rv = JarKey(sourceLocalFile, jarEntry, key);
1460 NS_ENSURE_SUCCESS(rv, rv);
1462 ModuleEntry* mod;
1463 nsAutoPtr<ModuleEntry> newEntry;
1464 if (!mImports.Get(key, &mod) && !mInProgressImports.Get(key, &mod)) {
1465 newEntry = new ModuleEntry;
1466 if (!newEntry || !mInProgressImports.Put(key, newEntry))
1467 return NS_ERROR_OUT_OF_MEMORY;
1469 jsval exception = JSVAL_VOID;
1470 rv = GlobalForLocation(sourceLocalFile, resURI, &newEntry->global,
1471 &newEntry->location, &exception);
1473 mInProgressImports.Remove(key);
1475 if (NS_FAILED(rv)) {
1476 *_retval = nsnull;
1478 if (!JSVAL_IS_VOID(exception)) {
1479 // An exception was thrown during compilation. Propagate it
1480 // out to our caller so they can report it.
1481 JSContext *callercx;
1482 cc->GetJSContext(&callercx);
1483 JS_SetPendingException(callercx, exception);
1484 return NS_OK;
1487 // Something failed, but we don't know what it is, guess.
1488 return NS_ERROR_FILE_NOT_FOUND;
1491 mod = newEntry;
1494 NS_ASSERTION(mod->global, "Import table contains entry with no global");
1495 *_retval = mod->global;
1497 jsval symbols;
1498 if (targetObj) {
1499 JSCLContextHelper cxhelper(this);
1501 JSAutoEnterCompartment ac;
1502 if (!ac.enter(mContext, mod->global))
1503 return NS_ERROR_FAILURE;
1505 if (!JS_GetProperty(mContext, mod->global,
1506 "EXPORTED_SYMBOLS", &symbols)) {
1507 return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT,
1508 PromiseFlatCString(aLocation).get());
1511 JSObject *symbolsObj = nsnull;
1512 if (!JSVAL_IS_OBJECT(symbols) ||
1513 !(symbolsObj = JSVAL_TO_OBJECT(symbols)) ||
1514 !JS_IsArrayObject(mContext, symbolsObj)) {
1515 return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY,
1516 PromiseFlatCString(aLocation).get());
1519 // Iterate over symbols array, installing symbols on targetObj:
1521 jsuint symbolCount = 0;
1522 if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) {
1523 return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
1524 PromiseFlatCString(aLocation).get());
1527 #ifdef DEBUG
1528 nsCAutoString logBuffer;
1529 #endif
1531 for (jsuint i = 0; i < symbolCount; ++i) {
1532 jsval val;
1533 jsid symbolId;
1535 if (!JS_GetElement(mContext, symbolsObj, i, &val) ||
1536 !JSVAL_IS_STRING(val) ||
1537 !JS_ValueToId(mContext, val, &symbolId)) {
1538 return ReportOnCaller(cxhelper, ERROR_ARRAY_ELEMENT,
1539 PromiseFlatCString(aLocation).get(), i);
1542 if (!JS_GetPropertyById(mContext, mod->global, symbolId, &val)) {
1543 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
1544 if (!bytes)
1545 return NS_ERROR_FAILURE;
1546 return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL,
1547 PromiseFlatCString(aLocation).get(),
1548 bytes.ptr());
1551 JSAutoEnterCompartment target_ac;
1553 if (!target_ac.enter(mContext, targetObj) ||
1554 !JS_WrapValue(mContext, &val) ||
1555 !JS_SetPropertyById(mContext, targetObj, symbolId, &val)) {
1556 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
1557 if (!bytes)
1558 return NS_ERROR_FAILURE;
1559 return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL,
1560 PromiseFlatCString(aLocation).get(),
1561 bytes.ptr());
1563 #ifdef DEBUG
1564 if (i == 0) {
1565 logBuffer.AssignLiteral("Installing symbols [ ");
1567 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
1568 if (!!bytes)
1569 logBuffer.Append(bytes.ptr());
1570 logBuffer.AppendLiteral(" ");
1571 if (i == symbolCount - 1) {
1572 LOG(("%s] from %s\n", PromiseFlatCString(logBuffer).get(),
1573 PromiseFlatCString(aLocation).get()));
1575 #endif
1579 // Cache this module for later
1580 if (newEntry) {
1581 if (!mImports.Put(key, newEntry))
1582 return NS_ERROR_OUT_OF_MEMORY;
1583 newEntry.forget();
1586 return NS_OK;
1589 NS_IMETHODIMP
1590 mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
1591 const PRUnichar *data)
1593 if (!strcmp(topic, "xpcom-shutdown-loaders")) {
1594 UnloadModules();
1596 else {
1597 NS_ERROR("Unexpected observer topic.");
1600 return NS_OK;
1603 /* static */ already_AddRefed<nsIFactory>
1604 mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
1605 const mozilla::Module::CIDEntry& entry)
1607 const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
1608 NS_ASSERTION(self.getfactoryobj, "Handing out an uninitialized module?");
1610 nsCOMPtr<nsIFactory> f;
1611 nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
1612 if (NS_FAILED(rv))
1613 return NULL;
1615 return f.forget();
1618 //----------------------------------------------------------------------
1620 JSCLContextHelper::JSCLContextHelper(mozJSComponentLoader *loader)
1621 : mContext(loader->mContext), mContextThread(0),
1622 mContextStack(loader->mContextStack)
1624 mContextStack->Push(mContext);
1625 mContextThread = JS_GetContextThread(mContext);
1626 if (mContextThread) {
1627 JS_BeginRequest(mContext);
1631 // Pops the context that was pushed and then returns the context that is now at
1632 // the top of the stack.
1633 JSContext*
1634 JSCLContextHelper::Pop()
1636 JSContext* cx = nsnull;
1637 if (mContextStack) {
1638 JS_ClearNewbornRoots(mContext);
1639 if (mContextThread) {
1640 JS_EndRequest(mContext);
1643 mContextStack->Pop(nsnull);
1644 mContextStack->Peek(&cx);
1645 mContextStack = nsnull;
1647 return cx;