Fixes issues #472, #627, #736, #739
Refactor hosted mode code and rewrite Mozilla code to properly handle garbage
collection of JavaScript objects held as references within Java objects. The
Mac changes simply adapt the existing Mac code to the new infrastructure, but
do not fix the outstanding memory leak problems -- these will be addressed in
a future patch.
Also added sample eclipse projects for the Mac and Linux JNI code.
Review by: scottb
knorton
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@554 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/jni/linux/ExternalWrapper.cpp b/jni/linux/ExternalWrapper.cpp
new file mode 100644
index 0000000..4f0ae72
--- /dev/null
+++ b/jni/linux/ExternalWrapper.cpp
@@ -0,0 +1,305 @@
+/*
+ * 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");
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ 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");
+ 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)
+{
+ 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 (!mScriptObject) {
+ *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;
+ }
+ JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
+ aContext->GetNativeContext());
+ if (!cx) {
+ tracer.setFail("can't get JSContext");
+ 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;
+ }
+ mScriptObject = newObj;
+ }
+
+ *aScriptObject = mScriptObject;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject)
+{
+ mScriptObject = 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;
+}