From a3172a285fd74b5aa6c8d68a4988c767c06f549c Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 22 Nov 2013 13:22:41 +0000 Subject: [PATCH] fix($httpBackend): only IE8 and below can't use `script.onload` for JSONP IE8, IE9 and IE10 can use `script.onreadystate` so up till now we have been using this if the sniffer says we are on IE. But IE11 now does not support `script.onreadystate` and only supports the more standard `script.onload` and `script.onerror`. IE9 and IE10 do support `script.onload` and `script.onerror`. So now we only test whether we are on IE8 or earlier before using `script.onreadystate`. See http://pieisgood.org/test/script-link-events/ jQuery just uses all these handlers at once and hopes for the best, but since IE9 and IE10 support both sets of handlers, this could cause the handlers to be run more than once. jQuery also notes that there is a potential memory leak in IE unless we remove the handlers from the script object once they are run. So we are doing this too, now. Closes #4523 Closes #4527 Closes #4922 --- src/ng/httpBackend.js | 11 ++++++++--- test/ng/httpBackendSpec.js | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/ng/httpBackend.js b/src/ng/httpBackend.js index 5496589be50f..3982fad0997d 100644 --- a/src/ng/httpBackend.js +++ b/src/ng/httpBackend.js @@ -136,6 +136,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, // - adds and immediately removes script elements from the document var script = rawDocument.createElement('script'), doneWrapper = function() { + script.onreadystatechange = script.onload = script.onerror = null; rawDocument.body.removeChild(script); if (done) done(); }; @@ -143,12 +144,16 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, script.type = 'text/javascript'; script.src = url; - if (msie) { + if (msie && msie <= 8) { script.onreadystatechange = function() { - if (/loaded|complete/.test(script.readyState)) doneWrapper(); + if (/loaded|complete/.test(script.readyState)) { + doneWrapper(); + } }; } else { - script.onload = script.onerror = doneWrapper; + script.onload = script.onerror = function() { + doneWrapper(); + }; } rawDocument.body.appendChild(script); diff --git a/test/ng/httpBackendSpec.js b/test/ng/httpBackendSpec.js index cc73ea8e2d13..0748d1161cf9 100644 --- a/test/ng/httpBackendSpec.js +++ b/test/ng/httpBackendSpec.js @@ -292,7 +292,7 @@ describe('$httpBackend', function() { script.readyState = 'complete'; script.onreadystatechange(); } else { - script.onload() + script.onload(); } expect(callback).toHaveBeenCalledOnce(); @@ -313,7 +313,7 @@ describe('$httpBackend', function() { script.readyState = 'complete'; script.onreadystatechange(); } else { - script.onload() + script.onload(); } expect(callbacks[callbackId]).toBeUndefined(); @@ -321,6 +321,38 @@ describe('$httpBackend', function() { }); + if(msie<=8) { + + it('should attach onreadystatechange handler to the script object', function() { + $backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, noop); + + expect(fakeDocument.$$scripts[0].onreadystatechange).toEqual(jasmine.any(Function)); + + var script = fakeDocument.$$scripts[0]; + + script.readyState = 'complete'; + script.onreadystatechange(); + + expect(script.onreadystatechange).toBe(null); + }); + + } else { + + it('should attach onload and onerror handlers to the script object', function() { + $backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, noop); + + expect(fakeDocument.$$scripts[0].onload).toEqual(jasmine.any(Function)); + expect(fakeDocument.$$scripts[0].onerror).toEqual(jasmine.any(Function)); + + var script = fakeDocument.$$scripts[0]; + script.onload(); + + expect(script.onload).toBe(null); + expect(script.onerror).toBe(null); + }); + + } + it('should call callback with status -2 when script fails to load', function() { callback.andCallFake(function(status, response) { expect(status).toBe(-2); @@ -335,7 +367,7 @@ describe('$httpBackend', function() { script.readyState = 'complete'; script.onreadystatechange(); } else { - script.onload() + script.onload(); } expect(callback).toHaveBeenCalledOnce(); });