[email protected] | 8f857ef8 | 2014-06-04 23:46:16 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | 120028b9c | 2012-07-03 01:32:24 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 5 | var GetAvailability = requireNative('v8_context').GetAvailability; |
| 6 | var GetGlobal = requireNative('sendRequest').GetGlobal; |
[email protected] | b9c34c8 | 2012-07-10 22:03:55 | [diff] [blame] | 7 | |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 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 | /** |
[email protected] | 65cda7572 | 2013-04-06 04:51:37 | [diff] [blame] | 18 | * Sets the last error for |name| on |targetChrome| to |message| with an |
| 19 | * optional |stack|. |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 20 | */ |
[email protected] | 65cda7572 | 2013-04-06 04:51:37 | [diff] [blame] | 21 | function set(name, message, stack, targetChrome) { |
[email protected] | 65cda7572 | 2013-04-06 04:51:37 | [diff] [blame] | 22 | var errorMessage = name + ': ' + message; |
| 23 | if (stack != null && stack != '') |
| 24 | errorMessage += '\n' + stack; |
[email protected] | 9c813da | 2013-07-19 20:35:21 | [diff] [blame] | 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 | |
[email protected] | 65cda7572 | 2013-04-06 04:51:37 | [diff] [blame] | 30 | var errorObject = { message: message }; |
[email protected] | 6629635 | 2014-05-31 03:45:48 | [diff] [blame] | 31 | if (GetAvailability('extension.lastError').is_available) |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 32 | targetChrome.extension.lastError = errorObject; |
[email protected] | 9c813da | 2013-07-19 20:35:21 | [diff] [blame] | 33 | |
| 34 | assertRuntimeIsAvailable(); |
[email protected] | 52b06286 | 2014-04-23 00:31:44 | [diff] [blame] | 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 | }}); |
[email protected] | 120028b9c | 2012-07-03 01:32:24 | [diff] [blame] | 48 | }; |
| 49 | |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 50 | /** |
[email protected] | 52b06286 | 2014-04-23 00:31:44 | [diff] [blame] | 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 | /** |
[email protected] | a33425c3c | 2014-05-15 19:22:34 | [diff] [blame] | 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 | /** |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 78 | * Clears the last error on |targetChrome|. |
| 79 | */ |
| 80 | function clear(targetChrome) { |
[email protected] | 9c813da | 2013-07-19 20:35:21 | [diff] [blame] | 81 | if (!targetChrome) |
| 82 | throw new Error('No target chrome to clear error'); |
| 83 | |
[email protected] | 6629635 | 2014-05-31 03:45:48 | [diff] [blame] | 84 | if (GetAvailability('extension.lastError').is_available) |
[email protected] | f0e9ac55 | 2014-05-07 14:26:50 | [diff] [blame] | 85 | delete targetChrome.extension.lastError; |
[email protected] | 9c813da | 2013-07-19 20:35:21 | [diff] [blame] | 86 | |
| 87 | assertRuntimeIsAvailable(); |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 88 | delete targetChrome.runtime.lastError; |
[email protected] | 52b06286 | 2014-04-23 00:31:44 | [diff] [blame] | 89 | delete privates(targetChrome.runtime).accessedLastError; |
[email protected] | 120028b9c | 2012-07-03 01:32:24 | [diff] [blame] | 90 | }; |
| 91 | |
[email protected] | 9c813da | 2013-07-19 20:35:21 | [diff] [blame] | 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 | |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 104 | /** |
[email protected] | 65cda7572 | 2013-04-06 04:51:37 | [diff] [blame] | 105 | * Runs |callback(args)| with last error args as in set(). |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 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 | */ |
[email protected] | 65cda7572 | 2013-04-06 04:51:37 | [diff] [blame] | 110 | function run(name, message, stack, callback, args) { |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 111 | var targetChrome = GetGlobal(callback).chrome; |
[email protected] | 65cda7572 | 2013-04-06 04:51:37 | [diff] [blame] | 112 | set(name, message, stack, targetChrome); |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 113 | try { |
[email protected] | 31bbfd7 | 2013-06-22 02:35:54 | [diff] [blame] | 114 | $Function.apply(callback, undefined, args); |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 115 | } finally { |
| 116 | clear(targetChrome); |
| 117 | } |
| 118 | } |
| 119 | |
[email protected] | 120028b9c | 2012-07-03 01:32:24 | [diff] [blame] | 120 | exports.clear = clear; |
[email protected] | 52b06286 | 2014-04-23 00:31:44 | [diff] [blame] | 121 | exports.hasAccessed = hasAccessed; |
[email protected] | a33425c3c | 2014-05-15 19:22:34 | [diff] [blame] | 122 | exports.hasError = hasError; |
[email protected] | 120028b9c | 2012-07-03 01:32:24 | [diff] [blame] | 123 | exports.set = set; |
[email protected] | 4f1633f | 2013-03-09 14:26:24 | [diff] [blame] | 124 | exports.run = run; |