Skip to content
Permalink
Browse files
CB-5988 android: Allow exec() only from file: or start-up URL's domain
  • Loading branch information
agrieve committed Jul 4, 2014
1 parent 0467512 commit 558e8d55db0699da095f1973de71dcf97a6184d9
Showing 4 changed files with 43 additions and 17 deletions.
@@ -19,17 +19,17 @@

/**
* Implements the API of ExposedJsApi.java, but uses prompt() to communicate.
* This is used only on the 2.3 simulator, where addJavascriptInterface() is broken.
* This is used pre-JellyBean, where addJavascriptInterface() is disabled.
*/

module.exports = {
exec: function(service, action, callbackId, argsJson) {
return prompt(argsJson, 'gap:'+JSON.stringify([service, action, callbackId]));
exec: function(bridgeSecret, service, action, callbackId, argsJson) {
return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]));
},
setNativeToJsBridgeMode: function(value) {
prompt(value, 'gap_bridge_mode:');
setNativeToJsBridgeMode: function(bridgeSecret, value) {
prompt(value, 'gap_bridge_mode:' + bridgeSecret);
},
retrieveJsMessages: function(fromOnlineEvent) {
return prompt(+fromOnlineEvent, 'gap_poll:');
retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) {
return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret);
}
};
@@ -37,6 +37,7 @@ var cordova = require('cordova'),
nativeApiProvider = require('cordova/android/nativeapiprovider'),
utils = require('cordova/utils'),
base64 = require('cordova/base64'),
channel = require('cordova/channel'),
jsToNativeModes = {
PROMPT: 0,
JS_OBJECT: 1,
@@ -63,9 +64,17 @@ var cordova = require('cordova'),
jsToNativeBridgeMode, // Set lazily.
nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT,
pollEnabled = false,
messagesFromNative = [];
messagesFromNative = [],
bridgeSecret = -1;

function androidExec(success, fail, service, action, args) {
if (bridgeSecret < 0) {
// If we ever catch this firing, we'll need to queue up exec()s
// and fire them once we get a secret. For now, I don't think
// it's possible for exec() to be called since plugins are parsed but
// not run until until after onNativeReady.
throw new Error('exec() called without bridgeSecret');
}
// Set default bridge modes if they have not already been set.
// By default, we use the failsafe, since addJavascriptInterface breaks too often
if (jsToNativeBridgeMode === undefined) {
@@ -89,7 +98,7 @@ function androidExec(success, fail, service, action, args) {
if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
} else {
var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson);
var messages = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
// If argsJson was received by Java as null, try again with the PROMPT bridge mode.
// This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666.
if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {
@@ -103,12 +112,22 @@ function androidExec(success, fail, service, action, args) {
}
}

androidExec.init = function() {
bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode);
channel.onNativeReady.fire();
};

function pollOnceFromOnlineEvent() {
pollOnce(true);
}

function pollOnce(opt_fromOnlineEvent) {
var msg = nativeApiProvider.get().retrieveJsMessages(!!opt_fromOnlineEvent);
if (bridgeSecret < 0) {
// This can happen when the NativeToJsMessageQueue resets the online state on page transitions.
// We know there's nothing to retrieve, so no need to poll.
return;
}
var msg = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent);
androidExec.processMessages(msg);
}

@@ -158,7 +177,10 @@ androidExec.setNativeToJsBridgeMode = function(mode) {

nativeToJsBridgeMode = mode;
// Tell the native side to switch modes.
nativeApiProvider.get().setNativeToJsBridgeMode(mode);
// Otherwise, it will be set by androidExec.init()
if (bridgeSecret >= 0) {
nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode);
}

if (mode == nativeToJsModes.POLLING) {
pollEnabled = true;
@@ -27,10 +27,8 @@ module.exports = {
exec = require('cordova/exec'),
modulemapper = require('cordova/modulemapper');

// Tell the native code that a page change has occurred.
exec(null, null, 'PluginManager', 'startup', []);
// Tell the JS that the native side is ready.
channel.onNativeReady.fire();
// Get the shared secret needed to use the bridge.
exec.init();

// TODO: Extract this as a proper plugin.
modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
@@ -37,6 +37,10 @@ describe('exec.processMessages', function () {
// Avoid a log message warning about the lack of _nativeApi.
exec.setJsToNativeBridgeMode(exec.jsToNativeModes.PROMPT);
nativeApiProvider.set(nativeApi);
var origPrompt = typeof prompt == 'undefined' ? undefined : prompt;
prompt = function() { return 1234; };
exec.init();
prompt = origPrompt;
});

afterEach(function() {
@@ -59,7 +63,8 @@ describe('exec.processMessages', function () {
it('should process messages in order even when called recursively', function() {
var firstCallbackId = null;
var callCount = 0;
nativeApi.exec.andCallFake(function(service, action, callbackId, argsJson) {
nativeApi.exec.andCallFake(function(secret, service, action, callbackId, argsJson) {
expect(secret).toBe(1234);
++callCount;
if (callCount == 1) {
firstCallbackId = callbackId;
@@ -95,7 +100,8 @@ describe('exec.processMessages', function () {
});
});
it('should process messages asynchronously', function() {
nativeApi.exec.andCallFake(function(service, action, callbackId, argsJson) {
nativeApi.exec.andCallFake(function(secret, service, action, callbackId, argsJson) {
expect(secret).toBe(1234);
return createCallbackMessage(true, false, 1, callbackId, 'stwo');
});

0 comments on commit 558e8d5

Please sign in to comment.