Back to index

lightning-sunbird  0.9+nobinonly
ProxyClassLoader.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim: set ts=4 sw=4 et tw=78: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Patrick Beard <beard@netscape.com> (original author)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "ProxyClassLoader.h"
00041 
00042 #include "jsapi.h"
00043 #include "jsjava.h"
00044 #include "prprf.h"
00045 
00046 #include "nsIServiceManager.h"
00047 #include "nsIScriptSecurityManager.h"
00048 #include "nsIJSContextStack.h"
00049 #include "nsIPrincipal.h"
00050 #include "nsIScriptContext.h"
00051 #include "nsIScriptGlobalObject.h"
00052 #include "nsIScriptObjectPrincipal.h"
00053 #include "nsNetUtil.h"
00054 #include "ProxyJNI.h"
00055 #include "nsCNullSecurityContext.h"
00056 
00065 static nsresult getScriptClassLoader(JNIEnv* env, jobject* classloader)
00066 {
00067     // get the current JSContext from the context stack service.
00068     nsresult rv;
00069     nsCOMPtr<nsIJSContextStack> contexts =
00070         do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
00071     if (NS_FAILED(rv)) return rv;
00072     JSContext* cx;
00073     rv = contexts->Peek(&cx);
00074     if (NS_FAILED(rv)) return rv;
00075     
00076     // lookup "window.navigator.javaclasses", if it exists, this is the class
00077     // loader bound to this page.
00078     JSObject* window = JS_GetGlobalObject(cx);
00079     if (!window) return NS_ERROR_FAILURE;
00080 
00081     jsval navigator = JSVAL_NULL;
00082     if (!JS_LookupProperty(cx, window, "navigator", &navigator))
00083         return NS_ERROR_FAILURE;
00084     
00085     jsval javaclasses = JSVAL_NULL;
00086     if (!JSVAL_IS_PRIMITIVE(navigator)) {
00087         uintN attrs;
00088         JSBool found;
00089 
00090         // Make sure that we pull out the correct javaclasses object that we
00091         // set.  Since content can't spoof READONLY or PERMANANT properties,
00092         // their presence on this property indicates that this truely is the
00093         // correct object.
00094         JSObject *obj = JSVAL_TO_OBJECT(navigator);
00095         if (!JS_GetPropertyAttributes(cx, obj, "javaclasses", &attrs, &found))
00096             return NS_ERROR_FAILURE;
00097         if ((~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)) == 0 &&
00098             !JS_GetProperty(cx, obj, "javaclasses", &javaclasses)) {
00099             return NS_ERROR_FAILURE;
00100         }
00101 
00102         // Unwrap this, the way LiveConnect does it. Note that this function
00103         // checks if javaclasses is primitive or not.
00104         if (JSJ_ConvertJSValueToJavaObject(cx, javaclasses, classloader))
00105             return NS_OK;
00106     }
00107 
00108     // use default netscape.oji.ProxyClassLoaderFactory (which is no longer
00109     // supported in recent JRE) as the classloader
00110     jclass netscape_oji_ProxyClassLoaderFac =
00111         env->FindClass("netscape/oji/ProxyClassLoaderFactory");
00112     if (!netscape_oji_ProxyClassLoaderFac) {
00113         env->ExceptionClear();
00114         return NS_ERROR_FAILURE;
00115     }
00116     jmethodID staticMethodID =
00117         env->GetStaticMethodID(netscape_oji_ProxyClassLoaderFac,
00118                                "createClassLoader",
00119                                "(Ljava/lang/String;)Ljava/lang/ClassLoader;");
00120     if (!staticMethodID) {
00121         env->ExceptionClear();
00122         return NS_ERROR_FAILURE;
00123     }
00124 
00125     // Obtain the URL of the document of the currently running script. This will
00126     // be used as the default location to download classes from.
00127     nsCOMPtr<nsIScriptSecurityManager> secMan =
00128              do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00129     if (NS_FAILED(rv)) return rv;
00130 
00131     nsCOMPtr<nsIPrincipal> principal, sysprincipal;
00132     rv = secMan->GetPrincipalFromContext(cx, getter_AddRefs(principal));
00133     if (NS_FAILED(rv)) return rv;
00134 
00135     rv = secMan->GetSystemPrincipal(getter_AddRefs(sysprincipal));
00136     if (NS_FAILED(rv)) return rv;
00137 
00138     PRBool equals;
00139     rv = principal->Equals(sysprincipal, &equals);
00140     // Can't get URI from system principal
00141     if (NS_FAILED(rv)) return rv;
00142     if (equals) return NS_ERROR_FAILURE;
00143 
00144     nsCOMPtr<nsIURI> codebase;
00145     rv = principal->GetURI(getter_AddRefs(codebase));
00146     if (NS_FAILED(rv)) return rv;
00147 
00148     // create a netscape.oji.ProxyClassLoader instance.
00149     nsCAutoString spec;
00150     rv = codebase->GetSpec(spec);
00151     if (NS_FAILED(rv)) return rv;
00152     
00153     jstring jspec = env->NewStringUTF(spec.get());
00154     if (!jspec) {
00155         env->ExceptionClear();
00156         return NS_ERROR_FAILURE;
00157     }
00158 
00159     // In order to have permission to create classloader, we need to grant
00160     // enough permission
00161     nsISecurityContext* origContext = nsnull;
00162     if (NS_FAILED(GetSecurityContext(env, &origContext))) {
00163         return NS_ERROR_FAILURE;
00164     }
00165     nsCOMPtr<nsISecurityContext> nullContext(new nsCNullSecurityContext());
00166     if (!nullContext) {
00167         return NS_ERROR_OUT_OF_MEMORY;
00168     }
00169     
00170     SetSecurityContext(env, nullContext);
00171     *classloader = env->CallStaticObjectMethod(netscape_oji_ProxyClassLoaderFac,
00172                                                staticMethodID, jspec);
00173     SetSecurityContext(env, origContext);
00174     if (!*classloader) {
00175         env->ExceptionClear();
00176         return NS_ERROR_FAILURE;
00177     }
00178 
00179     env->DeleteLocalRef(jspec);
00180     env->DeleteLocalRef(netscape_oji_ProxyClassLoaderFac);
00181     
00182     // now, cache the class loader in "window.navigator.javaclasses"
00183     if (!JSVAL_IS_PRIMITIVE(navigator) &&
00184         JSJ_ConvertJavaObjectToJSValue(cx, *classloader, &javaclasses) &&
00185         !JS_DefineProperty(cx, JSVAL_TO_OBJECT(navigator), "javaclasses",
00186                            javaclasses, NULL, NULL, JSPROP_ENUMERATE |
00187                            JSPROP_READONLY | JSPROP_PERMANENT)) {
00188         return NS_ERROR_FAILURE;
00189     }
00190     
00191     return NS_OK;
00192 }
00193 
00194 jclass ProxyFindClass(JNIEnv* env, const char* name)
00195 {
00196     do {
00197         // TODO:  prevent recursive call to ProxyFindClass, if netscape.oji.ProxyClassLoader
00198         // isn't found by getScriptClassLoader().
00199         jobject classloader;
00200         jthrowable jException = env->ExceptionOccurred();
00201         if (jException != NULL) {
00202             // Clean up exception
00203             env->ExceptionClear();
00204             // Release local ref
00205             env->DeleteLocalRef(jException);
00206         }
00207         nsresult rv = getScriptClassLoader(env, &classloader);
00208         if (NS_FAILED(rv)) break;
00209 
00210         jclass netscape_oji_ProxyClassLoader = env->GetObjectClass(classloader);
00211         jmethodID loadClassID = env->GetMethodID(netscape_oji_ProxyClassLoader, "loadClass",
00212                                                  "(Ljava/lang/String;)Ljava/lang/Class;");
00213         env->DeleteLocalRef(netscape_oji_ProxyClassLoader);
00214         if (!loadClassID) {
00215             env->ExceptionClear();
00216             break;
00217         }
00218         jstring jname = env->NewStringUTF(name);
00219         jvalue jargs[1]; jargs[0].l = jname;
00220         jclass c = (jclass) env->CallObjectMethodA(classloader, loadClassID, jargs);
00221         env->DeleteLocalRef(jname);
00222         return c;
00223     } while (0);
00224     return 0;
00225 }