| OLD | NEW |
| (Empty) |
| 1 <import src="/gen/mojo/public/sky/core.sky" as="core" /> | |
| 2 <import src="/gen/mojo/public/sky/unicode.sky" as="unicode" /> | |
| 3 <import src="/gen/mojo/services/network/public/interfaces/network_service.mojom.
sky" as="net" /> | |
| 4 <import src="/gen/mojo/services/network/public/interfaces/url_loader.mojom.sky"
as="loader" /> | |
| 5 <import src="shell.sky" as="shell" /> | |
| 6 <script> | |
| 7 // XHR keeps itself alive. | |
| 8 var outstandingRequests = new Set(); | |
| 9 | |
| 10 const kPrivate = Symbol("XMLHttpRequestPrivate"); | |
| 11 | |
| 12 class Private { | |
| 13 constructor() { | |
| 14 this.networkService = shell.connectToService( | |
| 15 "mojo:network_service", net.NetworkService); | |
| 16 this.request = null; | |
| 17 this.loader = null; | |
| 18 this.headers = new Map(); | |
| 19 this.responseArrayBuffer = null; | |
| 20 this.responseText = null; // Cached to avoid re-decoding each access. | |
| 21 } | |
| 22 } | |
| 23 | |
| 24 // https://xhr.spec.whatwg.org | |
| 25 class XMLHttpRequest { | |
| 26 constructor() { | |
| 27 this[kPrivate] = new Private; | |
| 28 this.responseType = ''; // Only text and arraybuffer support for now. | |
| 29 this.status = null; | |
| 30 this.statusText = null; | |
| 31 } | |
| 32 | |
| 33 onload() { | |
| 34 } | |
| 35 | |
| 36 onerror(error) { | |
| 37 } | |
| 38 | |
| 39 get responseText() { | |
| 40 if (this.responseType !== '' && this.responseType !== 'text') | |
| 41 throw 'Non-text responseType ' + this.responseType; | |
| 42 if (this[kPrivate].responseArrayBuffer === null) | |
| 43 return null; | |
| 44 if (this[kPrivate].responseText === null) { | |
| 45 var intArray = new Uint8Array(this[kPrivate].responseArrayBuffer); | |
| 46 this[kPrivate].responseText = unicode.decodeUtf8String(intArray); | |
| 47 } | |
| 48 return this[kPrivate].responseText; | |
| 49 } | |
| 50 | |
| 51 get response() { | |
| 52 if (this.responseType === 'text' || this.responseType == '') | |
| 53 return this.responseText; | |
| 54 else if (this.responseType === 'arraybuffer') | |
| 55 return this[kPrivate].responseArrayBuffer; | |
| 56 throw 'Unknown responseType ' + this.responseType; | |
| 57 } | |
| 58 | |
| 59 open(method, url) { | |
| 60 var request = new loader.URLRequest(); | |
| 61 request.url = String(new URL(url, document.URL)); | |
| 62 request.method = method; | |
| 63 request.auto_follow_redirects = true; | |
| 64 | |
| 65 var priv = this[kPrivate]; | |
| 66 priv.request = request; | |
| 67 priv.headers.clear(); | |
| 68 } | |
| 69 | |
| 70 setRequestHeader(header, value) { | |
| 71 this[kPrivate].headers.set(header, value); | |
| 72 } | |
| 73 | |
| 74 send(body) { | |
| 75 var priv = this[kPrivate]; | |
| 76 // Handle the body before the headers as it can affect Content-Type. | |
| 77 if (body) { | |
| 78 var bodyAsBufferView = null; | |
| 79 if (typeof(body) === "string") { | |
| 80 this.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); | |
| 81 var bodyAsBufferView = new Uint8Array(unicode.utf8Length(body)); | |
| 82 unicode.encodeUtf8String(body, bodyAsBufferView); | |
| 83 } else { | |
| 84 bodyAsBufferView = new Uint8Array(body); | |
| 85 } | |
| 86 var dataPipe = new core.createDataPipe(); | |
| 87 // FIXME: body is currently assumed to be an ArrayBuffer. | |
| 88 var writeResult = core.writeData(dataPipe.producerHandle, | |
| 89 bodyAsBufferView, core.WRITE_DATA_FLAG_ALL_OR_NONE); | |
| 90 core.close(dataPipe.producerHandle); | |
| 91 // FIXME: Much better error handling needed. | |
| 92 console.assert(writeResult.result === core.RESULT_OK); | |
| 93 console.assert(writeResult.numBytes === body.length); | |
| 94 // 'body' is actually an array of body segments. | |
| 95 priv.request.body = [dataPipe.consumerHandle]; | |
| 96 } | |
| 97 | |
| 98 var requestHeaders = []; | |
| 99 priv.headers.forEach(function(value, key) { | |
| 100 requestHeaders.push(key + ': ' + value); | |
| 101 }); | |
| 102 priv.request.headers = requestHeaders; | |
| 103 | |
| 104 priv.networkService.createURLLoader(function(urlLoaderProxy) { | |
| 105 priv.loader = urlLoaderProxy; | |
| 106 }); | |
| 107 | |
| 108 var self = this; | |
| 109 outstandingRequests.add(this); | |
| 110 priv.loader.start(priv.request).then(function(result) { | |
| 111 self.status = result.response.status_code; | |
| 112 self.statusText = result.response.status_line; | |
| 113 if (result.response.error) | |
| 114 throw new Error(result.response.error.description); | |
| 115 return core.drainData(result.response.body).then(function(result) { | |
| 116 outstandingRequests.delete(self); | |
| 117 priv.responseArrayBuffer = result.buffer; | |
| 118 // Use a setTimeout to avoid exceptions in onload tripping onerror. | |
| 119 window.setTimeout(function() { | |
| 120 self.onload(); | |
| 121 }); | |
| 122 }); | |
| 123 }).catch(function(error) { | |
| 124 outstandingRequests.delete(self); | |
| 125 // Technically this should throw a ProgressEvent. | |
| 126 self.onerror(error); | |
| 127 }); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 module.exports = XMLHttpRequest; | |
| 132 </script> | |
| OLD | NEW |