tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });