1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 var lastError = require('lastError'); 6 var logging = requireNative('logging'); 7 var natives = requireNative('sendRequest'); 8 var processNatives = requireNative('process'); 9 var validate = require('schemaUtils').validate; 10 11 // All outstanding requests from sendRequest(). 12 var requests = {}; 13 14 // Used to prevent double Activity Logging for API calls that use both custom 15 // bindings and ExtensionFunctions (via sendRequest). 16 var calledSendRequest = false; 17 18 // Runs a user-supplied callback safely. 19 function safeCallbackApply(name, request, callback, args) { 20 try { 21 $Function.apply(callback, request, args); 22 } catch (e) { 23 var errorMessage = "Error in response to " + name + ": " + e; 24 if (request.stack && request.stack != '') 25 errorMessage += "\n" + request.stack; 26 console.error(errorMessage); 27 } 28 } 29 30 // Callback handling. 31 function handleResponse(requestId, name, success, responseList, error) { 32 // The chrome objects we will set lastError on. Really we should only be 33 // setting this on the callback's chrome object, but set on ours too since 34 // it's conceivable that something relies on that. 35 var callerChrome = chrome; 36 37 try { 38 var request = requests[requestId]; 39 logging.DCHECK(request != null); 40 41 // lastError needs to be set on the caller's chrome object no matter what, 42 // though chances are it's the same as ours (it will be different when 43 // calling API methods on other contexts). 44 if (request.callback) 45 callerChrome = natives.GetGlobal(request.callback).chrome; 46 47 lastError.clear(chrome); 48 if (callerChrome !== chrome) 49 lastError.clear(callerChrome); 50 51 if (!success) { 52 if (!error) 53 error = "Unknown error."; 54 lastError.set(name, error, request.stack, chrome); 55 if (callerChrome !== chrome) 56 lastError.set(name, error, request.stack, callerChrome); 57 } 58 59 if (request.customCallback) { 60 safeCallbackApply(name, 61 request, 62 request.customCallback, 63 $Array.concat([name, request], responseList)); 64 } 65 66 if (request.callback) { 67 // Validate callback in debug only -- and only when the 68 // caller has provided a callback. Implementations of api 69 // calls may not return data if they observe the caller 70 // has not provided a callback. 71 if (logging.DCHECK_IS_ON() && !error) { 72 if (!request.callbackSchema.parameters) 73 throw new Error(name + ": no callback schema defined"); 74 validate(responseList, request.callbackSchema.parameters); 75 } 76 safeCallbackApply(name, request, request.callback, responseList); 77 } 78 } finally { 79 delete requests[requestId]; 80 lastError.clear(chrome); 81 if (callerChrome !== chrome) 82 lastError.clear(callerChrome); 83 } 84 }; 85 86 function getExtensionStackTrace(call_name) { 87 var stack = $String.split(new Error().stack, '\n'); 88 var id = processNatives.GetExtensionId(); 89 90 // Remove stack frames before and after that weren't associated with the 91 // extension. 92 return $Array.join(stack.filter(function(line) { 93 return line.indexOf(id) != -1; 94 }), '\n'); 95 } 96 97 function prepareRequest(args, argSchemas) { 98 var request = {}; 99 var argCount = args.length; 100 101 // Look for callback param. 102 if (argSchemas.length > 0 && 103 argSchemas[argSchemas.length - 1].type == "function") { 104 request.callback = args[args.length - 1]; 105 request.callbackSchema = argSchemas[argSchemas.length - 1]; 106 --argCount; 107 } 108 109 request.args = []; 110 for (var k = 0; k < argCount; k++) { 111 request.args[k] = args[k]; 112 } 113 114 return request; 115 } 116 117 // Send an API request and optionally register a callback. 118 // |optArgs| is an object with optional parameters as follows: 119 // - customCallback: a callback that should be called instead of the standard 120 // callback. 121 // - nativeFunction: the v8 native function to handle the request, or 122 // StartRequest if missing. 123 // - forIOThread: true if this function should be handled on the browser IO 124 // thread. 125 // - preserveNullInObjects: true if it is safe for null to be in objects. 126 function sendRequest(functionName, args, argSchemas, optArgs) { 127 calledSendRequest = true; 128 if (!optArgs) 129 optArgs = {}; 130 var request = prepareRequest(args, argSchemas); 131 request.stack = getExtensionStackTrace(); 132 if (optArgs.customCallback) { 133 request.customCallback = optArgs.customCallback; 134 } 135 136 var nativeFunction = optArgs.nativeFunction || natives.StartRequest; 137 138 var requestId = natives.GetNextRequestId(); 139 request.id = requestId; 140 requests[requestId] = request; 141 142 var hasCallback = request.callback || optArgs.customCallback; 143 return nativeFunction(functionName, 144 request.args, 145 requestId, 146 hasCallback, 147 optArgs.forIOThread, 148 optArgs.preserveNullInObjects); 149 } 150 151 function getCalledSendRequest() { 152 return calledSendRequest; 153 } 154 155 function clearCalledSendRequest() { 156 calledSendRequest = false; 157 } 158 159 exports.sendRequest = sendRequest; 160 exports.getCalledSendRequest = getCalledSendRequest; 161 exports.clearCalledSendRequest = clearCalledSendRequest; 162 163 // Called by C++. 164 exports.handleResponse = handleResponse; 165