blob: 14836b70067ec5d482b7d8a72e3ffef372d0cb68 [file] [log] [blame]
// Copyright 2014 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 GetAvailability = requireNative('v8_context').GetAvailability;
var GetGlobal = requireNative('sendRequest').GetGlobal;
// Utility for setting chrome.*.lastError.
//
// A utility here is useful for two reasons:
// 1. For backwards compatibility we need to set chrome.extension.lastError,
// but not all contexts actually have access to the extension namespace.
// 2. When calling across contexts, the global object that gets lastError set
// needs to be that of the caller. We force callers to explicitly specify
// the chrome object to try to prevent bugs here.
/**
* Sets the last error for |name| on |targetChrome| to |message| with an
* optional |stack|.
*/
function set(name, message, stack, targetChrome) {
if (!targetChrome) {
var errorMessage = name + ': ' + message;
if (stack != null && stack != '')
errorMessage += '\n' + stack;
throw new Error('No chrome object to set error: ' + errorMessage);
}
clear(targetChrome); // in case somebody has set a sneaky getter/setter
var errorObject = { message: message };
if (GetAvailability('extension.lastError').is_available)
targetChrome.extension.lastError = errorObject;
assertRuntimeIsAvailable();
// We check to see if developers access runtime.lastError in order to decide
// whether or not to log it in the (error) console.
privates(targetChrome.runtime).accessedLastError = false;
$Object.defineProperty(targetChrome.runtime, 'lastError', {
configurable: true,
get: function() {
privates(targetChrome.runtime).accessedLastError = true;
return errorObject;
},
set: function(error) {
errorObject = errorObject;
}});
};
/**
* Check if anyone has checked chrome.runtime.lastError since it was set.
* @param {Object} targetChrome the Chrome object to check.
* @return boolean True if the lastError property was set.
*/
function hasAccessed(targetChrome) {
assertRuntimeIsAvailable();
return privates(targetChrome.runtime).accessedLastError === true;
}
/**
* Check whether there is an error set on |targetChrome| without setting
* |accessedLastError|.
* @param {Object} targetChrome the Chrome object to check.
* @return boolean Whether lastError has been set.
*/
function hasError(targetChrome) {
if (!targetChrome)
throw new Error('No target chrome to check');
assertRuntimeIsAvailable();
return $Object.hasOwnProperty(targetChrome.runtime, 'lastError');
};
/**
* Clears the last error on |targetChrome|.
*/
function clear(targetChrome) {
if (!targetChrome)
throw new Error('No target chrome to clear error');
if (GetAvailability('extension.lastError').is_available)
delete targetChrome.extension.lastError;
assertRuntimeIsAvailable();
delete targetChrome.runtime.lastError;
delete privates(targetChrome.runtime).accessedLastError;
};
function assertRuntimeIsAvailable() {
// chrome.runtime should always be available, but maybe it's disappeared for
// some reason? Add debugging for http://crbug.com/258526.
var runtimeAvailability = GetAvailability('runtime.lastError');
if (!runtimeAvailability.is_available) {
throw new Error('runtime.lastError is not available: ' +
runtimeAvailability.message);
}
if (!chrome.runtime)
throw new Error('runtime namespace is null or undefined');
}
/**
* Runs |callback(args)| with last error args as in set().
*
* The target chrome object is the global object's of the callback, so this
* method won't work if the real callback has been wrapped (etc).
*/
function run(name, message, stack, callback, args) {
var global = GetGlobal(callback);
var targetChrome = global && global.chrome;
set(name, message, stack, targetChrome);
try {
$Function.apply(callback, undefined, args);
} finally {
reportIfUnchecked(name, targetChrome, stack);
clear(targetChrome);
}
}
/**
* Checks whether chrome.runtime.lastError has been accessed if set.
* If it was set but not accessed, the error is reported to the console.
*
* @param {string=} name - name of API.
* @param {Object} targetChrome - the Chrome object to check.
* @param {string=} stack - Stack trace of the call up to the error.
*/
function reportIfUnchecked(name, targetChrome, stack) {
if (hasAccessed(targetChrome) || !hasError(targetChrome))
return;
var message = targetChrome.runtime.lastError.message;
console.error("Unchecked runtime.lastError while running " +
(name || "unknown") + ": " + message + (stack ? "\n" + stack : ""));
}
exports.$set('clear', clear);
exports.$set('hasAccessed', hasAccessed);
exports.$set('hasError', hasError);
exports.$set('set', set);
exports.$set('run', run);
exports.$set('reportIfUnchecked', reportIfUnchecked);