gecko/ipc/testshell/XPCShellEnvironment.cpp

1108 lines
29 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stdlib.h>
#include <errno.h>
#ifdef HAVE_IO_H
#include <io.h> /* for isatty() */
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for isatty() */
#endif
#include "base/basictypes.h"
#include "jsapi.h"
#include "jsdbgapi.h"
#include "jsprf.h"
#include "xpcpublic.h"
#include "XPCShellEnvironment.h"
#include "mozilla/XPCOM.h"
#include "nsIChannel.h"
#include "nsIClassInfo.h"
#include "nsIDirectoryService.h"
#include "nsIJSRuntimeService.h"
#include "nsIPrincipal.h"
#include "nsIScriptSecurityManager.h"
#include "nsIURI.h"
#include "nsIXPConnect.h"
#include "nsIXPCScriptable.h"
#include "nsContentUtils.h"
#include "nsCxPusher.h"
#include "nsJSUtils.h"
#include "nsJSPrincipals.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "BackstagePass.h"
#include "TestShellChild.h"
#include "TestShellParent.h"
#define EXITCODE_RUNTIME_ERROR 3
#define EXITCODE_FILE_NOT_FOUND 4
using mozilla::ipc::XPCShellEnvironment;
using mozilla::ipc::TestShellChild;
using mozilla::ipc::TestShellParent;
namespace {
static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js";
class FullTrustSecMan : public nsIScriptSecurityManager
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCSECURITYMANAGER
NS_DECL_NSISCRIPTSECURITYMANAGER
FullTrustSecMan() { }
virtual ~FullTrustSecMan() { }
void SetSystemPrincipal(nsIPrincipal *aPrincipal) {
mSystemPrincipal = aPrincipal;
}
private:
nsCOMPtr<nsIPrincipal> mSystemPrincipal;
};
class XPCShellDirProvider : public nsIDirectoryServiceProvider
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDIRECTORYSERVICEPROVIDER
XPCShellDirProvider() { }
~XPCShellDirProvider() { }
bool SetGREDir(const char *dir);
void ClearGREDir() { mGREDir = nullptr; }
private:
nsCOMPtr<nsIFile> mGREDir;
};
inline XPCShellEnvironment*
Environment(JSContext* cx)
{
XPCShellEnvironment* env =
static_cast<XPCShellEnvironment*>(JS_GetContextPrivate(cx));
NS_ASSERTION(env, "Should never be null!");
return env;
}
static void
ScriptErrorReporter(JSContext *cx,
const char *message,
JSErrorReport *report)
{
int i, j, k, n;
char *prefix = NULL, *tmp;
const char *ctmp;
nsCOMPtr<nsIXPConnect> xpc;
// Don't report an exception from inner JS frames as the callers may intend
// to handle it.
if (JS_DescribeScriptedCaller(cx, nullptr, nullptr)) {
return;
}
// In some cases cx->fp is null here so use XPConnect to tell us about inner
// frames.
if ((xpc = do_GetService(nsIXPConnect::GetCID()))) {
nsAXPCNativeCallContext *cc = nullptr;
xpc->GetCurrentNativeCallContext(&cc);
if (cc) {
nsAXPCNativeCallContext *prev = cc;
while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
uint16_t lang;
if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
lang == nsAXPCNativeCallContext::LANG_JS) {
return;
}
}
}
}
if (!report) {
fprintf(stderr, "%s\n", message);
return;
}
/* Conditionally ignore reported warnings. */
if (JSREPORT_IS_WARNING(report->flags) &&
!Environment(cx)->ShouldReportWarnings()) {
return;
}
if (report->filename)
prefix = JS_smprintf("%s:", report->filename);
if (report->lineno) {
tmp = prefix;
prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
JS_free(cx, tmp);
}
if (JSREPORT_IS_WARNING(report->flags)) {
tmp = prefix;
prefix = JS_smprintf("%s%swarning: ",
tmp ? tmp : "",
JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
JS_free(cx, tmp);
}
/* embedded newlines -- argh! */
while ((ctmp = strchr(message, '\n')) != 0) {
ctmp++;
if (prefix) fputs(prefix, stderr);
fwrite(message, 1, ctmp - message, stderr);
message = ctmp;
}
/* If there were no filename or lineno, the prefix might be empty */
if (prefix)
fputs(prefix, stderr);
fputs(message, stderr);
if (!report->linebuf) {
fputc('\n', stderr);
goto out;
}
fprintf(stderr, ":\n%s%s\n%s", prefix, report->linebuf, prefix);
n = report->tokenptr - report->linebuf;
for (i = j = 0; i < n; i++) {
if (report->linebuf[i] == '\t') {
for (k = (j + 8) & ~7; j < k; j++) {
fputc('.', stderr);
}
continue;
}
fputc('.', stderr);
j++;
}
fputs("^\n", stderr);
out:
if (!JSREPORT_IS_WARNING(report->flags)) {
Environment(cx)->SetExitCode(EXITCODE_RUNTIME_ERROR);
}
JS_free(cx, prefix);
}
JSContextCallback gOldContextCallback = NULL;
static JSBool
ContextCallback(JSContext *cx,
unsigned contextOp)
{
if (gOldContextCallback && !gOldContextCallback(cx, contextOp))
return JS_FALSE;
if (contextOp == JSCONTEXT_NEW) {
JS_SetErrorReporter(cx, ScriptErrorReporter);
JS_SetVersion(cx, JSVERSION_LATEST);
}
return JS_TRUE;
}
static JSBool
Print(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
unsigned i, n;
JSString *str;
JS::Value *argv = JS_ARGV(cx, vp);
for (i = n = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
JSAutoByteString bytes(cx, str);
if (!bytes)
return JS_FALSE;
fprintf(stdout, "%s%s", i ? " " : "", bytes.ptr());
fflush(stdout);
}
n++;
if (n)
fputc('\n', stdout);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
static JSBool
GetLine(char *bufp,
FILE *file,
const char *prompt)
{
char line[256];
fputs(prompt, stdout);
fflush(stdout);
if (!fgets(line, sizeof line, file))
return JS_FALSE;
strcpy(bufp, line);
return JS_TRUE;
}
static JSBool
Dump(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
JS_SET_RVAL(cx, vp, JSVAL_VOID);
JSString *str;
if (!argc)
return JS_TRUE;
str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
if (!str)
return JS_FALSE;
JSAutoByteString bytes(cx, str);
if (!bytes)
return JS_FALSE;
fputs(bytes.ptr(), stdout);
fflush(stdout);
return JS_TRUE;
}
static JSBool
Load(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
JS::Rooted<JS::Value> result(cx);
JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
if (!obj)
return JS_FALSE;
JS::Value *argv = JS_ARGV(cx, vp);
for (unsigned i = 0; i < argc; i++) {
JSString *str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
argv[i] = STRING_TO_JSVAL(str);
JSAutoByteString filename(cx, str);
if (!filename)
return JS_FALSE;
FILE *file = fopen(filename.ptr(), "r");
if (!file) {
JS_ReportError(cx, "cannot open file '%s' for reading", filename.ptr());
return JS_FALSE;
}
JS::CompileOptions options(cx);
options.setUTF8(true)
.setFileAndLine(filename.ptr(), 1)
.setPrincipals(Environment(cx)->GetPrincipal());
JS::RootedObject rootedObj(cx, obj);
JSScript *script = JS::Compile(cx, rootedObj, options, file);
fclose(file);
if (!script)
return JS_FALSE;
if (!Environment(cx)->ShouldCompileOnly() &&
!JS_ExecuteScript(cx, obj, script, result.address())) {
return JS_FALSE;
}
}
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
static JSBool
Version(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
JS::Value *argv = JS_ARGV(cx, vp);
if (argc > 0 && JSVAL_IS_INT(argv[0]))
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(JSVAL_TO_INT(argv[0])))));
else
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(JS_GetVersion(cx)));
return JS_TRUE;
}
static JSBool
BuildDate(JSContext *cx, unsigned argc, JS::Value *vp)
{
fprintf(stdout, "built on %s at %s\n", __DATE__, __TIME__);
return JS_TRUE;
}
static JSBool
Quit(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
int exitCode = 0;
JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/ i", &exitCode);
XPCShellEnvironment* env = Environment(cx);
env->SetExitCode(exitCode);
env->SetIsQuitting();
return JS_FALSE;
}
static JSBool
DumpXPC(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
int32_t depth = 2;
if (argc > 0) {
if (!JS_ValueToInt32(cx, JS_ARGV(cx, vp)[0], &depth))
return JS_FALSE;
}
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if(xpc)
xpc->DebugDump(int16_t(depth));
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
static JSBool
GC(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
JSRuntime *rt = JS_GetRuntime(cx);
JS_GC(rt);
#ifdef JS_GCMETER
js_DumpGCStats(rt, stdout);
#endif
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
#ifdef JS_GC_ZEAL
static JSBool
GCZeal(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
JS::Value* argv = JS_ARGV(cx, vp);
uint32_t zeal;
if (!JS_ValueToECMAUint32(cx, argv[0], &zeal))
return JS_FALSE;
JS_SetGCZeal(cx, uint8_t(zeal), JS_DEFAULT_ZEAL_FREQ);
return JS_TRUE;
}
#endif
#ifdef DEBUG
static JSBool
DumpHeap(JSContext *cx,
unsigned argc,
JS::Value *vp)
{
JSAutoByteString fileName;
void* startThing = NULL;
JSGCTraceKind startTraceKind = JSTRACE_OBJECT;
void *thingToFind = NULL;
size_t maxDepth = (size_t)-1;
void *thingToIgnore = NULL;
FILE *dumpFile;
JSBool ok;
JS::Value *argv = JS_ARGV(cx, vp);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
vp = argv + 0;
if (argc > 0 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
JSString *str;
str = JS_ValueToString(cx, *vp);
if (!str)
return JS_FALSE;
*vp = STRING_TO_JSVAL(str);
if (!fileName.encodeLatin1(cx, str))
return JS_FALSE;
}
vp = argv + 1;
if (argc > 1 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
if (!JSVAL_IS_TRACEABLE(*vp))
goto not_traceable_arg;
startThing = JSVAL_TO_TRACEABLE(*vp);
startTraceKind = JSVAL_TRACE_KIND(*vp);
}
vp = argv + 2;
if (argc > 2 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
if (!JSVAL_IS_TRACEABLE(*vp))
goto not_traceable_arg;
thingToFind = JSVAL_TO_TRACEABLE(*vp);
}
vp = argv + 3;
if (argc > 3 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
uint32_t depth;
if (!JS_ValueToECMAUint32(cx, *vp, &depth))
return JS_FALSE;
maxDepth = depth;
}
vp = argv + 4;
if (argc > 4 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
if (!JSVAL_IS_TRACEABLE(*vp))
goto not_traceable_arg;
thingToIgnore = JSVAL_TO_TRACEABLE(*vp);
}
if (!fileName) {
dumpFile = stdout;
} else {
dumpFile = fopen(fileName.ptr(), "w");
if (!dumpFile) {
fprintf(stderr, "dumpHeap: can't open %s: %s\n",
fileName.ptr(), strerror(errno));
return JS_FALSE;
}
}
ok = JS_DumpHeap(JS_GetRuntime(cx), dumpFile, startThing, startTraceKind, thingToFind,
maxDepth, thingToIgnore);
if (dumpFile != stdout)
fclose(dumpFile);
if (!ok)
JS_ReportOutOfMemory(cx);
return ok;
not_traceable_arg:
fprintf(stderr,
"dumpHeap: argument %u is not null or a heap-allocated thing\n",
(unsigned)(vp - argv));
return JS_FALSE;
}
#endif /* DEBUG */
const JSFunctionSpec gGlobalFunctions[] =
{
JS_FS("print", Print, 0,0),
JS_FS("load", Load, 1,0),
JS_FS("quit", Quit, 0,0),
JS_FS("version", Version, 1,0),
JS_FS("build", BuildDate, 0,0),
JS_FS("dumpXPC", DumpXPC, 1,0),
JS_FS("dump", Dump, 1,0),
JS_FS("gc", GC, 0,0),
#ifdef JS_GC_ZEAL
JS_FS("gczeal", GCZeal, 1,0),
#endif
#ifdef DEBUG
JS_FS("dumpHeap", DumpHeap, 5,0),
#endif
JS_FS_END
};
typedef enum JSShellErrNum
{
#define MSG_DEF(name, number, count, exception, format) \
name = number,
#include "jsshell.msg"
#undef MSG_DEF
JSShellErr_Limit
#undef MSGDEF
} JSShellErrNum;
static void
ProcessFile(JSContext *cx,
JS::Handle<JSObject*> obj,
const char *filename,
FILE *file,
JSBool forceTTY)
{
XPCShellEnvironment* env = Environment(cx);
nsCxPusher pusher;
pusher.Push(env->GetContext());
JSScript *script;
JS::Rooted<JS::Value> result(cx);
int lineno, startline;
JSBool ok, hitEOF;
char *bufp, buffer[4096];
JSString *str;
if (forceTTY) {
file = stdin;
}
else
#ifdef HAVE_ISATTY
if (!isatty(fileno(file)))
#endif
{
/*
* It's not interactive - just execute it.
*
* Support the UNIX #! shell hack; gobble the first line if it starts
* with '#'. TODO - this isn't quite compatible with sharp variables,
* as a legal js program (using sharp variables) might start with '#'.
* But that would require multi-character lookahead.
*/
int ch = fgetc(file);
if (ch == '#') {
while((ch = fgetc(file)) != EOF) {
if(ch == '\n' || ch == '\r')
break;
}
}
ungetc(ch, file);
JSAutoRequest ar(cx);
JSAutoCompartment ac(cx, obj);
JS::CompileOptions options(cx);
options.setUTF8(true)
.setFileAndLine(filename, 1)
.setPrincipals(env->GetPrincipal());
JSScript* script = JS::Compile(cx, obj, options, file);
if (script && !env->ShouldCompileOnly())
(void)JS_ExecuteScript(cx, obj, script, result.address());
return;
}
/* It's an interactive filehandle; drop into read-eval-print loop. */
lineno = 1;
hitEOF = JS_FALSE;
do {
bufp = buffer;
*bufp = '\0';
JSAutoRequest ar(cx);
JSAutoCompartment ac(cx, obj);
/*
* Accumulate lines until we get a 'compilable unit' - one that either
* generates an error (before running out of source) or that compiles
* cleanly. This should be whenever we get a complete statement that
* coincides with the end of a line.
*/
startline = lineno;
do {
if (!GetLine(bufp, file, startline == lineno ? "js> " : "")) {
hitEOF = JS_TRUE;
break;
}
bufp += strlen(bufp);
lineno++;
} while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
/* Clear any pending exception from previous failed compiles. */
JS_ClearPendingException(cx);
script =
JS_CompileScriptForPrincipals(cx, obj, env->GetPrincipal(), buffer,
strlen(buffer), "typein", startline);
if (script) {
JSErrorReporter older;
if (!env->ShouldCompileOnly()) {
ok = JS_ExecuteScript(cx, obj, script, result.address());
if (ok && result != JSVAL_VOID) {
/* Suppress error reports from JS_ValueToString(). */
older = JS_SetErrorReporter(cx, NULL);
str = JS_ValueToString(cx, result);
JSAutoByteString bytes;
if (str)
bytes.encodeLatin1(cx, str);
JS_SetErrorReporter(cx, older);
if (!!bytes)
fprintf(stdout, "%s\n", bytes.ptr());
else
ok = JS_FALSE;
}
}
}
} while (!hitEOF && !env->IsQuitting());
fprintf(stdout, "\n");
}
} /* anonymous namespace */
NS_INTERFACE_MAP_BEGIN(FullTrustSecMan)
NS_INTERFACE_MAP_ENTRY(nsIXPCSecurityManager)
NS_INTERFACE_MAP_ENTRY(nsIScriptSecurityManager)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCSecurityManager)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(FullTrustSecMan)
NS_IMPL_RELEASE(FullTrustSecMan)
NS_IMETHODIMP
FullTrustSecMan::CanCreateWrapper(JSContext * aJSContext,
const nsIID & aIID,
nsISupports *aObj,
nsIClassInfo *aClassInfo,
void * *aPolicy)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CanCreateInstance(JSContext * aJSContext,
const nsCID & aCID)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CanGetService(JSContext * aJSContext,
const nsCID & aCID)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CanAccess(uint32_t aAction,
nsAXPCNativeCallContext *aCallContext,
JSContext * aJSContext,
JSObject * aJSObject,
nsISupports *aObj,
nsIClassInfo *aClassInfo,
jsid aName,
void * *aPolicy)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CheckPropertyAccess(JSContext * aJSContext,
JSObject * aJSObject,
const char *aClassName,
jsid aProperty,
uint32_t aAction)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CheckLoadURIFromScript(JSContext * cx,
nsIURI *uri)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CheckLoadURIWithPrincipal(nsIPrincipal *aPrincipal,
nsIURI *uri,
uint32_t flags)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CheckLoadURIStrWithPrincipal(nsIPrincipal *aPrincipal,
const nsACString & uri,
uint32_t flags)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CheckFunctionAccess(JSContext * cx,
void * funObj,
void * targetObj)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CanExecuteScripts(JSContext * cx,
nsIPrincipal *principal,
bool *_retval)
{
*_retval = true;
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::GetSubjectPrincipal(nsIPrincipal **_retval)
{
NS_IF_ADDREF(*_retval = mSystemPrincipal);
return *_retval ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
FullTrustSecMan::GetSystemPrincipal(nsIPrincipal **_retval)
{
NS_IF_ADDREF(*_retval = mSystemPrincipal);
return *_retval ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
FullTrustSecMan::GetSimpleCodebasePrincipal(nsIURI *aURI,
nsIPrincipal **_retval)
{
NS_IF_ADDREF(*_retval = mSystemPrincipal);
return *_retval ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
FullTrustSecMan::GetNoAppCodebasePrincipal(nsIURI *aURI,
nsIPrincipal **_retval)
{
return GetSimpleCodebasePrincipal(aURI, _retval);
}
NS_IMETHODIMP
FullTrustSecMan::GetCodebasePrincipal(nsIURI *aURI, nsIPrincipal **_retval)
{
return GetSimpleCodebasePrincipal(aURI, _retval);
}
NS_IMETHODIMP
FullTrustSecMan::GetAppCodebasePrincipal(nsIURI *aURI,
uint32_t aAppId,
bool aInMozBrowser,
nsIPrincipal **_retval)
{
return GetSimpleCodebasePrincipal(aURI, _retval);
}
NS_IMETHODIMP
FullTrustSecMan::GetDocShellCodebasePrincipal(nsIURI *aURI,
nsIDocShell* aDocShell,
nsIPrincipal **_retval)
{
return GetSimpleCodebasePrincipal(aURI, _retval);
}
NS_IMETHODIMP
FullTrustSecMan::GetObjectPrincipal(JSContext * cx,
JSObject * obj,
nsIPrincipal **_retval)
{
NS_IF_ADDREF(*_retval = mSystemPrincipal);
return *_retval ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
FullTrustSecMan::SubjectPrincipalIsSystem(bool *_retval)
{
*_retval = true;
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CheckSameOrigin(JSContext * aJSContext,
nsIURI *aTargetURI)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::CheckSameOriginURI(nsIURI *aSourceURI,
nsIURI *aTargetURI,
bool reportError)
{
return NS_OK;
}
NS_IMETHODIMP
FullTrustSecMan::GetChannelPrincipal(nsIChannel *aChannel,
nsIPrincipal **_retval)
{
NS_IF_ADDREF(*_retval = mSystemPrincipal);
return *_retval ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
FullTrustSecMan::IsSystemPrincipal(nsIPrincipal *aPrincipal,
bool *_retval)
{
*_retval = aPrincipal == mSystemPrincipal;
return NS_OK;
}
NS_IMETHODIMP_(nsIPrincipal *)
FullTrustSecMan::GetCxSubjectPrincipal(JSContext *cx)
{
return mSystemPrincipal;
}
NS_IMETHODIMP
FullTrustSecMan::GetExtendedOrigin(nsIURI* aURI, uint32_t aAppId,
bool aInMozBrowser,
nsACString& aExtendedOrigin)
{
aExtendedOrigin.Truncate();
return NS_OK;
}
NS_IMETHODIMP_(nsrefcnt)
XPCShellDirProvider::AddRef()
{
return 2;
}
NS_IMETHODIMP_(nsrefcnt)
XPCShellDirProvider::Release()
{
return 1;
}
NS_IMPL_QUERY_INTERFACE1(XPCShellDirProvider, nsIDirectoryServiceProvider)
bool
XPCShellDirProvider::SetGREDir(const char *dir)
{
nsresult rv = XRE_GetFileFromPath(dir, getter_AddRefs(mGREDir));
return NS_SUCCEEDED(rv);
}
NS_IMETHODIMP
XPCShellDirProvider::GetFile(const char *prop,
bool *persistent,
nsIFile* *result)
{
if (mGREDir && !strcmp(prop, NS_GRE_DIR)) {
*persistent = true;
NS_ADDREF(*result = mGREDir);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
// static
XPCShellEnvironment*
XPCShellEnvironment::CreateEnvironment()
{
XPCShellEnvironment* env = new XPCShellEnvironment();
if (env && !env->Init()) {
delete env;
env = nullptr;
}
return env;
}
XPCShellEnvironment::XPCShellEnvironment()
: mCx(NULL),
mJSPrincipals(NULL),
mExitCode(0),
mQuitting(JS_FALSE),
mReportWarnings(JS_TRUE),
mCompileOnly(JS_FALSE)
{
}
XPCShellEnvironment::~XPCShellEnvironment()
{
if (mCx) {
JS_BeginRequest(mCx);
JSObject* global = GetGlobalObject();
if (global) {
JS_SetAllNonReservedSlotsToUndefined(mCx, global);
}
mGlobalHolder.Release();
JSRuntime *rt = JS_GetRuntime(mCx);
JS_GC(rt);
if (mJSPrincipals) {
JS_DropPrincipals(rt, mJSPrincipals);
}
JS_EndRequest(mCx);
JS_DestroyContext(mCx);
if (gOldContextCallback) {
NS_ASSERTION(rt, "Should never be null!");
JS_SetContextCallback(rt, gOldContextCallback);
gOldContextCallback = NULL;
}
}
}
bool
XPCShellEnvironment::Init()
{
nsresult rv;
#ifdef HAVE_SETBUF
// unbuffer stdout so that output is in the correct order; note that stderr
// is unbuffered by default
setbuf(stdout, 0);
#endif
nsCOMPtr<nsIJSRuntimeService> rtsvc =
do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
if (!rtsvc) {
NS_ERROR("failed to get nsJSRuntimeService!");
return false;
}
JSRuntime *rt;
if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
NS_ERROR("failed to get JSRuntime from nsJSRuntimeService!");
return false;
}
if (!mGlobalHolder.Hold(rt)) {
NS_ERROR("Can't protect global object!");
return false;
}
gOldContextCallback = JS_SetContextCallback(rt, ContextCallback);
JSContext *cx = JS_NewContext(rt, 8192);
if (!cx) {
NS_ERROR("JS_NewContext failed!");
JS_SetContextCallback(rt, gOldContextCallback);
gOldContextCallback = NULL;
return false;
}
mCx = cx;
JS_SetContextPrivate(cx, this);
nsCOMPtr<nsIXPConnect> xpc =
do_GetService(nsIXPConnect::GetCID());
if (!xpc) {
NS_ERROR("failed to get nsXPConnect service!");
return false;
}
nsRefPtr<FullTrustSecMan> secman(new FullTrustSecMan());
xpc->SetSecurityManagerForJSContext(cx, secman, 0xFFFF);
nsCOMPtr<nsIPrincipal> principal;
nsCOMPtr<nsIScriptSecurityManager> securityManager =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && securityManager) {
rv = securityManager->GetSystemPrincipal(getter_AddRefs(principal));
if (NS_FAILED(rv)) {
fprintf(stderr, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n");
} else {
// fetch the JS principals and stick in a global
mJSPrincipals = nsJSPrincipals::get(principal);
JS_HoldPrincipals(mJSPrincipals);
secman->SetSystemPrincipal(principal);
}
} else {
fprintf(stderr, "+++ Failed to get ScriptSecurityManager service, running without principals");
}
nsCxPusher pusher;
pusher.Push(mCx);
nsRefPtr<BackstagePass> backstagePass;
rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
if (NS_FAILED(rv)) {
NS_ERROR("Failed to create backstage pass!");
return false;
}
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = xpc->InitClassesWithNewWrappedGlobal(cx,
static_cast<nsIGlobalObject *>(backstagePass),
principal, 0,
JS::SystemZone,
getter_AddRefs(holder));
if (NS_FAILED(rv)) {
NS_ERROR("InitClassesWithNewWrappedGlobal failed!");
return false;
}
JS::Rooted<JSObject*> globalObj(cx, holder->GetJSObject());
if (!globalObj) {
NS_ERROR("Failed to get global JSObject!");
return false;
}
backstagePass->SetGlobalObject(globalObj);
{
JSAutoRequest ar(cx);
JSAutoCompartment ac(cx, globalObj);
if (!JS_DefineFunctions(cx, globalObj, gGlobalFunctions) ||
!JS_DefineProfilingFunctions(cx, globalObj)) {
NS_ERROR("JS_DefineFunctions failed!");
return false;
}
}
mGlobalHolder = globalObj;
FILE* runtimeScriptFile = fopen(kDefaultRuntimeScriptFilename, "r");
if (runtimeScriptFile) {
fprintf(stdout, "[loading '%s'...]\n", kDefaultRuntimeScriptFilename);
ProcessFile(cx, globalObj, kDefaultRuntimeScriptFilename,
runtimeScriptFile, JS_FALSE);
fclose(runtimeScriptFile);
}
return true;
}
bool
XPCShellEnvironment::EvaluateString(const nsString& aString,
nsString* aResult)
{
XPCShellEnvironment* env = Environment(mCx);
nsCxPusher pusher;
pusher.Push(env->GetContext());
JSAutoRequest ar(mCx);
JS_ClearPendingException(mCx);
JS::Rooted<JSObject*> global(mCx, GetGlobalObject());
JSAutoCompartment ac(mCx, global);
JSScript* script =
JS_CompileUCScriptForPrincipals(mCx, global, GetPrincipal(),
aString.get(), aString.Length(),
"typein", 0);
if (!script) {
return false;
}
if (!ShouldCompileOnly()) {
if (aResult) {
aResult->Truncate();
}
JS::Rooted<JS::Value> result(mCx);
JSBool ok = JS_ExecuteScript(mCx, global, script, result.address());
if (ok && result != JSVAL_VOID) {
JSErrorReporter old = JS_SetErrorReporter(mCx, NULL);
JSString* str = JS_ValueToString(mCx, result);
nsDependentJSString depStr;
if (str)
depStr.init(mCx, str);
JS_SetErrorReporter(mCx, old);
if (!depStr.IsEmpty() && aResult) {
aResult->Assign(depStr);
}
}
}
return true;
}