mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 958706 - Add ThrowException and HandleUncaughtException methods to AndroidBridge; r=blassey
ThrowException throws a new exception with the appropriate message, meant for native JNI methods that return to Java code (i.e. methods in AndroidJNI.cpp). HandleUncaughtException will be called by the generated JNI stubs that C++ code uses. HandleUncaughtException calls the new GeckoAppShell.handleUncaughtException method, which behaves exactly like the normal uncaught exception handler (annotates the crash report and crashes). GeckoAppShell.handleUncaughtException has the noThrow annotation that will be seen by the generated code; as a result, its generated stub will not call HandleUncaughtException and result in a loop.
This commit is contained in:
parent
d5b9a4492a
commit
29cc16d495
@ -219,34 +219,7 @@ public class GeckoAppShell
|
|||||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void uncaughtException(Thread thread, Throwable e) {
|
public void uncaughtException(Thread thread, Throwable e) {
|
||||||
// If the uncaught exception was rethrown, walk the exception `cause` chain to find
|
handleUncaughtException(thread, e);
|
||||||
// the original exception so Socorro can correctly collate related crash reports.
|
|
||||||
Throwable cause;
|
|
||||||
while ((cause = e.getCause()) != null) {
|
|
||||||
e = cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD "
|
|
||||||
+ thread.getId() + " (\"" + thread.getName() + "\")", e);
|
|
||||||
|
|
||||||
Thread mainThread = ThreadUtils.getUiThread();
|
|
||||||
if (mainThread != null && thread != mainThread) {
|
|
||||||
Log.e(LOGTAG, "Main thread stack:");
|
|
||||||
for (StackTraceElement ste : mainThread.getStackTrace()) {
|
|
||||||
Log.e(LOGTAG, ste.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e instanceof OutOfMemoryError) {
|
|
||||||
SharedPreferences prefs = getSharedPreferences();
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, true);
|
|
||||||
editor.commit();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
reportJavaCrash(getStackTraceString(e));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -418,6 +391,42 @@ public class GeckoAppShell
|
|||||||
/*
|
/*
|
||||||
* The Gecko-side API: API methods that Gecko calls
|
* The Gecko-side API: API methods that Gecko calls
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@WrapElementForJNI(generateStatic = true, noThrow = true)
|
||||||
|
public static void handleUncaughtException(Thread thread, Throwable e) {
|
||||||
|
if (thread == null) {
|
||||||
|
thread = Thread.currentThread();
|
||||||
|
}
|
||||||
|
// If the uncaught exception was rethrown, walk the exception `cause` chain to find
|
||||||
|
// the original exception so Socorro can correctly collate related crash reports.
|
||||||
|
Throwable cause;
|
||||||
|
while ((cause = e.getCause()) != null) {
|
||||||
|
e = cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD "
|
||||||
|
+ thread.getId() + " (\"" + thread.getName() + "\")", e);
|
||||||
|
|
||||||
|
Thread mainThread = ThreadUtils.getUiThread();
|
||||||
|
if (mainThread != null && thread != mainThread) {
|
||||||
|
Log.e(LOGTAG, "Main thread stack:");
|
||||||
|
for (StackTraceElement ste : mainThread.getStackTrace()) {
|
||||||
|
Log.e(LOGTAG, ste.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof OutOfMemoryError) {
|
||||||
|
SharedPreferences prefs = getSharedPreferences();
|
||||||
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
|
editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, true);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
reportJavaCrash(getStackTraceString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@WrapElementForJNI(generateStatic = true)
|
@WrapElementForJNI(generateStatic = true)
|
||||||
public static void notifyIME(int type) {
|
public static void notifyIME(int type) {
|
||||||
if (mEditableListener != null) {
|
if (mEditableListener != null) {
|
||||||
|
@ -38,4 +38,10 @@ public @interface WrapElementForJNI {
|
|||||||
* Did I mention use of this function is discouraged?
|
* Did I mention use of this function is discouraged?
|
||||||
*/
|
*/
|
||||||
boolean allowMultithread() default false;
|
boolean allowMultithread() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the generated stub will not handle uncaught exceptions.
|
||||||
|
* Any exception must be handled or cleared by the code calling the stub.
|
||||||
|
*/
|
||||||
|
boolean noThrow() default false;
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,33 @@ public:
|
|||||||
return sBridge && sBridge->mJNIEnv;
|
return sBridge && sBridge->mJNIEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ThrowException(JNIEnv *aEnv, const char *aClass,
|
||||||
|
const char *aMessage) {
|
||||||
|
MOZ_ASSERT(aEnv, "Invalid thread JNI env");
|
||||||
|
jclass cls = aEnv->FindClass(aClass);
|
||||||
|
MOZ_ASSERT(cls, "Cannot find exception class");
|
||||||
|
bool ret = !aEnv->ThrowNew(cls, aMessage);
|
||||||
|
aEnv->DeleteLocalRef(cls);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ThrowException(JNIEnv *aEnv, const char *aMessage) {
|
||||||
|
return ThrowException(aEnv, "java/lang/Exception", aMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleUncaughtException(JNIEnv *aEnv) {
|
||||||
|
MOZ_ASSERT(aEnv);
|
||||||
|
if (!aEnv->ExceptionCheck()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jthrowable e = aEnv->ExceptionOccurred();
|
||||||
|
MOZ_ASSERT(e);
|
||||||
|
aEnv->ExceptionClear();
|
||||||
|
GeckoAppShell::HandleUncaughtException(nullptr, e);
|
||||||
|
// Should be dead by now...
|
||||||
|
MOZ_CRASH("Failed to handle uncaught exception");
|
||||||
|
}
|
||||||
|
|
||||||
// The bridge needs to be constructed via ConstructBridge first,
|
// The bridge needs to be constructed via ConstructBridge first,
|
||||||
// and then once the Gecko main thread is spun up (Gecko side),
|
// and then once the Gecko main thread is spun up (Gecko side),
|
||||||
// SetMainThread should be called which will create the JNIEnv for
|
// SetMainThread should be called which will create the JNIEnv for
|
||||||
|
@ -51,6 +51,7 @@ jmethodID GeckoAppShell::jGetScreenOrientationWrapper = 0;
|
|||||||
jmethodID GeckoAppShell::jGetShowPasswordSetting = 0;
|
jmethodID GeckoAppShell::jGetShowPasswordSetting = 0;
|
||||||
jmethodID GeckoAppShell::jGetSystemColoursWrapper = 0;
|
jmethodID GeckoAppShell::jGetSystemColoursWrapper = 0;
|
||||||
jmethodID GeckoAppShell::jHandleGeckoMessageWrapper = 0;
|
jmethodID GeckoAppShell::jHandleGeckoMessageWrapper = 0;
|
||||||
|
jmethodID GeckoAppShell::jHandleUncaughtException = 0;
|
||||||
jmethodID GeckoAppShell::jHideProgressDialog = 0;
|
jmethodID GeckoAppShell::jHideProgressDialog = 0;
|
||||||
jmethodID GeckoAppShell::jInitCameraWrapper = 0;
|
jmethodID GeckoAppShell::jInitCameraWrapper = 0;
|
||||||
jmethodID GeckoAppShell::jIsNetworkLinkKnown = 0;
|
jmethodID GeckoAppShell::jIsNetworkLinkKnown = 0;
|
||||||
@ -132,6 +133,7 @@ void GeckoAppShell::InitStubs(JNIEnv *jEnv) {
|
|||||||
jGetShowPasswordSetting = getStaticMethod("getShowPasswordSetting", "()Z");
|
jGetShowPasswordSetting = getStaticMethod("getShowPasswordSetting", "()Z");
|
||||||
jGetSystemColoursWrapper = getStaticMethod("getSystemColors", "()[I");
|
jGetSystemColoursWrapper = getStaticMethod("getSystemColors", "()[I");
|
||||||
jHandleGeckoMessageWrapper = getStaticMethod("handleGeckoMessage", "(Ljava/lang/String;)Ljava/lang/String;");
|
jHandleGeckoMessageWrapper = getStaticMethod("handleGeckoMessage", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||||
|
jHandleUncaughtException = getStaticMethod("handleUncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
|
||||||
jHideProgressDialog = getStaticMethod("hideProgressDialog", "()V");
|
jHideProgressDialog = getStaticMethod("hideProgressDialog", "()V");
|
||||||
jInitCameraWrapper = getStaticMethod("initCamera", "(Ljava/lang/String;III)[I");
|
jInitCameraWrapper = getStaticMethod("initCamera", "(Ljava/lang/String;III)[I");
|
||||||
jIsNetworkLinkKnown = getStaticMethod("isNetworkLinkKnown", "()Z");
|
jIsNetworkLinkKnown = getStaticMethod("isNetworkLinkKnown", "()Z");
|
||||||
@ -1307,6 +1309,33 @@ jstring GeckoAppShell::HandleGeckoMessageWrapper(const nsAString& a0) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeckoAppShell::HandleUncaughtException(jobject a0, jthrowable a1) {
|
||||||
|
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||||
|
if (!env) {
|
||||||
|
ALOG_BRIDGE("Aborted: No env - %s", __PRETTY_FUNCTION__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env->PushLocalFrame(2) != 0) {
|
||||||
|
ALOG_BRIDGE("Exceptional exit of: %s", __PRETTY_FUNCTION__);
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->CallStaticVoidMethod(mGeckoAppShellClass, jHandleUncaughtException, a0, a1);
|
||||||
|
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
ALOG_BRIDGE("Exceptional exit of: %s", __PRETTY_FUNCTION__);
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
env->PopLocalFrame(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->PopLocalFrame(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void GeckoAppShell::HideProgressDialog() {
|
void GeckoAppShell::HideProgressDialog() {
|
||||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||||
if (!env) {
|
if (!env) {
|
||||||
|
@ -58,6 +58,7 @@ public:
|
|||||||
static bool GetShowPasswordSetting();
|
static bool GetShowPasswordSetting();
|
||||||
static jintArray GetSystemColoursWrapper();
|
static jintArray GetSystemColoursWrapper();
|
||||||
static jstring HandleGeckoMessageWrapper(const nsAString& a0);
|
static jstring HandleGeckoMessageWrapper(const nsAString& a0);
|
||||||
|
static void HandleUncaughtException(jobject a0, jthrowable a1);
|
||||||
static void HideProgressDialog();
|
static void HideProgressDialog();
|
||||||
static jintArray InitCameraWrapper(const nsAString& a0, int32_t a1, int32_t a2, int32_t a3);
|
static jintArray InitCameraWrapper(const nsAString& a0, int32_t a1, int32_t a2, int32_t a3);
|
||||||
static bool IsNetworkLinkKnown();
|
static bool IsNetworkLinkKnown();
|
||||||
@ -138,6 +139,7 @@ protected:
|
|||||||
static jmethodID jGetShowPasswordSetting;
|
static jmethodID jGetShowPasswordSetting;
|
||||||
static jmethodID jGetSystemColoursWrapper;
|
static jmethodID jGetSystemColoursWrapper;
|
||||||
static jmethodID jHandleGeckoMessageWrapper;
|
static jmethodID jHandleGeckoMessageWrapper;
|
||||||
|
static jmethodID jHandleUncaughtException;
|
||||||
static jmethodID jHideProgressDialog;
|
static jmethodID jHideProgressDialog;
|
||||||
static jmethodID jInitCameraWrapper;
|
static jmethodID jInitCameraWrapper;
|
||||||
static jmethodID jIsNetworkLinkKnown;
|
static jmethodID jIsNetworkLinkKnown;
|
||||||
|
Loading…
Reference in New Issue
Block a user