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