diff --git a/mobile/android/base/IntentHelper.java b/mobile/android/base/IntentHelper.java index 7cde1e460d43..0c0ef91b9384 100644 --- a/mobile/android/base/IntentHelper.java +++ b/mobile/android/base/IntentHelper.java @@ -6,8 +6,11 @@ package org.mozilla.gecko; import org.mozilla.gecko.util.ActivityResultHandler; +import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.JSONUtils; +import org.mozilla.gecko.util.NativeEventListener; +import org.mozilla.gecko.util.NativeJSObject; import org.mozilla.gecko.util.WebActivityMapper; import org.json.JSONArray; @@ -26,16 +29,21 @@ import java.net.URLEncoder; import java.util.Arrays; import java.util.List; -public final class IntentHelper implements GeckoEventListener { +public final class IntentHelper implements GeckoEventListener, + NativeEventListener { + private static final String LOGTAG = "GeckoIntentHelper"; private static final String[] EVENTS = { "Intent:GetHandlers", "Intent:Open", "Intent:OpenForResult", - "Intent:OpenNoHandler", "WebActivity:Open" }; + private static final String[] NATIVE_EVENTS = { + "Intent:OpenNoHandler", + }; + // via http://developer.android.com/distribute/tools/promote/linking.html private static String MARKET_INTENT_URI_PACKAGE_PREFIX = "market://details?id="; private static String EXTRA_BROWSER_FALLBACK_URL = "browser_fallback_url"; @@ -49,7 +57,8 @@ public final class IntentHelper implements GeckoEventListener { private IntentHelper(Activity activity) { this.activity = activity; - EventDispatcher.getInstance().registerGeckoThreadListener(this, EVENTS); + EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this, EVENTS); + EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this, NATIVE_EVENTS); } public static IntentHelper init(Activity activity) { @@ -64,11 +73,19 @@ public final class IntentHelper implements GeckoEventListener { public static void destroy() { if (instance != null) { - EventDispatcher.getInstance().unregisterGeckoThreadListener(instance, EVENTS); + EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) instance, EVENTS); + EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) instance, NATIVE_EVENTS); instance = null; } } + @Override + public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) { + if (event.equals("Intent:OpenNoHandler")) { + openNoHandler(message, callback); + } + } + @Override public void handleMessage(String event, JSONObject message) { try { @@ -78,8 +95,6 @@ public final class IntentHelper implements GeckoEventListener { open(message); } else if (event.equals("Intent:OpenForResult")) { openForResult(message); - } else if (event.equals("Intent:OpenNoHandler")) { - openNoHandler(message); } else if (event.equals("WebActivity:Open")) { openWebActivity(message); } @@ -133,12 +148,12 @@ public final class IntentHelper implements GeckoEventListener { * not specified and there is a fallback url in the intent extras, we open that url. If neither * is present, we alert the user that we were unable to open the link. */ - private void openNoHandler(final JSONObject msg) { - final String uri = msg.optString("uri"); + private void openNoHandler(final NativeJSObject msg, final EventCallback callback) { + final String uri = msg.getString("uri"); if (TextUtils.isEmpty(uri)) { - openUnknownProtocolErrorPage(""); Log.w(LOGTAG, "Received empty URL - loading about:neterror"); + callback.sendError(getUnknownProtocolErrorPageUri("")); return; } @@ -147,14 +162,16 @@ public final class IntentHelper implements GeckoEventListener { // TODO (bug 1173626): This will not handle android-app uris on non 5.1 devices. intent = Intent.parseUri(uri, 0); } catch (final URISyntaxException e) { + String errorUri; try { - openUnknownProtocolErrorPage(URLEncoder.encode(uri, "UTF-8")); + errorUri = getUnknownProtocolErrorPageUri(URLEncoder.encode(uri, "UTF-8")); } catch (final UnsupportedEncodingException encodingE) { - openUnknownProtocolErrorPage(""); + errorUri = getUnknownProtocolErrorPageUri(""); } // Don't log the exception to prevent leaking URIs. Log.w(LOGTAG, "Unable to parse Intent URI - loading about:neterror"); + callback.sendError(errorUri); return; } @@ -174,28 +191,31 @@ public final class IntentHelper implements GeckoEventListener { final Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(marketUri)); marketIntent.addCategory(Intent.CATEGORY_BROWSABLE); marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // (Bug 1192436) We don't know if marketIntent matches any Activities (e.g. non-Play + // Store devices). If it doesn't, clicking the link will cause no action to occur. activity.startActivity(marketIntent); + callback.sendSuccess(null); } else if (intent.hasExtra(EXTRA_BROWSER_FALLBACK_URL)) { final String fallbackUrl = intent.getStringExtra(EXTRA_BROWSER_FALLBACK_URL); - Tabs.getInstance().loadUrl(fallbackUrl); + callback.sendError(fallbackUrl); } else { - openUnknownProtocolErrorPage(intent.getData().toString()); // Don't log the URI to prevent leaking it. Log.w(LOGTAG, "Unable to open URI, default case - loading about:neterror"); + callback.sendError(getUnknownProtocolErrorPageUri(intent.getData().toString())); } } /** - * Opens about:neterror with the unknownProtocolFound text. + * Returns an about:neterror uri with the unknownProtocolFound text as a parameter. * @param encodedUri The encoded uri. While the page does not open correctly without specifying * a uri parameter, it happily accepts the empty String so this argument may * be the empty String. */ - private void openUnknownProtocolErrorPage(final String encodedUri) { - final String errorUri = UNKNOWN_PROTOCOL_URI_PREFIX + encodedUri; - Tabs.getInstance().loadUrl(errorUri); + private String getUnknownProtocolErrorPageUri(final String encodedUri) { + return UNKNOWN_PROTOCOL_URI_PREFIX + encodedUri; } private void openWebActivity(JSONObject message) throws JSONException { diff --git a/mobile/android/components/ContentDispatchChooser.js b/mobile/android/components/ContentDispatchChooser.js index 62cfa2af6a4f..486d80f87d8e 100644 --- a/mobile/android/components/ContentDispatchChooser.js +++ b/mobile/android/components/ContentDispatchChooser.js @@ -63,7 +63,16 @@ ContentDispatchChooser.prototype = uri: aURI.spec, }; - Messaging.sendRequest(msg); + Messaging.sendRequestForResult(msg).then(() => { + // Java opens an app on success: take no action. + }, (uri) => { + // Java didn't load a page so load the page that Java wants us to load. + // + // Note: when we load the page here (rather than into the selected tab in + // java), we load it in the same context where the uri was specified (e.g. + // if it's in an iframe, we load the page in an iframe). + window.location.href = uri; + }); } }, };