gecko/extensions/jssh/nsJSSh.cpp

843 lines
25 KiB
C++

/* -*- 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 <alex@croczilla.com>
*
* 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)
{
JSAutoRequest ar(cx);
#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<nsIXPConnectWrappedNative*>(JS_GetPrivate(cx, global));
nsCOMPtr<nsISupports> native;
wrapper->GetNative(getter_AddRefs(native));
nsCOMPtr<nsIJSSh> jssh = do_QueryInterface(native);
NS_ASSERTION(jssh, "no jssh global");
*shell = static_cast<nsJSSh*>((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;
JSAutoRequest ar(cx);
PRUint32 bytesWritten;
#ifdef DEBUG
// nsCOMPtr<nsIThread> 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; i<argc; ++i) {
JSString *str = JS_ValueToString(cx, argv[i]);
if (!str) return JS_FALSE;
if (shell->mOutput) {
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;
JSAutoRequest ar(cx);
if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
for (unsigned int i=0; i<argc; ++i) {
JSString *str = JS_ValueToString(cx, argv[i]);
if (!str) return JS_FALSE;
//argv[i] = STRING_TO_JSVAL(str);
const char *url = JS_GetStringBytes(str);
if (!shell->LoadURL(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<nsIThread> 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;
JSAutoRequest ar(cx);
// 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;
JSAutoRequest ar(cx);
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;
JSAutoRequest ar(cx);
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;
JSAutoRequest ar(cx);
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;
JSAutoRequest ar(cx);
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (!xpc) {
NS_ERROR("failed to get xpconnect service");
return JS_FALSE;
}
nsCOMPtr<nsIXPConnectJSObjectHolder> 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;
JSAutoRequest ar(cx);
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (!xpc) {
NS_ERROR("failed to get xpconnect service");
return JS_FALSE;
}
nsCOMPtr<nsIXPConnectJSObjectHolder> 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<nsIRunnable>
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<nsIJSSh> proxied_shell;
if (!NS_IsMainThread()) {
nsCOMPtr<nsIProxyObjectManager> 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<nsIThread> 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 (mBufferPtr<cBufferSize &&
(mBuffer[mBufferPtr-1]!=10 || (NS_SUCCEEDED(proxied_shell->IsBufferCompilable(&iscompilable)) && !iscompilable)));
NS_ASSERTION(mBufferPtr<cBufferSize, "buffer overflow");
proxied_shell->ExecuteBuffer();
}
}
proxied_shell->Cleanup();
if (!NS_IsMainThread()) {
// Shutdown the current thread, which must be done from the main thread.
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
nsCOMPtr<nsIThread> proxied_thread;
nsCOMPtr<nsIProxyObjectManager> 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<nsIScriptSecurityManager> 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<nsIXPConnect> 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<nsIJSRuntimeService> 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;
}
JSAutoRequest ar(mJSContext);
// 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<nsIXPConnectJSObjectHolder> 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<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (!xpc) {
NS_ERROR("failed to get xpconnect service");
return NS_ERROR_FAILURE;
}
{
JSAutoRequest ar(mJSContext);
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<nsIThread> 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<const PRUnichar*>
(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)
{
JSAutoRequest ar(mJSContext);
*_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;
JSAutoRequest ar(cx);
*_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<nsIIOService> ioserv = do_GetService(NS_IOSERVICE_CONTRACTID);
if (!ioserv) {
NS_ERROR("could not get io service");
return PR_FALSE;
}
nsCOMPtr<nsIChannel> channel;
ioserv->NewChannel(nsDependentCString(url), nsnull, nsnull, getter_AddRefs(channel));
if (!channel) {
NS_ERROR("could not create channel");
return PR_FALSE;
}
nsCOMPtr<nsIInputStream> instream;
channel->Open(getter_AddRefs(instream));
if (!instream) {
NS_ERROR("could not open stream");
return PR_FALSE;
}
nsCString buffer;
nsAutoArrayPtr<char> 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;
}