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 GetAvailability = requireNative('v8_context').GetAvailability;
      6 var GetGlobal = requireNative('sendRequest').GetGlobal;
      7 
      8 // Utility for setting chrome.*.lastError.
      9 //
     10 // A utility here is useful for two reasons:
     11 //  1. For backwards compatibility we need to set chrome.extension.lastError,
     12 //     but not all contexts actually have access to the extension namespace.
     13 //  2. When calling across contexts, the global object that gets lastError set
     14 //     needs to be that of the caller. We force callers to explicitly specify
     15 //     the chrome object to try to prevent bugs here.
     16 
     17 /**
     18  * Sets the last error for |name| on |targetChrome| to |message| with an
     19  * optional |stack|.
     20  */
     21 function set(name, message, stack, targetChrome) {
     22   var errorMessage = name + ': ' + message;
     23   if (stack != null && stack != '')
     24     errorMessage += '\n' + stack;
     25 
     26   if (!targetChrome)
     27     throw new Error('No chrome object to set error: ' + errorMessage);
     28   clear(targetChrome);  // in case somebody has set a sneaky getter/setter
     29 
     30   var errorObject = { message: message };
     31   if (GetAvailability('extension.lastError').is_available)
     32     targetChrome.extension.lastError = errorObject;
     33 
     34   assertRuntimeIsAvailable();
     35 
     36   // We check to see if developers access runtime.lastError in order to decide
     37   // whether or not to log it in the (error) console.
     38   privates(targetChrome.runtime).accessedLastError = false;
     39   $Object.defineProperty(targetChrome.runtime, 'lastError', {
     40       configurable: true,
     41       get: function() {
     42         privates(targetChrome.runtime).accessedLastError = true;
     43         return errorObject;
     44       },
     45       set: function(error) {
     46         errorObject = errorObject;
     47       }});
     48 };
     49 
     50 /**
     51  * Check if anyone has checked chrome.runtime.lastError since it was set.
     52  * @param {Object} targetChrome the Chrome object to check.
     53  * @return boolean True if the lastError property was set.
     54  */
     55 function hasAccessed(targetChrome) {
     56   assertRuntimeIsAvailable();
     57   return privates(targetChrome.runtime).accessedLastError === true;
     58 }
     59 
     60 /**
     61  * Check whether there is an error set on |targetChrome| without setting
     62  * |accessedLastError|.
     63  * @param {Object} targetChrome the Chrome object to check.
     64  * @return boolean Whether lastError has been set.
     65  */
     66 function hasError(targetChrome) {
     67   if (!targetChrome)
     68     throw new Error('No target chrome to check');
     69 
     70   assertRuntimeIsAvailable();
     71   if ('lastError' in targetChrome.runtime)
     72     return true;
     73 
     74   return false;
     75 };
     76 
     77 /**
     78  * Clears the last error on |targetChrome|.
     79  */
     80 function clear(targetChrome) {
     81   if (!targetChrome)
     82     throw new Error('No target chrome to clear error');
     83 
     84   if (GetAvailability('extension.lastError').is_available)
     85    delete targetChrome.extension.lastError;
     86 
     87   assertRuntimeIsAvailable();
     88   delete targetChrome.runtime.lastError;
     89   delete privates(targetChrome.runtime).accessedLastError;
     90 };
     91 
     92 function assertRuntimeIsAvailable() {
     93   // chrome.runtime should always be available, but maybe it's disappeared for
     94   // some reason? Add debugging for http://crbug.com/258526.
     95   var runtimeAvailability = GetAvailability('runtime.lastError');
     96   if (!runtimeAvailability.is_available) {
     97     throw new Error('runtime.lastError is not available: ' +
     98                     runtimeAvailability.message);
     99   }
    100   if (!chrome.runtime)
    101     throw new Error('runtime namespace is null or undefined');
    102 }
    103 
    104 /**
    105  * Runs |callback(args)| with last error args as in set().
    106  *
    107  * The target chrome object is the global object's of the callback, so this
    108  * method won't work if the real callback has been wrapped (etc).
    109  */
    110 function run(name, message, stack, callback, args) {
    111   var targetChrome = GetGlobal(callback).chrome;
    112   set(name, message, stack, targetChrome);
    113   try {
    114     $Function.apply(callback, undefined, args);
    115   } finally {
    116     clear(targetChrome);
    117   }
    118 }
    119 
    120 exports.clear = clear;
    121 exports.hasAccessed = hasAccessed;
    122 exports.hasError = hasError;
    123 exports.set = set;
    124 exports.run = run;
    125