file_XHRResponseURL.js (12254B)
1 "use strict"; 2 3 // utility functions for worker/window communication 4 5 function isInWorker() { 6 try { 7 return !(self instanceof Window); 8 } catch (e) { 9 return true; 10 } 11 } 12 13 function message(aData) { 14 if (isInWorker()) { 15 self.postMessage(aData); 16 } else { 17 self.postMessage(aData, "*"); 18 } 19 } 20 message.ping = 0; 21 message.pong = 0; 22 23 function is(aActual, aExpected, aMessage) { 24 var obj = { 25 type: "is", 26 actual: aActual, 27 expected: aExpected, 28 message: aMessage, 29 }; 30 ++message.ping; 31 message(obj); 32 } 33 34 function ok(aBool, aMessage) { 35 var obj = { 36 type: "ok", 37 bool: aBool, 38 message: aMessage, 39 }; 40 ++message.ping; 41 message(obj); 42 } 43 44 function info(aMessage) { 45 var obj = { 46 type: "info", 47 message: aMessage, 48 }; 49 ++message.ping; 50 message(obj); 51 } 52 53 function request(aURL) { 54 return new Promise(function (aResolve) { 55 var xhr = new XMLHttpRequest(); 56 xhr.open("GET", aURL); 57 xhr.addEventListener("load", function () { 58 xhr.succeeded = true; 59 aResolve(xhr); 60 }); 61 xhr.addEventListener("error", function () { 62 xhr.succeeded = false; 63 aResolve(xhr); 64 }); 65 xhr.send(); 66 }); 67 } 68 69 function createSequentialRequest(aParameters, aTest) { 70 var sequence = aParameters.reduce(function (aPromise, aParam) { 71 return aPromise 72 .then(function () { 73 return request(aParam.requestURL); 74 }) 75 .then(function (aXHR) { 76 return aTest(aXHR, aParam); 77 }); 78 }, Promise.resolve()); 79 80 return sequence; 81 } 82 83 function testSuccessResponse() { 84 var blob = new Blob(["data"], { type: "text/plain" }); 85 var blobURL = URL.createObjectURL(blob); 86 87 var parameters = [ 88 // tests that start with same-origin request 89 { 90 message: "request to same-origin without redirect", 91 requestURL: 92 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 93 responseURL: 94 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 95 }, 96 { 97 message: "request to same-origin redirect to same-origin URL", 98 requestURL: 99 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 100 responseURL: 101 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 102 }, 103 { 104 message: 105 "request to same-origin redirects several times and finally go to same-origin URL", 106 requestURL: 107 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=" + 108 encodeURIComponent( 109 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text" 110 ), 111 responseURL: 112 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 113 }, 114 { 115 message: "request to same-origin redirect to cross-origin URL", 116 requestURL: 117 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 118 responseURL: 119 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 120 }, 121 { 122 message: 123 "request to same-origin redirects several times and finally go to cross-origin URL", 124 requestURL: 125 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=" + 126 encodeURIComponent( 127 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text" 128 ), 129 responseURL: 130 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 131 }, 132 133 // tests that start with cross-origin request 134 { 135 message: "request to cross-origin without redirect", 136 requestURL: 137 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 138 responseURL: 139 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 140 }, 141 { 142 message: "request to cross-origin redirect back to same-origin URL", 143 requestURL: 144 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 145 responseURL: 146 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 147 }, 148 { 149 message: "request to cross-origin redirect to the same cross-origin URL", 150 requestURL: 151 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 152 responseURL: 153 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 154 }, 155 { 156 message: "request to cross-origin redirect to another cross-origin URL", 157 requestURL: 158 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://example.org/tests/dom/xhr/tests/file_XHRResponseURL.text", 159 responseURL: 160 "http://example.org/tests/dom/xhr/tests/file_XHRResponseURL.text", 161 }, 162 { 163 message: 164 "request to cross-origin redirects several times and finally go to same-origin URL", 165 requestURL: 166 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=" + 167 encodeURIComponent( 168 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text" 169 ), 170 responseURL: 171 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 172 }, 173 { 174 message: 175 "request to cross-origin redirects several times and finally go to the same cross-origin URL", 176 requestURL: 177 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=" + 178 encodeURIComponent( 179 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text" 180 ), 181 responseURL: 182 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 183 }, 184 { 185 message: 186 "request to cross-origin redirects several times and finally go to another cross-origin URL", 187 requestURL: 188 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=" + 189 encodeURIComponent( 190 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://example.org/tests/dom/xhr/tests/file_XHRResponseURL.text" 191 ), 192 responseURL: 193 "http://example.org/tests/dom/xhr/tests/file_XHRResponseURL.text", 194 }, 195 { 196 message: 197 "request to cross-origin redirects to another cross-origin and finally go to the other cross-origin URL", 198 requestURL: 199 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=" + 200 encodeURIComponent( 201 "http://example.org/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://test1.example.com/tests/dom/xhr/tests/file_XHRResponseURL.text" 202 ), 203 responseURL: 204 "http://test1.example.com/tests/dom/xhr/tests/file_XHRResponseURL.text", 205 }, 206 { 207 message: "request URL has fragment", 208 requestURL: 209 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text#fragment", 210 responseURL: 211 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text", 212 }, 213 214 // tests for non-http(s) URL 215 { 216 message: "request to data: URL", 217 requestURL: "data:text/plain,data", 218 responseURL: "data:text/plain,data", 219 }, 220 { 221 message: "request to blob: URL", 222 requestURL: blobURL, 223 responseURL: blobURL, 224 }, 225 ]; 226 227 var sequence = createSequentialRequest(parameters, function (aXHR, aParam) { 228 ok(aXHR.succeeded, "assert request succeeded"); 229 is(aXHR.responseURL, aParam.responseURL, aParam.message); 230 }); 231 232 sequence.then(function () { 233 URL.revokeObjectURL(blobURL); 234 }); 235 236 return sequence; 237 } 238 239 function testFailedResponse() { 240 info("test not to leak responseURL for denied cross-origin request"); 241 var parameters = [ 242 { 243 message: 244 "should be empty for denied cross-origin request without redirect", 245 requestURL: 246 "http://example.com/tests/dom/xhr/tests/file_XHRResponseURL_nocors.text", 247 }, 248 { 249 message: "should be empty for denied cross-origin request with redirect", 250 requestURL: 251 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/xhr/tests/file_XHRResponseURL_nocors.text", 252 }, 253 ]; 254 255 var sequence = createSequentialRequest(parameters, function (aXHR, aParam) { 256 ok(!aXHR.succeeded, "assert request failed"); 257 is(aXHR.responseURL, "", aParam.message); 258 }); 259 260 return sequence; 261 } 262 263 function testNotToLeakResponseURLWhileDoingRedirects() { 264 info("test not to leak responeseURL while doing redirects"); 265 266 if (isInWorker()) { 267 return testNotToLeakResponseURLWhileDoingRedirectsInWorker(); 268 } 269 return testNotToLeakResponseURLWhileDoingRedirectsInWindow(); 270 } 271 272 function testNotToLeakResponseURLWhileDoingRedirectsInWindow() { 273 var xhr = new XMLHttpRequest(); 274 var requestObserver = { 275 observe() { 276 is(xhr.readyState, XMLHttpRequest.OPENED, "assert for XHR state"); 277 is( 278 xhr.responseURL, 279 "", 280 "responseURL should return empty string before HEADERS_RECEIVED" 281 ); 282 }, 283 }; 284 SpecialPowers.addObserver( 285 requestObserver, 286 "specialpowers-http-notify-request" 287 ); 288 289 return new Promise(function (aResolve) { 290 xhr.open( 291 "GET", 292 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text" 293 ); 294 xhr.addEventListener("load", function () { 295 SpecialPowers.removeObserver( 296 requestObserver, 297 "specialpowers-http-notify-request" 298 ); 299 aResolve(); 300 }); 301 xhr.addEventListener("error", function () { 302 ok(false, "unexpected request falilure"); 303 SpecialPowers.removeObserver( 304 requestObserver, 305 "specialpowers-http-notify-request" 306 ); 307 aResolve(); 308 }); 309 xhr.send(); 310 }); 311 } 312 313 function testNotToLeakResponseURLWhileDoingRedirectsInWorker() { 314 var xhr = new XMLHttpRequest(); 315 var testRedirect = function (e) { 316 if (e.data === "request" && xhr.readyState === XMLHttpRequest.OPENED) { 317 is( 318 xhr.responseURL, 319 "", 320 "responseURL should return empty string before HEADERS_RECEIVED" 321 ); 322 } 323 }; 324 325 return new Promise(function (aResolve) { 326 self.addEventListener("message", testRedirect); 327 message({ type: "redirect_test", status: "start" }); 328 xhr.open( 329 "GET", 330 "http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.sjs?url=http://mochi.test:8888/tests/dom/xhr/tests/file_XHRResponseURL.text" 331 ); 332 xhr.addEventListener("load", function () { 333 self.removeEventListener("message", testRedirect); 334 message({ type: "redirect_test", status: "end" }); 335 aResolve(); 336 }); 337 xhr.addEventListener("error", function () { 338 ok(false, "unexpected request falilure"); 339 self.removeEventListener("message", testRedirect); 340 message({ type: "redirect_test", status: "end" }); 341 aResolve(); 342 }); 343 xhr.send(); 344 }); 345 } 346 347 function waitForAllMessagesProcessed() { 348 return new Promise(function (aResolve) { 349 var id = setInterval(function () { 350 if (message.ping === message.pong) { 351 clearInterval(id); 352 aResolve(); 353 } 354 }, 100); 355 }); 356 } 357 358 self.addEventListener("message", function (aEvent) { 359 if (aEvent.data === "start") { 360 ok( 361 "responseURL" in new XMLHttpRequest(), 362 "XMLHttpRequest should have responseURL attribute" 363 ); 364 is( 365 new XMLHttpRequest().responseURL, 366 "", 367 "responseURL should be empty string if response's url is null" 368 ); 369 370 var promise = testSuccessResponse(); 371 promise 372 .then(function () { 373 return testFailedResponse(); 374 }) 375 .then(function () { 376 return testNotToLeakResponseURLWhileDoingRedirects(); 377 }) 378 .then(function () { 379 return waitForAllMessagesProcessed(); 380 }) 381 .then(function () { 382 message("done"); 383 }); 384 } 385 if (aEvent.data === "pong") { 386 ++message.pong; 387 } 388 });