Back to index

lightning-sunbird  0.9+nobinonly
lcglue.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "prthread.h"
00040 #include "nsJVMManager.h"
00041 #include "nsIPluginInstancePeer2.h"
00042 #include "ProxyJNI.h"
00043 #include "lcglue.h"
00044 #include "nscore.h"
00045 #include "nsIScriptContext.h"
00046 #include "nsISecurityContext.h"
00047 #include "nsCSecurityContext.h"
00048 #include "nsCRT.h"
00049 #include "nsIScriptGlobalObject.h"
00050 #include "nsIScriptObjectPrincipal.h"
00051 #include "nsIServiceManager.h"
00052 #include "nsIScriptSecurityManager.h"
00053 #include "nsNetUtil.h"
00054 
00055 static NS_DEFINE_CID(kJVMManagerCID, NS_JVMMANAGER_CID);
00056 
00057 extern "C" int XP_PROGRESS_STARTING_JAVA;
00058 extern "C" int XP_PROGRESS_STARTING_JAVA_DONE;
00059 extern "C" int XP_JAVA_NO_CLASSES;
00060 extern "C" int XP_JAVA_GENERAL_FAILURE;
00061 extern "C" int XP_JAVA_STARTUP_FAILED;
00062 extern "C" int XP_JAVA_DEBUGGER_FAILED;
00063 
00067 template <class T>
00068 class ThreadLocalStorage {
00069 public:
00070        ThreadLocalStorage(PRThreadPrivateDTOR dtor) : mIndex(0), mValid(PR_FALSE)
00071        {
00072               mValid = (PR_NewThreadPrivateIndex(&mIndex, dtor) == PR_SUCCESS);
00073        }
00074        
00075        void set(T value)
00076        {
00077               if (mValid) PR_SetThreadPrivate(mIndex, value);
00078        }
00079        
00080        T get()
00081        {
00082               return (T) (mValid ? PR_GetThreadPrivate(mIndex) : 0);
00083        }
00084 
00085 private:
00086        PRUintn mIndex;
00087        PRBool mValid;
00088 };
00089 
00090 
00091 static void PR_CALLBACK detach_JVMContext(void* storage)
00092 {
00093        JVMContext* context = NS_REINTERPRET_CAST(JVMContext*, storage);
00094        
00095        JNIEnv* proxyEnv = context->proxyEnv;
00096        if (proxyEnv != NULL) {
00097               DeleteProxyJNI(proxyEnv);
00098               context->proxyEnv = NULL;
00099        }
00100        
00101        delete context;
00102 }
00103 
00104 JVMContext* GetJVMContext()
00105 {
00106        /* Use NSPR thread private data to manage the per-thread JNIEnv* association. */
00107        static ThreadLocalStorage<JVMContext*> localContext((PRThreadPrivateDTOR)&detach_JVMContext);
00108        JVMContext* context = localContext.get();
00109        if (context == NULL) {
00110               context = new JVMContext;
00111               context->proxyEnv = NULL;
00112               context->jsj_env = NULL;
00113               localContext.set(context);
00114        }
00115        return context;
00116 }
00117 
00119 // LiveConnect callbacks
00121 
00122 JS_BEGIN_EXTERN_C
00123 
00124 #include "jscntxt.h"
00125 
00126 JS_STATIC_DLL_CALLBACK(JSContext*)
00127 map_jsj_thread_to_js_context_impl(JSJavaThreadState *jsj_env, void* java_applet_obj, JNIEnv *env, char **errp)
00128 {
00129        // Guess what? This design is totally invalid under Gecko, because there isn't a 1 to 1 mapping
00130        // between NSPR threads and JSContexts. We have to ask the plugin instance peer what JSContext
00131        // it lives in to make any sense of all this.
00132        JSContext* context = NULL;
00133        if (java_applet_obj != NULL) {
00134               nsIPluginInstance* pluginInstance = NS_REINTERPRET_CAST(nsIPluginInstance*, java_applet_obj);
00135                nsIPluginInstancePeer* pluginPeer = NULL;
00136               if (pluginInstance->GetPeer(&pluginPeer) == NS_OK) {
00137                      nsIPluginInstancePeer2* pluginPeer2 = NULL;
00138                      if (pluginPeer->QueryInterface(NS_GET_IID(nsIPluginInstancePeer2), (void**) &pluginPeer2) == NS_OK) {
00139                             pluginPeer2->GetJSContext(&context);
00140                             NS_RELEASE(pluginPeer2);
00141                      }
00142                      NS_RELEASE(pluginPeer);
00143               }
00144        }
00145        return context;
00146 }
00147 
00148 /*
00149 ** This callback is called to map a JSContext to a JSJavaThreadState which
00150 ** is a wrapper around JNIEnv. Hence this callback essentially maps a JSContext
00151 ** to a java thread. JSJ_AttachCurrentThreadToJava just calls AttachCurrentThread
00152 ** on the java vm.
00153 */
00154 JS_STATIC_DLL_CALLBACK(JSJavaThreadState*)
00155 map_js_context_to_jsj_thread_impl(JSContext *cx, char **errp)
00156 {
00157        *errp = NULL;
00158 
00159     // FIXME:  how do we ever break the association between the jsj_env and the
00160     // JVMContext? This needs to be figured out. Otherwise, we'll end up with the
00161     // same dangling JSContext problem we are trying to weed out.
00162 
00163        JVMContext* context = GetJVMContext();
00164        JSJavaThreadState* jsj_env = context->jsj_env;
00165        if (jsj_env != NULL)
00166               return jsj_env;
00167 
00168        JSJavaVM* js_jvm = NULL;
00169        nsresult rv;
00170        nsCOMPtr<nsIJVMManager> managerService = do_GetService(kJVMManagerCID, &rv);
00171        if (NS_FAILED(rv)) return NULL;
00172        nsJVMManager* pJVMMgr = (nsJVMManager*) managerService.get();  
00173        if (pJVMMgr != NULL) {
00174               js_jvm = pJVMMgr->GetJSJavaVM();
00175               if (js_jvm == NULL) {
00176                      *errp = strdup("Failed to attach to a Java VM.");
00177                      return NULL;
00178               }
00179        }
00180 
00181        jsj_env = JSJ_AttachCurrentThreadToJava(js_jvm, NULL, NULL);
00182        context->jsj_env = jsj_env;
00183 
00184        return jsj_env;
00185 }
00186 
00187 /*
00188 ** This callback is called in JSObject.getWindow implementation to get
00189 
00190 ** a java wrapper JSObject class for a applet only once.
00191 ** Note that once a mapping between applet -> javascript JSObject -> Java wrapper JSObject 
00192 ** is made, all subsequent method calls via JSObject use the internal field
00193 ** to get to the javascript JSObject.
00194 */
00195 
00196 JS_STATIC_DLL_CALLBACK(JSObject*)
00197 map_java_object_to_js_object_impl(JNIEnv *env, void *pluginInstancePtr, char* *errp)
00198 {
00199        JSObject        *window = NULL;
00200        PRBool           mayscript = PR_FALSE;
00201        PRBool           jvmMochaPrefsEnabled = PR_TRUE;
00202        nsresult         err = NS_OK;
00203 
00204        *errp = NULL;
00205 
00206        if (pluginInstancePtr == NULL) {
00207               env->ThrowNew(env->FindClass("java/lang/NullPointerException"), "plugin instance is NULL");
00208               return NULL;
00209        }
00210 
00211        //TODO: Check if Mocha is enabled. To get to any mocha api, we should use service 
00212        //      manager and get to the appropriate service.
00213        // jvmMochaPrefsEnabled = LM_GetMochaEnabled();
00214        if (!jvmMochaPrefsEnabled) {
00215               *errp = strdup("JSObject.getWindow() failed: java preference is disabled");
00216               return NULL;
00217        }
00218 
00219        /*
00220         * Check for the mayscript tag.
00221         */
00222        nsIPluginInstance* pluginInstance = NS_REINTERPRET_CAST(nsIPluginInstance*, pluginInstancePtr);
00223        nsIPluginInstancePeer* pluginPeer;
00224        if (pluginInstance->GetPeer(&pluginPeer) == NS_OK) {
00225               nsIJVMPluginTagInfo* tagInfo;
00226               if (pluginPeer->QueryInterface(NS_GET_IID(nsIJVMPluginTagInfo), (void**) &tagInfo) == NS_OK) {
00227                      err = tagInfo->GetMayScript(&mayscript);
00228                      // PR_ASSERT(err != NS_OK ? mayscript == PR_FALSE : PR_TRUE);
00229                      NS_RELEASE(tagInfo);
00230               }
00231               if ( !mayscript ) {
00232                      *errp = strdup("JSObject.getWindow() requires mayscript attribute on this Applet");
00233               } else {
00234                      nsIPluginInstancePeer2* pluginPeer2 = nsnull;
00235                      if (pluginPeer->QueryInterface(NS_GET_IID(nsIPluginInstancePeer2),
00236                                                    (void**) &pluginPeer2) == NS_OK) {
00237                             err = pluginPeer2->GetJSWindow(&window);
00238                             NS_RELEASE(pluginPeer2);
00239                      }
00240               }
00241               NS_RELEASE(pluginPeer);
00242        }
00243 
00244        //TODO: Get to the window object using DOM.
00245        // window = getDOMWindow().getScriptOwner().getJSObject().
00246        return window;
00247 }
00248 
00249 JS_STATIC_DLL_CALLBACK(JSPrincipals*)
00250 get_JSPrincipals_from_java_caller_impl(JNIEnv *pJNIEnv, JSContext *pJSContext, void  **ppNSIPrincipalArrayIN, int numPrincipals, void *pNSISecurityContext)
00251 {
00252     nsresult rv;
00253     nsCOMPtr<nsIScriptSecurityManager> secMan = 
00254         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00255     if (NS_FAILED(rv))
00256         return NULL;
00257 
00258     nsCOMPtr<nsIPrincipal> principal;
00259     rv = secMan->GetPrincipalFromContext(pJSContext,
00260                                          getter_AddRefs(principal));
00261     if (NS_FAILED(rv))
00262         return NULL;
00263 
00264     JSPrincipals* jsprincipals = NULL;
00265     principal->GetJSPrincipals(pJSContext, &jsprincipals);
00266     return jsprincipals;
00267 }
00268 
00269 JS_STATIC_DLL_CALLBACK(jobject)
00270 get_java_wrapper_impl(JNIEnv *pJNIEnv, lcjsobject a_jsobject)
00271 {
00272     nsresult       err    = NS_OK;
00273     jobject  pJSObjectWrapper = NULL;
00274     nsCOMPtr<nsIJVMManager> managerService = do_GetService(kJVMManagerCID, &err);
00275     if (NS_FAILED(err)) return NULL;
00276     nsJVMManager* pJVMMgr = (nsJVMManager *)managerService.get();  
00277     if (pJVMMgr != NULL) {
00278       nsIJVMPlugin* pJVMPI = pJVMMgr->GetJVMPlugin();
00279       if (pJVMPI != NULL) {
00280          err = pJVMPI->GetJavaWrapper(pJNIEnv, a_jsobject, &pJSObjectWrapper);
00281       }
00282     }
00283     if ( err != NS_OK )
00284     {
00285        return NULL;
00286     }
00287     return pJSObjectWrapper;
00288 }
00289 
00290 JS_STATIC_DLL_CALLBACK(lcjsobject)
00291 unwrap_java_wrapper_impl(JNIEnv *pJNIEnv, jobject java_wrapper)
00292 {
00293     lcjsobject obj = 0;
00294     nsresult       err    = NS_OK;
00295     nsCOMPtr<nsIJVMManager> managerService = do_GetService(kJVMManagerCID, &err);
00296     if (NS_FAILED(err)) return 0;
00297     nsJVMManager* pJVMMgr = (nsJVMManager *)managerService.get();  
00298     if (pJVMMgr != NULL) {
00299       nsIJVMPlugin* pJVMPI = pJVMMgr->GetJVMPlugin();
00300       if (pJVMPI != NULL) {
00301          err = pJVMPI->UnwrapJavaWrapper(pJNIEnv, java_wrapper, &obj);
00302       }
00303     }
00304     if ( err != NS_OK )
00305     {
00306        return 0;
00307     }
00308     return obj;
00309 }
00310 
00311 JS_STATIC_DLL_CALLBACK(JSBool)
00312 enter_js_from_java_impl(JNIEnv *jEnv, char **errp,
00313                         void **pNSIPrincipaArray, int numPrincipals, 
00314                         void *pNSISecurityContext,
00315                         void *java_applet_obj)
00316 {
00317        return PR_TRUE;
00318 }
00319 
00320 JS_STATIC_DLL_CALLBACK(void)
00321 exit_js_impl(JNIEnv *jEnv, JSContext *cx)
00322 {
00323     // The main idea is to execute terminate function if have any;
00324     if (cx)
00325     {
00326         nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
00327 
00328         if (scriptContext)
00329         {
00330             scriptContext->ScriptEvaluated(PR_TRUE);
00331         }
00332     }
00333     return;
00334 }
00335 
00336 JS_STATIC_DLL_CALLBACK(PRBool)
00337 create_java_vm_impl(SystemJavaVM* *jvm, JNIEnv* *initialEnv, void* initargs)
00338 {
00339     // const char* classpath = (const char*)initargs;
00340     nsCOMPtr<nsIJVMManager> serv = do_GetService(kJVMManagerCID);
00341     if (!serv)
00342         return PR_FALSE;
00343     *initialEnv = JVM_GetJNIEnv();
00344     if (!*initialEnv)
00345         return PR_FALSE;
00346     // serv will be released when this function returns, but that's OK because
00347     // the XPCOM service manager will keep it alive.
00348     *jvm = NS_REINTERPRET_CAST(SystemJavaVM*, serv.get());
00349     return PR_TRUE;
00350 }
00351 
00352 JS_STATIC_DLL_CALLBACK(PRBool)
00353 destroy_java_vm_impl(SystemJavaVM* jvm, JNIEnv* initialEnv)
00354 {
00355     JVM_ReleaseJNIEnv(initialEnv);
00356     // need to release jvm
00357     return PR_TRUE;
00358 }
00359 
00360 JS_STATIC_DLL_CALLBACK(JNIEnv*)
00361 attach_current_thread_impl(SystemJavaVM* jvm)
00362 {
00363     return JVM_GetJNIEnv();
00364 }
00365 
00366 JS_STATIC_DLL_CALLBACK(PRBool)
00367 detach_current_thread_impl(SystemJavaVM* jvm, JNIEnv* env)
00368 {
00369     JVM_ReleaseJNIEnv(env);
00370     return PR_TRUE;
00371 }
00372 
00373 JS_STATIC_DLL_CALLBACK(SystemJavaVM*)
00374 get_java_vm_impl(JNIEnv* env)
00375 {
00376     // only one SystemJavaVM for the whole browser, so it doesn't depend on env
00377     nsresult rv;
00378     nsCOMPtr<nsIJVMManager> managerService = do_GetService(kJVMManagerCID, &rv);
00379     if (NS_FAILED(rv)) return NULL;
00380     SystemJavaVM* jvm = NS_REINTERPRET_CAST(SystemJavaVM*, managerService.get());  
00381     return jvm;
00382 }
00383 
00384 JS_END_EXTERN_C
00385 
00386 static JSJCallbacks jsj_callbacks = {
00387     map_jsj_thread_to_js_context_impl,
00388     map_js_context_to_jsj_thread_impl,
00389     map_java_object_to_js_object_impl,
00390     get_JSPrincipals_from_java_caller_impl,
00391     enter_js_from_java_impl,
00392     exit_js_impl,
00393     NULL,       // error_print
00394     get_java_wrapper_impl,
00395     unwrap_java_wrapper_impl,
00396     create_java_vm_impl,
00397     destroy_java_vm_impl,
00398     attach_current_thread_impl,
00399     detach_current_thread_impl,
00400     get_java_vm_impl
00401 };
00402 
00403 void
00404 JVM_InitLCGlue(void)
00405 {
00406     JSJ_Init(&jsj_callbacks);
00407 }
00408 
00410 
00411 /*
00412 TODO:Tom Pixley.
00413 APIs required from Tom Pixley.
00414 o LM_LockJS(errp);         Grab the mocha lock before doing any liveconect stuff. 
00415                            This is because layers above JS engine including liveconnect
00416                            DLL itself are not thread safe.
00417 o LM_UnlockJS()
00418 o LM_GetMochaEnabled()     Check to see if Mocha is enabled.
00419 o LM_GetCrippledContext(). Get to a pre-created crippled context. All spontaneous
00420                            Java calls map into one crippled context.
00421 o ET_InitMoja(0) != LM_MOJA_OK: This tells if moja initialization went ok.
00422 o LM_GetJSPrincipalsFromJavaCaller : Wrap a nsIPrincipal array object to get back a JSPrincipals data struct.
00423 o LM_CanAccessTargetStr    This code is used to figure out if access is allowed. It is used during security
00424                            stack walking. The tricky thing is that we need to set the start frame into
00425                            TLS before calling this code.
00426                            Look into nsCSecurityContext::Implies
00427 */
00428