Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

exec.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. *
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. *
  20. */
  21. /**
  22. * Execute a cordova command. It is up to the native side whether this action
  23. * is synchronous or asynchronous. The native side can return:
  24. * Synchronous: PluginResult object as a JSON string
  25. * Asynchronous: Empty string ""
  26. * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
  27. * depending upon the result of the action.
  28. *
  29. * @param {Function} success The success callback
  30. * @param {Function} fail The fail callback
  31. * @param {String} service The name of the service to use
  32. * @param {String} action Action to be run in cordova
  33. * @param {String[]} [args] Zero or more arguments to pass to the method
  34. */
  35. var cordova = require('cordova'),
  36. nativeApiProvider = require('cordova/android/nativeapiprovider'),
  37. utils = require('cordova/utils'),
  38. base64 = require('cordova/base64'),
  39. channel = require('cordova/channel'),
  40. jsToNativeModes = {
  41. PROMPT: 0,
  42. JS_OBJECT: 1
  43. },
  44. nativeToJsModes = {
  45. // Polls for messages using the JS->Native bridge.
  46. POLLING: 0,
  47. // For LOAD_URL to be viable, it would need to have a work-around for
  48. // the bug where the soft-keyboard gets dismissed when a message is sent.
  49. LOAD_URL: 1,
  50. // For the ONLINE_EVENT to be viable, it would need to intercept all event
  51. // listeners (both through addEventListener and window.ononline) as well
  52. // as set the navigator property itself.
  53. ONLINE_EVENT: 2,
  54. EVAL_BRIDGE: 3
  55. },
  56. jsToNativeBridgeMode, // Set lazily.
  57. nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE,
  58. pollEnabled = false,
  59. bridgeSecret = -1;
  60. var messagesFromNative = [];
  61. var isProcessing = false;
  62. var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
  63. var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };
  64. function androidExec(success, fail, service, action, args) {
  65. if (bridgeSecret < 0) {
  66. // If we ever catch this firing, we'll need to queue up exec()s
  67. // and fire them once we get a secret. For now, I don't think
  68. // it's possible for exec() to be called since plugins are parsed but
  69. // not run until until after onNativeReady.
  70. throw new Error('exec() called without bridgeSecret');
  71. }
  72. // Set default bridge modes if they have not already been set.
  73. // By default, we use the failsafe, since addJavascriptInterface breaks too often
  74. if (jsToNativeBridgeMode === undefined) {
  75. androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
  76. }
  77. // If args is not provided, default to an empty array
  78. args = args || [];
  79. // Process any ArrayBuffers in the args into a string.
  80. for (var i = 0; i < args.length; i++) {
  81. if (utils.typeName(args[i]) == 'ArrayBuffer') {
  82. args[i] = base64.fromArrayBuffer(args[i]);
  83. }
  84. }
  85. var callbackId = service + cordova.callbackId++,
  86. argsJson = JSON.stringify(args);
  87. if (success || fail) {
  88. cordova.callbacks[callbackId] = {success:success, fail:fail};
  89. }
  90. var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
  91. // If argsJson was received by Java as null, try again with the PROMPT bridge mode.
  92. // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666.
  93. if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") {
  94. androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
  95. androidExec(success, fail, service, action, args);
  96. androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
  97. } else if (msgs) {
  98. messagesFromNative.push(msgs);
  99. // Always process async to avoid exceptions messing up stack.
  100. nextTick(processMessages);
  101. }
  102. }
  103. androidExec.init = function() {
  104. bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode);
  105. channel.onNativeReady.fire();
  106. };
  107. function pollOnceFromOnlineEvent() {
  108. pollOnce(true);
  109. }
  110. function pollOnce(opt_fromOnlineEvent) {
  111. if (bridgeSecret < 0) {
  112. // This can happen when the NativeToJsMessageQueue resets the online state on page transitions.
  113. // We know there's nothing to retrieve, so no need to poll.
  114. return;
  115. }
  116. var msgs = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent);
  117. if (msgs) {
  118. messagesFromNative.push(msgs);
  119. // Process sync since we know we're already top-of-stack.
  120. processMessages();
  121. }
  122. }
  123. function pollingTimerFunc() {
  124. if (pollEnabled) {
  125. pollOnce();
  126. setTimeout(pollingTimerFunc, 50);
  127. }
  128. }
  129. function hookOnlineApis() {
  130. function proxyEvent(e) {
  131. cordova.fireWindowEvent(e.type);
  132. }
  133. // The network module takes care of firing online and offline events.
  134. // It currently fires them only on document though, so we bridge them
  135. // to window here (while first listening for exec()-releated online/offline
  136. // events).
  137. window.addEventListener('online', pollOnceFromOnlineEvent, false);
  138. window.addEventListener('offline', pollOnceFromOnlineEvent, false);
  139. cordova.addWindowEventHandler('online');
  140. cordova.addWindowEventHandler('offline');
  141. document.addEventListener('online', proxyEvent, false);
  142. document.addEventListener('offline', proxyEvent, false);
  143. }
  144. hookOnlineApis();
  145. androidExec.jsToNativeModes = jsToNativeModes;
  146. androidExec.nativeToJsModes = nativeToJsModes;
  147. androidExec.setJsToNativeBridgeMode = function(mode) {
  148. if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
  149. mode = jsToNativeModes.PROMPT;
  150. }
  151. nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);
  152. jsToNativeBridgeMode = mode;
  153. };
  154. androidExec.setNativeToJsBridgeMode = function(mode) {
  155. if (mode == nativeToJsBridgeMode) {
  156. return;
  157. }
  158. if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {
  159. pollEnabled = false;
  160. }
  161. nativeToJsBridgeMode = mode;
  162. // Tell the native side to switch modes.
  163. // Otherwise, it will be set by androidExec.init()
  164. if (bridgeSecret >= 0) {
  165. nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode);
  166. }
  167. if (mode == nativeToJsModes.POLLING) {
  168. pollEnabled = true;
  169. setTimeout(pollingTimerFunc, 1);
  170. }
  171. };
  172. function buildPayload(payload, message) {
  173. var payloadKind = message.charAt(0);
  174. if (payloadKind == 's') {
  175. payload.push(message.slice(1));
  176. } else if (payloadKind == 't') {
  177. payload.push(true);
  178. } else if (payloadKind == 'f') {
  179. payload.push(false);
  180. } else if (payloadKind == 'N') {
  181. payload.push(null);
  182. } else if (payloadKind == 'n') {
  183. payload.push(+message.slice(1));
  184. } else if (payloadKind == 'A') {
  185. var data = message.slice(1);
  186. payload.push(base64.toArrayBuffer(data));
  187. } else if (payloadKind == 'S') {
  188. payload.push(window.atob(message.slice(1)));
  189. } else if (payloadKind == 'M') {
  190. var multipartMessages = message.slice(1);
  191. while (multipartMessages !== "") {
  192. var spaceIdx = multipartMessages.indexOf(' ');
  193. var msgLen = +multipartMessages.slice(0, spaceIdx);
  194. var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen);
  195. multipartMessages = multipartMessages.slice(spaceIdx + msgLen + 1);
  196. buildPayload(payload, multipartMessage);
  197. }
  198. } else {
  199. payload.push(JSON.parse(message));
  200. }
  201. }
  202. // Processes a single message, as encoded by NativeToJsMessageQueue.java.
  203. function processMessage(message) {
  204. var firstChar = message.charAt(0);
  205. if (firstChar == 'J') {
  206. // This is deprecated on the .java side. It doesn't work with CSP enabled.
  207. eval(message.slice(1));
  208. } else if (firstChar == 'S' || firstChar == 'F') {
  209. var success = firstChar == 'S';
  210. var keepCallback = message.charAt(1) == '1';
  211. var spaceIdx = message.indexOf(' ', 2);
  212. var status = +message.slice(2, spaceIdx);
  213. var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
  214. var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx);
  215. var payloadMessage = message.slice(nextSpaceIdx + 1);
  216. var payload = [];
  217. buildPayload(payload, payloadMessage);
  218. cordova.callbackFromNative(callbackId, success, status, payload, keepCallback);
  219. } else {
  220. console.log("processMessage failed: invalid message: " + JSON.stringify(message));
  221. }
  222. }
  223. function processMessages() {
  224. // Check for the reentrant case.
  225. if (isProcessing) {
  226. return;
  227. }
  228. if (messagesFromNative.length === 0) {
  229. return;
  230. }
  231. isProcessing = true;
  232. try {
  233. var msg = popMessageFromQueue();
  234. // The Java side can send a * message to indicate that it
  235. // still has messages waiting to be retrieved.
  236. if (msg == '*' && messagesFromNative.length === 0) {
  237. nextTick(pollOnce);
  238. return;
  239. }
  240. processMessage(msg);
  241. } finally {
  242. isProcessing = false;
  243. if (messagesFromNative.length > 0) {
  244. nextTick(processMessages);
  245. }
  246. }
  247. }
  248. function popMessageFromQueue() {
  249. var messageBatch = messagesFromNative.shift();
  250. if (messageBatch == '*') {
  251. return '*';
  252. }
  253. var spaceIdx = messageBatch.indexOf(' ');
  254. var msgLen = +messageBatch.slice(0, spaceIdx);
  255. var message = messageBatch.substr(spaceIdx + 1, msgLen);
  256. messageBatch = messageBatch.slice(spaceIdx + msgLen + 1);
  257. if (messageBatch) {
  258. messagesFromNative.unshift(messageBatch);
  259. }
  260. return message;
  261. }
  262. module.exports = androidExec;