michael@0: /* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/layers/CompositorChild.h" michael@0: #include "mozilla/layers/CompositorParent.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "mozilla/Hal.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include michael@0: #include "nsXPCOMStrings.h" michael@0: #include "AndroidBridge.h" michael@0: #include "AndroidJNIWrapper.h" michael@0: #include "AndroidBridgeUtilities.h" michael@0: #include "nsAppShell.h" michael@0: #include "nsOSHelperAppService.h" michael@0: #include "nsWindow.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIThreadManager.h" michael@0: #include "mozilla/dom/mobilemessage/PSms.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfxContext.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "gfxUtils.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "mozilla/dom/ScreenOrientation.h" michael@0: #include "nsIDOMWindowUtils.h" michael@0: #include "nsIDOMClientRect.h" michael@0: #include "StrongPointer.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "NativeJSContainer.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIScriptError.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::widget::android; michael@0: using namespace mozilla::gfx; michael@0: michael@0: StaticRefPtr AndroidBridge::sBridge; michael@0: static unsigned sJavaEnvThreadIndex = 0; michael@0: static jobject sGlobalContext = nullptr; michael@0: static void JavaThreadDetachFunc(void *arg); michael@0: michael@0: // This is a dummy class that can be used in the template for android::sp michael@0: class AndroidRefable { michael@0: void incStrong(void* thing) { } michael@0: void decStrong(void* thing) { } michael@0: }; michael@0: michael@0: // This isn't in AndroidBridge.h because including StrongPointer.h there is gross michael@0: static android::sp (*android_SurfaceTexture_getNativeWindow)(JNIEnv* env, jobject surfaceTexture) = nullptr; michael@0: michael@0: jclass AndroidBridge::GetClassGlobalRef(JNIEnv* env, const char* className) michael@0: { michael@0: jobject classLocalRef = env->FindClass(className); michael@0: if (!classLocalRef) { michael@0: ALOG(">>> FATAL JNI ERROR! FindClass(className=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", michael@0: className); michael@0: env->ExceptionDescribe(); michael@0: MOZ_CRASH(); michael@0: } michael@0: jobject classGlobalRef = env->NewGlobalRef(classLocalRef); michael@0: if (!classGlobalRef) { michael@0: env->ExceptionDescribe(); michael@0: MOZ_CRASH(); michael@0: } michael@0: // Local ref no longer necessary because we have a global ref. michael@0: env->DeleteLocalRef(classLocalRef); michael@0: classLocalRef = nullptr; michael@0: return static_cast(classGlobalRef); michael@0: } michael@0: michael@0: jmethodID AndroidBridge::GetMethodID(JNIEnv* env, jclass jClass, michael@0: const char* methodName, const char* methodType) michael@0: { michael@0: jmethodID methodID = env->GetMethodID(jClass, methodName, methodType); michael@0: if (!methodID) { michael@0: ALOG(">>> FATAL JNI ERROR! GetMethodID(methodName=\"%s\", " michael@0: "methodType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", michael@0: methodName, methodType); michael@0: env->ExceptionDescribe(); michael@0: MOZ_CRASH(); michael@0: } michael@0: return methodID; michael@0: } michael@0: michael@0: jmethodID AndroidBridge::GetStaticMethodID(JNIEnv* env, jclass jClass, michael@0: const char* methodName, const char* methodType) michael@0: { michael@0: jmethodID methodID = env->GetStaticMethodID(jClass, methodName, methodType); michael@0: if (!methodID) { michael@0: ALOG(">>> FATAL JNI ERROR! GetStaticMethodID(methodName=\"%s\", " michael@0: "methodType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", michael@0: methodName, methodType); michael@0: env->ExceptionDescribe(); michael@0: MOZ_CRASH(); michael@0: } michael@0: return methodID; michael@0: } michael@0: michael@0: jfieldID AndroidBridge::GetFieldID(JNIEnv* env, jclass jClass, michael@0: const char* fieldName, const char* fieldType) michael@0: { michael@0: jfieldID fieldID = env->GetFieldID(jClass, fieldName, fieldType); michael@0: if (!fieldID) { michael@0: ALOG(">>> FATAL JNI ERROR! GetFieldID(fieldName=\"%s\", " michael@0: "fieldType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", michael@0: fieldName, fieldType); michael@0: env->ExceptionDescribe(); michael@0: MOZ_CRASH(); michael@0: } michael@0: return fieldID; michael@0: } michael@0: michael@0: jfieldID AndroidBridge::GetStaticFieldID(JNIEnv* env, jclass jClass, michael@0: const char* fieldName, const char* fieldType) michael@0: { michael@0: jfieldID fieldID = env->GetStaticFieldID(jClass, fieldName, fieldType); michael@0: if (!fieldID) { michael@0: ALOG(">>> FATAL JNI ERROR! GetStaticFieldID(fieldName=\"%s\", " michael@0: "fieldType=\"%s\") failed. Did ProGuard optimize away something it shouldn't have?", michael@0: fieldName, fieldType); michael@0: env->ExceptionDescribe(); michael@0: MOZ_CRASH(); michael@0: } michael@0: return fieldID; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::ConstructBridge(JNIEnv *jEnv) michael@0: { michael@0: /* NSS hack -- bionic doesn't handle recursive unloads correctly, michael@0: * because library finalizer functions are called with the dynamic michael@0: * linker lock still held. This results in a deadlock when trying michael@0: * to call dlclose() while we're already inside dlclose(). michael@0: * Conveniently, NSS has an env var that can prevent it from unloading. michael@0: */ michael@0: putenv("NSS_DISABLE_UNLOAD=1"); michael@0: michael@0: PR_NewThreadPrivateIndex(&sJavaEnvThreadIndex, JavaThreadDetachFunc); michael@0: michael@0: AndroidBridge *bridge = new AndroidBridge(); michael@0: if (!bridge->Init(jEnv)) { michael@0: delete bridge; michael@0: } michael@0: sBridge = bridge; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::Init(JNIEnv *jEnv) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::Init"); michael@0: jEnv->GetJavaVM(&mJavaVM); michael@0: if (!mJavaVM) { michael@0: MOZ_CRASH(); // Nothing we can do here michael@0: } michael@0: michael@0: AutoLocalJNIFrame jniFrame(jEnv); michael@0: michael@0: mJNIEnv = nullptr; michael@0: mThread = -1; michael@0: mGLControllerObj = nullptr; michael@0: mOpenedGraphicsLibraries = false; michael@0: mHasNativeBitmapAccess = false; michael@0: mHasNativeWindowAccess = false; michael@0: mHasNativeWindowFallback = false; michael@0: michael@0: initInit(); michael@0: michael@0: #ifdef MOZ_WEBSMS_BACKEND michael@0: mAndroidSmsMessageClass = getClassGlobalRef("android/telephony/SmsMessage"); michael@0: jCalculateLength = getStaticMethod("calculateLength", "(Ljava/lang/CharSequence;Z)[I"); michael@0: #endif michael@0: michael@0: jStringClass = getClassGlobalRef("java/lang/String"); michael@0: michael@0: if (!GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &mAPIVersion, jEnv)) { michael@0: ALOG_BRIDGE("Failed to find API version"); michael@0: } michael@0: michael@0: jSurfaceClass = getClassGlobalRef("android/view/Surface"); michael@0: if (mAPIVersion <= 8 /* Froyo */) { michael@0: jSurfacePointerField = getField("mSurface", "I"); michael@0: } else if (mAPIVersion > 8 && mAPIVersion < 19 /* KitKat */) { michael@0: jSurfacePointerField = getField("mNativeSurface", "I"); michael@0: } else { michael@0: // We don't know how to get this, just set it to 0 michael@0: jSurfacePointerField = 0; michael@0: } michael@0: michael@0: jclass eglClass = getClassGlobalRef("com/google/android/gles_jni/EGLSurfaceImpl"); michael@0: if (eglClass) { michael@0: jEGLSurfacePointerField = getField("mEGLSurface", "I"); michael@0: } else { michael@0: jEGLSurfacePointerField = 0; michael@0: } michael@0: michael@0: InitAndroidJavaWrappers(jEnv); michael@0: michael@0: // jEnv should NOT be cached here by anything -- the jEnv here michael@0: // is not valid for the real gecko main thread, which is set michael@0: // at SetMainThread time. michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::SetMainThread(pthread_t thr) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::SetMainThread"); michael@0: if (thr) { michael@0: mThread = thr; michael@0: mJavaVM->GetEnv((void**) &mJNIEnv, JNI_VERSION_1_2); michael@0: return (bool) mJNIEnv; michael@0: } michael@0: michael@0: mJNIEnv = nullptr; michael@0: mThread = -1; michael@0: return true; michael@0: } michael@0: michael@0: // Raw JNIEnv variants. michael@0: jstring AndroidBridge::NewJavaString(JNIEnv* env, const char16_t* string, uint32_t len) { michael@0: jstring ret = env->NewString(reinterpret_cast(string), len); michael@0: if (env->ExceptionCheck()) { michael@0: ALOG_BRIDGE("Exceptional exit of: %s", __PRETTY_FUNCTION__); michael@0: env->ExceptionDescribe(); michael@0: env->ExceptionClear(); michael@0: return nullptr; michael@0: } michael@0: return ret; michael@0: } michael@0: michael@0: jstring AndroidBridge::NewJavaString(JNIEnv* env, const nsAString& string) { michael@0: return NewJavaString(env, string.BeginReading(), string.Length()); michael@0: } michael@0: michael@0: jstring AndroidBridge::NewJavaString(JNIEnv* env, const char* string) { michael@0: return NewJavaString(env, NS_ConvertUTF8toUTF16(string)); michael@0: } michael@0: michael@0: jstring AndroidBridge::NewJavaString(JNIEnv* env, const nsACString& string) { michael@0: return NewJavaString(env, NS_ConvertUTF8toUTF16(string)); michael@0: } michael@0: michael@0: // AutoLocalJNIFrame variants.. michael@0: jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const char16_t* string, uint32_t len) { michael@0: return NewJavaString(frame->GetEnv(), string, len); michael@0: } michael@0: michael@0: jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const nsAString& string) { michael@0: return NewJavaString(frame, string.BeginReading(), string.Length()); michael@0: } michael@0: michael@0: jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const char* string) { michael@0: return NewJavaString(frame, NS_ConvertUTF8toUTF16(string)); michael@0: } michael@0: michael@0: jstring AndroidBridge::NewJavaString(AutoLocalJNIFrame* frame, const nsACString& string) { michael@0: return NewJavaString(frame, NS_ConvertUTF8toUTF16(string)); michael@0: } michael@0: michael@0: extern "C" { michael@0: __attribute__ ((visibility("default"))) michael@0: JNIEnv * GetJNIForThread() michael@0: { michael@0: JNIEnv *jEnv = static_cast(PR_GetThreadPrivate(sJavaEnvThreadIndex)); michael@0: if (jEnv) { michael@0: return jEnv; michael@0: } michael@0: JavaVM *jVm = mozilla::AndroidBridge::GetVM(); michael@0: if (!jVm->GetEnv(reinterpret_cast(&jEnv), JNI_VERSION_1_2)) { michael@0: MOZ_ASSERT(jEnv); michael@0: return jEnv; michael@0: } michael@0: if (!jVm->AttachCurrentThread(&jEnv, nullptr)) { michael@0: MOZ_ASSERT(jEnv); michael@0: PR_SetThreadPrivate(sJavaEnvThreadIndex, jEnv); michael@0: return jEnv; michael@0: } michael@0: MOZ_CRASH(); michael@0: return nullptr; // unreachable michael@0: } michael@0: } michael@0: michael@0: void AutoGlobalWrappedJavaObject::Dispose() { michael@0: if (isNull()) { michael@0: return; michael@0: } michael@0: michael@0: GetJNIForThread()->DeleteGlobalRef(wrapped_obj); michael@0: wrapped_obj = nullptr; michael@0: } michael@0: michael@0: AutoGlobalWrappedJavaObject::~AutoGlobalWrappedJavaObject() { michael@0: Dispose(); michael@0: } michael@0: michael@0: static void michael@0: getHandlersFromStringArray(JNIEnv *aJNIEnv, jobjectArray jArr, jsize aLen, michael@0: nsIMutableArray *aHandlersArray, michael@0: nsIHandlerApp **aDefaultApp, michael@0: const nsAString& aAction = EmptyString(), michael@0: const nsACString& aMimeType = EmptyCString()) michael@0: { michael@0: nsString empty = EmptyString(); michael@0: for (jsize i = 0; i < aLen; i+=4) { michael@0: michael@0: AutoLocalJNIFrame jniFrame(aJNIEnv, 4); michael@0: nsJNIString name( michael@0: static_cast(aJNIEnv->GetObjectArrayElement(jArr, i)), aJNIEnv); michael@0: nsJNIString isDefault( michael@0: static_cast(aJNIEnv->GetObjectArrayElement(jArr, i + 1)), aJNIEnv); michael@0: nsJNIString packageName( michael@0: static_cast(aJNIEnv->GetObjectArrayElement(jArr, i + 2)), aJNIEnv); michael@0: nsJNIString className( michael@0: static_cast(aJNIEnv->GetObjectArrayElement(jArr, i + 3)), aJNIEnv); michael@0: nsIHandlerApp* app = nsOSHelperAppService:: michael@0: CreateAndroidHandlerApp(name, className, packageName, michael@0: className, aMimeType, aAction); michael@0: michael@0: aHandlersArray->AppendElement(app, false); michael@0: if (aDefaultApp && isDefault.Length() > 0) michael@0: *aDefaultApp = app; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::GetHandlersForMimeType(const nsAString& aMimeType, michael@0: nsIMutableArray *aHandlersArray, michael@0: nsIHandlerApp **aDefaultApp, michael@0: const nsAString& aAction) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetHandlersForMimeType"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jobjectArray arr = michael@0: mozilla::widget::android::GeckoAppShell::GetHandlersForMimeTypeWrapper(aMimeType, aAction); michael@0: if (!arr) michael@0: return false; michael@0: michael@0: jsize len = env->GetArrayLength(arr); michael@0: michael@0: if (!aHandlersArray) michael@0: return len > 0; michael@0: michael@0: getHandlersFromStringArray(env, arr, len, aHandlersArray, michael@0: aDefaultApp, aAction, michael@0: NS_ConvertUTF16toUTF8(aMimeType)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::GetHandlersForURL(const nsAString& aURL, michael@0: nsIMutableArray* aHandlersArray, michael@0: nsIHandlerApp **aDefaultApp, michael@0: const nsAString& aAction) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetHandlersForURL"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jobjectArray arr = mozilla::widget::android::GeckoAppShell::GetHandlersForURLWrapper(aURL, aAction); michael@0: if (!arr) michael@0: return false; michael@0: michael@0: jsize len = env->GetArrayLength(arr); michael@0: michael@0: if (!aHandlersArray) michael@0: return len > 0; michael@0: michael@0: getHandlersFromStringArray(env, arr, len, aHandlersArray, michael@0: aDefaultApp, aAction); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetMimeTypeFromExtensions(const nsACString& aFileExt, nsCString& aMimeType) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetMimeTypeFromExtensions"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jstring jstrType = mozilla::widget::android::GeckoAppShell::GetMimeTypeFromExtensionsWrapper michael@0: (NS_ConvertUTF8toUTF16(aFileExt)); michael@0: if (!jstrType) { michael@0: return; michael@0: } michael@0: nsJNIString jniStr(jstrType, env); michael@0: CopyUTF16toUTF8(jniStr.get(), aMimeType); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetExtensionFromMimeType(const nsACString& aMimeType, nsACString& aFileExt) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetExtensionFromMimeType"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jstring jstrExt = mozilla::widget::android::GeckoAppShell::GetExtensionFromMimeTypeWrapper michael@0: (NS_ConvertUTF8toUTF16(aMimeType)); michael@0: if (!jstrExt) { michael@0: return; michael@0: } michael@0: nsJNIString jniStr(jstrExt, env); michael@0: CopyUTF16toUTF8(jniStr.get(), aFileExt); michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::GetClipboardText(nsAString& aText) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetClipboardText"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jstring result = Clipboard::GetClipboardTextWrapper(); michael@0: if (!result) michael@0: return false; michael@0: michael@0: nsJNIString jniStr(result, env); michael@0: aText.Assign(jniStr); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::ShowAlertNotification(const nsAString& aImageUrl, michael@0: const nsAString& aAlertTitle, michael@0: const nsAString& aAlertText, michael@0: const nsAString& aAlertCookie, michael@0: nsIObserver *aAlertListener, michael@0: const nsAString& aAlertName) michael@0: { michael@0: if (nsAppShell::gAppShell && aAlertListener) { michael@0: // This will remove any observers already registered for this id michael@0: nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeAddObserver(aAlertName, aAlertListener)); michael@0: } michael@0: michael@0: mozilla::widget::android::GeckoAppShell::ShowAlertNotificationWrapper michael@0: (aImageUrl, aAlertTitle, aAlertText, aAlertCookie, aAlertName); michael@0: } michael@0: michael@0: int michael@0: AndroidBridge::GetDPI() michael@0: { michael@0: static int sDPI = 0; michael@0: if (sDPI) michael@0: return sDPI; michael@0: michael@0: const int DEFAULT_DPI = 160; michael@0: michael@0: sDPI = mozilla::widget::android::GeckoAppShell::GetDpiWrapper(); michael@0: if (!sDPI) { michael@0: return DEFAULT_DPI; michael@0: } michael@0: michael@0: return sDPI; michael@0: } michael@0: michael@0: int michael@0: AndroidBridge::GetScreenDepth() michael@0: { michael@0: ALOG_BRIDGE("%s", __PRETTY_FUNCTION__); michael@0: michael@0: static int sDepth = 0; michael@0: if (sDepth) michael@0: return sDepth; michael@0: michael@0: const int DEFAULT_DEPTH = 16; michael@0: michael@0: if (HasEnv()) { michael@0: sDepth = mozilla::widget::android::GeckoAppShell::GetScreenDepthWrapper(); michael@0: } michael@0: if (!sDepth) michael@0: return DEFAULT_DEPTH; michael@0: michael@0: return sDepth; michael@0: } michael@0: void michael@0: AndroidBridge::Vibrate(const nsTArray& aPattern) michael@0: { michael@0: ALOG_BRIDGE("%s", __PRETTY_FUNCTION__); michael@0: michael@0: uint32_t len = aPattern.Length(); michael@0: if (!len) { michael@0: ALOG_BRIDGE(" invalid 0-length array"); michael@0: return; michael@0: } michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: michael@0: // It's clear if this worth special-casing, but it creates less michael@0: // java junk, so dodges the GC. michael@0: if (len == 1) { michael@0: jlong d = aPattern[0]; michael@0: if (d < 0) { michael@0: ALOG_BRIDGE(" invalid vibration duration < 0"); michael@0: return; michael@0: } michael@0: mozilla::widget::android::GeckoAppShell::Vibrate1(d); michael@0: return; michael@0: } michael@0: michael@0: // First element of the array vibrate() expects is how long to wait michael@0: // *before* vibrating. For us, this is always 0. michael@0: michael@0: jlongArray array = env->NewLongArray(len + 1); michael@0: if (!array) { michael@0: ALOG_BRIDGE(" failed to allocate array"); michael@0: return; michael@0: } michael@0: michael@0: jlong* elts = env->GetLongArrayElements(array, nullptr); michael@0: elts[0] = 0; michael@0: for (uint32_t i = 0; i < aPattern.Length(); ++i) { michael@0: jlong d = aPattern[i]; michael@0: if (d < 0) { michael@0: ALOG_BRIDGE(" invalid vibration duration < 0"); michael@0: env->ReleaseLongArrayElements(array, elts, JNI_ABORT); michael@0: return; michael@0: } michael@0: elts[i + 1] = d; michael@0: } michael@0: env->ReleaseLongArrayElements(array, elts, 0); michael@0: michael@0: mozilla::widget::android::GeckoAppShell::VibrateA(array, -1/*don't repeat*/); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetSystemColors(AndroidSystemColors *aColors) michael@0: { michael@0: michael@0: NS_ASSERTION(aColors != nullptr, "AndroidBridge::GetSystemColors: aColors is null!"); michael@0: if (!aColors) michael@0: return; michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: michael@0: jintArray arr = mozilla::widget::android::GeckoAppShell::GetSystemColoursWrapper(); michael@0: if (!arr) michael@0: return; michael@0: michael@0: uint32_t len = static_cast(env->GetArrayLength(arr)); michael@0: jint *elements = env->GetIntArrayElements(arr, 0); michael@0: michael@0: uint32_t colorsCount = sizeof(AndroidSystemColors) / sizeof(nscolor); michael@0: if (len < colorsCount) michael@0: colorsCount = len; michael@0: michael@0: // Convert Android colors to nscolor by switching R and B in the ARGB 32 bit value michael@0: nscolor *colors = (nscolor*)aColors; michael@0: michael@0: for (uint32_t i = 0; i < colorsCount; i++) { michael@0: uint32_t androidColor = static_cast(elements[i]); michael@0: uint8_t r = (androidColor & 0x00ff0000) >> 16; michael@0: uint8_t b = (androidColor & 0x000000ff); michael@0: colors[i] = (androidColor & 0xff00ff00) | (b << 16) | r; michael@0: } michael@0: michael@0: env->ReleaseIntArrayElements(arr, elements, 0); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetIconForExtension(const nsACString& aFileExt, uint32_t aIconSize, uint8_t * const aBuf) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetIconForExtension"); michael@0: NS_ASSERTION(aBuf != nullptr, "AndroidBridge::GetIconForExtension: aBuf is null!"); michael@0: if (!aBuf) michael@0: return; michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: michael@0: jbyteArray arr = mozilla::widget::android::GeckoAppShell::GetIconForExtensionWrapper michael@0: (NS_ConvertUTF8toUTF16(aFileExt), aIconSize); michael@0: michael@0: NS_ASSERTION(arr != nullptr, "AndroidBridge::GetIconForExtension: Returned pixels array is null!"); michael@0: if (!arr) michael@0: return; michael@0: michael@0: uint32_t len = static_cast(env->GetArrayLength(arr)); michael@0: jbyte *elements = env->GetByteArrayElements(arr, 0); michael@0: michael@0: uint32_t bufSize = aIconSize * aIconSize * 4; michael@0: NS_ASSERTION(len == bufSize, "AndroidBridge::GetIconForExtension: Pixels array is incomplete!"); michael@0: if (len == bufSize) michael@0: memcpy(aBuf, elements, bufSize); michael@0: michael@0: env->ReleaseByteArrayElements(arr, elements, 0); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::SetLayerClient(JNIEnv* env, jobject jobj) michael@0: { michael@0: // if resetting is true, that means Android destroyed our GeckoApp activity michael@0: // and we had to recreate it, but all the Gecko-side things were not destroyed. michael@0: // We therefore need to link up the new java objects to Gecko, and that's what michael@0: // we do here. michael@0: bool resetting = (mLayerClient != nullptr); michael@0: michael@0: if (resetting) { michael@0: // clear out the old layer client michael@0: delete mLayerClient; michael@0: mLayerClient = nullptr; michael@0: } michael@0: michael@0: mLayerClient = mozilla::widget::android::GeckoLayerClient::Wrap(jobj); michael@0: michael@0: if (resetting) { michael@0: // since we are re-linking the new java objects to Gecko, we need to get michael@0: // the viewport from the compositor (since the Java copy was thrown away) michael@0: // and we do that by setting the first-paint flag. michael@0: nsWindow::ForceIsFirstPaint(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::RegisterCompositor(JNIEnv *env) michael@0: { michael@0: if (mGLControllerObj != nullptr && !mGLControllerObj->isNull()) { michael@0: // we already have this set up, no need to do it again michael@0: return; michael@0: } michael@0: michael@0: jobject glController = LayerView::RegisterCompositorWrapper(); michael@0: if (!glController) { michael@0: return; michael@0: } michael@0: michael@0: mGLControllerObj = GLController::Wrap(glController); michael@0: } michael@0: michael@0: EGLSurface michael@0: AndroidBridge::CreateEGLSurfaceForCompositor() michael@0: { michael@0: if (!jEGLSurfacePointerField) { michael@0: return nullptr; michael@0: } michael@0: MOZ_ASSERT(mGLControllerObj, "AndroidBridge::CreateEGLSurfaceForCompositor called with a null GL controller ref"); michael@0: michael@0: JNIEnv* env = GetJNIForThread(); // called on the compositor thread michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jobject eglSurface = mGLControllerObj->CreateEGLSurfaceForCompositorWrapper(); michael@0: if (!eglSurface) michael@0: return nullptr; michael@0: michael@0: EGLSurface ret = reinterpret_cast(env->GetIntField(eglSurface, jEGLSurfacePointerField)); michael@0: return ret; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::GetStaticIntField(const char *className, const char *fieldName, int32_t* aInt, JNIEnv* jEnv /* = nullptr */) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetStaticIntField %s", fieldName); michael@0: michael@0: if (!jEnv) { michael@0: if (!HasEnv()) { michael@0: return false; michael@0: } michael@0: jEnv = GetJNIEnv(); michael@0: } michael@0: michael@0: initInit(); michael@0: getClassGlobalRef(className); michael@0: jfieldID field = getStaticField(fieldName, "I"); michael@0: michael@0: if (!field) { michael@0: jEnv->DeleteGlobalRef(jClass); michael@0: return false; michael@0: } michael@0: michael@0: *aInt = static_cast(jEnv->GetStaticIntField(jClass, field)); michael@0: michael@0: jEnv->DeleteGlobalRef(jClass); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::GetStaticStringField(const char *className, const char *fieldName, nsAString &result, JNIEnv* jEnv /* = nullptr */) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetStaticStringField %s", fieldName); michael@0: michael@0: if (!jEnv) { michael@0: if (!HasEnv()) { michael@0: return false; michael@0: } michael@0: jEnv = GetJNIEnv(); michael@0: } michael@0: michael@0: AutoLocalJNIFrame jniFrame(jEnv, 1); michael@0: initInit(); michael@0: getClassGlobalRef(className); michael@0: jfieldID field = getStaticField(fieldName, "Ljava/lang/String;"); michael@0: michael@0: if (!field) { michael@0: jEnv->DeleteGlobalRef(jClass); michael@0: return false; michael@0: } michael@0: michael@0: jstring jstr = (jstring) jEnv->GetStaticObjectField(jClass, field); michael@0: jEnv->DeleteGlobalRef(jClass); michael@0: if (!jstr) michael@0: return false; michael@0: michael@0: result.Assign(nsJNIString(jstr, jEnv)); michael@0: return true; michael@0: } michael@0: michael@0: // Available for places elsewhere in the code to link to. michael@0: bool michael@0: mozilla_AndroidBridge_SetMainThread(pthread_t thr) michael@0: { michael@0: return AndroidBridge::Bridge()->SetMainThread(thr); michael@0: } michael@0: michael@0: void* michael@0: AndroidBridge::GetNativeSurface(JNIEnv* env, jobject surface) { michael@0: if (!env || !mHasNativeWindowFallback || !jSurfacePointerField) michael@0: return nullptr; michael@0: michael@0: return (void*)env->GetIntField(surface, jSurfacePointerField); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::OpenGraphicsLibraries() michael@0: { michael@0: if (!mOpenedGraphicsLibraries) { michael@0: // Try to dlopen libjnigraphics.so for direct bitmap access on michael@0: // Android 2.2+ (API level 8) michael@0: mOpenedGraphicsLibraries = true; michael@0: mHasNativeWindowAccess = false; michael@0: mHasNativeWindowFallback = false; michael@0: mHasNativeBitmapAccess = false; michael@0: michael@0: void *handle = dlopen("libjnigraphics.so", RTLD_LAZY | RTLD_LOCAL); michael@0: if (handle) { michael@0: AndroidBitmap_getInfo = (int (*)(JNIEnv *, jobject, void *))dlsym(handle, "AndroidBitmap_getInfo"); michael@0: AndroidBitmap_lockPixels = (int (*)(JNIEnv *, jobject, void **))dlsym(handle, "AndroidBitmap_lockPixels"); michael@0: AndroidBitmap_unlockPixels = (int (*)(JNIEnv *, jobject))dlsym(handle, "AndroidBitmap_unlockPixels"); michael@0: michael@0: mHasNativeBitmapAccess = AndroidBitmap_getInfo && AndroidBitmap_lockPixels && AndroidBitmap_unlockPixels; michael@0: michael@0: ALOG_BRIDGE("Successfully opened libjnigraphics.so, have native bitmap access? %d", mHasNativeBitmapAccess); michael@0: } michael@0: michael@0: // Try to dlopen libandroid.so for and native window access on michael@0: // Android 2.3+ (API level 9) michael@0: handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL); michael@0: if (handle) { michael@0: ANativeWindow_fromSurface = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurface"); michael@0: ANativeWindow_release = (void (*)(void*))dlsym(handle, "ANativeWindow_release"); michael@0: ANativeWindow_setBuffersGeometry = (int (*)(void*, int, int, int)) dlsym(handle, "ANativeWindow_setBuffersGeometry"); michael@0: ANativeWindow_lock = (int (*)(void*, void*, void*)) dlsym(handle, "ANativeWindow_lock"); michael@0: ANativeWindow_unlockAndPost = (int (*)(void*))dlsym(handle, "ANativeWindow_unlockAndPost"); michael@0: michael@0: // This is only available in Honeycomb and ICS. It was removed in Jelly Bean michael@0: ANativeWindow_fromSurfaceTexture = (void* (*)(JNIEnv*, jobject))dlsym(handle, "ANativeWindow_fromSurfaceTexture"); michael@0: michael@0: mHasNativeWindowAccess = ANativeWindow_fromSurface && ANativeWindow_release && ANativeWindow_lock && ANativeWindow_unlockAndPost; michael@0: michael@0: ALOG_BRIDGE("Successfully opened libandroid.so, have native window access? %d", mHasNativeWindowAccess); michael@0: } michael@0: michael@0: // We need one symbol from here on Jelly Bean michael@0: handle = dlopen("libandroid_runtime.so", RTLD_LAZY | RTLD_LOCAL); michael@0: if (handle) { michael@0: android_SurfaceTexture_getNativeWindow = (android::sp (*)(JNIEnv*, jobject))dlsym(handle, "_ZN7android38android_SurfaceTexture_getNativeWindowEP7_JNIEnvP8_jobject"); michael@0: } michael@0: michael@0: if (mHasNativeWindowAccess) michael@0: return; michael@0: michael@0: // Look up Surface functions, used for native window (surface) fallback michael@0: handle = dlopen("libsurfaceflinger_client.so", RTLD_LAZY); michael@0: if (handle) { michael@0: Surface_lock = (int (*)(void*, void*, void*, bool))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb"); michael@0: Surface_unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); michael@0: michael@0: handle = dlopen("libui.so", RTLD_LAZY); michael@0: if (handle) { michael@0: Region_constructor = (void (*)(void*))dlsym(handle, "_ZN7android6RegionC1Ev"); michael@0: Region_set = (void (*)(void*, void*))dlsym(handle, "_ZN7android6Region3setERKNS_4RectE"); michael@0: michael@0: mHasNativeWindowFallback = Surface_lock && Surface_unlockAndPost && Region_constructor && Region_set; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: namespace mozilla { michael@0: class TracerRunnable : public nsRunnable{ michael@0: public: michael@0: TracerRunnable() { michael@0: mTracerLock = new Mutex("TracerRunnable"); michael@0: mTracerCondVar = new CondVar(*mTracerLock, "TracerRunnable"); michael@0: mMainThread = do_GetMainThread(); michael@0: michael@0: } michael@0: ~TracerRunnable() { michael@0: delete mTracerCondVar; michael@0: delete mTracerLock; michael@0: mTracerLock = nullptr; michael@0: mTracerCondVar = nullptr; michael@0: } michael@0: michael@0: virtual nsresult Run() { michael@0: MutexAutoLock lock(*mTracerLock); michael@0: if (!AndroidBridge::Bridge()) michael@0: return NS_OK; michael@0: michael@0: mHasRun = true; michael@0: mTracerCondVar->Notify(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool Fire() { michael@0: if (!mTracerLock || !mTracerCondVar) michael@0: return false; michael@0: MutexAutoLock lock(*mTracerLock); michael@0: mHasRun = false; michael@0: mMainThread->Dispatch(this, NS_DISPATCH_NORMAL); michael@0: while (!mHasRun) michael@0: mTracerCondVar->Wait(); michael@0: return true; michael@0: } michael@0: michael@0: void Signal() { michael@0: MutexAutoLock lock(*mTracerLock); michael@0: mHasRun = true; michael@0: mTracerCondVar->Notify(); michael@0: } michael@0: private: michael@0: Mutex* mTracerLock; michael@0: CondVar* mTracerCondVar; michael@0: bool mHasRun; michael@0: nsCOMPtr mMainThread; michael@0: michael@0: }; michael@0: StaticRefPtr sTracerRunnable; michael@0: michael@0: bool InitWidgetTracing() { michael@0: if (!sTracerRunnable) michael@0: sTracerRunnable = new TracerRunnable(); michael@0: return true; michael@0: } michael@0: michael@0: void CleanUpWidgetTracing() { michael@0: sTracerRunnable = nullptr; michael@0: } michael@0: michael@0: bool FireAndWaitForTracerEvent() { michael@0: if (sTracerRunnable) michael@0: return sTracerRunnable->Fire(); michael@0: return false; michael@0: } michael@0: michael@0: void SignalTracerThread() michael@0: { michael@0: if (sTracerRunnable) michael@0: return sTracerRunnable->Signal(); michael@0: } michael@0: michael@0: } michael@0: bool michael@0: AndroidBridge::HasNativeBitmapAccess() michael@0: { michael@0: OpenGraphicsLibraries(); michael@0: michael@0: return mHasNativeBitmapAccess; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::ValidateBitmap(jobject bitmap, int width, int height) michael@0: { michael@0: // This structure is defined in Android API level 8's michael@0: // Because we can't depend on this, we get the function pointers via dlsym michael@0: // and define this struct ourselves. michael@0: struct BitmapInfo { michael@0: uint32_t width; michael@0: uint32_t height; michael@0: uint32_t stride; michael@0: uint32_t format; michael@0: uint32_t flags; michael@0: }; michael@0: michael@0: int err; michael@0: struct BitmapInfo info = { 0, }; michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: if ((err = AndroidBitmap_getInfo(env, bitmap, &info)) != 0) { michael@0: ALOG_BRIDGE("AndroidBitmap_getInfo failed! (error %d)", err); michael@0: return false; michael@0: } michael@0: michael@0: if ((int)info.width != width || (int)info.height != height) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::InitCamera(const nsCString& contentType, uint32_t camera, uint32_t *width, uint32_t *height, uint32_t *fps) michael@0: { michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jintArray arr = mozilla::widget::android::GeckoAppShell::InitCameraWrapper michael@0: (NS_ConvertUTF8toUTF16(contentType), (int32_t) camera, (int32_t) width, (int32_t) height); michael@0: michael@0: if (!arr) michael@0: return false; michael@0: michael@0: jint *elements = env->GetIntArrayElements(arr, 0); michael@0: michael@0: *width = elements[1]; michael@0: *height = elements[2]; michael@0: *fps = elements[3]; michael@0: michael@0: bool res = elements[0] == 1; michael@0: michael@0: env->ReleaseIntArrayElements(arr, elements, 0); michael@0: michael@0: return res; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetCurrentBatteryInformation"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: michael@0: // To prevent calling too many methods through JNI, the Java method returns michael@0: // an array of double even if we actually want a double and a boolean. michael@0: jdoubleArray arr = mozilla::widget::android::GeckoAppShell::GetCurrentBatteryInformationWrapper(); michael@0: if (!arr || env->GetArrayLength(arr) != 3) { michael@0: return; michael@0: } michael@0: michael@0: jdouble* info = env->GetDoubleArrayElements(arr, 0); michael@0: michael@0: aBatteryInfo->level() = info[0]; michael@0: aBatteryInfo->charging() = info[1] == 1.0f; michael@0: aBatteryInfo->remainingTime() = info[2]; michael@0: michael@0: env->ReleaseDoubleArrayElements(arr, info, 0); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::HandleGeckoMessage(JSContext* cx, JS::HandleObject object) michael@0: { michael@0: ALOG_BRIDGE("%s", __PRETTY_FUNCTION__); michael@0: michael@0: JNIEnv* const env = GetJNIEnv(); michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: const jobject message = michael@0: mozilla::widget::CreateNativeJSContainer(env, cx, object); michael@0: GeckoAppShell::HandleGeckoMessageWrapper(message); michael@0: } michael@0: michael@0: nsresult michael@0: AndroidBridge::GetSegmentInfoForText(const nsAString& aText, michael@0: nsIMobileMessageCallback* aRequest) michael@0: { michael@0: #ifndef MOZ_WEBSMS_BACKEND michael@0: return NS_ERROR_FAILURE; michael@0: #else michael@0: ALOG_BRIDGE("AndroidBridge::GetSegmentInfoForText"); michael@0: michael@0: dom::mobilemessage::SmsSegmentInfoData data; michael@0: michael@0: data.segments() = 0; michael@0: data.charsPerSegment() = 0; michael@0: data.charsAvailableInLastSegment() = 0; michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 2); michael@0: jstring jText = NewJavaString(&jniFrame, aText); michael@0: jobject obj = env->CallStaticObjectMethod(mAndroidSmsMessageClass, michael@0: jCalculateLength, jText, JNI_FALSE); michael@0: if (jniFrame.CheckForException()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: jintArray arr = static_cast(obj); michael@0: if (!arr || env->GetArrayLength(arr) != 4) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: jint* info = env->GetIntArrayElements(arr, JNI_FALSE); michael@0: michael@0: data.segments() = info[0]; // msgCount michael@0: data.charsPerSegment() = info[2]; // codeUnitsRemaining michael@0: // segmentChars = (codeUnitCount + codeUnitsRemaining) / msgCount michael@0: data.charsAvailableInLastSegment() = (info[1] + info[2]) / info[0]; michael@0: michael@0: env->ReleaseIntArrayElements(arr, info, JNI_ABORT); michael@0: michael@0: // TODO Bug 908598 - Should properly use |QueueSmsRequest(...)| to queue up michael@0: // the nsIMobileMessageCallback just like other functions. michael@0: nsCOMPtr info = new SmsSegmentInfo(data); michael@0: return aRequest->NotifySegmentInfoForTextGot(info); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::SendMessage(const nsAString& aNumber, michael@0: const nsAString& aMessage, michael@0: nsIMobileMessageCallback* aRequest) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::SendMessage"); michael@0: michael@0: uint32_t requestId; michael@0: if (!QueueSmsRequest(aRequest, &requestId)) michael@0: return; michael@0: michael@0: mozilla::widget::android::GeckoAppShell::SendMessageWrapper(aNumber, aMessage, requestId); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetMessage"); michael@0: michael@0: uint32_t requestId; michael@0: if (!QueueSmsRequest(aRequest, &requestId)) michael@0: return; michael@0: michael@0: mozilla::widget::android::GeckoAppShell::GetMessageWrapper(aMessageId, requestId); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::DeleteMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::DeleteMessage"); michael@0: michael@0: uint32_t requestId; michael@0: if (!QueueSmsRequest(aRequest, &requestId)) michael@0: return; michael@0: michael@0: mozilla::widget::android::GeckoAppShell::DeleteMessageWrapper(aMessageId, requestId); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, bool aReverse, michael@0: nsIMobileMessageCallback* aRequest) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::CreateMessageList"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: uint32_t requestId; michael@0: if (!QueueSmsRequest(aRequest, &requestId)) michael@0: return; michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 2); michael@0: michael@0: jobjectArray numbers = michael@0: (jobjectArray)env->NewObjectArray(aFilter.numbers().Length(), michael@0: jStringClass, michael@0: NewJavaString(&jniFrame, EmptyString())); michael@0: michael@0: for (uint32_t i = 0; i < aFilter.numbers().Length(); ++i) { michael@0: jstring elem = NewJavaString(&jniFrame, aFilter.numbers()[i]); michael@0: env->SetObjectArrayElement(numbers, i, elem); michael@0: env->DeleteLocalRef(elem); michael@0: } michael@0: michael@0: mozilla::widget::android::GeckoAppShell::CreateMessageListWrapper(aFilter.startDate(), michael@0: aFilter.endDate(), numbers, aFilter.numbers().Length(), michael@0: aFilter.delivery(), aReverse, requestId); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetNextMessageInList(int32_t aListId, nsIMobileMessageCallback* aRequest) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetNextMessageInList"); michael@0: michael@0: uint32_t requestId; michael@0: if (!QueueSmsRequest(aRequest, &requestId)) michael@0: return; michael@0: michael@0: mozilla::widget::android::GeckoAppShell::GetNextMessageInListWrapper(aListId, requestId); michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::QueueSmsRequest(nsIMobileMessageCallback* aRequest, uint32_t* aRequestIdOut) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: MOZ_ASSERT(aRequest && aRequestIdOut); michael@0: michael@0: const uint32_t length = mSmsRequests.Length(); michael@0: for (uint32_t i = 0; i < length; i++) { michael@0: if (!(mSmsRequests)[i]) { michael@0: (mSmsRequests)[i] = aRequest; michael@0: *aRequestIdOut = i; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: mSmsRequests.AppendElement(aRequest); michael@0: michael@0: // After AppendElement(), previous `length` points to the new tail element. michael@0: *aRequestIdOut = length; michael@0: return true; michael@0: } michael@0: michael@0: already_AddRefed michael@0: AndroidBridge::DequeueSmsRequest(uint32_t aRequestId) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: MOZ_ASSERT(aRequestId < mSmsRequests.Length()); michael@0: if (aRequestId >= mSmsRequests.Length()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return mSmsRequests[aRequestId].forget(); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetCurrentNetworkInformation"); michael@0: michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: michael@0: // To prevent calling too many methods through JNI, the Java method returns michael@0: // an array of double even if we actually want an integer, a boolean, and an integer. michael@0: michael@0: jdoubleArray arr = mozilla::widget::android::GeckoAppShell::GetCurrentNetworkInformationWrapper(); michael@0: if (!arr || env->GetArrayLength(arr) != 3) { michael@0: return; michael@0: } michael@0: michael@0: jdouble* info = env->GetDoubleArrayElements(arr, 0); michael@0: michael@0: aNetworkInfo->type() = info[0]; michael@0: aNetworkInfo->isWifi() = info[1] == 1.0f; michael@0: aNetworkInfo->dhcpGateway() = info[2]; michael@0: michael@0: env->ReleaseDoubleArrayElements(arr, info, 0); michael@0: } michael@0: michael@0: void * michael@0: AndroidBridge::LockBitmap(jobject bitmap) michael@0: { michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 0); michael@0: michael@0: int err; michael@0: void *buf; michael@0: michael@0: if ((err = AndroidBitmap_lockPixels(env, bitmap, &buf)) != 0) { michael@0: ALOG_BRIDGE("AndroidBitmap_lockPixels failed! (error %d)", err); michael@0: buf = nullptr; michael@0: } michael@0: michael@0: return buf; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::UnlockBitmap(jobject bitmap) michael@0: { michael@0: JNIEnv *env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 0); michael@0: michael@0: int err; michael@0: michael@0: if ((err = AndroidBitmap_unlockPixels(env, bitmap)) != 0) michael@0: ALOG_BRIDGE("AndroidBitmap_unlockPixels failed! (error %d)", err); michael@0: } michael@0: michael@0: michael@0: bool michael@0: AndroidBridge::HasNativeWindowAccess() michael@0: { michael@0: OpenGraphicsLibraries(); michael@0: michael@0: // We have a fallback hack in place, so return true if that will work as well michael@0: return mHasNativeWindowAccess || mHasNativeWindowFallback; michael@0: } michael@0: michael@0: void* michael@0: AndroidBridge::AcquireNativeWindow(JNIEnv* aEnv, jobject aSurface) michael@0: { michael@0: OpenGraphicsLibraries(); michael@0: michael@0: if (mHasNativeWindowAccess) michael@0: return ANativeWindow_fromSurface(aEnv, aSurface); michael@0: michael@0: if (mHasNativeWindowFallback) michael@0: return GetNativeSurface(aEnv, aSurface); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::ReleaseNativeWindow(void *window) michael@0: { michael@0: if (!window) michael@0: return; michael@0: michael@0: if (mHasNativeWindowAccess) michael@0: ANativeWindow_release(window); michael@0: michael@0: // XXX: we don't ref the pointer we get from the fallback (GetNativeSurface), so we michael@0: // have nothing to do here. We should probably ref it. michael@0: } michael@0: michael@0: void* michael@0: AndroidBridge::AcquireNativeWindowFromSurfaceTexture(JNIEnv* aEnv, jobject aSurfaceTexture) michael@0: { michael@0: OpenGraphicsLibraries(); michael@0: michael@0: if (mHasNativeWindowAccess && ANativeWindow_fromSurfaceTexture) michael@0: return ANativeWindow_fromSurfaceTexture(aEnv, aSurfaceTexture); michael@0: michael@0: if (mHasNativeWindowAccess && android_SurfaceTexture_getNativeWindow) { michael@0: android::sp window = android_SurfaceTexture_getNativeWindow(aEnv, aSurfaceTexture); michael@0: return window.get(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::ReleaseNativeWindowForSurfaceTexture(void *window) michael@0: { michael@0: if (!window) michael@0: return; michael@0: michael@0: // FIXME: we don't ref the pointer we get, so nothing to do currently. We should ref it. michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride) michael@0: { michael@0: /* Copied from native_window.h in Android NDK (platform-9) */ michael@0: typedef struct ANativeWindow_Buffer { michael@0: // The number of pixels that are show horizontally. michael@0: int32_t width; michael@0: michael@0: // The number of pixels that are shown vertically. michael@0: int32_t height; michael@0: michael@0: // The number of *pixels* that a line in the buffer takes in michael@0: // memory. This may be >= width. michael@0: int32_t stride; michael@0: michael@0: // The format of the buffer. One of WINDOW_FORMAT_* michael@0: int32_t format; michael@0: michael@0: // The actual bits. michael@0: void* bits; michael@0: michael@0: // Do not touch. michael@0: uint32_t reserved[6]; michael@0: } ANativeWindow_Buffer; michael@0: michael@0: // Very similar to the above, but the 'usage' field is included. We use this michael@0: // in the fallback case when NDK support is not available michael@0: struct SurfaceInfo { michael@0: uint32_t w; michael@0: uint32_t h; michael@0: uint32_t s; michael@0: uint32_t usage; michael@0: uint32_t format; michael@0: unsigned char* bits; michael@0: uint32_t reserved[2]; michael@0: }; michael@0: michael@0: int err; michael@0: *bits = nullptr; michael@0: *width = *height = *format = 0; michael@0: michael@0: if (mHasNativeWindowAccess) { michael@0: ANativeWindow_Buffer buffer; michael@0: michael@0: if ((err = ANativeWindow_lock(window, (void*)&buffer, nullptr)) != 0) { michael@0: ALOG_BRIDGE("ANativeWindow_lock failed! (error %d)", err); michael@0: return false; michael@0: } michael@0: michael@0: *bits = (unsigned char*)buffer.bits; michael@0: *width = buffer.width; michael@0: *height = buffer.height; michael@0: *format = buffer.format; michael@0: *stride = buffer.stride; michael@0: } else if (mHasNativeWindowFallback) { michael@0: SurfaceInfo info; michael@0: michael@0: if ((err = Surface_lock(window, &info, nullptr, true)) != 0) { michael@0: ALOG_BRIDGE("Surface_lock failed! (error %d)", err); michael@0: return false; michael@0: } michael@0: michael@0: *bits = info.bits; michael@0: *width = info.w; michael@0: *height = info.h; michael@0: *format = info.format; michael@0: *stride = info.s; michael@0: } else return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: jobject michael@0: AndroidBridge::GetGlobalContextRef() { michael@0: if (sGlobalContext == nullptr) { michael@0: JNIEnv *env = GetJNIForThread(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 4); michael@0: michael@0: jobject context = mozilla::widget::android::GeckoAppShell::GetContext(); michael@0: if (!context) { michael@0: ALOG_BRIDGE("%s: Could not GetContext()", __FUNCTION__); michael@0: return 0; michael@0: } michael@0: jclass contextClass = env->FindClass("android/content/Context"); michael@0: if (!contextClass) { michael@0: ALOG_BRIDGE("%s: Could not find Context class.", __FUNCTION__); michael@0: return 0; michael@0: } michael@0: jmethodID mid = env->GetMethodID(contextClass, "getApplicationContext", michael@0: "()Landroid/content/Context;"); michael@0: if (!mid) { michael@0: ALOG_BRIDGE("%s: Could not find getApplicationContext.", __FUNCTION__); michael@0: return 0; michael@0: } michael@0: jobject appContext = env->CallObjectMethod(context, mid); michael@0: if (!appContext) { michael@0: ALOG_BRIDGE("%s: getApplicationContext failed.", __FUNCTION__); michael@0: return 0; michael@0: } michael@0: michael@0: sGlobalContext = env->NewGlobalRef(appContext); michael@0: MOZ_ASSERT(sGlobalContext); michael@0: } michael@0: michael@0: return sGlobalContext; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::UnlockWindow(void* window) michael@0: { michael@0: int err; michael@0: michael@0: if (!HasNativeWindowAccess()) michael@0: return false; michael@0: michael@0: if (mHasNativeWindowAccess && (err = ANativeWindow_unlockAndPost(window)) != 0) { michael@0: ALOG_BRIDGE("ANativeWindow_unlockAndPost failed! (error %d)", err); michael@0: return false; michael@0: } else if (mHasNativeWindowFallback && (err = Surface_unlockAndPost(window)) != 0) { michael@0: ALOG_BRIDGE("Surface_unlockAndPost failed! (error %d)", err); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect) michael@0: { michael@0: mozilla::widget::android::GeckoLayerClient *client = mLayerClient; michael@0: if (!client) michael@0: return; michael@0: michael@0: client->SetFirstPaintViewport((float)aOffset.x, (float)aOffset.y, aZoom.scale, michael@0: aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost()); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::SetPageRect(const CSSRect& aCssPageRect) michael@0: { michael@0: mozilla::widget::android::GeckoLayerClient *client = mLayerClient; michael@0: if (!client) michael@0: return; michael@0: michael@0: client->SetPageRect(aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost()); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::SyncViewportInfo(const LayerIntRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution, michael@0: bool aLayersUpdated, ScreenPoint& aScrollOffset, CSSToScreenScale& aScale, michael@0: LayerMargin& aFixedLayerMargins, ScreenPoint& aOffset) michael@0: { michael@0: mozilla::widget::android::GeckoLayerClient *client = mLayerClient; michael@0: if (!client) { michael@0: ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); michael@0: return; michael@0: } michael@0: michael@0: jobject viewTransformJObj = client->SyncViewportInfo(aDisplayPort.x, aDisplayPort.y, michael@0: aDisplayPort.width, aDisplayPort.height, michael@0: aDisplayResolution.scale, aLayersUpdated); michael@0: NS_ABORT_IF_FALSE(viewTransformJObj, "No view transform object!"); michael@0: michael@0: if (!viewTransformJObj) { michael@0: return; michael@0: } michael@0: michael@0: ViewTransform* viewTransform = ViewTransform::Wrap(viewTransformJObj); michael@0: aScrollOffset = ScreenPoint(viewTransform->getx(), viewTransform->gety()); michael@0: aScale.scale = viewTransform->getscale(); michael@0: aFixedLayerMargins.top = viewTransform->getfixedLayerMarginTop(); michael@0: aFixedLayerMargins.right = viewTransform->getfixedLayerMarginRight(); michael@0: aFixedLayerMargins.bottom = viewTransform->getfixedLayerMarginBottom(); michael@0: aFixedLayerMargins.left = viewTransform->getfixedLayerMarginLeft(); michael@0: aOffset.x = viewTransform->getoffsetX(); michael@0: aOffset.y = viewTransform->getoffsetY(); michael@0: delete viewTransform; michael@0: } michael@0: michael@0: void AndroidBridge::SyncFrameMetrics(const ScreenPoint& aScrollOffset, float aZoom, const CSSRect& aCssPageRect, michael@0: bool aLayersUpdated, const CSSRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution, michael@0: bool aIsFirstPaint, LayerMargin& aFixedLayerMargins, ScreenPoint& aOffset) michael@0: { michael@0: mozilla::widget::android::GeckoLayerClient *client = mLayerClient; michael@0: if (!client) { michael@0: ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); michael@0: return; michael@0: } michael@0: michael@0: // convert the displayport rect from scroll-relative CSS pixels to document-relative device pixels michael@0: LayerRect dpUnrounded = aDisplayPort * aDisplayResolution; michael@0: dpUnrounded += LayerPoint::FromUnknownPoint(aScrollOffset.ToUnknownPoint()); michael@0: LayerIntRect dp = gfx::RoundedToInt(dpUnrounded); michael@0: michael@0: jobject viewTransformJObj = client->SyncFrameMetrics(aScrollOffset.x, aScrollOffset.y, aZoom, michael@0: aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost(), michael@0: aLayersUpdated, dp.x, dp.y, dp.width, dp.height, aDisplayResolution.scale, michael@0: aIsFirstPaint); michael@0: michael@0: NS_ABORT_IF_FALSE(viewTransformJObj, "No view transform object!"); michael@0: if (!viewTransformJObj) { michael@0: return; michael@0: } michael@0: ViewTransform* viewTransform = ViewTransform::Wrap(viewTransformJObj); michael@0: michael@0: aFixedLayerMargins.top = viewTransform->getfixedLayerMarginTop(); michael@0: aFixedLayerMargins.right = viewTransform->getfixedLayerMarginRight(); michael@0: aFixedLayerMargins.bottom = viewTransform->getfixedLayerMarginBottom(); michael@0: aFixedLayerMargins.left = viewTransform->getfixedLayerMarginLeft(); michael@0: michael@0: aOffset.x = viewTransform->getoffsetX(); michael@0: aOffset.y = viewTransform->getoffsetY(); michael@0: michael@0: delete viewTransform; michael@0: } michael@0: michael@0: AndroidBridge::AndroidBridge() michael@0: : mLayerClient(nullptr), michael@0: mNativePanZoomController(nullptr) michael@0: { michael@0: } michael@0: michael@0: AndroidBridge::~AndroidBridge() michael@0: { michael@0: } michael@0: michael@0: /* Implementation file */ michael@0: NS_IMPL_ISUPPORTS(nsAndroidBridge, nsIAndroidBridge) michael@0: michael@0: nsAndroidBridge::nsAndroidBridge() michael@0: { michael@0: } michael@0: michael@0: nsAndroidBridge::~nsAndroidBridge() michael@0: { michael@0: } michael@0: michael@0: /* void handleGeckoEvent (in AString message); */ michael@0: NS_IMETHODIMP nsAndroidBridge::HandleGeckoMessage(JS::HandleValue val, michael@0: JSContext *cx) michael@0: { michael@0: if (val.isObject()) { michael@0: JS::RootedObject object(cx, &val.toObject()); michael@0: AndroidBridge::Bridge()->HandleGeckoMessage(cx, object); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Now handle legacy JSON messages. michael@0: if (!val.isString()) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: JS::RootedString jsonStr(cx, val.toString()); michael@0: michael@0: size_t strLen = 0; michael@0: const jschar* strChar = JS_GetStringCharsAndLength(cx, jsonStr, &strLen); michael@0: if (!strChar) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: JS::RootedValue jsonVal(cx); michael@0: if (!JS_ParseJSON(cx, strChar, strLen, &jsonVal) || !jsonVal.isObject()) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // Spit out a warning before sending the message. michael@0: nsContentUtils::ReportToConsoleNonLocalized( michael@0: NS_LITERAL_STRING("Use of JSON is deprecated. " michael@0: "Please pass Javascript objects directly to handleGeckoMessage."), michael@0: nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("nsIAndroidBridge"), michael@0: nullptr); michael@0: michael@0: JS::RootedObject object(cx, &jsonVal.toObject()); michael@0: AndroidBridge::Bridge()->HandleGeckoMessage(cx, object); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIAndroidDisplayport getDisplayPort(in boolean aPageSizeUpdate, in boolean isBrowserContentDisplayed, in int32_t tabId, in nsIAndroidViewport metrics); */ michael@0: NS_IMETHODIMP nsAndroidBridge::GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort) michael@0: { michael@0: AndroidBridge::Bridge()->GetDisplayPort(aPageSizeUpdate, aIsBrowserContentDisplayed, tabId, metrics, displayPort); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void displayedDocumentChanged(); */ michael@0: NS_IMETHODIMP nsAndroidBridge::ContentDocumentChanged() michael@0: { michael@0: AndroidBridge::Bridge()->ContentDocumentChanged(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* boolean isContentDocumentDisplayed(); */ michael@0: NS_IMETHODIMP nsAndroidBridge::IsContentDocumentDisplayed(bool *aRet) michael@0: { michael@0: *aRet = AndroidBridge::Bridge()->IsContentDocumentDisplayed(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // DO NOT USE THIS unless you need to access JNI from michael@0: // non-main threads. This is probably not what you want. michael@0: // Questions, ask blassey or dougt. michael@0: michael@0: static void michael@0: JavaThreadDetachFunc(void *arg) michael@0: { michael@0: JNIEnv *env = (JNIEnv*) arg; michael@0: MOZ_ASSERT(env, "No JNIEnv on Gecko thread"); michael@0: if (!env) { michael@0: return; michael@0: } michael@0: JavaVM *vm = nullptr; michael@0: env->GetJavaVM(&vm); michael@0: MOZ_ASSERT(vm, "No JavaVM on Gecko thread"); michael@0: if (!vm) { michael@0: return; michael@0: } michael@0: vm->DetachCurrentThread(); michael@0: } michael@0: michael@0: uint32_t michael@0: AndroidBridge::GetScreenOrientation() michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::GetScreenOrientation"); michael@0: michael@0: int16_t orientation = mozilla::widget::android::GeckoAppShell::GetScreenOrientationWrapper(); michael@0: michael@0: if (!orientation) michael@0: return dom::eScreenOrientation_None; michael@0: michael@0: return static_cast(orientation); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::ScheduleComposite() michael@0: { michael@0: nsWindow::ScheduleComposite(); michael@0: } michael@0: michael@0: nsresult michael@0: AndroidBridge::GetProxyForURI(const nsACString & aSpec, michael@0: const nsACString & aScheme, michael@0: const nsACString & aHost, michael@0: const int32_t aPort, michael@0: nsACString & aResult) michael@0: { michael@0: if (!HasEnv()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: JNIEnv* env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: jstring jstrRet = michael@0: mozilla::widget::android::GeckoAppShell::GetProxyForURIWrapper(NS_ConvertUTF8toUTF16(aSpec), michael@0: NS_ConvertUTF8toUTF16(aScheme), michael@0: NS_ConvertUTF8toUTF16(aHost), michael@0: aPort); michael@0: michael@0: if (!jstrRet) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsJNIString jniStr(jstrRet, env); michael@0: CopyUTF16toUTF8(jniStr, aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /* attribute nsIAndroidBrowserApp browserApp; */ michael@0: NS_IMETHODIMP nsAndroidBridge::GetBrowserApp(nsIAndroidBrowserApp * *aBrowserApp) michael@0: { michael@0: if (nsAppShell::gAppShell) michael@0: nsAppShell::gAppShell->GetBrowserApp(aBrowserApp); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsAndroidBridge::SetBrowserApp(nsIAndroidBrowserApp *aBrowserApp) michael@0: { michael@0: if (nsAppShell::gAppShell) michael@0: nsAppShell::gAppShell->SetBrowserApp(aBrowserApp); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::AddPluginView(jobject view, const LayoutDeviceRect& rect, bool isFullScreen) { michael@0: nsWindow* win = nsWindow::TopWindow(); michael@0: if (!win) michael@0: return; michael@0: michael@0: CSSRect cssRect = rect / win->GetDefaultScale(); michael@0: mozilla::widget::android::GeckoAppShell::AddPluginViewWrapper(view, cssRect.x, cssRect.y, michael@0: cssRect.width, cssRect.height, isFullScreen); michael@0: } michael@0: michael@0: extern "C" michael@0: __attribute__ ((visibility("default"))) michael@0: jobject JNICALL michael@0: Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size); michael@0: michael@0: bool michael@0: AndroidBridge::GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult) michael@0: { michael@0: JNIEnv* env = GetJNIForThread(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: michael@0: jstring jstrThreadName = michael@0: mozilla::widget::android::GeckoJavaSampler::GetThreadNameJavaProfilingWrapper(aThreadId); michael@0: michael@0: if (!jstrThreadName) michael@0: return false; michael@0: michael@0: nsJNIString jniStr(jstrThreadName, env); michael@0: CopyUTF16toUTF8(jniStr.get(), aResult); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId, michael@0: uint32_t aFrameId, nsCString & aResult) michael@0: { michael@0: JNIEnv* env = GetJNIForThread(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 1); michael@0: michael@0: jstring jstrSampleName = mozilla::widget::android::GeckoJavaSampler::GetFrameNameJavaProfilingWrapper michael@0: (aThreadId, aSampleId, aFrameId); michael@0: michael@0: if (!jstrSampleName) michael@0: return false; michael@0: michael@0: nsJNIString jniStr(jstrSampleName, env); michael@0: CopyUTF16toUTF8(jniStr.get(), aResult); michael@0: return true; michael@0: } michael@0: michael@0: nsresult AndroidBridge::CaptureThumbnail(nsIDOMWindow *window, int32_t bufW, int32_t bufH, int32_t tabId, jobject buffer) michael@0: { michael@0: nsresult rv; michael@0: float scale = 1.0; michael@0: michael@0: if (!buffer) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // take a screenshot, as wide as possible, proportional to the destination size michael@0: nsCOMPtr utils = do_GetInterface(window); michael@0: if (!utils) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr rect; michael@0: rv = utils->GetRootBounds(getter_AddRefs(rect)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!rect) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: float left, top, width, height; michael@0: rect->GetLeft(&left); michael@0: rect->GetTop(&top); michael@0: rect->GetWidth(&width); michael@0: rect->GetHeight(&height); michael@0: michael@0: if (width == 0 || height == 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: int32_t srcX = left; michael@0: int32_t srcY = top; michael@0: int32_t srcW; michael@0: int32_t srcH; michael@0: michael@0: float aspectRatio = ((float) bufW) / bufH; michael@0: if (width / aspectRatio < height) { michael@0: srcW = width; michael@0: srcH = width / aspectRatio; michael@0: } else { michael@0: srcW = height * aspectRatio; michael@0: srcH = height; michael@0: } michael@0: michael@0: JNIEnv* env = GetJNIEnv(); michael@0: michael@0: AutoLocalJNIFrame jniFrame(env, 0); michael@0: michael@0: nsCOMPtr win = do_QueryInterface(window); michael@0: if (!win) michael@0: return NS_ERROR_FAILURE; michael@0: nsRefPtr presContext; michael@0: nsIDocShell* docshell = win->GetDocShell(); michael@0: if (docshell) { michael@0: docshell->GetPresContext(getter_AddRefs(presContext)); michael@0: } michael@0: if (!presContext) michael@0: return NS_ERROR_FAILURE; michael@0: nscolor bgColor = NS_RGB(255, 255, 255); michael@0: nsCOMPtr presShell = presContext->PresShell(); michael@0: uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING | michael@0: nsIPresShell::RENDER_DOCUMENT_RELATIVE); michael@0: nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale), michael@0: nsPresContext::CSSPixelsToAppUnits(srcY / scale), michael@0: nsPresContext::CSSPixelsToAppUnits(srcW / scale), michael@0: nsPresContext::CSSPixelsToAppUnits(srcH / scale)); michael@0: michael@0: bool is24bit = (GetScreenDepth() == 24); michael@0: uint32_t stride = bufW * (is24bit ? 4 : 2); michael@0: michael@0: void* data = env->GetDirectBufferAddress(buffer); michael@0: if (!data) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRefPtr surf = michael@0: new gfxImageSurface(static_cast(data), nsIntSize(bufW, bufH), stride, michael@0: is24bit ? gfxImageFormat::RGB24 : michael@0: gfxImageFormat::RGB16_565); michael@0: if (surf->CairoStatus() != 0) { michael@0: ALOG_BRIDGE("Error creating gfxImageSurface"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr context; michael@0: if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(BackendType::CAIRO)) { michael@0: RefPtr dt = michael@0: gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, IntSize(bufW, bufH)); michael@0: michael@0: if (!dt) { michael@0: ALOG_BRIDGE("Error creating DrawTarget"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: context = new gfxContext(dt); michael@0: } else { michael@0: context = new gfxContext(surf); michael@0: } michael@0: gfxPoint pt(0, 0); michael@0: context->Translate(pt); michael@0: context->Scale(scale * bufW / srcW, scale * bufH / srcH); michael@0: rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context); michael@0: if (is24bit) { michael@0: gfxUtils::ConvertBGRAtoRGBA(surf); michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort) michael@0: { michael@0: michael@0: ALOG_BRIDGE("Enter: %s", __PRETTY_FUNCTION__); michael@0: JNIEnv* env = GetJNIEnv(); michael@0: if (!mLayerClient || mLayerClient->isNull()) { michael@0: michael@0: ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); michael@0: return; michael@0: } michael@0: AutoLocalJNIFrame jniFrame(env, 0); michael@0: michael@0: float x, y, width, height, michael@0: pageLeft, pageTop, pageRight, pageBottom, michael@0: cssPageLeft, cssPageTop, cssPageRight, cssPageBottom, michael@0: zoom; michael@0: metrics->GetX(&x); michael@0: metrics->GetY(&y); michael@0: metrics->GetWidth(&width); michael@0: metrics->GetHeight(&height); michael@0: metrics->GetPageLeft(&pageLeft); michael@0: metrics->GetPageTop(&pageTop); michael@0: metrics->GetPageRight(&pageRight); michael@0: metrics->GetPageBottom(&pageBottom); michael@0: metrics->GetCssPageLeft(&cssPageLeft); michael@0: metrics->GetCssPageTop(&cssPageTop); michael@0: metrics->GetCssPageRight(&cssPageRight); michael@0: metrics->GetCssPageBottom(&cssPageBottom); michael@0: metrics->GetZoom(&zoom); michael@0: michael@0: ImmutableViewportMetrics jmetrics = ImmutableViewportMetrics(pageLeft, pageTop, pageRight, pageBottom, michael@0: cssPageLeft, cssPageTop, cssPageRight, cssPageBottom, michael@0: x, y, x + width, y + height, michael@0: zoom); michael@0: michael@0: jobject jobj = mLayerClient->GetDisplayPort(aPageSizeUpdate, aIsBrowserContentDisplayed, tabId, jmetrics.wrappedObject()); michael@0: if (!jobj) { michael@0: ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); michael@0: return; michael@0: } michael@0: DisplayPortMetrics* displayPortMetrics = DisplayPortMetrics::Wrap(jobj); michael@0: michael@0: AndroidRectF rect(env, displayPortMetrics->getMPosition()); michael@0: if (jniFrame.CheckForException()) { michael@0: ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); michael@0: return; michael@0: } michael@0: michael@0: float resolution = displayPortMetrics->getResolution(); michael@0: if (jniFrame.CheckForException()) { michael@0: ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); michael@0: return; michael@0: } michael@0: michael@0: *displayPort = new nsAndroidDisplayport(rect, resolution); michael@0: (*displayPort)->AddRef(); michael@0: michael@0: delete displayPortMetrics; michael@0: ALOG_BRIDGE("Exit: %s", __PRETTY_FUNCTION__); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::ContentDocumentChanged() michael@0: { michael@0: if (!mLayerClient) { michael@0: return; michael@0: } michael@0: mLayerClient->ContentDocumentChanged(); michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::IsContentDocumentDisplayed() michael@0: { michael@0: if (!mLayerClient) michael@0: return false; michael@0: michael@0: return mLayerClient->IsContentDocumentDisplayed(); michael@0: } michael@0: michael@0: bool michael@0: AndroidBridge::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, ParentLayerRect& aCompositionBounds, CSSToParentLayerScale& aZoom) michael@0: { michael@0: mozilla::widget::android::GeckoLayerClient *client = mLayerClient; michael@0: if (!client) { michael@0: ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__); michael@0: return false; michael@0: } michael@0: michael@0: jobject progressiveUpdateDataJObj = client->ProgressiveUpdateCallback(aHasPendingNewThebesContent, michael@0: (float)aDisplayPort.x, michael@0: (float)aDisplayPort.y, michael@0: (float)aDisplayPort.width, michael@0: (float)aDisplayPort.height, michael@0: aDisplayResolution, michael@0: !aDrawingCritical); michael@0: michael@0: NS_ABORT_IF_FALSE(progressiveUpdateDataJObj, "No progressive update data!"); michael@0: michael@0: ProgressiveUpdateData* progressiveUpdateData = ProgressiveUpdateData::Wrap(progressiveUpdateDataJObj); michael@0: michael@0: aCompositionBounds.x = progressiveUpdateData->getx(); michael@0: aCompositionBounds.y = progressiveUpdateData->gety(); michael@0: aCompositionBounds.width = progressiveUpdateData->getwidth(); michael@0: aCompositionBounds.height = progressiveUpdateData->getheight(); michael@0: aZoom.scale = progressiveUpdateData->getscale(); michael@0: michael@0: bool ret = progressiveUpdateData->getabort(); michael@0: delete progressiveUpdateData; michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: mozilla::widget::android::NativePanZoomController* michael@0: AndroidBridge::SetNativePanZoomController(jobject obj) michael@0: { michael@0: mozilla::widget::android::NativePanZoomController* old = mNativePanZoomController; michael@0: mNativePanZoomController = mozilla::widget::android::NativePanZoomController::Wrap(obj); michael@0: return old; michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics) michael@0: { michael@0: ALOG_BRIDGE("AndroidBridge::RequestContentRepaint"); michael@0: michael@0: // FIXME implement this michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId, michael@0: const uint32_t& aScrollGeneration) michael@0: { michael@0: // FIXME implement this michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::HandleDoubleTap(const CSSPoint& aPoint, michael@0: int32_t aModifiers, michael@0: const mozilla::layers::ScrollableLayerGuid& aGuid) michael@0: { michael@0: nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y); michael@0: nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent( michael@0: NS_LITERAL_CSTRING("Gesture:DoubleTap"), data)); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::HandleSingleTap(const CSSPoint& aPoint, michael@0: int32_t aModifiers, michael@0: const mozilla::layers::ScrollableLayerGuid& aGuid) michael@0: { michael@0: // TODO Send the modifier data to Gecko for use in mouse events. michael@0: nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y); michael@0: nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent( michael@0: NS_LITERAL_CSTRING("Gesture:SingleTap"), data)); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::HandleLongTap(const CSSPoint& aPoint, michael@0: int32_t aModifiers, michael@0: const mozilla::layers::ScrollableLayerGuid& aGuid) michael@0: { michael@0: nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y); michael@0: nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent( michael@0: NS_LITERAL_CSTRING("Gesture:LongPress"), data)); michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::HandleLongTapUp(const CSSPoint& aPoint, michael@0: int32_t aModifiers, michael@0: const mozilla::layers::ScrollableLayerGuid& aGuid) michael@0: { michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::SendAsyncScrollDOMEvent(bool aIsRoot, michael@0: const CSSRect& aContentRect, michael@0: const CSSSize& aScrollableSize) michael@0: { michael@0: // FIXME implement this michael@0: } michael@0: michael@0: void michael@0: AndroidBridge::PostDelayedTask(Task* aTask, int aDelayMs) michael@0: { michael@0: // add the new task into the mDelayedTaskQueue, sorted with michael@0: // the earliest task first in the queue michael@0: DelayedTask* newTask = new DelayedTask(aTask, aDelayMs); michael@0: uint32_t i = 0; michael@0: while (i < mDelayedTaskQueue.Length()) { michael@0: if (newTask->IsEarlierThan(mDelayedTaskQueue[i])) { michael@0: mDelayedTaskQueue.InsertElementAt(i, newTask); michael@0: break; michael@0: } michael@0: i++; michael@0: } michael@0: if (i == mDelayedTaskQueue.Length()) { michael@0: // this new task will run after all the existing tasks in the queue michael@0: mDelayedTaskQueue.AppendElement(newTask); michael@0: } michael@0: if (i == 0) { michael@0: // if we're inserting it at the head of the queue, notify Java because michael@0: // we need to get a callback at an earlier time than the last scheduled michael@0: // callback michael@0: mNativePanZoomController->PostDelayedCallbackWrapper((int64_t)aDelayMs); michael@0: } michael@0: } michael@0: michael@0: int64_t michael@0: AndroidBridge::RunDelayedTasks() michael@0: { michael@0: while (mDelayedTaskQueue.Length() > 0) { michael@0: DelayedTask* nextTask = mDelayedTaskQueue[0]; michael@0: int64_t timeLeft = nextTask->MillisecondsToRunTime(); michael@0: if (timeLeft > 0) { michael@0: // this task (and therefore all remaining tasks) michael@0: // have not yet reached their runtime. return the michael@0: // time left until we should be called again michael@0: return timeLeft; michael@0: } michael@0: michael@0: // we have a delayed task to run. extract it from michael@0: // the wrapper and free the wrapper michael@0: michael@0: mDelayedTaskQueue.RemoveElementAt(0); michael@0: Task* task = nextTask->GetTask(); michael@0: delete nextTask; michael@0: michael@0: task->Run(); michael@0: } michael@0: return -1; michael@0: }