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