Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/debug/errorhandler.js
3987 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Error handling utilities.
9
*/
10
11
goog.provide('goog.debug.ErrorHandler');
12
goog.provide('goog.debug.ErrorHandler.ProtectedFunctionError');
13
14
goog.require('goog.Disposable');
15
goog.require('goog.asserts');
16
goog.require('goog.debug.EntryPointMonitor');
17
goog.require('goog.debug.Error');
18
19
20
21
/**
22
* The ErrorHandler can be used to to wrap functions with a try/catch
23
* statement. If an exception is thrown, the given error handler function will
24
* be called.
25
*
26
* When this object is disposed, it will stop handling exceptions and tracing.
27
* It will also try to restore window.setTimeout and window.setInterval
28
* if it wrapped them. Notice that in the general case, it is not technically
29
* possible to remove the wrapper, because functions have no knowledge of
30
* what they have been assigned to. So the app is responsible for other
31
* forms of unwrapping.
32
*
33
* @param {Function} handler Handler for exceptions.
34
* @constructor
35
* @extends {goog.Disposable}
36
* @implements {goog.debug.EntryPointMonitor}
37
*/
38
goog.debug.ErrorHandler = function(handler) {
39
'use strict';
40
goog.debug.ErrorHandler.base(this, 'constructor');
41
42
/**
43
* Handler for exceptions, which can do logging, reporting, etc.
44
* @type {Function}
45
* @private
46
*/
47
this.errorHandlerFn_ = handler;
48
49
/**
50
* Whether errors should be wrapped in
51
* goog.debug.ErrorHandler.ProtectedFunctionError before rethrowing.
52
* @type {boolean}
53
* @private
54
*/
55
this.wrapErrors_ = true; // TODO(malteubl) Change default.
56
57
/**
58
* Whether to add a prefix to all error messages. The prefix is
59
* goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX. This option
60
* only has an effect if this.wrapErrors_ is set to false.
61
* @type {boolean}
62
* @private
63
*/
64
this.prefixErrorMessages_ = false;
65
};
66
goog.inherits(goog.debug.ErrorHandler, goog.Disposable);
67
68
69
/** @override */
70
goog.debug.ErrorHandler.prototype.wrap = function(fn) {
71
'use strict';
72
return this.protectEntryPoint(goog.asserts.assertFunction(fn));
73
};
74
75
76
/** @override */
77
goog.debug.ErrorHandler.prototype.unwrap = function(fn) {
78
'use strict';
79
goog.asserts.assertFunction(fn);
80
return fn[this.getFunctionIndex_(false)] || fn;
81
};
82
83
84
/**
85
* Get the index for a function. Used for internal indexing.
86
* @param {boolean} wrapper True for the wrapper; false for the wrapped.
87
* @return {string} The index where we should store the function in its
88
* wrapper/wrapped function.
89
* @private
90
*/
91
goog.debug.ErrorHandler.prototype.getFunctionIndex_ = function(wrapper) {
92
'use strict';
93
return (wrapper ? '__wrapper_' : '__protected_') + goog.getUid(this) + '__';
94
};
95
96
97
/**
98
* Installs exception protection for an entry point function. When an exception
99
* is thrown from a protected function, a handler will be invoked to handle it.
100
*
101
* @param {!Function} fn An entry point function to be protected.
102
* @return {!Function} A protected wrapper function that calls the entry point
103
* function.
104
*/
105
goog.debug.ErrorHandler.prototype.protectEntryPoint = function(fn) {
106
'use strict';
107
var protectedFnName = this.getFunctionIndex_(true);
108
if (!fn[protectedFnName]) {
109
var wrapper = fn[protectedFnName] = this.getProtectedFunction(fn);
110
wrapper[this.getFunctionIndex_(false)] = fn;
111
}
112
return fn[protectedFnName];
113
};
114
115
116
/**
117
* Helps {@link #protectEntryPoint} by actually creating the protected
118
* wrapper function, after {@link #protectEntryPoint} determines that one does
119
* not already exist for the given function. Can be overridden by subclasses
120
* that may want to implement different error handling, or add additional
121
* entry point hooks.
122
* @param {!Function} fn An entry point function to be protected.
123
* @return {!Function} protected wrapper function.
124
* @protected
125
*/
126
goog.debug.ErrorHandler.prototype.getProtectedFunction = function(fn) {
127
'use strict';
128
var that = this;
129
var googDebugErrorHandlerProtectedFunction = function() {
130
'use strict';
131
var self = /** @type {?} */ (this);
132
if (that.isDisposed()) {
133
return fn.apply(self, arguments);
134
}
135
136
try {
137
return fn.apply(self, arguments);
138
} catch (e) {
139
that.handleError_(e);
140
}
141
};
142
googDebugErrorHandlerProtectedFunction[this.getFunctionIndex_(false)] = fn;
143
return googDebugErrorHandlerProtectedFunction;
144
};
145
146
147
/**
148
* Internal error handler.
149
* @param {?} e The error string or an Error-like object.
150
* @private
151
*/
152
goog.debug.ErrorHandler.prototype.handleError_ = function(e) {
153
'use strict';
154
// Don't re-report errors that have already been handled by this code.
155
var MESSAGE_PREFIX =
156
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX;
157
if ((e && typeof e === 'object' && typeof e.message === 'string' &&
158
e.message.indexOf(MESSAGE_PREFIX) == 0) ||
159
(typeof e === 'string' && e.indexOf(MESSAGE_PREFIX) == 0)) {
160
return;
161
}
162
this.errorHandlerFn_(e);
163
if (!this.wrapErrors_) {
164
// Add the prefix to the existing message.
165
if (this.prefixErrorMessages_) {
166
if (typeof e === 'object' && e && typeof e.message === 'string') {
167
/** @type {{message}} */ (e).message = MESSAGE_PREFIX + e.message;
168
} else {
169
e = MESSAGE_PREFIX + e;
170
}
171
}
172
if (goog.DEBUG) {
173
// Work around for https://code.google.com/p/v8/issues/detail?id=2625
174
// and https://code.google.com/p/chromium/issues/detail?id=237059
175
// Custom errors and errors with custom stack traces show the wrong
176
// stack trace
177
// If it has a stack and Error.captureStackTrace is supported (only
178
// supported in V8 as of May 2013) log the stack to the console.
179
if (e && typeof e.stack === 'string' && Error.captureStackTrace &&
180
goog.global['console']) {
181
goog.global['console']['error'](e.message, e.stack);
182
}
183
}
184
// Re-throw original error. This is great for debugging as it makes
185
// browser JS dev consoles show the correct error and stack trace.
186
throw e;
187
}
188
// Re-throw it since this may be expected by the caller.
189
throw new goog.debug.ErrorHandler.ProtectedFunctionError(e);
190
};
191
192
193
// TODO(mknichel): Allow these functions to take in the window to protect.
194
/**
195
* Installs exception protection for window.setTimeout to handle exceptions.
196
*/
197
goog.debug.ErrorHandler.prototype.protectWindowSetTimeout = function() {
198
'use strict';
199
this.protectWindowFunctionsHelper_('setTimeout');
200
};
201
202
203
/**
204
* Install exception protection for window.setInterval to handle exceptions.
205
*/
206
goog.debug.ErrorHandler.prototype.protectWindowSetInterval = function() {
207
'use strict';
208
this.protectWindowFunctionsHelper_('setInterval');
209
};
210
211
212
/**
213
* Install an unhandledrejection event listener that reports rejected promises.
214
* Note: this will only work with Chrome 49+ and friends, but so far is the only
215
* way to report uncaught errors in aysnc/await functions.
216
* @param {!Window=} win the window to instrument, defaults to current window
217
*/
218
goog.debug.ErrorHandler.prototype.catchUnhandledRejections = function(win) {
219
'use strict';
220
win = win || goog.global['window'] || goog.global['globalThis'];
221
if ('onunhandledrejection' in win) {
222
win.onunhandledrejection = (event) => {
223
// event.reason contains the rejection reason. When an Error is
224
// thrown, this is the Error object. If it is undefined, create a new
225
// error object.
226
const e =
227
event && event.reason ? event.reason : new Error('uncaught error');
228
this.handleError_(e);
229
};
230
}
231
};
232
233
234
/**
235
* Install exception protection for window.requestAnimationFrame to handle
236
* exceptions.
237
*/
238
goog.debug.ErrorHandler.prototype.protectWindowRequestAnimationFrame =
239
function() {
240
'use strict';
241
const win = goog.global['window'] || goog.global['globalThis'];
242
var fnNames = [
243
'requestAnimationFrame', 'mozRequestAnimationFrame', 'webkitAnimationFrame',
244
'msRequestAnimationFrame'
245
];
246
for (var i = 0; i < fnNames.length; i++) {
247
var fnName = fnNames[i];
248
if (fnNames[i] in win) {
249
this.protectWindowFunctionsHelper_(fnName);
250
}
251
}
252
};
253
254
255
/**
256
* Helper function for protecting a function that causes a function to be
257
* asynchronously called, for example setTimeout or requestAnimationFrame.
258
* @param {string} fnName The name of the function to protect.
259
* @private
260
*/
261
goog.debug.ErrorHandler.prototype.protectWindowFunctionsHelper_ = function(
262
fnName) {
263
'use strict';
264
const win = goog.global['window'] || goog.global['globalThis'];
265
var originalFn = win[fnName];
266
if (!originalFn) throw new Error(fnName + ' not on global?');
267
var that = this;
268
win[fnName] = function(fn, time) {
269
'use strict';
270
if (typeof fn === 'string') {
271
fn = goog.partial(goog.globalEval, fn);
272
}
273
// The first arg (function to call) might be undefined or null, and
274
// protectEntryPoint doesn't like this.
275
// If the fn was a string, the call to goog.partial above always returns a
276
// function, so they will always be called protected.
277
// (e.g. setTimeout(undefined, 1000))
278
if (fn) {
279
arguments[0] = fn = that.protectEntryPoint(fn);
280
}
281
282
// IE doesn't support .call for setInterval/setTimeout, but it
283
// also doesn't care what "this" is, so we can just call the
284
// original function directly
285
if (originalFn.apply) {
286
return originalFn.apply(/** @type {?} */ (this), arguments);
287
} else {
288
var callback = fn;
289
if (arguments.length > 2) {
290
var args = Array.prototype.slice.call(arguments, 2);
291
callback = function() {
292
'use strict';
293
fn.apply(/** @type {?} */ (this), args);
294
};
295
}
296
return originalFn(callback, time);
297
}
298
};
299
win[fnName][this.getFunctionIndex_(false)] = originalFn;
300
};
301
302
303
/**
304
* Set whether to wrap errors that occur in protected functions in a
305
* goog.debug.ErrorHandler.ProtectedFunctionError.
306
* @param {boolean} wrapErrors Whether to wrap errors.
307
*/
308
goog.debug.ErrorHandler.prototype.setWrapErrors = function(wrapErrors) {
309
'use strict';
310
this.wrapErrors_ = wrapErrors;
311
};
312
313
314
/**
315
* Set whether to add a prefix to all error messages that occur in protected
316
* functions.
317
* @param {boolean} prefixErrorMessages Whether to add a prefix to error
318
* messages.
319
*/
320
goog.debug.ErrorHandler.prototype.setPrefixErrorMessages = function(
321
prefixErrorMessages) {
322
'use strict';
323
this.prefixErrorMessages_ = prefixErrorMessages;
324
};
325
326
327
/** @override */
328
goog.debug.ErrorHandler.prototype.disposeInternal = function() {
329
'use strict';
330
// Try to unwrap window.setTimeout and window.setInterval.
331
const win = goog.global['window'] || goog.global['globalThis'];
332
win.setTimeout = this.unwrap(win.setTimeout);
333
win.setInterval = this.unwrap(win.setInterval);
334
335
goog.debug.ErrorHandler.base(this, 'disposeInternal');
336
};
337
338
339
340
/**
341
* Error thrown to the caller of a protected entry point if the entry point
342
* throws an error.
343
* @param {*} cause The error thrown by the entry point.
344
* @constructor
345
* @extends {goog.debug.Error}
346
* @final
347
*/
348
goog.debug.ErrorHandler.ProtectedFunctionError = function(cause) {
349
'use strict';
350
/** @suppress {missingProperties} message may not be defined. */
351
var message = goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
352
(cause && cause.message ? String(cause.message) : String(cause));
353
goog.debug.ErrorHandler.ProtectedFunctionError.base(
354
this, 'constructor', message, /** @type {?} */ (cause));
355
356
/** @suppress {missingProperties} stack may not be defined. */
357
var stack = cause && cause.stack;
358
if (stack && typeof stack === 'string') {
359
this.stack = /** @type {string} */ (stack);
360
}
361
};
362
goog.inherits(goog.debug.ErrorHandler.ProtectedFunctionError, goog.debug.Error);
363
364
365
/**
366
* Text to prefix the message with.
367
* @type {string}
368
*/
369
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX =
370
'Error in protected function: ';
371
372