| <import src="/gen/mojo/public/sky/core.sky" as="core" /> |
| <import src="/gen/mojo/public/sky/unicode.sky" as="unicode" /> |
| <import src="/gen/mojo/services/network/public/interfaces/network_service.mojom.sky" as="net" /> |
| <import src="/gen/mojo/services/network/public/interfaces/url_loader.mojom.sky" as="loader" /> |
| <import src="shell.sky" as="shell" /> |
| <script> |
| // XHR keeps itself alive. |
| var outstandingRequests = new Set(); |
| |
| const kPrivate = Symbol("XMLHttpRequestPrivate"); |
| |
| class Private { |
| constructor() { |
| this.networkService = shell.connectToService( |
| "mojo:network_service", net.NetworkService); |
| this.request = null; |
| this.loader = null; |
| this.headers = new Map(); |
| this.responseArrayBuffer = null; |
| this.responseText = null; // Cached to avoid re-decoding each access. |
| } |
| } |
| |
| // https://xhr.spec.whatwg.org |
| class XMLHttpRequest { |
| constructor() { |
| this[kPrivate] = new Private; |
| this.responseType = ''; // Only text and arraybuffer support for now. |
| this.status = null; |
| this.statusText = null; |
| } |
| |
| onload() { |
| } |
| |
| onerror(error) { |
| } |
| |
| get responseText() { |
| if (this.responseType !== '' && this.responseType !== 'text') |
| throw 'Non-text responseType ' + this.responseType; |
| if (this[kPrivate].responseArrayBuffer === null) |
| return null; |
| if (this[kPrivate].responseText === null) { |
| var intArray = new Uint8Array(this[kPrivate].responseArrayBuffer); |
| this[kPrivate].responseText = unicode.decodeUtf8String(intArray); |
| } |
| return this[kPrivate].responseText; |
| } |
| |
| get response() { |
| if (this.responseType === 'text' || this.responseType == '') |
| return this.responseText; |
| else if (this.responseType === 'arraybuffer') |
| return this[kPrivate].responseArrayBuffer; |
| throw 'Unknown responseType ' + this.responseType; |
| } |
| |
| open(method, url) { |
| var request = new loader.URLRequest(); |
| request.url = String(new URL(url, document.URL)); |
| request.method = method; |
| request.auto_follow_redirects = true; |
| |
| var priv = this[kPrivate]; |
| priv.request = request; |
| priv.headers.clear(); |
| } |
| |
| setRequestHeader(header, value) { |
| this[kPrivate].headers.set(header, value); |
| } |
| |
| send(body) { |
| var priv = this[kPrivate]; |
| // Handle the body before the headers as it can affect Content-Type. |
| if (body) { |
| var bodyAsBufferView = null; |
| if (typeof(body) === "string") { |
| this.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); |
| var bodyAsBufferView = new Uint8Array(unicode.utf8Length(body)); |
| unicode.encodeUtf8String(body, bodyAsBufferView); |
| } else { |
| bodyAsBufferView = new Uint8Array(body); |
| } |
| var dataPipe = new core.createDataPipe(); |
| // FIXME: body is currently assumed to be an ArrayBuffer. |
| var writeResult = core.writeData(dataPipe.producerHandle, |
| bodyAsBufferView, core.WRITE_DATA_FLAG_ALL_OR_NONE); |
| core.close(dataPipe.producerHandle); |
| // FIXME: Much better error handling needed. |
| console.assert(writeResult.result === core.RESULT_OK); |
| console.assert(writeResult.numBytes === body.length); |
| // 'body' is actually an array of body segments. |
| priv.request.body = [dataPipe.consumerHandle]; |
| } |
| |
| var requestHeaders = []; |
| priv.headers.forEach(function(value, key) { |
| requestHeaders.push(key + ': ' + value); |
| }); |
| priv.request.headers = requestHeaders; |
| |
| priv.networkService.createURLLoader(function(urlLoaderProxy) { |
| priv.loader = urlLoaderProxy; |
| }); |
| |
| var self = this; |
| outstandingRequests.add(this); |
| priv.loader.start(priv.request).then(function(result) { |
| self.status = result.response.status_code; |
| self.statusText = result.response.status_line; |
| if (result.response.error) |
| throw new Error(result.response.error.description); |
| return core.drainData(result.response.body).then(function(result) { |
| outstandingRequests.delete(self); |
| priv.responseArrayBuffer = result.buffer; |
| // Use a setTimeout to avoid exceptions in onload tripping onerror. |
| window.setTimeout(function() { |
| self.onload(); |
| }); |
| }); |
| }).catch(function(error) { |
| outstandingRequests.delete(self); |
| // Technically this should throw a ProgressEvent. |
| self.onerror(error); |
| }); |
| } |
| } |
| |
| module.exports = XMLHttpRequest; |
| </script> |