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 |