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 chromesForLastError = [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       var chromeForCallback = natives.GetGlobal(request.callback).chrome;
     46       if (chromeForCallback != chrome)
     47         $Array.push(chromesForLastError, chromeForCallback);
     48     }
     49 
     50     $Array.forEach(chromesForLastError, function(c) {lastError.clear(c)});
     51     if (!success) {
     52       if (!error)
     53         error = "Unknown error.";
     54       $Array.forEach(chromesForLastError, function(c) {
     55         lastError.set(name, error, request.stack, c)
     56       });
     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     $Array.forEach(chromesForLastError, function(c) {lastError.clear(c)});
     81   }
     82 };
     83 
     84 function getExtensionStackTrace(call_name) {
     85   var stack = $String.split(new Error().stack, '\n');
     86 
     87   // Remove stack frames before and after that weren't associated with the
     88   // extension.
     89   var id = processNatives.GetExtensionId();
     90   while (stack.length > 0 && stack[0].indexOf(id) == -1)
     91     stack.shift();
     92   while (stack.length > 0 && stack[stack.length - 1].indexOf(id) == -1)
     93     stack.pop();
     94 
     95   return stack.join('\n');
     96 }
     97 
     98 function prepareRequest(args, argSchemas) {
     99   var request = {};
    100   var argCount = args.length;
    101 
    102   // Look for callback param.
    103   if (argSchemas.length > 0 &&
    104       argSchemas[argSchemas.length - 1].type == "function") {
    105     request.callback = args[args.length - 1];
    106     request.callbackSchema = argSchemas[argSchemas.length - 1];
    107     --argCount;
    108   }
    109 
    110   request.args = [];
    111   for (var k = 0; k < argCount; k++) {
    112     request.args[k] = args[k];
    113   }
    114 
    115   return request;
    116 }
    117 
    118 // Send an API request and optionally register a callback.
    119 // |optArgs| is an object with optional parameters as follows:
    120 // - customCallback: a callback that should be called instead of the standard
    121 //   callback.
    122 // - nativeFunction: the v8 native function to handle the request, or
    123 //   StartRequest if missing.
    124 // - forIOThread: true if this function should be handled on the browser IO
    125 //   thread.
    126 // - preserveNullInObjects: true if it is safe for null to be in objects.
    127 function sendRequest(functionName, args, argSchemas, optArgs) {
    128   calledSendRequest = true;
    129   if (!optArgs)
    130     optArgs = {};
    131   var request = prepareRequest(args, argSchemas);
    132   request.stack = getExtensionStackTrace();
    133   if (optArgs.customCallback) {
    134     request.customCallback = optArgs.customCallback;
    135   }
    136 
    137   var nativeFunction = optArgs.nativeFunction || natives.StartRequest;
    138 
    139   var requestId = natives.GetNextRequestId();
    140   request.id = requestId;
    141   requests[requestId] = request;
    142 
    143   var hasCallback = request.callback || optArgs.customCallback;
    144   return nativeFunction(functionName,
    145                         request.args,
    146                         requestId,
    147                         hasCallback,
    148                         optArgs.forIOThread,
    149                         optArgs.preserveNullInObjects);
    150 }
    151 
    152 function getCalledSendRequest() {
    153   return calledSendRequest;
    154 }
    155 
    156 function clearCalledSendRequest() {
    157   calledSendRequest = false;
    158 }
    159 
    160 exports.sendRequest = sendRequest;
    161 exports.getCalledSendRequest = getCalledSendRequest;
    162 exports.clearCalledSendRequest = clearCalledSendRequest;
    163 
    164 // Called by C++.
    165 exports.handleResponse = handleResponse;
    166