mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
811 lines
24 KiB
C++
811 lines
24 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"
|
|
|
|
//**********************************************************************
|
|
// 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<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;
|
|
|
|
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(JS_GetStringBytes(str)); // use cout if no output stream given.
|
|
#ifdef DEBUG
|
|
// printf(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; 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) {
|
|
#ifdef DEBUG
|
|
printf("|");
|
|
#endif
|
|
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<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;
|
|
|
|
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()
|
|
{
|
|
#ifdef DEBUG
|
|
printf("JSSh: ~connection!\n");
|
|
#endif
|
|
}
|
|
|
|
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 {
|
|
#ifdef DEBUG
|
|
printf("jssh shell will block main thread!\n");
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
// Enable e4x:
|
|
JS_SetOptions(mJSContext, JS_GetOptions(mJSContext) | JSOPTION_XML);
|
|
|
|
// Enable js1.7 features (let, yield, etc - see bug#351515)
|
|
JS_SetVersion(mJSContext, JSVERSION_1_7);
|
|
|
|
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;
|
|
}
|
|
|
|
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) {
|
|
PRUint32 bytesWritten;
|
|
mOutput->Write(JS_GetStringBytes(str), JS_GetStringLength(str), &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<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;
|
|
}
|
|
|
|
PRInt32 content_length=-1;
|
|
if (NS_FAILED(channel->GetContentLength(&content_length))) {
|
|
NS_ERROR("could not get content length");
|
|
return PR_FALSE;
|
|
}
|
|
|
|
char *buf = new char[content_length+1];
|
|
if (!buf) {
|
|
NS_ERROR("could not alloc buffer");
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRUint32 bytesRead=0;
|
|
instream->Read(buf, content_length, &bytesRead);
|
|
if (bytesRead!=content_length) {
|
|
NS_ERROR("stream read error");
|
|
return PR_FALSE;
|
|
}
|
|
|
|
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, buf, content_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);
|
|
|
|
delete[] buf;
|
|
|
|
return ok;
|
|
}
|
|
|