// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. var lastError = require('lastError'); var logging = requireNative('logging'); var natives = requireNative('sendRequest'); var processNatives = requireNative('process'); var validate = require('schemaUtils').validate; // All outstanding requests from sendRequest(). var requests = {}; // Used to prevent double Activity Logging for API calls that use both custom // bindings and ExtensionFunctions (via sendRequest). var calledSendRequest = false; // Runs a user-supplied callback safely. function safeCallbackApply(name, request, callback, args) { try { $Function.apply(callback, request, args); } catch (e) { var errorMessage = "Error in response to " + name + ": " + e; if (request.stack && request.stack != '') errorMessage += "\n" + request.stack; console.error(errorMessage); } } // Callback handling. function handleResponse(requestId, name, success, responseList, error) { // The chrome objects we will set lastError on. Really we should only be // setting this on the callback's chrome object, but set on ours too since // it's conceivable that something relies on that. var callerChrome = chrome; try { var request = requests[requestId]; logging.DCHECK(request != null); // lastError needs to be set on the caller's chrome object no matter what, // though chances are it's the same as ours (it will be different when // calling API methods on other contexts). if (request.callback) callerChrome = natives.GetGlobal(request.callback).chrome; lastError.clear(chrome); if (callerChrome !== chrome) lastError.clear(callerChrome); if (!success) { if (!error) error = "Unknown error."; lastError.set(name, error, request.stack, chrome); if (callerChrome !== chrome) lastError.set(name, error, request.stack, callerChrome); } if (request.customCallback) { safeCallbackApply(name, request, request.customCallback, $Array.concat([name, request], responseList)); } if (request.callback) { // Validate callback in debug only -- and only when the // caller has provided a callback. Implementations of api // calls may not return data if they observe the caller // has not provided a callback. if (logging.DCHECK_IS_ON() && !error) { if (!request.callbackSchema.parameters) throw new Error(name + ": no callback schema defined"); validate(responseList, request.callbackSchema.parameters); } safeCallbackApply(name, request, request.callback, responseList); } } finally { delete requests[requestId]; lastError.clear(chrome); if (callerChrome !== chrome) lastError.clear(callerChrome); } }; function getExtensionStackTrace(call_name) { var stack = $String.split(new Error().stack, '\n'); var id = processNatives.GetExtensionId(); // Remove stack frames before and after that weren't associated with the // extension. return $Array.join(stack.filter(function(line) { return line.indexOf(id) != -1; }), '\n'); } function prepareRequest(args, argSchemas) { var request = {}; var argCount = args.length; // Look for callback param. if (argSchemas.length > 0 && argSchemas[argSchemas.length - 1].type == "function") { request.callback = args[args.length - 1]; request.callbackSchema = argSchemas[argSchemas.length - 1]; --argCount; } request.args = []; for (var k = 0; k < argCount; k++) { request.args[k] = args[k]; } return request; } // Send an API request and optionally register a callback. // |optArgs| is an object with optional parameters as follows: // - customCallback: a callback that should be called instead of the standard // callback. // - nativeFunction: the v8 native function to handle the request, or // StartRequest if missing. // - forIOThread: true if this function should be handled on the browser IO // thread. // - preserveNullInObjects: true if it is safe for null to be in objects. function sendRequest(functionName, args, argSchemas, optArgs) { calledSendRequest = true; if (!optArgs) optArgs = {}; var request = prepareRequest(args, argSchemas); request.stack = getExtensionStackTrace(); if (optArgs.customCallback) { request.customCallback = optArgs.customCallback; } var nativeFunction = optArgs.nativeFunction || natives.StartRequest; var requestId = natives.GetNextRequestId(); request.id = requestId; requests[requestId] = request; var hasCallback = request.callback || optArgs.customCallback; return nativeFunction(functionName, request.args, requestId, hasCallback, optArgs.forIOThread, optArgs.preserveNullInObjects); } function getCalledSendRequest() { return calledSendRequest; } function clearCalledSendRequest() { calledSendRequest = false; } exports.sendRequest = sendRequest; exports.getCalledSendRequest = getCalledSendRequest; exports.clearCalledSendRequest = clearCalledSendRequest; // Called by C++. exports.handleResponse = handleResponse;