Skip to content

Commit

Permalink
refactor(ajax): Drop support for IE10 and lower
Browse files Browse the repository at this point in the history
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
  • Loading branch information
benlesh committed Aug 2, 2020
1 parent 889a9e9 commit 0eaadd6
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 216 deletions.
165 changes: 2 additions & 163 deletions spec/observables/dom/ajax-spec.ts
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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>{
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
}
}
}
56 changes: 3 additions & 53 deletions src/internal/observable/dom/AjaxObservable.ts
Expand Up @@ -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>;
Expand Down Expand Up @@ -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: {},
Expand Down Expand Up @@ -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) {
Expand All @@ -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).
Expand Down

0 comments on commit 0eaadd6

Please sign in to comment.