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 5var GetAvailability = requireNative('v8_context').GetAvailability; 6var 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 */ 21function 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 */ 55function 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 */ 66function 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 */ 80function 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 92function 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 */ 110function 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 120exports.clear = clear; 121exports.hasAccessed = hasAccessed; 122exports.hasError = hasError; 123exports.set = set; 124exports.run = run; 125