Reworked to use nsIThreadManager::PostEvent() to execute JSObject methods from an arbitrary Java thread.

This commit is contained in:
beard%netscape.com 1999-04-12 03:29:00 +00:00
parent 8921d7ff71
commit f56a05205d

View File

@ -27,6 +27,7 @@
#include "nsILiveconnect.h"
#include "nsICapsManager.h"
#include "nsISecurityContext.h"
#include "nsIPluginInstancePeer2.h"
#include "MRJPlugin.h"
#include "MRJContext.h"
@ -35,6 +36,7 @@
#include "JavaMessageQueue.h"
#include "MRJMonitor.h"
#include "NativeMonitor.h"
#include "RunnableMixin.h"
#include "netscape_javascript_JSObject.h" /* javah-generated headers */
@ -146,6 +148,20 @@ static jobject NewLocalRef(JNIEnv* env, jobject global_ref)
return env->CallStaticObjectMethod(netscape_oji_JNIUtils, netscape_oji_JNIUtils_NewLocalRef, global_ref);
}
static jobject ToGlobalRef(JNIEnv* env, jobject localRef)
{
jobject globalRef = env->NewGlobalRef(localRef);
env->DeleteLocalRef(localRef);
return globalRef;
}
static jobject ToLocalRef(JNIEnv* env, jobject globalRef)
{
jobject localRef = NewLocalRef(env, globalRef);
env->DeleteGlobalRef(globalRef);
return localRef;
}
static jobject GetCurrentThread(JNIEnv* env)
{
return env->CallStaticObjectMethod(netscape_oji_JNIUtils, netscape_oji_JNIUtils_GetCurrentThread);
@ -196,7 +212,13 @@ static jobject GetObjectClassLoader(JNIEnv* env, jobject object)
return env->CallStaticObjectMethod(netscape_oji_JNIUtils, netscape_oji_JNIUtils_GetObjectClassLoader, object);
}
static MRJPluginInstance* GetCurrentPlugin(JNIEnv* env)
/**
* Maps the given JNIEnv to a given plugin instance by comparing the current class loader
* with the class loader of the applet of each plugin instance. This could be made
* faster, but it's probably good enough. Note: the reference count of the plugin
* instance isn't affected by this call.
*/
static MRJPluginInstance* GetCurrentInstance(JNIEnv* env)
{
MRJPluginInstance* pluginInstance = NULL;
jobject classLoader = GetCurrentClassLoader(env);
@ -206,14 +228,80 @@ static MRJPluginInstance* GetCurrentPlugin(JNIEnv* env)
jobject applet;
pluginInstance->GetJavaObject(&applet);
jobject appletClassLoader = GetObjectClassLoader(env, applet);
if (env->IsSameObject(appletClassLoader, classLoader))
jboolean sameClassLoader = env->IsSameObject(appletClassLoader, classLoader);
env->DeleteLocalRef(appletClassLoader);
if (sameClassLoader)
break;
pluginInstance = pluginInstance->getNextInstance();
}
env->DeleteLocalRef(classLoader);
}
return pluginInstance;
}
/**
* Wraps a JavaMessage in an nsIRunnable form, so that it runs on the correct native thread.
*/
class MessageRunnable : public JavaMessage, public RunnableMixin {
public:
MessageRunnable(PRUint32 threadID, JavaMessage* msg);
virtual void execute(JNIEnv* env);
NS_IMETHOD Run();
private:
PRUint32 mThreadID;
JavaMessage* mMessage;
};
MessageRunnable::MessageRunnable(PRUint32 threadID, JavaMessage* msg)
: mThreadID(threadID), mMessage(msg)
{
}
void MessageRunnable::execute(JNIEnv* env)
{
// because a spontaneous Java thread called us, we have to switch to the JavaScript thread
// to handle this request.
nsIThreadManager* threadManager = NULL;
if (theServiceManager->GetService(nsIJVMManager::GetCID(), nsIThreadManager::GetIID(), (nsISupports**)&threadManager) == NS_OK) {
threadManager->PostEvent(mThreadID, this, PR_FALSE);
theServiceManager->ReleaseService(nsIJVMManager::GetCID(), threadManager);
}
}
NS_IMETHODIMP MessageRunnable::Run()
{
nsIJVMManager* javaManager = NULL;
if (theServiceManager->GetService(nsIJVMManager::GetCID(), nsIJVMManager::GetIID(), (nsISupports**)&javaManager) == NS_OK) {
JNIEnv* proxyEnv = NULL;
if (javaManager->GetProxyJNI(&proxyEnv) == NS_OK && proxyEnv != NULL)
mMessage->execute(proxyEnv);
theServiceManager->ReleaseService(nsIJVMManager::GetCID(), javaManager);
}
return NS_OK;
}
static PRUint32 getJavaScriptThread(JNIEnv* env)
{
PRUint32 threadID = 0;
MRJPluginInstance* pluginInstance = GetCurrentInstance(env);
if (pluginInstance != NULL) {
nsIPluginInstancePeer* peer;
if (pluginInstance->GetPeer(&peer) == NS_OK) {
nsIPluginInstancePeer2* peer2 = NULL;
if (peer->QueryInterface(nsIPluginInstancePeer2::GetIID(), &peer2) == NS_OK) {
if (peer2->GetJSThread(&threadID) != NS_OK)
threadID = 0;
NS_RELEASE(peer2);
}
NS_RELEASE(peer);
}
}
return threadID;
}
/**
* Sends a message by rendezvousing with an existing JavaScript thread, or creates a new one
* if none exists for this thread already.
@ -251,7 +339,20 @@ static void sendMessage(JNIEnv* env, JavaMessage* msg)
sharedEnv->AddRef();
}
sharedEnv->setJavaEnv(env);
sharedEnv->sendMessageFromJava(env, msg);
// In the current Seamonkey architecture, there's really only one thread that JavaScript
// can execute in. We take advantage of that fact here. When we have a more multithreaded
// system, this will have to be revisited.
static PRUint32 theJavaScriptThread = getJavaScriptThread(env);
// if the JavaScript thread is known, wrap the message in a MessageRunnable to handle
// the message in the JavaScript thread.
if (theJavaScriptThread != 0) {
MessageRunnable* runnableMsg = new MessageRunnable(theJavaScriptThread, msg);
NS_ADDREF(runnableMsg);
sharedEnv->sendMessageFromJava(env, runnableMsg);
NS_IF_RELEASE(runnableMsg);
}
}
sharedMonitor.exit();
}
@ -277,6 +378,29 @@ static nsIPrincipal* newCodebasePrincipal(const char* codebaseURL)
* Method: getMember
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
class GetMemberMessage : public JavaMessage {
jsobject mObject;
const jchar* mPropertyName;
jsize mLength;
jobject* mResultObject;
public:
GetMemberMessage(jsobject js_obj, const jchar* propertyName, jsize nameLength, jobject* member)
: mObject(js_obj), mPropertyName(propertyName), mLength(nameLength), mResultObject(member)
{
}
virtual void execute(JNIEnv* env)
{
jobject member;
nsresult result = theLiveConnectManager->GetMember(env, mObject, mPropertyName, mLength, NULL, 0, NULL, &member);
if (result == NS_OK) {
// convert reference to a global reference, in case we're switching threads.
*mResultObject = ToGlobalRef(env, member);
}
}
};
JNIEXPORT jobject JNICALL
Java_netscape_javascript_JSObject_getMember(JNIEnv* env,
jobject java_wrapper_obj,
@ -292,10 +416,15 @@ Java_netscape_javascript_JSObject_getMember(JNIEnv* env,
const jchar* property_name_ucs2 = env->GetStringChars(property_name_jstr, &is_copy);
jsize property_name_len = env->GetStringLength(property_name_jstr);
jsobject js_obj = Unwrap_JSObject(env, java_wrapper_obj);
jobject member = NULL;
nsresult result = theLiveConnectManager->GetMember(env, Unwrap_JSObject(env, java_wrapper_obj), property_name_ucs2, property_name_len,
NULL, 0, NULL, &member);
if (result != NS_OK) member = NULL;
GetMemberMessage msg(js_obj, property_name_ucs2, property_name_len, &member);
sendMessage(env, &msg);
// convert the resulting reference back to a local reference.
if (member != NULL)
member = ToLocalRef(env, member);
env->ReleaseStringChars(property_name_jstr, property_name_ucs2);
@ -307,13 +436,40 @@ Java_netscape_javascript_JSObject_getMember(JNIEnv* env,
* Method: getSlot
* Signature: (I)Ljava/lang/Object;
*/
class GetSlotMessage : public JavaMessage {
jsobject mObject;
jint mSlot;
jobject* mResultObject;
public:
GetSlotMessage(jsobject js_obj, jint slot, jobject* member)
: mObject(js_obj), mSlot(slot), mResultObject(member)
{
}
virtual void execute(JNIEnv* env)
{
jobject member;
nsresult result = theLiveConnectManager->GetSlot(env, mObject, mSlot, NULL, 0, NULL, &member);
if (result == NS_OK) {
// convert reference to a global reference, in case we're switching threads.
*mResultObject = ToGlobalRef(env, member);
}
}
};
JNIEXPORT jobject JNICALL
Java_netscape_javascript_JSObject_getSlot(JNIEnv* env,
jobject java_wrapper_obj,
jint slot)
{
jsobject js_obj = Unwrap_JSObject(env, java_wrapper_obj);
jobject member = NULL;
nsresult result = theLiveConnectManager->GetSlot(env, Unwrap_JSObject(env, java_wrapper_obj), slot, NULL, 0, NULL, &member);
GetSlotMessage msg(js_obj, slot, &member);
sendMessage(env, &msg);
// convert the resulting reference back to a local reference.
if (member != NULL)
member = ToLocalRef(env, member);
return member;
}
@ -322,6 +478,24 @@ Java_netscape_javascript_JSObject_getSlot(JNIEnv* env,
* Method: setMember
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
class SetMemberMessage : public JavaMessage {
jsobject mObject;
const jchar* mPropertyName;
jsize mLength;
jobject mJavaObject;
public:
SetMemberMessage(jsobject js_obj, const jchar* propertyName, jsize nameLength, jobject java_obj)
: mObject(js_obj), mPropertyName(propertyName), mLength(nameLength), mJavaObject(java_obj)
{
}
virtual void execute(JNIEnv* env)
{
nsresult result = theLiveConnectManager->SetMember(env, mObject, mPropertyName, mLength, NULL, 0, NULL, mJavaObject);
}
};
JNIEXPORT void JNICALL
Java_netscape_javascript_JSObject_setMember(JNIEnv* env,
jobject java_wrapper_obj,
@ -337,11 +511,14 @@ Java_netscape_javascript_JSObject_setMember(JNIEnv* env,
jboolean is_copy;
const jchar* property_name_ucs2 = env->GetStringChars(property_name_jstr, &is_copy);
jsize property_name_len = env->GetStringLength(property_name_jstr);
jobject member = NULL;
nsresult result = theLiveConnectManager->SetMember(env, Unwrap_JSObject(env, java_wrapper_obj), property_name_ucs2, property_name_len, NULL, 0, NULL, java_obj);
if (result != NS_OK) member = NULL;
jsobject js_obj = Unwrap_JSObject(env, java_wrapper_obj);
java_obj = ToGlobalRef(env, java_obj);
SetMemberMessage msg(js_obj, property_name_ucs2, property_name_len, java_obj);
sendMessage(env, &msg);
env->DeleteGlobalRef(java_obj);
env->ReleaseStringChars(property_name_jstr, property_name_ucs2);
}
@ -350,13 +527,34 @@ Java_netscape_javascript_JSObject_setMember(JNIEnv* env,
* Method: setSlot
* Signature: (ILjava/lang/Object;)V
*/
class SetSlotMessage : public JavaMessage {
jsobject mObject;
jint mSlot;
jobject mJavaObject;
public:
SetSlotMessage(jsobject js_obj, jint slot, jobject java_obj)
: mObject(js_obj), mSlot(slot), mJavaObject(java_obj)
{
}
virtual void execute(JNIEnv* env)
{
nsresult result = theLiveConnectManager->SetSlot(env, mObject, mSlot, NULL, 0, NULL, mJavaObject);
}
};
JNIEXPORT void JNICALL
Java_netscape_javascript_JSObject_setSlot(JNIEnv* env,
jobject java_wrapper_obj,
jint slot,
jobject java_obj)
{
nsresult result = theLiveConnectManager->SetSlot(env, Unwrap_JSObject(env, java_wrapper_obj), slot, NULL, 0, NULL, java_obj);
jsobject js_obj = Unwrap_JSObject(env, java_wrapper_obj);
java_obj = ToGlobalRef(env, java_obj);
SetSlotMessage msg(js_obj, slot, java_obj);
sendMessage(env, &msg);
env->DeleteGlobalRef(java_obj);
}
/*
@ -370,21 +568,17 @@ class RemoveMemberMessage : public JavaMessage {
const jchar* mPropertyName;
jsize mLength;
public:
RemoveMemberMessage(jsobject obj, const jchar* propertyName, jsize length);
RemoveMemberMessage(jsobject obj, const jchar* propertyName, jsize length)
: mObject(obj), mPropertyName(propertyName), mLength(length)
{
}
virtual void execute(JNIEnv* env);
virtual void execute(JNIEnv* env)
{
nsresult result = theLiveConnectManager->RemoveMember(env, mObject, mPropertyName, mLength, NULL, 0, NULL);
}
};
RemoveMemberMessage::RemoveMemberMessage(jsobject obj, const jchar* propertyName, jsize length)
: mObject(obj), mPropertyName(propertyName), mLength(length)
{
}
void RemoveMemberMessage::execute(JNIEnv* env)
{
nsresult result = theLiveConnectManager->RemoveMember(env, mObject, mPropertyName, mLength, NULL, 0, NULL);
}
JNIEXPORT void JNICALL
Java_netscape_javascript_JSObject_removeMember(JNIEnv* env,
jobject java_wrapper_obj,
@ -402,6 +596,7 @@ Java_netscape_javascript_JSObject_removeMember(JNIEnv* env,
jsobject js_obj = Unwrap_JSObject(env, java_wrapper_obj);
RemoveMemberMessage msg(js_obj, property_name_ucs2, property_name_len);
sendMessage(env, &msg);
env->ReleaseStringChars(property_name_jstr, property_name_ucs2);
@ -415,30 +610,29 @@ class CallMessage : public JavaMessage {
jobject* mJavaResult;
const char* mCodebaseURL;
public:
CallMessage(jsobject obj, const jchar* functionName, jsize length, jobjectArray java_args, jobject* javaResult, const char* codebaseURL);
virtual void execute(JNIEnv* env);
};
CallMessage::CallMessage(jsobject obj, const jchar* functionName, jsize length, jobjectArray javaArgs, jobject* javaResult, const char* codebaseURL)
: mObject(obj), mFunctionName(functionName), mLength(length),
mJavaArgs(javaArgs), mJavaResult(javaResult), mCodebaseURL(codebaseURL)
{
}
void CallMessage::execute(JNIEnv* env)
{
/* If we have an applet, try to create a codebase principle. */
nsIPrincipal* principal = NULL;
nsISecurityContext* context = NULL;
if (mCodebaseURL != NULL) {
principal = newCodebasePrincipal(mCodebaseURL);
context = newSecurityContext();
CallMessage(jsobject obj, const jchar* functionName, jsize length, jobjectArray javaArgs, jobject* javaResult, const char* codebaseURL)
: mObject(obj), mFunctionName(functionName), mLength(length),
mJavaArgs(javaArgs), mJavaResult(javaResult), mCodebaseURL(codebaseURL)
{
}
nsresult status = theLiveConnectManager->Call(env, mObject, mFunctionName, mLength, mJavaArgs, &principal, (principal != NULL ? 1 : 0), context, mJavaResult);
NS_IF_RELEASE(principal);
NS_IF_RELEASE(context);
}
virtual void execute(JNIEnv* env)
{
/* If we have an applet, try to create a codebase principle. */
nsIPrincipal* principal = NULL;
nsISecurityContext* context = NULL;
if (mCodebaseURL != NULL) {
principal = newCodebasePrincipal(mCodebaseURL);
context = newSecurityContext();
}
jobject jresult = NULL;
nsresult result = theLiveConnectManager->Call(env, mObject, mFunctionName, mLength, mJavaArgs, &principal, (principal != NULL ? 1 : 0), context, &jresult);
NS_IF_RELEASE(principal);
NS_IF_RELEASE(context);
if (result == NS_OK)
*mJavaResult = ToGlobalRef(env, jresult);
}
};
/*
* Class: netscape_javascript_JSObject
@ -456,7 +650,7 @@ Java_netscape_javascript_JSObject_call(JNIEnv* env, jobject java_wrapper_obj,
/* Try to determine which plugin instance is responsible for this thread. This is done by checking class loaders. */
const char* codebaseURL = NULL;
MRJPluginInstance* pluginInstance = GetCurrentPlugin(env);
MRJPluginInstance* pluginInstance = GetCurrentInstance(env);
if (pluginInstance != NULL)
codebaseURL = pluginInstance->getContext()->getDocumentBase();
@ -474,31 +668,12 @@ Java_netscape_javascript_JSObject_call(JNIEnv* env, jobject java_wrapper_obj,
env->ReleaseStringChars(function_name_jstr, function_name_ucs2);
if (jresult != NULL)
jresult = ToLocalRef(env, jresult);
return jresult;
}
class EvalMessage : public JavaMessage {
JNIEnv* mEnv;
jsobject mObject;
const jchar* mScript;
jsize mLength;
jobject* mJavaResult;
public:
EvalMessage(jsobject obj, const jchar* script, jsize length, jobject* javaResult);
virtual void execute(JNIEnv* env);
};
EvalMessage::EvalMessage(jsobject obj, const jchar* script, jsize length, jobject* javaResult)
: mObject(obj), mScript(script), mLength(length), mJavaResult(javaResult)
{
}
void EvalMessage::execute(JNIEnv* env)
{
nsresult status = theLiveConnectManager->Eval(env, mObject, mScript, mLength, NULL, 0, NULL, mJavaResult);
}
/*
* Class: netscape_javascript_JSObject
* Method: eval
@ -525,8 +700,32 @@ Java_netscape_javascript_JSObject_eval(JNIEnv* env,
#ifdef MRJPLUGIN_4X
nsresult status = theLiveConnectManager->Eval(env, js_obj, script_ucs2, script_len, NULL, 0, NULL, &jresult);
#else
class EvalMessage : public JavaMessage {
JNIEnv* mEnv;
jsobject mObject;
const jchar* mScript;
jsize mLength;
jobject* mJavaResult;
public:
EvalMessage(jsobject obj, const jchar* script, jsize length, jobject* javaResult)
: mObject(obj), mScript(script), mLength(length), mJavaResult(javaResult)
{
}
virtual void execute(JNIEnv* env)
{
jobject jresult = NULL;
nsresult result = theLiveConnectManager->Eval(env, mObject, mScript, mLength, NULL, 0, NULL, &jresult);
if (result == NS_OK && jresult != NULL) {
*mJavaResult = ToGlobalRef(env, jresult);
}
}
};
/* determine the plugin instance so we can obtain its codebase. */
MRJPluginInstance* pluginInstance = theJVMPlugin->getPluginInstance(env);
// beard: should file a bug with Apple that JMJNIToAWTContext doesn't work.
// MRJPluginInstance* pluginInstance = theJVMPlugin->getPluginInstance(env);
MRJPluginInstance* pluginInstance = GetCurrentInstance(env);
if (pluginInstance == NULL) {
env->ThrowNew(env->FindClass("java/lang/NullPointerException"), "illegal JNIEnv (can't find plugin)");
return NULL;
@ -534,9 +733,10 @@ Java_netscape_javascript_JSObject_eval(JNIEnv* env,
EvalMessage msg(js_obj, script_ucs2, script_len, &jresult);
sendMessage(env, &msg);
// Make sure it gets released!
pluginInstance->Release();
if (jresult != NULL)
jresult = ToLocalRef(env, jresult);
#endif
env->ReleaseStringChars(script_jstr, script_ucs2);
@ -572,6 +772,7 @@ Java_netscape_javascript_JSObject_toString(JNIEnv* env, jobject java_wrapper_obj
} msg(js_obj, &jresult);
sendMessage(env, &msg);
return jresult;
}
@ -581,25 +782,6 @@ Java_netscape_javascript_JSObject_toString(JNIEnv* env, jobject java_wrapper_obj
* Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
*/
class GetWindowMessage : public JavaMessage {
nsIPluginInstance* mPluginInstance;
jsobject* mWindowResult;
public:
GetWindowMessage(nsIPluginInstance* pluginInstance, jsobject* windowResult);
virtual void execute(JNIEnv* env);
};
GetWindowMessage::GetWindowMessage(nsIPluginInstance* pluginInstance, jsobject* windowResult)
: mPluginInstance(pluginInstance), mWindowResult(windowResult)
{
}
void GetWindowMessage::execute(JNIEnv* env)
{
nsresult status = theLiveConnectManager->GetWindow(env,mPluginInstance, NULL, 0, NULL, mWindowResult);
}
JNIEXPORT jobject JNICALL
Java_netscape_javascript_JSObject_getWindow(JNIEnv* env,
jclass js_object_class,
@ -612,6 +794,21 @@ Java_netscape_javascript_JSObject_getWindow(JNIEnv* env,
jobject jwindow = Wrap_JSObject(env, jsobject(pluginInstance));
return jwindow;
#else
class GetWindowMessage : public JavaMessage {
nsIPluginInstance* mPluginInstance;
jsobject* mWindowResult;
public:
GetWindowMessage(nsIPluginInstance* pluginInstance, jsobject* windowResult)
: mPluginInstance(pluginInstance), mWindowResult(windowResult)
{
}
virtual void execute(JNIEnv* env)
{
nsresult status = theLiveConnectManager->GetWindow(env,mPluginInstance, NULL, 0, NULL, mWindowResult);
}
};
jsobject jswindow = NULL;
GetWindowMessage msg(pluginInstance, &jswindow);
sendMessage(env, &msg);
@ -651,6 +848,7 @@ Java_netscape_javascript_JSObject_finalize(JNIEnv* env, jobject java_wrapper_obj
nsresult result = theLiveConnectManager->FinalizeJSObject(env, m_jsobj);
}
};
FinalizeMessage msg(jsobj);
sendMessage(env, &msg);
#endif