Bug 1052158 - Forward the GeckoRequest error stack to Java. r=wesj
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -223,16 +223,17 @@ public class GeckoAppShell
private static Sensor gAccelerometerSensor;
private static Sensor gLinearAccelerometerSensor;
private static Sensor gGyroscopeSensor;
private static Sensor gOrientationSensor;
private static Sensor gProximitySensor;
private static Sensor gLightSensor;
private static final String GECKOREQUEST_RESPONSE_KEY = "response";
+ private static final String GECKOREQUEST_ERROR_KEY = "error";
/*
* Keep in sync with constants found here:
* http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl
*/
static public final int WPL_STATE_START = 0x00000001;
static public final int WPL_STATE_STOP = 0x00000010;
static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
@@ -429,17 +430,17 @@ public class GeckoAppShell
public static void sendRequestToGecko(final GeckoRequest request) {
final String responseMessage = "Gecko:Request" + request.getId();
EventDispatcher.getInstance().registerGeckoThreadListener(new NativeEventListener() {
@Override
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, event);
if (!message.has(GECKOREQUEST_RESPONSE_KEY)) {
- request.onError();
+ request.onError(message.getObject(GECKOREQUEST_ERROR_KEY));
return;
}
request.onResponse(message.getObject(GECKOREQUEST_RESPONSE_KEY));
}
}, responseMessage);
sendEventToGecko(GeckoEvent.createBroadcastEvent(request.getName(), request.getData()));
}
--- a/mobile/android/base/tests/testGeckoRequest.java
+++ b/mobile/android/base/tests/testGeckoRequest.java
@@ -91,17 +91,17 @@ public class testGeckoRequest extends UI
GeckoAppShell.sendRequestToGecko(new GeckoRequest(REQUEST_EXCEPTION_EVENT, null) {
@Override
public void onResponse(NativeJSObject nativeJSObject) {
responseReceived.set(true);
}
@Override
- public void onError() {
+ public void onError(NativeJSObject error) {
errorReceived.set(true);
}
});
WaitHelper.waitFor("Received error for listener with exception", new Condition() {
@Override
public boolean isSatisfied() {
return errorReceived.get();
--- a/mobile/android/base/util/GeckoRequest.java
+++ b/mobile/android/base/util/GeckoRequest.java
@@ -1,8 +1,11 @@
+/* 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/. */
package org.mozilla.gecko.util;
import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.mozglue.RobocopTarget;
@@ -72,20 +75,20 @@ public abstract class GeckoRequest {
* @param nativeJSObject The response data from Gecko
*/
@RobocopTarget
public abstract void onResponse(NativeJSObject nativeJSObject);
/**
* Callback executed when the request fails.
*
- * In general, this should not be overridden since there's no way to differentiate between
- * expected errors and logic errors in JS. If the Gecko-side request handler wants to send a
- * recoverable error to Java, it should include any error data in the response object that the
- * {@link #onResponse(NativeJSObject)} callback can handle as necessary.
+ * By default, an exception is thrown. This should be overridden if the
+ * GeckoRequest is able to recover from the error.
*
* @throws RuntimeException
*/
@RobocopTarget
- public void onError() {
- throw new RuntimeException("Unhandled error for GeckoRequest: " + name);
+ public void onError(NativeJSObject error) {
+ final String message = error.optString("message", "<no message>");
+ final String stack = error.optString("stack", "<no stack>");
+ throw new RuntimeException("Unhandled error for GeckoRequest " + name + ": " + message + "\nJS stack:\n" + stack);
}
-}
\ No newline at end of file
+}
--- a/mobile/android/modules/Messaging.jsm
+++ b/mobile/android/modules/Messaging.jsm
@@ -138,29 +138,31 @@ let requestHandler = {
delete this._listeners[aMessage];
Services.obs.removeObserver(this, aMessage);
},
observe: Task.async(function* (aSubject, aTopic, aData) {
let wrapper = JSON.parse(aData);
let listener = this._listeners[aTopic];
- // A null response indicates an error. If an error occurs in the callback
- // below, the response will remain null, and Java will fire onError for
- // this request.
- let response = null;
-
try {
- let result = yield listener(wrapper.data);
- if (typeof result !== "object" || result === null) {
+ let response = yield listener(wrapper.data);
+ if (typeof response !== "object" || response === null) {
throw new Error("Gecko request listener did not return an object");
}
- response = result;
+
+ Messaging.sendRequest({
+ type: "Gecko:Request" + wrapper.id,
+ response: response
+ });
} catch (e) {
- Cu.reportError(e);
- }
+ Cu.reportError("Error in Messaging handler for " + aTopic + ": " + e);
- Messaging.sendRequest({
- type: "Gecko:Request" + wrapper.id,
- response: response
- });
+ Messaging.sendRequest({
+ type: "Gecko:Request" + wrapper.id,
+ error: {
+ message: e.message || (e && e.toString()),
+ stack: e.stack || Components.stack.formattedStack,
+ }
+ });
+ }
})
};