|  | /* | 
|  | * 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" | 
|  |  | 
|  | #ifdef ENABLE_TRACING | 
|  | extern void PrintJSValue(JSContext* cx, jsval val, char* prefix=""); | 
|  | #else | 
|  | // Include a null version just to keep from cluttering up call sites. | 
|  | static inline void PrintJSValue(JSContext* cx, jsval val, char* prefix="") { } | 
|  | #endif | 
|  |  | 
|  | // 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 < 3) { | 
|  | tracer.setFail("less than 3 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]); | 
|  | } | 
|  |  | 
|  | JSString* version = 0; | 
|  | if (argv[2] != JSVAL_NULL && argv[2] != JSVAL_VOID) { | 
|  | version = JS_ValueToString(cx, argv[2]); | 
|  | } | 
|  |  | 
|  | 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"); | 
|  | } | 
|  |  | 
|  | jstring jVersion(0); | 
|  | if (version) { | 
|  | jVersion = savedJNIEnv->NewString(JS_GetStringChars(version), | 
|  | JS_GetStringLength(version)); | 
|  | if (!jVersion || savedJNIEnv->ExceptionCheck()) { | 
|  | tracer.setFail("can't get module name in Java string"); | 
|  | return JS_FALSE; | 
|  | } | 
|  | tracer.log("version=%s", JS_GetStringBytes(version)); | 
|  | } else { | 
|  | tracer.log("null version"); | 
|  | } | 
|  |  | 
|  | 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;Ljava/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, jVersion); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * definition of JavaScript initModule method which maps to the Java | 
|  | * initModule method. | 
|  | */ | 
|  | static JSBool initModule(JSContext *cx, JSObject *obj, uintN argc, | 
|  | jsval *argv, jsval *rval) | 
|  | { | 
|  | Tracer tracer("initModule"); | 
|  | tracer.log("context=%08x", unsigned(cx)); | 
|  | JsRootedValue::ContextManager context(cx); | 
|  | JsRootedValue::ensureRuntime(cx); | 
|  | if (argc < 1) { | 
|  | tracer.setFail("less than 1 args"); | 
|  | return JS_FALSE; | 
|  | } | 
|  |  | 
|  | JSString* moduleName = 0; | 
|  | if (argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) { | 
|  | moduleName = JS_ValueToString(cx, argv[0]); | 
|  | } | 
|  |  | 
|  | 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, "initModule", | 
|  | "(Ljava/lang/String;)Z"); | 
|  | if (!methodID || savedJNIEnv->ExceptionCheck()) { | 
|  | tracer.setFail("can't get initModule method"); | 
|  | return JS_FALSE; | 
|  | } | 
|  |  | 
|  | jboolean result = savedJNIEnv->CallBooleanMethod(externalObject, methodID, | 
|  | jModuleName); | 
|  | if (savedJNIEnv->ExceptionCheck()) { | 
|  | tracer.setFail("LowLevelMoz.ExternalObject.initModule() 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) { | 
|  | // we setup the gwtOnLoad function as a property in GetScriptObject and | 
|  | // do not maintain a copy of it anywhere.  So, if there is a cached | 
|  | // value for a property we must return it.  As we never redefine any | 
|  | // property values, this is safe.  If we were going to keep this code | 
|  | // around and add Toby's profiling code we would need to revisit this. | 
|  | 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; | 
|  | } | 
|  | if (!JS_DefineFunction(cx, newObj, "initModule", initModule, 1, | 
|  | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { | 
|  | tracer.setFail("can't define initModule 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; | 
|  | } |