Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/debug/errorhandler.js
2868 views
1
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Error handling utilities.
17
*
18
*/
19
20
goog.provide('goog.debug.ErrorHandler');
21
goog.provide('goog.debug.ErrorHandler.ProtectedFunctionError');
22
23
goog.require('goog.Disposable');
24
goog.require('goog.asserts');
25
goog.require('goog.debug');
26
goog.require('goog.debug.EntryPointMonitor');
27
goog.require('goog.debug.Error');
28
goog.require('goog.debug.Trace');
29
30
31
32
/**
33
* The ErrorHandler can be used to to wrap functions with a try/catch
34
* statement. If an exception is thrown, the given error handler function will
35
* be called.
36
*
37
* When this object is disposed, it will stop handling exceptions and tracing.
38
* It will also try to restore window.setTimeout and window.setInterval
39
* if it wrapped them. Notice that in the general case, it is not technically
40
* possible to remove the wrapper, because functions have no knowledge of
41
* what they have been assigned to. So the app is responsible for other
42
* forms of unwrapping.
43
*
44
* @param {Function} handler Handler for exceptions.
45
* @constructor
46
* @extends {goog.Disposable}
47
* @implements {goog.debug.EntryPointMonitor}
48
*/
49
goog.debug.ErrorHandler = function(handler) {
50
goog.debug.ErrorHandler.base(this, 'constructor');
51
52
/**
53
* Handler for exceptions, which can do logging, reporting, etc.
54
* @type {Function}
55
* @private
56
*/
57
this.errorHandlerFn_ = handler;
58
59
/**
60
* Whether errors should be wrapped in
61
* goog.debug.ErrorHandler.ProtectedFunctionError before rethrowing.
62
* @type {boolean}
63
* @private
64
*/
65
this.wrapErrors_ = true; // TODO(user) Change default.
66
67
/**
68
* Whether to add a prefix to all error messages. The prefix is
69
* goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX. This option
70
* only has an effect if this.wrapErrors_ is set to false.
71
* @type {boolean}
72
* @private
73
*/
74
this.prefixErrorMessages_ = false;
75
};
76
goog.inherits(goog.debug.ErrorHandler, goog.Disposable);
77
78
79
/**
80
* Whether to add tracers when instrumenting entry points.
81
* @type {boolean}
82
* @private
83
*/
84
goog.debug.ErrorHandler.prototype.addTracersToProtectedFunctions_ = false;
85
86
87
/**
88
* Enable tracers when instrumenting entry points.
89
* @param {boolean} newVal See above.
90
*/
91
goog.debug.ErrorHandler.prototype.setAddTracersToProtectedFunctions = function(
92
newVal) {
93
this.addTracersToProtectedFunctions_ = newVal;
94
};
95
96
97
/** @override */
98
goog.debug.ErrorHandler.prototype.wrap = function(fn) {
99
return this.protectEntryPoint(goog.asserts.assertFunction(fn));
100
};
101
102
103
/** @override */
104
goog.debug.ErrorHandler.prototype.unwrap = function(fn) {
105
goog.asserts.assertFunction(fn);
106
return fn[this.getFunctionIndex_(false)] || fn;
107
};
108
109
110
/**
111
* Private helper function to return a span that can be clicked on to display
112
* an alert with the current stack trace. Newlines are replaced with a
113
* placeholder so that they will not be html-escaped.
114
* @param {string} stackTrace The stack trace to create a span for.
115
* @return {string} A span which can be clicked on to show the stack trace.
116
* @private
117
*/
118
goog.debug.ErrorHandler.prototype.getStackTraceHolder_ = function(stackTrace) {
119
var buffer = [];
120
buffer.push('##PE_STACK_START##');
121
buffer.push(stackTrace.replace(/(\r\n|\r|\n)/g, '##STACK_BR##'));
122
buffer.push('##PE_STACK_END##');
123
return buffer.join('');
124
};
125
126
127
/**
128
* Get the index for a function. Used for internal indexing.
129
* @param {boolean} wrapper True for the wrapper; false for the wrapped.
130
* @return {string} The index where we should store the function in its
131
* wrapper/wrapped function.
132
* @private
133
*/
134
goog.debug.ErrorHandler.prototype.getFunctionIndex_ = function(wrapper) {
135
return (wrapper ? '__wrapper_' : '__protected_') + goog.getUid(this) + '__';
136
};
137
138
139
/**
140
* Installs exception protection for an entry point function. When an exception
141
* is thrown from a protected function, a handler will be invoked to handle it.
142
*
143
* @param {Function} fn An entry point function to be protected.
144
* @return {!Function} A protected wrapper function that calls the entry point
145
* function.
146
*/
147
goog.debug.ErrorHandler.prototype.protectEntryPoint = function(fn) {
148
var protectedFnName = this.getFunctionIndex_(true);
149
if (!fn[protectedFnName]) {
150
var wrapper = fn[protectedFnName] = this.getProtectedFunction(fn);
151
wrapper[this.getFunctionIndex_(false)] = fn;
152
}
153
return fn[protectedFnName];
154
};
155
156
157
/**
158
* Helps {@link #protectEntryPoint} by actually creating the protected
159
* wrapper function, after {@link #protectEntryPoint} determines that one does
160
* not already exist for the given function. Can be overriden by subclasses
161
* that may want to implement different error handling, or add additional
162
* entry point hooks.
163
* @param {!Function} fn An entry point function to be protected.
164
* @return {!Function} protected wrapper function.
165
* @protected
166
*/
167
goog.debug.ErrorHandler.prototype.getProtectedFunction = function(fn) {
168
var that = this;
169
var tracers = this.addTracersToProtectedFunctions_;
170
if (tracers) {
171
var stackTrace = goog.debug.getStacktraceSimple(15);
172
}
173
var googDebugErrorHandlerProtectedFunction = function() {
174
if (that.isDisposed()) {
175
return fn.apply(this, arguments);
176
}
177
178
if (tracers) {
179
var tracer = goog.debug.Trace.startTracer(
180
'protectedEntryPoint: ' + that.getStackTraceHolder_(stackTrace));
181
}
182
try {
183
return fn.apply(this, arguments);
184
} catch (e) {
185
// Don't re-report errors that have already been handled by this code.
186
var MESSAGE_PREFIX =
187
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX;
188
if ((e && typeof e === 'object' && e.message &&
189
e.message.indexOf(MESSAGE_PREFIX) == 0) ||
190
(typeof e === 'string' && e.indexOf(MESSAGE_PREFIX) == 0)) {
191
return;
192
}
193
that.errorHandlerFn_(e);
194
if (!that.wrapErrors_) {
195
// Add the prefix to the existing message.
196
if (that.prefixErrorMessages_) {
197
if (typeof e === 'object' && e && 'message' in e) {
198
e.message = MESSAGE_PREFIX + e.message;
199
} else {
200
e = MESSAGE_PREFIX + e;
201
}
202
}
203
if (goog.DEBUG) {
204
// Work around for https://code.google.com/p/v8/issues/detail?id=2625
205
// and https://code.google.com/p/chromium/issues/detail?id=237059
206
// Custom errors and errors with custom stack traces show the wrong
207
// stack trace
208
// If it has a stack and Error.captureStackTrace is supported (only
209
// supported in V8 as of May 2013) log the stack to the console.
210
if (e && e.stack && Error.captureStackTrace &&
211
goog.global['console']) {
212
goog.global['console']['error'](e.message, e.stack);
213
}
214
}
215
// Re-throw original error. This is great for debugging as it makes
216
// browser JS dev consoles show the correct error and stack trace.
217
throw e;
218
}
219
// Re-throw it since this may be expected by the caller.
220
throw new goog.debug.ErrorHandler.ProtectedFunctionError(e);
221
} finally {
222
if (tracers) {
223
goog.debug.Trace.stopTracer(tracer);
224
}
225
}
226
};
227
googDebugErrorHandlerProtectedFunction[this.getFunctionIndex_(false)] = fn;
228
return googDebugErrorHandlerProtectedFunction;
229
};
230
231
232
// TODO(mknichel): Allow these functions to take in the window to protect.
233
/**
234
* Installs exception protection for window.setTimeout to handle exceptions.
235
*/
236
goog.debug.ErrorHandler.prototype.protectWindowSetTimeout = function() {
237
this.protectWindowFunctionsHelper_('setTimeout');
238
};
239
240
241
/**
242
* Install exception protection for window.setInterval to handle exceptions.
243
*/
244
goog.debug.ErrorHandler.prototype.protectWindowSetInterval = function() {
245
this.protectWindowFunctionsHelper_('setInterval');
246
};
247
248
249
/**
250
* Install exception protection for window.requestAnimationFrame to handle
251
* exceptions.
252
*/
253
goog.debug.ErrorHandler.prototype.protectWindowRequestAnimationFrame =
254
function() {
255
var win = goog.getObjectByName('window');
256
var fnNames = [
257
'requestAnimationFrame', 'mozRequestAnimationFrame', 'webkitAnimationFrame',
258
'msRequestAnimationFrame'
259
];
260
for (var i = 0; i < fnNames.length; i++) {
261
var fnName = fnNames[i];
262
if (fnNames[i] in win) {
263
this.protectWindowFunctionsHelper_(fnName);
264
}
265
}
266
};
267
268
269
/**
270
* Helper function for protecting a function that causes a function to be
271
* asynchronously called, for example setTimeout or requestAnimationFrame.
272
* @param {string} fnName The name of the function to protect.
273
* @private
274
*/
275
goog.debug.ErrorHandler.prototype.protectWindowFunctionsHelper_ = function(
276
fnName) {
277
var win = goog.getObjectByName('window');
278
var originalFn = win[fnName];
279
var that = this;
280
win[fnName] = function(fn, time) {
281
// Don't try to protect strings. In theory, we could try to globalEval
282
// the string, but this seems to lead to permission errors on IE6.
283
if (goog.isString(fn)) {
284
fn = goog.partial(goog.globalEval, fn);
285
}
286
arguments[0] = fn = that.protectEntryPoint(fn);
287
288
// IE doesn't support .call for setInterval/setTimeout, but it
289
// also doesn't care what "this" is, so we can just call the
290
// original function directly
291
if (originalFn.apply) {
292
return originalFn.apply(this, arguments);
293
} else {
294
var callback = fn;
295
if (arguments.length > 2) {
296
var args = Array.prototype.slice.call(arguments, 2);
297
callback = function() { fn.apply(this, args); };
298
}
299
return originalFn(callback, time);
300
}
301
};
302
win[fnName][this.getFunctionIndex_(false)] = originalFn;
303
};
304
305
306
/**
307
* Set whether to wrap errors that occur in protected functions in a
308
* goog.debug.ErrorHandler.ProtectedFunctionError.
309
* @param {boolean} wrapErrors Whether to wrap errors.
310
*/
311
goog.debug.ErrorHandler.prototype.setWrapErrors = function(wrapErrors) {
312
this.wrapErrors_ = wrapErrors;
313
};
314
315
316
/**
317
* Set whether to add a prefix to all error messages that occur in protected
318
* functions.
319
* @param {boolean} prefixErrorMessages Whether to add a prefix to error
320
* messages.
321
*/
322
goog.debug.ErrorHandler.prototype.setPrefixErrorMessages = function(
323
prefixErrorMessages) {
324
this.prefixErrorMessages_ = prefixErrorMessages;
325
};
326
327
328
/** @override */
329
goog.debug.ErrorHandler.prototype.disposeInternal = function() {
330
// Try to unwrap window.setTimeout and window.setInterval.
331
var win = goog.getObjectByName('window');
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
var message = goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
350
(cause && cause.message ? String(cause.message) : String(cause));
351
goog.debug.ErrorHandler.ProtectedFunctionError.base(
352
this, 'constructor', message);
353
354
/**
355
* The error thrown by the entry point.
356
* @type {*}
357
*/
358
this.cause = cause;
359
360
var stack = cause && cause.stack;
361
if (stack && goog.isString(stack)) {
362
this.stack = /** @type {string} */ (stack);
363
}
364
};
365
goog.inherits(goog.debug.ErrorHandler.ProtectedFunctionError, goog.debug.Error);
366
367
368
/**
369
* Text to prefix the message with.
370
* @type {string}
371
*/
372
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX =
373
'Error in protected function: ';
374
375