diff --git a/package.json b/package.json index 04855b108..0a93475b4 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "keybinding-resolver": "0.20.0", "link": "0.26.0", "markdown-preview": "0.110.0", - "metrics": "0.38.0", + "metrics": "0.39.0", "open-on-github": "0.30.0", "package-generator": "0.32.0", "release-notes": "0.36.0", diff --git a/spec/atom-spec.coffee b/spec/atom-spec.coffee index 615e510f7..d802ee2cc 100644 --- a/spec/atom-spec.coffee +++ b/spec/atom-spec.coffee @@ -52,3 +52,72 @@ describe "the `atom` global", -> it 'loads the default core config', -> expect(atom.config.get('core.excludeVcsIgnoredPaths')).toBe true expect(atom.config.get('editor.showInvisibles')).toBe false + + describe "window onerror handler", -> + beforeEach -> + spyOn atom, 'openDevTools' + spyOn atom, 'executeJavaScriptInDevTools' + + it "will open the dev tools when an error is triggered", -> + try + a + 1 + catch e + window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + + expect(atom.openDevTools).toHaveBeenCalled() + expect(atom.executeJavaScriptInDevTools).toHaveBeenCalled() + + describe "::onWillThrowError", -> + willThrowSpy = null + beforeEach -> + willThrowSpy = jasmine.createSpy() + + it "is called when there is an error", -> + error = null + atom.onWillThrowError(willThrowSpy) + try + a + 1 + catch e + error = e + window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + + delete willThrowSpy.mostRecentCall.args[0].preventDefault + expect(willThrowSpy).toHaveBeenCalledWith + message: error.toString() + url: 'abc' + line: 2 + column: 3 + originalError: error + + it "will not show the devtools when preventDefault() is called", -> + willThrowSpy.andCallFake (errorObject) -> errorObject.preventDefault() + atom.onWillThrowError(willThrowSpy) + + try + a + 1 + catch e + window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + + expect(willThrowSpy).toHaveBeenCalled() + expect(atom.openDevTools).not.toHaveBeenCalled() + expect(atom.executeJavaScriptInDevTools).not.toHaveBeenCalled() + + describe "::onDidThrowError", -> + didThrowSpy = null + beforeEach -> + didThrowSpy = jasmine.createSpy() + + it "is called when there is an error", -> + error = null + atom.onDidThrowError(didThrowSpy) + try + a + 1 + catch e + error = e + window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + expect(didThrowSpy).toHaveBeenCalledWith + message: error.toString() + url: 'abc' + line: 2 + column: 3 + originalError: error diff --git a/src/atom.coffee b/src/atom.coffee index 944e5049d..be23d1cf6 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -173,11 +173,21 @@ class Atom extends Model require('grim').deprecate = -> window.onerror = => - @openDevTools() - @executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()') @lastUncaughtError = Array::slice.call(arguments) + [message, url, line, column, originalError] = @lastUncaughtError + eventObject = {message, url, line, column, originalError} + + openDevTools = true + eventObject.preventDefault = -> openDevTools = false + + @emitter.emit 'will-throw-error', eventObject + + if openDevTools + @openDevTools() + @executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()') + @emit 'uncaught-error', arguments... - @emitter.emit 'did-throw-error', arguments... + @emitter.emit 'did-throw-error', {message, url, line, column, originalError} @unsubscribe() @setBodyPlatformClass() @@ -248,10 +258,31 @@ class Atom extends Model onDidBeep: (callback) -> @emitter.on 'did-beep', callback + # Extended: Invoke the given callback when there is an unhandled error, but + # before the devtools pop open + # + # * `callback` {Function} to be called whenever there is an unhandled error + # * `event` {Object} + # * `originalError` {Object} the original error object + # * `message` {String} the original error object + # * `url` {String} Url to the file where the error originated. + # * `line` {Number} + # * `column` {Number} + # * `preventDefault` {Function} call this to avoid popping up the dev tools. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onWillThrowError: (callback) -> + @emitter.on 'will-throw-error', callback + # Extended: Invoke the given callback whenever there is an unhandled error. # # * `callback` {Function} to be called whenever there is an unhandled error - # * `errorMessage` {String} + # * `event` {Object} + # * `originalError` {Object} the original error object + # * `message` {String} the original error object + # * `url` {String} Url to the file where the error originated. + # * `line` {Number} + # * `column` {Number} # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidThrowError: (callback) ->