diff --git a/android/capacitor/src/main/assets/native-bridge.js b/android/capacitor/src/main/assets/native-bridge.js index 8d23090f57..59b22caadb 100644 --- a/android/capacitor/src/main/assets/native-bridge.js +++ b/android/capacitor/src/main/assets/native-bridge.js @@ -391,9 +391,11 @@ var nativeBridge = (function (exports) { win.CapacitorWebFetch = window.fetch; win.CapacitorWebXMLHttpRequest = { abort: window.XMLHttpRequest.prototype.abort, + constructor: window.XMLHttpRequest.prototype.constructor, getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, open: window.XMLHttpRequest.prototype.open, + prototype: window.XMLHttpRequest.prototype, send: window.XMLHttpRequest.prototype.send, setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, }; @@ -469,60 +471,15 @@ var nativeBridge = (function (exports) { return Promise.reject(error); } }; - // XHR event listeners - const addEventListeners = function () { - this.addEventListener('abort', function () { - if (typeof this.onabort === 'function') - this.onabort(); - }); - this.addEventListener('error', function () { - if (typeof this.onerror === 'function') - this.onerror(); - }); - this.addEventListener('load', function () { - if (typeof this.onload === 'function') - this.onload(); - }); - this.addEventListener('loadend', function () { - if (typeof this.onloadend === 'function') - this.onloadend(); - }); - this.addEventListener('loadstart', function () { - if (typeof this.onloadstart === 'function') - this.onloadstart(); - }); - this.addEventListener('readystatechange', function () { - if (typeof this.onreadystatechange === 'function') - this.onreadystatechange(); - }); - this.addEventListener('timeout', function () { - if (typeof this.ontimeout === 'function') - this.ontimeout(); - }); - }; - // XHR patch abort - window.XMLHttpRequest.prototype.abort = function () { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.abort.call(this); - } - this.readyState = 0; - this.dispatchEvent(new Event('abort')); - this.dispatchEvent(new Event('loadend')); - }; - // XHR patch open - window.XMLHttpRequest.prototype.open = function (method, url) { - this._url = url; - if (!(url.startsWith('http:') || url.toString().startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); - } - Object.defineProperties(this, { + window.XMLHttpRequest = function () { + const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); + Object.defineProperties(xhr, { _headers: { value: {}, writable: true, }, _method: { - value: method, + value: xhr.method, writable: true, }, readyState: { @@ -532,7 +489,9 @@ var nativeBridge = (function (exports) { }, set: function (val) { this._readyState = val; - this.dispatchEvent(new Event('readystatechange')); + setTimeout(() => { + this.dispatchEvent(new Event('readystatechange')); + }); }, }, response: { @@ -552,106 +511,159 @@ var nativeBridge = (function (exports) { writable: true, }, }); - addEventListeners.call(this); - this.readyState = 1; - }; - // XHR patch set request header - window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); - } - this._headers[header] = value; - }; - // XHR patch send - window.XMLHttpRequest.prototype.send = function (body) { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.send.call(this, body); - } - const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; - console.time(tag); - try { - this.readyState = 2; - convertBody(body).then(({ data, type, headers }) => { - const otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 - ? this._headers - : undefined; - // intercept request & pass to the bridge - cap - .nativePromise('CapacitorHttp', 'request', { - url: this._url, - method: this._method, - data: data !== null ? data : undefined, - headers: Object.assign(Object.assign({}, headers), otherHeaders), - dataType: type, - }) - .then((nativeResponse) => { - var _a; - // intercept & parse response before returning - if (this.readyState == 2) { - this.dispatchEvent(new Event('loadstart')); - this._headers = nativeResponse.headers; - this.status = nativeResponse.status; - this.response = nativeResponse.data; - this.responseText = ((_a = nativeResponse.headers['Content-Type']) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - this.responseURL = nativeResponse.url; + xhr.readyState = 0; + const prototype = win.CapacitorWebXMLHttpRequest.prototype; + // XHR patch abort + prototype.abort = function () { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } + this.readyState = 0; + setTimeout(() => { + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }); + }; + // XHR patch open + prototype.open = function (method, url) { + this._url = url; + if (!(url.startsWith('http:') || url.toString().startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + } + setTimeout(() => { + this.dispatchEvent(new Event('loadstart')); + }); + this.readyState = 1; + }; + // XHR patch set request header + prototype.setRequestHeader = function (header, value) { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); + } + this._headers[header] = value; + }; + // XHR patch send + prototype.send = function (body) { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } + const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; + console.time(tag); + try { + this.readyState = 2; + convertBody(body).then(({ data, type, headers }) => { + const otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 + ? this._headers + : undefined; + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: data !== null ? data : undefined, + headers: Object.assign(Object.assign({}, headers), otherHeaders), + dataType: type, + }) + .then((nativeResponse) => { + var _a; + // intercept & parse response before returning + if (this.readyState == 2) { + //TODO: Add progress event emission on native side + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: true, + loaded: nativeResponse.data.length, + total: nativeResponse.data.length, + })); + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + if (this.responseType === '' || + this.responseType === 'text') { + this.response = + typeof nativeResponse.data !== 'string' + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + } + else { + this.response = nativeResponse.data; + } + this.responseText = ((_a = nativeResponse.headers['Content-Type']) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + this.responseURL = nativeResponse.url; + this.readyState = 4; + setTimeout(() => { + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + }); + } + console.timeEnd(tag); + }) + .catch((error) => { + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; this.readyState = 4; - this.dispatchEvent(new Event('load')); - this.dispatchEvent(new Event('loadend')); - } - console.timeEnd(tag); - }) - .catch((error) => { - this.dispatchEvent(new Event('loadstart')); - this.status = error.status; - this._headers = error.headers; - this.response = error.data; - this.responseText = JSON.stringify(error.data); - this.responseURL = error.url; - this.readyState = 4; + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + }); + }); + } + catch (error) { + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + setTimeout(() => { this.dispatchEvent(new Event('error')); this.dispatchEvent(new Event('loadend')); - console.timeEnd(tag); }); - }); - } - catch (error) { - this.dispatchEvent(new Event('loadstart')); - this.status = 500; - this._headers = {}; - this.response = error; - this.responseText = error.toString(); - this.responseURL = this._url; - this.readyState = 4; - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - console.timeEnd(tag); - } - }; - // XHR patch getAllResponseHeaders - window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); - } - let returnString = ''; - for (const key in this._headers) { - if (key != 'Set-Cookie') { - returnString += key + ': ' + this._headers[key] + '\r\n'; + console.timeEnd(tag); } - } - return returnString; - }; - // XHR patch getResponseHeader - window.XMLHttpRequest.prototype.getResponseHeader = function (name) { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); - } - return this._headers[name]; + }; + // XHR patch getAllResponseHeaders + prototype.getAllResponseHeaders = function () { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); + } + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + // XHR patch getResponseHeader + prototype.getResponseHeader = function (name) { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); + } + return this._headers[name]; + }; + Object.setPrototypeOf(xhr, prototype); + return xhr; }; } } diff --git a/core/native-bridge.ts b/core/native-bridge.ts index 3491eadd2b..c691036603 100644 --- a/core/native-bridge.ts +++ b/core/native-bridge.ts @@ -432,10 +432,12 @@ const initBridge = (w: any): void => { win.CapacitorWebFetch = window.fetch; win.CapacitorWebXMLHttpRequest = { abort: window.XMLHttpRequest.prototype.abort, + constructor: window.XMLHttpRequest.prototype.constructor, getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, open: window.XMLHttpRequest.prototype.open, + prototype: window.XMLHttpRequest.prototype, send: window.XMLHttpRequest.prototype.send, setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, }; @@ -542,71 +544,20 @@ const initBridge = (w: any): void => { } }; - // XHR event listeners - const addEventListeners = function () { - this.addEventListener('abort', function () { - if (typeof this.onabort === 'function') this.onabort(); - }); - - this.addEventListener('error', function () { - if (typeof this.onerror === 'function') this.onerror(); - }); - - this.addEventListener('load', function () { - if (typeof this.onload === 'function') this.onload(); - }); - - this.addEventListener('loadend', function () { - if (typeof this.onloadend === 'function') this.onloadend(); - }); - - this.addEventListener('loadstart', function () { - if (typeof this.onloadstart === 'function') this.onloadstart(); - }); - - this.addEventListener('readystatechange', function () { - if (typeof this.onreadystatechange === 'function') - this.onreadystatechange(); - }); - - this.addEventListener('timeout', function () { - if (typeof this.ontimeout === 'function') this.ontimeout(); - }); - }; - - // XHR patch abort - window.XMLHttpRequest.prototype.abort = function () { - if ( - this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:')) - ) { - return win.CapacitorWebXMLHttpRequest.abort.call(this); - } - this.readyState = 0; - this.dispatchEvent(new Event('abort')); - this.dispatchEvent(new Event('loadend')); - }; - - // XHR patch open - window.XMLHttpRequest.prototype.open = function ( - method: string, - url: string, - ) { - this._url = url; - - if ( - !(url.startsWith('http:') || url.toString().startsWith('https:')) - ) { - return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); - } + // XHR patch + interface PatchedXMLHttpRequestConstructor extends XMLHttpRequest { + new (): XMLHttpRequest; + } - Object.defineProperties(this, { + window.XMLHttpRequest = function () { + const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); + Object.defineProperties(xhr, { _headers: { value: {}, writable: true, }, _method: { - value: method, + value: xhr.method, writable: true, }, readyState: { @@ -615,7 +566,9 @@ const initBridge = (w: any): void => { }, set: function (val: number) { this._readyState = val; - this.dispatchEvent(new Event('readystatechange')); + setTimeout(() => { + this.dispatchEvent(new Event('readystatechange')); + }); }, }, response: { @@ -636,141 +589,214 @@ const initBridge = (w: any): void => { }, }); - addEventListeners.call(this); - this.readyState = 1; - }; + xhr.readyState = 0; + const prototype = win.CapacitorWebXMLHttpRequest.prototype; - // XHR patch set request header - window.XMLHttpRequest.prototype.setRequestHeader = function ( - header: string, - value: string, - ) { - if ( - this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:')) - ) { - return win.CapacitorWebXMLHttpRequest.setRequestHeader.call( - this, - header, - value, - ); - } - this._headers[header] = value; - }; + // XHR patch abort + prototype.abort = function () { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } + this.readyState = 0; + setTimeout(() => { + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }); + }; - // XHR patch send - window.XMLHttpRequest.prototype.send = function ( - body?: Document | XMLHttpRequestBodyInit, - ) { - if ( - this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:')) - ) { - return win.CapacitorWebXMLHttpRequest.send.call(this, body); - } + // XHR patch open + prototype.open = function (method: string, url: string) { + this._url = url; - const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; - console.time(tag); + if ( + !(url.startsWith('http:') || url.toString().startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.open.call( + this, + method, + url, + ); + } - try { - this.readyState = 2; - convertBody(body).then(({ data, type, headers }) => { - const otherHeaders = - this._headers != null && Object.keys(this._headers).length > 0 - ? this._headers - : undefined; - - // intercept request & pass to the bridge - cap - .nativePromise('CapacitorHttp', 'request', { - url: this._url, - method: this._method, - data: data !== null ? data : undefined, - headers: { - ...headers, - ...otherHeaders, - }, - dataType: type, - }) - .then((nativeResponse: any) => { - // intercept & parse response before returning - if (this.readyState == 2) { - this.dispatchEvent(new Event('loadstart')); - this._headers = nativeResponse.headers; - this.status = nativeResponse.status; - this.response = nativeResponse.data; - this.responseText = nativeResponse.headers[ - 'Content-Type' - ]?.startsWith('application/json') - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - this.responseURL = nativeResponse.url; - this.readyState = 4; - this.dispatchEvent(new Event('load')); - this.dispatchEvent(new Event('loadend')); - } - console.timeEnd(tag); - }) - .catch((error: any) => { - this.dispatchEvent(new Event('loadstart')); - this.status = error.status; - this._headers = error.headers; - this.response = error.data; - this.responseText = JSON.stringify(error.data); - this.responseURL = error.url; - this.readyState = 4; - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - console.timeEnd(tag); - }); + setTimeout(() => { + this.dispatchEvent(new Event('loadstart')); }); - } catch (error) { - this.dispatchEvent(new Event('loadstart')); - this.status = 500; - this._headers = {}; - this.response = error; - this.responseText = error.toString(); - this.responseURL = this._url; - this.readyState = 4; - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - console.timeEnd(tag); - } - }; + this.readyState = 1; + }; - // XHR patch getAllResponseHeaders - window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { - if ( - this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:')) + // XHR patch set request header + prototype.setRequestHeader = function ( + header: string, + value: string, ) { - return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call( - this, - ); - } + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call( + this, + header, + value, + ); + } + this._headers[header] = value; + }; - let returnString = ''; - for (const key in this._headers) { - if (key != 'Set-Cookie') { - returnString += key + ': ' + this._headers[key] + '\r\n'; + // XHR patch send + prototype.send = function (body?: Document | XMLHttpRequestBodyInit) { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); } - } - return returnString; - }; - // XHR patch getResponseHeader - window.XMLHttpRequest.prototype.getResponseHeader = function (name) { - if ( - this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:')) - ) { - return win.CapacitorWebXMLHttpRequest.getResponseHeader.call( - this, - name, - ); - } - return this._headers[name]; - }; + const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${ + this._url + }`; + console.time(tag); + + try { + this.readyState = 2; + convertBody(body).then(({ data, type, headers }) => { + const otherHeaders = + this._headers != null && Object.keys(this._headers).length > 0 + ? this._headers + : undefined; + + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: data !== null ? data : undefined, + headers: { + ...headers, + ...otherHeaders, + }, + dataType: type, + }) + .then((nativeResponse: any) => { + // intercept & parse response before returning + if (this.readyState == 2) { + //TODO: Add progress event emission on native side + this.dispatchEvent( + new ProgressEvent('progress', { + lengthComputable: true, + loaded: nativeResponse.data.length, + total: nativeResponse.data.length, + }), + ); + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + if ( + this.responseType === '' || + this.responseType === 'text' + ) { + this.response = + typeof nativeResponse.data !== 'string' + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + } else { + this.response = nativeResponse.data; + } + this.responseText = nativeResponse.headers[ + 'Content-Type' + ]?.startsWith('application/json') + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + this.responseURL = nativeResponse.url; + this.readyState = 4; + setTimeout(() => { + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + }); + } + console.timeEnd(tag); + }) + .catch((error: any) => { + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; + this.readyState = 4; + this.dispatchEvent( + new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + }), + ); + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + }); + }); + } catch (error) { + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + this.dispatchEvent( + new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + }), + ); + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + } + }; + + // XHR patch getAllResponseHeaders + prototype.getAllResponseHeaders = function () { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call( + this, + ); + } + + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + + // XHR patch getResponseHeader + prototype.getResponseHeader = function (name: string) { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call( + this, + name, + ); + } + return this._headers[name]; + }; + + Object.setPrototypeOf(xhr, prototype); + return xhr; + } as unknown as PatchedXMLHttpRequestConstructor; } } diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js index 8d23090f57..59b22caadb 100644 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ b/ios/Capacitor/Capacitor/assets/native-bridge.js @@ -391,9 +391,11 @@ var nativeBridge = (function (exports) { win.CapacitorWebFetch = window.fetch; win.CapacitorWebXMLHttpRequest = { abort: window.XMLHttpRequest.prototype.abort, + constructor: window.XMLHttpRequest.prototype.constructor, getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, open: window.XMLHttpRequest.prototype.open, + prototype: window.XMLHttpRequest.prototype, send: window.XMLHttpRequest.prototype.send, setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, }; @@ -469,60 +471,15 @@ var nativeBridge = (function (exports) { return Promise.reject(error); } }; - // XHR event listeners - const addEventListeners = function () { - this.addEventListener('abort', function () { - if (typeof this.onabort === 'function') - this.onabort(); - }); - this.addEventListener('error', function () { - if (typeof this.onerror === 'function') - this.onerror(); - }); - this.addEventListener('load', function () { - if (typeof this.onload === 'function') - this.onload(); - }); - this.addEventListener('loadend', function () { - if (typeof this.onloadend === 'function') - this.onloadend(); - }); - this.addEventListener('loadstart', function () { - if (typeof this.onloadstart === 'function') - this.onloadstart(); - }); - this.addEventListener('readystatechange', function () { - if (typeof this.onreadystatechange === 'function') - this.onreadystatechange(); - }); - this.addEventListener('timeout', function () { - if (typeof this.ontimeout === 'function') - this.ontimeout(); - }); - }; - // XHR patch abort - window.XMLHttpRequest.prototype.abort = function () { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.abort.call(this); - } - this.readyState = 0; - this.dispatchEvent(new Event('abort')); - this.dispatchEvent(new Event('loadend')); - }; - // XHR patch open - window.XMLHttpRequest.prototype.open = function (method, url) { - this._url = url; - if (!(url.startsWith('http:') || url.toString().startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); - } - Object.defineProperties(this, { + window.XMLHttpRequest = function () { + const xhr = new win.CapacitorWebXMLHttpRequest.constructor(); + Object.defineProperties(xhr, { _headers: { value: {}, writable: true, }, _method: { - value: method, + value: xhr.method, writable: true, }, readyState: { @@ -532,7 +489,9 @@ var nativeBridge = (function (exports) { }, set: function (val) { this._readyState = val; - this.dispatchEvent(new Event('readystatechange')); + setTimeout(() => { + this.dispatchEvent(new Event('readystatechange')); + }); }, }, response: { @@ -552,106 +511,159 @@ var nativeBridge = (function (exports) { writable: true, }, }); - addEventListeners.call(this); - this.readyState = 1; - }; - // XHR patch set request header - window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); - } - this._headers[header] = value; - }; - // XHR patch send - window.XMLHttpRequest.prototype.send = function (body) { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.send.call(this, body); - } - const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; - console.time(tag); - try { - this.readyState = 2; - convertBody(body).then(({ data, type, headers }) => { - const otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 - ? this._headers - : undefined; - // intercept request & pass to the bridge - cap - .nativePromise('CapacitorHttp', 'request', { - url: this._url, - method: this._method, - data: data !== null ? data : undefined, - headers: Object.assign(Object.assign({}, headers), otherHeaders), - dataType: type, - }) - .then((nativeResponse) => { - var _a; - // intercept & parse response before returning - if (this.readyState == 2) { - this.dispatchEvent(new Event('loadstart')); - this._headers = nativeResponse.headers; - this.status = nativeResponse.status; - this.response = nativeResponse.data; - this.responseText = ((_a = nativeResponse.headers['Content-Type']) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) - ? JSON.stringify(nativeResponse.data) - : nativeResponse.data; - this.responseURL = nativeResponse.url; + xhr.readyState = 0; + const prototype = win.CapacitorWebXMLHttpRequest.prototype; + // XHR patch abort + prototype.abort = function () { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } + this.readyState = 0; + setTimeout(() => { + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }); + }; + // XHR patch open + prototype.open = function (method, url) { + this._url = url; + if (!(url.startsWith('http:') || url.toString().startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + } + setTimeout(() => { + this.dispatchEvent(new Event('loadstart')); + }); + this.readyState = 1; + }; + // XHR patch set request header + prototype.setRequestHeader = function (header, value) { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); + } + this._headers[header] = value; + }; + // XHR patch send + prototype.send = function (body) { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } + const tag = `CapacitorHttp XMLHttpRequest ${Date.now()} ${this._url}`; + console.time(tag); + try { + this.readyState = 2; + convertBody(body).then(({ data, type, headers }) => { + const otherHeaders = this._headers != null && Object.keys(this._headers).length > 0 + ? this._headers + : undefined; + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: data !== null ? data : undefined, + headers: Object.assign(Object.assign({}, headers), otherHeaders), + dataType: type, + }) + .then((nativeResponse) => { + var _a; + // intercept & parse response before returning + if (this.readyState == 2) { + //TODO: Add progress event emission on native side + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: true, + loaded: nativeResponse.data.length, + total: nativeResponse.data.length, + })); + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + if (this.responseType === '' || + this.responseType === 'text') { + this.response = + typeof nativeResponse.data !== 'string' + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + } + else { + this.response = nativeResponse.data; + } + this.responseText = ((_a = nativeResponse.headers['Content-Type']) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) + ? JSON.stringify(nativeResponse.data) + : nativeResponse.data; + this.responseURL = nativeResponse.url; + this.readyState = 4; + setTimeout(() => { + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + }); + } + console.timeEnd(tag); + }) + .catch((error) => { + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; this.readyState = 4; - this.dispatchEvent(new Event('load')); - this.dispatchEvent(new Event('loadend')); - } - console.timeEnd(tag); - }) - .catch((error) => { - this.dispatchEvent(new Event('loadstart')); - this.status = error.status; - this._headers = error.headers; - this.response = error.data; - this.responseText = JSON.stringify(error.data); - this.responseURL = error.url; - this.readyState = 4; + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + setTimeout(() => { + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + console.timeEnd(tag); + }); + }); + } + catch (error) { + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + this.dispatchEvent(new ProgressEvent('progress', { + lengthComputable: false, + loaded: 0, + total: 0, + })); + setTimeout(() => { this.dispatchEvent(new Event('error')); this.dispatchEvent(new Event('loadend')); - console.timeEnd(tag); }); - }); - } - catch (error) { - this.dispatchEvent(new Event('loadstart')); - this.status = 500; - this._headers = {}; - this.response = error; - this.responseText = error.toString(); - this.responseURL = this._url; - this.readyState = 4; - this.dispatchEvent(new Event('error')); - this.dispatchEvent(new Event('loadend')); - console.timeEnd(tag); - } - }; - // XHR patch getAllResponseHeaders - window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); - } - let returnString = ''; - for (const key in this._headers) { - if (key != 'Set-Cookie') { - returnString += key + ': ' + this._headers[key] + '\r\n'; + console.timeEnd(tag); } - } - return returnString; - }; - // XHR patch getResponseHeader - window.XMLHttpRequest.prototype.getResponseHeader = function (name) { - if (this._url == null || - !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { - return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); - } - return this._headers[name]; + }; + // XHR patch getAllResponseHeaders + prototype.getAllResponseHeaders = function () { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); + } + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + // XHR patch getResponseHeader + prototype.getResponseHeader = function (name) { + if (this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); + } + return this._headers[name]; + }; + Object.setPrototypeOf(xhr, prototype); + return xhr; }; } }