diff --git a/plugin/oji/MRJ/plugin/Source/LiveConnectNativeMethods.cpp b/plugin/oji/MRJ/plugin/Source/LiveConnectNativeMethods.cpp index 7d13d93c1574..2ff64105eef3 100644 --- a/plugin/oji/MRJ/plugin/Source/LiveConnectNativeMethods.cpp +++ b/plugin/oji/MRJ/plugin/Source/LiveConnectNativeMethods.cpp @@ -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