Home | History | Annotate | Download | only in extensions
      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