Skip to content

Commit 837acd1

Browse files
Thomas Graingergkalpak
Thomas Grainger
authored andcommitted
fix(*): correctly detect Error instances from different contexts
Previously, errors thrown from different contexts (such as an iframe or webworker) were not detected as `Error` instances and handled accordingly. This commit fixes it by introducing an `isError()` helper, that is able to correctly detect such instances. Fixes angular#15868 Closes angular#15872
1 parent 63b6d64 commit 837acd1

File tree

8 files changed

+62
-4
lines changed

8 files changed

+62
-4
lines changed

src/.eslintrc.json

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"isNumber": false,
5151
"isNumberNaN": false,
5252
"isDate": false,
53+
"isError": false,
5354
"isArray": false,
5455
"isFunction": false,
5556
"isRegExp": false,

src/Angular.js

+19
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
isNumber,
4646
isNumberNaN,
4747
isDate,
48+
isError,
4849
isArray,
4950
isFunction,
5051
isRegExp,
@@ -629,6 +630,24 @@ function isDate(value) {
629630
*/
630631
var isArray = Array.isArray;
631632

633+
/**
634+
* @description
635+
* Determines if a reference is an `Error`.
636+
* Loosely based on https://www.npmjs.com/package/iserror
637+
*
638+
* @param {*} value Reference to check.
639+
* @returns {boolean} True if `value` is an `Error`.
640+
*/
641+
function isError(value) {
642+
var tag = toString.call(value);
643+
switch (tag) {
644+
case '[object Error]': return true;
645+
case '[object Exception]': return true;
646+
case '[object DOMException]': return true;
647+
default: return value instanceof Error;
648+
}
649+
}
650+
632651
/**
633652
* @ngdoc function
634653
* @name angular.isFunction

src/ng/compile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3098,7 +3098,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
30983098
}
30993099
linkQueue = null;
31003100
}).catch(function(error) {
3101-
if (error instanceof Error) {
3101+
if (isError(error)) {
31023102
$exceptionHandler(error);
31033103
}
31043104
});

src/ng/log.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ function $LogProvider() {
132132
};
133133

134134
function formatError(arg) {
135-
if (arg instanceof Error) {
135+
if (isError(arg)) {
136136
if (arg.stack && formatStackTrace) {
137137
arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
138138
? 'Error: ' + arg.message + '\n' + arg.stack

src/ng/q.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
381381
if (!toCheck.pur) {
382382
toCheck.pur = true;
383383
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
384-
if (toCheck.value instanceof Error) {
384+
if (isError(toCheck.value)) {
385385
exceptionHandler(toCheck.value, errorMessage);
386386
} else {
387387
exceptionHandler(errorMessage);

test/.eslintrc.json

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"isNumber": false,
6565
"isNumberNaN": false,
6666
"isDate": false,
67+
"isError": false,
6768
"isArray": false,
6869
"isFunction": false,
6970
"isRegExp": false,

test/AngularSpec.js

+37
Original file line numberDiff line numberDiff line change
@@ -1855,6 +1855,43 @@ describe('angular', function() {
18551855
});
18561856
});
18571857

1858+
describe('isError', function() {
1859+
function testErrorFromDifferentContext(createError) {
1860+
var iframe = document.createElement('iframe');
1861+
document.body.appendChild(iframe);
1862+
try {
1863+
var error = createError(iframe.contentWindow);
1864+
expect(isError(error)).toBe(true);
1865+
} finally {
1866+
iframe.parentElement.removeChild(iframe);
1867+
}
1868+
}
1869+
1870+
it('should not assume objects are errors', function() {
1871+
var fakeError = { message: 'A fake error', stack: 'no stack here'};
1872+
expect(isError(fakeError)).toBe(false);
1873+
});
1874+
1875+
it('should detect simple error instances', function() {
1876+
expect(isError(new Error())).toBe(true);
1877+
});
1878+
1879+
it('should detect errors from another context', function() {
1880+
testErrorFromDifferentContext(function(win) {
1881+
return new win.Error();
1882+
});
1883+
});
1884+
1885+
it('should detect DOMException errors from another context', function() {
1886+
testErrorFromDifferentContext(function(win) {
1887+
try {
1888+
win.document.querySelectorAll('');
1889+
} catch (e) {
1890+
return e;
1891+
}
1892+
});
1893+
});
1894+
});
18581895

18591896
describe('isRegExp', function() {
18601897
it('should return true for RegExp object', function() {

test/ng/qSpec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('q', function() {
3737
// The following private functions are used to help with logging for testing invocation of the
3838
// promise callbacks.
3939
function _argToString(arg) {
40-
return (typeof arg === 'object' && !(arg instanceof Error)) ? toJson(arg) : '' + arg;
40+
return (typeof arg === 'object' && !(isError(arg))) ? toJson(arg) : '' + arg;
4141
}
4242

4343
function _argumentsToString(args) {

0 commit comments

Comments
 (0)