Files
Mozilla/mozilla/modules/oji/src/jvmmgr.cpp
warren%netscape.com d6bf9584b7 Fix from toshok.
git-svn-id: svn://10.0.0.236/trunk@13131 18797224-902f-48f8-a5cc-f745e15eee43
1998-10-19 22:01:53 +00:00

1442 lines
40 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
////////////////////////////////////////////////////////////////////////////////
// Plugin Manager Methods to support the JVM Plugin API
////////////////////////////////////////////////////////////////////////////////
#include "jvmmgr.h"
#include "npglue.h"
#include "xp.h"
#include "prefapi.h"
#include "xp_str.h"
#include "np.h"
#include "prio.h"
#include "prmem.h"
#include "prthread.h"
#include "pprthred.h"
#include "plstr.h"
#include "jni.h"
#include "jsjava.h"
#include "libmocha.h"
#include "libevent.h"
#include "nsCCapsManager.h"
#include "xpgetstr.h"
extern "C" int XP_PROGRESS_STARTING_JAVA;
extern "C" int XP_PROGRESS_STARTING_JAVA_DONE;
extern "C" int XP_JAVA_NO_CLASSES;
extern "C" int XP_JAVA_GENERAL_FAILURE;
extern "C" int XP_JAVA_STARTUP_FAILED;
extern "C" int XP_JAVA_DEBUGGER_FAILED;
// FIXME -- need prototypes for these functions!!! XXX
#ifdef XP_MAC
extern "C" {
OSErr ConvertUnixPathToMacPath(const char *, char **);
void startAsyncCursors(void);
void stopAsyncCursors(void);
}
#endif // XP_MAC
static PRUintn tlsIndex_g = 0;
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIJVMManagerIID, NS_IJVMMANAGER_IID);
static NS_DEFINE_IID(kIJVMPluginIID, NS_IJVMPLUGIN_IID);
static NS_DEFINE_IID(kISymantecDebugManagerIID, NS_ISYMANTECDEBUGMANAGER_IID);
static NS_DEFINE_IID(kIJVMPluginInstanceIID, NS_IJVMPLUGININSTANCE_IID);
static NS_DEFINE_IID(kIJVMPluginTagInfoIID, NS_IJVMPLUGINTAGINFO_IID);
static NS_DEFINE_IID(kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID);
static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID);
static NS_DEFINE_IID(kIJVMConsoleIID, NS_IJVMCONSOLE_IID);
static NS_DEFINE_IID(kISymantecDebuggerIID, NS_ISYMANTECDEBUGGER_IID);
////////////////////////////////////////////////////////////////////////////////
NS_IMPL_AGGREGATED(nsJVMMgr);
NS_METHOD
nsJVMMgr::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
{
if (outer && !aIID.Equals(kISupportsIID))
return NS_NOINTERFACE; // XXX right error?
nsJVMMgr* jvmmgr = new nsJVMMgr(outer);
if (jvmmgr == NULL)
return NS_ERROR_OUT_OF_MEMORY;
jvmmgr->AddRef();
*aInstancePtr = (outer != NULL ? (void*) jvmmgr->GetInner() : (void*) jvmmgr);
return NS_OK;
}
nsJVMMgr::nsJVMMgr(nsISupports* outer)
: fJVM(NULL), fStatus(nsJVMStatus_Enabled),
fRegisteredJavaPrefChanged(PR_FALSE), fDebugManager(NULL), fJSJavaVM(NULL),
fClassPathAdditions(new nsVector())
{
NS_INIT_AGGREGATED(outer);
PR_NewThreadPrivateIndex(&tlsIndex_g, NULL);
}
nsJVMMgr::~nsJVMMgr()
{
int count = fClassPathAdditions->GetSize();
for (int i = 0; i < count; i++) {
PR_Free((*fClassPathAdditions)[i]);
}
delete fClassPathAdditions;
if (fJVM) {
/*nsrefcnt c =*/ fJVM->Release(); // Release for QueryInterface in GetJVM
// XXX unload plugin if c == 1 ? (should this be done inside Release?)
}
}
NS_METHOD
nsJVMMgr::AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr)
{
if (aIID.Equals(kIJVMManagerIID)) {
*aInstancePtr = this;
AddRef();
return NS_OK;
}
#ifdef XP_PC
// Aggregates...
if (fDebugManager == NULL) {
nsresult rslt =
nsSymantecDebugManager::Create(this, kISupportsIID,
(void**)&fDebugManager, this);
if (rslt != NS_OK) return rslt;
}
return fDebugManager->QueryInterface(aIID, aInstancePtr);
#else
return NS_NOINTERFACE;
#endif
}
////////////////////////////////////////////////////////////////////////////////
// LiveConnect callbacks
////////////////////////////////////////////////////////////////////////////////
template <class T>
class ThreadLocalStorage {
public:
ThreadLocalStorage(PRThreadPrivateDTOR dtor) : mIndex(0), mValid(PR_FALSE)
{
mValid = (PR_NewThreadPrivateIndex(&mIndex, dtor) == PR_SUCCESS);
}
void set(T value)
{
if (mValid) PR_SetThreadPrivate(mIndex, value);
}
T get()
{
return (T) (mValid ? PR_GetThreadPrivate(mIndex) : 0);
}
private:
PRUintn mIndex;
PRBool mValid;
};
PR_BEGIN_EXTERN_C
#include "jscntxt.h"
static JSContext* PR_CALLBACK
map_jsj_thread_to_js_context_impl(JSJavaThreadState *jsj_env, JNIEnv *env, char **errp)
{
JSContext *cx = LM_GetCrippledContext();
PRBool jvmMochaPrefsEnabled = PR_FALSE;
*errp = NULL;
#if 0
nsJVMMgr* pJVMMgr = JVM_GetJVMMgr();
if (pJVMMgr != NULL) {
nsIJVMPlugin* pJVMPI = pJVMMgr->GetJVMPlugin();
jvmMochaPrefsEnabled = LM_GetMochaEnabled();
if (pJVMPI != NULL) {
nsIPluginInstance* pPIT;
nsresult err = pJVMPI->GetPluginInstance(env, &pPIT);
if ( (err == NS_OK) && (pPIT != NULL) ) {
nsIJVMPluginInstance* pJVMPIT;
if (pPIT->QueryInterface(kIJVMPluginInstanceIID,
(void**)&pJVMPIT) == NS_OK) {
nsPluginInstancePeer* pPITP;
err = pJVMPIT->GetPeer((nsIPluginInstancePeer**)&pPITP);
if ( (err == NS_OK) &&(pPITP != NULL) ) {
cx = pPITP->GetJSContext();
pPITP->Release();
}
pJVMPIT->Release();
}
pPIT->Release();
}
// pJVMPI->Release(); // GetJVMPlugin no longer calls AddRef
}
pJVMMgr->Release();
}
if (jvmMochaPrefsEnabled == PR_FALSE)
{
*errp = strdup("Java preference is disabled");
return NULL;
}
if ( cx == NULL )
{
*errp = strdup("Java thread could not be associated with a JSContext");
return NULL;
}
#endif
return cx;
}
static void PR_CALLBACK detach_jsjava_thread_state(void* env)
{
JSJavaThreadState *jsj_env = (JSJavaThreadState*)env;
JSJ_DetachCurrentThreadFromJava(jsj_env);
}
/*
** This callback is called to map a JSContext to a JSJavaThreadState which
** is a wrapper around JNIEnv. Hence this callback essentially maps a JSContext
** to a java thread. JSJ_AttachCurrentThreadToJava just calls AttachCurrentThread
** on the java vm.
*/
static JSJavaThreadState* PR_CALLBACK
map_js_context_to_jsj_thread_impl(JSContext *cx, char **errp)
{
*errp = NULL;
static ThreadLocalStorage<JSJavaThreadState*> localThreadState(&detach_jsjava_thread_state);
JSJavaThreadState* jsj_env = localThreadState.get();
if (jsj_env != NULL)
return jsj_env;
if (ET_InitMoja(0) != LM_MOJA_OK) {
*errp = strdup("LiveConnect initialization failed.");
return NULL;
}
JSJavaVM* js_jvm = NULL;
nsJVMMgr* pJVMMgr = JVM_GetJVMMgr();
if (pJVMMgr != NULL) {
js_jvm = pJVMMgr->GetJSJavaVM();
pJVMMgr->Release();
if (js_jvm == NULL) {
*errp = strdup("Failed to attach to a Java VM.");
return NULL;
}
}
jsj_env = JSJ_AttachCurrentThreadToJava(js_jvm, NULL, NULL);
localThreadState.set(jsj_env);
return jsj_env;
}
/*
** This callback is called to map a applet,bean to its corresponding JSObject
** created on javascript side and then map that to a java wrapper JSObject class.
** This callback is called in JSObject.getWindow implementation to get
** a java wrapper JSObject class for a applet only once.
** Note that once a mapping between applet -> javascript JSObject -> Java wrapper JSObject
** is made, all subsequent method calls via JSObject use the internal field
** to get to the javascript JSObject.
*/
static JSObject* PR_CALLBACK
map_java_object_to_js_object_impl(JNIEnv *env, void *pNSIPluginInstanceIn, char **errp)
{
MWContext *cx;
JSObject *window;
MochaDecoder *decoder;
PRBool mayscript = PR_FALSE;
PRBool jvmMochaPrefsEnabled = PR_FALSE;
nsresult err = NS_OK;
*errp = NULL;
/* XXX assert JS is locked */
if (!pNSIPluginInstanceIn) {
env->ThrowNew(env->FindClass("java/lang/NullPointerException"),0);
return 0;
}
nsJVMMgr* pJVMMgr = JVM_GetJVMMgr();
if (pJVMMgr != NULL) {
nsIJVMPlugin* pJVMPI = pJVMMgr->GetJVMPlugin();
jvmMochaPrefsEnabled = LM_GetMochaEnabled();
if (pJVMPI != NULL) {
//jobject javaObject = applet;
nsIPluginInstance* pPIT;
//nsresult err = pJVMPI->GetPluginInstance(javaObject, &pPIT);
pPIT = (nsIPluginInstance*)pNSIPluginInstanceIn;
if ( (err == NS_OK) && (pPIT != NULL) ) {
nsIJVMPluginInstance* pJVMPIT;
if (pPIT->QueryInterface(kIJVMPluginInstanceIID,
(void**)&pJVMPIT) == NS_OK) {
nsPluginInstancePeer* pPITP;
err = pJVMPIT->GetPeer((nsIPluginInstancePeer**)&pPITP);
if ( (err == NS_OK) &&(pPITP != NULL) ) {
nsIJVMPluginTagInfo* pJVMTagInfo;
if (pPITP->QueryInterface(kIJVMPluginTagInfoIID,
(void**)&pJVMTagInfo) == NS_OK) {
err = pJVMTagInfo->GetMayScript(&mayscript);
PR_ASSERT(err != NS_OK ? mayscript == PR_FALSE : PR_TRUE);
pJVMTagInfo->Release();
}
cx = pPITP->GetMWContext();
pPITP->Release();
}
pJVMPIT->Release();
}
pPIT->Release();
}
// pJVMPI->Release(); // GetJVMPlugin no longer calls AddRef
}
pJVMMgr->Release();
}
if ( (mayscript == PR_FALSE)
||(jvmMochaPrefsEnabled == PR_FALSE)
)
{
*errp = strdup("JSObject.getWindow() requires mayscript attribute on this Applet or java preference is disabled");
goto except;
}
if (!cx || (cx->type != MWContextBrowser && cx->type != MWContextPane))
{
*errp = strdup("JSObject.getWindow() can only be called in MWContextBrowser or MWContextPane");
return 0;
}
decoder = LM_GetMochaDecoder(cx);
/* if there is a decoder now, reflect the window */
if (decoder && (jvmMochaPrefsEnabled == PR_TRUE)) {
window = decoder->window_object;
}
LM_PutMochaDecoder(decoder);
return window;
except:
return 0;
}
#if 0
static JavaVM* PR_CALLBACK
get_java_vm_impl(char **errp)
{
*errp = NULL;
JavaVM *pJavaVM = NULL;
nsJVMMgr* pJVMMgr = JVM_GetJVMMgr();
if (pJVMMgr != NULL) {
nsIJVMPlugin* pJVMPI = pJVMMgr->GetJVMPlugin();
if (pJVMPI != NULL) {
nsresult err = pJVMPI->GetJavaVM(&pJavaVM);
PR_ASSERT(err != NS_OK ? pJavaVM == NULL : PR_TRUE);
// pJVMPI->Release(); // GetJVMPlugin no longer calls AddRef
}
pJVMMgr->Release();
}
if ( pJavaVM == NULL )
{
*errp = strdup("Could not find a JavaVM to attach to.");
}
return pJavaVM;
}
#endif
static JSPrincipals* PR_CALLBACK
get_JSPrincipals_from_java_caller_impl(JNIEnv *pJNIEnv, JSContext *pJSContext, void **ppNSIPrincipalArrayIN, int numPrincipals, void *pNSISecurityContext)
{
nsIPrincipal **ppNSIPrincipalArray = (nsIPrincipal **)ppNSIPrincipalArrayIN;
PRInt32 length = 0;
nsresult err = NS_OK;
nsJVMMgr* pJVMMgr = JVM_GetJVMMgr();
void *pNSPrincipalArray = NULL;
if (pJVMMgr != NULL) {
if (ppNSIPrincipalArray != NULL) {
nsIPluginManager *pNSIPluginManager = NULL;
NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID);
err = pJVMMgr->QueryInterface(kIPluginManagerIID,
(void**)&pNSIPluginManager);
if( (err == NS_OK)
&& (pNSIPluginManager != NULL )
)
{
nsCCapsManager *pNSCCapsManager = NULL;
NS_DEFINE_IID(kICapsManagerIID, NS_ICAPSMANAGER_IID);
err = pNSIPluginManager->QueryInterface(kICapsManagerIID, (void**)&pNSCCapsManager);
if( (err == NS_OK)
&& (pNSCCapsManager != NULL)
)
{
PRInt32 i=0;
nsPrincipal *pNSPrincipal = NULL;
pNSPrincipalArray = nsCapsNewPrincipalArray(length);
if (pNSPrincipalArray != NULL)
{
while( i<length )
{
err = pNSCCapsManager->GetNSPrincipal(ppNSIPrincipalArray[i], &pNSPrincipal);
nsCapsSetPrincipalArrayElement(pNSPrincipalArray, i, pNSPrincipal);
i++;
}
}
pNSCCapsManager->Release();
}
pNSIPluginManager->Release();
}
}
pJVMMgr->Release();
}
if ( (pNSPrincipalArray != NULL)
&&(length != 0)
)
{
return LM_GetJSPrincipalsFromJavaCaller(pJSContext, pNSPrincipalArray, pNSISecurityContext);
}
return NULL;
}
static jobject PR_CALLBACK
get_java_wrapper_impl(JNIEnv *pJNIEnv, jint jsobject)
{
nsresult err = NS_OK;
jobject pJSObjectWrapper = NULL;
nsJVMMgr* pJVMMgr = JVM_GetJVMMgr();
if (pJVMMgr != NULL) {
nsIJVMPlugin* pJVMPI = pJVMMgr->GetJVMPlugin();
if (pJVMPI != NULL) {
err = pJVMPI->GetJavaWrapper(pJNIEnv, jsobject, &pJSObjectWrapper);
//pJVMPI->Release();
}
pJVMMgr->Release();
}
if ( err != NS_OK )
{
return NULL;
}
return pJSObjectWrapper;
}
static JSBool PR_CALLBACK
enter_js_from_java_impl(JNIEnv *jEnv, char **errp)
{
MWContext *cx = XP_FindSomeContext(); /* XXXMLM */
#ifdef OJI
ThreadLocalStorageAtIndex0 *priv = NULL;
if ( PR_GetCurrentThread() == NULL )
{
PR_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL);
priv = (ThreadLocalStorageAtIndex0 *)malloc(sizeof(ThreadLocalStorageAtIndex0));
priv->refcount=1;
PR_SetThreadPrivate(tlsIndex_g, (void *)priv);
}
else
{
priv = (ThreadLocalStorageAtIndex0 *)PR_GetThreadPrivate(tlsIndex_g);
if(priv != NULL)
{
priv->refcount++;
}
}
return LM_LockJS(cx, errp);
#else
return JS_TRUE;
#endif
}
static void PR_CALLBACK
exit_js_impl(JNIEnv *jEnv)
{
MWContext *cx = XP_FindSomeContext(); /* XXXMLM */
ThreadLocalStorageAtIndex0 *priv = NULL;
LM_UnlockJS(cx);
if ( (PR_GetCurrentThread() != NULL )
&& ((priv = (ThreadLocalStorageAtIndex0 *)PR_GetThreadPrivate(tlsIndex_g)) != NULL)
)
{
priv->refcount--;
if(priv->refcount == 0)
{
PR_SetThreadPrivate(tlsIndex_g, NULL);
PR_DetachThread();
free(priv);
}
}
return;
}
static PRBool PR_CALLBACK
create_java_vm_impl(SystemJavaVM* *jvm, JNIEnv* *initialEnv, void* initargs)
{
const char* classpath = (const char*)initargs; // unused (should it be?)
*jvm = (SystemJavaVM*)JVM_GetJVMMgr(); // unused in the browser
*initialEnv = JVM_GetJNIEnv();
return (*jvm != NULL && *initialEnv != NULL);
}
static PRBool PR_CALLBACK
destroy_java_vm_impl(SystemJavaVM* jvm, JNIEnv* initialEnv)
{
JVM_ReleaseJNIEnv(initialEnv);
// need to release jvm
return PR_TRUE;
}
static JNIEnv* PR_CALLBACK
attach_current_thread_impl(SystemJavaVM* jvm)
{
return JVM_GetJNIEnv();
}
static PRBool PR_CALLBACK
detach_current_thread_impl(SystemJavaVM* jvm, JNIEnv* env)
{
JVM_ReleaseJNIEnv(env);
return PR_TRUE;
}
static SystemJavaVM* PR_CALLBACK
get_java_vm_impl(JNIEnv* env)
{
// only one SystemJavaVM for the whole browser, so it doesn't depend on env
return (SystemJavaVM*)JVM_GetJVMMgr();
}
PR_END_EXTERN_C
/*
* Callbacks for client-specific jsjava glue
*/
static JSJCallbacks jsj_callbacks = {
map_jsj_thread_to_js_context_impl,
map_js_context_to_jsj_thread_impl,
map_java_object_to_js_object_impl,
get_JSPrincipals_from_java_caller_impl,
enter_js_from_java_impl,
exit_js_impl,
NULL, // error_print
get_java_wrapper_impl,
create_java_vm_impl,
destroy_java_vm_impl,
attach_current_thread_impl,
detach_current_thread_impl,
get_java_vm_impl
};
////////////////////////////////////////////////////////////////////////////////
#if 0
static const char*
ConvertToPlatformPathList(const char* cp)
{
const char* c = strdup(cp);
if (c == NULL)
return NULL;
#ifdef XP_MAC
{
const char* path;
const char* strtok_path;
char* result = (char*) malloc(1);
if (result == NULL)
return NULL;
result[0] = '\0';
path = c;
strtok_path = path;
while ((path = XP_STRTOK(strtok_path, PR_PATH_SEPARATOR_STR)))
{
const char* macPath;
OSErr err = ConvertUnixPathToMacPath(path, &macPath);
char* r = PR_smprintf("%s\r%s", result, macPath);
strtok_path = NULL;
if (r == NULL)
return result; /* return what we could come up with */
free(result);
result = r;
}
free(c);
return result;
}
#else
return c;
#endif
}
#endif
void
nsJVMMgr::ReportJVMError(nsresult err)
{
MWContext* cx = XP_FindSomeContext();
char *s = NULL;
switch (err) {
case NS_JVM_ERROR_NO_CLASSES: {
s = PR_smprintf(XP_GetString(XP_JAVA_NO_CLASSES));
break;
}
case NS_JVM_ERROR_JAVA_ERROR: {
nsIJVMPlugin* plugin = GetJVMPlugin();
PR_ASSERT(plugin != NULL);
if (plugin == NULL) break;
JNIEnv* env;
plugin->GetJNIEnv(&env);
const char* msg = GetJavaErrorString(env);
plugin->ReleaseJNIEnv(env);
#ifdef DEBUG
env->ExceptionDescribe();
#endif
s = PR_smprintf(XP_GetString(XP_JAVA_STARTUP_FAILED),
(msg ? msg : ""));
if (msg) free((void*)msg);
break;
}
case NS_JVM_ERROR_NO_DEBUGGER: {
s = PR_smprintf(XP_GetString(XP_JAVA_DEBUGGER_FAILED));
break;
}
default: {
s = PR_smprintf(XP_GetString(XP_JAVA_GENERAL_FAILURE), err);
break;
}
}
if (s) {
FE_Alert(cx, s);
free(s);
}
}
/* stolen from java_lang_Object.h (jri version) */
#define classname_java_lang_Object "java/lang/Object"
#define name_java_lang_Object_toString "toString"
#define sig_java_lang_Object_toString "()Ljava/lang/String;"
const char*
nsJVMMgr::GetJavaErrorString(JNIEnv* env)
{
/* XXX javah is a pain wrt mixing JRI and JDK native methods.
Since we need to call a method on Object, we'll do it the hard way
to avoid using javah for this.
Maybe we need a new JRI entry point for toString. Yikes! */
#if 1 //def XP_MAC
jthrowable exc = env->ExceptionOccurred();
if (exc == NULL) {
return strdup(""); /* XXX better "no error" message? */
}
/* Got to do this before trying to find a class (like Object).
This is because the runtime refuses to do this with a pending
exception! I think it's broken. */
env->ExceptionClear();
jclass classObject = env->FindClass(classname_java_lang_Object);
jmethodID toString = env->GetMethodID(classObject, name_java_lang_Object_toString, sig_java_lang_Object_toString);
jstring excString = (jstring) env->CallObjectMethod(exc, toString);
jboolean isCopy;
const char* msg = env->GetStringUTFChars(excString, &isCopy);
if (msg != NULL) {
const char* dupmsg = (msg == NULL ? NULL : strdup(msg));
env->ReleaseStringUTFChars(excString, msg);
msg = dupmsg;
}
return msg;
#else
const char* msg;
struct java_lang_Class* classObject;
JRIMethodID toString;
struct java_lang_String* excString;
struct java_lang_Throwable* exc = JRI_ExceptionOccurred(env);
if (exc == NULL) {
return strdup(""); /* XXX better "no error" message? */
}
/* Got to do this before trying to find a class (like Object).
This is because the runtime refuses to do this with a pending
exception! I think it's broken. */
JRI_ExceptionClear(env);
classObject = (struct java_lang_Class*)
JRI_FindClass(env, classname_java_lang_Object);
toString = JRI_GetMethodID(env, classObject,
name_java_lang_Object_toString,
sig_java_lang_Object_toString);
excString = (struct java_lang_String *)
JRI_CallMethod(env)(env, JRI_CallMethod_op,
(struct java_lang_Object*)exc, toString);
/* XXX change to JRI_GetStringPlatformChars */
msg = JRI_GetStringPlatformChars(env, excString, NULL, 0);
if (msg == NULL) return NULL;
return strdup(msg);
#endif
}
////////////////////////////////////////////////////////////////////////////////
PRLogModuleInfo* NSJAVA = NULL;
nsJVMStatus
nsJVMMgr::StartupJVM(void)
{
// Be sure to check the prefs first before asking java to startup.
switch (GetJVMStatus()) {
case nsJVMStatus_Disabled:
return nsJVMStatus_Disabled;
case nsJVMStatus_Running:
return nsJVMStatus_Running;
default:
break;
}
#ifdef DEBUG
PRIntervalTime start = PR_IntervalNow();
if (NSJAVA == NULL)
NSJAVA = PR_NewLogModule("NSJAVA");
PR_LOG(NSJAVA, PR_LOG_ALWAYS, ("Starting java..."));
#endif
MWContext* someRandomContext = XP_FindSomeContext();
if (someRandomContext) {
FE_Progress(someRandomContext, XP_GetString(XP_PROGRESS_STARTING_JAVA));
}
PR_ASSERT(fJVM == NULL);
nsIPlugin* plugin = NPL_LoadPluginByType(NPJVM_MIME_TYPE);
if (plugin == NULL) {
fStatus = nsJVMStatus_Failed;
return fStatus;
}
nsresult rslt = plugin->QueryInterface(kIJVMPluginIID, (void**)&fJVM);
if (rslt != NS_OK) {
PR_ASSERT(fJVM == NULL);
fStatus = nsJVMStatus_Failed;
return fStatus;
}
nsJVMInitArgs initargs;
int count = fClassPathAdditions->GetSize();
char* classpathAdditions = NULL;
for (int i = 0; i < count; i++) {
const char* path = (const char*)(*fClassPathAdditions)[i];
char* oldPath = classpathAdditions;
if (oldPath) {
classpathAdditions = PR_smprintf("%s%c%s", oldPath, PR_PATH_SEPARATOR, path);
PR_Free(oldPath);
}
else
classpathAdditions = PL_strdup(path);
}
initargs.version = nsJVMInitArgs_Version;
initargs.classpathAdditions = classpathAdditions;
nsresult err = fJVM->StartupJVM(&initargs);
if (err == NS_OK) {
/* assume the JVM is running. */
fStatus = nsJVMStatus_Running;
}
else {
ReportJVMError(err);
fStatus = nsJVMStatus_Failed;
}
#if 0
JSContext* crippledContext = LM_GetCrippledContext();
MaybeStartupLiveConnect(crippledContext, JS_GetGlobalObject(crippledContext));
#endif
fJVM->Release();
#ifdef DEBUG
PRIntervalTime end = PR_IntervalNow();
PRInt32 d = PR_IntervalToMilliseconds(end - start);
PR_LOG(NSJAVA, PR_LOG_ALWAYS,
("Starting java...%s (%ld ms)",
(fStatus == nsJVMStatus_Running ? "done" : "failed"), d));
#endif
if (someRandomContext) {
FE_Progress(someRandomContext, XP_GetString(XP_PROGRESS_STARTING_JAVA_DONE));
}
return fStatus;
}
nsJVMStatus
nsJVMMgr::ShutdownJVM(PRBool fullShutdown)
{
if (fStatus == nsJVMStatus_Running) {
PR_ASSERT(fJVM != NULL);
(void)MaybeShutdownLiveConnect();
nsresult err = fJVM->ShutdownJVM(fullShutdown);
if (err == NS_OK)
fStatus = nsJVMStatus_Enabled;
else {
ReportJVMError(err);
fStatus = nsJVMStatus_Disabled;
}
fJVM = NULL;
}
PR_ASSERT(fJVM == NULL);
return fStatus;
}
////////////////////////////////////////////////////////////////////////////////
static int PR_CALLBACK
JavaPrefChanged(const char *prefStr, void* data)
{
nsJVMMgr* mgr = (nsJVMMgr*)data;
XP_Bool prefBool;
#if defined(XP_MAC)
// beard: under Mozilla, no way to enable this right now.
prefBool = TRUE;
#else
PREF_GetBoolPref(prefStr, &prefBool);
#endif
mgr->SetJVMEnabled(prefBool);
return 0;
}
void
nsJVMMgr::SetJVMEnabled(PRBool enabled)
{
if (enabled) {
if (fStatus != nsJVMStatus_Running)
fStatus = nsJVMStatus_Enabled;
// don't start the JVM here, do it lazily
}
else {
if (fStatus == nsJVMStatus_Running)
(void)ShutdownJVM();
fStatus = nsJVMStatus_Disabled;
}
}
void
nsJVMMgr::EnsurePrefCallbackRegistered(void)
{
if (fRegisteredJavaPrefChanged != PR_TRUE) {
fRegisteredJavaPrefChanged = PR_TRUE;
PREF_RegisterCallback("security.enable_java", JavaPrefChanged, this);
JavaPrefChanged("security.enable_java", this);
}
}
nsJVMStatus
nsJVMMgr::GetJVMStatus(void)
{
EnsurePrefCallbackRegistered();
return fStatus;
}
#ifdef XP_MAC
#define JSJDLL "LiveConnect"
#endif
PRBool
nsJVMMgr::MaybeStartupLiveConnect()
{
if (fJSJavaVM)
return PR_TRUE;
do {
static PRBool registeredLiveConnectFactory = PR_FALSE;
if (!registeredLiveConnectFactory) {
NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
registeredLiveConnectFactory =
(nsRepository::RegisterFactory(kCLiveconnectCID, (const char *)JSJDLL,
PR_FALSE, PR_FALSE) == NS_OK);
}
if (IsLiveConnectEnabled() && StartupJVM() == nsJVMStatus_Running) {
JSJ_Init(&jsj_callbacks);
nsIJVMPlugin* plugin = GetJVMPlugin();
if (plugin) {
#if 0
const char* classpath = NULL;
nsresult err = plugin->GetClassPath(&classpath);
if (err != NS_OK) break;
JavaVM* javaVM = NULL;
err = plugin->GetJavaVM(&javaVM);
if (err != NS_OK) break;
#endif
fJSJavaVM = JSJ_ConnectToJavaVM(NULL, NULL);
if (fJSJavaVM != NULL)
return PR_TRUE;
// plugin->Release(); // GetJVMPlugin no longer calls AddRef
}
}
} while (0);
return PR_FALSE;
}
PRBool
nsJVMMgr::MaybeShutdownLiveConnect(void)
{
if (fJSJavaVM) {
JSJ_DisconnectFromJavaVM(fJSJavaVM);
fJSJavaVM = NULL;
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsJVMMgr::IsLiveConnectEnabled(void)
{
if (LM_GetMochaEnabled()) {
nsJVMStatus status = GetJVMStatus();
return (status == nsJVMStatus_Enabled || status == nsJVMStatus_Running);
}
return PR_FALSE;
}
////////////////////////////////////////////////////////////////////////////////
nsresult
nsJVMMgr::AddToClassPath(const char* dirPath)
{
nsIJVMPlugin* jvm = GetJVMPlugin();
/* Add any zip or jar files in this directory to the classpath: */
PRDir* dir = PR_OpenDir(dirPath);
if (dir != NULL) {
PRDirEntry* dirent;
while ((dirent = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) {
PRFileInfo info;
char* path = PR_smprintf("%s%c%s", dirPath, PR_DIRECTORY_SEPARATOR, PR_DirName(dirent));
if (path != NULL) {
PRBool freePath = PR_TRUE;
if ((PR_GetFileInfo(path, &info) == PR_SUCCESS)
&& (info.type == PR_FILE_FILE)) {
int len = PL_strlen(path);
/* Is it a zip or jar file? */
if ((len > 4) &&
((PL_strcasecmp(path+len-4, ".zip") == 0) ||
(PL_strcasecmp(path+len-4, ".jar") == 0))) {
fClassPathAdditions->Add((void*)path);
if (jvm) {
/* Add this path to the classpath: */
jvm->AddToClassPath(path);
}
freePath = PR_FALSE;
}
}
// Don't leak the path!
if (freePath)
PR_smprintf_free(path);
}
}
PR_CloseDir(dir);
}
/* Also add the directory to the classpath: */
fClassPathAdditions->Add((void*)dirPath);
if (jvm) {
jvm->AddToClassPath(dirPath);
// jvm->Release(); // GetJVMPlugin no longer calls AddRef
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsJVMPluginTagInfo
////////////////////////////////////////////////////////////////////////////////
nsJVMPluginTagInfo::nsJVMPluginTagInfo(nsISupports* outer, nsIPluginTagInfo2* info)
: fPluginTagInfo(info), fSimulatedCodebase(NULL), fSimulatedCode(NULL)
{
NS_INIT_AGGREGATED(outer);
}
nsJVMPluginTagInfo::~nsJVMPluginTagInfo(void)
{
if (fSimulatedCodebase)
PL_strfree(fSimulatedCodebase);
if (fSimulatedCode)
PL_strfree(fSimulatedCode);
}
NS_IMPL_AGGREGATED(nsJVMPluginTagInfo);
NS_METHOD
nsJVMPluginTagInfo::AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr)
{
if (aIID.Equals(kIJVMPluginTagInfoIID) ||
aIID.Equals(kISupportsIID)) {
*aInstancePtr = this;
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
static void
oji_StandardizeCodeAttribute(char* buf)
{
// strip off the ".class" suffix
char* cp;
if ((cp = PL_strrstr(buf, ".class")) != NULL)
*cp = '\0';
// Convert '/' to '.'
cp = buf;
while ((*cp) != '\0') {
if ((*cp) == '/')
(*cp) = '.';
++cp;
}
}
NS_METHOD
nsJVMPluginTagInfo::GetCode(const char* *result)
{
if (fSimulatedCode) {
*result = fSimulatedCode;
return NS_OK;
}
const char* code;
nsresult err = fPluginTagInfo->GetAttribute("code", &code);
if (err == NS_OK && code) {
fSimulatedCode = PL_strdup(code);
oji_StandardizeCodeAttribute(fSimulatedCode);
*result = fSimulatedCode;
return NS_OK;
}
const char* classid;
err = fPluginTagInfo->GetAttribute("classid", &classid);
if (err == NS_OK && classid && PL_strncasecmp(classid, "java:", 5) == 0) {
fSimulatedCode = PL_strdup(classid + 5); // skip "java:"
oji_StandardizeCodeAttribute(fSimulatedCode);
*result = fSimulatedCode;
return NS_OK;
}
// XXX what about "javaprogram:" and "javabean:"?
return NS_ERROR_FAILURE;
}
NS_METHOD
nsJVMPluginTagInfo::GetCodeBase(const char* *result)
{
// If we've already cached and computed the value, use it...
if (fSimulatedCodebase) {
*result = fSimulatedCodebase;
return NS_OK;
}
// See if it's supplied as an attribute...
const char* codebase;
nsresult err = fPluginTagInfo->GetAttribute("codebase", &codebase);
if (err == NS_OK && codebase != NULL) {
*result = codebase;
return NS_OK;
}
// Okay, we'll need to simulate it from the layout tag's base URL.
const char* docBase;
err = fPluginTagInfo->GetDocumentBase(&docBase);
if (err != NS_OK) return err;
PA_LOCK(codebase, const char*, docBase);
if ((fSimulatedCodebase = PL_strdup(codebase)) != NULL) {
char* lastSlash = PL_strrchr(fSimulatedCodebase, '/');
// chop of the filename from the original document base URL to
// generate the codebase.
if (lastSlash != NULL)
*(lastSlash + 1) = '\0';
}
PA_UNLOCK(docBase);
*result = fSimulatedCodebase;
return NS_OK;
}
NS_METHOD
nsJVMPluginTagInfo::GetArchive(const char* *result)
{
return fPluginTagInfo->GetAttribute("archive", result);
}
NS_METHOD
nsJVMPluginTagInfo::GetName(const char* *result)
{
const char* attrName;
nsPluginTagType type;
nsresult err = fPluginTagInfo->GetTagType(&type);
if (err != NS_OK) return err;
switch (type) {
case nsPluginTagType_Applet:
attrName = "name";
break;
default:
attrName = "id";
break;
}
return fPluginTagInfo->GetAttribute(attrName, result);
}
NS_METHOD
nsJVMPluginTagInfo::GetMayScript(PRBool *result)
{
const char* attr;
*result = PR_FALSE;
nsresult err = fPluginTagInfo->GetAttribute("mayscript", &attr);
if (err) return err;
if (PL_strcasecmp(attr, "true") == 0)
{
*result = PR_TRUE;
}
return NS_OK;
}
NS_METHOD
nsJVMPluginTagInfo::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr,
nsIPluginTagInfo2* info)
{
if (outer && !aIID.Equals(kISupportsIID))
return NS_NOINTERFACE; // XXX right error?
nsJVMPluginTagInfo* jvmTagInfo = new nsJVMPluginTagInfo(outer, info);
if (jvmTagInfo == NULL)
return NS_ERROR_OUT_OF_MEMORY;
jvmTagInfo->AddRef();
*aInstancePtr = jvmTagInfo->GetInner();
nsresult result = outer->QueryInterface(kIPluginTagInfo2IID,
(void**)&jvmTagInfo->fPluginTagInfo);
if (result != NS_OK) goto error;
outer->Release(); // no need to AddRef outer
return result;
error:
delete jvmTagInfo;
return result;
}
////////////////////////////////////////////////////////////////////////////////
// Convenience Routines
PR_BEGIN_EXTERN_C
extern nsPluginManager* thePluginManager;
PR_IMPLEMENT(nsJVMMgr*)
JVM_GetJVMMgr(void)
{
nsresult result = NS_OK;
if (thePluginManager == NULL) {
result = nsPluginManager::Create(NULL, kIPluginManagerIID, (void**)&thePluginManager);
if (result != NS_OK)
return NULL;
}
nsJVMMgr* mgr = NULL;
result = thePluginManager->QueryInterface(kIJVMManagerIID, (void**)&mgr);
if (result != NS_OK)
return NULL;
return mgr;
}
static nsIJVMPlugin*
GetRunningJVM(void)
{
nsIJVMPlugin* jvm = NULL;
nsJVMMgr* jvmMgr = JVM_GetJVMMgr();
if (jvmMgr) {
nsJVMStatus status = jvmMgr->GetJVMStatus();
if (status == nsJVMStatus_Enabled)
status = jvmMgr->StartupJVM();
if (status == nsJVMStatus_Running) {
jvm = jvmMgr->GetJVMPlugin();
}
jvmMgr->Release();
}
return jvm;
}
PR_IMPLEMENT(nsJVMStatus)
JVM_GetJVMStatus(void)
{
nsJVMStatus status = nsJVMStatus_Disabled;
nsJVMMgr* mgr = JVM_GetJVMMgr();
if (mgr) {
status = mgr->GetJVMStatus();
mgr->Release();
}
return status;
}
PR_IMPLEMENT(PRBool)
JVM_AddToClassPath(const char* dirPath)
{
nsresult err = NS_ERROR_FAILURE;
nsJVMMgr* mgr = JVM_GetJVMMgr();
if (mgr) {
err = mgr->AddToClassPath(dirPath);
mgr->Release();
}
return err == NS_OK;
}
// This will get the JVMConsole if one is available. You have to Release it
// when you're done with it.
static nsIJVMConsole*
GetConsole(void)
{
nsIJVMConsole* console = NULL;
nsIJVMPlugin* jvm = GetRunningJVM();
if (jvm) {
jvm->QueryInterface(kIJVMConsoleIID, (void**)&console);
// jvm->Release(); // GetRunningJVM no longer calls AddRef
}
return console;
}
PR_IMPLEMENT(void)
JVM_ShowConsole(void)
{
nsIJVMConsole* console = GetConsole();
if (console) {
console->ShowConsole();
console->Release();
}
}
PR_IMPLEMENT(void)
JVM_HideConsole(void)
{
nsJVMStatus status = JVM_GetJVMStatus();
if (status == nsJVMStatus_Running) {
nsIJVMConsole* console = GetConsole();
if (console) {
console->HideConsole();
console->Release();
}
}
}
PR_IMPLEMENT(PRBool)
JVM_IsConsoleVisible(void)
{
PRBool result = PR_FALSE;
nsJVMStatus status = JVM_GetJVMStatus();
if (status == nsJVMStatus_Running) {
nsIJVMConsole* console = GetConsole();
if (console) {
nsresult err = console->IsConsoleVisible(&result);
PR_ASSERT(err != NS_OK ? result == PR_FALSE : PR_TRUE);
console->Release();
}
}
return result;
}
PR_IMPLEMENT(void)
JVM_ToggleConsole(void)
{
nsIJVMConsole* console = GetConsole();
if (console) {
PRBool visible = PR_FALSE;
nsresult err = console->IsConsoleVisible(&visible);
PR_ASSERT(err != NS_OK ? visible == PR_FALSE : PR_TRUE);
if (visible)
console->HideConsole();
else
console->ShowConsole();
console->Release();
}
}
PR_IMPLEMENT(void)
JVM_PrintToConsole(const char* msg)
{
nsJVMStatus status = JVM_GetJVMStatus();
if (status != nsJVMStatus_Running)
return;
nsIJVMConsole* console = GetConsole();
if (console) {
console->Print(msg);
console->Release();
}
}
PR_IMPLEMENT(void)
JVM_StartDebugger(void)
{
nsIJVMPlugin* jvm = GetRunningJVM();
if (jvm) {
nsISymantecDebugger* debugger;
if (jvm->QueryInterface(kISymantecDebuggerIID, (void**)&debugger) == NS_OK) {
// XXX should we make sure the vm is started first?
debugger->StartDebugger(nsSymantecDebugPort_SharedMemory);
debugger->Release();
}
// jvm->Release(); // GetRunningJVM no longer calls AddRef
}
}
#if 0
static void PR_CALLBACK detach_JNIEnv(void* env)
{
JNIEnv* jenv = (JNIEnv*)env;
JavaVM* vm = NULL;
jenv->GetJavaVM(&vm);
vm->DetachCurrentThread();
}
#endif
PR_IMPLEMENT(JNIEnv*)
JVM_GetJNIEnv(void)
{
JNIEnv* env = NULL;
/* Use NSPR thread private data to manage the per-thread JNIEnv* association. */
#ifdef NOT_YET /* Talked to patrick about it and he said it was ok to remove it
for now. This code should be in the jvm plugin code which should also*/
static ThreadLocalStorage<JNIEnv*> localEnv(&detach_JNIEnv);
env = localEnv.get();
if (env != NULL)
return env;
#endif
nsIJVMPlugin* jvm = GetRunningJVM();
if (jvm) {
(void)jvm->GetJNIEnv(&env);
// jvm->Release(); // GetRunningJVM no longer calls AddRef
}
#ifdef NOT_YET
/* Associate the JNIEnv with the current thread. */
localEnv.set(env);
#endif
return env;
}
PR_IMPLEMENT(void)
JVM_ReleaseJNIEnv(JNIEnv* env)
{
nsIJVMPlugin* jvm = GetRunningJVM();
if (jvm) {
(void)jvm->ReleaseJNIEnv(env);
// jvm->Release(); // GetRunningJVM no longer calls AddRef
}
}
PR_IMPLEMENT(nsresult)
JVM_SpendTime(PRUint32 timeMillis)
{
#ifdef XP_MAC
nsresult result = NS_ERROR_NOT_INITIALIZED;
nsIJVMPlugin* jvm = GetRunningJVM();
if (jvm != NULL)
result = jvm->SpendTime(timeMillis);
return result;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
PR_IMPLEMENT(PRBool)
JVM_MaybeStartupLiveConnect()
{
PRBool result = PR_FALSE;
nsJVMMgr* mgr = JVM_GetJVMMgr();
if (mgr) {
result = mgr->MaybeStartupLiveConnect();
mgr->Release();
}
return result;
}
PR_IMPLEMENT(PRBool)
JVM_MaybeShutdownLiveConnect(void)
{
PRBool result = PR_FALSE;
nsJVMMgr* mgr = JVM_GetJVMMgr();
if (mgr) {
result = mgr->MaybeShutdownLiveConnect();
mgr->Release();
}
return result;
}
PR_IMPLEMENT(PRBool)
JVM_IsLiveConnectEnabled(void)
{
PRBool result = PR_FALSE;
nsJVMMgr* mgr = JVM_GetJVMMgr();
if (mgr) {
result = mgr->IsLiveConnectEnabled();
mgr->Release();
}
return result;
}
PR_IMPLEMENT(nsJVMStatus)
JVM_ShutdownJVM(void)
{
nsJVMStatus status = nsJVMStatus_Failed;
nsJVMMgr* mgr = JVM_GetJVMMgr();
if (mgr) {
status = mgr->ShutdownJVM();
mgr->Release();
}
return status;
}
PR_END_EXTERN_C
////////////////////////////////////////////////////////////////////////////////