/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Hal.h" #include "nsIFile.h" #include "nsString.h" #include "AndroidBridge.h" #include "AndroidGraphicBuffer.h" #include #include #include #include #include #include #include "nsAppShell.h" #include "nsWindow.h" #include #include "nsIObserverService.h" #include "mozilla/Services.h" #include "nsThreadUtils.h" #ifdef MOZ_CRASHREPORTER #include "nsICrashReporter.h" #include "nsExceptionHandler.h" #endif #include "mozilla/unused.h" #include "mozilla/dom/SmsMessage.h" #include "mozilla/dom/mobilemessage/Constants.h" #include "mozilla/dom/mobilemessage/Types.h" #include "mozilla/dom/mobilemessage/PSms.h" #include "mozilla/dom/mobilemessage/SmsParent.h" #include "mozilla/layers/APZCTreeManager.h" #include "nsIMobileMessageDatabaseService.h" #include "nsPluginInstanceOwner.h" #include "nsSurfaceTexture.h" #include "GeckoProfiler.h" #include "GeckoProfiler.h" using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::mobilemessage; using namespace mozilla::layers; /* Forward declare all the JNI methods as extern "C" */ extern "C" { /* * Incoming JNI methods */ NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *jenv, jclass jc) { AndroidBridge::ConstructBridge(jenv); } 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(AndroidGeckoEvent::MakeFromJavaObject(jenv, event)); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_processNextNativeEvent(JNIEnv *jenv, jclass, jboolean mayWait) { // poke the appshell if (nsAppShell::gAppShell) nsAppShell::gAppShell->ProcessNextNativeEvent(mayWait != JNI_FALSE); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject obj) { AndroidBridge::Bridge()->SetLayerClient(jenv, obj); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *jenv, jclass jc) { if (nsAppShell::gAppShell) nsAppShell::gAppShell->OnResume(); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *jenv, jclass, jstring jStackTrace) { #ifdef MOZ_CRASHREPORTER const nsJNIString stackTrace16(jStackTrace, jenv); const NS_ConvertUTF16toUTF8 stackTrace8(stackTrace16); CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("JavaStackTrace"), stackTrace8); #endif // MOZ_CRASHREPORTER abort(); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv* jenv, jclass, jdouble aLevel, jboolean aCharging, jdouble aRemainingTime) { class NotifyBatteryChangeRunnable : public nsRunnable { public: NotifyBatteryChangeRunnable(double aLevel, bool aCharging, double aRemainingTime) : mLevel(aLevel) , mCharging(aCharging) , mRemainingTime(aRemainingTime) {} NS_IMETHODIMP Run() { hal::NotifyBatteryChange(hal::BatteryInformation(mLevel, mCharging, mRemainingTime)); return NS_OK; } private: double mLevel; bool mCharging; double mRemainingTime; }; nsCOMPtr runnable = new NotifyBatteryChangeRunnable(aLevel, aCharging, aRemainingTime); NS_DispatchToMainThread(runnable); } #ifdef MOZ_WEBSMS_BACKEND NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(JNIEnv* jenv, jclass, jstring aSender, jstring aBody, jint aMessageClass, jlong aTimestamp) { class NotifySmsReceivedRunnable : public nsRunnable { public: NotifySmsReceivedRunnable(const SmsMessageData& aMessageData) : mMessageData(aMessageData) {} NS_IMETHODIMP Run() { nsCOMPtr obs = services::GetObserverService(); if (!obs) { return NS_OK; } nsCOMPtr message = new SmsMessage(mMessageData); obs->NotifyObservers(message, kSmsReceivedObserverTopic, nullptr); return NS_OK; } private: SmsMessageData mMessageData; }; // TODO Need to correct the message `threadId` parameter value. Bug 859098 SmsMessageData message(0, 0, eDeliveryState_Received, eDeliveryStatus_Success, nsJNIString(aSender, jenv), EmptyString(), nsJNIString(aBody, jenv), static_cast(aMessageClass), aTimestamp, false); nsCOMPtr runnable = new NotifySmsReceivedRunnable(message); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifySmsSent(JNIEnv* jenv, jclass, jint aId, jstring aReceiver, jstring aBody, jlong aTimestamp, jint aRequestId) { class NotifySmsSentRunnable : public nsRunnable { public: NotifySmsSentRunnable(const SmsMessageData& aMessageData, int32_t aRequestId) : mMessageData(aMessageData) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { /* * First, we are going to notify all SmsManager that a message has * been sent. Then, we will notify the SmsRequest object about it. */ nsCOMPtr obs = services::GetObserverService(); if (!obs) { return NS_OK; } nsCOMPtr message = new SmsMessage(mMessageData); obs->NotifyObservers(message, kSmsSentObserverTopic, nullptr); nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); request->NotifyMessageSent(message); return NS_OK; } private: SmsMessageData mMessageData; int32_t mRequestId; }; // TODO Need to add the message `messageClass` parameter value. Bug 804476 // TODO Need to correct the message `threadId` parameter value. Bug 859098 SmsMessageData message(aId, 0, eDeliveryState_Sent, eDeliveryStatus_Pending, EmptyString(), nsJNIString(aReceiver, jenv), nsJNIString(aBody, jenv), eMessageClass_Normal, aTimestamp, true); nsCOMPtr runnable = new NotifySmsSentRunnable(message, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDelivery(JNIEnv* jenv, jclass, jint aId, jint aDeliveryStatus, jstring aReceiver, jstring aBody, jlong aTimestamp) { class NotifySmsDeliveredRunnable : public nsRunnable { public: NotifySmsDeliveredRunnable(const SmsMessageData& aMessageData) : mMessageData(aMessageData) {} NS_IMETHODIMP Run() { nsCOMPtr obs = services::GetObserverService(); if (!obs) { return NS_OK; } nsCOMPtr message = new SmsMessage(mMessageData); const char* topic = (mMessageData.deliveryStatus() == eDeliveryStatus_Success) ? kSmsDeliverySuccessObserverTopic : kSmsDeliveryErrorObserverTopic; obs->NotifyObservers(message, topic, nullptr); return NS_OK; } private: SmsMessageData mMessageData; }; // TODO Need to add the message `messageClass` parameter value. Bug 804476 // TODO Need to correct the message `threadId` parameter value. Bug 859098 SmsMessageData message(aId, 0, eDeliveryState_Sent, static_cast(aDeliveryStatus), EmptyString(), nsJNIString(aReceiver, jenv), nsJNIString(aBody, jenv), eMessageClass_Normal, aTimestamp, true); nsCOMPtr runnable = new NotifySmsDeliveredRunnable(message); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifySmsSendFailed(JNIEnv* jenv, jclass, jint aError, jint aRequestId) { class NotifySmsSendFailedRunnable : public nsRunnable { public: NotifySmsSendFailedRunnable(int32_t aError, int32_t aRequestId) : mError(aError) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); request->NotifySendMessageFailed(mError); return NS_OK; } private: int32_t mError; int32_t mRequestId; }; nsCOMPtr runnable = new NotifySmsSendFailedRunnable(aError, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifyGetSms(JNIEnv* jenv, jclass, jint aId, jint aDeliveryStatus, jstring aReceiver, jstring aSender, jstring aBody, jlong aTimestamp, jint aRequestId) { class NotifyGetSmsRunnable : public nsRunnable { public: NotifyGetSmsRunnable(const SmsMessageData& aMessageData, int32_t aRequestId) : mMessageData(aMessageData) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); nsCOMPtr message = new SmsMessage(mMessageData); request->NotifyMessageGot(message); return NS_OK; } private: SmsMessageData mMessageData; int32_t mRequestId; }; nsJNIString receiver = nsJNIString(aReceiver, jenv); DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received : eDeliveryState_Sent; // TODO Need to add the message `read` parameter value. Bug 748391 // TODO Need to add the message `messageClass` parameter value. Bug 804476 // TODO Need to correct the message `threadId` parameter value. Bug 859098 SmsMessageData message(aId, 0, state, static_cast(aDeliveryStatus), nsJNIString(aSender, jenv), receiver, nsJNIString(aBody, jenv), eMessageClass_Normal, aTimestamp, true); nsCOMPtr runnable = new NotifyGetSmsRunnable(message, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifyGetSmsFailed(JNIEnv* jenv, jclass, jint aError, jint aRequestId) { class NotifyGetSmsFailedRunnable : public nsRunnable { public: NotifyGetSmsFailedRunnable(int32_t aError, int32_t aRequestId) : mError(aError) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); request->NotifyGetMessageFailed(mError); return NS_OK; } private: int32_t mError; int32_t mRequestId; }; nsCOMPtr runnable = new NotifyGetSmsFailedRunnable(aError, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDeleted(JNIEnv* jenv, jclass, jboolean aDeleted, jint aRequestId) { class NotifySmsDeletedRunnable : public nsRunnable { public: NotifySmsDeletedRunnable(bool aDeleted, int32_t aRequestId) : mDeleted(aDeleted) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); // For android, we support only single SMS deletion. request->NotifyMessageDeleted(&mDeleted, 1); return NS_OK; } private: bool mDeleted; int32_t mRequestId; }; nsCOMPtr runnable = new NotifySmsDeletedRunnable(aDeleted, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDeleteFailed(JNIEnv* jenv, jclass, jint aError, jint aRequestId) { class NotifySmsDeleteFailedRunnable : public nsRunnable { public: NotifySmsDeleteFailedRunnable(int32_t aError, int32_t aRequestId) : mError(aError) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); request->NotifyDeleteMessageFailed(mError); return NS_OK; } private: int32_t mError; int32_t mRequestId; }; nsCOMPtr runnable = new NotifySmsDeleteFailedRunnable(aError, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifyNoMessageInList(JNIEnv* jenv, jclass, jint aRequestId) { class NotifyNoMessageInListRunnable : public nsRunnable { public: NotifyNoMessageInListRunnable(int32_t aRequestId) : mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); request->NotifyNoMessageInList(); return NS_OK; } private: int32_t mRequestId; }; nsCOMPtr runnable = new NotifyNoMessageInListRunnable(aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifyListCreated(JNIEnv* jenv, jclass, jint aListId, jint aMessageId, jint aDeliveryStatus, jstring aReceiver, jstring aSender, jstring aBody, jlong aTimestamp, jint aRequestId) { class NotifyCreateMessageListRunnable : public nsRunnable { public: NotifyCreateMessageListRunnable(int32_t aListId, const SmsMessageData& aMessageData, int32_t aRequestId) : mListId(aListId) , mMessageData(aMessageData) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); nsCOMPtr message = new SmsMessage(mMessageData); request->NotifyMessageListCreated(mListId, message); return NS_OK; } private: int32_t mListId; SmsMessageData mMessageData; int32_t mRequestId; }; nsJNIString receiver = nsJNIString(aReceiver, jenv); DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received : eDeliveryState_Sent; // TODO Need to add the message `read` parameter value. Bug 748391 // TODO Need to add the message `messageClass` parameter value. Bug 804476 // TODO Need to correct the message `threadId` parameter value. Bug 859098 SmsMessageData message(aMessageId, 0, state, static_cast(aDeliveryStatus), nsJNIString(aSender, jenv), receiver, nsJNIString(aBody, jenv), eMessageClass_Normal, aTimestamp, true); nsCOMPtr runnable = new NotifyCreateMessageListRunnable(aListId, message, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifyGotNextMessage(JNIEnv* jenv, jclass, jint aMessageId, jint aDeliveryStatus, jstring aReceiver, jstring aSender, jstring aBody, jlong aTimestamp, jint aRequestId) { class NotifyGotNextMessageRunnable : public nsRunnable { public: NotifyGotNextMessageRunnable(const SmsMessageData& aMessageData, int32_t aRequestId) : mMessageData(aMessageData) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); nsCOMPtr message = new SmsMessage(mMessageData); request->NotifyNextMessageInListGot(message); return NS_OK; } private: SmsMessageData mMessageData; int32_t mRequestId; }; nsJNIString receiver = nsJNIString(aReceiver, jenv); DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received : eDeliveryState_Sent; // TODO Need to add the message `read` parameter value. Bug 748391 // TODO Need to add the message `messageClass` parameter value. Bug 804476 // TODO Need to correct the message `threadId` parameter value. Bug 859098 SmsMessageData message(aMessageId, 0, state, static_cast(aDeliveryStatus), nsJNIString(aSender, jenv), receiver, nsJNIString(aBody, jenv), eMessageClass_Normal, aTimestamp, true); nsCOMPtr runnable = new NotifyGotNextMessageRunnable(message, aRequestId); NS_DispatchToMainThread(runnable); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoSmsManager_notifyReadingMessageListFailed(JNIEnv* jenv, jclass, jint aError, jint aRequestId) { class NotifyReadListFailedRunnable : public nsRunnable { public: NotifyReadListFailedRunnable(int32_t aError, int32_t aRequestId) : mError(aError) , mRequestId(aRequestId) {} NS_IMETHODIMP Run() { nsCOMPtr request = AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); request->NotifyReadMessageListFailed(mError); return NS_OK; } private: int32_t mError; int32_t mRequestId; }; nsCOMPtr runnable = new NotifyReadListFailedRunnable(aError, aRequestId); NS_DispatchToMainThread(runnable); } #endif // MOZ_WEBSMS_BACKEND NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(JNIEnv*, jclass) { nsWindow::ScheduleComposite(); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv*, jclass, jint width, jint height) { nsWindow::ScheduleResumeComposition(width, height); } NS_EXPORT float JNICALL Java_org_mozilla_gecko_GeckoAppShell_computeRenderIntegrity(JNIEnv*, jclass) { return nsWindow::ComputeRenderIntegrity(); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult(JNIEnv* jenv, jclass, jstring filePath, jlong callback) { class NotifyFilePickerResultRunnable : public nsRunnable { public: NotifyFilePickerResultRunnable(nsString& fileDir, long callback) : mFileDir(fileDir), mCallback(callback) {} NS_IMETHODIMP Run() { nsFilePickerCallback* handler = (nsFilePickerCallback*)mCallback; handler->handleResult(mFileDir); handler->Release(); return NS_OK; } private: nsString mFileDir; long mCallback; }; nsString path = nsJNIString(filePath, jenv); nsCOMPtr runnable = new NotifyFilePickerResultRunnable(path, (long)callback); NS_DispatchToMainThread(runnable); } static int NextPowerOfTwo(int value) { // code taken from http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html if (0 == value--) { return 1; } value = (value >> 1) | value; value = (value >> 2) | value; value = (value >> 4) | value; value = (value >> 8) | value; value = (value >> 16) | value; return value + 1; } #define MAX_LOCK_ATTEMPTS 10 static bool LockWindowWithRetry(void* window, unsigned char** bits, int* width, int* height, int* format, int* stride) { int count = 0; while (count < MAX_LOCK_ATTEMPTS) { if (AndroidBridge::Bridge()->LockWindow(window, bits, width, height, format, stride)) return true; count++; usleep(500); } return false; } NS_EXPORT jobject JNICALL Java_org_mozilla_gecko_GeckoAppShell_getSurfaceBits(JNIEnv* jenv, jclass, jobject surface) { static jclass jSurfaceBitsClass = nullptr; static jmethodID jSurfaceBitsCtor = 0; static jfieldID jSurfaceBitsWidth, jSurfaceBitsHeight, jSurfaceBitsFormat, jSurfaceBitsBuffer; jobject surfaceBits = nullptr; unsigned char* bitsCopy = nullptr; int dstWidth, dstHeight, dstSize; void* window = AndroidBridge::Bridge()->AcquireNativeWindow(jenv, surface); if (!window) return nullptr; unsigned char* bits; int srcWidth, srcHeight, format, srcStride; // So we lock/unlock once here in order to get whatever is currently the front buffer. It sucks. if (!LockWindowWithRetry(window, &bits, &srcWidth, &srcHeight, &format, &srcStride)) return nullptr; AndroidBridge::Bridge()->UnlockWindow(window); // This is lock will result in the front buffer, since the last unlock rotated it to the back. Probably. if (!LockWindowWithRetry(window, &bits, &srcWidth, &srcHeight, &format, &srcStride)) return nullptr; // These are from android.graphics.PixelFormat int bpp; switch (format) { case 1: // RGBA_8888 bpp = 4; break; case 4: // RGB_565 bpp = 2; break; default: goto cleanup; } dstWidth = NextPowerOfTwo(srcWidth); dstHeight = NextPowerOfTwo(srcHeight); dstSize = dstWidth * dstHeight * bpp; bitsCopy = (unsigned char*)malloc(dstSize); bzero(bitsCopy, dstSize); for (int i = 0; i < srcHeight; i++) { memcpy(bitsCopy + ((dstHeight - i - 1) * dstWidth * bpp), bits + (i * srcStride * bpp), srcStride * bpp); } if (!jSurfaceBitsClass) { jSurfaceBitsClass = (jclass)jenv->NewGlobalRef(jenv->FindClass("org/mozilla/gecko/SurfaceBits")); jSurfaceBitsCtor = jenv->GetMethodID(jSurfaceBitsClass, "", "()V"); jSurfaceBitsWidth = jenv->GetFieldID(jSurfaceBitsClass, "width", "I"); jSurfaceBitsHeight = jenv->GetFieldID(jSurfaceBitsClass, "height", "I"); jSurfaceBitsFormat = jenv->GetFieldID(jSurfaceBitsClass, "format", "I"); jSurfaceBitsBuffer = jenv->GetFieldID(jSurfaceBitsClass, "buffer", "Ljava/nio/ByteBuffer;"); } surfaceBits = jenv->NewObject(jSurfaceBitsClass, jSurfaceBitsCtor); jenv->SetIntField(surfaceBits, jSurfaceBitsWidth, dstWidth); jenv->SetIntField(surfaceBits, jSurfaceBitsHeight, dstHeight); jenv->SetIntField(surfaceBits, jSurfaceBitsFormat, format); jenv->SetObjectField(surfaceBits, jSurfaceBitsBuffer, jenv->NewDirectByteBuffer(bitsCopy, dstSize)); cleanup: AndroidBridge::Bridge()->UnlockWindow(window); AndroidBridge::Bridge()->ReleaseNativeWindow(window); return surfaceBits; } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onFullScreenPluginHidden(JNIEnv* jenv, jclass, jobject view) { class ExitFullScreenRunnable : public nsRunnable { public: ExitFullScreenRunnable(jobject view) : mView(view) {} NS_IMETHODIMP Run() { JNIEnv* env = AndroidBridge::GetJNIEnv(); if (!env) { NS_WARNING("Failed to acquire JNI env, can't exit plugin fullscreen mode"); return NS_OK; } nsPluginInstanceOwner::ExitFullScreen(mView); env->DeleteGlobalRef(mView); return NS_OK; } private: jobject mView; }; nsCOMPtr runnable = new ExitFullScreenRunnable(jenv->NewGlobalRef(view)); NS_DispatchToMainThread(runnable); } NS_EXPORT jobject JNICALL Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue(JNIEnv* jenv, jclass, jobject queue) { static jclass jMessageQueueCls = nullptr; static jfieldID jMessagesField; static jmethodID jNextMethod; if (!jMessageQueueCls) { jMessageQueueCls = (jclass) jenv->NewGlobalRef(jenv->FindClass("android/os/MessageQueue")); jNextMethod = jenv->GetMethodID(jMessageQueueCls, "next", "()Landroid/os/Message;"); jMessagesField = jenv->GetFieldID(jMessageQueueCls, "mMessages", "Landroid/os/Message;"); } if (!jMessageQueueCls || !jNextMethod) return nullptr; if (jMessagesField) { jobject msg = jenv->GetObjectField(queue, jMessagesField); // if queue.mMessages is null, queue.next() will block, which we don't want // It turns out to be an order of magnitude more performant to do this extra check here and // block less vs. one fewer checks here and more blocking. if (!msg) { return nullptr; } } return jenv->CallObjectMethod(queue, jNextMethod); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onSurfaceTextureFrameAvailable(JNIEnv* jenv, jclass, jobject surfaceTexture, jint id) { nsSurfaceTexture* st = nsSurfaceTexture::Find(id); if (!st) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "Failed to find nsSurfaceTexture with id %d", id); return; } st->NotifyFrameAvailable(); } NS_EXPORT jdouble JNICALL Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv *jenv, jclass jc) { return profiler_time(); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_abortAnimation(JNIEnv* env, jobject instance) { APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); if (controller) { controller->CancelAnimation(ScrollableLayerGuid(nsWindow::RootLayerTreeId())); } } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_init(JNIEnv* env, jobject instance) { if (!AndroidBridge::Bridge()) { return; } jobject oldRef = AndroidBridge::Bridge()->SetNativePanZoomController(env->NewGlobalRef(instance)); if (oldRef) { MOZ_ASSERT(false, "Registering a new NPZC when we already have one"); env->DeleteGlobalRef(oldRef); } } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_handleTouchEvent(JNIEnv* env, jobject instance, jobject event) { APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); if (controller) { AndroidGeckoEvent* wrapper = AndroidGeckoEvent::MakeFromJavaObject(env, event); const MultiTouchInput& input = wrapper->MakeMultiTouchInput(nsWindow::TopWindow()); delete wrapper; if (input.mType >= 0) { controller->ReceiveInputEvent(input, nullptr); } } } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_handleMotionEvent(JNIEnv* env, jobject instance, jobject event) { // FIXME implement this } NS_EXPORT jlong JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback(JNIEnv* env, jobject instance) { if (!AndroidBridge::Bridge()) { return -1; } return AndroidBridge::Bridge()->RunDelayedTasks(); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy(JNIEnv* env, jobject instance) { if (!AndroidBridge::Bridge()) { return; } jobject oldRef = AndroidBridge::Bridge()->SetNativePanZoomController(nullptr); if (!oldRef) { MOZ_ASSERT(false, "Clearing a non-existent NPZC"); } else { env->DeleteGlobalRef(oldRef); } } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_notifyDefaultActionPrevented(JNIEnv* env, jobject instance, jboolean prevented) { APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); if (controller) { controller->ContentReceivedTouch(ScrollableLayerGuid(nsWindow::RootLayerTreeId()), prevented); } } NS_EXPORT jboolean JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_getRedrawHint(JNIEnv* env, jobject instance) { // FIXME implement this return true; } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode(JNIEnv* env, jobject instance, jint overscrollMode) { // FIXME implement this } NS_EXPORT jint JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode(JNIEnv* env, jobject instance) { // FIXME implement this return 0; } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_updateScrollOffset(JNIEnv* env, jobject instance, jfloat cssX, jfloat cssY) { APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); if (controller) { controller->UpdateScrollOffset(ScrollableLayerGuid(nsWindow::RootLayerTreeId()), CSSPoint(cssX, cssY)); } } NS_EXPORT jboolean JNICALL Java_org_mozilla_gecko_ANRReporter_requestNativeStack(JNIEnv*, jclass) { if (profiler_is_active()) { // Don't proceed if profiler is already running return JNI_FALSE; } // WARNING: we are on the ANR reporter thread at this point and it is // generally unsafe to use the profiler from off the main thread. However, // the risk here is limited because for most users, the profiler is not run // elsewhere. See the discussion in Bug 863777, comment 13 const char *NATIVE_STACK_FEATURES[] = {"leaf", "threads", "privacy"}; // Buffer one sample and let the profiler wait a long time profiler_start(100, 10000, NATIVE_STACK_FEATURES, sizeof(NATIVE_STACK_FEATURES) / sizeof(char*), nullptr, 0); return JNI_TRUE; } NS_EXPORT jstring JNICALL Java_org_mozilla_gecko_ANRReporter_getNativeStack(JNIEnv* jenv, jclass) { if (!profiler_is_active()) { // Maybe profiler support is disabled? return nullptr; } char *profile = profiler_get_profile(); while (profile && !strlen(profile)) { // no sample yet? sched_yield(); profile = profiler_get_profile(); } jstring result = nullptr; if (profile) { result = jenv->NewStringUTF(profile); free(profile); } return result; } NS_EXPORT void JNICALL Java_org_mozilla_gecko_ANRReporter_releaseNativeStack(JNIEnv* jenv, jclass) { if (!profiler_is_active()) { // Maybe profiler support is disabled? return; } mozilla_sampler_stop(); } }