Bug 564327 - Add Android widgets backend [2/2], patch by vlad, alexp, blassey, and me. r=dougt, sr=roc
authorMichael Wu <mwu@mozilla.com>
Thu, 03 Jun 2010 13:56:36 -0700
changeset 43066 1c416ab33c8e6cebc2c74d0f36cf76b5a0a6b2dd
parent 43065 fb1e226d7c1171084d32d1c0ec5ee1e0a8bd8fe5
child 43067 cac6db5caec80fb1f3fbd926ad4338ff835a503b
push idunknown
push userunknown
push dateunknown
reviewersdougt, roc
bugs564327
milestone1.9.3a5pre
Bug 564327 - Add Android widgets backend [2/2], patch by vlad, alexp, blassey, and me. r=dougt, sr=roc
toolkit/library/libxul-config.mk
toolkit/library/nsStaticXULComponents.cpp
widget/src/Makefile.in
widget/src/android/AndroidBridge.cpp
widget/src/android/AndroidBridge.h
widget/src/android/AndroidJNI.cpp
widget/src/android/AndroidJavaWrappers.cpp
widget/src/android/AndroidJavaWrappers.h
widget/src/android/Makefile.in
widget/src/android/nsAccelerometerAndroid.cpp
widget/src/android/nsAccelerometerAndroid.h
widget/src/android/nsAppShell.cpp
widget/src/android/nsAppShell.h
widget/src/android/nsIdleServiceAndroid.cpp
widget/src/android/nsIdleServiceAndroid.h
widget/src/android/nsLookAndFeel.cpp
widget/src/android/nsLookAndFeel.h
widget/src/android/nsScreenManagerAndroid.cpp
widget/src/android/nsScreenManagerAndroid.h
widget/src/android/nsToolkit.cpp
widget/src/android/nsToolkit.h
widget/src/android/nsWidgetFactory.cpp
widget/src/android/nsWindow.cpp
widget/src/android/nsWindow.h
widget/src/xpwidgets/nsWidgetAtomList.h
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -290,16 +290,20 @@ DEFINES += -DMOZ_ENABLE_POSTSCRIPT
 STATIC_LIBS += gfxpsshar
 endif
 
 ifneq (,$(filter icon,$(MOZ_IMG_DECODERS)))
 DEFINES += -DICON_DECODER
 COMPONENT_LIBS += imgicon
 endif
 
+ifeq ($(MOZ_WIDGET_TOOLKIT),android)
+COMPONENT_LIBS += widget_android
+endif
+
 STATIC_LIBS += thebes ycbcr
 COMPONENT_LIBS += gkgfxthebes
 
 ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
 COMPONENT_LIBS += gkwidget
 endif
 ifeq (beos,$(MOZ_WIDGET_TOOLKIT))
 COMPONENT_LIBS += widget_beos
@@ -370,8 +374,12 @@ ifdef MOZ_SYDNEYAUDIO
 ifeq ($(OS_ARCH),Linux)
 EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
 endif
 endif
 
 ifdef HAVE_CLOCK_MONOTONIC
 EXTRA_DSO_LDOPTS += $(REALTIME_LIBS)
 endif
+
+ifeq (android,$(MOZ_WIDGET_TOOLKIT))
+OS_LIBS += -lGLESv2
+endif
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -76,16 +76,18 @@
 #elif defined(XP_BEOS)
 #  define WIDGET_MODULES MODULE(nsWidgetBeOSModule)
 #elif defined(XP_OS2)
 #  define WIDGET_MODULES MODULE(nsWidgetOS2Module)
 #elif defined(MOZ_WIDGET_GTK2)
 #  define WIDGET_MODULES MODULE(nsWidgetGtk2Module)
 #elif defined(MOZ_WIDGET_QT)
 #  define WIDGET_MODULES MODULE(nsWidgetQtModule)
+#elif defined(MOZ_WIDGET_ANDROID)
+#  define WIDGET_MODULES MODULE(nsWidgetAndroidModule)
 #else
 #  error Unknown widget module.
 #endif
 
 #ifdef ICON_DECODER
 #define ICON_MODULE MODULE(nsIconDecoderModule)
 #else
 #define ICON_MODULE
--- a/widget/src/Makefile.in
+++ b/widget/src/Makefile.in
@@ -41,17 +41,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE  = widget
 
 DIRS		= xpwidgets
 
-ifneq (,$(filter beos os2 cocoa qt,$(MOZ_WIDGET_TOOLKIT)))
+ifneq (,$(filter beos os2 cocoa qt android,$(MOZ_WIDGET_TOOLKIT)))
 DIRS		+= $(MOZ_WIDGET_TOOLKIT)
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 DIRS		+= windows build
 endif
 
 #
new file mode 100644
--- /dev/null
+++ b/widget/src/android/AndroidBridge.cpp
@@ -0,0 +1,237 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <android/log.h>
+
+#include <pthread.h>
+#include <prthread.h>
+
+#include "AndroidBridge.h"
+
+using namespace mozilla;
+
+static PRUintn sJavaEnvThreadIndex = 0;
+
+AndroidBridge *AndroidBridge::sBridge = 0;
+
+static void
+JavaThreadDetachFunc(void *arg)
+{
+    JNIEnv *env = (JNIEnv*) arg;
+    JavaVM *vm = NULL;
+    env->GetJavaVM(&vm);
+    vm->DetachCurrentThread();
+}
+
+AndroidBridge *
+AndroidBridge::ConstructBridge(JNIEnv *jEnv,
+                               jclass jGeckoAppShellClass)
+{
+    /* NSS hack -- bionic doesn't handle recursive unloads correctly,
+     * because library finalizer functions are called with the dynamic
+     * linker lock still held.  This results in a deadlock when trying
+     * to call dlclose() while we're already inside dlclose().
+     * Conveniently, NSS has an env var that can prevent it from unloading.
+     */
+    putenv(strdup("NSS_DISABLE_UNLOAD=1"));
+
+    sBridge = new AndroidBridge();
+    if (!sBridge->Init(jEnv, jGeckoAppShellClass)) {
+        delete sBridge;
+        sBridge = 0;
+    }
+
+    PR_NewThreadPrivateIndex(&sJavaEnvThreadIndex, JavaThreadDetachFunc);
+
+    return sBridge;
+}
+
+PRBool
+AndroidBridge::Init(JNIEnv *jEnv,
+                    jclass jGeckoAppShellClass)
+{
+    jEnv->GetJavaVM(&mJavaVM);
+
+    mJNIEnv = nsnull;
+    mThread = nsnull;
+
+    mGeckoAppShellClass = (jclass) jEnv->NewGlobalRef(jGeckoAppShellClass);
+
+    jShowIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showIME", "(I)V");
+    jEnableAccelerometer = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableAccelerometer", "(Z)V");
+    jReturnIMEQueryResult = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "returnIMEQueryResult", "(Ljava/lang/String;II)V");
+    jScheduleRestart = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scheduleRestart", "()V");
+    jNotifyXreExit = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onXreExit", "()V");
+
+    InitAndroidJavaWrappers(jEnv);
+
+    // jEnv should NOT be cached here by anything -- the jEnv here
+    // is not valid for the real gecko main thread, which is set
+    // at SetMainThread time.
+
+    return PR_TRUE;
+}
+
+JNIEnv *
+AndroidBridge::AttachThread(PRBool asDaemon)
+{
+    JNIEnv *jEnv = (JNIEnv*) PR_GetThreadPrivate(sJavaEnvThreadIndex);
+    if (jEnv)
+        return jEnv;
+
+    JavaVMAttachArgs args = {
+        JNI_VERSION_1_2,
+        "GeckoThread",
+        NULL
+    };
+
+    jint res = 0;
+
+    if (asDaemon) {
+        res = mJavaVM->AttachCurrentThreadAsDaemon(&jEnv, &args);
+    } else {
+        res = mJavaVM->AttachCurrentThread(&jEnv, &args);
+    }
+
+    if (res != 0) {
+        ALOG("AttachCurrentThread failed!");
+        return nsnull;
+    }
+
+    PR_SetThreadPrivate(sJavaEnvThreadIndex, jEnv);
+
+    return jEnv;
+}
+
+PRBool
+AndroidBridge::SetMainThread(void *thr)
+{
+    if (thr) {
+        mJNIEnv = AttachThread(PR_FALSE);
+        if (!mJNIEnv)
+            return PR_FALSE;
+
+        mThread = thr;
+    } else {
+        mJNIEnv = nsnull;
+        mThread = nsnull;
+    }
+
+    return PR_TRUE;
+}
+
+void
+AndroidBridge::EnsureJNIThread()
+{
+    JNIEnv *env;
+    if (mJavaVM->AttachCurrentThread(&env, NULL) != 0) {
+        ALOG("EnsureJNIThread: test Attach failed!");
+        return;
+    }
+
+    if ((void*)pthread_self() != mThread) {
+        ALOG("###!!!!!!! Something's grabbing the JNIEnv from the wrong thread! (thr %p should be %p)",
+             (void*)pthread_self(), (void*)mThread);
+    }
+}
+
+void
+AndroidBridge::ShowIME(int aState)
+{
+    mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jShowIME, aState);
+}
+
+void
+AndroidBridge::EnableAccelerometer(bool aEnable)
+{
+    mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jEnableAccelerometer, aEnable);
+}
+
+void
+AndroidBridge::ReturnIMEQueryResult(const PRUnichar *result, PRUint32 len, int selectionStart, int selectionEnd)
+{
+    jvalue args[3];
+    args[0].l = mJNIEnv->NewString(result, len);
+    args[1].i = selectionStart;
+    args[2].i = selectionEnd;
+    mJNIEnv->CallStaticVoidMethodA(mGeckoAppShellClass, jReturnIMEQueryResult, args);
+}
+
+void
+AndroidBridge::ScheduleRestart()
+{
+    ALOG("scheduling reboot");
+    mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jScheduleRestart);
+}
+
+void
+AndroidBridge::NotifyXreExit()
+{
+    ALOG("xre exiting");
+    mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jNotifyXreExit);
+}
+
+void
+AndroidBridge::SetSurfaceView(jobject obj)
+{
+    mSurfaceView.Init(obj);
+}
+
+// Available for places elsewhere in the code to link to.
+PRBool
+mozilla_AndroidBridge_SetMainThread(void *thr)
+{
+    return AndroidBridge::Bridge()->SetMainThread(thr);
+}
+
+JavaVM *
+mozilla_AndroidBridge_GetJavaVM()
+{
+    return AndroidBridge::Bridge()->VM();
+}
+
+JNIEnv *
+mozilla_AndroidBridge_AttachThread(PRBool asDaemon)
+{
+    return AndroidBridge::Bridge()->AttachThread(asDaemon);
+}
+
+extern "C" JNIEnv * GetJNIForThread()
+{
+  return mozilla::AndroidBridge::JNIForThread();
+}
+
new file mode 100644
--- /dev/null
+++ b/widget/src/android/AndroidBridge.h
@@ -0,0 +1,154 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef AndroidBridge_h__
+#define AndroidBridge_h__
+
+#include <jni.h>
+#include <android/log.h>
+
+#include "nsCOMPtr.h"
+#include "nsIRunnable.h"
+
+#include "AndroidJavaWrappers.h"
+
+// Some debug #defines
+// #define ANDROID_DEBUG_EVENTS
+// #define ANDROID_DEBUG_WIDGET
+
+class nsWindow;
+
+namespace mozilla {
+
+class AndroidBridge
+{
+public:
+    static AndroidBridge *ConstructBridge(JNIEnv *jEnv,
+                                          jclass jGeckoAppShellClass);
+
+    static AndroidBridge *Bridge() {
+        return sBridge;
+    }
+
+    static JavaVM *VM() {
+        return sBridge->mJavaVM;
+    }
+
+    static JNIEnv *JNI() {
+        sBridge->EnsureJNIThread();
+        return sBridge->mJNIEnv;
+    }
+
+    static JNIEnv *JNIForThread() {
+        return sBridge->AttachThread();
+    }
+
+    // The bridge needs to be constructed via ConstructBridge first,
+    // and then once the Gecko main thread is spun up (Gecko side),
+    // SetMainThread should be called which will create the JNIEnv for
+    // us to use.  toolkit/xre/nsAndroidStartup.cpp calls
+    // SetMainThread.
+    PRBool SetMainThread(void *thr);
+
+    JNIEnv* AttachThread(PRBool asDaemon = PR_TRUE);
+
+    /* These are all implemented in Java */
+    void ShowIME(int aState);
+
+    void EnableAccelerometer(bool aEnable);
+
+    void ReturnIMEQueryResult(const PRUnichar *result, PRUint32 len, int selectionStart, int selectionEnd);
+
+    void NotifyXreExit();
+
+    void ScheduleRestart();
+
+    void SetSurfaceView(jobject jobj);
+    AndroidGeckoSurfaceView& SurfaceView() { return mSurfaceView; }
+
+    struct AutoLocalJNIFrame {
+        AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) {
+            AndroidBridge::Bridge()->JNI()->PushLocalFrame(mEntries);
+        }
+        // Note! Calling Purge makes all previous local refs created in
+        // the AutoLocalJNIFrame's scope INVALID; be sure that you locked down
+        // any local refs that you need to keep around in global refs!
+        void Purge() {
+            AndroidBridge::Bridge()->JNI()->PopLocalFrame(NULL);
+            AndroidBridge::Bridge()->JNI()->PushLocalFrame(mEntries);
+        }
+        ~AutoLocalJNIFrame() {
+            AndroidBridge::Bridge()->JNI()->PopLocalFrame(NULL);
+        }
+        int mEntries;
+    };
+
+protected:
+    static AndroidBridge *sBridge;
+
+    // the global JavaVM
+    JavaVM *mJavaVM;
+
+    // the JNIEnv for the main thread
+    JNIEnv *mJNIEnv;
+    void *mThread;
+
+    // the GeckoSurfaceView
+    AndroidGeckoSurfaceView mSurfaceView;
+
+    // the GeckoAppShell java class
+    jclass mGeckoAppShellClass;
+
+    AndroidBridge() { }
+    PRBool Init(JNIEnv *jEnv, jclass jGeckoApp);
+
+    void EnsureJNIThread();
+
+    // other things
+    jmethodID jShowIME;
+    jmethodID jEnableAccelerometer;
+    jmethodID jReturnIMEQueryResult;
+    jmethodID jNotifyXreExit;
+    jmethodID jScheduleRestart;
+    jmethodID jGetOutstandingDrawEvents;
+};
+
+}
+
+extern "C" JNIEnv * GetJNIForThread();
+
+#endif /* AndroidBridge_h__ */
new file mode 100644
--- /dev/null
+++ b/widget/src/android/AndroidJNI.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsILocalFile.h"
+
+#include "AndroidBridge.h"
+
+#include <jni.h>
+#include <pthread.h>
+#include <dlfcn.h>
+
+#include "nsAppShell.h"
+#include "nsWindow.h"
+#include <android/log.h>
+
+using namespace mozilla;
+
+/* Forward declare all the JNI methods as extern "C" */
+
+extern "C" {
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *, jclass);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *, jclass, jobject event);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject sv);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setInitialSize(JNIEnv *jenv, jclass, int width, int height);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_putenv(JNIEnv *jenv, jclass, jstring map);
+}
+
+
+/*
+ * Incoming JNI methods
+ */
+
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *jenv, jclass jc)
+{
+    AndroidBridge::ConstructBridge(jenv, jc);
+}
+
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *jenv, jclass jc, jobject event)
+{
+    // poke the appshell
+    if (nsAppShell::gAppShell)
+        nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(jenv, event));
+    else if (!nsAppShell::gEarlyEvent)
+        nsAppShell::gEarlyEvent = new AndroidGeckoEvent(jenv, event);
+}
+
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject obj)
+{
+    AndroidBridge::Bridge()->SetSurfaceView(jenv->NewGlobalRef(obj));
+}
+
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_setInitialSize(JNIEnv *jenv, jclass, int width, int height)
+{
+    nsWindow::SetInitialAndroidBounds(gfxIntSize(width, height));
+}
+
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_putenv(JNIEnv *jenv, jclass, jstring map)
+{
+    const char* str;
+    str = jenv->GetStringUTFChars(map, NULL);
+    if (str == NULL)
+        return;
+#ifdef DEBUG
+    __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", "putenv(%s)", str);
+#endif
+    putenv(strdup(str));
+    jenv->ReleaseStringUTFChars(map, str);
+    
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/AndroidJavaWrappers.cpp
@@ -0,0 +1,360 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "AndroidJavaWrappers.h"
+#include "AndroidBridge.h"
+
+using namespace mozilla;
+
+jclass AndroidGeckoEvent::jGeckoEventClass = 0;
+jfieldID AndroidGeckoEvent::jActionField = 0;
+jfieldID AndroidGeckoEvent::jTypeField = 0;
+jfieldID AndroidGeckoEvent::jTimeField = 0;
+jfieldID AndroidGeckoEvent::jP0Field = 0;
+jfieldID AndroidGeckoEvent::jP1Field = 0;
+jfieldID AndroidGeckoEvent::jXField = 0;
+jfieldID AndroidGeckoEvent::jYField = 0;
+jfieldID AndroidGeckoEvent::jZField = 0;
+jfieldID AndroidGeckoEvent::jRectField = 0;
+jfieldID AndroidGeckoEvent::jNativeWindowField = 0;
+
+jfieldID AndroidGeckoEvent::jCharactersField = 0;
+jfieldID AndroidGeckoEvent::jKeyCodeField = 0;
+jfieldID AndroidGeckoEvent::jMetaStateField = 0;
+jfieldID AndroidGeckoEvent::jFlagsField = 0;
+jfieldID AndroidGeckoEvent::jUnicodeCharField = 0;
+jfieldID AndroidGeckoEvent::jCountField = 0;
+jfieldID AndroidGeckoEvent::jCount2Field = 0;
+
+jclass AndroidPoint::jPointClass = 0;
+jfieldID AndroidPoint::jXField = 0;
+jfieldID AndroidPoint::jYField = 0;
+
+jclass AndroidRect::jRectClass = 0;
+jfieldID AndroidRect::jBottomField = 0;
+jfieldID AndroidRect::jLeftField = 0;
+jfieldID AndroidRect::jRightField = 0;
+jfieldID AndroidRect::jTopField = 0;
+
+jclass AndroidGeckoSurfaceView::jGeckoSurfaceViewClass = 0;
+jmethodID AndroidGeckoSurfaceView::jBeginDrawingMethod = 0;
+jmethodID AndroidGeckoSurfaceView::jEndDrawingMethod = 0;
+jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBufferMethod = 0;
+
+#define JNI()  (AndroidBridge::JNI())
+
+#define initInit() jclass jClass
+
+// note that this also sets jClass
+#define getClassGlobalRef(cname) \
+    (jClass = jclass(jEnv->NewGlobalRef(jEnv->FindClass(cname))))
+
+#define getField(fname, ftype) \
+    ((jfieldID) jEnv->GetFieldID(jClass, fname, ftype))
+
+#define getMethod(fname, ftype) \
+    ((jmethodID) jEnv->GetMethodID(jClass, fname, ftype))
+
+void
+mozilla::InitAndroidJavaWrappers(JNIEnv *jEnv)
+{
+    AndroidGeckoEvent::InitGeckoEventClass(jEnv);
+    AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(jEnv);
+    AndroidPoint::InitPointClass(jEnv);
+}
+
+void
+AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
+{
+    initInit();
+
+    jGeckoEventClass = getClassGlobalRef("org/mozilla/gecko/GeckoEvent");
+
+    jActionField = getField("mAction", "I");
+    jTypeField = getField("mType", "I");
+    jTimeField = getField("mTime", "J");
+    jP0Field = getField("mP0", "Landroid/graphics/Point;");
+    jP1Field = getField("mP1", "Landroid/graphics/Point;");
+    jXField = getField("mX", "F");
+    jYField = getField("mY", "F");
+    jZField = getField("mZ", "F");
+    jRectField = getField("mRect", "Landroid/graphics/Rect;");
+    jNativeWindowField = getField("mNativeWindow", "I");
+
+    jCharactersField = getField("mCharacters", "Ljava/lang/String;");
+    jKeyCodeField = getField("mKeyCode", "I");
+    jMetaStateField = getField("mMetaState", "I");
+    jFlagsField = getField("mFlags", "I");
+    jUnicodeCharField = getField("mUnicodeChar", "I");
+    jCountField = getField("mCount", "I");
+    jCount2Field = getField("mCount2", "I");
+}
+
+void
+AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
+{
+    initInit();
+
+    jGeckoSurfaceViewClass = getClassGlobalRef("org/mozilla/gecko/GeckoSurfaceView");
+
+    jBeginDrawingMethod = getMethod("beginDrawing", "()I");
+    jGetSoftwareDrawBufferMethod = getMethod("getSoftwareDrawBuffer", "()Ljava/nio/ByteBuffer;");
+    jEndDrawingMethod = getMethod("endDrawing", "()V");
+}
+
+void
+AndroidPoint::InitPointClass(JNIEnv *jEnv)
+{
+    initInit();
+
+    jPointClass = getClassGlobalRef("android/graphics/Point");
+
+    jXField = getField("x", "I");
+    jYField = getField("y", "I");
+}
+
+void
+AndroidRect::InitRectClass(JNIEnv *jEnv)
+{
+    initInit();
+
+    jRectClass = getClassGlobalRef("android/graphics/Rect");
+
+    jBottomField = getField("bottom", "I");
+    jLeftField = getField("left", "I");
+    jTopField = getField("top", "I");
+    jRightField = getField("right", "I");
+}
+
+#undef initInit
+#undef initClassGlobalRef
+#undef getField
+#undef getMethod
+
+void
+AndroidGeckoEvent::ReadP0Field(JNIEnv *jenv)
+{
+    AndroidPoint p0(jenv, jenv->GetObjectField(wrappedObject(), jP0Field));
+    mP0.x = p0.X();
+    mP0.y = p0.Y();
+}
+
+void
+AndroidGeckoEvent::ReadP1Field(JNIEnv *jenv)
+{
+    AndroidPoint p1(jenv, jenv->GetObjectField(wrappedObject(), jP1Field));
+    mP1.x = p1.X();
+    mP1.y = p1.Y();
+}
+
+void
+AndroidGeckoEvent::ReadRectField(JNIEnv *jenv)
+{
+    AndroidRect r(jenv, jenv->GetObjectField(wrappedObject(), jRectField));
+    if (!r.isNull()) {
+        mRect.SetRect(r.Left(),
+                      r.Top(),
+                      r.Right() - r.Left(),
+                      r.Bottom() - r.Top());
+    } else {
+        mRect.Empty();
+    }
+}
+
+void
+AndroidGeckoEvent::ReadCharactersField(JNIEnv *jenv)
+{
+    jstring s = (jstring) jenv->GetObjectField(wrapped_obj, jCharactersField);
+    if (!s) {
+        mCharacters.SetIsVoid(PR_TRUE);
+        return;
+    }
+
+    int len = jenv->GetStringLength(s);
+    mCharacters.SetLength(len);
+    jenv->GetStringRegion(s, 0, len, mCharacters.BeginWriting());
+}
+
+void
+AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
+{
+    NS_ASSERTION(!wrapped_obj, "Init called on non-null wrapped_obj!");
+
+    wrapped_obj = jobj;
+
+    if (!jobj)
+        return;
+
+    mAction = jenv->GetIntField(jobj, jActionField);
+    mType = jenv->GetIntField(jobj, jTypeField);
+    mNativeWindow = (void*) jenv->GetIntField(jobj, jNativeWindowField);
+
+    switch (mType) {
+        case SIZE_CHANGED:
+            ReadP0Field(jenv);
+            ReadP1Field(jenv);
+            break;
+
+        case KEY_EVENT:
+            mTime = jenv->GetLongField(jobj, jTimeField);
+            mMetaState = jenv->GetIntField(jobj, jMetaStateField);
+            mFlags = jenv->GetIntField(jobj, jFlagsField);
+            mKeyCode = jenv->GetIntField(jobj, jKeyCodeField);
+            mUnicodeChar = jenv->GetIntField(jobj, jUnicodeCharField);
+            ReadCharactersField(jenv);
+            break;
+
+        case MOTION_EVENT:
+            mTime = jenv->GetLongField(jobj, jTimeField);
+            ReadP0Field(jenv);
+            break;
+
+        case IME_EVENT:
+            mCount = jenv->GetIntField(jobj, jCountField);
+            mCount2 = jenv->GetIntField(jobj, jCount2Field);
+            ReadCharactersField(jenv);
+            break;
+
+        case DRAW:
+            ReadRectField(jenv);
+            break;
+
+        case SENSOR_EVENT:
+            mX = jenv->GetFloatField(jobj, jXField);
+            mY = jenv->GetFloatField(jobj, jYField);
+            mZ = jenv->GetFloatField(jobj, jZField);
+            break;
+
+        default:
+            break;
+    }
+
+#ifndef ANDROID_DEBUG_EVENTS
+    ALOG("AndroidGeckoEvent: %p : %d %p", (void*)jobj, mType, (void*)mNativeWindow);
+#endif
+}
+
+void
+AndroidGeckoEvent::Init(int aType)
+{
+    mType = aType;
+}
+
+void
+AndroidGeckoEvent::Init(void *window, int x1, int y1, int x2, int y2)
+{
+    mType = DRAW;
+    mNativeWindow = window;
+    mRect.Empty();
+}
+
+void
+AndroidGeckoSurfaceView::Init(jobject jobj)
+{
+    NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
+
+    wrapped_obj = jobj;
+}
+
+int
+AndroidGeckoSurfaceView::BeginDrawing()
+{
+    NS_ASSERTION(!isNull(), "BeginDrawing called on null surfaceview!");
+
+    return JNI()->CallIntMethod(wrapped_obj, jBeginDrawingMethod);
+}
+
+void
+AndroidGeckoSurfaceView::EndDrawing()
+{
+    JNI()->CallVoidMethod(wrapped_obj, jEndDrawingMethod);
+}
+
+unsigned char *
+AndroidGeckoSurfaceView::GetSoftwareDrawBuffer(int *cap)
+{
+    jobject buf = JNI()->CallObjectMethod(wrapped_obj, jGetSoftwareDrawBufferMethod);
+    if (!buf)
+        return nsnull;
+
+    void *bp = JNI()->GetDirectBufferAddress(buf);
+    jlong blen = JNI()->GetDirectBufferCapacity(buf);
+
+    if (!bp || blen == -1)
+        return nsnull;
+
+    if (cap)
+        *cap = blen;
+
+    return (unsigned char*) bp;
+}
+
+void
+AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
+{
+    NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
+
+    wrapped_obj = jobj;
+
+    if (jobj) {
+        mX = jenv->GetIntField(jobj, jXField);
+        mY = jenv->GetIntField(jobj, jYField);
+    } else {
+        mX = 0;
+        mY = 0;
+    }
+}
+
+void
+AndroidRect::Init(JNIEnv *jenv, jobject jobj)
+{
+    NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
+
+    wrapped_obj = jobj;
+
+    if (jobj) {
+        mTop = jenv->GetIntField(jobj, jTopField);
+        mLeft = jenv->GetIntField(jobj, jLeftField);
+        mRight = jenv->GetIntField(jobj, jRightField);
+        mBottom = jenv->GetIntField(jobj, jBottomField);
+    } else {
+        mTop = 0;
+        mLeft = 0;
+        mRight = 0;
+        mBottom = 0;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/AndroidJavaWrappers.h
@@ -0,0 +1,426 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef AndroidJavaWrappers_h__
+#define AndroidJavaWrappers_h__
+
+#include <jni.h>
+#include <android/log.h>
+
+#include "nsPoint.h"
+#include "nsRect.h"
+#include "nsString.h"
+
+#ifndef ALOG
+#ifdef DEBUG
+#define ALOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
+#else
+#define ALOG(args...)
+#endif
+#endif
+
+namespace mozilla {
+
+void InitAndroidJavaWrappers(JNIEnv *jEnv);
+
+/*
+ * Note: do not store global refs to any WrappedJavaObject;
+ * these are live only during a particular JNI method, as
+ * NewGlobalRef is -not- called on the jobject.
+ *
+ * If this is needed, WrappedJavaObject can be extended to
+ * handle it.
+ */
+
+class WrappedJavaObject {
+public:
+    WrappedJavaObject() :
+        wrapped_obj(0)
+    { }
+
+    WrappedJavaObject(jobject jobj) {
+        Init(jobj);
+    }
+
+    void Init(jobject jobj) {
+        wrapped_obj = jobj;
+    }
+
+    bool isNull() const {
+        return wrapped_obj == 0;
+    }
+
+    jobject wrappedObject() const {
+        return wrapped_obj;
+    }
+
+protected:
+    jobject wrapped_obj;
+};
+
+class AndroidPoint : public WrappedJavaObject
+{
+public:
+    static void InitPointClass(JNIEnv *jEnv);
+
+    AndroidPoint() { }
+    AndroidPoint(JNIEnv *jenv, jobject jobj) {
+        Init(jenv, jobj);
+    }
+
+    void Init(JNIEnv *jenv, jobject jobj);
+
+    int X() { return mX; }
+    int Y() { return mY; }
+
+protected:
+    int mX;
+    int mY;
+
+    static jclass jPointClass;
+    static jfieldID jXField;
+    static jfieldID jYField;
+};
+
+class AndroidRect : public WrappedJavaObject
+{
+public:
+    static void InitRectClass(JNIEnv *jEnv);
+
+    AndroidRect() { }
+    AndroidRect(JNIEnv *jenv, jobject jobj) {
+        Init(jenv, jobj);
+    }
+
+    void Init(JNIEnv *jenv, jobject jobj);
+
+    int Bottom() { return mBottom; }
+    int Left() { return mLeft; }
+    int Right() { return mRight; }
+    int Top() { return mTop; }
+
+protected:
+    int mBottom;
+    int mLeft;
+    int mRight;
+    int mTop;
+
+    static jclass jRectClass;
+    static jfieldID jBottomField;
+    static jfieldID jLeftField;
+    static jfieldID jRightField;
+    static jfieldID jTopField;
+};
+
+class AndroidGeckoSurfaceView : public WrappedJavaObject
+{
+public:
+    static void InitGeckoSurfaceViewClass(JNIEnv *jEnv);
+
+    AndroidGeckoSurfaceView() { }
+    AndroidGeckoSurfaceView(jobject jobj) {
+        Init(jobj);
+    }
+
+    void Init(jobject jobj);
+
+    enum {
+        DRAW_ERROR = 0,
+        DRAW_GLES_2 = 1,
+        DRAW_SOFTWARE = 2
+    };
+
+    int BeginDrawing();
+    unsigned char *GetSoftwareDrawBuffer(int *cap);
+    void EndDrawing();
+protected:
+    static jclass jGeckoSurfaceViewClass;
+    static jmethodID jBeginDrawingMethod;
+    static jmethodID jEndDrawingMethod;
+    static jmethodID jGetSoftwareDrawBufferMethod;
+};
+
+class AndroidKeyEvent
+{
+public:
+    enum {
+        KEYCODE_UNKNOWN            = 0,
+        KEYCODE_SOFT_LEFT          = 1,
+        KEYCODE_SOFT_RIGHT         = 2,
+        KEYCODE_HOME               = 3,
+        KEYCODE_BACK               = 4,
+        KEYCODE_CALL               = 5,
+        KEYCODE_ENDCALL            = 6,
+        KEYCODE_0                  = 7,
+        KEYCODE_1                  = 8,
+        KEYCODE_2                  = 9,
+        KEYCODE_3                  = 10,
+        KEYCODE_4                  = 11,
+        KEYCODE_5                  = 12,
+        KEYCODE_6                  = 13,
+        KEYCODE_7                  = 14,
+        KEYCODE_8                  = 15,
+        KEYCODE_9                  = 16,
+        KEYCODE_STAR               = 17,
+        KEYCODE_POUND              = 18,
+        KEYCODE_DPAD_UP            = 19,
+        KEYCODE_DPAD_DOWN          = 20,
+        KEYCODE_DPAD_LEFT          = 21,
+        KEYCODE_DPAD_RIGHT         = 22,
+        KEYCODE_DPAD_CENTER        = 23,
+        KEYCODE_VOLUME_UP          = 24,
+        KEYCODE_VOLUME_DOWN        = 25,
+        KEYCODE_POWER              = 26,
+        KEYCODE_CAMERA             = 27,
+        KEYCODE_CLEAR              = 28,
+        KEYCODE_A                  = 29,
+        KEYCODE_B                  = 30,
+        KEYCODE_C                  = 31,
+        KEYCODE_D                  = 32,
+        KEYCODE_E                  = 33,
+        KEYCODE_F                  = 34,
+        KEYCODE_G                  = 35,
+        KEYCODE_H                  = 36,
+        KEYCODE_I                  = 37,
+        KEYCODE_J                  = 38,
+        KEYCODE_K                  = 39,
+        KEYCODE_L                  = 40,
+        KEYCODE_M                  = 41,
+        KEYCODE_N                  = 42,
+        KEYCODE_O                  = 43,
+        KEYCODE_P                  = 44,
+        KEYCODE_Q                  = 45,
+        KEYCODE_R                  = 46,
+        KEYCODE_S                  = 47,
+        KEYCODE_T                  = 48,
+        KEYCODE_U                  = 49,
+        KEYCODE_V                  = 50,
+        KEYCODE_W                  = 51,
+        KEYCODE_X                  = 52,
+        KEYCODE_Y                  = 53,
+        KEYCODE_Z                  = 54,
+        KEYCODE_COMMA              = 55,
+        KEYCODE_PERIOD             = 56,
+        KEYCODE_ALT_LEFT           = 57,
+        KEYCODE_ALT_RIGHT          = 58,
+        KEYCODE_SHIFT_LEFT         = 59,
+        KEYCODE_SHIFT_RIGHT        = 60,
+        KEYCODE_TAB                = 61,
+        KEYCODE_SPACE              = 62,
+        KEYCODE_SYM                = 63,
+        KEYCODE_EXPLORER           = 64,
+        KEYCODE_ENVELOPE           = 65,
+        KEYCODE_ENTER              = 66,
+        KEYCODE_DEL                = 67,
+        KEYCODE_GRAVE              = 68,
+        KEYCODE_MINUS              = 69,
+        KEYCODE_EQUALS             = 70,
+        KEYCODE_LEFT_BRACKET       = 71,
+        KEYCODE_RIGHT_BRACKET      = 72,
+        KEYCODE_BACKSLASH          = 73,
+        KEYCODE_SEMICOLON          = 74,
+        KEYCODE_APOSTROPHE         = 75,
+        KEYCODE_SLASH              = 76,
+        KEYCODE_AT                 = 77,
+        KEYCODE_NUM                = 78,
+        KEYCODE_HEADSETHOOK        = 79,
+        KEYCODE_FOCUS              = 80,
+        KEYCODE_PLUS               = 81,
+        KEYCODE_MENU               = 82,
+        KEYCODE_NOTIFICATION       = 83,
+        KEYCODE_SEARCH             = 84,
+        KEYCODE_MEDIA_PLAY_PAUSE   = 85,
+        KEYCODE_MEDIA_STOP         = 86,
+        KEYCODE_MEDIA_NEXT         = 87,
+        KEYCODE_MEDIA_PREVIOUS     = 88,
+        KEYCODE_MEDIA_REWIND       = 89,
+        KEYCODE_MEDIA_FAST_FORWARD = 90,
+        KEYCODE_MUTE               = 91,
+        ACTION_DOWN                = 0,
+        ACTION_UP                  = 1,
+        ACTION_MULTIPLE            = 2,
+        META_ALT_ON                = 0x00000002,
+        META_ALT_LEFT_ON           = 0x00000010,
+        META_ALT_RIGHT_ON          = 0x00000020,
+        META_SHIFT_ON              = 0x00000001,
+        META_SHIFT_LEFT_ON         = 0x00000040,
+        META_SHIFT_RIGHT_ON        = 0x00000080,
+        META_SYM_ON                = 0x00000004,
+        FLAG_WOKE_HERE             = 0x00000001,
+        FLAG_SOFT_KEYBOARD         = 0x00000002,
+        FLAG_KEEP_TOUCH_MODE       = 0x00000004,
+        FLAG_FROM_SYSTEM           = 0x00000008,
+        FLAG_EDITOR_ACTION         = 0x00000010,
+        FLAG_CANCELED              = 0x00000020,
+        FLAG_VIRTUAL_HARD_KEY      = 0x00000040,
+        FLAG_LONG_PRESS            = 0x00000080,
+        FLAG_CANCELED_LONG_PRESS   = 0x00000100,
+        FLAG_TRACKING              = 0x00000200,
+        FLAG_START_TRACKING        = 0x40000000,
+        dummy_java_enum_list_end
+    };
+};
+
+class AndroidMotionEvent
+{
+public:
+    enum {
+        ACTION_MASK = 0xff,
+        ACTION_DOWN = 0,
+        ACTION_UP = 1,
+        ACTION_MOVE = 2,
+        ACTION_CANCEL = 3,
+        ACTION_OUTSIDE = 4,
+        ACTION_POINTER_DOWN = 5,
+        ACTION_POINTER_UP = 6,
+        ACTION_POINTER_ID_MASK = 0xff00,
+        ACTION_POINTER_ID_SHIFT = 8,
+        EDGE_TOP = 0x00000001,
+        EDGE_BOTTOM = 0x00000002,
+        EDGE_LEFT = 0x00000004,
+        EDGE_RIGHT = 0x00000008,
+        SAMPLE_X = 0,
+        SAMPLE_Y = 1,
+        SAMPLE_PRESSURE = 2,
+        SAMPLE_SIZE = 3,
+        NUM_SAMPLE_DATA = 4,
+        dummy_java_enum_list_end
+    };
+};
+
+class AndroidGeckoEvent : public WrappedJavaObject
+{
+public:
+    static void InitGeckoEventClass(JNIEnv *jEnv);
+
+    AndroidGeckoEvent() { }
+    AndroidGeckoEvent(int aType) {
+        Init(aType);
+    }
+    AndroidGeckoEvent(void *window, int x1, int y1, int x2, int y2) {
+        Init(window, x1, y1, x2, y2);
+    }
+    AndroidGeckoEvent(JNIEnv *jenv, jobject jobj) {
+        Init(jenv, jobj);
+    }
+
+    void Init(JNIEnv *jenv, jobject jobj);
+    void Init(int aType);
+    void Init(void *window, int x1, int y1, int x2, int y2);
+
+    int Action() { return mAction; }
+    int Type() { return mType; }
+    int64_t Time() { return mTime; }
+    void *NativeWindow() { return mNativeWindow; }
+    const nsIntPoint& P0() { return mP0; }
+    const nsIntPoint& P1() { return mP1; }
+    float X() { return mX; }
+    float Y() { return mY; }
+    float Z() { return mZ; }
+    const nsIntRect& Rect() { return mRect; }
+    nsAString& Characters() { return mCharacters; }
+    int KeyCode() { return mKeyCode; }
+    int MetaState() { return mMetaState; }
+    int Flags() { return mFlags; }
+    int UnicodeChar() { return mUnicodeChar; }
+    int Count() { return mCount; }
+    int Count2() { return mCount2; }
+
+protected:
+    int mAction;
+    int mType;
+    int64_t mTime;
+    void *mNativeWindow;
+    nsIntPoint mP0;
+    nsIntPoint mP1;
+    nsIntRect mRect;
+    int mFlags, mMetaState;
+    int mKeyCode, mUnicodeChar;
+    int mCount, mCount2;
+    float mX, mY, mZ;
+    nsString mCharacters;
+
+    void ReadP0Field(JNIEnv *jenv);
+    void ReadP1Field(JNIEnv *jenv);
+    void ReadRectField(JNIEnv *jenv);
+    void ReadCharactersField(JNIEnv *jenv);
+
+    static jclass jGeckoEventClass;
+    static jfieldID jActionField;
+    static jfieldID jTypeField;
+    static jfieldID jTimeField;
+    static jfieldID jP0Field;
+    static jfieldID jP1Field;
+    static jfieldID jXField;
+    static jfieldID jYField;
+    static jfieldID jZField;
+    static jfieldID jRectField;
+    static jfieldID jNativeWindowField;
+
+    static jfieldID jCharactersField;
+    static jfieldID jKeyCodeField;
+    static jfieldID jMetaStateField;
+    static jfieldID jFlagsField;
+    static jfieldID jCountField;
+    static jfieldID jCount2Field;
+    static jfieldID jUnicodeCharField;
+
+public:
+    enum {
+        NATIVE_POKE = 0,
+        KEY_EVENT = 1,
+        MOTION_EVENT = 2,
+        SENSOR_EVENT = 3,
+        IME_EVENT = 4,
+        DRAW = 5,
+        SIZE_CHANGED = 6,
+        ACTIVITY_STOPPING = 7,
+        dummy_java_enum_list_end
+    };
+
+    enum {
+        IME_BATCH_END = 0,
+        IME_BATCH_BEGIN = 1,
+        IME_SET_TEXT = 2,
+        IME_GET_TEXT = 3,
+        IME_DELETE_TEXT = 4
+    };
+};
+
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/widget/src/android/Makefile.in
@@ -0,0 +1,103 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+#   Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2009-2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Vladimir Vukicevic <vladimir@pobox.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH           = ../../..
+topsrcdir       = @top_srcdir@
+srcdir          = @srcdir@
+VPATH           = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE          = widget
+LIBRARY_NAME    = widget_android
+EXPORT_LIBRARY  = 1
+IS_COMPONENT    = 1
+MODULE_NAME     = nsWidgetAndroidModule
+GRE_MODULE      = 1
+LIBXUL_LIBRARY  = 1
+
+
+CPPSRCS	= \
+	nsWidgetFactory.cpp \
+	nsAppShell.cpp \
+	nsToolkit.cpp \
+	AndroidJavaWrappers.cpp \
+	AndroidBridge.cpp \
+	AndroidJNI.cpp \
+	nsWindow.cpp \
+	nsLookAndFeel.cpp \
+	nsScreenManagerAndroid.cpp \
+	nsIdleServiceAndroid.cpp \
+	nsAccelerometerAndroid.cpp \
+	$(NULL)
+
+NOT_THERE_YET_CPPSRCS = \
+	nsQtKeyUtils.cpp \
+	nsClipboard.cpp \
+	nsBidiKeyboard.cpp \
+	nsDragService.cpp \
+	nsNativeThemeQt.cpp \
+	mozqwidget.cpp \
+	nsSound.cpp \
+	nsFilePicker.cpp \
+	$(NULL)
+
+SHARED_LIBRARY_LIBS = ../xpwidgets/libxpwidgets_s.a
+
+EXTRA_DSO_LDOPTS = \
+	$(MOZ_COMPONENT_LIBS) \
+	-lgkgfx \
+	-lthebes \
+	$(MOZ_JS_LIBS) \
+	$(QCMS_LIBS) \
+	$(NULL)
+
+
+EXTRA_DSO_LDOPTS += -L$(DIST)/lib
+
+EXPORTS = AndroidBridge.h AndroidJavaWrappers.h
+
+include $(topsrcdir)/config/rules.mk
+
+DEFINES += -D_IMPL_NS_WIDGET
+#DEFINES += -DDEBUG_WIDGETS
+
+LOCAL_INCLUDES += \
+	-I$(topsrcdir)/widget/src/xpwidgets \
+	-I$(srcdir) \
+	$(NULL)
+
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsAccelerometerAndroid.cpp
@@ -0,0 +1,62 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Michael Wu <mwu@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsAccelerometerAndroid.h"
+
+#include "AndroidBridge.h"
+
+using namespace mozilla;
+
+extern nsAccelerometerAndroid *gAccel;
+
+nsAccelerometerAndroid::nsAccelerometerAndroid()
+{
+    gAccel = this;
+}
+
+nsAccelerometerAndroid::~nsAccelerometerAndroid()
+{
+}
+
+void nsAccelerometerAndroid::Startup()
+{
+    AndroidBridge::Bridge()->EnableAccelerometer(true);
+}
+
+void nsAccelerometerAndroid::Shutdown()
+{
+    AndroidBridge::Bridge()->EnableAccelerometer(false);
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsAccelerometerAndroid.h
@@ -0,0 +1,54 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Michael Wu <mwu@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsAccelerometerAndroid_h
+#define nsAccelerometerAndroid_h
+
+#include "nsAccelerometer.h"
+
+class nsAccelerometerAndroid : public nsAccelerometer
+{
+public:
+  nsAccelerometerAndroid();
+  virtual ~nsAccelerometerAndroid();
+
+private:
+  virtual void Startup();
+  virtual void Shutdown();
+};
+
+#endif /* nsAccelerometerAndroid_h */
+
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsAppShell.cpp
@@ -0,0 +1,310 @@
+/* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsAppShell.h"
+#include "nsWindow.h"
+#include "nsThreadUtils.h"
+#include "nsIObserverService.h"
+#include "nsIAppStartup.h"
+
+#include "prenv.h"
+
+#include "AndroidBridge.h"
+#include "nsAccelerometerAndroid.h"
+#include <android/log.h>
+#include <pthread.h>
+
+#ifdef MOZ_LOGGING
+#define FORCE_PR_LOG
+#include "prlog.h"
+#endif
+
+#ifdef ANDROID_DEBUG_EVENTS
+#define EVLOG(args...)  ALOG(args)
+#else
+#define EVLOG(args...) do { } while (0)
+#endif
+
+using namespace mozilla;
+
+#ifdef PR_LOGGING
+PRLogModuleInfo *gWidgetLog = nsnull;
+#endif
+
+nsAccelerometerAndroid *gAccel = nsnull;
+
+nsAppShell *nsAppShell::gAppShell = nsnull;
+AndroidGeckoEvent *nsAppShell::gEarlyEvent = nsnull;
+
+nsAppShell::nsAppShell()
+    : mQueueLock(nsnull),
+      mCondLock(nsnull),
+      mQueueCond(nsnull),
+      mNumDraws(0)
+{
+    gAppShell = this;
+    if (gEarlyEvent) {
+        mEventQueue.AppendElement(gEarlyEvent);
+        gEarlyEvent = nsnull;
+    }
+}
+
+nsAppShell::~nsAppShell()
+{
+    gAppShell = nsnull;
+}
+
+void
+nsAppShell::NotifyNativeEvent()
+{
+    PR_Lock(mCondLock);
+    PR_NotifyCondVar(mQueueCond);
+    PR_Unlock(mCondLock);
+}
+
+nsresult
+nsAppShell::Init()
+{
+#ifdef PR_LOGGING
+    if (!gWidgetLog)
+        gWidgetLog = PR_NewLogModule("Widget");
+#endif
+
+    mQueueLock = PR_NewLock();
+    mCondLock = PR_NewLock();
+    mQueueCond = PR_NewCondVar(mCondLock);
+
+    return nsBaseAppShell::Init();
+}
+
+
+void
+nsAppShell::ScheduleNativeEventCallback()
+{
+    EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread());
+
+    // this is valid to be called from any thread, so do so.
+    PostEvent(new AndroidGeckoEvent(AndroidGeckoEvent::NATIVE_POKE));
+}
+
+
+PRBool
+nsAppShell::ProcessNextNativeEvent(PRBool mayWait)
+{
+    EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
+
+    PR_Lock(mCondLock);
+
+    nsAutoPtr<AndroidGeckoEvent> curEvent;
+    AndroidGeckoEvent *nextEvent;
+
+    curEvent = GetNextEvent();
+    if (!curEvent && mayWait) {
+        // hmm, should we really hardcode this 10s?
+#if defined(ANDROID_DEBUG_EVENTS)
+        PRTime t0, t1;
+        EVLOG("nsAppShell: waiting on mQueueCond");
+        t0 = PR_Now();
+
+        PR_WaitCondVar(mQueueCond, PR_MillisecondsToInterval(10000));
+        t1 = PR_Now();
+        EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000);
+#else
+        PR_WaitCondVar(mQueueCond, PR_INTERVAL_NO_TIMEOUT);
+#endif
+
+        curEvent = GetNextEvent();
+    }
+
+    PR_Unlock(mCondLock);
+
+    if (!curEvent)
+        return false;
+
+    // Combine subsequent events of the same type
+
+    nextEvent = PeekNextEvent();
+
+    while (nextEvent) {
+        int curType = curEvent->Type();
+        int nextType = nextEvent->Type();
+
+        while (nextType == AndroidGeckoEvent::DRAW &&
+               mNumDraws > 1)
+        {
+            // skip this draw, since there's a later one already in the queue.. this will let us
+            // deal with sequences that look like:
+            //   MOVE DRAW MOVE DRAW MOVE DRAW
+            // and end up with just
+            //   MOVE DRAW
+            // when we process all the events.
+            RemoveNextEvent();
+
+#if defined(ANDROID_DEBUG_EVENTS)
+            ALOG("# Removing DRAW event (%d outstanding)", mNumDraws);
+#endif
+
+            nextEvent = PeekNextEvent();
+            nextType = nextEvent->Type();
+        }
+
+        // If the next type of event isn't the same as the current type,
+        // we don't coalesce.
+        if (nextType != curType)
+            break;
+
+        // Can only coalesce motion move events, for motion events
+        if (curType != AndroidGeckoEvent::MOTION_EVENT)
+            break;
+
+        if (!(curEvent->Action() == AndroidMotionEvent::ACTION_MOVE &&
+              nextEvent->Action() == AndroidMotionEvent::ACTION_MOVE))
+            break;
+
+#if defined(ANDROID_DEBUG_EVENTS)
+        ALOG("# Removing % 2d event", curType);
+#endif
+
+        RemoveNextEvent();
+        curEvent = nextEvent;
+        nextEvent = PeekNextEvent();
+    }
+
+    EVLOG("nsAppShell: event %p %d [ndraws %d]", (void*)curEvent.get(), curEvent->Type(), mNumDraws);
+
+    nsWindow *target = (nsWindow*) curEvent->NativeWindow();
+
+    switch (curEvent->Type()) {
+    case AndroidGeckoEvent::NATIVE_POKE:
+        NativeEventCallback();
+        break;
+
+    case AndroidGeckoEvent::SENSOR_EVENT:
+        gAccel->AccelerationChanged(-curEvent->X(), curEvent->Y(), curEvent->Z());
+        break;
+
+    case AndroidGeckoEvent::ACTIVITY_STOPPING: {
+        nsCOMPtr<nsIAppStartup> appSvc = do_GetService("@mozilla.org/toolkit/app-startup;1");
+        if (appSvc)
+            appSvc->Quit(nsIAppStartup::eForceQuit);
+        break;
+    }
+
+    default:
+        if (target)
+            target->OnAndroidEvent(curEvent);
+        else
+            nsWindow::OnGlobalAndroidEvent(curEvent);
+    }
+
+    EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type());
+
+    return true;
+}
+
+AndroidGeckoEvent*
+nsAppShell::GetNextEvent()
+{
+    AndroidGeckoEvent *ae = nsnull;
+    PR_Lock(mQueueLock);
+    if (mEventQueue.Length()) {
+        ae = mEventQueue[0];
+        mEventQueue.RemoveElementAt(0);
+        if (ae->Type() == AndroidGeckoEvent::DRAW) {
+            mNumDraws--;
+        }
+    }
+    PR_Unlock(mQueueLock);
+
+    return ae;
+}
+
+AndroidGeckoEvent*
+nsAppShell::PeekNextEvent()
+{
+    AndroidGeckoEvent *ae = nsnull;
+    PR_Lock(mQueueLock);
+    if (mEventQueue.Length()) {
+        ae = mEventQueue[0];
+    }
+    PR_Unlock(mQueueLock);
+
+    return ae;
+}
+
+void
+nsAppShell::PostEvent(AndroidGeckoEvent *ae)
+{
+    PR_Lock(mQueueLock);
+    mEventQueue.AppendElement(ae);
+    if (ae->Type() == AndroidGeckoEvent::DRAW) {
+        mNumDraws++;
+    }
+    PR_Unlock(mQueueLock);
+    NotifyNativeEvent();
+}
+
+void
+nsAppShell::RemoveNextEvent()
+{
+    AndroidGeckoEvent *ae = nsnull;
+    PR_Lock(mQueueLock);
+    if (mEventQueue.Length()) {
+        ae = mEventQueue[0];
+        mEventQueue.RemoveElementAt(0);
+        if (ae->Type() == AndroidGeckoEvent::DRAW) {
+            mNumDraws--;
+        }
+    }
+    PR_Unlock(mQueueLock);
+}
+
+// Used by IPC code
+namespace mozilla {
+
+bool ProcessNextEvent()
+{
+    return nsAppShell::gAppShell->ProcessNextNativeEvent(PR_TRUE) ? true : false;
+}
+
+void NotifyEvent()
+{
+    nsAppShell::gAppShell->NotifyNativeEvent();
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsAppShell.h
@@ -0,0 +1,86 @@
+/* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsAppShell_h__
+#define nsAppShell_h__
+
+#include "nsBaseAppShell.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+#include "prcvar.h"
+
+namespace mozilla {
+class AndroidGeckoEvent;
+bool ProcessNextEvent();
+void NotifyEvent();
+}
+
+class nsAppShell :
+    public nsBaseAppShell
+{
+public:
+    static nsAppShell *gAppShell;
+    static mozilla::AndroidGeckoEvent *gEarlyEvent;
+
+    nsAppShell();
+
+    nsresult Init();
+
+    void NotifyNativeEvent();
+
+    virtual PRBool ProcessNextNativeEvent(PRBool mayWait);
+
+    void PostEvent(mozilla::AndroidGeckoEvent *event);
+    void RemoveNextEvent();
+protected:
+    virtual void ScheduleNativeEventCallback();
+    virtual ~nsAppShell();
+
+    int mNumDraws;
+    PRLock *mQueueLock;
+    PRLock *mCondLock;
+    PRCondVar *mQueueCond;
+    nsTArray<mozilla::AndroidGeckoEvent *> mEventQueue;
+
+    mozilla::AndroidGeckoEvent *GetNextEvent();
+    mozilla::AndroidGeckoEvent *PeekNextEvent();
+};
+
+#endif // nsAppShell_h__
+
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsIdleServiceAndroid.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Gijs Kruitbosch <gijskruitbosch@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Michael Wu <mwu@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIdleServiceAndroid.h"
+#include "nsIServiceManager.h"
+
+NS_IMPL_ISUPPORTS2(nsIdleServiceAndroid, nsIIdleService, nsIdleService)
+
+bool
+nsIdleServiceAndroid::PollIdleTime(PRUint32 *aIdleTime)
+{
+    return false;
+}
+
+bool
+nsIdleServiceAndroid::UsePollMode()
+{
+    return false;
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsIdleServiceAndroid.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Gijs Kruitbosch <gijskruitbosch@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Michael Wu <mwu@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsIdleServiceAndroid_h__
+#define nsIdleServiceAndroid_h__
+
+#include "nsIdleService.h"
+
+class nsIdleServiceAndroid : public nsIdleService
+{
+public:
+    NS_DECL_ISUPPORTS
+
+    bool PollIdleTime(PRUint32* aIdleTime);
+protected:
+    bool UsePollMode();
+};
+
+#endif // nsIdleServiceAndroid_h__
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsLookAndFeel.cpp
@@ -0,0 +1,506 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsLookAndFeel.h"
+
+nsLookAndFeel::nsLookAndFeel()
+    : nsXPLookAndFeel()
+{
+}
+
+nsLookAndFeel::~nsLookAndFeel()
+{
+}
+
+nsresult
+nsLookAndFeel::NativeGetColor(const nsColorID aID, nscolor &aColor)
+{
+    nsresult rv = NS_OK;
+
+#define BASE_ACTIVE_COLOR     NS_RGB(0xaa,0xaa,0xaa)
+#define BASE_NORMAL_COLOR     NS_RGB(0xff,0xff,0xff)
+#define BASE_SELECTED_COLOR   NS_RGB(0xaa,0xaa,0xaa)
+#define BG_ACTIVE_COLOR       NS_RGB(0xff,0xff,0xff)
+#define BG_INSENSITIVE_COLOR  NS_RGB(0xaa,0xaa,0xaa)
+#define BG_NORMAL_COLOR       NS_RGB(0xff,0xff,0xff)
+#define BG_PRELIGHT_COLOR     NS_RGB(0xee,0xee,0xee)
+#define BG_SELECTED_COLOR     NS_RGB(0x99,0x99,0x99)
+#define DARK_NORMAL_COLOR     NS_RGB(0x88,0x88,0x88)
+#define FG_INSENSITIVE_COLOR  NS_RGB(0x44,0x44,0x44)
+#define FG_NORMAL_COLOR       NS_RGB(0x00,0x00,0x00)
+#define FG_PRELIGHT_COLOR     NS_RGB(0x77,0x77,0x77)
+#define FG_SELECTED_COLOR     NS_RGB(0xaa,0xaa,0xaa)
+#define LIGHT_NORMAL_COLOR    NS_RGB(0xaa,0xaa,0xaa)
+#define TEXT_ACTIVE_COLOR     NS_RGB(0x99,0x99,0x99)
+#define TEXT_NORMAL_COLOR     NS_RGB(0x00,0x00,0x00)
+#define TEXT_SELECTED_COLOR   NS_RGB(0x00,0x00,0x00)
+
+    // XXX we'll want to use context.obtainStyledAttributes on the java side to
+    // get all of these; see TextView.java for a good exmaple.
+
+    switch (aID) {
+        // These colors don't seem to be used for anything anymore in Mozilla
+        // (except here at least TextSelectBackground and TextSelectForeground)
+        // The CSS2 colors below are used.
+    case eColor_WindowBackground:
+        aColor = BASE_NORMAL_COLOR;
+        break;
+    case eColor_WindowForeground:
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+    case eColor_WidgetBackground:
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_WidgetForeground:
+        aColor = FG_NORMAL_COLOR;
+        break;
+    case eColor_WidgetSelectBackground:
+        aColor = BG_SELECTED_COLOR;
+        break;
+    case eColor_WidgetSelectForeground:
+        aColor = FG_SELECTED_COLOR;
+        break;
+    case eColor_Widget3DHighlight:
+        aColor = NS_RGB(0xa0,0xa0,0xa0);
+        break;
+    case eColor_Widget3DShadow:
+        aColor = NS_RGB(0x40,0x40,0x40);
+        break;
+    case eColor_TextBackground:
+        // not used?
+        aColor = BASE_NORMAL_COLOR;
+        break;
+    case eColor_TextForeground:
+        // not used?
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+    case eColor_TextSelectBackground:
+    case eColor_IMESelectedRawTextBackground:
+    case eColor_IMESelectedConvertedTextBackground:
+        // still used
+        aColor = BASE_SELECTED_COLOR;
+        break;
+    case eColor_TextSelectForeground:
+    case eColor_IMESelectedRawTextForeground:
+    case eColor_IMESelectedConvertedTextForeground:
+        // still used
+        aColor = TEXT_SELECTED_COLOR;
+        break;
+    case eColor_IMERawInputBackground:
+    case eColor_IMEConvertedTextBackground:
+        aColor = NS_TRANSPARENT;
+        break;
+    case eColor_IMERawInputForeground:
+    case eColor_IMEConvertedTextForeground:
+        aColor = NS_SAME_AS_FOREGROUND_COLOR;
+        break;
+    case eColor_IMERawInputUnderline:
+    case eColor_IMEConvertedTextUnderline:
+        aColor = NS_SAME_AS_FOREGROUND_COLOR;
+        break;
+    case eColor_IMESelectedRawTextUnderline:
+    case eColor_IMESelectedConvertedTextUnderline:
+        aColor = NS_TRANSPARENT;
+        break;
+    case eColor_SpellCheckerUnderline:
+      aColor = NS_RGB(0xff, 0, 0);
+      break;
+
+        // css2  http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
+    case eColor_activeborder:
+        // active window border
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_activecaption:
+        // active window caption background
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_appworkspace:
+        // MDI background color
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_background:
+        // desktop background
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_captiontext:
+        // text in active window caption, size box, and scrollbar arrow box (!)
+        aColor = FG_NORMAL_COLOR;
+        break;
+    case eColor_graytext:
+        // disabled text in windows, menus, etc.
+        aColor = FG_INSENSITIVE_COLOR;
+        break;
+    case eColor_highlight:
+        // background of selected item
+        aColor = BASE_SELECTED_COLOR;
+        break;
+    case eColor_highlighttext:
+        // text of selected item
+        aColor = TEXT_SELECTED_COLOR;
+        break;
+    case eColor_inactiveborder:
+        // inactive window border
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_inactivecaption:
+        // inactive window caption
+        aColor = BG_INSENSITIVE_COLOR;
+        break;
+    case eColor_inactivecaptiontext:
+        // text in inactive window caption
+        aColor = FG_INSENSITIVE_COLOR;
+        break;
+    case eColor_infobackground:
+        // tooltip background color
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_infotext:
+        // tooltip text color
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+    case eColor_menu:
+        // menu background
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor_menutext:
+        // menu text
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+    case eColor_scrollbar:
+        // scrollbar gray area
+        aColor = BG_ACTIVE_COLOR;
+        break;
+
+    case eColor_threedface:
+    case eColor_buttonface:
+        // 3-D face color
+        aColor = BG_NORMAL_COLOR;
+        break;
+
+    case eColor_buttontext:
+        // text on push buttons
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+
+    case eColor_buttonhighlight:
+        // 3-D highlighted edge color
+    case eColor_threedhighlight:
+        // 3-D highlighted outer edge color
+        aColor = LIGHT_NORMAL_COLOR;
+        break;
+
+    case eColor_threedlightshadow:
+        // 3-D highlighted inner edge color
+        aColor = BG_NORMAL_COLOR;
+        break;
+
+    case eColor_buttonshadow:
+        // 3-D shadow edge color
+    case eColor_threedshadow:
+        // 3-D shadow inner edge color
+        aColor = DARK_NORMAL_COLOR;
+        break;
+
+    case eColor_threeddarkshadow:
+        // 3-D shadow outer edge color
+        aColor = NS_RGB(0,0,0);
+        break;
+
+    case eColor_window:
+    case eColor_windowframe:
+        aColor = BG_NORMAL_COLOR;
+        break;
+
+    case eColor_windowtext:
+        aColor = FG_NORMAL_COLOR;
+        break;
+
+    case eColor__moz_eventreerow:
+    case eColor__moz_field:
+        aColor = BASE_NORMAL_COLOR;
+        break;
+    case eColor__moz_fieldtext:
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+    case eColor__moz_dialog:
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor__moz_dialogtext:
+        aColor = FG_NORMAL_COLOR;
+        break;
+    case eColor__moz_dragtargetzone:
+        aColor = BG_SELECTED_COLOR;
+        break;
+    case eColor__moz_buttondefault:
+        // default button border color
+        aColor = NS_RGB(0,0,0);
+        break;
+    case eColor__moz_buttonhoverface:
+        aColor = BG_PRELIGHT_COLOR;
+        break;
+    case eColor__moz_buttonhovertext:
+        aColor = FG_PRELIGHT_COLOR;
+        break;
+    case eColor__moz_cellhighlight:
+    case eColor__moz_html_cellhighlight:
+        aColor = BASE_ACTIVE_COLOR;
+        break;
+    case eColor__moz_cellhighlighttext:
+    case eColor__moz_html_cellhighlighttext:
+        aColor = TEXT_ACTIVE_COLOR;
+        break;
+    case eColor__moz_menuhover:
+        aColor = BG_PRELIGHT_COLOR;
+        break;
+    case eColor__moz_menuhovertext:
+        aColor = FG_PRELIGHT_COLOR;
+        break;
+    case eColor__moz_oddtreerow:
+        aColor = NS_TRANSPARENT;
+        break;
+    case eColor__moz_nativehyperlinktext:
+        aColor = NS_SAME_AS_FOREGROUND_COLOR;
+        break;
+    case eColor__moz_comboboxtext:
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+    case eColor__moz_combobox:
+        aColor = BG_NORMAL_COLOR;
+        break;
+    case eColor__moz_menubartext:
+        aColor = TEXT_NORMAL_COLOR;
+        break;
+    case eColor__moz_menubarhovertext:
+        aColor = FG_PRELIGHT_COLOR;
+        break;
+    default:
+        /* default color is BLACK */
+        aColor = 0;
+        rv = NS_ERROR_FAILURE;
+        break;
+    }
+
+    return rv;
+}
+
+
+NS_IMETHODIMP
+nsLookAndFeel::GetMetric(const nsMetricID aID, PRInt32 &aMetric)
+{
+    nsresult rv = nsXPLookAndFeel::GetMetric(aID, aMetric);
+    if (NS_SUCCEEDED(rv))
+        return rv;
+
+    rv = NS_OK;
+
+    switch (aID) {
+        case eMetric_WindowTitleHeight:
+        case eMetric_WindowBorderWidth:
+        case eMetric_WindowBorderHeight:
+            aMetric = 0;
+            break;
+
+        case eMetric_Widget3DBorder:
+            aMetric = 4;
+            break;
+
+        case eMetric_TextFieldHeight:
+            aMetric = 15;
+            break;
+
+        case eMetric_TextFieldBorder:
+            aMetric = 2;
+            break;
+
+        case eMetric_TextVerticalInsidePadding:
+            aMetric = 0;
+            break;
+
+        case eMetric_TextShouldUseVerticalInsidePadding:
+            aMetric = 0;
+            break;
+
+        case eMetric_TextHorizontalInsideMinimumPadding:
+            aMetric = 15;
+            break;
+
+        case eMetric_TextShouldUseHorizontalInsideMinimumPadding:
+            aMetric = 1;
+            break;
+
+        case eMetric_ButtonHorizontalInsidePaddingNavQuirks:
+            aMetric = 10;
+            break;
+
+        case eMetric_ButtonHorizontalInsidePaddingOffsetNavQuirks:
+            aMetric = 8;
+            break;
+
+        case eMetric_CheckboxSize:
+        case eMetric_RadioboxSize:
+            aMetric = 15;
+            break;
+
+        case eMetric_ListShouldUseHorizontalInsideMinimumPadding:
+            aMetric = 15;
+            break;
+
+        case eMetric_ListHorizontalInsideMinimumPadding:
+            aMetric = 15;
+            break;
+
+        case eMetric_ListShouldUseVerticalInsidePadding:
+            aMetric = 1;
+            break;
+
+        case eMetric_ListVerticalInsidePadding:
+            aMetric = 1;
+            break;
+
+        case eMetric_CaretBlinkTime:
+            aMetric = 500;
+            break;
+
+        case eMetric_CaretWidth:
+            aMetric = 1;
+            break;
+
+        case eMetric_ShowCaretDuringSelection:
+            aMetric = 0;
+            break;
+
+        case eMetric_SelectTextfieldsOnKeyFocus:
+            // Select textfield content when focused by kbd
+            // used by nsEventStateManager::sTextfieldSelectModel
+            aMetric = 1;
+            break;
+
+        case eMetric_SubmenuDelay:
+            aMetric = 200;
+            break;
+
+        case eMetric_MenusCanOverlapOSBar:
+            // we want XUL popups to be able to overlap the task bar.
+            aMetric = 1;
+            break;
+
+        case eMetric_ScrollArrowStyle:
+            aMetric = eMetric_ScrollArrowStyleSingle;
+            break;
+
+        case eMetric_ScrollSliderStyle:
+            aMetric = eMetric_ScrollThumbStyleProportional;
+            break;
+
+        case eMetric_WindowsDefaultTheme:
+        case eMetric_TouchEnabled:
+        case eMetric_MaemoClassic:
+            aMetric = 0;
+            rv = NS_ERROR_NOT_IMPLEMENTED;
+            break;
+
+        case eMetric_SpellCheckerUnderlineStyle:
+            aMetric = NS_UNDERLINE_STYLE_WAVY;
+            break;
+
+        default:
+            aMetric = 0;
+            rv = NS_ERROR_FAILURE;
+    }
+
+    return rv;
+}
+
+NS_IMETHODIMP
+nsLookAndFeel::GetMetric(const nsMetricFloatID aID,
+                         float &aMetric)
+{
+    nsresult rv = nsXPLookAndFeel::GetMetric(aID, aMetric);
+    if (NS_SUCCEEDED(rv))
+        return rv;
+    rv = NS_OK;
+
+    switch (aID) {
+        case eMetricFloat_TextFieldVerticalInsidePadding:
+            aMetric = 0.25f;
+            break;
+
+        case eMetricFloat_TextFieldHorizontalInsidePadding:
+            aMetric = 0.95f; // large number on purpose so minimum padding is used
+            break;
+
+        case eMetricFloat_TextAreaVerticalInsidePadding:
+            aMetric = 0.40f;
+            break;
+
+        case eMetricFloat_TextAreaHorizontalInsidePadding:
+            aMetric = 0.40f; // large number on purpose so minimum padding is used
+            break;
+
+        case eMetricFloat_ListVerticalInsidePadding:
+            aMetric = 0.10f;
+            break;
+
+        case eMetricFloat_ListHorizontalInsidePadding:
+            aMetric = 0.40f;
+            break;
+
+        case eMetricFloat_ButtonVerticalInsidePadding:
+            aMetric = 0.25f;
+            break;
+
+        case eMetricFloat_ButtonHorizontalInsidePadding:
+            aMetric = 0.25f;
+            break;
+
+        case eMetricFloat_IMEUnderlineRelativeSize:
+            aMetric = 1.0f;
+            break;
+
+        case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+            aMetric = 1.0f;
+            break;
+
+        default:
+            aMetric = -1.0;
+            rv = NS_ERROR_FAILURE;
+            break;
+    }
+    return rv;
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsLookAndFeel.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Lars Knoll <knoll@kde.org>
+ *   John C. Griggs <johng@corel.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#ifndef __nsLookAndFeel
+#define __nsLookAndFeel
+
+#include "nsXPLookAndFeel.h"
+
+class nsLookAndFeel: public nsXPLookAndFeel
+{
+public:
+    nsLookAndFeel();
+    virtual ~nsLookAndFeel();
+
+    nsresult NativeGetColor(const nsColorID aID, nscolor &aColor);
+    NS_IMETHOD GetMetric(const nsMetricID aID, PRInt32 & aMetric);
+    NS_IMETHOD GetMetric(const nsMetricFloatID aID, float & aMetric);
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsScreenManagerAndroid.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsScreenManagerAndroid.h"
+#include "nsWindow.h"
+
+NS_IMPL_ISUPPORTS1(nsScreenAndroid, nsIScreen)
+
+nsScreenAndroid::nsScreenAndroid(void *nativeScreen)
+{
+}
+
+nsScreenAndroid::~nsScreenAndroid()
+{
+}
+
+NS_IMETHODIMP
+nsScreenAndroid::GetRect(PRInt32 *outLeft, PRInt32 *outTop, PRInt32 *outWidth, PRInt32 *outHeight)
+{
+    gfxIntSize sz = nsWindow::GetAndroidBounds();
+
+    *outLeft = 0;
+    *outTop = 0;
+
+    *outWidth = sz.width;
+    *outHeight = sz.height;
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsScreenAndroid::GetAvailRect(PRInt32 *outLeft, PRInt32 *outTop, PRInt32 *outWidth, PRInt32 *outHeight)
+{
+    return GetRect(outLeft, outTop, outWidth, outHeight);
+}
+
+
+
+NS_IMETHODIMP
+nsScreenAndroid::GetPixelDepth(PRInt32 *aPixelDepth)
+{
+    // XXX do we need to lie here about 16bpp?  Or
+    // should we actually check and return the right thing?
+    *aPixelDepth = 24;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsScreenAndroid::GetColorDepth(PRInt32 *aColorDepth)
+{
+    return GetPixelDepth(aColorDepth);
+}
+
+NS_IMPL_ISUPPORTS1(nsScreenManagerAndroid, nsIScreenManager)
+
+nsScreenManagerAndroid::nsScreenManagerAndroid()
+{
+    mOneScreen = new nsScreenAndroid(nsnull);
+}
+
+nsScreenManagerAndroid::~nsScreenManagerAndroid()
+{
+}
+
+NS_IMETHODIMP
+nsScreenManagerAndroid::GetPrimaryScreen(nsIScreen **outScreen)
+{
+    NS_IF_ADDREF(*outScreen = mOneScreen.get());
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScreenManagerAndroid::ScreenForRect(PRInt32 inLeft,
+                                      PRInt32 inTop,
+                                      PRInt32 inWidth,
+                                      PRInt32 inHeight,
+                                      nsIScreen **outScreen)
+{
+    return GetPrimaryScreen(outScreen);
+}
+
+NS_IMETHODIMP
+nsScreenManagerAndroid::ScreenForNativeWidget(void *aWidget, nsIScreen **outScreen)
+{
+    return GetPrimaryScreen(outScreen);
+}
+
+NS_IMETHODIMP
+nsScreenManagerAndroid::GetNumberOfScreens(PRUint32 *aNumberOfScreens)
+{
+    *aNumberOfScreens = 1;
+    return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsScreenManagerAndroid.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsScreenManagerAndroid_h___
+#define nsScreenManagerAndroid_h___
+
+#include "nsCOMPtr.h"
+
+#include "nsIScreenManager.h"
+#include "nsIScreen.h"
+
+class nsScreenAndroid :
+    public nsIScreen
+{
+public:
+    nsScreenAndroid(void *platformScreen);
+    ~nsScreenAndroid();
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISCREEN
+};
+
+class nsScreenManagerAndroid :
+    public nsIScreenManager
+{
+public:
+    nsScreenManagerAndroid();
+    ~nsScreenManagerAndroid();
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISCREENMANAGER
+
+protected:
+    nsCOMPtr<nsIScreen> mOneScreen;
+};
+
+#endif /* nsScreenManagerAndroid_h___ */
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsToolkit.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsToolkit.h"
+#include "nsGUIEvent.h"
+#include "nsWidgetAtoms.h"
+
+NS_IMPL_ISUPPORTS1(nsToolkit, nsIToolkit)
+
+// why do we have this?
+static PRUintn gToolkitTLSIndex = 0;
+
+nsToolkit::nsToolkit()
+{
+}
+
+nsToolkit::~nsToolkit()
+{
+    PR_SetThreadPrivate(gToolkitTLSIndex, nsnull);
+}
+
+NS_IMETHODIMP
+nsToolkit::Init(PRThread *aThread)
+{
+    nsWidgetAtoms::RegisterAtoms();
+    return NS_OK;
+}
+
+NS_METHOD
+NS_GetCurrentToolkit(nsIToolkit* *aResult)
+{
+    nsCOMPtr<nsIToolkit> toolkit = nsnull;
+    nsresult rv = NS_OK;
+    PRStatus status;
+
+    if (gToolkitTLSIndex == 0) {
+        status = PR_NewThreadPrivateIndex(&gToolkitTLSIndex, NULL);
+        if (PR_FAILURE == status)
+            rv = NS_ERROR_FAILURE;
+    }
+
+    if (NS_FAILED(rv))
+        return rv;
+
+    toolkit = (nsIToolkit*) PR_GetThreadPrivate(gToolkitTLSIndex);
+    if (!toolkit) {
+        toolkit = new nsToolkit();
+
+        if (toolkit) {
+            toolkit->Init(PR_GetCurrentThread());
+
+            PR_SetThreadPrivate(gToolkitTLSIndex, (void*)toolkit.get());
+        } else {
+            rv = NS_ERROR_OUT_OF_MEMORY;
+        }
+    }
+
+    NS_IF_ADDREF(*aResult = toolkit);
+
+    return rv;
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsToolkit.h
@@ -0,0 +1,57 @@
+/* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsToolkit_h__
+#define nsToolkit_h__
+
+#include <nsIToolkit.h>
+
+class nsToolkit :
+    public nsIToolkit
+{
+public:
+    NS_DECL_ISUPPORTS
+
+    // nsIToolkit
+    NS_IMETHOD Init(PRThread *aThread);
+
+    nsToolkit();
+    virtual ~nsToolkit();
+};
+
+#endif /* nsToolkit_h__ */
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsWidgetFactory.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIGenericFactory.h"
+#include "nsIModule.h"
+
+#include "nsCOMPtr.h"
+#include "nsWidgetsCID.h"
+#include "nsAppShell.h"
+#include "nsToolkit.h"
+
+#include "nsWindow.h"
+#include "nsLookAndFeel.h"
+#include "nsAppShellSingleton.h"
+#include "nsScreenManagerAndroid.h"
+
+#include "nsAccelerometerAndroid.h"
+#include "nsIdleServiceAndroid.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsToolkit)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLookAndFeel)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerAndroid)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAccelerometerAndroid)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceAndroid)
+
+
+static const nsModuleComponentInfo components[] =
+{
+    { "Android Toolkit",
+      NS_TOOLKIT_CID,
+      "@mozilla.org/widget/toolkit/android;1",
+      nsToolkitConstructor },
+    { "Android AppShell",
+      NS_APPSHELL_CID,
+      "@mozilla.org/widget/appshell/android;1",
+      nsAppShellConstructor },
+    { "Android nsWindow",
+      NS_WINDOW_CID,
+      "@mozilla.org/widgets/window/android;1",
+      nsWindowConstructor },
+    { "Android Child nsWindow",
+      NS_CHILD_CID,
+      "@mozilla.org/widgets/child_window/android;1",
+      nsWindowConstructor }, /* Note: same as Window! */
+    { "Android Look And Feel",
+      NS_LOOKANDFEEL_CID,
+      "@mozilla.org/widget/lookandfeel/android;1",
+      nsLookAndFeelConstructor },
+    { "Android Screen Manager",
+      NS_SCREENMANAGER_CID,
+      "@mozilla.org/gfx/screenmanager;1",
+      nsScreenManagerAndroidConstructor },
+    { "Android Idle Service",
+      NS_IDLE_SERVICE_CID,
+      "@mozilla.org/widget/idleservice;1",
+      nsIdleServiceAndroidConstructor },
+    { "Accelerometer",
+      NS_ACCELEROMETER_CID,
+      NS_ACCELEROMETER_CONTRACTID,
+      nsAccelerometerAndroidConstructor },
+
+};
+
+static void
+nsWidgetAndroidModuleDtor(nsIModule *aSelf)
+{
+    nsAppShellShutdown(aSelf);
+}
+
+NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(nsWidgetAndroidModule,
+                                   components,
+                                   nsAppShellInit,
+                                   nsWidgetAndroidModuleDtor)
+
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsWindow.cpp
@@ -0,0 +1,1654 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <android/log.h>
+
+#include "nsAppShell.h"
+#include "nsIdleService.h"
+#include "nsWindow.h"
+
+#include "nsIDeviceContext.h"
+#include "nsIRenderingContext.h"
+
+#include "nsWidgetAtoms.h"
+#include "nsWidgetsCID.h"
+#include "nsGfxCIID.h"
+
+#include "gfxImageSurface.h"
+#include "gfxContext.h"
+
+#include "nsTArray.h"
+
+#include "AndroidBridge.h"
+
+/* OpenGL */
+#define USE_GLES2
+
+#ifdef USE_GLES2
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#else
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#endif
+
+#ifndef GL_BGRA_EXT
+#define GL_BGRA_EXT 0x80E1
+#endif
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
+
+// The dimensions of the current android view
+static gfxIntSize gAndroidBounds;
+
+static PRBool gLeftShift;
+static PRBool gRightShift;
+static PRBool gLeftAlt;
+static PRBool gRightAlt;
+static PRBool gSym;
+
+// All the toplevel windows that have been created; these are in
+// stacking order, so the window at gAndroidBounds[0] is the topmost
+// one.
+static nsTArray<nsWindow*> gTopLevelWindows;
+static nsWindow* gFocusedWindow = nsnull;
+static PRUint32 gIMEState;
+
+static nsWindow*
+TopWindow()
+{
+    if (!gTopLevelWindows.IsEmpty())
+        return gTopLevelWindows[0];
+    return nsnull;
+}
+
+void
+nsWindow::LogWindow(nsWindow *win, int index, int indent)
+{
+    char spaces[] = "                    ";
+    spaces[indent < 20 ? indent : 20] = 0;
+    ALOG("%s [% 2d] 0x%08x [parent 0x%08x] [% 3d,% 3d % 3dx% 3d] vis %d type %d",
+         spaces, index, (intptr_t)win, (intptr_t)win->mParent,
+         win->mBounds.x, win->mBounds.y,
+         win->mBounds.width, win->mBounds.height,
+         win->mIsVisible, win->mWindowType);
+}
+
+void
+nsWindow::DumpWindows()
+{
+    DumpWindows(gTopLevelWindows);
+}
+
+void
+nsWindow::DumpWindows(const nsTArray<nsWindow*>& wins, int indent)
+{
+    for (int i = 0; i < wins.Length(); ++i) {
+        nsWindow *w = wins[i];
+        LogWindow(w, i, indent);
+        DumpWindows(w->mChildren, indent+1);
+    }
+}
+
+nsWindow::nsWindow() :
+    mIsVisible(PR_FALSE),
+    mParent(nsnull),
+    mSpecialKeyTracking(0)
+{
+}
+
+nsWindow::~nsWindow()
+{
+    gTopLevelWindows.RemoveElement(this);
+    ALOG("nsWindow %p destructor", (void*)this);
+}
+
+PRBool
+nsWindow::IsTopLevel()
+{
+    return mWindowType == eWindowType_toplevel ||
+        mWindowType == eWindowType_dialog ||
+        mWindowType == eWindowType_invisible;
+}
+
+NS_IMETHODIMP
+nsWindow::Create(nsIWidget *aParent,
+                 nsNativeWidget aNativeParent,
+                 const nsIntRect &aRect,
+                 EVENT_CALLBACK aHandleEventFunction,
+                 nsIDeviceContext *aContext,
+                 nsIAppShell *aAppShell,
+                 nsIToolkit *aToolkit,
+                 nsWidgetInitData *aInitData)
+{
+    ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent, aRect.x, aRect.y, aRect.width, aRect.height);
+    nsWindow *parent = (nsWindow*) aParent;
+
+    if (aNativeParent) {
+        if (parent) {
+            ALOG("Ignoring native parent on Android window [%p], since parent was specified (%p %p)", (void*)this, (void*)aNativeParent, (void*)aParent);
+        } else {
+            parent = (nsWindow*) aNativeParent;
+        }
+    }
+
+    mBounds = aRect;
+
+    // for toplevel windows, bounds are fixed to full screen size
+    if (!parent) {
+        mBounds.x = 0;
+        mBounds.y = 0;
+        mBounds.width = gAndroidBounds.width;
+        mBounds.height = gAndroidBounds.height;
+    }
+
+    BaseCreate(nsnull, mBounds, aHandleEventFunction, aContext,
+               aAppShell, aToolkit, aInitData);
+
+    NS_ASSERTION(IsTopLevel() || parent, "non top level windowdoesn't have a parent!");
+
+    if (IsTopLevel()) {
+        gTopLevelWindows.AppendElement(this);
+    }
+
+    if (parent) {
+        parent->mChildren.AppendElement(this);
+        mParent = parent;
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Destroy(void)
+{
+    for (PRUint32 i = 0; i < mChildren.Length(); ++i) {
+        // why do we still have children?
+        ALOG("### Warning: Destroying window %p and reparenting child %p to null!", (void*)this, (void*)mChildren[i]);
+        mChildren[i]->SetParent(nsnull);
+    }
+
+    if (IsTopLevel())
+        gTopLevelWindows.RemoveElement(this);
+
+    if (mParent)
+        mParent->mChildren.RemoveElement(this);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config)
+{
+    for (PRUint32 i = 0; i < config.Length(); ++i) {
+        nsWindow *childWin = (nsWindow*) config[i].mChild;
+        childWin->Resize(config[i].mBounds.x,
+                         config[i].mBounds.y,
+                         config[i].mBounds.width,
+                         config[i].mBounds.height,
+                         PR_FALSE);
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetParent(nsIWidget *aNewParent)
+{
+    if ((nsIWidget*)mParent == aNewParent)
+        return NS_OK;
+
+    // If we had a parent before, remove ourselves from its list of
+    // children.  If we didn't have a parent, then remove ourselves
+    // from the list of toplevel windows if we're about to get a
+    // parent.
+    if (mParent)
+        mParent->mChildren.RemoveElement(this);
+
+    mParent = (nsWindow*)aNewParent;
+
+    if (mParent)
+        mParent->mChildren.AppendElement(this);
+
+    // if we are now in the toplevel window's hierarchy, schedule a redraw
+    if (FindTopLevel() == TopWindow())
+        nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(TopWindow(), -1, -1, -1, -1));
+
+    return NS_OK;
+}
+
+nsIWidget*
+nsWindow::GetParent()
+{
+    return mParent;
+}
+
+NS_IMETHODIMP
+nsWindow::Show(PRBool aState)
+{
+    ALOG("nsWindow[%p]::Show %d", (void*)this, aState);
+
+    if (mWindowType == eWindowType_invisible) {
+        ALOG("trying to show invisible window! ignoring..");
+        return NS_ERROR_FAILURE;
+    }
+
+    if ((aState && !mIsVisible) ||
+        (!aState && mIsVisible))
+    {
+        mIsVisible = aState;
+
+        if (IsTopLevel()) {
+            // XXX should we bring this to the front when it's shown,
+            // if it's a toplevel widget?
+
+            // XXX we should synthesize a NS_MOUSE_EXIT (for old top
+            // window)/NS_MOUSE_ENTER (for new top window) since we need
+            // to pretend that the top window always has focus.  Not sure
+            // if Show() is the right place to do this, though.
+
+            if (mIsVisible) {
+                // It just became visible, so send a resize update if necessary
+                // and bring it to the front.
+                Resize(0, 0, gAndroidBounds.width, gAndroidBounds.height, PR_FALSE);
+                BringToFront();
+            }
+        } else if (FindTopLevel() == TopWindow()) {
+            nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(TopWindow(), -1, -1, -1, -1));
+        }
+    }
+
+#ifdef ANDROID_DEBUG_WIDGET
+    DumpWindows();
+#endif
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetModal(PRBool aState)
+{
+    ALOG("nsWindow[%p]::SetModal %d ignored", (void*)this, aState);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::IsVisible(PRBool& aState)
+{
+    aState = mIsVisible;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::ConstrainPosition(PRBool aAllowSlop,
+                            PRInt32 *aX,
+                            PRInt32 *aY)
+{
+    ALOG("nsWindow[%p]::ConstrainPosition %d [%d %d]", (void*)this, aAllowSlop, *aX, *aY);
+
+    // constrain toplevel windows; children we don't care about
+    if (IsTopLevel()) {
+        *aX = 0;
+        *aY = 0;
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Move(PRInt32 aX,
+               PRInt32 aY)
+{
+    if (IsTopLevel())
+        return NS_OK;
+
+    return Resize(aX,
+                  aY,
+                  mBounds.width,
+                  mBounds.height,
+                  PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsWindow::Resize(PRInt32 aWidth,
+                 PRInt32 aHeight,
+                 PRBool aRepaint)
+{
+    return Resize(mBounds.x,
+                  mBounds.y,
+                  aWidth,
+                  aHeight,
+                  aRepaint);
+}
+
+NS_IMETHODIMP
+nsWindow::Resize(PRInt32 aX,
+                 PRInt32 aY,
+                 PRInt32 aWidth,
+                 PRInt32 aHeight,
+                 PRBool aRepaint)
+{
+    ALOG("nsWindow[%p]::Resize [%d %d %d %d] (repaint %d)", (void*)this, aX, aY, aWidth, aHeight, aRepaint);
+
+    PRBool needSizeDispatch = aWidth != mBounds.width || aHeight != mBounds.height;
+
+    if (IsTopLevel()) {
+        ALOG("... ignoring Resize sizes on toplevel window");
+        aX = 0;
+        aY = 0;
+        aWidth = gAndroidBounds.width;
+        aHeight = gAndroidBounds.height;
+    }
+
+    mBounds.x = aX;
+    mBounds.y = aY;
+    mBounds.width = aWidth;
+    mBounds.height = aHeight;
+
+    if (needSizeDispatch)
+        OnSizeChanged(gfxIntSize(aWidth, aHeight));
+
+    // Should we skip honoring aRepaint here?
+    if (aRepaint && FindTopLevel() == TopWindow())
+        nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(TopWindow(), -1, -1, -1, -1));
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetZIndex(PRInt32 aZIndex)
+{
+    ALOG("nsWindow[%p]::SetZIndex %d ignored", (void*)this, aZIndex);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
+                      nsIWidget *aWidget,
+                      PRBool aActivate)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::SetSizeMode(PRInt32 aMode)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Enable(PRBool aState)
+{
+    ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::IsEnabled(PRBool *aState)
+{
+    *aState = PR_TRUE;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Invalidate(const nsIntRect &aRect,
+                     PRBool aIsSynchronous)
+{
+    ALOG("nsWindow::Invalidate %p [%d %d %d %d]", (void*) this, aRect.x, aRect.y, aRect.width, aRect.height);
+    nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(TopWindow(), -1, -1, -1, -1));
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::Update()
+{
+    return NS_OK;
+}
+
+void
+nsWindow::Scroll(const nsIntPoint&,
+                 const nsTArray<nsIntRect>&,
+                 const nsTArray<nsIWidget::Configuration>&)
+{
+    ALOG("nsWindow[%p]::Scroll ignored!", (void*)this);
+}
+
+nsWindow*
+nsWindow::FindTopLevel()
+{
+    nsWindow *toplevel = this;
+    while (toplevel) {
+        if (toplevel->IsTopLevel())
+            return toplevel;
+
+        toplevel = toplevel->mParent;
+    }
+
+    ALOG("nsWindow::FindTopLevel(): couldn't find a toplevel or dialog window in this [%p] widget's hierarchy!", (void*)this);
+    return this;
+}
+
+NS_IMETHODIMP
+nsWindow::SetFocus(PRBool aRaise)
+{
+    if (!aRaise)
+        ALOG("nsWindow::SetFocus: can't set focus without raising, ignoring aRaise = false!");
+
+    gFocusedWindow = this;
+    FindTopLevel()->BringToFront();
+
+    return NS_OK;
+}
+
+void
+nsWindow::BringToFront()
+{
+    if (FindTopLevel() == TopWindow())
+        return;
+
+    if (!IsTopLevel()) {
+        FindTopLevel()->BringToFront();
+        return;
+    }
+
+    nsWindow *oldTop = nsnull;
+    if (!gTopLevelWindows.IsEmpty())
+        oldTop = gTopLevelWindows[0];
+
+    gTopLevelWindows.RemoveElement(this);
+    gTopLevelWindows.InsertElementAt(0, this);
+
+    if (oldTop) {
+        nsGUIEvent event(PR_TRUE, NS_DEACTIVATE, gTopLevelWindows[0]);
+        DispatchEvent(&event);
+    }
+
+    nsGUIEvent event(PR_TRUE, NS_ACTIVATE, this);
+    DispatchEvent(&event);
+
+    nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(TopWindow(), -1, -1, -1, -1));
+}
+
+NS_IMETHODIMP
+nsWindow::GetScreenBounds(nsIntRect &aRect)
+{
+    nsIntPoint p = WidgetToScreenOffset();
+
+    aRect.x = p.x;
+    aRect.y = p.y;
+    aRect.width = mBounds.width;
+    aRect.height = mBounds.height;
+    
+    return NS_OK;
+}
+
+nsIntPoint
+nsWindow::WidgetToScreenOffset()
+{
+    nsIntPoint p(0, 0);
+    nsWindow *w = this;
+
+    while (w && !w->IsTopLevel()) {
+        p.x += w->mBounds.x;
+        p.y += w->mBounds.y;
+
+        w = w->mParent;
+    }
+
+    return p;
+}
+
+NS_IMETHODIMP
+nsWindow::DispatchEvent(nsGUIEvent *aEvent,
+                        nsEventStatus &aStatus)
+{
+    aStatus = DispatchEvent(aEvent);
+    return NS_OK;
+}
+
+nsEventStatus
+nsWindow::DispatchEvent(nsGUIEvent *aEvent)
+{
+    if (mEventCallback)
+        return (*mEventCallback)(aEvent);
+    return nsEventStatus_eIgnore;
+}
+
+NS_IMETHODIMP
+nsWindow::SetWindowClass(const nsAString& xulWinType)
+{
+    return NS_OK;
+}
+
+gfxASurface*
+nsWindow::GetThebesSurface()
+{
+    /* This is really a dummy surface; this is only used when doing reflow, because
+     * we need a RenderingContext to measure text against.
+     */
+
+    // XXX this really wants to return already_AddRefed, but this only really gets used
+    // on direct assignment to a gfxASurface
+    return new gfxImageSurface(gfxIntSize(5,5), gfxImageSurface::ImageFormatRGB24);
+}
+
+void
+nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
+{
+    switch (ae->Type()) {
+        case AndroidGeckoEvent::SIZE_CHANGED: {
+            int nw = ae->P0().x;
+            int nh = ae->P0().y;
+
+            if (nw == gAndroidBounds.width &&
+                nh == gAndroidBounds.height)
+            {
+                return;
+            }
+
+            gAndroidBounds.width = nw;
+            gAndroidBounds.height = nh;
+
+            // tell all the windows about the new size
+            for (size_t i = 0; i < gTopLevelWindows.Length(); ++i) {
+                if (gTopLevelWindows[i]->mIsVisible)
+                    gTopLevelWindows[i]->Resize(gAndroidBounds.width, gAndroidBounds.height, PR_TRUE);
+            }
+
+            break;
+        }
+
+        case AndroidGeckoEvent::MOTION_EVENT: {
+            TopWindow()->UserActivity();
+            if (!gTopLevelWindows.IsEmpty()) {
+                nsIntPoint pt(ae->P0());
+                pt.x = NS_MIN(NS_MAX(pt.x, 0), gAndroidBounds.width - 1);
+                pt.y = NS_MIN(NS_MAX(pt.y, 0), gAndroidBounds.height - 1);
+                nsWindow *target = TopWindow()->FindWindowForPoint(pt);
+
+#if 0
+                ALOG("MOTION_EVENT %f,%f -> %p (visible: %d children: %d)", ae->P0().x, ae->P0().y, (void*)target,
+                     target ? target->mIsVisible : 0,
+                     target ? target->mChildren.Length() : 0);
+
+                DumpWindows();
+#endif
+
+                if (target)
+                    target->OnMotionEvent(ae);
+            }
+            break;
+        }
+
+        case AndroidGeckoEvent::KEY_EVENT:
+            TopWindow()->UserActivity();
+            if (gFocusedWindow)
+                gFocusedWindow->OnKeyEvent(ae);
+            break;
+
+        case AndroidGeckoEvent::DRAW:
+            if (TopWindow())
+                TopWindow()->OnDraw(ae);
+            break;
+
+        case AndroidGeckoEvent::IME_EVENT:
+            TopWindow()->UserActivity();
+            if (gFocusedWindow)
+                gFocusedWindow->OnIMEEvent(ae);
+            break;
+
+        default:
+            break;
+    }
+}
+
+void
+nsWindow::OnAndroidEvent(AndroidGeckoEvent *ae)
+{
+    switch (ae->Type()) {
+        case AndroidGeckoEvent::DRAW:
+            OnDraw(ae);
+            break;
+
+        default:
+            ALOG("Window got targetted android event type %d, but didn't handle!", ae->Type());
+            break;
+    }
+}
+
+PRBool
+nsWindow::DrawTo(gfxASurface *targetSurface)
+{
+    if (!mIsVisible)
+        return PR_FALSE;
+
+    nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
+
+    nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
+
+    nsPaintEvent event(PR_TRUE, NS_PAINT, this);
+    event.region = boundsRect;
+    {
+        AutoLayerManagerSetup setupLayerManager(this, ctx);
+        nsEventStatus status = DispatchEvent(&event);
+    }
+
+    // XXX uhh.. we can't just ignore this because we no longer have
+    // what we needed before, but let's keep drawing the children anyway?
+#if 0
+    if (status == nsEventStatus_eIgnore)
+        return PR_FALSE;
+#endif
+
+    // XXX if we got an ignore for the parent, do we still want to draw the children?
+    // We don't really have a good way not to...
+
+    gfxPoint offset = targetSurface->GetDeviceOffset();
+
+    for (PRUint32 i = 0; i < mChildren.Length(); ++i) {
+        targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x,
+                                                         mChildren[i]->mBounds.y));
+        if (!mChildren[i]->DrawTo(targetSurface)) {
+            ALOG("nsWindow[%p]::DrawTo child %d[%p] returned FALSE!", (void*) this, i, (void*)mChildren[i]);
+        }
+    }
+
+    targetSurface->SetDeviceOffset(offset);
+
+    return PR_TRUE;
+}
+
+static int
+next_power_of_two(int v)
+{
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+
+    return v;
+}
+
+void
+nsWindow::OnDraw(AndroidGeckoEvent *ae)
+{
+    AndroidBridge::AutoLocalJNIFrame jniFrame;
+
+    static bool firstDraw = true;
+
+    ALOG(">> OnDraw");
+
+    AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
+
+    NS_ASSERTION(!sview.isNull(), "SurfaceView is null!");
+
+    if (!IsTopLevel()) {
+        ALOG("##### redraw for window %p, which is not a toplevel window -- sending to toplevel!", (void*) this);
+        DumpWindows();
+        return;
+    }
+
+    if (!mIsVisible) {
+        ALOG("##### redraw for window %p, which is not visible -- ignoring!", (void*) this);
+        DumpWindows();
+        return;
+    }
+
+    int drawType = sview.BeginDrawing();
+
+    if (drawType == AndroidGeckoSurfaceView::DRAW_ERROR) {
+        ALOG("##### BeginDrawing failed!");
+        return;
+    }
+
+    nsRefPtr<gfxImageSurface> targetSurface;
+
+    if (drawType == AndroidGeckoSurfaceView::DRAW_SOFTWARE) {
+        int bufCap;
+        unsigned char *buf = sview.GetSoftwareDrawBuffer(&bufCap);
+        if (!buf || bufCap != mBounds.width * mBounds.height * 4) {
+            ALOG("### Software drawing, but too small a buffer %d expected %d (or no buffer %p)!", bufCap, mBounds.width * mBounds.height * 4, (void*)buf);
+            sview.EndDrawing();
+            return;
+        }
+        targetSurface =
+            new gfxImageSurface(buf,
+                                gfxIntSize(mBounds.width, mBounds.height),
+                                mBounds.width * 4,
+                                gfxASurface::ImageFormatARGB32);
+
+        DrawTo(targetSurface);
+
+        // need to swap B and R channels, to get ABGR instead of ARGB
+        unsigned int *ibuf = (unsigned int*) buf;
+        unsigned int *ibufMax = ibuf + mBounds.width * mBounds.height;
+        while (ibuf < ibufMax) {
+            *ibuf++ = (*ibuf & 0xff00ff00) | ((*ibuf & 0x00ff0000) >> 16) | ((*ibuf & 0x000000ff) << 16);
+        }
+
+        sview.EndDrawing();
+        return;
+    }
+
+    targetSurface =
+        new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height), gfxASurface::ImageFormatARGB32);
+
+    if (!DrawTo(targetSurface)) {
+        sview.EndDrawing();
+        return;
+    }
+
+#ifdef USE_GLES2
+    if (drawType != AndroidGeckoSurfaceView::DRAW_GLES_2) {
+        ALOG("#### GL drawing path, but beingDrawing wanted something else!");
+        sview.EndDrawing();
+        return;
+    }
+
+    static bool hasBGRA = false;
+
+    if (firstDraw) {
+        const char *ext = (const char *) glGetString(GL_EXTENSIONS);
+        ALOG("GL extensions: %s", ext);
+        if (strstr(ext, "GL_EXT_bgra") ||
+            strstr(ext, "GL_IMG_texture_format_BGRA8888") ||
+            strstr(ext, "GL_EXT_texture_format_BGRA8888"))
+            hasBGRA = true;
+
+        firstDraw = false;
+    }
+
+    static GLuint textureId = GLuint(-1);
+    static GLuint programId = GLuint(-1);
+    static GLint positionLoc, texCoordLoc, textureLoc;
+    if (textureId == GLuint(-1) || !glIsTexture(textureId)) {
+        glGenTextures(1, &textureId);
+
+        glBindTexture(GL_TEXTURE_2D, textureId);
+
+        /* Set required NPOT texture params, for baseline ES2 NPOT support */
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+        const char *vsString =
+            "attribute vec3 aPosition; \n"
+            "attribute vec2 aTexCoord; \n"
+            "varying vec2 vTexCoord; \n"
+            "void main() { \n"
+            "  gl_Position = vec4(aPosition, 1.0); \n"
+            "  vTexCoord = aTexCoord; \n"
+            "}";
+
+        /* Note that while the swizzle is, afaik, "free", the texture upload
+         * can potentially be faster if the native hardware format is BGRA.  So
+         * we use BGRA if it's available.
+         */
+
+        const char *fsStringNoBGRA =
+            "precision mediump float; \n"
+            "varying vec2 vTexCoord; \n"
+            "uniform sampler2D sTexture; \n"
+            "void main() { \n"
+            "  gl_FragColor = vec4(texture2D(sTexture, vTexCoord).bgr, 1.0); \n"
+            "}";
+
+        const char *fsStringBGRA =
+            "precision mediump float; \n"
+            "varying vec2 vTexCoord; \n"
+            "uniform sampler2D sTexture; \n"
+            "void main() { \n"
+            "  gl_FragColor = vec4(texture2D(sTexture, vTexCoord).rgb, 1.0); \n"
+            "}";
+
+        GLint status;
+
+        GLuint vsh = glCreateShader(GL_VERTEX_SHADER);
+        glShaderSource(vsh, 1, &vsString, NULL);
+        glCompileShader(vsh);
+        glGetShaderiv(vsh, GL_COMPILE_STATUS, &status);
+        if (!status) {
+            ALOG("Failed to compile vertex shader");
+            return;
+        }
+
+        GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER);
+        glShaderSource(fsh, 1, hasBGRA ? &fsStringBGRA : &fsStringNoBGRA, NULL);
+        glCompileShader(fsh);
+        glGetShaderiv(fsh, GL_COMPILE_STATUS, &status);
+        if (!status) {
+            ALOG("Failed to compile fragment shader");
+            return;
+        }
+
+        programId = glCreateProgram();
+        glAttachShader(programId, vsh);
+        glAttachShader(programId, fsh);
+
+        glLinkProgram(programId);
+        glGetProgramiv(programId, GL_LINK_STATUS, &status);
+        if (!status) {
+            ALOG("Failed to link program");
+            return;
+        }
+
+        positionLoc = glGetAttribLocation(programId, "aPosition");
+        texCoordLoc = glGetAttribLocation(programId, "aTexCoord");
+        textureLoc = glGetUniformLocation(programId, "sTexture");
+    }
+
+    int texDimWidth = targetSurface->Width();
+    int texDimHeight = targetSurface->Height();
+
+    glClearColor(1.0f, 0.3f, 0.3f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glFrontFace(GL_CCW);
+
+    glDisable(GL_CULL_FACE);
+    glDisable(GL_DEPTH_TEST);
+
+    glBindTexture(GL_TEXTURE_2D, textureId);
+
+    glTexImage2D(GL_TEXTURE_2D,
+                 0,
+                 hasBGRA ? GL_BGRA_EXT : GL_RGBA,
+                 texDimWidth,
+                 texDimHeight,
+                 0,
+                 hasBGRA ? GL_BGRA_EXT : GL_RGBA,
+                 GL_UNSIGNED_BYTE,
+                 targetSurface->Data());
+
+    GLfloat texCoords[] = { 0.0f, 1.0f,
+                            0.0f, 0.0f,
+                            1.0f, 1.0f,
+                            1.0f, 0.0f };
+
+    GLfloat vCoords[] = { -1.0f, -1.0f, 0.0f,
+                          -1.0f,  1.0f, 0.0f,
+                           1.0f, -1.0f, 0.0f,
+                           1.0f,  1.0f, 0.0f };
+
+    glUseProgram(programId);
+
+    glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, vCoords);
+    glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
+
+    glEnableVertexAttribArray(positionLoc);
+    glEnableVertexAttribArray(texCoordLoc);
+
+    glUniform1i(textureLoc, 0);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+    int err = glGetError();
+    if (err)
+        ALOG("GL error: %d", err);
+#else
+
+    /* GLES 1.0[1?] code.
+     *
+     * Two things:
+     *   NPOT textures are not supported by default, unlike with GLES 2
+     *   BGRA texture support is generally available, and might have
+     *   one of three different extension names.
+     */
+
+    static bool hasBGRA = false;
+    static bool hasNPOT = false;
+    static bool hasDrawTex = false;
+
+    if (firstDraw) {
+        const char *ext = (const char *) glGetString(GL_EXTENSIONS);
+        ALOG("GL extensions: %s", ext);
+        if (strstr(ext, "GL_EXT_bgra") ||
+            strstr(ext, "GL_IMG_texture_format_BGRA8888") ||
+            strstr(ext, "GL_EXT_texture_format_BGRA8888"))
+            hasBGRA = true;
+
+        if (strstr(ext, "GL_ARB_texture_non_power_of_two"))
+            hasNPOT = true;
+
+        if (strstr(ext, "GL_OES_draw_texture"))
+            hasDrawTex = true;
+
+        if (!hasBGRA)
+            ALOG("No BGRA extension found -- colors will be weird! XXX FIXME");
+
+        firstDraw = false;
+    }
+
+    glClearColor(1.0f, 0.3f, 0.3f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    // This is all the way a hack and will need to be changed with
+    // real code to handle lost/reset context issues (e.g. when
+    // rotating.
+    static int lastTexDimWidth = -1;
+    static int lastTexDimHeight = -1;
+    static GLuint textureId = GLuint(-1);
+    if (textureId == GLuint(-1) || !glIsTexture(textureId)) {
+        glGenTextures(1, &textureId);
+
+        // Reset these to ensure that the non-NPOT path
+        // calls TexImage2D first.
+        lastTexDimWidth = -1;
+        lastTexDimHeight = -1;
+    }
+
+    glBindTexture(GL_TEXTURE_2D, textureId);
+
+    int texDimWidth = targetSurface->Width();
+    int texDimHeight = targetSurface->Height();
+
+    // Not sure if it would be faster to just call TexImage2D
+    // if we have NPOT textures -- I'd hope that the driver
+    // can turn that SubImage2D to an equivalent operation,
+    // given that the dimensions are going to cover the full
+    // size of the texture.
+
+
+    // There seems to be a Tegra bug here, where we can't
+    // do TexSubImage2D with GL_BGRA_EXT.  We have NPOT there,
+    // but it means that we have to have a separate codepath
+    // for when we have NPOT to avoid the update with SubImage2D.
+
+    if (hasNPOT) {
+        glTexImage2D(GL_TEXTURE_2D,
+                     0,
+                     hasBGRA ? GL_BGRA_EXT : GL_RGBA,
+                     texDimWidth,
+                     texDimHeight,
+                     0,
+                     hasBGRA ? GL_BGRA_EXT : GL_RGBA,
+                     GL_UNSIGNED_BYTE,
+                     targetSurface->Data());
+    } else {
+        texDimWidth = next_power_of_two(targetSurface->Width());
+        texDimHeight = next_power_of_two(targetSurface->Height());
+
+        if (lastTexDimWidth != texDimWidth ||
+            lastTexDimHeight != texDimHeight)
+        {
+            // Set the texture size, but don't load
+            // data.
+            glTexImage2D(GL_TEXTURE_2D,
+                         0,
+                         hasBGRA ? GL_BGRA_EXT : GL_RGBA,
+                         texDimWidth,
+                         texDimHeight,
+                         0,
+                         hasBGRA ? GL_BGRA_EXT : GL_RGBA,
+                         GL_UNSIGNED_BYTE,
+                         NULL);
+
+            lastTexDimWidth = texDimWidth;
+            lastTexDimHeight = texDimHeight;
+        }
+
+        // then actually load the data in the sub-rectangle
+        glTexSubImage2D(GL_TEXTURE_2D,
+                        0,
+                        0, 0,
+                        targetSurface->Width(),
+                        targetSurface->Height(),
+                        hasBGRA ? GL_BGRA_EXT : GL_RGBA,
+                        GL_UNSIGNED_BYTE,
+                        targetSurface->Data());
+    }
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glEnable(GL_TEXTURE_2D);
+
+    glFrontFace(GL_CCW);
+
+    glDisable(GL_CULL_FACE);
+    glDisable(GL_ALPHA_TEST);
+    glDisable(GL_DEPTH_TEST);
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+
+    GLfloat texCoordW = GLfloat(targetSurface->Width()) / GLfloat(texDimWidth);
+    GLfloat texCoordH = GLfloat(targetSurface->Height()) / GLfloat(texDimHeight);
+
+    GLfloat texCoords[] = { 0.0f, texCoordH,
+                            0.0f, 0.0f,
+                            texCoordW, texCoordH,
+                            texCoordW, 0.0f };
+
+    GLfloat vCoords[] = { -1.0f, -1.0f, 0.0f,
+                          -1.0f,  1.0f, 0.0f,
+                           1.0f, -1.0f, 0.0f,
+                           1.0f,  1.0f, 0.0f };
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+    glVertexPointer(3, GL_FLOAT, 0, vCoords);
+    glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+#endif
+
+    sview.EndDrawing();
+}
+
+void
+nsWindow::OnSizeChanged(const gfxIntSize& aSize)
+{
+    int w = aSize.width;
+    int h = aSize.height;
+
+    ALOG("nsWindow: %p OnSizeChanged [%d %d]", (void*)this, w, h);
+
+    nsSizeEvent event(PR_TRUE, NS_SIZE, this);
+    InitEvent(event);
+
+    nsIntRect wsz(0, 0, w, h);
+    event.windowSize = &wsz;
+    event.mWinWidth = w;
+    event.mWinHeight = h;
+
+    mBounds.width = w;
+    mBounds.height = h;
+
+    DispatchEvent(&event);
+}
+
+void
+nsWindow::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint)
+{
+    if (aPoint) {
+        event.refPoint.x = aPoint->x;
+        event.refPoint.y = aPoint->y;
+    } else {
+        event.refPoint.x = 0;
+        event.refPoint.y = 0;
+    }
+
+    event.time = PR_Now() / 1000;
+}
+
+void
+nsWindow::SetInitialAndroidBounds(const gfxIntSize& sz)
+{
+    if (!gTopLevelWindows.IsEmpty()) {
+        NS_WARNING("SetInitialAndroidBounds called way too late, we already have toplevel windows!");
+    }
+
+    gAndroidBounds = sz;
+}
+
+gfxIntSize
+nsWindow::GetAndroidBounds()
+{
+    return gAndroidBounds;
+}
+
+void *
+nsWindow::GetNativeData(PRUint32 aDataType)
+{
+    switch (aDataType) {
+        case NS_NATIVE_WIDGET:
+            return (void *) this;
+    }
+
+    return nsnull;
+}
+
+void
+nsWindow::OnMotionEvent(AndroidGeckoEvent *ae)
+{
+    PRUint32 msg;
+    switch (ae->Action() & AndroidMotionEvent::ACTION_MASK) {
+        case AndroidMotionEvent::ACTION_DOWN:
+            msg = NS_MOUSE_BUTTON_DOWN;
+            break;
+
+        case AndroidMotionEvent::ACTION_MOVE:
+            msg = NS_MOUSE_MOVE;
+            break;
+
+        case AndroidMotionEvent::ACTION_UP:
+        case AndroidMotionEvent::ACTION_CANCEL:
+            msg = NS_MOUSE_BUTTON_UP;
+            break;
+
+        default:
+            // we don't handle any other motion events yet
+            return;
+    }
+
+    nsIntPoint pt(ae->P0());
+    nsIntPoint offset = WidgetToScreenOffset();
+
+    //ALOG("#### motion pt: %d %d offset: %d %d", pt.x, pt.y, offset.x, offset.y);
+
+    pt.x -= offset.x;
+    pt.y -= offset.y;
+
+    // XXX possibly bound the range of pt here. some code may get confused.
+
+send_again:
+
+    nsMouseEvent event(PR_TRUE,
+                       msg, this,
+                       nsMouseEvent::eReal, nsMouseEvent::eNormal);
+    InitEvent(event, &pt);
+
+    event.time = ae->Time();
+    event.isShift = gLeftShift || gRightShift;
+    event.isControl = gSym;
+    event.isMeta = PR_FALSE;
+    event.isAlt = gLeftAlt || gRightAlt;
+
+    // XXX can we synthesize different buttons?
+    event.button = nsMouseEvent::eLeftButton;
+
+    if (msg != NS_MOUSE_MOVE)
+        event.clickCount = 1;
+
+    // XXX add the double-click handling logic here
+
+    DispatchEvent(&event);
+
+    if (msg == NS_MOUSE_BUTTON_DOWN) {
+        msg = NS_MOUSE_MOVE;
+        goto send_again;
+    }
+}
+
+void
+nsWindow::InitKeyEvent(nsKeyEvent& event, AndroidGeckoEvent& key)
+{
+    switch (key.KeyCode()) {
+    case AndroidKeyEvent::KEYCODE_UNKNOWN:
+    case AndroidKeyEvent::KEYCODE_HOME:
+        break;
+    case AndroidKeyEvent::KEYCODE_BACK:
+        event.keyCode = NS_VK_ESCAPE;
+        break;
+    case AndroidKeyEvent::KEYCODE_CALL:
+    case AndroidKeyEvent::KEYCODE_ENDCALL:
+        break;
+    case AndroidKeyEvent::KEYCODE_0:
+    case AndroidKeyEvent::KEYCODE_1:
+    case AndroidKeyEvent::KEYCODE_2:
+    case AndroidKeyEvent::KEYCODE_3:
+    case AndroidKeyEvent::KEYCODE_4:
+    case AndroidKeyEvent::KEYCODE_5:
+    case AndroidKeyEvent::KEYCODE_6:
+    case AndroidKeyEvent::KEYCODE_7:
+    case AndroidKeyEvent::KEYCODE_8:
+    case AndroidKeyEvent::KEYCODE_9:
+        event.keyCode = key.KeyCode() - AndroidKeyEvent::KEYCODE_0 + NS_VK_0;
+        break;
+    case AndroidKeyEvent::KEYCODE_STAR:
+        event.keyCode = NS_VK_MULTIPLY;
+        break;
+    case AndroidKeyEvent::KEYCODE_POUND:
+        break;
+    case AndroidKeyEvent::KEYCODE_DPAD_UP:
+        event.keyCode = NS_VK_UP;
+        break;
+    case AndroidKeyEvent::KEYCODE_DPAD_DOWN:
+        event.keyCode = NS_VK_DOWN;
+        break;
+    case AndroidKeyEvent::KEYCODE_SOFT_LEFT:
+    case AndroidKeyEvent::KEYCODE_DPAD_LEFT:
+        event.keyCode = NS_VK_LEFT;
+        break;
+    case AndroidKeyEvent::KEYCODE_SOFT_RIGHT:
+    case AndroidKeyEvent::KEYCODE_DPAD_RIGHT:
+        event.keyCode = NS_VK_RIGHT;
+        break;
+    case AndroidKeyEvent::KEYCODE_VOLUME_UP:
+    case AndroidKeyEvent::KEYCODE_VOLUME_DOWN:
+    case AndroidKeyEvent::KEYCODE_POWER:
+    case AndroidKeyEvent::KEYCODE_CAMERA:
+    case AndroidKeyEvent::KEYCODE_CLEAR:
+        break;
+    case AndroidKeyEvent::KEYCODE_A:
+    case AndroidKeyEvent::KEYCODE_B:
+    case AndroidKeyEvent::KEYCODE_C:
+    case AndroidKeyEvent::KEYCODE_D:
+    case AndroidKeyEvent::KEYCODE_E:
+    case AndroidKeyEvent::KEYCODE_F:
+    case AndroidKeyEvent::KEYCODE_G:
+    case AndroidKeyEvent::KEYCODE_H:
+    case AndroidKeyEvent::KEYCODE_I:
+    case AndroidKeyEvent::KEYCODE_J:
+    case AndroidKeyEvent::KEYCODE_K:
+    case AndroidKeyEvent::KEYCODE_L:
+    case AndroidKeyEvent::KEYCODE_M:
+    case AndroidKeyEvent::KEYCODE_N:
+    case AndroidKeyEvent::KEYCODE_O:
+    case AndroidKeyEvent::KEYCODE_P:
+    case AndroidKeyEvent::KEYCODE_Q:
+    case AndroidKeyEvent::KEYCODE_R:
+    case AndroidKeyEvent::KEYCODE_S:
+    case AndroidKeyEvent::KEYCODE_T:
+    case AndroidKeyEvent::KEYCODE_U:
+    case AndroidKeyEvent::KEYCODE_V:
+    case AndroidKeyEvent::KEYCODE_W:
+    case AndroidKeyEvent::KEYCODE_X:
+    case AndroidKeyEvent::KEYCODE_Y:
+    case AndroidKeyEvent::KEYCODE_Z:
+        event.keyCode = key.KeyCode() - AndroidKeyEvent::KEYCODE_A + NS_VK_A;
+        break;
+    case AndroidKeyEvent::KEYCODE_COMMA:
+        event.keyCode = NS_VK_COMMA;
+        break;
+    case AndroidKeyEvent::KEYCODE_PERIOD:
+        event.keyCode = NS_VK_PERIOD;
+        break;
+    case AndroidKeyEvent::KEYCODE_ALT_LEFT:
+    case AndroidKeyEvent::KEYCODE_ALT_RIGHT:
+    case AndroidKeyEvent::KEYCODE_SHIFT_LEFT:
+    case AndroidKeyEvent::KEYCODE_SHIFT_RIGHT:
+        break;
+    case AndroidKeyEvent::KEYCODE_TAB:
+        event.keyCode = NS_VK_TAB;
+        break;
+    case AndroidKeyEvent::KEYCODE_SPACE:
+        event.keyCode = NS_VK_SPACE;
+        break;
+    case AndroidKeyEvent::KEYCODE_SYM:
+    case AndroidKeyEvent::KEYCODE_EXPLORER:
+    case AndroidKeyEvent::KEYCODE_ENVELOPE:
+        break;
+    case AndroidKeyEvent::KEYCODE_DPAD_CENTER:
+    case AndroidKeyEvent::KEYCODE_ENTER:
+        event.keyCode = NS_VK_RETURN;
+        break;
+    case AndroidKeyEvent::KEYCODE_DEL:
+        event.keyCode = NS_VK_BACK;
+        break;
+    case AndroidKeyEvent::KEYCODE_GRAVE:
+        break;
+    case AndroidKeyEvent::KEYCODE_MINUS:
+        event.keyCode = NS_VK_SUBTRACT;
+        break;
+    case AndroidKeyEvent::KEYCODE_EQUALS:
+        event.keyCode = NS_VK_EQUALS;
+        break;
+    case AndroidKeyEvent::KEYCODE_LEFT_BRACKET:
+        event.keyCode = NS_VK_OPEN_BRACKET;
+        break;
+    case AndroidKeyEvent::KEYCODE_RIGHT_BRACKET:
+        event.keyCode = NS_VK_CLOSE_BRACKET;
+        break;
+    case AndroidKeyEvent::KEYCODE_BACKSLASH:
+        event.keyCode = NS_VK_BACK_SLASH;
+        break;
+    case AndroidKeyEvent::KEYCODE_SEMICOLON:
+        event.keyCode = NS_VK_SEMICOLON;
+        break;
+    case AndroidKeyEvent::KEYCODE_APOSTROPHE:
+        event.keyCode = NS_VK_QUOTE;
+        break;
+    case AndroidKeyEvent::KEYCODE_SLASH:
+        event.keyCode = NS_VK_SLASH;
+        break;
+    case AndroidKeyEvent::KEYCODE_AT:
+    case AndroidKeyEvent::KEYCODE_NUM:
+    case AndroidKeyEvent::KEYCODE_HEADSETHOOK:
+    case AndroidKeyEvent::KEYCODE_FOCUS:
+        break;
+    case AndroidKeyEvent::KEYCODE_PLUS:
+        event.keyCode = NS_VK_ADD;
+        break;
+    case AndroidKeyEvent::KEYCODE_MENU:
+    case AndroidKeyEvent::KEYCODE_NOTIFICATION:
+    case AndroidKeyEvent::KEYCODE_SEARCH:
+    case AndroidKeyEvent::KEYCODE_MEDIA_PLAY_PAUSE:
+    case AndroidKeyEvent::KEYCODE_MEDIA_STOP:
+    case AndroidKeyEvent::KEYCODE_MEDIA_NEXT:
+    case AndroidKeyEvent::KEYCODE_MEDIA_PREVIOUS:
+    case AndroidKeyEvent::KEYCODE_MEDIA_REWIND:
+    case AndroidKeyEvent::KEYCODE_MEDIA_FAST_FORWARD:
+    case AndroidKeyEvent::KEYCODE_MUTE:
+        break;
+    default:
+        ALOG("Unknown key code!");
+        break;
+    }
+
+    event.charCode = key.UnicodeChar();
+    event.isShift = gLeftShift || gRightShift;
+    event.isControl = PR_FALSE;
+    event.isAlt = PR_FALSE;
+    event.isMeta = PR_FALSE;
+    event.time = key.Time();
+}
+
+void
+nsWindow::HandleSpecialKey(AndroidGeckoEvent *ae)
+{
+    nsCOMPtr<nsIAtom> command;
+    PRBool isDown = ae->Action() == AndroidKeyEvent::ACTION_DOWN;
+    PRBool doCommand = PR_FALSE;
+    PRUint32 keyCode = ae->KeyCode();
+
+    if (isDown) {
+        switch (keyCode) {
+            case AndroidKeyEvent::KEYCODE_BACK:
+            case AndroidKeyEvent::KEYCODE_MENU:
+            case AndroidKeyEvent::KEYCODE_SEARCH:
+                mSpecialKeyTracking = keyCode;
+                break;
+
+            case AndroidKeyEvent::KEYCODE_VOLUME_UP:
+                command = nsWidgetAtoms::VolumeUp;
+                doCommand = PR_TRUE;
+                break;
+            case AndroidKeyEvent::KEYCODE_VOLUME_DOWN:
+                command = nsWidgetAtoms::VolumeDown;
+                doCommand = PR_TRUE;
+                break;
+        }
+    } else {
+        // Dispatch BACK, MENU, SEARCH key events only on key-up after the corresponding key-down
+        if (mSpecialKeyTracking != keyCode) {
+            mSpecialKeyTracking = 0;
+            return;
+        }
+        switch (keyCode) {
+            case AndroidKeyEvent::KEYCODE_BACK: {
+                nsKeyEvent pressEvent(PR_TRUE, NS_KEY_PRESS, this);
+                InitKeyEvent(pressEvent, *ae);
+                DispatchEvent(&pressEvent);
+                return;
+            }
+            case AndroidKeyEvent::KEYCODE_MENU:
+                command = nsWidgetAtoms::Menu;
+                doCommand = PR_TRUE;
+                break;
+            case AndroidKeyEvent::KEYCODE_SEARCH:
+                command = nsWidgetAtoms::Search;
+                doCommand = PR_TRUE;
+                break;
+            default:
+                ALOG("Unknown special key code!");
+                return;
+        }
+    }
+    if (doCommand) {
+        nsCommandEvent event(PR_TRUE, nsWidgetAtoms::onAppCommand, command, this);
+        InitEvent(event);
+        DispatchEvent(&event);
+        mSpecialKeyTracking = 0;
+    }
+}
+
+void
+nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
+{
+    PRUint32 msg;
+    switch (ae->Action()) {
+    case AndroidKeyEvent::ACTION_DOWN:
+        msg = NS_KEY_DOWN;
+        break;
+    case AndroidKeyEvent::ACTION_UP:
+        msg = NS_KEY_UP;
+        break;
+    case AndroidKeyEvent::ACTION_MULTIPLE:
+        {
+            nsTextEvent event(PR_TRUE, NS_TEXT_TEXT, this);
+            event.theText.Assign(ae->Characters());
+            DispatchEvent(&event);
+        }
+        return;
+    default:
+        ALOG("Unknown key action event!");
+        return;
+    }
+
+    PRBool isDown = ae->Action() == AndroidKeyEvent::ACTION_DOWN;
+    switch (ae->KeyCode()) {
+    case AndroidKeyEvent::KEYCODE_SHIFT_LEFT:
+        gLeftShift = isDown;
+        break;
+    case AndroidKeyEvent::KEYCODE_SHIFT_RIGHT:
+        gRightShift = isDown;
+        break;
+    case AndroidKeyEvent::KEYCODE_ALT_LEFT:
+        gLeftAlt = isDown;
+        break;
+    case AndroidKeyEvent::KEYCODE_ALT_RIGHT:
+        gRightAlt = isDown;
+        break;
+    case AndroidKeyEvent::KEYCODE_SYM:
+        gSym = isDown;
+        break;
+    case AndroidKeyEvent::KEYCODE_BACK:
+    case AndroidKeyEvent::KEYCODE_MENU:
+    case AndroidKeyEvent::KEYCODE_SEARCH:
+    case AndroidKeyEvent::KEYCODE_VOLUME_UP:
+    case AndroidKeyEvent::KEYCODE_VOLUME_DOWN:
+        HandleSpecialKey(ae);
+        return;
+    }
+
+    mSpecialKeyTracking = 0;
+
+    nsKeyEvent event(PR_TRUE, msg, this);
+    InitKeyEvent(event, *ae);
+    if (event.charCode)
+        event.keyCode = 0;
+    DispatchEvent(&event);
+
+    if (isDown) {
+        nsKeyEvent pressEvent(PR_TRUE, NS_KEY_PRESS, this);
+        InitKeyEvent(pressEvent, *ae);
+#ifdef ANDROID_DEBUG_WIDGET
+        ALOG("Dispatching key event with keyCode %d charCode %d shift %d alt %d sym/ctrl %d metamask %d", event.keyCode, event.charCode, event.isShift, event.isAlt, event.isControl, ae->MetaState());
+#endif
+        DispatchEvent(&pressEvent);
+    }
+}
+
+nsresult
+nsWindow::GetCurrentOffset(PRUint32 &aOffset, PRUint32 &aLength)
+{
+    nsQueryContentEvent event(PR_TRUE, NS_QUERY_SELECTED_TEXT, this);
+    DispatchEvent(&event);
+
+    if (!event.mSucceeded)
+        return NS_ERROR_FAILURE;
+
+    aOffset = event.mReply.mOffset;
+    aLength = event.mReply.mString.Length();
+    return NS_OK;
+}
+
+nsresult
+nsWindow::DeleteRange(int aOffset, int aLen)
+{
+    nsSelectionEvent selectEvent(PR_TRUE, NS_SELECTION_SET, this);
+    selectEvent.mOffset = aOffset;
+    selectEvent.mLength = aLen;
+    DispatchEvent(&selectEvent);
+    NS_ENSURE_TRUE(selectEvent.mSucceeded, NS_ERROR_FAILURE);
+
+    nsContentCommandEvent event(PR_TRUE, NS_CONTENT_COMMAND_DELETE, this);
+    DispatchEvent(&event);
+    NS_ENSURE_TRUE(event.mSucceeded, NS_ERROR_FAILURE);
+
+    return NS_OK;
+}
+
+void
+nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
+{
+    switch (ae->Action()) {
+    case AndroidGeckoEvent::IME_BATCH_END:
+        {
+            nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_END, this);
+            event.time = PR_Now() / 1000;
+            DispatchEvent(&event);
+        }
+        return;
+    case AndroidGeckoEvent::IME_BATCH_BEGIN:
+        {
+            nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_START, this);
+            event.time = PR_Now() / 1000;
+            DispatchEvent(&event);
+        }
+        return;
+    case AndroidGeckoEvent::IME_SET_TEXT:
+        {
+            nsTextEvent event(PR_TRUE, NS_TEXT_TEXT, this);
+            event.theText.Assign(ae->Characters());
+            event.time = PR_Now() / 1000;
+            DispatchEvent(&event);
+        }
+        return;
+    case AndroidGeckoEvent::IME_GET_TEXT:
+        {
+            PRUint32 offset, len;
+            if (NS_FAILED(GetCurrentOffset(offset, len))) {
+                AndroidBridge::Bridge()->ReturnIMEQueryResult(nsnull, 0, 0, 0);
+                return;
+            }
+
+            PRUint32 readLen = ae->Count();
+            PRUint32 readOffset = offset;
+            if (!ae->Count() && !ae->Count2()) {
+                readOffset = 0;
+                readLen = PR_UINT32_MAX;
+            } else if (!readLen) { // backwards
+                readLen = ae->Count2();
+                if (readLen > offset) {
+                    readLen = offset;
+                    readOffset = 0;
+                } else
+                    readOffset -= readLen;
+            } else
+                readOffset += len;
+
+            nsQueryContentEvent event(PR_TRUE, NS_QUERY_TEXT_CONTENT, this);
+            event.InitForQueryTextContent(0, PR_UINT32_MAX);
+            DispatchEvent(&event);
+
+            if (!event.mSucceeded) {
+                AndroidBridge::Bridge()->ReturnIMEQueryResult(nsnull, 0, 0, 0);
+                return;
+            }
+
+            nsAutoString textContent(Substring(event.mReply.mString, readOffset, readLen));
+            AndroidBridge::Bridge()->ReturnIMEQueryResult(textContent.get(), textContent.Length(), offset, offset + len);
+        }
+        return;
+    case AndroidGeckoEvent::IME_DELETE_TEXT:
+        {
+            PRUint32 offset, len;
+            PRUint32 count = ae->Count();
+            if (NS_FAILED(GetCurrentOffset(offset, len)))
+                return;
+
+            DeleteRange(offset + len, ae->Count2());
+            offset -= ae->Count();
+            if (offset < 0) {
+                count += offset;
+                offset = 0;
+            }
+            DeleteRange(offset, count);
+        }
+        return;
+    }
+}
+
+nsWindow *
+nsWindow::FindWindowForPoint(const nsIntPoint& pt)
+{
+    if (!mBounds.Contains(pt))
+        return nsnull;
+
+    // children mBounds are relative to their parent
+    nsIntPoint childPoint(pt.x - mBounds.x, pt.y - mBounds.y);
+
+    for (PRUint32 i = 0; i < mChildren.Length(); ++i) {
+        if (mChildren[i]->mBounds.Contains(childPoint))
+            return mChildren[i]->FindWindowForPoint(childPoint);
+    }
+
+    return this;
+}
+
+void
+nsWindow::UserActivity()
+{
+  if (!mIdleService) {
+    mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
+  }
+
+  if (mIdleService) {
+    mIdleService->ResetIdleTimeOut();
+  }
+}
+
+NS_IMETHODIMP
+nsWindow::SetIMEEnabled(PRUint32 aState)
+{
+    gIMEState = aState;
+    if (AndroidBridge::Bridge())
+        AndroidBridge::Bridge()->ShowIME(aState);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindow::GetIMEEnabled(PRUint32* aState)
+{
+    *aState = gIMEState;
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/android/nsWindow.h
@@ -0,0 +1,189 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef NSWINDOW_H_
+#define NSWINDOW_H_
+
+#include "nsBaseWidget.h"
+#include "gfxPoint.h"
+
+#include "nsTArray.h"
+
+class gfxASurface;
+class nsIdleService;
+
+namespace mozilla {
+    class AndroidGeckoEvent;
+    class AndroidKeyEvent;
+}
+
+class nsWindow :
+    public nsBaseWidget
+{
+public:
+    nsWindow();
+    virtual ~nsWindow();
+
+    NS_DECL_ISUPPORTS_INHERITED
+
+    static void OnGlobalAndroidEvent(mozilla::AndroidGeckoEvent *ae);
+    static void SetInitialAndroidBounds(const gfxIntSize& sz);
+    static gfxIntSize GetAndroidBounds();
+
+    nsWindow* FindWindowForPoint(const nsIntPoint& pt);
+
+    void OnAndroidEvent(mozilla::AndroidGeckoEvent *ae);
+    void OnDraw(mozilla::AndroidGeckoEvent *ae);
+    void OnMotionEvent(mozilla::AndroidGeckoEvent *ae);
+    void OnKeyEvent(mozilla::AndroidGeckoEvent *ae);
+    void OnIMEEvent(mozilla::AndroidGeckoEvent *ae);
+
+    void OnSizeChanged(const gfxIntSize& aSize);
+
+    void InitEvent(nsGUIEvent& event, nsIntPoint* aPoint = 0);
+
+    //
+    // nsIWidget
+    //
+
+    NS_IMETHOD Create(nsIWidget *aParent,
+                      nsNativeWidget aNativeParent,
+                      const nsIntRect &aRect,
+                      EVENT_CALLBACK aHandleEventFunction,
+                      nsIDeviceContext *aContext,
+                      nsIAppShell *aAppShell,
+                      nsIToolkit *aToolkit,
+                      nsWidgetInitData *aInitData);
+    NS_IMETHOD Destroy(void);
+    NS_IMETHOD ConfigureChildren(const nsTArray<nsIWidget::Configuration>&);
+    NS_IMETHOD SetParent(nsIWidget* aNewParent);
+    virtual nsIWidget *GetParent(void);
+    NS_IMETHOD Show(PRBool aState);
+    NS_IMETHOD SetModal(PRBool aModal);
+    NS_IMETHOD IsVisible(PRBool & aState);
+    NS_IMETHOD ConstrainPosition(PRBool aAllowSlop,
+                                 PRInt32 *aX,
+                                 PRInt32 *aY);
+    NS_IMETHOD Move(PRInt32 aX,
+                    PRInt32 aY);
+    NS_IMETHOD Resize(PRInt32 aWidth,
+                      PRInt32 aHeight,
+                      PRBool  aRepaint);
+    NS_IMETHOD Resize(PRInt32 aX,
+                      PRInt32 aY,
+                      PRInt32 aWidth,
+                      PRInt32 aHeight,
+                      PRBool aRepaint);
+    NS_IMETHOD SetZIndex(PRInt32 aZIndex);
+    NS_IMETHOD PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
+                           nsIWidget *aWidget,
+                           PRBool aActivate);
+    NS_IMETHOD SetSizeMode(PRInt32 aMode);
+    NS_IMETHOD Enable(PRBool aState);
+    NS_IMETHOD IsEnabled(PRBool *aState);
+    NS_IMETHOD Invalidate(const nsIntRect &aRect,
+                          PRBool aIsSynchronous);
+    NS_IMETHOD Update();
+    void Scroll(const nsIntPoint&,
+                const nsTArray<nsIntRect>&,
+                const nsTArray<nsIWidget::Configuration>&);
+    NS_IMETHOD SetFocus(PRBool aRaise = PR_FALSE);
+    NS_IMETHOD GetScreenBounds(nsIntRect &aRect);
+    virtual nsIntPoint WidgetToScreenOffset();
+    NS_IMETHOD DispatchEvent(nsGUIEvent *aEvent, nsEventStatus &aStatus);
+    nsEventStatus DispatchEvent(nsGUIEvent *aEvent);
+    NS_IMETHOD SetWindowClass(const nsAString& xulWinType);
+
+
+
+    NS_IMETHOD SetForegroundColor(const nscolor &aColor) { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD SetBackgroundColor(const nscolor &aColor) { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD SetCursor(nsCursor aCursor) { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD SetCursor(imgIContainer* aCursor,
+                         PRUint32 aHotspotX,
+                         PRUint32 aHotspotY) { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD SetHasTransparentBackground(PRBool aTransparent) { return NS_OK; }
+    NS_IMETHOD GetHasTransparentBackground(PRBool& aTransparent) { aTransparent = PR_FALSE; return NS_OK; }
+    NS_IMETHOD HideWindowChrome(PRBool aShouldHide) { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD MakeFullScreen(PRBool aFullScreen) { return NS_ERROR_NOT_IMPLEMENTED; }
+    virtual void* GetNativeData(PRUint32 aDataType);
+    NS_IMETHOD SetTitle(const nsAString& aTitle) { return NS_OK; }
+    NS_IMETHOD SetIcon(const nsAString& aIconSpec) { return NS_OK; }
+    NS_IMETHOD EnableDragDrop(PRBool aEnable) { return NS_OK; }
+    NS_IMETHOD CaptureMouse(PRBool aCapture) { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD CaptureRollupEvents(nsIRollupListener *aListener,
+                                   nsIMenuRollup *aMenuRollup,
+                                   PRBool aDoCapture,
+                                   PRBool aConsumeRollupEvent) { return NS_ERROR_NOT_IMPLEMENTED; }
+
+    NS_IMETHOD GetAttention(PRInt32 aCycleCount) { return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical) { return NS_ERROR_NOT_IMPLEMENTED; }
+
+    NS_IMETHOD SetIMEEnabled(PRUint32 aState);
+    NS_IMETHOD GetIMEEnabled(PRUint32* aState);
+
+    gfxASurface* GetThebesSurface();
+
+protected:
+    void BringToFront();
+    nsWindow *FindTopLevel();
+    PRBool DrawTo(gfxASurface *targetSurface);
+    PRBool IsTopLevel();
+    nsresult GetCurrentOffset(PRUint32 &aOffset, PRUint32 &aLength);
+    nsresult DeleteRange(int aOffset, int aLen);
+
+    // Call this function when the users activity is the direct cause of an
+    // event (like a keypress or mouse click).
+    void UserActivity();
+
+    PRPackedBool mIsVisible;
+    nsTArray<nsWindow*> mChildren;
+    nsWindow* mParent;
+    nsCOMPtr<nsIdleService> mIdleService;
+
+    static void DumpWindows();
+    static void DumpWindows(const nsTArray<nsWindow*>& wins, int indent = 0);
+    static void LogWindow(nsWindow *win, int index, int indent);
+
+private:
+    void InitKeyEvent(nsKeyEvent& event, mozilla::AndroidGeckoEvent& key);
+    void HandleSpecialKey(mozilla::AndroidGeckoEvent *ae);
+
+    PRUint32 mSpecialKeyTracking;
+};
+
+#endif /* NSWINDOW_H_ */
--- a/widget/src/xpwidgets/nsWidgetAtomList.h
+++ b/widget/src/xpwidgets/nsWidgetAtomList.h
@@ -82,16 +82,17 @@ WIDGET_ATOM(vertical, "vertical")
 WIDGET_ATOM(id, "id")
 WIDGET_ATOM(image, "image")
 WIDGET_ATOM(input, "input")
 WIDGET_ATOM(indeterminate, "indeterminate")
 WIDGET_ATOM(key, "key") // The key element / attribute
 WIDGET_ATOM(label, "label")
 WIDGET_ATOM(max, "max")
 WIDGET_ATOM(maxpos, "maxpos")
+WIDGET_ATOM(Menu, "Menu") // AppCommand to open a menu
 WIDGET_ATOM(menu, "menu") // Represents an XP menu
 WIDGET_ATOM(menuitem, "menuitem") // Represents an XP menu item
 WIDGET_ATOM(menupopup, "menupopup") // The XP menu's children.
 WIDGET_ATOM(menuseparator, "menuseparator")  // Divider between menu items
 WIDGET_ATOM(menuFrame, "MenuFrame")
 WIDGET_ATOM(minpos, "minpos")
 WIDGET_ATOM(mode, "mode")
 WIDGET_ATOM(modifiers, "modifiers") // The modifiers attribute
@@ -119,9 +120,11 @@ WIDGET_ATOM(state, "state")
 WIDGET_ATOM(step, "step")
 WIDGET_ATOM(Stop, "Stop")
 WIDGET_ATOM(_true, "true")
 WIDGET_ATOM(tab, "tab")
 WIDGET_ATOM(tree, "tree")
 WIDGET_ATOM(treecolpicker, "treecolpicker")
 WIDGET_ATOM(type, "type")
 WIDGET_ATOM(value, "value")
+WIDGET_ATOM(VolumeUp, "VolumeUp")
+WIDGET_ATOM(VolumeDown, "VolumeDown")