[android] Add non-eval based plugin result decoding.
Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/commit/74ae56fa Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/tree/74ae56fa Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/diff/74ae56fa Branch: refs/heads/master Commit: 74ae56fa12b18a479e06674618ed1777622d896e Parents: 898933d Author: Andrew Grieve <agri...@chromium.org> Authored: Fri Sep 7 15:14:32 2012 -0400 Committer: Andrew Grieve <agri...@chromium.org> Committed: Tue Sep 18 10:46:48 2012 -0400 ---------------------------------------------------------------------- lib/android/exec.js | 133 +++++++++++++-------------- lib/android/plugin/android/callback.js | 11 +-- lib/android/plugin/android/polling.js | 11 +-- lib/cordova.js | 57 +++++------- 4 files changed, 91 insertions(+), 121 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/74ae56fa/lib/android/exec.js ---------------------------------------------------------------------- diff --git a/lib/android/exec.js b/lib/android/exec.js index 3a836fe..95c9652 100644 --- a/lib/android/exec.js +++ b/lib/android/exec.js @@ -78,77 +78,22 @@ function androidExec(success, fail, service, action, args) { androidExec.setNativeToJsBridgeMode(nativeToJsModes.POLLING); } } - try { - var callbackId = service + cordova.callbackId++, - argsJson = JSON.stringify(args), - result; - if (success || fail) { - cordova.callbacks[callbackId] = {success:success, fail:fail}; - } - - if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) { - window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson; - } else { - // Explicit cast to string is required on Android 2.1 to convert from - // a Java string to a JS string. - result = '' + nativeApiProvider.get().exec(service, action, callbackId, argsJson); - } - - // If a result was returned - if (result) { - var v = JSON.parse(result); - - // If status is OK, then return value back to caller - if (v.status === cordova.callbackStatus.OK) { - - // If there is a success callback, then call it now with - // returned value - if (success) { - try { - success(v.message); - } catch (e) { - console.log("Error in success callback: " + callbackId + " = " + e); - } - - // Clear callback if not expecting any more results - if (!v.keepCallback) { - delete cordova.callbacks[callbackId]; - } - } - return v.message; - } - - // If no result - else if (v.status === cordova.callbackStatus.NO_RESULT) { - // Clear callback if not expecting any more results - if (!v.keepCallback) { - delete cordova.callbacks[callbackId]; - } - } - - // If error, then display error - else { - console.log("Error: Status="+v.status+" Message="+v.message); - - // If there is a fail callback, then call it now with returned value - if (fail) { - try { - fail(v.message); - } - catch (e1) { - console.log("Error in error callback: "+callbackId+" = "+e1); - } + var callbackId = service + cordova.callbackId++, + argsJson = JSON.stringify(args); + if (success || fail) { + cordova.callbacks[callbackId] = {success:success, fail:fail}; + } - // Clear callback if not expecting any more results - if (!v.keepCallback) { - delete cordova.callbacks[callbackId]; - } - } - return null; - } - } - } catch (e2) { - console.log("Error: "+e2); + if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) { + window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson; + } else { + var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson); + // Explicit cast to string is required on Android 2.1 to convert from + // a Java string to a JS string. + if (messages) { + messages = String(messages); + } + androidExec.processMessages(messages); } } @@ -206,4 +151,52 @@ androidExec.setNativeToJsBridgeMode = function(mode) { } }; +// Processes a single message, as encoded by NativeToJsMessageQueue.java. +function processMessage(message) { + try { + var firstChar = message.charAt(0); + if (firstChar == 'J') { + eval(message.slice(1)); + } else if (firstChar == 'S' || firstChar == 'F') { + var success = firstChar == 'S'; + var keepCallback = message.charAt(1) == '1'; + var spaceIdx = message.indexOf(' ', 2); + var status = +message.slice(2, spaceIdx); + var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1); + var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx); + var payloadKind = message.charAt(nextSpaceIdx + 1); + var payload; + if (payloadKind == 's') { + payload = message.slice(nextSpaceIdx + 2); + } else if (payloadKind == 't') { + payload = true; + } else if (payloadKind == 'f') { + payload = false; + } else if (payloadKind == 'n') { + payload = +message.slice(nextSpaceIdx + 2); + } else { + payload = JSON.parse(message.slice(nextSpaceIdx + 1)); + } + cordova.callbackFromNative(callbackId, success, status, payload, keepCallback); + } else { + console.log("processMessage failed: invalid message:" + message); + } + } catch (e) { + console.log("processMessage failed: Message: " + message); + console.log("processMessage failed: Error: " + e); + console.log("processMessage failed: Stack: " + e.stack); + } +} + +// This is called from the NativeToJsMessageQueue.java. +androidExec.processMessages = function(messages) { + while (messages) { + var spaceIdx = messages.indexOf(' '); + var msgLen = +messages.slice(0, spaceIdx); + var message = messages.substr(spaceIdx + 1, msgLen); + messages = messages.slice(spaceIdx + msgLen + 1); + processMessage(message); + } +}; + module.exports = androidExec; http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/74ae56fa/lib/android/plugin/android/callback.js ---------------------------------------------------------------------- diff --git a/lib/android/plugin/android/callback.js b/lib/android/plugin/android/callback.js index f101655..a87a645 100644 --- a/lib/android/plugin/android/callback.js +++ b/lib/android/plugin/android/callback.js @@ -39,17 +39,8 @@ function startXhr() { // Need to url decode the response var msg = decodeURIComponent(xmlhttp.responseText); - setTimeout(function() { - try { - var t = eval(msg); - } - catch (e) { - // If we're getting an error here, seeing the message will help in debugging - console.log("JSCallback: Message from Server: " + msg); - console.log("JSCallback Error: "+e); - } - }, 1); setTimeout(startXhr, 1); + exec.processMessages(msg); } // If callback ping (used to keep XHR request from timing out) http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/74ae56fa/lib/android/plugin/android/polling.js ---------------------------------------------------------------------- diff --git a/lib/android/plugin/android/polling.js b/lib/android/plugin/android/polling.js index b3b99bb..89b5f0f 100644 --- a/lib/android/plugin/android/polling.js +++ b/lib/android/plugin/android/polling.js @@ -25,15 +25,10 @@ var cordova = require('cordova'), enabled = false; function pollOnce() { - var msg = nativeApiProvider.get().retrieveJsMessages(); + var exec = require('cordova/exec'), + msg = nativeApiProvider.get().retrieveJsMessages(); if (msg) { - try { - eval(""+msg); - } - catch (e) { - console.log("JSCallbackPolling: Message from Server: " + msg); - console.log("JSCallbackPolling Error: "+e); - } + exec.processMessages(msg); return true; } return false; http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/74ae56fa/lib/cordova.js ---------------------------------------------------------------------- diff --git a/lib/cordova.js b/lib/cordova.js index e8fda63..fc4a744 100644 --- a/lib/cordova.js +++ b/lib/cordova.js @@ -192,51 +192,42 @@ var cordova = { /** * Called by native code when returning successful result from an action. - * - * @param callbackId - * @param args */ callbackSuccess: function(callbackId, args) { - if (cordova.callbacks[callbackId]) { - - // If result is to be sent to callback - if (args.status == cordova.callbackStatus.OK) { - try { - if (cordova.callbacks[callbackId].success) { - cordova.callbacks[callbackId].success(args.message); - } - } - catch (e) { - console.log("Error in success callback: "+callbackId+" = "+e); - } - } - - // Clear callback if not expecting any more results - if (!args.keepCallback) { - delete cordova.callbacks[callbackId]; - } + try { + cordova.callbackFromNative(callbackId, true, args.status, args.message, args.keepCallback); + } catch (e) { + console.log("Error in error callback: " + callbackId + " = "+e); } }, /** * Called by native code when returning error result from an action. - * - * @param callbackId - * @param args */ callbackError: function(callbackId, args) { - if (cordova.callbacks[callbackId]) { - try { - if (cordova.callbacks[callbackId].fail) { - cordova.callbacks[callbackId].fail(args.message); - } - } - catch (e) { - console.log("Error in error callback: "+callbackId+" = "+e); + // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative. + // Derive success from status. + try { + cordova.callbackFromNative(callbackId, false, args.status, args.message, args.keepCallback); + } catch (e) { + console.log("Error in error callback: " + callbackId + " = "+e); + } + }, + + /** + * Called by native code when returning the result from an action. + */ + callbackFromNative: function(callbackId, success, status, message, keepCallback) { + var callback = cordova.callbacks[callbackId]; + if (callback) { + if (success && status == cordova.callbackStatus.OK) { + callback.success && callback.success(message); + } else if (!success) { + callback.fail && callback.fail(message); } // Clear callback if not expecting any more results - if (!args.keepCallback) { + if (!keepCallback) { delete cordova.callbacks[callbackId]; } }