| /* | 
 |  * Copyright 2007 Google Inc. | 
 |  *  | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
 |  * use this file except in compliance with the License. You may obtain a copy of | 
 |  * the License at | 
 |  *  | 
 |  * http://www.apache.org/licenses/LICENSE-2.0 | 
 |  *  | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
 |  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
 |  * License for the specific language governing permissions and limitations under | 
 |  * the License. | 
 |  */ | 
 |  | 
 | /* | 
 |  * Defines the JavaScript class gwt_external_class, which interface via JNI | 
 |  * to Java objects. | 
 |  */ | 
 |  | 
 | #include <jni.h> | 
 | #include "JsRootedValue.h" | 
 | #include "gwt-jni.h" | 
 | #include "ExternalWrapper.h" | 
 | #include "Tracer.h" | 
 | #include "JsStringWrap.h" | 
 |  | 
 | // note that this does not use the NS_DEFINE_CID macro because it defines | 
 | // the variable as const, which by default has internal linkage.  I could | 
 | // work around it by putting extern in front of the macro, but that seems | 
 | // fragile if the macro changes. | 
 | nsCID kGwtExternalCID = GWT_EXTERNAL_FACTORY_CID; | 
 |  | 
 | /* | 
 |  * definition of JavaScript gwtOnLoad method which maps to the Java | 
 |  * gwtOnLoad method. | 
 |  */ | 
 | static JSBool gwtOnLoad(JSContext *cx, JSObject *obj, uintN argc, | 
 |     jsval *argv, jsval *rval) | 
 | { | 
 |   Tracer tracer("gwtOnLoad"); | 
 |   tracer.log("context=%08x", unsigned(cx)); | 
 |   JsRootedValue::ContextManager context(cx); | 
 |   JsRootedValue::ensureRuntime(cx); | 
 |   if (argc < 2) { | 
 |     tracer.setFail("less than 2 args"); | 
 |     return JS_FALSE; | 
 |   } | 
 |  | 
 |   JSObject* scriptWindow = 0; | 
 |   if (argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) { | 
 |     if (!JS_ValueToObject(cx, argv[0], &scriptWindow)) { | 
 |       tracer.setFail("can't get script window object"); | 
 |       return JS_FALSE; | 
 |     } | 
 |   } | 
 |  | 
 |   JSString* moduleName = 0; | 
 |   if (argv[1] != JSVAL_NULL && argv[1] != JSVAL_VOID) { | 
 |     moduleName = JS_ValueToString(cx, argv[1]); | 
 |   } | 
 |  | 
 |   nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(0); | 
 |   if (scriptWindow) { | 
 |     nsCOMPtr<nsIXPConnect> xpConnect = do_GetService(nsIXPConnect::GetCID()); | 
 |     nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative; | 
 |     xpConnect->GetWrappedNativeOfJSObject(cx, scriptWindow, | 
 |         getter_AddRefs(wrappedNative)); | 
 |     if (wrappedNative) { | 
 |         nsCOMPtr<nsISupports> native; | 
 |         wrappedNative->GetNative(getter_AddRefs(native)); | 
 |         scriptGlobal = do_QueryInterface(native); | 
 |     } | 
 |   } | 
 |   jstring jModuleName(0); | 
 |   if (moduleName) { | 
 |     jModuleName = savedJNIEnv->NewString(JS_GetStringChars(moduleName), | 
 |         JS_GetStringLength(moduleName)); | 
 |     if (!jModuleName || savedJNIEnv->ExceptionCheck()) { | 
 |       tracer.setFail("can't get module name in Java string"); | 
 |       return JS_FALSE; | 
 |     } | 
 |     tracer.log("module name=%s", JS_GetStringBytes(moduleName)); | 
 |   } else { | 
 |   	tracer.log("null module name"); | 
 |   } | 
 |    | 
 |   jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj)); | 
 |   jclass objClass = savedJNIEnv->GetObjectClass(externalObject); | 
 |   if (!objClass || savedJNIEnv->ExceptionCheck()) { | 
 |     tracer.setFail("can't get LowLevelMoz.ExternalObject class"); | 
 |     return JS_FALSE; | 
 |   } | 
 |    | 
 |   jmethodID methodID = savedJNIEnv->GetMethodID(objClass, "gwtOnLoad", | 
 |       "(ILjava/lang/String;)Z"); | 
 |   if (!methodID || savedJNIEnv->ExceptionCheck()) { | 
 |     tracer.setFail("can't get gwtOnLoad method"); | 
 |     return JS_FALSE; | 
 |   } | 
 |    | 
 |   tracer.log("scriptGlobal=%08x", unsigned(scriptGlobal.get())); | 
 |    | 
 |   jboolean result = savedJNIEnv->CallBooleanMethod(externalObject, methodID, | 
 |       NS_REINTERPRET_CAST(jint, scriptGlobal.get()), jModuleName); | 
 |   if (savedJNIEnv->ExceptionCheck()) { | 
 |     tracer.setFail("LowLevelMoz.ExternalObject.gwtOnLoad() threw an exception"); | 
 |     return JS_FALSE; | 
 |   } | 
 |   *rval = BOOLEAN_TO_JSVAL((result == JNI_FALSE) ? JS_FALSE : JS_TRUE); | 
 |   return JS_TRUE; | 
 | } | 
 |  | 
 | /*=======================================================================*/ | 
 | /* Implementation of the getProperty, setProperty, and finalize methods  */ | 
 | /* for the JavaScript class gwt_external_class.                          */ | 
 | /*=======================================================================*/ | 
 |  | 
 | static JSBool JS_DLL_CALLBACK gwt_external_getProperty(JSContext *cx, | 
 |     JSObject *obj, jsval id, jsval *vp) | 
 | { | 
 |   Tracer tracer("gwt_external_getProperty"); | 
 |   JsRootedValue::ContextManager context(cx); | 
 |   if (*vp != JSVAL_VOID) | 
 |     return JS_TRUE; | 
 |  | 
 |   if (!JSVAL_IS_STRING(id)) { | 
 |     tracer.setFail("not a string"); | 
 |     return JS_FALSE; | 
 |   } | 
 |   JsStringWrap jsStr(cx, id); | 
 |   tracer.log("obj=%08x, property=%.*s", obj, jsStr.length(), jsStr.bytes()); | 
 |  | 
 | // All this is for calling resolveReference which only returns void.  So, | 
 | // just replace this code to return void for now.  TODO(jat): revisit when | 
 | // merging in Toby's code.  | 
 | #if 0 | 
 |   jstring jident = savedJNIEnv->NewString(jsStr.chars(), jsStr.length()); | 
 |   if (!jident || savedJNIEnv->ExceptionCheck()) { | 
 |     tracer.setFail("can't create Java string "); | 
 |     return JS_FALSE; | 
 |   } | 
 |   jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj)); | 
 |   jclass objClass = savedJNIEnv->GetObjectClass(externalObject); | 
 |   if (!objClass || savedJNIEnv->ExceptionCheck()) { | 
 |     tracer.setFail("can't find Java class for JS object"); | 
 |     return JS_FALSE; | 
 |   } | 
 |  | 
 |   jmethodID methodID = savedJNIEnv->GetMethodID(objClass, "resolveReference", | 
 |       "(Ljava/lang/String;)I"); | 
 |   if (!methodID || savedJNIEnv->ExceptionCheck()) { | 
 |     tracer.setFail("exception getting method for int resolveReference(String)"); | 
 |     return JS_FALSE; | 
 |   } | 
 |   int retval = savedJNIEnv->CallIntMethod(externalObject, methodID, jident); | 
 |   if (savedJNIEnv->ExceptionCheck()) { | 
 |     tracer.setFail("exception calling int resolveReference(String)"); | 
 |     return JS_FALSE; | 
 |   } | 
 |   *vp = retval; | 
 | #else | 
 |   *vp = JSVAL_VOID; | 
 | #endif | 
 |   return JS_TRUE; | 
 | } | 
 |  | 
 | static void JS_DLL_CALLBACK gwt_external_finalize(JSContext *cx, JSObject *obj) | 
 | { | 
 |   // We don't need to push a context if all we do is DeleteGlobalRef | 
 |   Tracer tracer("gwt_external_finalize", obj); | 
 |   jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj)); | 
 |   if (externalObject) { | 
 |     savedJNIEnv->DeleteGlobalRef(externalObject); | 
 |   } | 
 |   JS_FinalizeStub(cx, obj); | 
 | } | 
 |  | 
 | static JSBool JS_DLL_CALLBACK gwt_external_setProperty(JSContext *cx, | 
 |     JSObject *obj, jsval id, jsval *vp) | 
 | { | 
 |   return JS_FALSE; | 
 | } | 
 |   | 
 | static JSClass gwt_external_class = { | 
 |     "gwt_external_class", JSCLASS_HAS_PRIVATE, | 
 |     JS_PropertyStub, JS_PropertyStub, gwt_external_getProperty, | 
 |     gwt_external_setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, | 
 |     gwt_external_finalize, | 
 |     JSCLASS_NO_OPTIONAL_MEMBERS | 
 | }; | 
 |  | 
 | NS_IMPL_ISUPPORTS1(ExternalWrapper, nsIScriptObjectOwner) | 
 |  | 
 | /* | 
 |  * Create a new LowLevelMoz.ExternalObject instance and a JavaScript object | 
 |  * that refers to it, mapping the gwtOnLoad member function mapping to | 
 |  * a Javascript method. | 
 |  */ | 
 | NS_IMETHODIMP ExternalWrapper::GetScriptObject(nsIScriptContext *aContext, | 
 |     void** aScriptObject) | 
 | { | 
 |   Tracer tracer("ExternalWrapper::GetScriptObject");  | 
 |   if (!aScriptObject) { | 
 |     tracer.setFail("null script object pointer"); | 
 |     return NS_ERROR_INVALID_POINTER; | 
 |   } | 
 |   if (!jsWindowExternalObject) { | 
 |     JSContext* cx = NS_REINTERPRET_CAST(JSContext*, | 
 |         aContext->GetNativeContext()); | 
 |     if (!cx) { | 
 |       tracer.setFail("can't get JSContext"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |     JsRootedValue::ContextManager context(cx); | 
 |     *aScriptObject = 0; | 
 |  | 
 |     nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject(); | 
 |     nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(globalObject)); | 
 |     if (!domWindow) { | 
 |       tracer.setFail("can't get DOM window"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |     nsCOMPtr<nsIDOMWindow> topWindow; | 
 |     domWindow->GetTop(getter_AddRefs(topWindow)); | 
 |     if (!topWindow) { | 
 |       tracer.setFail("Can't get top window"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |  | 
 |     tracer.log("savedJNIEnv=%08x, llClass=%08x", unsigned(savedJNIEnv), | 
 |         lowLevelMozClass); | 
 |      | 
 |     jmethodID methodID = savedJNIEnv->GetStaticMethodID(lowLevelMozClass, | 
 |         "createExternalObjectForDOMWindow", | 
 |         "(I)Lcom/google/gwt/dev/shell/moz/LowLevelMoz$ExternalObject;"); | 
 |     if (!methodID || savedJNIEnv->ExceptionCheck()) { | 
 |       tracer.setFail("Can't get createExternalObjectForDOMWindow method"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |  | 
 |     jobject externalObject = savedJNIEnv->CallStaticObjectMethod( | 
 |         lowLevelMozClass, methodID, NS_REINTERPRET_CAST(jint, topWindow.get())); | 
 |     if (!externalObject || savedJNIEnv->ExceptionCheck()) { | 
 |       tracer.setFail("createExternalObjectForDOMWindow failed"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |     externalObject = savedJNIEnv->NewGlobalRef(externalObject); | 
 |     if (!externalObject || savedJNIEnv->ExceptionCheck()) { | 
 |       tracer.setFail("can't get GlobalRef for external object"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |     JSObject* newObj = JS_NewObject(cx, &gwt_external_class, 0, | 
 |         globalObject->GetGlobalJSObject()); | 
 |     if (!newObj) { | 
 |       tracer.setFail("can't create new gwt_external_class object"); | 
 |       return NS_ERROR_OUT_OF_MEMORY; | 
 |     } | 
 |     if (!JS_SetPrivate(cx, newObj, externalObject)) { | 
 |       savedJNIEnv->DeleteGlobalRef(externalObject); | 
 |       tracer.setFail("can't store external object private reference"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |    | 
 |     if (!JS_DefineFunction(cx, newObj, "gwtOnLoad", gwtOnLoad, 3, | 
 |         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { | 
 |       tracer.setFail("can't define gwtOnLoad function on JavaScript object"); | 
 |       return NS_ERROR_UNEXPECTED; | 
 |     } | 
 |     jsWindowExternalObject = newObj; | 
 |   } | 
 |  | 
 |   *aScriptObject = jsWindowExternalObject; | 
 |   return NS_OK; | 
 | } | 
 |  | 
 | NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject) | 
 | { | 
 |     jsWindowExternalObject = aScriptObject; | 
 |     return NS_OK; | 
 | } | 
 |  | 
 | NS_IMPL_ISUPPORTS1(nsRpExternalFactory, nsIFactory) | 
 |  | 
 | NS_IMETHODIMP nsRpExternalFactory::CreateInstance(nsISupports *aOuter, | 
 |     const nsIID& aIID, void** aResult) | 
 | { | 
 |   Tracer tracer("nsRpExternalFactory::CreateInstance"); | 
 |   if (!aResult) { | 
 |     tracer.setFail("null pointer for return value"); | 
 |     return NS_ERROR_INVALID_POINTER; | 
 |   } | 
 |   *aResult  = NULL; | 
 |  | 
 |   if (aOuter) { | 
 |     tracer.setFail("aOuter is not null"); | 
 |     return NS_ERROR_NO_AGGREGATION; | 
 |   } | 
 |  | 
 |   nsISupports* object = new ExternalWrapper(); | 
 |   if (!object) { | 
 |     tracer.setFail("can't create a new ExternalWrapper"); | 
 |     return NS_ERROR_OUT_OF_MEMORY; | 
 |   } | 
 |        | 
 |   nsresult result = object->QueryInterface(aIID, aResult); | 
 |   if (!*aResult || NS_FAILED(result)) { | 
 |     tracer.setFail("ExternalWrapper::QueryInterface failed"); | 
 |     delete object; | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | NS_IMETHODIMP nsRpExternalFactory::LockFactory(PRBool lock) | 
 | { | 
 |     return NS_ERROR_NOT_IMPLEMENTED; | 
 | } |