Skip to content

Commit a8a7637

Browse files
authored
Chrome and Firefox Extensions stack trace parsing (#1235)
* feat: Handle moz-extension stack traces in TraceKit * feat: Handle chrome-extension stack traces in TraceKit
1 parent 97b63e2 commit a8a7637

File tree

4 files changed

+111
-63
lines changed

4 files changed

+111
-63
lines changed

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/vendor/fixtures/captured-errors.js

+24-4
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,18 @@ CapturedExceptions.CHROME_XX_WEBPACK = {
193193
name: 'TypeError',
194194
stack:
195195
"TypeError: Cannot read property 'error' of undefined\n" +
196-
' at TESTTESTTEST.eval(webpack:///./src/components/test/test.jsx?:295:108)\n' +
197-
' at TESTTESTTEST.render(webpack:///./src/components/test/test.jsx?:272:32)\n' +
198-
' at TESTTESTTEST.tryRender(webpack:///./~/react-transform-catch-errors/lib/index.js?:34:31)\n' +
199-
' at TESTTESTTEST.proxiedMethod(webpack:///./~/react-proxy/modules/createPrototypeProxy.js?:44:30)'
196+
' at TESTTESTTEST.eval(webpack:///./src/components/test/test.jsx?:295:108)\n' +
197+
' at TESTTESTTEST.render(webpack:///./src/components/test/test.jsx?:272:32)\n' +
198+
' at TESTTESTTEST.tryRender(webpack:///./~/react-transform-catch-errors/lib/index.js?:34:31)\n' +
199+
' at TESTTESTTEST.proxiedMethod(webpack:///./~/react-proxy/modules/createPrototypeProxy.js?:44:30)'
200+
};
201+
202+
CapturedExceptions.CHROME_EXTENSION = {
203+
message: 'oops!',
204+
name: 'Error',
205+
stack:
206+
'Error: oops!\n' +
207+
' at chrome-extension://negengibeecgiokdifncekejdgkcoono/content-script-main.js:93:12'
200208
};
201209

202210
CapturedExceptions.FIREFOX_3 = {
@@ -297,6 +305,18 @@ CapturedExceptions.FIREFOX_50_RESOURCE_URL = {
297305
name: 'TypeError'
298306
};
299307

308+
CapturedExceptions.FIREFOX_MOZ_EXTENSION = {
309+
stack:
310+
'render@moz-extension://path/data/content/bundle.js:5529:16\n' +
311+
'dispatchEvent@moz-extension://path/data/content/vendor.bundle.js:18:23028\n' +
312+
'wrapped@moz-extension//path/data/content/bundle.js:7270:25',
313+
fileName: 'moz-extension://path/data/content/bundle.js',
314+
lineNumber: 5529,
315+
columnNumber: 16,
316+
message: 'this.props.raw[this.state.dataSource].rows is undefined',
317+
name: 'TypeError'
318+
};
319+
300320
CapturedExceptions.SAFARI_6 = {
301321
message: "'null' is not an object (evaluating 'x.undef')",
302322
stack:

test/vendor/tracekit-parser.test.js

+28
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,19 @@ describe('TraceKit', function() {
431431
});
432432
});
433433

434+
it('should parse Chrome Extension errors', function() {
435+
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.CHROME_EXTENSION);
436+
assert.ok(stackFrames);
437+
assert.deepEqual(stackFrames.stack.length, 1);
438+
assert.deepEqual(stackFrames.stack[0], {
439+
url: 'chrome-extension://negengibeecgiokdifncekejdgkcoono/content-script-main.js',
440+
func: '?',
441+
args: [],
442+
line: 93,
443+
column: 12
444+
});
445+
});
446+
434447
it('should parse nested eval() from Chrome', function() {
435448
var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.CHROME_48_EVAL);
436449
assert.ok(stackFrames);
@@ -788,6 +801,21 @@ describe('TraceKit', function() {
788801
});
789802
});
790803

804+
it('should parse Firefox Extension errors', function() {
805+
var stackFrames = TraceKit.computeStackTrace(
806+
CapturedExceptions.FIREFOX_MOZ_EXTENSION
807+
);
808+
assert.ok(stackFrames);
809+
assert.deepEqual(stackFrames.stack.length, 3);
810+
assert.deepEqual(stackFrames.stack[0], {
811+
url: 'moz-extension://path/data/content/bundle.js',
812+
func: 'render',
813+
args: [],
814+
line: 5529,
815+
column: 16
816+
});
817+
});
818+
791819
it('should parse React Native errors on Android', function() {
792820
var stackFrames = TraceKit.computeStackTrace(
793821
CapturedExceptions.ANDROID_REACT_NATIVE

vendor/TraceKit/tracekit.js

+58-58
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,18 @@ TraceKit.report = (function reportModuleWrapper() {
8080
lastExceptionStack = null;
8181

8282
/**
83-
* Add a crash handler.
84-
* @param {Function} handler
85-
*/
83+
* Add a crash handler.
84+
* @param {Function} handler
85+
*/
8686
function subscribe(handler) {
8787
installGlobalHandler();
8888
handlers.push(handler);
8989
}
9090

9191
/**
92-
* Remove a crash handler.
93-
* @param {Function} handler
94-
*/
92+
* Remove a crash handler.
93+
* @param {Function} handler
94+
*/
9595
function unsubscribe(handler) {
9696
for (var i = handlers.length - 1; i >= 0; --i) {
9797
if (handlers[i] === handler) {
@@ -101,17 +101,17 @@ TraceKit.report = (function reportModuleWrapper() {
101101
}
102102

103103
/**
104-
* Remove all crash handlers.
105-
*/
104+
* Remove all crash handlers.
105+
*/
106106
function unsubscribeAll() {
107107
uninstallGlobalHandler();
108108
handlers = [];
109109
}
110110

111111
/**
112-
* Dispatch stack information to all handlers.
113-
* @param {Object.<string, *>} stack
114-
*/
112+
* Dispatch stack information to all handlers.
113+
* @param {Object.<string, *>} stack
114+
*/
115115
function notifyHandlers(stack, isWindowError) {
116116
var exception = null;
117117
if (isWindowError && !TraceKit.collectWindowErrors) {
@@ -135,16 +135,16 @@ TraceKit.report = (function reportModuleWrapper() {
135135
var _oldOnerrorHandler, _onErrorHandlerInstalled;
136136

137137
/**
138-
* Ensures all global unhandled exceptions are recorded.
139-
* Supported by Gecko and IE.
140-
* @param {string} msg Error message.
141-
* @param {string} url URL of script that generated the exception.
142-
* @param {(number|string)} lineNo The line number at which the error
143-
* occurred.
144-
* @param {?(number|string)} colNo The column number at which the error
145-
* occurred.
146-
* @param {?Error} ex The actual Error object.
147-
*/
138+
* Ensures all global unhandled exceptions are recorded.
139+
* Supported by Gecko and IE.
140+
* @param {string} msg Error message.
141+
* @param {string} url URL of script that generated the exception.
142+
* @param {(number|string)} lineNo The line number at which the error
143+
* occurred.
144+
* @param {?(number|string)} colNo The column number at which the error
145+
* occurred.
146+
* @param {?Error} ex The actual Error object.
147+
*/
148148
function traceKitWindowOnError(msg, url, lineNo, colNo, ex) {
149149
var stack = null;
150150
// If 'ex' is ErrorEvent, get real Error from inside
@@ -232,12 +232,12 @@ TraceKit.report = (function reportModuleWrapper() {
232232
}
233233

234234
/**
235-
* Reports an unhandled Error to TraceKit.
236-
* @param {Error} ex
237-
* @param {?boolean} rethrow If false, do not re-throw the exception.
238-
* Only used for window.onerror to not cause an infinite loop of
239-
* rethrowing.
240-
*/
235+
* Reports an unhandled Error to TraceKit.
236+
* @param {Error} ex
237+
* @param {?boolean} rethrow If false, do not re-throw the exception.
238+
* Only used for window.onerror to not cause an infinite loop of
239+
* rethrowing.
240+
*/
241241
function report(ex, rethrow) {
242242
var args = _slice.call(arguments, 1);
243243
if (lastExceptionStack) {
@@ -364,16 +364,16 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
364364
// ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'
365365

366366
/**
367-
* Computes stack trace information from the stack property.
368-
* Chrome and Gecko use this property.
369-
* @param {Error} ex
370-
* @return {?Object.<string, *>} Stack trace information.
371-
*/
367+
* Computes stack trace information from the stack property.
368+
* Chrome and Gecko use this property.
369+
* @param {Error} ex
370+
* @return {?Object.<string, *>} Stack trace information.
371+
*/
372372
function computeStackTraceFromStackProp(ex) {
373373
if (typeof ex.stack === 'undefined' || !ex.stack) return;
374374

375-
var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
376-
gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,
375+
var chrome = /^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
376+
gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|moz-extension|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,
377377
winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx(?:-web)|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
378378
// Used to additionally parse URL/line/column from eval frames
379379
geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i,
@@ -455,18 +455,18 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
455455
}
456456

457457
/**
458-
* Adds information about the first frame to incomplete stack traces.
459-
* Safari and IE require this to get complete data on the first frame.
460-
* @param {Object.<string, *>} stackInfo Stack trace information from
461-
* one of the compute* methods.
462-
* @param {string} url The URL of the script that caused an error.
463-
* @param {(number|string)} lineNo The line number of the script that
464-
* caused an error.
465-
* @param {string=} message The error generated by the browser, which
466-
* hopefully contains the name of the object that caused the error.
467-
* @return {boolean} Whether or not the stack information was
468-
* augmented.
469-
*/
458+
* Adds information about the first frame to incomplete stack traces.
459+
* Safari and IE require this to get complete data on the first frame.
460+
* @param {Object.<string, *>} stackInfo Stack trace information from
461+
* one of the compute* methods.
462+
* @param {string} url The URL of the script that caused an error.
463+
* @param {(number|string)} lineNo The line number of the script that
464+
* caused an error.
465+
* @param {string=} message The error generated by the browser, which
466+
* hopefully contains the name of the object that caused the error.
467+
* @return {boolean} Whether or not the stack information was
468+
* augmented.
469+
*/
470470
function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {
471471
var initial = {
472472
url: url,
@@ -505,14 +505,14 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
505505
}
506506

507507
/**
508-
* Computes stack trace information by walking the arguments.caller
509-
* chain at the time the exception occurred. This will cause earlier
510-
* frames to be missed but is the only way to get any stack trace in
511-
* Safari and IE. The top frame is restored by
512-
* {@link augmentStackTraceWithInitialElement}.
513-
* @param {Error} ex
514-
* @return {?Object.<string, *>} Stack trace information.
515-
*/
508+
* Computes stack trace information by walking the arguments.caller
509+
* chain at the time the exception occurred. This will cause earlier
510+
* frames to be missed but is the only way to get any stack trace in
511+
* Safari and IE. The top frame is restored by
512+
* {@link augmentStackTraceWithInitialElement}.
513+
* @param {Error} ex
514+
* @return {?Object.<string, *>} Stack trace information.
515+
*/
516516
function computeStackTraceByWalkingCallerChain(ex, depth) {
517517
var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,
518518
stack = [],
@@ -582,10 +582,10 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
582582
}
583583

584584
/**
585-
* Computes a stack trace for an exception.
586-
* @param {Error} ex
587-
* @param {(string|number)=} depth
588-
*/
585+
* Computes a stack trace for an exception.
586+
* @param {Error} ex
587+
* @param {(string|number)=} depth
588+
*/
589589
function computeStackTrace(ex, depth) {
590590
var stack = null;
591591
depth = depth == null ? 0 : +depth;

0 commit comments

Comments
 (0)