| OLD | NEW |
| 1 <import src="/mojo/public/sky/core.sky" as="core" /> | 1 <import src="/mojo/public/sky/core.sky" as="core" /> |
| 2 <import src="/mojo/public/sky/unicode.sky" as="unicode" /> | 2 <import src="/mojo/public/sky/unicode.sky" as="unicode" /> |
| 3 <import src="/mojo/services/network/public/interfaces/network_service.mojom.sky"
as="net" /> | 3 <import src="/mojo/services/network/public/interfaces/network_service.mojom.sky"
as="net" /> |
| 4 <import src="/mojo/services/network/public/interfaces/url_loader.mojom.sky" as="
loader" /> | 4 <import src="/mojo/services/network/public/interfaces/url_loader.mojom.sky" as="
loader" /> |
| 5 <import src="/sky/framework/shell.sky" as="shell" /> | 5 <import src="/sky/framework/shell.sky" as="shell" /> |
| 6 <script> | 6 <script> |
| 7 // XHR keeps itself alive. | 7 // XHR keeps itself alive. |
| 8 var outstandingRequests = new Set(); | 8 var outstandingRequests = new Set(); |
| 9 | 9 |
| 10 const kPrivate = Symbol("XMLHttpRequestPrivate"); | 10 const kPrivate = Symbol("XMLHttpRequestPrivate"); |
| 11 | 11 |
| 12 class Private { | 12 class Private { |
| 13 constructor() { | 13 constructor() { |
| 14 this.networkService = shell.connectToService( | 14 this.networkService = shell.connectToService( |
| 15 "mojo:network_service", net.NetworkService); | 15 "mojo:network_service", net.NetworkService); |
| 16 this.request = null; | 16 this.request = null; |
| 17 this.loader = null; | 17 this.loader = null; |
| 18 this.headers = new Map(); | 18 this.headers = new Map(); |
| 19 this.responseArrayBuffer = null; | 19 this.responseArrayBuffer = null; |
| 20 this.responseText = null; // Cached to avoid re-decoding each access. | 20 this.responseText = null; // Cached to avoid re-decoding each access. |
| 21 } | 21 } |
| 22 } | 22 } |
| 23 | 23 |
| 24 // Somewhat hacky, but works. | |
| 25 function stringToUTF8Buffer(string) { | |
| 26 var string = unescape(encodeURIComponent(string)); | |
| 27 var charList = string.split(''); | |
| 28 var uintArray = []; | |
| 29 for (var i = 0; i < charList.length; i++) { | |
| 30 uintArray.push(charList[i].charCodeAt(0)); | |
| 31 } | |
| 32 return new Uint8Array(uintArray); | |
| 33 } | |
| 34 | |
| 35 // https://xhr.spec.whatwg.org | 24 // https://xhr.spec.whatwg.org |
| 36 class XMLHttpRequest { | 25 class XMLHttpRequest { |
| 37 constructor() { | 26 constructor() { |
| 38 this[kPrivate] = new Private; | 27 this[kPrivate] = new Private; |
| 39 this.responseType = ''; // Only text and arraybuffer support for now. | 28 this.responseType = ''; // Only text and arraybuffer support for now. |
| 40 this.status = null; | |
| 41 this.statusText = null; | |
| 42 } | 29 } |
| 43 | 30 |
| 44 onload() { | 31 onload() { |
| 45 } | 32 } |
| 46 | 33 |
| 47 onerror(error) { | 34 onerror(error) { |
| 48 } | 35 } |
| 49 | 36 |
| 50 get responseText() { | 37 get responseText() { |
| 51 if (this.responseType !== '' && this.responseType !== 'text') | 38 if (this.responseType !== '' && this.responseType !== 'text') |
| (...skipping 23 matching lines...) Expand all Loading... |
| 75 | 62 |
| 76 var priv = this[kPrivate]; | 63 var priv = this[kPrivate]; |
| 77 priv.request = request; | 64 priv.request = request; |
| 78 priv.headers.clear(); | 65 priv.headers.clear(); |
| 79 } | 66 } |
| 80 | 67 |
| 81 setRequestHeader(header, value) { | 68 setRequestHeader(header, value) { |
| 82 this[kPrivate].headers.set(header, value); | 69 this[kPrivate].headers.set(header, value); |
| 83 } | 70 } |
| 84 | 71 |
| 85 send(body) { | 72 send() { |
| 86 var priv = this[kPrivate]; | 73 var priv = this[kPrivate]; |
| 87 // Handle the body before the headers as it can affect Content-Type. | |
| 88 if (body) { | |
| 89 var bodyAsBufferView = null; | |
| 90 if (typeof(body) === "string") { | |
| 91 this.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); | |
| 92 bodyAsBufferView = stringToUTF8Buffer(body); | |
| 93 } else { | |
| 94 bodyAsBufferView = new Uint8Array(body); | |
| 95 } | |
| 96 var dataPipe = new core.createDataPipe(); | |
| 97 // FIXME: body is currently assumed to be an ArrayBuffer. | |
| 98 var writeResult = core.writeData(dataPipe.producerHandle, | |
| 99 bodyAsBufferView, core.WRITE_DATA_FLAG_ALL_OR_NONE); | |
| 100 core.close(dataPipe.producerHandle); | |
| 101 // FIXME: Much better error handling needed. | |
| 102 console.assert(writeResult.result === core.RESULT_OK); | |
| 103 console.assert(writeResult.numBytes === body.length); | |
| 104 // 'body' is actually an array of body segments. | |
| 105 priv.request.body = [dataPipe.consumerHandle]; | |
| 106 } | |
| 107 | |
| 108 var requestHeaders = []; | 74 var requestHeaders = []; |
| 109 priv.headers.forEach(function(value, key) { | 75 priv.headers.forEach(function(value, key) { |
| 110 requestHeaders.push(key + ': ' + value); | 76 requestHeaders.push(key + ': ' + value); |
| 111 }); | 77 }); |
| 112 priv.request.headers = requestHeaders; | 78 priv.request.headers = requestHeaders; |
| 113 | 79 |
| 114 // FIXME: Factor this into the JS bindings. | 80 // FIXME: Factor this into the JS bindings. |
| 115 var pipe = new core.createMessagePipe(); | 81 var pipe = new core.createMessagePipe(); |
| 116 priv.networkService.createURLLoader(pipe.handle1); | 82 priv.networkService.createURLLoader(pipe.handle1); |
| 117 priv.loader = shell.wrapHandle(pipe.handle0, loader.URLLoader); | 83 priv.loader = shell.wrapHandle(pipe.handle0, loader.URLLoader); |
| 118 | 84 |
| 119 var self = this; | 85 var self = this; |
| 120 outstandingRequests.add(this); | 86 outstandingRequests.add(this); |
| 121 priv.loader.start(priv.request).then(function(result) { | 87 priv.loader.start(priv.request).then(function(result) { |
| 122 self.status = result.response.status_code; | |
| 123 self.statusText = result.response.status_line; | |
| 124 if (result.response.error) | |
| 125 throw new Error(result.response.error.description); | |
| 126 return core.drainData(result.response.body).then(function(result) { | 88 return core.drainData(result.response.body).then(function(result) { |
| 127 outstandingRequests.delete(self); | 89 outstandingRequests.delete(self); |
| 128 priv.responseArrayBuffer = result.buffer; | 90 priv.responseArrayBuffer = result.buffer; |
| 129 // Use a setTimeout to avoid exceptions in onload tripping onerror. | 91 // FIXME: Catch exceptions during onload so they don't trip onerror. |
| 130 window.setTimeout(function() { | 92 self.onload(); |
| 131 self.onload(); | |
| 132 }); | |
| 133 }); | 93 }); |
| 134 }).catch(function(error) { | 94 }).catch(function(error) { |
| 135 outstandingRequests.delete(self); | 95 outstandingRequests.delete(self); |
| 136 // Technically this should throw a ProgressEvent. | |
| 137 self.onerror(error); | 96 self.onerror(error); |
| 138 }); | 97 }); |
| 139 } | 98 } |
| 140 } | 99 } |
| 141 | 100 |
| 142 module.exports = XMLHttpRequest; | 101 module.exports = XMLHttpRequest; |
| 143 </script> | 102 </script> |
| OLD | NEW |