Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | <!DOCTYPE HTML> |
michael@0 | 2 | <html> |
michael@0 | 3 | <head> |
michael@0 | 4 | <title>Test for XMLHttpRequest Progress Events</title> |
michael@0 | 5 | <script type="text/javascript" src="/MochiKit/packed.js"></script> |
michael@0 | 6 | <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> |
michael@0 | 7 | <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> |
michael@0 | 8 | </head> |
michael@0 | 9 | <body onload="gen.next();"> |
michael@0 | 10 | <pre id=l></pre> |
michael@0 | 11 | <script type="application/javascript;version=1.7"> |
michael@0 | 12 | SimpleTest.waitForExplicitFinish(); |
michael@0 | 13 | |
michael@0 | 14 | var gen = runTests(); |
michael@0 | 15 | |
michael@0 | 16 | function log(s) { |
michael@0 | 17 | // Uncomment these to get debugging information |
michael@0 | 18 | /* |
michael@0 | 19 | document.getElementById("l").textContent += s + "\n"; |
michael@0 | 20 | dump(s + "\n"); |
michael@0 | 21 | */ |
michael@0 | 22 | } |
michael@0 | 23 | |
michael@0 | 24 | function getEvent(e) { |
michael@0 | 25 | log("got event: " + e.type + " (" + e.target.readyState + ")"); |
michael@0 | 26 | gen.send(e); |
michael@0 | 27 | } |
michael@0 | 28 | |
michael@0 | 29 | function startsWith(a, b) { |
michael@0 | 30 | return a.substr(0, b.length) === b; |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | function updateProgress(e, data, testName) { |
michael@0 | 34 | var test = " while running " + testName; |
michael@0 | 35 | is(e.type, "progress", "event type" + test); |
michael@0 | 36 | |
michael@0 | 37 | let response; |
michael@0 | 38 | if (data.nodata) { |
michael@0 | 39 | is(e.target.response, null, "response should be null" + test); |
michael@0 | 40 | response = null; |
michael@0 | 41 | } |
michael@0 | 42 | else if (data.text) { |
michael@0 | 43 | is(typeof e.target.response, "string", "response should be a string" + test); |
michael@0 | 44 | response = e.target.response; |
michael@0 | 45 | } |
michael@0 | 46 | else if (data.blob) { |
michael@0 | 47 | ok(e.target.response instanceof Blob, "response should be a Blob" + test); |
michael@0 | 48 | response = e.target.response; |
michael@0 | 49 | } |
michael@0 | 50 | else { |
michael@0 | 51 | ok(e.target.response instanceof ArrayBuffer, "response should be an ArrayBuffer" + test); |
michael@0 | 52 | response = bufferToString(e.target.response); |
michael@0 | 53 | } |
michael@0 | 54 | is(e.target.response, e.target.response, "reflexivity should hold" + test); |
michael@0 | 55 | |
michael@0 | 56 | if (!data.nodata && !data.encoded) { |
michael@0 | 57 | if (data.blob) { |
michael@0 | 58 | is(e.loaded, response.size, "event.loaded matches response size" + test); |
michael@0 | 59 | } |
michael@0 | 60 | else if (!data.chunked) { |
michael@0 | 61 | is(e.loaded, response.length, "event.loaded matches response size" + test); |
michael@0 | 62 | } |
michael@0 | 63 | else { |
michael@0 | 64 | is(e.loaded - data.receivedBytes, response.length, |
michael@0 | 65 | "event.loaded grew by response size" + test); |
michael@0 | 66 | } |
michael@0 | 67 | } |
michael@0 | 68 | ok(e.loaded > data.receivedBytes, "event.loaded increased" + test); |
michael@0 | 69 | ok(e.loaded - data.receivedBytes <= data.pendingBytes, |
michael@0 | 70 | "event.loaded didn't increase too much" + test); |
michael@0 | 71 | |
michael@0 | 72 | if (!data.nodata && !data.blob) { |
michael@0 | 73 | var newData; |
michael@0 | 74 | ok(startsWith(response, data.receivedResult), |
michael@0 | 75 | "response strictly grew" + test); |
michael@0 | 76 | newData = response.substr(data.receivedResult.length); |
michael@0 | 77 | |
michael@0 | 78 | if (!data.encoded) { |
michael@0 | 79 | ok(newData.length > 0, "sanity check for progress" + test); |
michael@0 | 80 | } |
michael@0 | 81 | ok(startsWith(data.pendingResult, newData), "new data matches expected" + test); |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | is(e.lengthComputable, "total" in data, "lengthComputable" + test); |
michael@0 | 85 | if ("total" in data) { |
michael@0 | 86 | is(e.total, data.total, "total" + test); |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | if (!data.nodata && !data.blob) { |
michael@0 | 90 | data.pendingResult = data.pendingResult.substr(newData.length); |
michael@0 | 91 | } |
michael@0 | 92 | data.pendingBytes -= e.loaded - data.receivedBytes; |
michael@0 | 93 | data.receivedResult = response; |
michael@0 | 94 | data.receivedBytes = e.loaded; |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | function sendData(s) { |
michael@0 | 98 | var xhr = new XMLHttpRequest(); |
michael@0 | 99 | xhr.open("POST", "progressserver.sjs?send"); |
michael@0 | 100 | xhr.sendAsBinary(s); |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | function closeConn() { |
michael@0 | 104 | log("in closeConn"); |
michael@0 | 105 | var xhr = new XMLHttpRequest(); |
michael@0 | 106 | xhr.open("POST", "progressserver.sjs?close"); |
michael@0 | 107 | xhr.send(); |
michael@0 | 108 | return xhr; |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | var longString = "long"; |
michael@0 | 112 | while(longString.length < 65536) |
michael@0 | 113 | longString += longString; |
michael@0 | 114 | |
michael@0 | 115 | function utf8encode(s) { |
michael@0 | 116 | return unescape(encodeURIComponent(s)); |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | function bufferToString(buffer) { |
michael@0 | 120 | return String.fromCharCode.apply(String, new Uint8Array(buffer)); |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | function runTests() { |
michael@0 | 124 | var xhr = new XMLHttpRequest(); |
michael@0 | 125 | xhr.onprogress = xhr.onload = xhr.onerror = xhr.onreadystatechange = xhr.onloadend = getEvent; |
michael@0 | 126 | |
michael@0 | 127 | var responseTypes = [{ type: "text", text: true }, |
michael@0 | 128 | { type: "arraybuffer", text: false, nodata: true }, |
michael@0 | 129 | { type: "blob", text: false, nodata: true, blob: true }, |
michael@0 | 130 | { type: "moz-blob", text: false, nodata: false, blob: true }, |
michael@0 | 131 | { type: "document", text: true, nodata: true }, |
michael@0 | 132 | { type: "json", text: true, nodata: true }, |
michael@0 | 133 | { type: "", text: true }, |
michael@0 | 134 | { type: "moz-chunked-text", text: true, chunked: true }, |
michael@0 | 135 | { type: "moz-chunked-arraybuffer", text: false, chunked: true }, |
michael@0 | 136 | ]; |
michael@0 | 137 | var responseType; |
michael@0 | 138 | var fileExpectedResult = ""; |
michael@0 | 139 | for (var i = 0; i < 65536; i++) { |
michael@0 | 140 | fileExpectedResult += String.fromCharCode(i & 255); |
michael@0 | 141 | } |
michael@0 | 142 | while (responseType = responseTypes.shift()) { |
michael@0 | 143 | let tests = [{ open: "Content-Type=text/plain", name: "simple test" }, |
michael@0 | 144 | { data: "hello world" }, |
michael@0 | 145 | { data: "\u0000\u0001\u0002\u0003" }, |
michael@0 | 146 | { data: longString }, |
michael@0 | 147 | { data: "x" }, |
michael@0 | 148 | { close: true }, |
michael@0 | 149 | { open: "Content-Type=text/plain&Content-Length=20", name: "with length", total: 20 }, |
michael@0 | 150 | // 5 bytes from the "ready" in the open step |
michael@0 | 151 | { data: "abcde" }, |
michael@0 | 152 | { data: "0123456789" }, |
michael@0 | 153 | { close: true }, |
michael@0 | 154 | { open: "Content-Type=application/xml", name: "without length, as xml" }, |
michael@0 | 155 | { data: "<out>" }, |
michael@0 | 156 | { data: "text" }, |
michael@0 | 157 | { data: "</foo>invalid" }, |
michael@0 | 158 | { close: true }, |
michael@0 | 159 | { open: "Content-Type=text/plain;charset%3dutf-8", name: "utf8 data", encoded: true }, |
michael@0 | 160 | { data: utf8encode("räksmörgås"), utf16: "räksmörgås" }, |
michael@0 | 161 | { data: utf8encode("Å").substr(0,1), utf16: "" }, |
michael@0 | 162 | { data: utf8encode("Å").substr(1), utf16: "Å" }, |
michael@0 | 163 | { data: utf8encode("aöb").substr(0,2), utf16: "a" }, |
michael@0 | 164 | { data: utf8encode("aöb").substr(2), utf16: "öb" }, |
michael@0 | 165 | { data: utf8encode("a\u867Eb").substr(0,3), utf16: "a" }, |
michael@0 | 166 | { data: utf8encode("a\u867Eb").substr(3,1), utf16: "\u867E" }, |
michael@0 | 167 | { data: utf8encode("a\u867Eb").substr(4), utf16: "b" }, |
michael@0 | 168 | { close: true }, |
michael@0 | 169 | ]; |
michael@0 | 170 | if (responseType.blob) { |
michael@0 | 171 | tests.push({ file: "file_XHR_binary2.bin", name: "cacheable data", total: 65536 }, |
michael@0 | 172 | { close: true }, |
michael@0 | 173 | { file: "file_XHR_binary2.bin", name: "cached data", total: 65536 }, |
michael@0 | 174 | { close: true }); |
michael@0 | 175 | } |
michael@0 | 176 | let testState = { index: 0 }; |
michael@0 | 177 | |
michael@0 | 178 | for (let i = 0; i < tests.length; ++i) { |
michael@0 | 179 | let test = tests[i]; |
michael@0 | 180 | testState.index++; |
michael@0 | 181 | if ("open" in test || "file" in test) { |
michael@0 | 182 | log("opening " + testState.name); |
michael@0 | 183 | testState = { name: test.name + " for " + responseType.type, |
michael@0 | 184 | index: 0, |
michael@0 | 185 | pendingResult: "ready", |
michael@0 | 186 | pendingBytes: 5, |
michael@0 | 187 | receivedResult: "", |
michael@0 | 188 | receivedBytes: 0, |
michael@0 | 189 | total: test.total, |
michael@0 | 190 | encoded: test.encoded, |
michael@0 | 191 | nodata: responseType.nodata, |
michael@0 | 192 | chunked: responseType.chunked, |
michael@0 | 193 | text: responseType.text, |
michael@0 | 194 | blob: responseType.blob, |
michael@0 | 195 | file: test.file }; |
michael@0 | 196 | |
michael@0 | 197 | xhr.onreadystatechange = null; |
michael@0 | 198 | if (testState.file) |
michael@0 | 199 | xhr.open("GET", test.file); |
michael@0 | 200 | else |
michael@0 | 201 | xhr.open("POST", "progressserver.sjs?open&" + test.open); |
michael@0 | 202 | xhr.responseType = responseType.type; |
michael@0 | 203 | xhr.send("ready"); |
michael@0 | 204 | xhr.onreadystatechange = getEvent; |
michael@0 | 205 | |
michael@0 | 206 | let e = yield undefined; |
michael@0 | 207 | is(e.type, "readystatechange", "should readystate to headers-received starting " + testState.name); |
michael@0 | 208 | is(xhr.readyState, xhr.HEADERS_RECEIVED, "should be in state HEADERS_RECEIVED starting " + testState.name); |
michael@0 | 209 | |
michael@0 | 210 | e = yield undefined; |
michael@0 | 211 | is(e.type, "readystatechange", "should readystate to loading starting " + testState.name); |
michael@0 | 212 | is(xhr.readyState, xhr.LOADING, "should be in state LOADING starting " + testState.name); |
michael@0 | 213 | if (typeof testState.total == "undefined") |
michael@0 | 214 | delete testState.total; |
michael@0 | 215 | } |
michael@0 | 216 | if ("file" in test) { |
michael@0 | 217 | testState.pendingBytes = testState.total; |
michael@0 | 218 | testState.pendingResult = fileExpectedResult; |
michael@0 | 219 | } |
michael@0 | 220 | if ("close" in test) { |
michael@0 | 221 | log("closing"); |
michael@0 | 222 | let xhrClose; |
michael@0 | 223 | if (!testState.file) |
michael@0 | 224 | xhrClose = closeConn(); |
michael@0 | 225 | |
michael@0 | 226 | e = yield undefined; |
michael@0 | 227 | is(e.type, "readystatechange", "should readystate to done closing " + testState.name); |
michael@0 | 228 | is(xhr.readyState, xhr.DONE, "should be in state DONE closing " + testState.name); |
michael@0 | 229 | log("readystate to 4"); |
michael@0 | 230 | |
michael@0 | 231 | if (responseType.chunked) { |
michael@0 | 232 | xhr.responseType; |
michael@0 | 233 | is(xhr.response, null, "chunked data has null response for " + testState.name); |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | e = yield undefined; |
michael@0 | 237 | is(e.type, "load", "should fire load closing " + testState.name); |
michael@0 | 238 | is(e.lengthComputable, true, "length should be computable during load closing " + testState.name); |
michael@0 | 239 | log("got load"); |
michael@0 | 240 | |
michael@0 | 241 | if (responseType.chunked) { |
michael@0 | 242 | is(xhr.response, null, "chunked data has null response for " + testState.name); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | e = yield undefined; |
michael@0 | 246 | is(e.type, "loadend", "should fire loadend closing " + testState.name); |
michael@0 | 247 | is(e.lengthComputable, true, "length should be computable during loadend closing " + testState.name); |
michael@0 | 248 | log("got loadend"); |
michael@0 | 249 | |
michael@0 | 250 | // if we closed the connection using an explicit request, make sure that goes through before |
michael@0 | 251 | // running the next test in order to avoid reordered requests from closing the wrong |
michael@0 | 252 | // connection. |
michael@0 | 253 | if (xhrClose && xhrClose.readyState != xhrClose.DONE) { |
michael@0 | 254 | log("wait for closeConn to finish"); |
michael@0 | 255 | xhrClose.onloadend = getEvent; |
michael@0 | 256 | yield undefined; |
michael@0 | 257 | is(xhrClose.readyState, xhrClose.DONE, "closeConn finished"); |
michael@0 | 258 | } |
michael@0 | 259 | |
michael@0 | 260 | if (responseType.chunked) { |
michael@0 | 261 | is(xhr.response, null, "chunked data has null response for " + testState.name); |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | if (!testState.nodata && !responseType.blob || responseType.chunked) { |
michael@0 | 265 | // This branch intentionally left blank |
michael@0 | 266 | // Under these conditions we check the response during updateProgress |
michael@0 | 267 | } |
michael@0 | 268 | else if (responseType.type === "arraybuffer") { |
michael@0 | 269 | is(bufferToString(xhr.response), testState.pendingResult, |
michael@0 | 270 | "full response for " + testState.name); |
michael@0 | 271 | } |
michael@0 | 272 | else if (responseType.blob) { |
michael@0 | 273 | let reader = new FileReader; |
michael@0 | 274 | reader.readAsBinaryString(xhr.response); |
michael@0 | 275 | reader.onloadend = getEvent; |
michael@0 | 276 | yield undefined; |
michael@0 | 277 | |
michael@0 | 278 | is(reader.result, testState.pendingResult, |
michael@0 | 279 | "full response in blob for " + testState.name); |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | testState.name = ""; |
michael@0 | 283 | } |
michael@0 | 284 | if ("data" in test) { |
michael@0 | 285 | log("sending"); |
michael@0 | 286 | if (responseType.text) { |
michael@0 | 287 | testState.pendingResult += "utf16" in test ? test.utf16 : test.data; |
michael@0 | 288 | } |
michael@0 | 289 | else { |
michael@0 | 290 | testState.pendingResult += test.data; |
michael@0 | 291 | } |
michael@0 | 292 | testState.pendingBytes = test.data.length; |
michael@0 | 293 | sendData(test.data); |
michael@0 | 294 | } |
michael@0 | 295 | |
michael@0 | 296 | while(testState.pendingBytes) { |
michael@0 | 297 | log("waiting for more bytes: " + testState.pendingBytes); |
michael@0 | 298 | e = yield undefined; |
michael@0 | 299 | // Readystate can fire several times between each progress event. |
michael@0 | 300 | if (e.type === "readystatechange") |
michael@0 | 301 | continue; |
michael@0 | 302 | |
michael@0 | 303 | updateProgress(e, testState, "data for " + testState.name + "[" + testState.index + "]"); |
michael@0 | 304 | if (responseType.chunked) { |
michael@0 | 305 | testState.receivedResult = ""; |
michael@0 | 306 | } |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | if (!testState.nodata && !testState.blob) { |
michael@0 | 310 | is(testState.pendingResult, "", |
michael@0 | 311 | "should have consumed the expected result"); |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | log("done with this test"); |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | is(testState.name, "", "forgot to close last test"); |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | SimpleTest.finish(); |
michael@0 | 321 | yield undefined; |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | </script> |
michael@0 | 325 | |
michael@0 | 326 | </body> |
michael@0 | 327 | </html> |