Skip to content

Commit 76e9a95

Browse files
committed
Ajax: trigger error callback on native abort
- IE9 does not have onabort. Use onreadystatechange instead. Fixes gh-2079 Close gh-2684
1 parent 70605c8 commit 76e9a95

File tree

2 files changed

+61
-9
lines changed

2 files changed

+61
-9
lines changed

src/ajax/xhr.js

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
2525
support.ajax = xhrSupported = !!xhrSupported;
2626

2727
jQuery.ajaxTransport( function( options ) {
28-
var callback;
28+
var callback, errorCallback;
2929

3030
// Cross domain only allowed if supported through XMLHttpRequest
3131
if ( support.cors || xhrSupported && !options.crossDomain ) {
@@ -72,17 +72,26 @@ jQuery.ajaxTransport( function( options ) {
7272
callback = function( type ) {
7373
return function() {
7474
if ( callback ) {
75-
callback = xhr.onload = xhr.onerror = null;
75+
callback = errorCallback = xhr.onload =
76+
xhr.onerror = xhr.onabort = xhr.onreadystatechange = null;
7677

7778
if ( type === "abort" ) {
7879
xhr.abort();
7980
} else if ( type === "error" ) {
80-
complete(
8181

82-
// File: protocol always yields status 0; see #8605, #14207
83-
xhr.status,
84-
xhr.statusText
85-
);
82+
// Support: IE9
83+
// On a manual native abort, IE9 throws
84+
// errors on any property access that is not readyState
85+
if ( typeof xhr.status !== "number" ) {
86+
complete( 0, "error" );
87+
} else {
88+
complete(
89+
90+
// File: protocol always yields status 0; see #8605, #14207
91+
xhr.status,
92+
xhr.statusText
93+
);
94+
}
8695
} else {
8796
complete(
8897
xhrSuccessStatus[ xhr.status ] || xhr.status,
@@ -103,7 +112,31 @@ jQuery.ajaxTransport( function( options ) {
103112

104113
// Listen to events
105114
xhr.onload = callback();
106-
xhr.onerror = callback( "error" );
115+
errorCallback = xhr.onerror = callback( "error" );
116+
117+
// Support: IE9
118+
// Use onreadystatechange to replace onabort
119+
// to handle uncaught aborts
120+
if ( xhr.onabort !== undefined ) {
121+
xhr.onabort = errorCallback;
122+
} else {
123+
xhr.onreadystatechange = function() {
124+
125+
// Check readyState before timeout as it changes
126+
if ( xhr.readyState === 4 ) {
127+
128+
// Allow onerror to be called first,
129+
// but that will not handle a native abort
130+
// Also, save errorCallback to a variable
131+
// as xhr.onerror cannot be accessed
132+
window.setTimeout( function() {
133+
if ( callback ) {
134+
errorCallback();
135+
}
136+
} );
137+
}
138+
};
139+
}
107140

108141
// Create the abort callback
109142
callback = callback( "abort" );

test/unit/ajax.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ QUnit.module( "ajax", {
3838
);
3939

4040
ajaxTest( "jQuery.ajax() - success callbacks", 8, function( assert ) {
41-
return {
41+
return {
4242
setup: addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess", assert ),
4343
url: url( "data/name.html" ),
4444
beforeSend: function() {
@@ -437,6 +437,25 @@ QUnit.module( "ajax", {
437437
};
438438
} );
439439

440+
ajaxTest( "jQuery.ajax() - native abort", 2, function( assert ) {
441+
return {
442+
url: url( "data/name.php?wait=1" ),
443+
xhr: function() {
444+
var xhr = new window.XMLHttpRequest();
445+
setTimeout( function() {
446+
xhr.abort();
447+
}, 100 );
448+
return xhr;
449+
},
450+
error: function( xhr, msg ) {
451+
assert.strictEqual( msg, "error", "Native abort triggers error callback" );
452+
},
453+
complete: function() {
454+
assert.ok( true, "complete" );
455+
}
456+
};
457+
} );
458+
440459
ajaxTest( "jQuery.ajax() - events with context", 12, function( assert ) {
441460
var context = document.createElement( "div" );
442461

0 commit comments

Comments
 (0)