• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5var handleUncaughtException = require('uncaught_exception_handler').handle;
6var lastError = require('lastError');
7var logging = requireNative('logging');
8var natives = requireNative('sendRequest');
9var processNatives = requireNative('process');
10var validate = require('schemaUtils').validate;
11
12// All outstanding requests from sendRequest().
13var requests = {};
14
15// Used to prevent double Activity Logging for API calls that use both custom
16// bindings and ExtensionFunctions (via sendRequest).
17var calledSendRequest = false;
18
19// Runs a user-supplied callback safely.
20function 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.
32function 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
98function 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
109function 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.
138function 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
163function getCalledSendRequest() {
164  return calledSendRequest;
165}
166
167function clearCalledSendRequest() {
168  calledSendRequest = false;
169}
170
171exports.sendRequest = sendRequest;
172exports.getCalledSendRequest = getCalledSendRequest;
173exports.clearCalledSendRequest = clearCalledSendRequest;
174exports.safeCallbackApply = safeCallbackApply;
175exports.getExtensionStackTrace = getExtensionStackTrace;
176
177// Called by C++.
178exports.handleResponse = handleResponse;
179