Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / renderer / resources / last_error.js
blob9b5b3c0674e79fef7c1792ed2b9e391cd0bbdd3f
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.
5 var GetAvailability = requireNative('v8_context').GetAvailability;
6 var GetGlobal = requireNative('sendRequest').GetGlobal;
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.
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   if (!targetChrome) {
23     var errorMessage = name + ': ' + message;
24     if (stack != null && stack != '')
25       errorMessage += '\n' + stack;
26     throw new Error('No chrome object to set error: ' + errorMessage);
27   }
28   clear(targetChrome);  // in case somebody has set a sneaky getter/setter
30   var errorObject = { message: message };
31   if (GetAvailability('extension.lastError').is_available)
32     targetChrome.extension.lastError = errorObject;
34   assertRuntimeIsAvailable();
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       }});
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;
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');
70   assertRuntimeIsAvailable();
71   if ('lastError' in targetChrome.runtime)
72     return true;
74   return false;
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');
84   if (GetAvailability('extension.lastError').is_available)
85    delete targetChrome.extension.lastError;
87   assertRuntimeIsAvailable();
88   delete targetChrome.runtime.lastError;
89   delete privates(targetChrome.runtime).accessedLastError;
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');
105  * Runs |callback(args)| with last error args as in set().
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     reportIfUnchecked(name, targetChrome, stack);
117     clear(targetChrome);
118   }
122  * Checks whether chrome.runtime.lastError has been accessed if set.
123  * If it was set but not accessed, the error is reported to the console.
125  * @param {string=} name - name of API.
126  * @param {Object} targetChrome - the Chrome object to check.
127  * @param {string=} stack - Stack trace of the call up to the error.
128  */
129 function reportIfUnchecked(name, targetChrome, stack) {
130   if (hasAccessed(targetChrome) || !hasError(targetChrome))
131     return;
132   var message = targetChrome.runtime.lastError.message;
133   console.error("Unchecked runtime.lastError while running " +
134       (name || "unknown") + ": " + message + (stack ? "\n" + stack : ""));
137 exports.clear = clear;
138 exports.hasAccessed = hasAccessed;
139 exports.hasError = hasError;
140 exports.set = set;
141 exports.run = run;
142 exports.reportIfUnchecked = reportIfUnchecked;