Commit 8ab23aec authored by jaubourg's avatar jaubourg

Fixes #2994. Not finding a transport now fires the error callbacks and doesn't...

Fixes #2994. Not finding a transport now fires the error callbacks and doesn't make ajax return false. Had to revise how jsonp and script prefilters & transports work (better separation of concerns). Also took the opportunity to revise jXHR getRequestHeader and abort methods and enabled early transport garbage collection when the request completes.
parent d9cb6987
...@@ -306,9 +306,7 @@ jQuery.extend({ ...@@ -306,9 +306,7 @@ jQuery.extend({
// (match is used internally) // (match is used internally)
getResponseHeader: function( key , match ) { getResponseHeader: function( key , match ) {
if ( state !== 2 ) { if ( state === 2 ) {
return null;
}
if ( responseHeaders === undefined ) { if ( responseHeaders === undefined ) {
...@@ -321,15 +319,22 @@ jQuery.extend({ ...@@ -321,15 +319,22 @@ jQuery.extend({
} }
} }
} }
return responseHeaders[ key.toLowerCase() ]; match = responseHeaders[ key.toLowerCase() ];
} else {
match = null;
}
return match;
}, },
// Cancel the request // Cancel the request
abort: function( statusText ) { abort: function( statusText ) {
if ( transport && state !== 2 ) { if ( transport ) {
transport.abort( statusText || "abort" ); transport.abort( statusText || "abort" );
done( 0 , statusText );
} }
done( 0 , statusText );
return this; return this;
} }
}; };
...@@ -347,6 +352,10 @@ jQuery.extend({ ...@@ -347,6 +352,10 @@ jQuery.extend({
// State is "done" now // State is "done" now
state = 2; state = 2;
// Dereference transport for early garbage collection
// (no matter how long the jXHR transport will be used
transport = 0;
// Set readyState // Set readyState
jXHR.readyState = status ? 4 : 0; jXHR.readyState = status ? 4 : 0;
...@@ -599,22 +608,14 @@ jQuery.extend({ ...@@ -599,22 +608,14 @@ jQuery.extend({
s.data = jQuery.param( s.data , s.traditional ); s.data = jQuery.param( s.data , s.traditional );
} }
// Get transport // Apply prefilters
transport = jQuery.ajaxPrefilter( s , options ).ajaxTransport( s ); jQuery.ajaxPrefilter( s , options );
// Watch for a new set of requests // Watch for a new set of requests
if ( s.global && jQuery.active++ === 0 ) { if ( s.global && jQuery.active++ === 0 ) {
jQuery.event.trigger( "ajaxStart" ); jQuery.event.trigger( "ajaxStart" );
} }
// If no transport, we auto-abort
if ( ! transport ) {
done( 0 , "transport not found" );
jXHR = false;
} else {
// More options handling for requests with no content // More options handling for requests with no content
if ( ! s.hasContent ) { if ( ! s.hasContent ) {
...@@ -665,19 +666,30 @@ jQuery.extend({ ...@@ -665,19 +666,30 @@ jQuery.extend({
// Abort if not done already // Abort if not done already
done( 0 , "abort" ); done( 0 , "abort" );
// Return false
jXHR = false; jXHR = false;
} else { } else {
// Set state as sending
state = 1;
jXHR.readyState = 1;
// Install callbacks on deferreds // Install callbacks on deferreds
for ( i in { success:1, error:1, complete:1 } ) { for ( i in { success:1, error:1, complete:1 } ) {
jXHR[ i ]( s[ i ] ); jXHR[ i ]( s[ i ] );
} }
// Get transport
transport = jQuery.ajaxTransport( s );
// If no transport, we auto-abort
if ( ! transport ) {
done( 0 , "notransport" );
} else {
// Set state as sending
state = jXHR.readyState = 1;
// Send global event // Send global event
if ( s.global ) { if ( s.global ) {
globalEventContext.trigger( "ajaxSend" , [ jXHR , s ] ); globalEventContext.trigger( "ajaxSend" , [ jXHR , s ] );
......
...@@ -11,9 +11,7 @@ jQuery.ajaxSetup({ ...@@ -11,9 +11,7 @@ jQuery.ajaxSetup({
return "jsonp" + jsc++; return "jsonp" + jsc++;
} }
// Normalize jsonp queries // Detect, normalize options and install callbacks for jsonp requests
// 1) put callback parameter in url or data
// 2) sneakily ensure transportDataType is always jsonp for jsonp requests
}).ajaxPrefilter("json jsonp", function(s, originalSettings) { }).ajaxPrefilter("json jsonp", function(s, originalSettings) {
if ( s.dataTypes[ 0 ] === "jsonp" || if ( s.dataTypes[ 0 ] === "jsonp" ||
...@@ -22,8 +20,10 @@ jQuery.ajaxSetup({ ...@@ -22,8 +20,10 @@ jQuery.ajaxSetup({
jsre.test(s.url) || jsre.test(s.url) ||
typeof(s.data) === "string" && jsre.test(s.data) ) { typeof(s.data) === "string" && jsre.test(s.data) ) {
var jsonpCallback = s.jsonpCallback = var responseContainer,
jsonpCallback = s.jsonpCallback =
jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback, jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
previous = window[ jsonpCallback ],
url = s.url.replace(jsre, "$1" + jsonpCallback + "$2"), url = s.url.replace(jsre, "$1" + jsonpCallback + "$2"),
data = s.url === url && typeof(s.data) === "string" ? s.data.replace(jsre, "$1" + jsonpCallback + "$2") : s.data; data = s.url === url && typeof(s.data) === "string" ? s.data.replace(jsre, "$1" + jsonpCallback + "$2") : s.data;
...@@ -33,16 +33,6 @@ jQuery.ajaxSetup({ ...@@ -33,16 +33,6 @@ jQuery.ajaxSetup({
s.url = url; s.url = url;
s.data = data; s.data = data;
s.dataTypes[ 0 ] = "jsonp";
}
// Bind transport to jsonp dataType
}).ajaxTransport("jsonp", function(s) {
// Put callback in place
var responseContainer,
jsonpCallback = s.jsonpCallback,
previous = window[ jsonpCallback ];
window [ jsonpCallback ] = function( response ) { window [ jsonpCallback ] = function( response ) {
responseContainer = [response]; responseContainer = [response];
...@@ -65,9 +55,6 @@ jQuery.ajaxSetup({ ...@@ -65,9 +55,6 @@ jQuery.ajaxSetup({
}, s.complete ]; }, s.complete ];
// Sneakily ensure this will be handled as json
s.dataTypes[ 0 ] = "json";
// Use data converter to retrieve json after script execution // Use data converter to retrieve json after script execution
s.converters["script json"] = function() { s.converters["script json"] = function() {
if ( ! responseContainer ) { if ( ! responseContainer ) {
...@@ -76,8 +63,12 @@ jQuery.ajaxSetup({ ...@@ -76,8 +63,12 @@ jQuery.ajaxSetup({
return responseContainer[ 0 ]; return responseContainer[ 0 ];
}; };
// Delegate to script transport // force json dataType
s.dataTypes[ 0 ] = "json";
// Delegate to script
return "script"; return "script";
}
}); });
})( jQuery ); })( jQuery );
...@@ -15,18 +15,22 @@ jQuery.ajaxSetup({ ...@@ -15,18 +15,22 @@ jQuery.ajaxSetup({
"text script": jQuery.globalEval "text script": jQuery.globalEval
} }
// Bind script tag hack transport // Handle cache's special case and global
}).ajaxTransport("script", function(s) { }).ajaxPrefilter("script", function(s) {
// Handle cache special case
if ( s.cache === undefined ) { if ( s.cache === undefined ) {
s.cache = false; s.cache = false;
} }
// This transport only deals with cross domain get requests if ( s.crossDomain ) {
if ( s.crossDomain && s.async && ( s.type === "GET" || ! s.data ) ) {
s.global = false; s.global = false;
}
// Bind script tag hack transport
}).ajaxTransport("script", function(s) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script, var script,
head = document.getElementsByTagName("head")[0] || document.documentElement; head = document.getElementsByTagName("head")[0] || document.documentElement;
......
...@@ -1865,25 +1865,19 @@ test("jQuery ajax - failing cross-domain", function() { ...@@ -1865,25 +1865,19 @@ test("jQuery ajax - failing cross-domain", function() {
var i = 2; var i = 2;
if ( jQuery.ajax({ jQuery.ajax({
url: 'http://somewebsitethatdoesnotexist-67864863574657654.com', url: 'http://somewebsitethatdoesnotexist-67864863574657654.com',
success: function(){ ok( false , "success" ); }, success: function(){ ok( false , "success" ); },
error: function(xhr,_,e){ ok( true , "file not found: " + xhr.status + " => " + e ); }, error: function(xhr,_,e){ ok( true , "file not found: " + xhr.status + " => " + e ); },
complete: function() { if ( ! --i ) start(); } complete: function() { if ( ! --i ) start(); }
}) === false ) { });
ok( true , "no transport" );
if ( ! --i ) start();
}
if ( jQuery.ajax({ jQuery.ajax({
url: 'http://www.google.com', url: 'http://www.google.com',
success: function(){ ok( false , "success" ); }, success: function(){ ok( false , "success" ); },
error: function(xhr,_,e){ ok( true , "access denied: " + xhr.status + " => " + e ); }, error: function(xhr,_,e){ ok( true , "access denied: " + xhr.status + " => " + e ); },
complete: function() { if ( ! --i ) start(); } complete: function() { if ( ! --i ) start(); }
}) === false ) { });
ok( true , "no transport" );
if ( ! --i ) start();
}
}); });
...@@ -1937,7 +1931,7 @@ test( "jQuery.ajax - statusCode" , function() { ...@@ -1937,7 +1931,7 @@ test( "jQuery.ajax - statusCode" , function() {
404: function() { 404: function() {
ok( ! isSuccess , name ); ok( ! isSuccess , name );
} }
} };
} }
jQuery.each( { jQuery.each( {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment