Correct semantics for XMLHttpRequest.responseText
Summary: Accessing the `responseText` property when `responseType` is not `''` or `'text'` should throw. Also, the property is read-only. **Test Plan:** UIExplorer example, unit tests Closes https://github.com/facebook/react-native/pull/7284 Differential Revision: D3366893 fbshipit-source-id: a4cf5ebabcd1e03d6e2dc9d51230982922746c11
This commit is contained in:
parent
16a97c8027
commit
e29350214a
|
@ -85,7 +85,6 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
|
||||
readyState: number = UNSENT;
|
||||
responseHeaders: ?Object;
|
||||
responseText: string = '';
|
||||
status: number = 0;
|
||||
timeout: number = 0;
|
||||
responseURL: ?string;
|
||||
|
@ -103,6 +102,7 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
_method: ?string = null;
|
||||
_response: string | ?Object;
|
||||
_responseType: ResponseType;
|
||||
_responseText: string = '';
|
||||
_sent: boolean;
|
||||
_url: ?string = null;
|
||||
_timedOut: boolean = false;
|
||||
|
@ -116,7 +116,6 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
_reset(): void {
|
||||
this.readyState = this.UNSENT;
|
||||
this.responseHeaders = undefined;
|
||||
this.responseText = '';
|
||||
this.status = 0;
|
||||
delete this.responseURL;
|
||||
|
||||
|
@ -125,6 +124,7 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
this._cachedResponse = undefined;
|
||||
this._hasError = false;
|
||||
this._headers = {};
|
||||
this._responseText = '';
|
||||
this._responseType = '';
|
||||
this._sent = false;
|
||||
this._lowerCaseResponseHeaders = {};
|
||||
|
@ -148,7 +148,9 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
}
|
||||
if (!SUPPORTED_RESPONSE_TYPES.hasOwnProperty(responseType)) {
|
||||
warning(
|
||||
`The provided value '${responseType}' is not a valid 'responseType'.`);
|
||||
false,
|
||||
`The provided value '${responseType}' is not a valid 'responseType'.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -160,13 +162,27 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
this._responseType = responseType;
|
||||
}
|
||||
|
||||
// $FlowIssue #10784535
|
||||
get responseText(): string {
|
||||
if (this._responseType !== '' && this._responseType !== 'text') {
|
||||
throw new Error(
|
||||
`The 'responseText' property is only available if 'responseType' ` +
|
||||
`is set to '' or 'text', but it is '${this._responseType}'.`
|
||||
);
|
||||
}
|
||||
if (this.readyState < LOADING) {
|
||||
return '';
|
||||
}
|
||||
return this._responseText;
|
||||
}
|
||||
|
||||
// $FlowIssue #10784535
|
||||
get response(): Response {
|
||||
const {responseType} = this;
|
||||
if (responseType === '' || responseType === 'text') {
|
||||
return this.readyState < LOADING || this._hasError
|
||||
? ''
|
||||
: this.responseText;
|
||||
: this._responseText;
|
||||
}
|
||||
|
||||
if (this.readyState !== DONE) {
|
||||
|
@ -177,26 +193,26 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
return this._cachedResponse;
|
||||
}
|
||||
|
||||
switch (this.responseType) {
|
||||
switch (this._responseType) {
|
||||
case 'document':
|
||||
this._cachedResponse = null;
|
||||
break;
|
||||
|
||||
case 'arraybuffer':
|
||||
this._cachedResponse = toArrayBuffer(
|
||||
this.responseText, this.getResponseHeader('content-type') || '');
|
||||
this._responseText, this.getResponseHeader('content-type') || '');
|
||||
break;
|
||||
|
||||
case 'blob':
|
||||
this._cachedResponse = new global.Blob(
|
||||
[this.responseText],
|
||||
[this._responseText],
|
||||
{type: this.getResponseHeader('content-type') || ''}
|
||||
);
|
||||
break;
|
||||
|
||||
case 'json':
|
||||
try {
|
||||
this._cachedResponse = JSON.parse(this.responseText);
|
||||
this._cachedResponse = JSON.parse(this._responseText);
|
||||
} catch (_) {
|
||||
this._cachedResponse = null;
|
||||
}
|
||||
|
@ -226,7 +242,12 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
}
|
||||
}
|
||||
|
||||
_didReceiveResponse(requestId: number, status: number, responseHeaders: ?Object, responseURL: ?string): void {
|
||||
__didReceiveResponse(
|
||||
requestId: number,
|
||||
status: number,
|
||||
responseHeaders: ?Object,
|
||||
responseURL: ?string
|
||||
): void {
|
||||
if (requestId === this._requestId) {
|
||||
this.status = status;
|
||||
this.setResponseHeaders(responseHeaders);
|
||||
|
@ -239,12 +260,12 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
}
|
||||
}
|
||||
|
||||
_didReceiveData(requestId: number, responseText: string): void {
|
||||
__didReceiveData(requestId: number, responseText: string): void {
|
||||
if (requestId === this._requestId) {
|
||||
if (!this.responseText) {
|
||||
this.responseText = responseText;
|
||||
if (!this._responseText) {
|
||||
this._responseText = responseText;
|
||||
} else {
|
||||
this.responseText += responseText;
|
||||
this._responseText += responseText;
|
||||
}
|
||||
this._cachedResponse = undefined; // force lazy recomputation
|
||||
this.setReadyState(this.LOADING);
|
||||
|
@ -252,10 +273,14 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
}
|
||||
|
||||
// exposed for testing
|
||||
__didCompleteResponse(requestId: number, error: string, timeOutError: boolean): void {
|
||||
__didCompleteResponse(
|
||||
requestId: number,
|
||||
error: string,
|
||||
timeOutError: boolean
|
||||
): void {
|
||||
if (requestId === this._requestId) {
|
||||
if (error) {
|
||||
this.responseText = error;
|
||||
this._responseText = error;
|
||||
this._hasError = true;
|
||||
if (timeOutError) {
|
||||
this._timedOut = true;
|
||||
|
@ -330,11 +355,11 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
|
|||
));
|
||||
this._subscriptions.push(RCTNetworking.addListener(
|
||||
'didReceiveNetworkResponse',
|
||||
(args) => this._didReceiveResponse(...args)
|
||||
(args) => this.__didReceiveResponse(...args)
|
||||
));
|
||||
this._subscriptions.push(RCTNetworking.addListener(
|
||||
'didReceiveNetworkData',
|
||||
(args) => this._didReceiveData(...args)
|
||||
(args) => this.__didReceiveData(...args)
|
||||
));
|
||||
this._subscriptions.push(RCTNetworking.addListener(
|
||||
'didCompleteNetworkResponse',
|
||||
|
|
|
@ -56,15 +56,53 @@ describe('XMLHttpRequest', function(){
|
|||
handleLoad = null;
|
||||
});
|
||||
|
||||
it('should transition readyState correctly', function() {
|
||||
expect(xhr.readyState).toBe(xhr.UNSENT);
|
||||
it('should transition readyState correctly', function() {
|
||||
|
||||
expect(xhr.readyState).toBe(xhr.UNSENT);
|
||||
|
||||
xhr.open('GET', 'blabla');
|
||||
|
||||
expect(xhr.onreadystatechange.mock.calls.length).toBe(1);
|
||||
expect(handleReadyStateChange.mock.calls.length).toBe(1);
|
||||
expect(xhr.readyState).toBe(xhr.OPENED);
|
||||
});
|
||||
});
|
||||
|
||||
it('should expose responseType correctly', function() {
|
||||
expect(xhr.responseType).toBe('');
|
||||
|
||||
// Setting responseType to an unsupported value has no effect.
|
||||
xhr.responseType = 'arrayblobbuffertextfile';
|
||||
expect(xhr.responseType).toBe('');
|
||||
|
||||
xhr.responseType = 'arraybuffer';
|
||||
expect(xhr.responseType).toBe('arraybuffer');
|
||||
|
||||
// Can't change responseType after first data has been received.
|
||||
xhr.__didReceiveData(1, 'Some data');
|
||||
expect(() => { xhr.responseType = 'text'; }).toThrow();
|
||||
});
|
||||
|
||||
it('should expose responseText correctly', function() {
|
||||
xhr.responseType = '';
|
||||
expect(xhr.responseText).toBe('');
|
||||
expect(xhr.response).toBe('');
|
||||
|
||||
xhr.responseType = 'arraybuffer';
|
||||
expect(() => xhr.responseText).toThrow();
|
||||
expect(xhr.response).toBe(null);
|
||||
|
||||
xhr.responseType = 'text';
|
||||
expect(xhr.responseText).toBe('');
|
||||
expect(xhr.response).toBe('');
|
||||
|
||||
// responseText is read-only.
|
||||
expect(() => { xhr.responseText = 'hi'; }).toThrow();
|
||||
expect(xhr.responseText).toBe('');
|
||||
expect(xhr.response).toBe('');
|
||||
|
||||
xhr.__didReceiveData(1, 'Some data');
|
||||
expect(xhr.responseText).toBe('Some data');
|
||||
});
|
||||
|
||||
it('should call ontimeout function when the request times out', function(){
|
||||
xhr.__didCompleteResponse(1, 'Timeout', true);
|
||||
|
|
Loading…
Reference in New Issue