Add back IE proxy handling for CheckForUpdates.
Also remove branch-info.txt inadvertently committed to trunk.

Patch by: scottb
Review by: jat


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6390 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/jni/linux/ExternalWrapper.cpp b/jni/linux/ExternalWrapper.cpp
new file mode 100644
index 0000000..3b2854e
--- /dev/null
+++ b/jni/linux/ExternalWrapper.cpp
@@ -0,0 +1,412 @@
+/*
+ * 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;
+}
diff --git a/jni/linux/ExternalWrapper.h b/jni/linux/ExternalWrapper.h
new file mode 100644
index 0000000..0168083
--- /dev/null
+++ b/jni/linux/ExternalWrapper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 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.
+ */
+
+#ifndef EXTERNALWRAPPER_H_
+#define EXTERNALWRAPPER_H_
+
+class ExternalWrapper : public nsIScriptObjectOwner {
+public:
+  NS_DECL_ISUPPORTS
+  NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
+  NS_IMETHOD SetScriptObject(void* aScriptObject);
+  ExternalWrapper(): jsWindowExternalObject(0) { }
+private:
+  ~ExternalWrapper() { }
+  void *jsWindowExternalObject;
+};
+
+class nsRpExternalFactory : public nsIFactory {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIFACTORY
+  nsRpExternalFactory() { }
+private:
+  ~nsRpExternalFactory() { }
+};
+
+#define GWT_EXTERNAL_FACTORY_CID \
+{ 0xF56E23F8, 0x5D06, 0x47F9, \
+{ 0x88, 0x5A, 0xD9, 0xCA, 0xC3, 0x38, 0x41, 0x7F } }
+#define GWT_EXTERNAL_CONTRACTID "@com.google/GWT/external;1"
+
+#endif /*EXTERNALWRAPPER_H_*/
diff --git a/jni/linux/JStringWrap.h b/jni/linux/JStringWrap.h
new file mode 100644
index 0000000..9d2ebcc
--- /dev/null
+++ b/jni/linux/JStringWrap.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef JNI_LINUX_JSTRINGWRAP_H_
+#define JNI_LINUX_JSTRINGWRAP_H_
+
+/*
+ * Wrapper arouond Java Strings, keeps pointers to unpacked strings
+ * and makes sure they are cleaned up. 
+ */
+class JStringWrap {
+  public:
+    JStringWrap(JNIEnv* env, jstring str): env(env), s(str), p(0), jp(0) { }
+    ~JStringWrap() {
+      if (p) env->ReleaseStringUTFChars(s, p);
+      if (jp) env->ReleaseStringChars(s, jp);
+    }
+    const char* str() {
+      if (!p) p = env->GetStringUTFChars(s, 0);
+      return p;
+    }
+    const jchar* jstr() {
+      if (!jp) jp = env->GetStringChars(s, 0);
+      return jp;
+    }
+    int length() {
+      return env->GetStringLength(s);
+    }
+  private:
+    JNIEnv* env;
+    jstring s;
+    const char* p;
+    const jchar* jp;
+};
+
+
+#endif // JNI_LINUX_JSTRINGWRAP_H_
diff --git a/jni/linux/JsRootedValue.cpp b/jni/linux/JsRootedValue.cpp
new file mode 100644
index 0000000..29b95ca
--- /dev/null
+++ b/jni/linux/JsRootedValue.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#include "JsRootedValue.h"
+
+// Initialize static value used to hold the JavaScript String class.
+JSClass* JsRootedValue::stringClass = 0;
+
+// Initialize static reference to the sole JSRuntime value in Gecko.
+JSRuntime* JsRootedValue::runtime = 0;
+
+// Static stack of JavaScript execution contexts.
+std::stack<JSContext*> JsRootedValue::contextStack;
+
+/*
+ * Actually get the stringClass pointer from JavaScript.
+ */
+void JsRootedValue::fetchStringClass() const {
+  Tracer tracer("JsRootedValue::fetchStringClass");
+  JSContext* cx = currentContext();
+  jsval val = JS_GetEmptyStringValue(cx);
+  JSObject* obj;
+  // on error, leave stringClass null
+  if (!JS_ValueToObject(cx, val, &obj)) return;
+  if (!obj) {
+    tracer.log("ensureStringClass: null object");
+    return;
+  }
+  stringClass = JS_GET_CLASS(cx, obj);
+  tracer.log("stringClass=%08x", unsigned(stringClass));
+}
diff --git a/jni/linux/JsRootedValue.h b/jni/linux/JsRootedValue.h
new file mode 100644
index 0000000..65c25af
--- /dev/null
+++ b/jni/linux/JsRootedValue.h
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+#ifndef JNI_LINUX_JSROOTEDVALUE_H_
+#define JNI_LINUX_JSROOTEDVALUE_H_
+
+// Mozilla header files
+#include "mozilla-headers.h"
+
+#include "Tracer.h"
+#include <stack>
+
+extern "C" JSClass gwt_nativewrapper_class;
+
+/*
+ * Holds a root for Javascript objects, so the JS interpreter knows not to
+ * garbage-collect the underlying object as long as this object exists.
+ * Java code will pass a pointer to this object around (as an int/long) for
+ * referring to the underlying Javascript object.
+ *
+ * There are also convenience routines for manipulating the underlying value.
+ * Note that all get* methods assume the type is correct, so the corresponding
+ * is* method should be called first if you aren't sure of the type.
+ *
+ * See http://developer.mozilla.org/en/docs/JS_AddRoot for details.
+ *
+ * TODO(jat): rewrite this to minimize the number of roots held and to
+ *    improve 64-bit compatibility.
+ */
+class JsRootedValue
+{
+private:
+  // the JavaScript String class
+  static JSClass* stringClass;
+  
+  // Javascript runtime
+  static JSRuntime* runtime;
+
+  // stack of Javascript contexts
+  static std::stack<JSContext*> contextStack;
+
+  // underlying Javascript value
+  jsval         value_;
+
+protected:
+  /*
+   * Fetch the JavaScript String class.
+   * Not inlined to minimize code bloat since it should only be called once.
+   */
+  void fetchStringClass() const;
+  
+  /*
+   * Make sure we have the JS code to identify String objects installed.
+   */
+  void ensureStringClass() const {
+    if(stringClass) return;
+    fetchStringClass();
+  }
+  
+  /*
+   * Helper for the various constructors
+   */
+  void constructorHelper(const char* ctorDesc) {
+    Tracer tracer(ctorDesc, this);
+    if (!JS_AddNamedRootRT(runtime, &value_, ctorDesc)) {
+      tracer.log("JS_AddNamedRootRT failed");
+      // TODO(jat): handle errors
+    }
+  }
+
+  /*
+   * Push a new JavaScript execution context.
+   */
+  static void pushContext(JSContext* context) {
+    Tracer tracer("JsRootedValue::pushContext");
+    tracer.log("pushed context=%08x", unsigned(context));
+    contextStack.push(context);
+  }
+  
+  /*
+   * Pop a JavaScript execution context from the context stack.
+   */
+  static void popContext() {
+    Tracer tracer("JsRootedValue::popContext");
+    JSContext* context = currentContext();
+    contextStack.pop();
+    tracer.log("popped context=%08x", unsigned(context));
+  }
+
+public:
+  /*
+   * This is a helper class used to push/pop JSContext values automatically,
+   * using RAII.  Simply create a ContextManager object on the stack and it
+   * will push the context at creation time and pop it at destruction time,
+   * so you don't have to worry about covering all the exit paths from a
+   * function.
+   */
+  class ContextManager {
+  public:
+    explicit ContextManager(JSContext* context) {
+      JsRootedValue::pushContext(context);
+    }
+    ~ContextManager() {
+      JsRootedValue::popContext();
+    }
+  };
+
+  /*
+   * This is a helper class to manage short-lived roots on the stack, using
+   * RAII to free the roots when no longer needed.
+   */
+  class Temp {
+  private:
+    void* ptr_;
+  public:
+    explicit Temp(void* ptr) : ptr_(ptr) {
+      JS_AddNamedRootRT(runtime, ptr_, "temporary root");
+    }
+    ~Temp() {
+      JS_RemoveRootRT(runtime, ptr_);
+    }
+  };
+  
+  /*
+   * Copy constructor - make another rooted value that refers to the same
+   * JavaScript object (or has the same value if a primitive)
+   */
+  JsRootedValue(const JsRootedValue& rooted_value) : value_(rooted_value.value_)
+  {
+    constructorHelper("JsRootedValue copy ctor");
+  }
+  
+  /*
+   * Create a value with a given jsval value
+   */
+  JsRootedValue(jsval value) : value_(value)
+  {
+    constructorHelper("JsRootedValue jsval ctor");
+  }
+  
+  /*
+   * Create a void value
+   */
+  JsRootedValue() : value_(JSVAL_VOID) {
+    constructorHelper("JsRootedValue void ctor");
+  }
+  
+  /*
+   * Destroy this object.
+   */
+  ~JsRootedValue() {
+    Tracer tracer("~JsRootedValue", this);
+    // ignore error since currently it is not possible to fail
+    JS_RemoveRootRT(runtime, &value_);
+  }
+  
+  /*
+   * Save a pointer to the JSRuntime if we don't have it yet
+   */
+  static void ensureRuntime(JSContext* context) {
+    if(!runtime) runtime = JS_GetRuntime(context);
+  }
+  
+  /*
+   * Return the current JavaScript execution context.
+   */
+  static JSContext* currentContext() {
+    Tracer tracer("JsRootedValue::currentContext");
+    if (contextStack.empty()) {
+      // TODO(jat): better error handling?
+      fprintf(stderr, "JsRootedValue::currentContext - context stack empty\n");
+      ::abort();
+    }
+    JSContext* context = contextStack.top();
+    tracer.log("context=%08x", unsigned(context));
+    return context;
+  }
+
+  /* 
+   * Return the underlying JS object
+   */
+  jsval getValue() const { return value_; }
+  
+  /*
+   * Sets the value of the underlying JS object.
+   * 
+   * Returns false if an error occurred.
+   */
+  bool setValue(jsval new_value) {
+    value_ = new_value;
+    return true;
+  }
+  
+   /*
+   * Returns true if the underlying value is of some number type.
+   */
+  bool isNumber() const {
+    return JSVAL_IS_NUMBER(value_);
+  }
+  /*
+   * Returns the underlying value as a double.
+   * Result is 0.0 if the underlying value is not a number
+   * type.
+   */
+  double getDouble() const {
+    jsdouble return_value=0.0;
+    // ignore return value -- if it fails, value will remain 0.0
+    JS_ValueToNumber(currentContext(), value_, &return_value); 
+    return double(return_value);
+  }
+  /*
+   * Set the underlying value to a double value.
+   * 
+   * Returns false on failure.
+   */
+  bool setDouble(double val) {
+    jsval js_double;
+    if(!JS_NewDoubleValue(currentContext(), jsdouble(val), &js_double)) {
+      return false;
+    }
+    return setValue(js_double);
+  }
+
+  /*
+   * Returns the underlying value as an integer value.  Note that the result
+   * is undefined if isInt() does not return true.
+   */
+  int getInt() {
+    return JSVAL_TO_INT(value_);
+  }
+  
+  /*
+   * Set the underlying value to an integer value.
+   * 
+   * Returns false on failure.
+   */
+  bool setInt(int val) {
+    // check if it fits in 31 bits (ie, top two bits are equal).
+    // if not, store it as a double
+    if ((val & 0x80000000) != ((val << 1) & 0x80000000)) {
+      return setDouble(val);
+    } else {
+      return setValue(INT_TO_JSVAL(val));
+    }
+  }
+  
+  /*
+   * Returns true if the underlying value is a boolean.
+   */
+  bool isBoolean() const {
+    return JSVAL_IS_BOOLEAN(value_);
+  }
+  /*
+   * Returns the underlying value as a boolean.
+   * Result is undefined if the value is not actually
+   * a boolean.
+   */
+  bool getBoolean() const {
+    return value_ != JSVAL_FALSE;
+  }
+  /*
+   * Set the underlying value to a boolean value.
+   * 
+   * Returns false on failure (impossible?).
+   */
+  bool setBoolean(bool val) {
+    return setValue(val ? JSVAL_TRUE : JSVAL_FALSE);
+  }
+
+  /*
+   * Returns true if the underlying value is a string.
+   */
+  bool isInt() const {
+    return JSVAL_IS_INT(value_);
+  }
+  
+  /*
+   * Returns true if the underlying value is a string.
+   */
+  bool isString() const {
+    return JSVAL_IS_STRING(value_);
+  }
+
+  /*
+   * Check if the value is a JavaScript String object.
+   */
+  bool isJavaScriptStringObject() const {
+    if (!isObject()) return false;
+    ensureStringClass();
+    return getObjectClass() == stringClass;
+  }
+  
+  /*
+   * Return this value as a string, converting as necessary.
+   */
+  JSString* asString() const {
+    return JS_ValueToString(currentContext(), value_);
+  }
+  
+  /* Returns the string as a JSString pointer.
+   * Result is undefined if the value is not actually a string or String object.
+   */
+  const JSString* getString() const {
+    if (JSVAL_IS_STRING(value_)) {
+      return JSVAL_TO_STRING(value_);
+    }
+    return asString();
+  }
+  /*
+   * Returns the string as a zero-terminated array of UTF16 characters.
+   * Note that this pointer may become invalid when JS performs GC, so it
+   * may only be used without calling other JS functions.  Result is
+   * undefined if the value is not actually a string.
+   */
+  const wchar_t* getStringChars() const {
+    return reinterpret_cast<const wchar_t*>(JS_GetStringChars(
+        const_cast<JSString*>(getString())));
+  }
+  
+  /*
+   * Returns the length of the underlying string.  Result is undefined
+   * if the value is not actually a string.
+   */
+  int getStringLength() const {
+    return JS_GetStringLength(const_cast<JSString*>(getString()));
+  } 
+  
+  /*
+   * Sets the underlying value, defined by a null-terminated array of UTF16
+   * chars.
+   * 
+   * Returns false on failure.
+   */
+  bool setString(const wchar_t* utf16) {
+    JSString* str = JS_NewUCStringCopyZ(currentContext(),
+        reinterpret_cast<const jschar*>(utf16));
+    return setValue(STRING_TO_JSVAL(str));
+  }
+  
+  /*
+   * Sets the underlying value, defined by a counted array of UTF16 chars.
+   * 
+   * Returns false on failure.
+   */
+  bool setString(const wchar_t* utf16, size_t len) {
+    JSString* str = JS_NewUCStringCopyN(currentContext(),
+        reinterpret_cast<const jschar*>(utf16), len);
+    return setValue(STRING_TO_JSVAL(str));
+  }
+  
+  /*
+   * Returns true if the underlying value is an object.
+   */
+  bool isObject() const {
+    return JSVAL_IS_OBJECT(value_);
+  }
+  
+  /*
+   * Returns the underlying value as an object.
+   * Result is undefined if it is not actually an object.
+   */
+  JSObject* getObject() const {
+    return isObject() ? JSVAL_TO_OBJECT(value_) : 0;
+  }
+  
+  /*
+   * Returns the class name of the underlying value.
+   *
+   * Result is undefined if it is not actually an object.
+   */
+  const JSClass* getObjectClass() const {
+    return isObject() ? JS_GET_CLASS(currentContext(), getObject()) : 0;
+  }
+  
+  /*
+   * Sets the underlying value to be an object.
+   *
+   * Returns false on failure.
+   */
+  bool setObject(JSObject* obj) {
+    return setValue(OBJECT_TO_JSVAL(obj));
+  }
+
+  /*
+   * Returns true if the underlying value is undefined (void).
+   */
+  bool isUndefined() const {
+    return JSVAL_IS_VOID(value_);
+  }
+  
+  /*
+   * Sets the underlying value to be undefined (void).
+   * 
+   * Returns false on failure (impossible?)
+   */
+  bool setUndefined() {
+    return setValue(JSVAL_VOID);
+  }
+  
+  /*
+   * Returns true if the underlying value is null.
+   */
+  bool isNull() const {
+    return JSVAL_IS_NULL(value_);
+  }
+  /*
+   * Sets the underlying value to be null.
+   * 
+   * Returns false on failure (impossible?)
+   */
+  bool setNull() {
+    return setValue(JSVAL_NULL);
+  }
+};
+
+#endif /*JNI_LINUX_JSROOTEDVALUE_H_*/
diff --git a/jni/linux/JsStringWrap.h b/jni/linux/JsStringWrap.h
new file mode 100644
index 0000000..7ab0ce6
--- /dev/null
+++ b/jni/linux/JsStringWrap.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef JNI_LINUX_JSSTRINGWRAP_H_
+#define JNI_LINUX_JSSTRINGWRAP_H_
+
+/*
+ * Wrapper arouond JavaScript Strings, keeps pointers to unpacked strings
+ * and makes sure they are not cleaned up early. 
+ */
+class JsStringWrap {
+  private:
+    JSContext* context_;
+    JSString* string_;
+    const char* bytes_;
+    const wchar_t* chars_;
+
+  public:
+    JsStringWrap(JSContext* context, JSString* str)
+        : context_(context), string_(str), bytes_(0), chars_(0) {
+      JS_AddRoot(context_, &string_);
+      JS_AddRoot(context_, &bytes_);
+      JS_AddRoot(context_, &chars_);
+    }
+    JsStringWrap(JSContext* context, jsval str)
+        : context_(context), string_(JSVAL_TO_STRING(str)), bytes_(0), chars_(0) {
+      JS_AddRoot(context_, &string_);
+      JS_AddRoot(context_, &bytes_);
+      JS_AddRoot(context_, &chars_);
+    }
+    ~JsStringWrap() {
+      JS_RemoveRoot(context_, &string_);
+      JS_RemoveRoot(context_, &bytes_);
+      JS_RemoveRoot(context_, &chars_);
+    }
+    const char* bytes() {
+      if (!bytes_) bytes_ = JS_GetStringBytes(string_);
+      return bytes_;
+    }
+    const wchar_t* chars() {
+      if (!chars_) {
+        chars_ = reinterpret_cast<wchar_t*>(JS_GetStringChars(string_));
+      }
+      return chars_;
+    }
+    int length() {
+      return JS_GetStringLength(string_);
+    }
+};
+
+#endif // JNI_LINUX_JSSTRINGWRAP_H_
diff --git a/jni/linux/JsValueMoz.cpp b/jni/linux/JsValueMoz.cpp
new file mode 100644
index 0000000..195306c
--- /dev/null
+++ b/jni/linux/JsValueMoz.cpp
@@ -0,0 +1,993 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+
+// include javah-generated header to make sure things match
+#include "JsValueMoz.h"
+
+/*
+ * Returns the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to retreive field from
+ * fieldName - name of field on Java object to retrieve
+ * 
+ * Returns null on failure.  Caller is responsible for deleting
+ * returned JsRootedValue when done with it.
+ */
+static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz,
+    jobject obj, jstring fieldName)
+{
+  Tracer tracer("GetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+    return 0;
+  }
+
+  JsRootedValue* jsRootedValue = new JsRootedValue();
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+  	 reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck()) {
+  	 delete jsRootedValue;
+    return 0;
+  }
+  return jsRootedValue;
+}
+  
+/*
+ * Sets the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to store into field
+ * fieldName - name of field on Java object to store into
+ * jsRootedValue - the value to store in the field
+ * 
+ * returns true on success, false on failure
+ */
+static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz,
+    jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
+{
+  Tracer tracer("SetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+    return false;
+  }
+
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+  	 reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck()) {
+    return false;
+  }
+
+  return true;
+}
+
+/*
+ * Throws a HostedModeException with the specified message.
+ */
+static void ThrowHostedModeException(JNIEnv* jniEnv, const char* msg) {
+  jclass exceptionClass
+      = jniEnv->FindClass("com/google/gwt/dev/shell/HostedModeException");
+  jniEnv->ThrowNew(exceptionClass, msg);
+}
+
+/*
+ * Types of jsvals.
+ */
+enum JsValueType {
+  JSVAL_TYPE_VOID=0,
+  JSVAL_TYPE_NULL,
+  JSVAL_TYPE_BOOLEAN,
+  JSVAL_TYPE_NUMBER,
+  JSVAL_TYPE_STRING,
+  JSVAL_TYPE_OBJECT,
+  JSVAL_TYPE_UNKNOWN,
+};
+
+/*
+ * Names of jsval types -- must match the order of the enum above.
+ */
+static const char* JsValueTypeStrings[]={
+  "undefined",
+  "null",
+  "boolean",
+  "number",
+  "string",
+  "object",
+  "unknown",
+};
+
+/*
+ * Return the type of a jsval.
+ */
+static JsValueType GetValueType(jsval val) {
+  if(JSVAL_IS_VOID(val)) {
+    return JSVAL_TYPE_VOID;
+  } else if(JSVAL_IS_NULL(val)) {
+    return JSVAL_TYPE_NULL;
+  } else if(JSVAL_IS_BOOLEAN(val)) {
+    return JSVAL_TYPE_BOOLEAN;
+  } else if(JSVAL_IS_NUMBER(val)) {
+    return JSVAL_TYPE_NUMBER;
+  } else if(JSVAL_IS_STRING(val)) {
+    return JSVAL_TYPE_STRING;
+  } else if(JSVAL_IS_OBJECT(val)) {
+    return JSVAL_TYPE_OBJECT;
+  } else {
+    return JSVAL_TYPE_UNKNOWN;
+  } 
+}
+
+/*
+ * Called from JavaScript to call a Java method that has previously been
+ * wrapped.  See _setWrappedFunction for use.
+ */ 
+static JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc,
+    jsval *argv, jsval *rval)
+{
+  Tracer tracer("invokeJavaMethod");
+  JsRootedValue::ContextManager context(cx);
+
+  // I kid you not; this is how XPConnect gets their function object so they can
+  // multiplex dispatch the call from a common site.  See XPCDispObject.cpp(466)
+  //
+  // I now have a secondary confirmation that this trick is legit.
+  // brandon@mozilla.org writes:
+  //
+  // argv[-2] is part of the JS API, unabstracted.  Just as argv[0] is the
+  // first argument (if argc != 0), argv[-1] is the |this| parameter (equal
+  // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj|
+  // second formal parameter name), and argv[-2] is the callee object, tagged
+  // as a jsval.
+  if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) {
+    tracer.setFail("not a function type");
+    return JS_FALSE;
+  }
+  JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
+
+  // Pull the wrapper object out of the funObj's reserved slot
+  jsval jsCleanupObj;
+  if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) {
+    tracer.setFail("JS_GetReservedSlot failed");
+    return JS_FALSE;
+  }
+  JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
+  if (!cleanupObj) {
+    tracer.setFail("cleanupObj is null");
+    return JS_FALSE;
+  }
+
+  // Get DispatchMethod instance out of the wrapper object
+  jobject dispMeth =
+      NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
+  if (!dispMeth) {
+    tracer.setFail("dispMeth is null");
+    return JS_FALSE;
+  }
+  jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth);
+  if (!dispClass || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("GetObjectClass returns null");
+    return JS_FALSE;
+  }
+
+  // lookup the invoke method on the dispatch object
+  jmethodID invokeID =
+      savedJNIEnv->GetMethodID(dispClass, "invoke", "(I[II)V");
+  if (!invokeID || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("GetMethodID failed");
+    return JS_FALSE;
+  }
+
+  // create an array of integers to hold the JsRootedValue pointers passed
+  // to the invoke method
+  jintArray args = savedJNIEnv->NewIntArray(argc);
+  if (!args || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("NewIntArray failed");
+    return JS_FALSE;
+  }
+
+  // these arguments are already rooted by the JS interpreter, but we
+  // can't easily take advantage of that without complicating the JsRootedValue
+  // interface.
+  
+  // argv[-1] is OBJECT_TO_JSVAL(this)
+  JsRootedValue* jsThis = new JsRootedValue(argv[-1]);
+  tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis));
+
+  // create JsRootedValues for arguments  
+  JsRootedValue *jsArgs[argc]; 
+  for (uintN i = 0; i < argc; ++i) {
+    jsArgs[i] = new JsRootedValue(argv[i]);
+  }
+  savedJNIEnv->SetIntArrayRegion(args, 0, argc,
+      reinterpret_cast<jint*>(jsArgs));
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("SetIntArrayRegion failed");
+    return JS_FALSE;
+  }
+  
+  // slot for return value
+  JsRootedValue* jsReturnVal = new JsRootedValue();
+
+  // TODO(jat): small window here where invocation may fail before Java
+  // takes ownership of the JsRootedValue objects.  One solution would be
+  // to reference-count them between Java and C++ (so the reference count
+  // would always be 0, 1, or 2).  Also setField has a similar problem.
+  // I plan to fix this when switching away from Java holding pointers to
+  // C++ objects as part of the fix for 64-bit support (which we could
+  // accomplish inefficiently by changing int to long everywhere, but there
+  // are other 64-bit issues to resolve and we need to reduce the number of
+  // roots the JS interpreter has to search.
+  
+  // call Java method
+  savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(jsThis),
+      args, reinterpret_cast<int>(jsReturnVal));
+  
+  JSBool returnValue = JS_TRUE;
+  
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.log("dispMeth=%08x", unsigned(dispMeth));
+    tracer.setFail("java exception is active:");
+    jobject exception = savedJNIEnv->ExceptionOccurred();
+    if (exception) {
+      fprintf(stderr, "Exception occurred in MethodDispatch.invoke:\n");
+      savedJNIEnv->ExceptionDescribe();
+      savedJNIEnv->DeleteLocalRef(exception);
+    }
+    returnValue = JS_FALSE;
+  } else if (JS_IsExceptionPending(cx)) {
+    tracer.setFail("js exception is active");
+    returnValue = JS_FALSE;
+  }
+
+  // extract return value
+  *rval = jsReturnVal->getValue();
+
+#if 0
+  // NOTE: C++ objects are not cleaned up here because Java now owns them.
+  // TODO(jat): if reference-counted, they *do* need to be Released here.
+  
+  // free JsRootedValues
+  for (uintN i = 0; i < argc; ++i) {
+    delete jsArgs[i];
+  }
+  delete jsThis;
+  delete jsReturnVal;
+#endif
+
+  return returnValue;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _createJsRootedValue()
+ * Signature: (I)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint jsval)
+{
+  Tracer tracer("JsValueMoz._createJsRootedValue");
+  JsRootedValue* jsRootedValue = new JsRootedValue(jsval);
+  return NS_REINTERPRET_CAST(jint, jsRootedValue);
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _copyJsRootedValue()
+ * Signature: (I)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1copyJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  const JsRootedValue* jsRootedValue = reinterpret_cast<const JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._copyJsRootedValue", jsRootedValue);
+  JsRootedValue* newRootedValue = new JsRootedValue(*jsRootedValue);
+  return NS_REINTERPRET_CAST(jint, newRootedValue);
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _destroyJsRootedValue()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1destroyJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._destroyJsRootedValue", jsRootedValue);
+  delete jsRootedValue;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getBoolean()
+ * Signature: (I)Z
+ * 
+ * TODO(jat): unboxing Javascript Boolean type?
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getBoolean
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getBoolean", jsRootedValue);
+  return jsRootedValue->getBoolean();
+}
+
+/**
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getInt()
+ * Signature: (I)I
+ * 
+ * @see com.google.gwt.dev.shell.moz.JsValueMoz#getInt()
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getInt
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getInt", jsRootedValue);
+  int val = jsRootedValue->getInt();
+  tracer.log("value=%d", val);
+  return val;
+}
+
+/**
+ * Return a Javascript number as a Java double.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getNumber()
+ * Signature: (I)D
+ */
+extern "C" JNIEXPORT jdouble JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getNumber
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getNumber", jsRootedValue);
+  return jsRootedValue->getDouble();
+}
+
+/**
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getJsval()
+ * Signature: (I)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getJsval
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getObjectPointer", jsRootedValue);
+  int val = jsRootedValue->getValue();
+  tracer.log("value=%d", val);
+  return val;
+}
+
+/**
+ * Return a Javascript string as a Java string.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getString()
+ * Signature: (I)Ljava/lang/String;
+ * 
+ * Note that this relies on jschar being assignment compatible with jchar
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getString", jsRootedValue);
+  const JSString* str = jsRootedValue->getString();
+  int len = JS_GetStringLength(const_cast<JSString*>(str));
+  jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>(
+      JS_GetStringChars(const_cast<JSString*>(str))), len);
+  return javaStr;
+}
+
+/*
+ * Returns a human-readable Java string describing the type of a
+ * JavaScript object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getTypeString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getTypeString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getTypeString", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  JSContext* cx = JsRootedValue::currentContext();
+  JsValueType valueType = GetValueType(val);
+  const char* typeString = 0;
+  char buf[256];
+  if(valueType == JSVAL_TYPE_OBJECT) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    JSClass* objClass = JS_GET_CLASS(cx, jsObject);
+    if (JS_InstanceOf(cx, jsObject,
+        &gwt_nativewrapper_class, 0)) {
+      typeString = "Java object";
+    } else {
+      snprintf(buf, sizeof(buf), "class %s", objClass->name);
+      typeString = buf;
+    }
+  } else {
+    typeString = JsValueTypeStrings[valueType];
+  }
+  jstring returnValue = jniEnv->NewStringUTF(typeString);
+  return returnValue;
+}
+
+/*
+ * Unwraps a wrapped Java object from a JS object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getWrappedJavaObject
+ * Signature: (I)Ljava/lang/Object;
+ */
+extern "C" JNIEXPORT jobject JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getWrappedJavaObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getWrappedJavaObject", jsRootedValue);
+  JSObject* jsObject = jsRootedValue->getObject();
+  if(!jsObject) {
+    tracer.throwHostedModeException(jniEnv, "Javascript value not an object");
+    return 0;
+  }
+  JSContext* cx = JsRootedValue::currentContext();
+  if(!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)) {
+    tracer.throwHostedModeException(jniEnv,
+      "Javascript object not a Java object");
+    return 0;
+  } 
+  jobject javaObject
+      = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, jsObject));
+  return javaObject;
+} 
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isBoolean()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isBoolean
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isBoolean", jsRootedValue);
+  return jsRootedValue->isBoolean();
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isInt()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isInt
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isBoolean", jsRootedValue);
+  return jsRootedValue->isInt();
+}
+
+/*
+ * Checks if a JS object is a JavaScript object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isJavaScriptObject
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isJavaScriptObject", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  bool returnValue = false;
+  if(JSVAL_IS_OBJECT(val)) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    returnValue = !JS_InstanceOf(JsRootedValue::currentContext(), jsObject,
+        &gwt_nativewrapper_class, 0);
+    tracer.log("jsobject=%08x, isJSObject=%s", unsigned(jsObject),
+        returnValue ? "true" : "false");
+  } else {
+    tracer.log("not an object");
+  }
+  return returnValue;
+} 
+
+/*
+ * Checks if a JS object is a JavaScript String object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isJavaScriptString
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isJavaScriptString", jsRootedValue);
+  bool returnValue = jsRootedValue->isJavaScriptStringObject();
+  tracer.log("value=%s", returnValue ? "true" : "false");
+  return returnValue;
+} 
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isNull()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNull
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isNull", jsRootedValue);
+  return jsRootedValue->isNull();
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isNumber()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNumber
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isNumber", jsRootedValue);
+  return jsRootedValue->isNumber();
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isString()
+ * Signature: (I)Z
+ * 
+ * Handles the case of JavaScript String objects as well
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isString", jsRootedValue);
+  return jsRootedValue->isString();
+}
+
+/*
+ * Checks if a JS object is undefined (void)
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isUndefined
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isUndefined
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isUndefined", jsRootedValue);
+  return jsRootedValue->isUndefined();
+} 
+
+/*
+ * Checks if a JS object is a wrapped Java object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isWrappedJavaObject
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isWrappedJavaObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isWrappedJavaObject", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  bool returnValue = false;
+  if(JSVAL_IS_OBJECT(val)) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    returnValue = JS_InstanceOf(JsRootedValue::currentContext(), jsObject,
+        &gwt_nativewrapper_class, 0);
+    tracer.log("jsobject=%08x, wrappedJava=%s", unsigned(jsObject),
+        returnValue ? "true" : "false");
+  } else {
+    tracer.log("not an object");
+  }
+  return returnValue;
+} 
+
+/*
+ * Set the JavaScript value to be a boolean of the supplied value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setBoolean()
+ * Signature: (IZ)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setBoolean
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jboolean val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setBoolean", jsRootedValue);
+  jsRootedValue->setBoolean(val == JNI_TRUE);
+  return;
+}
+
+/*
+ * Set the JavaScript value to be a double of the supplied value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setDouble()
+ * Signature: (ID)V
+ */
+extern "C" JNIEXPORT void
+JNICALL Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setDouble
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jdouble val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setDouble", jsRootedValue);
+  if(!jsRootedValue->setDouble(val)) {
+    tracer.throwHostedModeException(jniEnv, "Unable to allocate JS double");
+    return;
+  }
+}
+
+/*
+ * Set the Javascript value to be an integer.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setInt()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setInt
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setInt", jsRootedValue);
+  tracer.log("val=%d", val);
+  jsRootedValue->setInt(val);
+}
+
+/*
+ * Set the Javascript value to be another JsRootedValue's value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setJsRootedValue()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsOtherRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  JsRootedValue* jsOtherRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsOtherRootedValueInt);
+  Tracer tracer("JsValueMoz._setJsRootedValue", jsRootedValue);
+  jsRootedValue->setValue(jsOtherRootedValue->getValue());
+}
+
+/*
+ * Set the Javascript value to a specific jsval.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setJsval()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsval
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsval)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setJsval", jsRootedValue);
+  jsRootedValue->setValue(jsval);
+}
+
+/*
+ * Set the JavaScript value to be null.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setNull()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setNull
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setNull", jsRootedValue);
+  jsRootedValue->setNull();
+}
+
+/*
+ * Set the JavaScript value to be a string of the supplied value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setString()
+ * Signature: (ILjava/lang/String;)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setString", jsRootedValue);
+  JStringWrap strVal(jniEnv, val);
+  const jchar* stringUTF16 = strVal.jstr();
+  if(!stringUTF16) {
+    tracer.throwHostedModeException(jniEnv, "Unable to retrieve Java string");
+    return;
+  }
+  tracer.log("string=%s", strVal.str());
+  if(!jsRootedValue->setString(reinterpret_cast<const wchar_t*>(stringUTF16),
+      strVal.length())) {
+    tracer.throwHostedModeException(jniEnv, "Unable to allocate JS string");
+    return;
+  }
+}
+
+/*
+ * Set the JavaScript value to be undefined (void).
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setUndefined()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setUndefined
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setUndefined", jsRootedValue);
+  jsRootedValue->setUndefined();
+}
+
+/*
+ * Wraps a Java object in a JS object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setWrappedJavaObject
+ * Signature: (ILjava/lang/Object)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedJavaObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jobject obj)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setWrappedJavaObject", jsRootedValue);
+  JSContext* cx = JsRootedValue::currentContext();
+  JSObject* scriptWindow = JS_GetGlobalObject(cx);
+  JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0,
+      scriptWindow);
+  if (!newObj) {
+    tracer.throwHostedModeException(jniEnv,
+        "Unable to allocate JS object to wrap Java object");
+    return;
+  }
+  // Save in output value so it won't get GCed.
+  jsRootedValue->setObject(newObj); 
+  tracer.log("jsobject=%08x", unsigned(newObj));
+  
+  // This is collected when the gwt_nativewrapper_class destructor runs.
+  jobject dispObjRef = jniEnv->NewGlobalRef(obj);
+  if (!dispObjRef || jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv,
+        "Unable to allocate global reference for JS wrapper");
+    return;
+  } 
+  if (!JS_SetPrivate(cx, newObj, dispObjRef)) {
+    jniEnv->DeleteGlobalRef(dispObjRef);
+    tracer.throwHostedModeException(jniEnv,
+        "Unable to allocate global reference for JS wrapper");
+    return;
+  } 
+  // forcibly setup a "toString" method to override the default
+  jclass dispClass = jniEnv->GetObjectClass(obj);
+  if (jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv, "Can't get object class");
+    return;
+  } 
+  jmethodID getFieldMeth = jniEnv->GetMethodID(dispClass, "getField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv, "Can't get getField method");
+    return;
+  } 
+  jstring ident = jniEnv->NewStringUTF("@java.lang.Object::toString()");
+  if (!ident || jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv,
+        "Can't create Java string for toString method name");
+    return;
+  }
+  // allocate a new root to hold the result of the getField call
+  JsRootedValue* toStringFunc = new JsRootedValue(); 
+  jniEnv->CallVoidMethod(obj, getFieldMeth, ident,
+      NS_REINTERPRET_CAST(jint, toStringFunc));
+  if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv, "getField(toString) failed");
+    return;
+  } 
+  if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc->getValue(),
+      JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
+    tracer.throwHostedModeException(jniEnv, "Can't define JS toString method");
+    return;
+  }
+} 
+
+/*
+ * Wraps a Java function in a JS object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setWrappedFunction
+ * Signature: (ILjava/lang/String;Lcom/google/gwt/dev/shell/moz/DispatchMethod;)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedFunction
+    (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring methodName,
+     jobject dispatchMethod)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setWrappedFunction", jsRootedValue);
+  JSContext* cx = JsRootedValue::currentContext();
+  JSObject* scriptWindow = JS_GetGlobalObject(cx);
+  JStringWrap nameStr(jniEnv, methodName);
+  if (!nameStr.str()) {
+    tracer.throwHostedModeException(jniEnv,
+       "null method name passed to setWrappedFunction");
+    return;
+  }
+  tracer.log("JsRootedValue=%08x, method=%s, obj=%08x", jsRootedValueInt,
+      nameStr.str(), unsigned(dispatchMethod));
+  JSFunction* function = JS_NewFunction(cx, invokeJavaMethod, 0,
+      JSFUN_LAMBDA, 0, nameStr.str());
+  if (!function) {
+    tracer.throwHostedModeException(jniEnv, "JS_NewFunction failed");
+    return;
+  }
+  JSObject* funObj = JS_GetFunctionObject(function);
+  if (!funObj) {
+    tracer.throwHostedModeException(jniEnv, "JS_GetFunctionObject failed");
+    return;
+  }
+  // Save in output value so it won't get GCed.
+  jsRootedValue->setObject(funObj); 
+
+  // Create a cleanup object to hold and clean up dispMeth
+  JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0,
+      scriptWindow);
+  if (!cleanupObj) {
+    tracer.throwHostedModeException(jniEnv, "JS_NewObject failed");
+    return;
+  }
+  tracer.log("funObj=%08x, cleanupObj=%08x", unsigned(funObj),
+      unsigned(cleanupObj));
+  // Store the cleanup object in funObj's reserved slot; now GC protected.
+  if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) {
+    tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed");
+    return;
+  }
+  jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod);
+  if (!dispMethRef || jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv,
+        "NewGlobalRef(dispatchMethod) failed");
+    return;
+  }
+  // Store our global ref in the wrapper object
+  if (!JS_SetPrivate(cx, cleanupObj, dispMethRef)) {
+    jniEnv->DeleteGlobalRef(dispMethRef);
+    tracer.throwHostedModeException(jniEnv, "JS_SetPrivate(cleanupObj) failed");
+    return;
+  }
+}      
+
+/*
+ * Returns a JavaScript value as a string.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _toString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1toString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._toString", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  JSContext* cx = JsRootedValue::currentContext();
+
+  // if it is a JavaScript object that has a toString member function
+  // call that, otherwise call JS_ValueToString
+  if(JSVAL_IS_OBJECT(val)) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    jsval fval;
+    jsval rval;
+    if (!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)
+        && JS_GetProperty(cx, jsObject, "toString", &fval)
+        && JS_ValueToFunction(cx, fval)
+        && JS_CallFunctionValue(cx, jsObject, fval, 0, 0, &rval)) {
+      // all the steps succeeded, so use the result of toString() instead
+      // of the value for JS_ValueToString below
+      val = rval;
+    }
+  }
+  JSString* str = JS_ValueToString(cx, val);
+  if (!str) {
+    return 0;
+  }
+  int len = JS_GetStringLength(str);
+  jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>(
+      JS_GetStringChars(str)), len);
+  return javaStr;
+} 
diff --git a/jni/linux/LowLevelMoz.cpp b/jni/linux/LowLevelMoz.cpp
new file mode 100644
index 0000000..9c3aba2
--- /dev/null
+++ b/jni/linux/LowLevelMoz.cpp
@@ -0,0 +1,380 @@
+/*
+ * 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.
+ */
+
+// Mozilla-specific hosted-mode methods
+
+// Define to log debug-level output rather than just warnings.
+#define DEBUG
+
+#include <cstdio>
+#include <cstdarg>
+#include <cwchar>
+
+// Mozilla header files
+#include "mozilla-headers.h"
+
+#include <jni.h>
+#include "gwt-jni.h"
+#include "JsRootedValue.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+#include "JsStringWrap.h"
+
+/*
+ * Debug definitions -- define FILETRACE to have debug output written to
+ * a file named gwt-ll.log, or JAVATRACE to have debug output passed to the
+ * Java LowLevelMoz.trace method.
+ */
+#ifdef ENABLE_TRACING
+#define FILETRACE
+//#define JAVATRACE
+#endif
+
+// include javah-generated header to make sure we match
+#include "LowLevelMoz.h"
+
+JNIEnv* savedJNIEnv = 0;
+jclass lowLevelMozClass;
+
+// Only include debugging code if we are tracing somewhere.
+#ifdef ENABLE_TRACING
+
+/*
+ * Template so vsnprintf/vswprintf can be used interchangeably in the
+ * append_sprintf template below.
+ *   buf - pointer to the start of the output buffer
+ *   len - maximum number of characters to write into the buffer
+ *         (including the null terminator)
+ *   fmt - printf-style format string
+ *   args - stdarg-style variable arguments list
+ *  Returns the number of characters written (excluding the null terminator)
+ *   or -1 if an error occurred.
+ * 
+ * Note that %lc and %ls are only legal in the wchar_t implementation.
+ */
+template<class charT>
+int safe_vsprintf(charT* buf, size_t len, const charT* fmt, va_list args); 
+
+// specialization for char that maps to vsnprintf
+template<>
+inline int safe_vsprintf<char>(char* buf, size_t len, const char* fmt,
+    va_list args) {
+  return ::vsnprintf(buf, len, fmt, args);
+}
+
+// specialization for wchar_t that maps to vswprintf
+template<>
+inline int safe_vsprintf<wchar_t>(wchar_t* buf, size_t len, const wchar_t* fmt,
+    va_list args) {
+  return ::vswprintf(buf, len, fmt, args);
+}
+
+/*
+ * Safely append to a string buffer, updating the output pointer and always
+ * reserving the last character of the buffer for a null terminator.
+ *   bufStart - pointer to the start of the output buffer
+ *   bufEnd - pointer just past the end of the output buffer
+ *   fmt - format string
+ *   additional arguments as passed to *printf
+ * Returns the number of characters actually written, not including the null
+ * terminator.  Nothing is written, including the null terminator, if the
+ * buffer start points beyond the output buffer.
+ *
+ * Templated to work with any character type that has a safe_vsprintf
+ * implementation.
+ */
+template<class charT>
+static int append_sprintf(charT* bufStart, const charT* bufEnd,
+    const charT* fmt, ...) {
+  va_list args;
+  va_start(args, fmt); // initialize variable arguments list
+  // compute space left in buffer: -1 for null terminator
+  int maxlen = bufEnd - bufStart - 1;
+  if (maxlen <= 0) return 0;
+  int n = safe_vsprintf(bufStart, maxlen, fmt, args);
+  va_end(args);
+  if (n > maxlen) {
+    n = maxlen;
+  }
+  bufStart[n] = 0;
+  return n;
+}
+
+/*
+ * Log a given jsval with a prefix.
+ *  cx - JSContext for the JS execution context to use
+ *  val - jsval to print
+ *  prefix - string to print before the value, defaults to empty string
+ *
+ * TODO(jat): this whole printf-style logging needs to be replaced, but we
+ * run into library version issues if we use C++ iostreams so we would need
+ * to implement our own equivalent.  Given that this code is all likely to
+ * be rewritten for out-of-process hosted mode, it seems unlikely to be worth
+ * the effort until that is completed.
+ */
+void PrintJSValue(JSContext* cx, jsval val, char* prefix="") {
+  JSType type = JS_TypeOfValue(cx, val);
+  const char* typeString=JS_GetTypeName(cx, type);
+  static const int BUF_SIZE = 256;
+  char buf[BUF_SIZE];
+  const char *bufEnd = buf + BUF_SIZE;
+  char* p = buf;
+  p += append_sprintf(p, bufEnd, "%s%s", prefix, typeString);
+  switch(type) {
+    case JSTYPE_VOID:
+      break;
+    case JSTYPE_BOOLEAN:
+      p += append_sprintf(p, bufEnd, ": %s",
+          JSVAL_TO_BOOLEAN(val) ? "true" : "false");
+      break;
+    case JSTYPE_NUMBER:
+      if (JSVAL_IS_INT(val)) {
+        p += append_sprintf(p, bufEnd, ": %d", JSVAL_TO_INT(val));
+      } else {
+        p += append_sprintf(p, bufEnd, ": %lf", (double)*JSVAL_TO_DOUBLE(val));
+      }
+      break;
+    case JSTYPE_OBJECT: {
+      JSObject* obj = JSVAL_TO_OBJECT(val);
+      if (!JSVAL_IS_OBJECT(val)) break;
+      JSClass* clazz = obj ? JS_GET_CLASS(cx, obj) : 0;
+      p += append_sprintf(p, bufEnd, " @ %08x, class %s",
+          (unsigned)obj, clazz ? clazz->name : "<null>");
+      break;
+    }
+    case JSTYPE_FUNCTION:
+    case JSTYPE_LIMIT:
+      break;
+    case JSTYPE_STRING: {
+      /*
+       * TODO(jat): support JS strings with international characters
+       */
+      JsStringWrap str(cx, JSVAL_TO_STRING(val));
+      p += append_sprintf(p, bufEnd, ": %.*s", str.length(), str.bytes());
+      break;
+    }
+  }
+  Tracer::log("%s", buf);
+}
+#else
+// Include a null version just to keep from cluttering up call sites.
+static inline void PrintJSValue(JSContext* cx, jsval val, char* prefix="") { }
+#endif
+
+
+static bool InitGlobals(JNIEnv* env, jclass llClass) {
+  if (savedJNIEnv)
+    return false;
+
+#ifdef FILETRACE
+  Tracer::setFile("gwt-ll.log");
+#endif // FILETRACE
+
+#ifdef JAVATRACE
+  Tracer::setJava(env, llClass);
+#endif // JAVATRACE
+
+#ifdef DEBUG
+  Tracer::setLevel(Tracer::LEVEL_DEBUG);
+#endif
+
+  savedJNIEnv = env;
+  lowLevelMozClass = static_cast<jclass>(env->NewGlobalRef(llClass));
+  return true;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
+ * Method:    _executeScriptWithInfo
+ * Signature: (ILjava/lang/String;Ljava/lang/String;I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo
+    (JNIEnv* env, jclass llClass, jint scriptObjectInt, jstring code,
+     jstring file, jint line)
+{
+  Tracer tracer("LowLevelMoz._executeScriptWithInfo");
+  JStringWrap jcode(env, code);
+  if (!jcode.jstr()) {
+    tracer.setFail("null code string");
+    return JNI_FALSE;
+  }
+  JStringWrap jfile(env, file);
+  if (!jfile.str()) {
+    tracer.setFail("null file name");
+    return JNI_FALSE;
+  }
+  tracer.log("code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
+  JSContext* cx = JsRootedValue::currentContext();
+  nsCOMPtr<nsIScriptContext> scriptContext(GetScriptContextFromJSContext(cx));
+
+  nsIScriptGlobalObject* scriptObject =
+      NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjectInt);
+  JSObject* scriptWindow =
+      reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject());
+  nsXPIDLString scriptString;
+  scriptString = jcode.jstr();
+
+  nsXPIDLString aRetValue;
+  PRBool aIsUndefined;
+  if (NS_FAILED(scriptContext->EvaluateString(scriptString, scriptWindow, 0,
+      jfile.str(), line, 0, aRetValue, &aIsUndefined))) {
+    tracer.setFail("EvaluateString failed");
+    return JNI_FALSE;
+  }
+  return JNI_TRUE;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
+ * Method:    _invoke
+ * Signature: (ILjava/lang/String;I[I)I
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke
+    (JNIEnv* env, jclass, jint scriptObjInt, jstring methodName, jint jsThisInt,
+     jintArray jsArgsInt, jint jsRetValInt)
+{
+  Tracer tracer("LowLevelMoz._invoke");
+
+  JStringWrap methodStr(env, methodName);
+  if (!methodStr.str()) {
+    tracer.setFail("null method name");
+    return JNI_FALSE;
+  }
+  JsRootedValue* jsThisRV = reinterpret_cast<JsRootedValue*>(jsThisInt);
+  jint jsArgc = env->GetArrayLength(jsArgsInt);
+  tracer.log("method=%s, jsthis=%08x, #args=%d", methodStr.str(), jsThisInt,
+     jsArgc);
+  JSContext* cx = JsRootedValue::currentContext();
+  nsIScriptGlobalObject* scriptObject =
+      NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
+  JSObject* scriptWindow
+      = reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject());
+
+  jsval fval;
+  if (!JS_GetProperty(cx, scriptWindow, methodStr.str(), &fval)) {
+    tracer.setFail("JS_GetProperty(method) failed");
+    return JNI_FALSE;
+  }
+  JSFunction* jsFunction = JS_ValueToFunction(cx, fval);
+  if (!jsFunction) {
+    tracer.setFail("JS_ValueToFunction failed");
+    return JNI_FALSE;
+  }
+  
+  // extract arguments in jsval form
+  nsAutoArrayPtr<jint> jsargvals(new jint[jsArgc]);
+  if (!jsargvals) {
+    tracer.setFail("failed to allocate arg array");
+    return JNI_FALSE;
+  }
+  env->GetIntArrayRegion(jsArgsInt, 0, jsArgc, jsargvals);
+  if (env->ExceptionCheck()) {
+    tracer.setFail("copy from Java array failed");
+    return JNI_FALSE;
+  }
+  nsAutoArrayPtr<jsval> jsargs(new jsval[jsArgc]);
+  for (int i = 0; i < jsArgc; ++i) {
+    JsRootedValue* arg = reinterpret_cast<JsRootedValue*>(jsargvals[i]);
+    jsargs[i] = arg->getValue();
+  }
+
+  jsval jsrval;
+  JSObject* jsThis;
+  if (jsThisRV->isNull()) {
+    jsThis = scriptWindow;
+  } else {
+    jsThis = jsThisRV->getObject();
+  }
+  
+  PrintJSValue(cx, OBJECT_TO_JSVAL(jsThis), "jsThis=");
+  for (int i = 0; i < jsArgc; ++i) {
+    char buf[256];
+    snprintf(buf, sizeof(buf), "arg[%d]=", i);
+    PrintJSValue(cx, jsargs[i], buf);
+  }
+  //tracer.log("fval = %08x, args=%08x", fval, jsargs.get());
+  if (!JS_CallFunctionValue(cx, jsThis, fval, jsArgc, jsargs.get(), &jsrval)) {
+    tracer.setFail("JS_CallFunctionValue failed");
+    return JNI_FALSE;
+  }
+
+  PrintJSValue(cx, jsrval, "return value=");
+  JsRootedValue* returnVal = reinterpret_cast<JsRootedValue*>(jsRetValInt);
+  returnVal->setValue(jsrval);
+  return JNI_TRUE;
+}
+
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
+ * Method:    _raiseJavaScriptException
+ * Signature: ()Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException
+    (JNIEnv* env, jclass)
+{
+  Tracer tracer("LowLevelMoz._raiseJavaScriptException");
+  JS_SetPendingException(JsRootedValue::currentContext(), JSVAL_NULL);
+  return JNI_TRUE;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
+ * Method:    _registerExternalFactoryHandler
+ * Signature: ()Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler
+    (JNIEnv* env, jclass llClass)
+{
+  if (!InitGlobals(env, llClass))
+    return JNI_FALSE;
+
+  // tracing isn't setup until after InitGlobals is called
+  Tracer tracer("LowLevelMoz._registerExternalFactoryHandler");
+
+  char buf[256];
+  sprintf(buf, " jniEnv=%08x, llClass=%08x", (unsigned)env, (unsigned)llClass);
+  tracer.log(buf);
+  
+  // Register "window.external" as our own class
+  if (NS_FAILED(nsComponentManager::RegisterFactory(
+      kGwtExternalCID, "externalFactory", GWT_EXTERNAL_CONTRACTID,
+      new nsRpExternalFactory(), PR_TRUE))) {
+    tracer.setFail("RegisterFactory failed");
+    return JNI_FALSE;
+  }
+
+  nsCOMPtr<nsICategoryManager> categoryManager =
+      do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+  if (!categoryManager) {
+    tracer.setFail("unable to get category manager");
+    return JNI_FALSE;
+  }
+
+  nsXPIDLCString previous;
+  if (NS_FAILED(categoryManager->AddCategoryEntry(
+      JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY, "external", GWT_EXTERNAL_CONTRACTID,
+      PR_TRUE, PR_TRUE, getter_Copies(previous)))) {
+    tracer.setFail("AddCategoryEntry failed");
+    return JNI_FALSE;
+  }
+
+  return JNI_TRUE;
+}
diff --git a/jni/linux/Makefile b/jni/linux/Makefile
new file mode 100644
index 0000000..8b3a8cf
--- /dev/null
+++ b/jni/linux/Makefile
@@ -0,0 +1,153 @@
+# 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.
+
+##
+# Target settings
+##
+#JAVA_HOME
+#JAVA_HOME=/usr/lib/j2sdk1.5-sun
+
+# set GWT_TOOLS only if it is not already defined
+GWT_TOOLS ?= ../../../tools
+
+GWT_ROOT = ../../
+OBJDIR  := $(GWT_ROOT)build/out/jni/linux/
+OUTDIR  := $(GWT_ROOT)build/jni/linux/
+OUT     := $(OUTDIR)libgwt-ll.so
+STAGING := $(GWT_ROOT)build/staging/gwt-linux-0.0.0/
+
+##
+# The location to get .class files from for javah
+##
+CLASSDIR := $(GWT_ROOT)build/out/dev/linux/bin
+
+# use this instead if you want to use eclipse to build the class files during
+# development
+#CLASSDIR := $(GWT_ROOT)eclipse/dev/linux/bin
+
+##
+# Tools
+##
+CXX      := g++
+AR       := ar
+STRIP    := strip
+LD       := $(CXX)
+JAVAH    := $(JAVA_HOME)/bin/javah
+
+##
+# List of source, object, and dependency paths plus the path to them
+##
+SRCDIRS := ./:../core/
+VPATH   := .:../core
+SRCS    := gwt-ll.cpp LowLevelMoz.cpp JsValueMoz.cpp Tracer.cpp \
+	ExternalWrapper.cpp NativeWrapper.cpp JsRootedValue.cpp
+OBJS    := $(addprefix $(OBJDIR),$(SRCS:.cpp=.o))
+DEPS    := $(addprefix $(OBJDIR),$(SRCS:.cpp=.d))
+
+MOZDIR = $(GWT_TOOLS)/sdk/mozilla-1.7.12
+MOZINC = $(MOZDIR)/include
+MOZLIB = $(MOZDIR)/lib
+
+##
+# Include path configuration
+##
+SYSINCS := \
+    $(JAVA_HOME)/include \
+    $(JAVA_HOME)/include/linux \
+    $(MOZINC) $(MOZINC)/extra
+
+INCS := $(OBJDIR)
+INCS := $(addprefix -I ,$(INCS)) $(addprefix -isystem ,$(SYSINCS))
+
+##
+# Libraries and library path
+##
+LIBS    = xpcomglue_s
+LIBPATH = -L$(MOZLIB)
+LIBS     := $(addprefix -l,$(LIBS))
+
+# for notes on auto-dependency generation, see
+#   http://make.paulandlesley.org/autodep.html
+# -MP obviates the need for sed hackery
+CFLAGS   := -ggdb -m32 -Os -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -MMD -MP -Wno-system-headers $(CFLAGS)
+LDFLAGS  := -ggdb -m32 -s -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -Wl,-shared-gcc $(LDFLAGS)
+
+#-------------------------------------------------------------------------------
+# Rules
+#-------------------------------------------------------------------------------
+
+##
+# default rule
+##
+all: $(OUT)
+
+# install into prebuilt directory
+install: $(OUT)
+	cp $(OUT) prebuilt/
+
+##
+# Include the dependency rules
+##
+-include $(DEPS)
+
+##
+# javah-generated headers for native methods
+##
+$(OBJDIR)LowLevelMoz.h : $(CLASSDIR)/com/google/gwt/dev/shell/moz/LowLevelMoz.class
+	$(JAVAH) -classpath $(CLASSDIR) -o $(OBJDIR)LowLevelMoz.h com.google.gwt.dev.shell.moz.LowLevelMoz
+
+$(OBJDIR)JsValueMoz.h : $(CLASSDIR)/com/google/gwt/dev/shell/moz/JsValueMoz.class
+	$(JAVAH) -classpath $(CLASSDIR) -o $(OBJDIR)JsValueMoz.h com.google.gwt.dev.shell.moz.JsValueMoz
+
+##
+# Dependency rules for generated headers
+# TODO(jat): autogenerate these and others
+##
+$(OBJDIR)LowLevelMoz.o: $(OBJDIR)LowLevelMoz.h $(JAVA_HOME)/include/jni.h
+$(OBJDIR)JsValueMoz.o: $(OBJDIR)JsValueMoz.h $(JAVA_HOME)/include/jni.h
+
+##
+# Compilation rule for cpp files
+##
+$(OBJDIR)%.o : $(SRCDIR)%.cpp
+	@[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR)
+	$(CXX) -c $(CFLAGS) $(INCS) -o $@ $<
+
+# generate preprocessed output files upon request for debugging
+%.i : $(SRCDIR)%.cpp
+	$(CXX) -E $(CFLAGS) $(INCS) -o $@ $<
+
+%.I : $(SRCDIR)%.cpp
+	$(CXX) -E -dDI $(CFLAGS) $(INCS) -o $@ $<
+
+##
+# Actual output file
+##
+$(OUT): $(OBJS) $(MOZLIB)/libxpcomglue_s.a
+	@[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR)
+	$(LD) -shared $(LDFLAGS) $(LIBPATH) -o $@ $^ $(LIBS)
+	$(STRIP) --strip-unneeded $@
+
+##
+# copy to staging area for hosted-mode development
+##
+staging: $(OUT)
+	@[ -d $(STAGING) ] || mkdir -p $(STAGING)
+	cp $(OUT) $(STAGING)
+
+##
+# Clean rule
+##
+clean:
+	@-rm -rf $(OBJDIR) $(OUT)
diff --git a/jni/linux/NativeWrapper.cpp b/jni/linux/NativeWrapper.cpp
new file mode 100644
index 0000000..38958d0
--- /dev/null
+++ b/jni/linux/NativeWrapper.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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 classes gwt_nativewrapper_class and
+ * gwt_functionwrapper_class, which interface via JNI to Java objects.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "Tracer.h"
+
+/*
+ * Helper function to get reference Java attributes from Javascript.
+ * 
+ * cx - JSContext pointer
+ * obj - JavaScript object which is a wrapped Java object
+ * id - property name, as a jsval string
+ * dispClass - output parameter of DispatchMethod subclass
+ * dispObj - output parameter of Java object
+ * jident - output parameter of property name as a Java string
+ */
+static JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
+    jclass& dispClass, jobject& dispObj, jstring& jident)
+{
+  Tracer tracer("getJavaPropertyStats");
+  if (!JSVAL_IS_STRING(id)) {
+    tracer.setFail("id is not a string");
+    return JS_FALSE;
+  }
+
+  jident = savedJNIEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)),
+      JS_GetStringLength(JSVAL_TO_STRING(id)));
+  if (!jident || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("unable to create Java string");
+    return JS_FALSE;
+  }
+
+  dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  if (!dispObj) {
+    tracer.setFail("can't get dispatch object");
+    return JS_FALSE;
+  }
+
+  dispClass = savedJNIEnv->GetObjectClass(dispObj);
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("can't get class of dispatch object");
+    return JS_FALSE;
+  }
+
+  return JS_TRUE;
+}
+  
+/*
+ * Returns the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to retreive field from
+ * fieldName - name of field on Java object to retrieve
+ * 
+ * Returns null on failure.  Caller is responsible for deleting
+ * returned JsRootedValue when done with it.
+ */
+static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz,
+    jobject obj, jstring fieldName)
+{
+  Tracer tracer("GetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+    return 0;
+  }
+
+  JsRootedValue* jsRootedValue = new JsRootedValue();
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+    reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck()) {
+    delete jsRootedValue;
+    return 0;
+  }
+  return jsRootedValue;
+}
+  
+/*
+ * Sets the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to store into field
+ * fieldName - name of field on Java object to store into
+ * jsRootedValue - the value to store in the field
+ * 
+ * returns true on success, false on failure
+ */
+static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz,
+    jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
+{
+  Tracer tracer("SetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+    return false;
+  }
+
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+    reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck()) {
+    return false;
+  }
+
+  return true;
+}
+
+static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx,
+    JSObject *obj, jsval id, jsval *vp)
+{
+  Tracer tracer("gwt_nativewrapper_getProperty");
+  tracer.log("context=%08x, obj=%08x", unsigned(cx), unsigned(obj));
+  JsRootedValue::ContextManager context(cx);
+
+  jclass dispClass;
+  jobject dispObj;
+  jstring ident;
+  if (!getJavaPropertyStats(cx, obj, id, dispClass, dispObj, ident)) {
+    tracer.setFail("getJavaPropertyStats failed");
+    return JS_FALSE;
+  }
+  JsRootedValue* js_rooted_value = GetFieldAsRootedValue(cx, dispClass,
+      dispObj, ident);
+  if (!js_rooted_value) {
+    tracer.setFail("can't get field");
+    return JS_FALSE;
+  }
+  *vp = js_rooted_value->getValue();
+  return JS_TRUE;
+}
+
+static void JS_DLL_CALLBACK gwt_nativewrapper_finalize(JSContext *cx,
+    JSObject *obj)
+{
+  Tracer tracer("gwt_nativewrapper_finalize");
+  jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  if (dispObj) {
+    // Remove this pairing from the global map.
+    jmethodID removeMethod = savedJNIEnv->GetStaticMethodID(lowLevelMozClass, "removeJsvalForObject",
+      "(Ljava/lang/Object;)V");
+    if (!removeMethod || savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("Cannot GetMethodID for removeJsvalForObject");
+      return;
+    }
+    savedJNIEnv->CallStaticVoidMethod(lowLevelMozClass, removeMethod, dispObj);
+    if (savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("Exception calling removeJsvalForObject");
+      return;
+    }
+    savedJNIEnv->DeleteGlobalRef(dispObj);
+  }
+}
+
+static JSBool JS_DLL_CALLBACK gwt_nativewrapper_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+  Tracer tracer("gwt_nativewrapper_setProperty");
+  tracer.log("context=%08x", unsigned(cx));
+  JsRootedValue::ContextManager context(cx);
+  jclass dispClass;
+  jobject dispObj;
+  jstring ident;
+  if (!getJavaPropertyStats(cx, obj, id, dispClass, dispObj, ident)) {
+    tracer.setFail("getJavaPropertyStats failed");
+    return JS_FALSE;
+  }
+  JsRootedValue* js_rooted_value = new JsRootedValue(*vp); 
+  if (!SetFieldFromRootedValue(cx, dispClass, dispObj, ident,
+      js_rooted_value)) {
+    tracer.setFail("can't set field");
+    return JS_FALSE;
+  }
+  return JS_TRUE;
+}
+
+JSClass gwt_nativewrapper_class = {
+  "gwt_nativewrapper_class", JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, gwt_nativewrapper_getProperty,
+  gwt_nativewrapper_setProperty, JS_EnumerateStub, JS_ResolveStub,
+  JS_ConvertStub, gwt_nativewrapper_finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSClass gwt_functionwrapper_class = {
+  "gwt_functionwrapper_class", JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
diff --git a/jni/linux/Tracer.cpp b/jni/linux/Tracer.cpp
new file mode 100644
index 0000000..e5f3189
--- /dev/null
+++ b/jni/linux/Tracer.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#include "Tracer.h"
+
+#ifdef ENABLE_TRACING
+
+// initialize static fields
+FILE* Tracer::outfp = 0;
+JNIEnv* Tracer::jniEnv = 0;
+jclass Tracer::traceClass;
+jmethodID Tracer::traceMethod;
+int Tracer::indentation = 0;
+Tracer::LogLevel Tracer::logLevel = Tracer::LEVEL_WARNING;
+
+/*
+ * Sets a JNI environment and Java class to pass trace messages to.
+ * 
+ * env - JNI environment to use for trace calls
+ * clazz - Java class, which must provide static void trace(String)
+ */
+bool Tracer::setJava(JNIEnv* env, jclass clazz) {
+  jniEnv = env;
+  if (!env) {
+    return true;
+  }
+  traceClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+  if (!traceClass || env->ExceptionCheck()) {
+    return false;
+  }
+  traceMethod = env->GetStaticMethodID(traceClass, "trace",
+    "(Ljava/lang/String;)V");
+  if (!traceMethod || env->ExceptionCheck()) {
+    return false;
+  }
+
+  jstring msg = jniEnv->NewStringUTF("== Java trace started ==");
+  jniEnv->CallStaticVoidMethod(traceClass, traceMethod, msg);
+  return true;
+}
+
+#endif // ENABLE_TRACING
+
+/*
+ * Throw a HostedModeException and log a failure message.
+ * 
+ * Creates a new HostedModeException with the failure message,
+ * and also logs the failure message 
+ * 
+ * env - JNI environment to throw the exception in
+ * msg - failure message 
+ */
+void Tracer::throwHostedModeException(JNIEnv* env, const char* msg) {
+#ifdef ENABLE_TRACING
+  setFail(msg);
+#endif // ENABLE_TRACING
+  jclass exceptionClass
+      = env->FindClass("com/google/gwt/dev/shell/HostedModeException");
+  env->ThrowNew(exceptionClass, msg);
+}
diff --git a/jni/linux/Tracer.h b/jni/linux/Tracer.h
new file mode 100644
index 0000000..ef9fd7d
--- /dev/null
+++ b/jni/linux/Tracer.h
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+#ifndef JNI_LINUX_TRACER_H_
+#define JNI_LINUX_TRACER_H_
+
+#include <cstdio>
+#include <cstdarg>
+#include <cstring>
+#include <jni.h>
+
+// comment this out to remove almost all runtime overhead (with usual compiler
+// support) from tracing.
+//#define ENABLE_TRACING
+
+/*
+ * Utility class for tracing.  This class is intended to be used as follows:
+ * 
+ * {
+ *   Tracer tracer("method name");
+ *   ... do work
+ *   if (fail) {
+ *      tracer.setFail("failure explanation");
+ *      return;
+ *   }
+ *   if (fail2) {
+ *      tracer.throwHostedModeException("failure explanation");
+ *      return;
+ *   }
+ *   return;
+ * }
+ * 
+ * The class automatically logs an enter message when it is created, as well
+ * as leave/fail messages when it is destroyed.  Logging is performed to a
+ * file or to a Java static member function on a class (or both) -- these
+ * are configured by using static member functions setFile() and setJava().
+ * 
+ * This class knows about the Java class
+ *   com.google.gwt.dev.shell.HostedModeException
+ * and throws a new instance of that exception if requested.
+ */
+class Tracer {
+public:
+  enum LogLevel {
+    LEVEL_ERROR = 0,
+    LEVEL_WARNING,
+    LEVEL_NOTICE,
+    LEVEL_INFO,
+    LEVEL_DEBUG,
+    LEVEL_DEBUG_V1,
+    LEVEL_DEBUG_V2,
+  };
+protected:
+#ifdef ENABLE_TRACING
+  // static variables that specify where logging is performed.  This are
+  // set by calling setFile() and setJava().
+  static FILE*     outfp;
+  static JNIEnv*   jniEnv;
+  static jclass    traceClass;
+  static jmethodID traceMethod;
+  static int       indentation;
+  static LogLevel  logLevel;
+
+  // method is set when the instance is created.
+  const char*		method_;
+  // fail_msg is set to indicate a failure has occurred.
+  const char*		fail_msg_;
+  // level of this trace object
+  LogLevel  log_level_;
+#endif
+  
+public:
+  /*
+   * Set the logging level.
+   */
+  static void setLevel(LogLevel level) {
+#ifdef ENABLE_TRACING
+    logLevel = level;
+#endif
+  }
+  
+protected:
+  /*
+   * Log a message (with supplied prefix) to the configured file.
+   * Only called if a file was specified and successfully opened for writing.
+   */
+  static void logFile(const char* msg) {
+#ifdef ENABLE_TRACING
+    for (int i = 0; i < indentation; ++i) {
+      putc(' ', outfp);
+    }
+    fputs(msg, outfp);
+    putc('\n', outfp);
+    fflush(outfp);
+#else
+    (void)msg; // avoid unused warning
+#endif
+  }
+
+  /*
+   * Log a message (with supplied prefix) to the configured Java class.
+   * Only called if a file was specified and successfully accessed.
+   * 
+   * Call static void trace(String msg) on the configured class. 
+   */
+  static void logJava(const char* msg) {
+#ifdef ENABLE_TRACING
+    // TODO(jat): fixed buffer size
+    char buf[512];
+    for (int i = 0; (i < indentation) && (i < int(sizeof(buf))); ++i) {
+      buf[i] = ' ';
+    }
+    strncpy(buf + indentation, msg, sizeof(buf) - indentation);
+    buf[sizeof(buf) - 1] = 0; // ensure null termination
+    jstring str = jniEnv->NewStringUTF(buf);
+    jniEnv->CallStaticVoidMethod(traceClass, traceMethod, str);
+#else
+    (void)msg; // avoid unused warning
+#endif
+  }
+
+  /*
+   * Log a message to a file and/or class with the default logging level.
+   * 
+   * If the preprocessor symbol DISABLE_TRACING has been defined, this is
+   * completely removed from the code path.
+   */
+  void logPrefix(const char* prefix) {
+#ifdef ENABLE_TRACING
+    logPrefix(prefix, log_level_);
+#else
+    (void)prefix; // avoid unused warning
+#endif
+  }
+  
+  /*
+   * Log a message to a file and/or class.
+   * 
+   * If the preprocessor symbol DISABLE_TRACING has been defined, this is
+   * completely removed from the code path.
+   */
+  void logPrefix(const char* prefix, LogLevel level) {
+#ifdef ENABLE_TRACING
+    if (level>logLevel) return;
+    log("%-5.5s %s%s%s", prefix, method_, fail_msg_ ? ": " : "",
+        fail_msg_ ? fail_msg_ : "");
+#endif
+  }
+    
+public:
+  /*
+   * Create an instance with the specified method name and no failure
+   * message.  Log an ENTER message.
+   */
+  Tracer(const char* method, LogLevel log_level = LEVEL_ERROR)
+#ifdef ENABLE_TRACING
+      : method_(method), fail_msg_(0), log_level_(log_level) {
+    log("ENTER %s", method);
+    indentation++;
+#else
+  { (void)method; (void)log_level; // avoid unused warnings
+#endif
+  }
+
+  /*
+   * Create an instance with the specified method name and no failure
+   * message.  Log an ENTER message and the this pointer.
+   */
+  Tracer(const char* method, const void* objThis,
+      LogLevel log_level = LEVEL_ERROR)
+#ifdef ENABLE_TRACING
+      : method_(method), fail_msg_(0), log_level_(log_level) {
+    log("ENTER %s(this=%08x)", method, unsigned(objThis));
+    indentation++;
+#else
+  { (void)method; (void)objThis; (void)log_level; // avoid unused warnings
+#endif
+  }
+
+  /*
+   * Destroy the instance and log a fail or leave message.
+   */
+  ~Tracer() {
+#ifdef ENABLE_TRACING
+    --indentation;
+    if(fail_msg_) {
+      logPrefix("*FAIL", LEVEL_ERROR);
+    } else {
+      logPrefix("LEAVE");
+    }
+#endif
+  }
+  
+  /*
+   * Specify a filename to receive logging output.  Close any previously
+   * opened file.  If a null filename is passed, disable logging to a
+   * file.
+   * 
+   * filename - the file path to receive logging output.  This file is
+   *     truncated if it already exists.
+   * 
+   * Returns false on failure.
+   */
+  static bool setFile(const char* filename) {
+#ifdef ENABLE_TRACING
+    if (outfp) {
+      fclose(outfp);
+      outfp = 0;
+    }
+    if (!filename) {
+      return true;
+    }
+    outfp = fopen(filename, "w");
+    if (!outfp) {
+      return false;
+    }
+    fprintf(outfp, "== started logging ==\n");
+    fflush(outfp);
+#else
+    (void)filename; // avoid unused warning
+#endif
+    return true;
+  }
+  
+  /*
+   * Specify a Java class to receive logging output.  The supplied class
+   * must have a static void trace(String) member function which is called
+   * for output.  Logging to a Java class is disabled if the supplied JNI
+   * environment is null.
+   * 
+   * env - JNI environment
+   * clazz - the Java class to receive logging output
+   * 
+   * Returns false on failure.
+   */ 
+  static bool setJava(JNIEnv* env, jclass clazz)
+#ifdef ENABLE_TRACING  
+  ;
+#else
+  // inline a null body if we aren't debugging; avoid unused warnings
+  { (void)env; (void)clazz; return true; }
+#endif
+  
+  /*
+   * Set a failure message, overwriting any previously specified failure
+   * message.  Passing a null string will remove any previous failure
+   * notification.
+   */
+  void setFail(const char* fail_msg) {
+#ifdef ENABLE_TRACING
+    fail_msg_ = fail_msg;
+#else
+    (void)fail_msg; // avoid unused warning
+#endif
+  }
+  
+  /*
+   * Throw a Java HostedModeException as well as set a failure message to
+   * be logged.
+   * 
+   * env - JNI environment to throw exception into
+   * fail_msg - failure message 
+   */
+  void throwHostedModeException(JNIEnv* env, const char* fail_msg);
+  
+  /*
+   * Log an arbitrary message.
+   */
+  static void log(const char* format, ...) {
+#ifdef ENABLE_TRACING
+    va_list args;
+    va_start(args, format);
+    char msg[512]; // TODO(jat): fixed size buffer
+    vsnprintf(msg, sizeof(msg), format, args);
+    msg[sizeof(msg) - 1] = 0; // ensure null termination
+    if(outfp) logFile(msg);
+    if(jniEnv) logJava(msg);
+    va_end(args);
+#else
+    (void)format; // avoid unused warning
+#endif
+  }
+};
+
+#endif /* JNI_LINUX_TRACER_H_ */
diff --git a/jni/linux/build.xml b/jni/linux/build.xml
new file mode 100755
index 0000000..5f0db6b
--- /dev/null
+++ b/jni/linux/build.xml
@@ -0,0 +1,18 @@
+<project name="jni-linux" default="build" basedir=".">
+  <property name="gwt.root" location="../.." />
+  <property name="project.tail" value="jni/linux" />
+  <import file="${gwt.root}/common.ant.xml" />
+
+  <target name="build" description="Builds a JNI lib">
+    <mkdir dir="${project.jni}" />
+    <!-- TODO: Actually build this from source! -->
+    <copy todir="${project.jni}">
+      <fileset dir="prebuilt" />
+    </copy>
+  </target>
+
+  <target name="clean" description="Cleans this project's intermediate and output files">
+    <delete dir="${project.build}" failonerror="false" />
+    <delete dir="${project.jni}" failonerror="false" />
+  </target>
+</project>
diff --git a/jni/linux/gwt-jni.h b/jni/linux/gwt-jni.h
new file mode 100644
index 0000000..c477f1c
--- /dev/null
+++ b/jni/linux/gwt-jni.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#ifndef JNI_LINUX_GWT_JNI_H_
+#define JNI_LINUX_GWT_JNI_H_
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "JStringWrap.h"
+
+extern JNIEnv* savedJNIEnv;
+extern jclass lowLevelMozClass;
+
+extern nsCID kGwtExternalCID;
+
+// JavaScript class objects
+extern JSClass gwt_nativewrapper_class;
+extern JSClass gwt_functionwrapper_class;
+
+#endif /*JNI_LINUX_GWT_JNI_H_*/
diff --git a/jni/linux/mozilla-headers.h b/jni/linux/mozilla-headers.h
new file mode 100644
index 0000000..6259b41
--- /dev/null
+++ b/jni/linux/mozilla-headers.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+// include all of the necessary Mozilla headers.
+#include "mozilla-config.h"
+#include "nsIServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsICategoryManager.h"
+#include "nsIScriptNameSpaceManager.h"
+#include "nsIScriptObjectOwner.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsIDOMWindow.h"
+#include "nsIXPConnect.h"
+#include "nsIFactory.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so
new file mode 100755
index 0000000..97ead651
--- /dev/null
+++ b/jni/linux/prebuilt/libgwt-ll.so
Binary files differ