/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Mozilla JavaScript Shell project. * * The Initial Developer of the Original Code is * Alex Fritze. * Portions created by the Initial Developer are Copyright (C) 2003 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Alex Fritze * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsJSSh.h" #include "nsIJSRuntimeService.h" #include "nsIServiceManager.h" #include "nsIXPConnect.h" #include "nsIProxyObjectManager.h" #include "nsIScriptSecurityManager.h" #include "nsIIOService.h" #include "nsNetCID.h" #include "nsIChannel.h" #include "nsThreadUtils.h" #include "nsServiceManagerUtils.h" #include "nsXPCOMCIDInternal.h" #include "nsMemory.h" #include "nsAutoPtr.h" #ifdef PR_LOGGING PRLogModuleInfo *gJSShLog = PR_NewLogModule("jssh"); #endif //********************************************************************** // Javascript Environment const char *gWelcome = "Welcome to the Mozilla JavaScript Shell!\n"; const char *gGoodbye = "Goodbye!\n"; // GetJSShGlobal: helper for native js functions to obtain the global // JSSh object PRBool GetJSShGlobal(JSContext *cx, JSObject *obj, nsJSSh** shell) { #ifdef DEBUG // printf("GetJSShGlobal(cx=%p, obj=%p)\n", cx, obj); #endif JSObject *parent, *global = obj; if (!global) { NS_ERROR("need a non-null obj to find script global"); return PR_FALSE; } // in most of our cases, 'global' is already the correct obj, since // we use bound methods. For cases where we call // GetJSShGlobal from an unbound method, we need to walk the // parent chain: // XXX I think the comment above is obsolete. We only call // GetJSShGlobal from bound methods, in which case the // parent is the global obj. For non-bound methods, walking the // parent chain to obtain the global object will only work when the // method is rooted in the current script context, so it is not // reliable (???). ASSERTION below to test the assumption. Once proven, // remove loop. while ((parent = JS_GetParent(cx, global))) { NS_ERROR("Parent chain weirdness. Probably benign, but we should not reach this."); #ifdef DEBUG // printf(" obj's parent = %p\n", parent); #endif global = parent; } // JSClass* clazz = JS_GET_CLASS(cx, global); // if (!IS_WRAPPER_CLASS(clazz)) { // NS_ERROR("the script global's class is not of the right type"); // return PR_FALSE; // } // XXX use GetWrappedNativeOfJSObject nsIXPConnectWrappedNative *wrapper = static_cast(JS_GetPrivate(cx, global)); nsCOMPtr native; wrapper->GetNative(getter_AddRefs(native)); nsCOMPtr jssh = do_QueryInterface(native); NS_ASSERTION(jssh, "no jssh global"); *shell = static_cast((nsIJSSh*)(jssh.get())); return PR_TRUE; } JS_STATIC_DLL_CALLBACK(void) my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { // xxx getting the global obj from the cx. will that give us grief? JSObject* obj = JS_GetGlobalObject(cx); nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return; // XXX use JSErrorReport for better info PRUint32 bytesWritten; if (shell->mOutput) { if (shell->mEmitHeader) { char buf[80]; sprintf(buf, "[%d]", strlen(message)); shell->mOutput->Write(buf, strlen(buf), &bytesWritten); } shell->mOutput->Write(message, strlen(message), &bytesWritten); } } JS_STATIC_DLL_CALLBACK(JSBool) Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; PRUint32 bytesWritten; #ifdef DEBUG // nsCOMPtr thread; // nsIThread::GetCurrent(getter_AddRefs(thread)); // printf("printing on thread %p, shell %p, output %p, cx=%p, obj=%p\n", thread.get(), shell, shell->mOutput, cx, obj); #endif for (unsigned int i=0; imOutput) { if (shell->mEmitHeader) { char buf[80]; sprintf(buf, "[%d]", JS_GetStringLength(str)); shell->mOutput->Write(buf, strlen(buf), &bytesWritten); } shell->mOutput->Write(JS_GetStringBytes(str), JS_GetStringLength(str), &bytesWritten); } else printf("%s", JS_GetStringBytes(str)); // use cout if no output stream given. #ifdef DEBUG // printf("%s", JS_GetStringBytes(str)); #endif } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; PRUint32 bytesWritten; if (shell->mOutput) shell->mOutput->Write(gGoodbye, strlen(gGoodbye), &bytesWritten); shell->mQuit = PR_TRUE; return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; for (unsigned int i=0; iLoadURL(url, rval)) return JS_FALSE; } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) FlushEventQueue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; NS_ProcessPendingEvents(nsnull); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Suspend(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; nsCOMPtr thread = do_GetCurrentThread(); PR_AtomicIncrement(&shell->mSuspendCount); while (shell->mSuspendCount) { LOG(("|")); NS_ProcessNextEvent(thread); } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Resume(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; PR_AtomicDecrement(&shell->mSuspendCount); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) AddressOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc!=1) return JS_FALSE; // xxx If argv[0] is not an obj already, we'll get a transient // address from JS_ValueToObject. Maybe we should throw an exception // instead. JSObject *arg_obj; if (!JS_ValueToObject(cx, argv[0], &arg_obj)) { return JS_FALSE; } char buf[80]; sprintf(buf, "%p", arg_obj); JSString *str = JS_NewStringCopyZ(cx, buf); *rval = STRING_TO_JSVAL(str); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) SetProtocol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc!=1) return JS_FALSE; nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; JSString *str = JS_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; char *protocol = JS_GetStringBytes(str); if (!strcmp(protocol, "interactive")) { shell->mEmitHeader = PR_FALSE; shell->mPrompt = NS_LITERAL_CSTRING("\n> "); shell->mProtocol = protocol; } else if (!strcmp(protocol, "synchronous")) { shell->mEmitHeader = PR_TRUE; shell->mPrompt = NS_LITERAL_CSTRING("\n> "); shell->mProtocol = protocol; } else if (!strcmp(protocol, "plain")) { shell->mEmitHeader = PR_FALSE; shell->mPrompt = NS_LITERAL_CSTRING("\n"); shell->mProtocol = protocol; } else return JS_FALSE; return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) GetProtocol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; JSString *str = JS_NewStringCopyZ(cx, shell->mProtocol.get()); *rval = STRING_TO_JSVAL(str); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) SetContextObj(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; if (argc!=1) return JS_FALSE; JSObject *arg_obj; if (!JS_ValueToObject(cx, argv[0], &arg_obj)) { return JS_FALSE; } if (shell->mContextObj != shell->mGlobal) JS_RemoveRoot(cx, &(shell->mContextObj)); shell->mContextObj = arg_obj; if (shell->mContextObj != shell->mGlobal) JS_AddRoot(cx, &(shell->mContextObj)); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) DebugBreak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; NS_BREAK(); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) GetInputStream(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if (!xpc) { NS_ERROR("failed to get xpconnect service"); return JS_FALSE; } nsCOMPtr wrapper; xpc->WrapNative(cx, shell->mGlobal, shell->mInput, NS_GET_IID(nsIInputStream), getter_AddRefs(wrapper)); if (!wrapper) { NS_ERROR("could not wrap input stream object"); return JS_FALSE; } JSObject* wrapper_jsobj = nsnull; wrapper->GetJSObject(&wrapper_jsobj); NS_ASSERTION(wrapper_jsobj, "could not get jsobject of wrapped native"); *rval = OBJECT_TO_JSVAL(wrapper_jsobj); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) GetOutputStream(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { nsJSSh* shell; if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE; nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if (!xpc) { NS_ERROR("failed to get xpconnect service"); return JS_FALSE; } nsCOMPtr wrapper; xpc->WrapNative(cx, shell->mGlobal, shell->mOutput, NS_GET_IID(nsIOutputStream), getter_AddRefs(wrapper)); if (!wrapper) { NS_ERROR("could not wrap output stream object"); return JS_FALSE; } JSObject* wrapper_jsobj = nsnull; wrapper->GetJSObject(&wrapper_jsobj); NS_ASSERTION(wrapper_jsobj, "could not get jsobject of wrapped native"); *rval = OBJECT_TO_JSVAL(wrapper_jsobj); return JS_TRUE; } // these all need JSFUN_BOUND_METHOD flags, so that we can do // things like: // win.p = print, where win is rooted in some other global // object. static JSFunctionSpec global_functions[] = { {"print", Print, 1, JSFUN_BOUND_METHOD, 0}, {"dump", Print, 1, JSFUN_BOUND_METHOD, 0}, {"quit", Quit, 0, JSFUN_BOUND_METHOD, 0}, {"exit", Quit, 0, JSFUN_BOUND_METHOD, 0}, {"load", Load, 1, JSFUN_BOUND_METHOD, 0}, {"suspend", Suspend, 0, JSFUN_BOUND_METHOD, 0}, {"resume", Resume, 0, JSFUN_BOUND_METHOD, 0}, {"flushEventQueue", FlushEventQueue,0, JSFUN_BOUND_METHOD, 0}, {"addressOf", AddressOf, 1, 0, 0}, {"setProtocol", SetProtocol, 1, JSFUN_BOUND_METHOD, 0}, {"getProtocol", GetProtocol, 0, JSFUN_BOUND_METHOD, 0}, {"setContextObj", SetContextObj, 1, JSFUN_BOUND_METHOD, 0}, {"debugBreak", DebugBreak, 0, JSFUN_BOUND_METHOD, 0}, {"getInputStream", GetInputStream, 0, JSFUN_BOUND_METHOD, 0}, {"getOutputStream", GetOutputStream,0, JSFUN_BOUND_METHOD, 0}, {nsnull, nsnull, 0, 0, 0} }; //********************************************************************** // nsJSSh Implementation nsJSSh::nsJSSh(nsIInputStream* input, nsIOutputStream*output, const nsACString &startupURI) : mInput(input), mOutput(output), mQuit(PR_FALSE), mStartupURI(startupURI), mSuspendCount(0), mPrompt("\n> "), mEmitHeader(PR_FALSE), mProtocol("interactive") { } nsJSSh::~nsJSSh() { LOG(("JSSh: ~connection!\n")); } already_AddRefed CreateJSSh(nsIInputStream* input, nsIOutputStream*output, const nsACString &startupURI) { nsIRunnable* obj = new nsJSSh(input, output, startupURI); NS_IF_ADDREF(obj); return obj; } //---------------------------------------------------------------------- // nsISupports methods: NS_IMPL_THREADSAFE_ADDREF(nsJSSh) NS_IMPL_THREADSAFE_RELEASE(nsJSSh) NS_INTERFACE_MAP_BEGIN(nsJSSh) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSSh) NS_INTERFACE_MAP_ENTRY(nsIRunnable) NS_INTERFACE_MAP_ENTRY(nsIJSSh) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) NS_INTERFACE_MAP_END //---------------------------------------------------------------------- // nsIRunnable methods: NS_IMETHODIMP nsJSSh::Run() { nsCOMPtr proxied_shell; if (!NS_IsMainThread()) { nsCOMPtr pom = do_GetService(NS_XPCOMPROXY_CONTRACTID); NS_ASSERTION(pom, "uh-oh, no proxy object manager!"); pom->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD, NS_GET_IID(nsIJSSh), (nsIJSSh*)this, NS_PROXY_SYNC, getter_AddRefs(proxied_shell)); } else { LOG(("jssh shell will block main thread!\n")); proxied_shell = this; } proxied_shell->Init(); if (mInput) { // read-eval-print loop PRUint32 bytesWritten; if (mOutput && !mProtocol.Equals(NS_LITERAL_CSTRING("plain"))) mOutput->Write(gWelcome, strlen(gWelcome), &bytesWritten); while (!mQuit) { if (mOutput) mOutput->Write(mPrompt.get(), mPrompt.Length(), &bytesWritten); // accumulate input until we get a compilable unit: PRBool iscompilable; mBufferPtr = 0; #ifdef DEBUG // nsCOMPtr thread; // nsIThread::GetCurrent(getter_AddRefs(thread)); // printf("blocking on thread %p\n", thread.get()); #endif do { PRUint32 bytesRead = 0; mInput->Read(mBuffer+mBufferPtr, 1, &bytesRead); if (bytesRead) { ++mBufferPtr; } else { // connection was terminated by the client mQuit = PR_TRUE; break; } // XXX signal buffer overflow ?? // XXX ideally we want a dynamically resizing buffer. }while (mBufferPtrIsBufferCompilable(&iscompilable)) && !iscompilable))); NS_ASSERTION(mBufferPtrExecuteBuffer(); } } proxied_shell->Cleanup(); if (!NS_IsMainThread()) { // Shutdown the current thread, which must be done from the main thread. nsCOMPtr thread = do_GetCurrentThread(); nsCOMPtr proxied_thread; nsCOMPtr pom = do_GetService(NS_XPCOMPROXY_CONTRACTID); NS_ASSERTION(pom, "uh-oh, no proxy object manager!"); pom->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD, NS_GET_IID(nsIThread), thread.get(), NS_PROXY_ASYNC, getter_AddRefs(proxied_thread)); if (proxied_thread) proxied_thread->Shutdown(); } return NS_OK; } //---------------------------------------------------------------------- // nsIJSSh methods NS_IMETHODIMP nsJSSh::Init() { nsCOMPtr ssm = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); if (!ssm) { NS_ERROR("failed to get script security manager"); return NS_ERROR_FAILURE; } ssm->GetSystemPrincipal(getter_AddRefs(mPrincipal)); if (!mPrincipal) { NS_ERROR("failed to get system principal"); return NS_ERROR_FAILURE; } nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if (!xpc) { NS_ERROR("failed to get xpconnect service"); return NS_ERROR_FAILURE; } // Let xpconnect resync its JSContext tracker. We do this before creating // a new JSContext just in case the heap manager recycles the JSContext // struct. xpc->SyncJSContexts(); nsCOMPtr rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); // get the JSRuntime from the runtime svc if (!rtsvc) { NS_ERROR("failed to get nsJSRuntimeService"); return NS_ERROR_FAILURE; } JSRuntime *rt = nsnull; if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) { NS_ERROR("failed to get JSRuntime from nsJSRuntimeService"); return NS_ERROR_FAILURE; } mJSContext = JS_NewContext(rt, 8192); if (!mJSContext) { NS_ERROR("JS_NewContext failed"); return NS_ERROR_FAILURE; } // Enable e4x: JS_SetOptions(mJSContext, JS_GetOptions(mJSContext) | JSOPTION_XML); // Always use the latest js version JS_SetVersion(mJSContext, JSVERSION_LATEST); mContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1"); if (!mContextStack) { NS_ERROR("failed to get the nsThreadJSContextStack service"); return NS_ERROR_FAILURE; } JS_SetErrorReporter(mJSContext, my_ErrorReporter); nsCOMPtr holder; xpc->InitClassesWithNewWrappedGlobal(mJSContext, (nsIJSSh*)this, NS_GET_IID(nsISupports), PR_TRUE, getter_AddRefs(holder)); if (!holder) { NS_ERROR("global initialization failed"); return NS_ERROR_FAILURE; } holder->GetJSObject(&mGlobal); if (!mGlobal) { NS_ERROR("bad global object"); return NS_ERROR_FAILURE; } mContextObj = mGlobal; if (!JS_DefineFunctions(mJSContext, mGlobal, global_functions)) { NS_ERROR("failed to initialise global functions"); return NS_ERROR_FAILURE; } if (!mStartupURI.IsEmpty()) LoadURL(mStartupURI.get()); return NS_OK; } NS_IMETHODIMP nsJSSh::Cleanup() { nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if (!xpc) { NS_ERROR("failed to get xpconnect service"); return NS_ERROR_FAILURE; } if (mContextObj != mGlobal) JS_RemoveRoot(mJSContext, &(mContextObj)); JS_ClearScope(mJSContext, mGlobal); JS_GC(mJSContext); JS_DestroyContext(mJSContext); xpc->SyncJSContexts(); return NS_OK; } NS_IMETHODIMP nsJSSh::ExecuteBuffer() { #ifdef DEBUG // nsCOMPtr thread; // nsIThread::GetCurrent(getter_AddRefs(thread)); // printf("executing on thread %p\n", thread.get()); #endif JS_BeginRequest(mJSContext); JS_ClearPendingException(mJSContext); JSPrincipals *jsprincipals; mPrincipal->GetJSPrincipals(mJSContext, &jsprincipals); if(NS_FAILED(mContextStack->Push(mJSContext))) { NS_ERROR("failed to push the current JSContext on the nsThreadJSContextStack"); return NS_ERROR_FAILURE; } JSScript *script = JS_CompileScriptForPrincipals(mJSContext, mContextObj, jsprincipals, mBuffer, mBufferPtr, "interactive", 0); if (script) { jsval result; if (JS_ExecuteScript(mJSContext, mContextObj, script, &result) && result!=JSVAL_VOID && mOutput) { // XXX for some wrapped native objects the following code will crash; probably because the // native object is getting released before we reach this: JSString *str = JS_ValueToString(mJSContext, result); if (str) { nsDependentString chars(reinterpret_cast (JS_GetStringChars(str)), JS_GetStringLength(str)); NS_ConvertUTF16toUTF8 cstr(chars); PRUint32 bytesWritten; mOutput->Write(cstr.get(), cstr.Length(), &bytesWritten); } } JS_DestroyScript(mJSContext, script); } JSContext *oldcx; mContextStack->Pop(&oldcx); NS_ASSERTION(oldcx == mJSContext, "JS thread context push/pop mismatch"); JSPRINCIPALS_DROP(mJSContext, jsprincipals); JS_EndRequest(mJSContext); return NS_OK; } NS_IMETHODIMP nsJSSh::IsBufferCompilable(PRBool *_retval) { *_retval = JS_BufferIsCompilableUnit(mJSContext, mContextObj, mBuffer, mBufferPtr); return NS_OK; } //---------------------------------------------------------------------- // nsIScriptObjectPrincipal methods: nsIPrincipal * nsJSSh::GetPrincipal() { return mPrincipal; } //---------------------------------------------------------------------- // nsIXPCScriptable methods: #define XPC_MAP_CLASSNAME nsJSSh #define XPC_MAP_QUOTED_CLASSNAME "JSSh" #define XPC_MAP_WANT_NEWRESOLVE #define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY | \ nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY | \ nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY | \ nsIXPCScriptable::DONT_ENUM_STATIC_PROPS | \ nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \ nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES #include "xpc_map_end.h" /* This will #undef the above */ /* PRBool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in PRUint32 flags, out JSObjectPtr objp); */ NS_IMETHODIMP nsJSSh::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, jsval id, PRUint32 flags, JSObject * *objp, PRBool *_retval) { JSBool resolved; *_retval = JS_ResolveStandardClass(cx, obj, id, &resolved); if (*_retval && resolved) *objp = obj; return NS_OK; } //---------------------------------------------------------------------- // Implementation helpers: PRBool nsJSSh::LoadURL(const char *url, jsval* retval) { nsCOMPtr ioserv = do_GetService(NS_IOSERVICE_CONTRACTID); if (!ioserv) { NS_ERROR("could not get io service"); return PR_FALSE; } nsCOMPtr channel; ioserv->NewChannel(nsDependentCString(url), nsnull, nsnull, getter_AddRefs(channel)); if (!channel) { NS_ERROR("could not create channel"); return PR_FALSE; } nsCOMPtr instream; channel->Open(getter_AddRefs(instream)); if (!instream) { NS_ERROR("could not open stream"); return PR_FALSE; } nsCString buffer; nsAutoArrayPtr buf(new char[1024]); if (!buf) { NS_ERROR("could not alloc buffer"); return PR_FALSE; } PRUint32 bytesRead = 0; do { if (NS_FAILED(instream->Read(buf, 1024, &bytesRead))) { NS_ERROR("stream read error"); return PR_FALSE; } buffer.Append(buf, bytesRead); LOG(("appended %d bytes:\n%s", bytesRead, buffer.get())); } while (bytesRead > 0); LOG(("loaded %d bytes:\n%s", buffer.Length(), buffer.get())); JS_BeginRequest(mJSContext); JSPrincipals *jsprincipals; mPrincipal->GetJSPrincipals(mJSContext, &jsprincipals); if(NS_FAILED(mContextStack->Push(mJSContext))) { NS_ERROR("failed to push the current JSContext on the nsThreadJSContextStack"); return NS_ERROR_FAILURE; } jsval result; JSBool ok = JS_EvaluateScriptForPrincipals(mJSContext, mContextObj, jsprincipals, buffer.get(), buffer.Length(), url, 1, &result); JSPRINCIPALS_DROP(mJSContext, jsprincipals); JSContext *oldcx; mContextStack->Pop(&oldcx); NS_ASSERTION(oldcx == mJSContext, "JS thread context push/pop mismatch"); if (ok && retval) *retval=result; JS_EndRequest(mJSContext); return ok; }