From 0eaadd60c716050f5e3701d513a028a9cd49085a Mon Sep 17 00:00:00 2001 From: Ben Lesh <ben@benlesh.com> Date: Fri, 31 Jul 2020 14:59:22 -0500 Subject: [PATCH] refactor(ajax): Drop support for IE10 and lower This gets us in-line with other AJAX implementations like Axios. BREAKING CHANGE: Ajax implementation drops support for IE10 and lower. This puts us in-line with other implementations and helps clean up code in this area --- spec/observables/dom/ajax-spec.ts | 165 +----------------- src/internal/observable/dom/AjaxObservable.ts | 56 +----- 2 files changed, 5 insertions(+), 216 deletions(-) diff --git a/spec/observables/dom/ajax-spec.ts b/spec/observables/dom/ajax-spec.ts index b94ed58d51..c37a8d77b1 100644 --- a/spec/observables/dom/ajax-spec.ts +++ b/spec/observables/dom/ajax-spec.ts @@ -40,22 +40,6 @@ describe('ajax', () => { expect(MockXMLHttpRequest.mostRecent.withCredentials).to.be.false; }); - it('should try to create AXObject for XHR in old version of IE', () => { - const axObjectStub = sandbox.stub(); - axObjectStub.returns(sinon.stub(new MockXMLHttpRequest())); - root.ActiveXObject = axObjectStub; - root.XMLHttpRequest = null; - - const obj: AjaxRequest = { - url: '/', - method: '', - crossDomain: false, - }; - - ajax(obj).subscribe(); - expect(axObjectStub).to.have.been.called; - }); - it('should raise an error if not able to create XMLHttpRequest', () => { root.XMLHttpRequest = null; root.ActiveXObject = null; @@ -80,23 +64,6 @@ describe('ajax', () => { expect(MockXMLHttpRequest.mostRecent.withCredentials).to.be.true; }); - it('should try to create XDomainRequest for CORS if XMLHttpRequest is not available', () => { - const xDomainStub = sandbox.stub(); - xDomainStub.returns(sinon.stub(new MockXMLHttpRequest())); - root.XDomainRequest = xDomainStub; - root.XMLHttpRequest = null; - - const obj: AjaxRequest = { - url: '/', - method: '', - crossDomain: true, - withCredentials: true - }; - - ajax(obj).subscribe(); - expect(xDomainStub).to.have.been.called; - }); - it('should raise an error if not able to create CORS request', () => { root.XMLHttpRequest = null; root.XDomainRequest = null; @@ -820,39 +787,7 @@ describe('ajax', () => { expect(result!.response).to.equal(expected); expect(complete).to.be.true; }); - - it('should succeed in IE on 204 No Content', () => { - const expected: null = null; - let result: AjaxResponse; - let complete = false; - - root.XMLHttpRequest = MockXMLHttpRequestInternetExplorer; - - ajax.post('/flibbertyJibbet', expected) - .subscribe(x => { - result = x; - }, null, () => { - complete = true; - }); - - const request = MockXMLHttpRequest.mostRecent; - - expect(request.method).to.equal('POST'); - expect(request.url).to.equal('/flibbertyJibbet'); - expect(request.requestHeaders).to.deep.equal({ - 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' - }); - - //IE behavior: IE does not provide the a responseText property, so also exercise the code which handles this. - request.respondWith({ - 'status': 204, - 'contentType': 'application/json' - }); - - expect(result!.response).to.equal(expected); - expect(complete).to.be.true; - }); - + it('should emit progress event when progressSubscriber is specified', function() { const spy = sinon.spy(); const progressSubscriber = (<any>{ @@ -881,69 +816,6 @@ describe('ajax', () => { expect(spy).to.be.calledThrice; }); - - it('should emit progress event when progressSubscriber is specified in IE', function() { - const spy = sinon.spy(); - const progressSubscriber = (<any>{ - next: spy, - error: () => { - // noop - }, - complete: () => { - // noop - } - }); - - root.XMLHttpRequest = MockXMLHttpRequestInternetExplorer; - root.XDomainRequest = MockXMLHttpRequestInternetExplorer; - - ajax({ - url: '/flibbertyJibbet', - progressSubscriber - }) - .subscribe(); - - const request = MockXMLHttpRequest.mostRecent; - - request.respondWith({ - 'status': 200, - 'contentType': 'application/json', - 'responseText': JSON.stringify({}) - }, 3); - - expect(spy.callCount).to.equal(3); - }); - - }); - - it('should work fine when XMLHttpRequest onreadystatechange property is monkey patched', function() { - Object.defineProperty(root.XMLHttpRequest.prototype, 'onreadystatechange', { - set: function (fn: (e: ProgressEvent) => any) { - const wrapFn = (ev: ProgressEvent) => { - const result = fn.call(this, ev); - if (result === false) { - ev.preventDefault(); - } - }; - this['_onreadystatechange'] = wrapFn; - }, - get() { - return this['_onreadystatechange']; - }, - configurable: true - }); - - ajax({ - url: '/flibbertyJibbet' - }) - .subscribe(); - - const request = MockXMLHttpRequest.mostRecent; - expect(() => { - request.onreadystatechange((<any>'onreadystatechange')); - }).not.throw(); - - delete root.XMLHttpRequest.prototype.onreadystatechange; }); it('should work fine when XMLHttpRequest ontimeout property is monkey patched', function(done) { @@ -1255,37 +1127,4 @@ class MockXMLHttpRequest { this.upload['on' + name](e); } } -} - -class MockXMLHttpRequestInternetExplorer extends MockXMLHttpRequest { - - private mockHttp204() { - this.responseType = ''; - this.responseText = ''; - this.response = ''; - } - - protected jsonResponseValue(response: any) { - if (this.status == 204) { - this.mockHttp204(); - return; - } - return super.jsonResponseValue(response); - } - - protected defaultResponseValue() { - if (this.status == 204) { - this.mockHttp204(); - return; - } - return super.defaultResponseValue(); - } - - triggerUploadEvent(this: any, name: any, eventObj?: any): void { - // TODO: create a better default event - const e: any = eventObj || {}; - if (this['on' + name]) { - this['on' + name](e); - } - } -} +} \ No newline at end of file diff --git a/src/internal/observable/dom/AjaxObservable.ts b/src/internal/observable/dom/AjaxObservable.ts index 31c39e6209..3ef9b70edc 100644 --- a/src/internal/observable/dom/AjaxObservable.ts +++ b/src/internal/observable/dom/AjaxObservable.ts @@ -21,45 +21,10 @@ export interface AjaxRequest { responseType?: string; } -// Declare older APIs we'll check for on global scope. -declare const XDomainRequest: any; -declare const ActiveXObject: any; - function isFormData(body: any): body is FormData { return typeof FormData !== 'undefined' && body instanceof FormData; } -function getCORSRequest(): XMLHttpRequest { - if (typeof XMLHttpRequest === 'function') { - return new XMLHttpRequest(); - } else if (typeof XDomainRequest === 'function') { - return new XDomainRequest(); - } else { - throw new Error('CORS is not supported by your browser'); - } -} - -const ancientInternetExplorerProgIDs = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; - -function getXMLHttpRequest(): XMLHttpRequest { - if (XMLHttpRequest) { - return new XMLHttpRequest(); - } else if (typeof ActiveXObject === 'function') { - // TODO: Remove when we stop supporting IE. - for (const progId of ancientInternetExplorerProgIDs) { - try { - const axoXHR = new ActiveXObject(progId); - if (axoXHR) { - return axoXHR; - } - } catch (e) { - // Ignore and try the next one - } - } - } - throw new Error('XMLHttpRequest is not supported by your browser'); -} - export interface AjaxCreationMethod { (urlOrRequest: string | AjaxRequest): Observable<AjaxResponse>; get(url: string, headers?: Object): Observable<AjaxResponse>; @@ -162,9 +127,7 @@ export class AjaxObservable<T> extends Observable<T> { const request: AjaxRequest = { async: true, - createXHR: function (this: AjaxRequest) { - return this.crossDomain ? getCORSRequest() : getXMLHttpRequest(); - }, + createXHR: () => new XMLHttpRequest(), crossDomain: true, withCredentials: false, headers: {}, @@ -351,12 +314,8 @@ export class AjaxSubscriber<T> extends Subscriber<Event> { const { progressSubscriber } = <any>xhrProgress; progressSubscriber.next(e); }; - if (typeof XDomainRequest === 'function') { - xhr.onprogress = xhrProgress; - } else { - xhr.upload.onprogress = xhrProgress; - } (<any>xhrProgress).progressSubscriber = progressSubscriber; + xhr.upload.onprogress = xhrProgress; } let xhrError: (e: any) => void; xhrError = function (this: XMLHttpRequest, e: ErrorEvent) { @@ -378,19 +337,10 @@ export class AjaxSubscriber<T> extends Subscriber<Event> { (<any>xhrError).progressSubscriber = progressSubscriber; } - function xhrReadyStateChange(this: XMLHttpRequest) { - return; - } - xhr.onreadystatechange = xhrReadyStateChange; - (<any>xhrReadyStateChange).subscriber = this; - (<any>xhrReadyStateChange).progressSubscriber = progressSubscriber; - (<any>xhrReadyStateChange).request = request; - function xhrLoad(this: XMLHttpRequest, e: Event) { const { subscriber, progressSubscriber, request } = <any>xhrLoad; if (this.readyState === 4) { - // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) - let status: number = this.status === 1223 ? 204 : this.status; + let status = this.status; let response: any = this.responseType === 'text' ? this.response || this.responseText : this.response; // fix status code when it is 0 (0 status is undocumented).