diff --git a/framework/src/org/apache/cordova/CordovaChromeClient.java b/framework/src/org/apache/cordova/CordovaChromeClient.java index 72c86265..4fe56ad1 100755 --- a/framework/src/org/apache/cordova/CordovaChromeClient.java +++ b/framework/src/org/apache/cordova/CordovaChromeClient.java @@ -20,6 +20,7 @@ package org.apache.cordova; import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; +import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; @@ -202,8 +203,8 @@ public class CordovaChromeClient extends WebChromeClient { String action = array.getString(1); String callbackId = array.getString(2); boolean async = array.getBoolean(3); - String r = this.appView.pluginManager.exec(service, action, callbackId, message, async); - result.confirm(r); + PluginResult r = this.appView.pluginManager.exec(service, action, callbackId, message, async); + result.confirm(r == null ? "" : r.getJSONString()); } catch (JSONException e) { e.printStackTrace(); } diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 7962e81b..3f7a504d 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -30,6 +30,7 @@ import java.util.regex.Pattern; import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; import org.apache.cordova.api.PluginManager; +import org.apache.cordova.api.PluginResult; import org.json.JSONException; import org.xmlpull.v1.XmlPullParserException; @@ -248,7 +249,8 @@ public class CordovaWebView extends WebView { this.addJavascriptInterface(new Object() { @SuppressWarnings("unused") public String exec(String service, String action, String callbackId, String arguments) throws JSONException { - return pluginManager.exec(service, action, callbackId, arguments, true /* async */); + PluginResult r = pluginManager.exec(service, action, callbackId, arguments, true /* async */); + return r == null ? "" : r.getJSONString(); } }, "_cordovaExec"); } @@ -489,7 +491,7 @@ public class CordovaWebView extends WebView { // Load url this.loadUrlIntoView(url); } - + /** * Send JavaScript statement back to JavaScript. * (This is a convenience method) diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 4513a811..28e754ae 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -21,6 +21,8 @@ package org.apache.cordova; import java.util.Hashtable; import org.apache.cordova.api.CordovaInterface; +import org.apache.cordova.api.PluginResult; + import java.io.IOException; import java.io.InputStream; @@ -38,6 +40,7 @@ import android.content.res.AssetManager; import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; +import android.util.Log; import android.view.View; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; @@ -50,7 +53,11 @@ import android.webkit.WebViewClient; */ public class CordovaWebViewClient extends WebViewClient { - private static final String TAG = "Cordova"; + private static final String TAG = "Cordova"; + // Disable URL-based exec() bridge by default since it's a bit of a + // security concern. + private static boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false; + private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/"; CordovaInterface cordova; CordovaWebView appView; private boolean doClearHistory = false; @@ -87,6 +94,29 @@ public class CordovaWebViewClient extends WebViewClient { this.appView = view; } + + // Parses commands sent by setting the webView's URL to: + // cdvbrg:service/action/callbackId#jsonArgs + private void handleExecUrl(String url) { + int idx1 = CORDOVA_EXEC_URL_PREFIX.length(); + int idx2 = url.indexOf('#', idx1 + 1); + int idx3 = url.indexOf('#', idx2 + 1); + int idx4 = url.indexOf('#', idx3 + 1); + if (idx1 == -1 || idx2 == -1 || idx3 == -1 || idx4 == -1) { + Log.e(TAG, "Could not decode URL command: " + url); + return; + } + String service = url.substring(idx1, idx2); + String action = url.substring(idx2 + 1, idx3); + String callbackId = url.substring(idx3 + 1, idx4); + String jsonArgs = url.substring(idx4 + 1); + PluginResult r = appView.pluginManager.exec(service, action, callbackId, jsonArgs, true /* async */); + String callbackString = r.toCallbackString(callbackId); + if (r != null) { + appView.sendJavascript(callbackString); + } + } + /** * Give the host application a chance to take over the control when a new url * is about to be loaded in the current WebView. @@ -95,11 +125,15 @@ public class CordovaWebViewClient extends WebViewClient { * @param url The url to be loaded. * @return true to override, false for default behavior */ - @Override + @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { + // Check if it's an exec() bridge command message. + if (ENABLE_LOCATION_CHANGE_EXEC_MODE && url.startsWith(CORDOVA_EXEC_URL_PREFIX)) { + handleExecUrl(url); + } - // First give any plugins the chance to handle the url themselves - if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) { + // Give plugins the chance to handle the url + else if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) { } // If dialing phone (tel:5551212) diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java index a48de67b..1f4ea1ea 100755 --- a/framework/src/org/apache/cordova/api/PluginManager.java +++ b/framework/src/org/apache/cordova/api/PluginManager.java @@ -172,9 +172,9 @@ public class PluginManager { * immediate return value. If true, either Cordova.callbackSuccess(...) or * Cordova.callbackError(...) is called once the plugin code has executed. * - * @return JSON encoded string with a response message and status. + * @return PluginResult to send to the page, or null if no response is ready yet. */ - public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) { + public PluginResult exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) { PluginResult cr = null; boolean runAsync = async; try { @@ -190,20 +190,9 @@ public class PluginManager { try { // Call execute on the plugin so that it can do it's thing PluginResult cr = plugin.execute(action, args, callbackId); - int status = cr.getStatus(); - - // If no result to be sent and keeping callback, then no need to sent back to JavaScript - if ((status == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) { - } - - // Check the success (OK, NO_RESULT & !KEEP_CALLBACK) - else if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) { - app.sendJavascript(cr.toSuccessCallbackString(callbackId)); - } - - // If error - else { - app.sendJavascript(cr.toErrorCallbackString(callbackId)); + String callbackString = cr.toCallbackString(callbackId); + if (callbackString != null) { + app.sendJavascript(callbackString); } } catch (Exception e) { PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage()); @@ -212,14 +201,14 @@ public class PluginManager { } }); thread.start(); - return ""; + return null; } else { // Call execute on the plugin so that it can do it's thing cr = plugin.execute(action, args, callbackId); // If no result to be sent and keeping callback, then no need to sent back to JavaScript if ((cr.getStatus() == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) { - return ""; + return null; } } } @@ -234,7 +223,10 @@ public class PluginManager { } app.sendJavascript(cr.toErrorCallbackString(callbackId)); } - return (cr != null ? cr.getJSONString() : "{ status: 0, message: 'all good' }"); + if (cr == null) { + cr = new PluginResult(PluginResult.Status.NO_RESULT); + } + return cr; } /** diff --git a/framework/src/org/apache/cordova/api/PluginResult.java b/framework/src/org/apache/cordova/api/PluginResult.java index b5ae3048..eacf6d5c 100755 --- a/framework/src/org/apache/cordova/api/PluginResult.java +++ b/framework/src/org/apache/cordova/api/PluginResult.java @@ -82,6 +82,19 @@ public class PluginResult { return "{\"status\":" + this.status + ",\"message\":" + this.message + ",\"keepCallback\":" + this.keepCallback + "}"; } + public String toCallbackString(String callbackId) { + // If no result to be sent and keeping callback, then no need to sent back to JavaScript + if ((status == PluginResult.Status.NO_RESULT.ordinal()) && keepCallback) { + return null; + } + + // Check the success (OK, NO_RESULT & !KEEP_CALLBACK) + if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) { + return toSuccessCallbackString(callbackId); + } + + return toErrorCallbackString(callbackId); + } public String toSuccessCallbackString(String callbackId) { return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");"; }