tor-browser

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

test_URIs.js (36098B)


      1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 "use strict";
      7 
      8 // Run by: cd objdir;  make -C netwerk/test/ xpcshell-tests
      9 // or: cd objdir; make SOLO_FILE="test_URIs.js" -C netwerk/test/ check-one
     10 
     11 // See also test_URIs2.js.
     12 
     13 // Relevant RFCs: 1738, 1808, 2396, 3986 (newer than the code)
     14 // http://greenbytes.de/tech/webdav/rfc3986.html#rfc.section.5.4
     15 // http://greenbytes.de/tech/tc/uris/
     16 
     17 // TEST DATA
     18 // ---------
     19 var gTests = [
     20  {
     21    spec: "about:blank",
     22    scheme: "about",
     23    prePath: "about:",
     24    pathQueryRef: "blank",
     25    ref: "",
     26    nsIURL: false,
     27    nsINestedURI: true,
     28    immutable: true,
     29  },
     30  {
     31    spec: "about:foobar",
     32    scheme: "about",
     33    prePath: "about:",
     34    pathQueryRef: "foobar",
     35    ref: "",
     36    nsIURL: false,
     37    nsINestedURI: false,
     38    immutable: true,
     39  },
     40  {
     41    spec: "chrome://foobar/somedir/somefile.xml",
     42    scheme: "chrome",
     43    prePath: "chrome://foobar",
     44    pathQueryRef: "/somedir/somefile.xml",
     45    ref: "",
     46    nsIURL: true,
     47    nsINestedURI: false,
     48    immutable: true,
     49  },
     50  {
     51    spec: "data:text/html;charset=utf-8,<html></html>",
     52    scheme: "data",
     53    prePath: "data:",
     54    pathQueryRef: "text/html;charset=utf-8,<html></html>",
     55    ref: "",
     56    nsIURL: false,
     57    nsINestedURI: false,
     58  },
     59  {
     60    spec: "data:text/html;charset=utf-8,<html>\r\n\t</html>",
     61    scheme: "data",
     62    prePath: "data:",
     63    pathQueryRef: "text/html;charset=utf-8,<html></html>",
     64    ref: "",
     65    nsIURL: false,
     66    nsINestedURI: false,
     67  },
     68  {
     69    spec: "data:text/plain,hello%20world",
     70    scheme: "data",
     71    prePath: "data:",
     72    pathQueryRef: "text/plain,hello%20world",
     73    ref: "",
     74    nsIURL: false,
     75    nsINestedURI: false,
     76  },
     77  {
     78    spec: "data:text/plain,hello world",
     79    scheme: "data",
     80    prePath: "data:",
     81    pathQueryRef: "text/plain,hello world",
     82    ref: "",
     83    nsIURL: false,
     84    nsINestedURI: false,
     85  },
     86  {
     87    spec: "file:///dir/afile",
     88    scheme: "data",
     89    prePath: "data:",
     90    pathQueryRef: "text/plain,2",
     91    ref: "",
     92    relativeURI: "data:te\nxt/plain,2",
     93    nsIURL: false,
     94    nsINestedURI: false,
     95  },
     96  {
     97    spec: "file://",
     98    scheme: "file",
     99    prePath: "file://",
    100    pathQueryRef: "/",
    101    ref: "",
    102    nsIURL: true,
    103    nsINestedURI: false,
    104  },
    105  {
    106    spec: "file:///",
    107    scheme: "file",
    108    prePath: "file://",
    109    pathQueryRef: "/",
    110    ref: "",
    111    nsIURL: true,
    112    nsINestedURI: false,
    113  },
    114  {
    115    spec: "file:///myFile.html",
    116    scheme: "file",
    117    prePath: "file://",
    118    pathQueryRef: "/myFile.html",
    119    ref: "",
    120    nsIURL: true,
    121    nsINestedURI: false,
    122  },
    123  {
    124    spec: "file:///dir/afile",
    125    scheme: "file",
    126    prePath: "file://",
    127    pathQueryRef: "/dir/data/text/plain,2",
    128    ref: "",
    129    relativeURI: "data/text/plain,2",
    130    nsIURL: true,
    131    nsINestedURI: false,
    132  },
    133  {
    134    spec: "file:///dir/dir2/",
    135    scheme: "file",
    136    prePath: "file://",
    137    pathQueryRef: "/dir/dir2/data/text/plain,2",
    138    ref: "",
    139    relativeURI: "data/text/plain,2",
    140    nsIURL: true,
    141    nsINestedURI: false,
    142  },
    143  {
    144    spec: "ftp://ftp.mozilla.org/pub/mozilla.org/README",
    145    scheme: "ftp",
    146    prePath: "ftp://ftp.mozilla.org",
    147    pathQueryRef: "/pub/mozilla.org/README",
    148    ref: "",
    149    nsIURL: true,
    150    nsINestedURI: false,
    151  },
    152  {
    153    spec: "ftp://foo:bar@ftp.mozilla.org:100/pub/mozilla.org/README",
    154    scheme: "ftp",
    155    prePath: "ftp://foo:bar@ftp.mozilla.org:100",
    156    port: 100,
    157    username: "foo",
    158    password: "bar",
    159    pathQueryRef: "/pub/mozilla.org/README",
    160    ref: "",
    161    nsIURL: true,
    162    nsINestedURI: false,
    163  },
    164  {
    165    spec: "ftp://foo:@ftp.mozilla.org:100/pub/mozilla.org/README",
    166    scheme: "ftp",
    167    prePath: "ftp://foo@ftp.mozilla.org:100",
    168    port: 100,
    169    username: "foo",
    170    password: "",
    171    pathQueryRef: "/pub/mozilla.org/README",
    172    ref: "",
    173    nsIURL: true,
    174    nsINestedURI: false,
    175  },
    176  //Bug 706249
    177  {
    178    spec: "gopher://mozilla.org/",
    179    scheme: "gopher",
    180    prePath: "gopher://mozilla.org",
    181    pathQueryRef: "/",
    182    ref: "",
    183    nsIURL: false,
    184    nsINestedURI: false,
    185  },
    186  {
    187    spec: "http://www.example.com/",
    188    scheme: "http",
    189    prePath: "http://www.example.com",
    190    pathQueryRef: "/",
    191    ref: "",
    192    nsIURL: true,
    193    nsINestedURI: false,
    194  },
    195  {
    196    spec: "http://www.exa\nmple.com/",
    197    scheme: "http",
    198    prePath: "http://www.example.com",
    199    pathQueryRef: "/",
    200    ref: "",
    201    nsIURL: true,
    202    nsINestedURI: false,
    203  },
    204  {
    205    spec: "http://10.32.4.239/",
    206    scheme: "http",
    207    prePath: "http://10.32.4.239",
    208    host: "10.32.4.239",
    209    pathQueryRef: "/",
    210    ref: "",
    211    nsIURL: true,
    212    nsINestedURI: false,
    213  },
    214  {
    215    spec: "http://[::192.9.5.5]/ipng",
    216    scheme: "http",
    217    prePath: "http://[::c009:505]",
    218    host: "::c009:505",
    219    pathQueryRef: "/ipng",
    220    ref: "",
    221    nsIURL: true,
    222    nsINestedURI: false,
    223  },
    224  {
    225    spec: "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:8888/index.html",
    226    scheme: "http",
    227    prePath: "http://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:8888",
    228    host: "fedc:ba98:7654:3210:fedc:ba98:7654:3210",
    229    port: 8888,
    230    pathQueryRef: "/index.html",
    231    ref: "",
    232    nsIURL: true,
    233    nsINestedURI: false,
    234  },
    235  {
    236    spec: "http://bar:foo@www.mozilla.org:8080/pub/mozilla.org/README.html",
    237    scheme: "http",
    238    prePath: "http://bar:foo@www.mozilla.org:8080",
    239    port: 8080,
    240    username: "bar",
    241    password: "foo",
    242    host: "www.mozilla.org",
    243    pathQueryRef: "/pub/mozilla.org/README.html",
    244    ref: "",
    245    nsIURL: true,
    246    nsINestedURI: false,
    247  },
    248  {
    249    spec: "jar:resource://!/",
    250    scheme: "jar",
    251    prePath: "jar:",
    252    pathQueryRef: "resource:///!/",
    253    ref: "",
    254    nsIURL: true,
    255    nsINestedURI: true,
    256  },
    257  {
    258    spec: "jar:resource://gre/chrome.toolkit.jar!/",
    259    scheme: "jar",
    260    prePath: "jar:",
    261    pathQueryRef: "resource://gre/chrome.toolkit.jar!/",
    262    ref: "",
    263    nsIURL: true,
    264    nsINestedURI: true,
    265  },
    266  {
    267    spec: "mailto:webmaster@mozilla.com",
    268    scheme: "mailto",
    269    prePath: "mailto:",
    270    pathQueryRef: "webmaster@mozilla.com",
    271    ref: "",
    272    nsIURL: false,
    273    nsINestedURI: false,
    274  },
    275  {
    276    spec: "javascript:new Date()",
    277    scheme: "javascript",
    278    prePath: "javascript:",
    279    pathQueryRef: "new Date()",
    280    ref: "",
    281    nsIURL: false,
    282    nsINestedURI: false,
    283  },
    284  {
    285    spec: "blob:123456",
    286    scheme: "blob",
    287    prePath: "blob:",
    288    pathQueryRef: "123456",
    289    ref: "",
    290    nsIURL: false,
    291    nsINestedURI: false,
    292    immutable: true,
    293  },
    294  {
    295    spec: "place:sort=8&maxResults=10",
    296    scheme: "place",
    297    prePath: "place:",
    298    pathQueryRef: "sort=8&maxResults=10",
    299    ref: "",
    300    nsIURL: false,
    301    nsINestedURI: false,
    302  },
    303  {
    304    spec: "resource://gre/",
    305    scheme: "resource",
    306    prePath: "resource://gre",
    307    pathQueryRef: "/",
    308    ref: "",
    309    nsIURL: true,
    310    nsINestedURI: false,
    311  },
    312  {
    313    spec: "resource://gre/components/",
    314    scheme: "resource",
    315    prePath: "resource://gre",
    316    pathQueryRef: "/components/",
    317    ref: "",
    318    nsIURL: true,
    319    nsINestedURI: false,
    320  },
    321 
    322  // Adding more? Consider adding to test_URIs2.js instead, so that neither
    323  // test runs for *too* long, risking timeouts on slow platforms.
    324 ];
    325 
    326 var gHashSuffixes = ["#", "#myRef", "#myRef?a=b", "#myRef#", "#myRef#x:yz"];
    327 
    328 // TEST HELPER FUNCTIONS
    329 // ---------------------
    330 function do_info(text, stack) {
    331  if (!stack) {
    332    stack = Components.stack.caller;
    333  }
    334 
    335  dump(
    336    "\n" +
    337      "TEST-INFO | " +
    338      stack.filename +
    339      " | [" +
    340      stack.name +
    341      " : " +
    342      stack.lineNumber +
    343      "] " +
    344      text +
    345      "\n"
    346  );
    347 }
    348 
    349 // Checks that the URIs satisfy equals(), in both possible orderings.
    350 // Also checks URI.equalsExceptRef(), because equal URIs should also be equal
    351 // when we ignore the ref.
    352 //
    353 // The third argument is optional. If the client passes a third argument
    354 // (e.g. todo_check_true), we'll use that in lieu of ok.
    355 function do_check_uri_eq(aURI1, aURI2, aCheckTrueFunc = ok) {
    356  do_info("(uri equals check: '" + aURI1.spec + "' == '" + aURI2.spec + "')");
    357  aCheckTrueFunc(aURI1.equals(aURI2));
    358  do_info("(uri equals check: '" + aURI2.spec + "' == '" + aURI1.spec + "')");
    359  aCheckTrueFunc(aURI2.equals(aURI1));
    360 
    361  // (Only take the extra step of testing 'equalsExceptRef' when we expect the
    362  // URIs to really be equal.  In 'todo' cases, the URIs may or may not be
    363  // equal when refs are ignored - there's no way of knowing in general.)
    364  if (aCheckTrueFunc == ok) {
    365    do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc);
    366  }
    367 }
    368 
    369 // Checks that the URIs satisfy equalsExceptRef(), in both possible orderings.
    370 //
    371 // The third argument is optional. If the client passes a third argument
    372 // (e.g. todo_check_true), we'll use that in lieu of ok.
    373 function do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc = ok) {
    374  do_info(
    375    "(uri equalsExceptRef check: '" + aURI1.spec + "' == '" + aURI2.spec + "')"
    376  );
    377  aCheckTrueFunc(aURI1.equalsExceptRef(aURI2));
    378  do_info(
    379    "(uri equalsExceptRef check: '" + aURI2.spec + "' == '" + aURI1.spec + "')"
    380  );
    381  aCheckTrueFunc(aURI2.equalsExceptRef(aURI1));
    382 }
    383 
    384 // Checks that the given property on aURI matches the corresponding property
    385 // in the test bundle (or matches some function of that corresponding property,
    386 // if aTestFunctor is passed in).
    387 function do_check_property(aTest, aURI, aPropertyName, aTestFunctor) {
    388  if (aTest[aPropertyName]) {
    389    var expectedVal = aTestFunctor
    390      ? aTestFunctor(aTest[aPropertyName])
    391      : aTest[aPropertyName];
    392 
    393    do_info(
    394      "testing " +
    395        aPropertyName +
    396        " of " +
    397        (aTestFunctor ? "modified '" : "'") +
    398        aTest.spec +
    399        "' is '" +
    400        expectedVal +
    401        "'"
    402    );
    403    Assert.equal(aURI[aPropertyName], expectedVal);
    404  }
    405 }
    406 
    407 // Test that a given URI parses correctly into its various components.
    408 function do_test_uri_basic(aTest) {
    409  var URI;
    410 
    411  do_info(
    412    "Basic tests for " +
    413      aTest.spec +
    414      " relative URI: " +
    415      (aTest.relativeURI === undefined ? "(none)" : aTest.relativeURI)
    416  );
    417 
    418  try {
    419    URI = NetUtil.newURI(aTest.spec);
    420  } catch (e) {
    421    do_info("Caught error on parse of" + aTest.spec + " Error: " + e.result);
    422    if (aTest.fail) {
    423      Assert.equal(e.result, aTest.result);
    424      return;
    425    }
    426    do_throw(e.result);
    427  }
    428 
    429  if (aTest.relativeURI) {
    430    var relURI;
    431 
    432    try {
    433      relURI = Services.io.newURI(aTest.relativeURI, null, URI);
    434    } catch (e) {
    435      do_info(
    436        "Caught error on Relative parse of " +
    437          aTest.spec +
    438          " + " +
    439          aTest.relativeURI +
    440          " Error: " +
    441          e.result
    442      );
    443      if (aTest.relativeFail) {
    444        Assert.equal(e.result, aTest.relativeFail);
    445        return;
    446      }
    447      do_throw(e.result);
    448    }
    449    do_info(
    450      "relURI.pathQueryRef = " +
    451        relURI.pathQueryRef +
    452        ", was " +
    453        URI.pathQueryRef
    454    );
    455    URI = relURI;
    456    do_info("URI.pathQueryRef now = " + URI.pathQueryRef);
    457  }
    458 
    459  // Sanity-check
    460  do_info("testing " + aTest.spec + " equals a clone of itself");
    461  do_check_uri_eq(URI, URI.mutate().finalize());
    462  do_check_uri_eqExceptRef(URI, URI.mutate().setRef("").finalize());
    463  do_info("testing " + aTest.spec + " instanceof nsIURL");
    464  Assert.equal(URI instanceof Ci.nsIURL, aTest.nsIURL);
    465  do_info("testing " + aTest.spec + " instanceof nsINestedURI");
    466  Assert.equal(URI instanceof Ci.nsINestedURI, aTest.nsINestedURI);
    467 
    468  do_info(
    469    "testing that " +
    470      aTest.spec +
    471      " throws or returns false " +
    472      "from equals(null)"
    473  );
    474  // XXXdholbert At some point it'd probably be worth making this behavior
    475  // (throwing vs. returning false) consistent across URI implementations.
    476  var threw = false;
    477  var isEqualToNull;
    478  try {
    479    isEqualToNull = URI.equals(null);
    480  } catch (e) {
    481    threw = true;
    482  }
    483  Assert.ok(threw || !isEqualToNull);
    484 
    485  // Check the various components
    486  do_check_property(aTest, URI, "scheme");
    487  do_check_property(aTest, URI, "prePath");
    488  do_check_property(aTest, URI, "pathQueryRef");
    489  do_check_property(aTest, URI, "query");
    490  do_check_property(aTest, URI, "ref");
    491  do_check_property(aTest, URI, "port");
    492  do_check_property(aTest, URI, "username");
    493  do_check_property(aTest, URI, "password");
    494  do_check_property(aTest, URI, "host");
    495  do_check_property(aTest, URI, "specIgnoringRef");
    496 
    497  do_info("testing hasRef");
    498  Assert.equal(URI.hasRef, !!aTest.ref, "URI.hasRef is correct");
    499  do_info("testing hasUserPass");
    500  Assert.equal(
    501    URI.hasUserPass,
    502    !!aTest.username || !!aTest.password,
    503    "URI.hasUserPass is correct"
    504  );
    505 }
    506 
    507 // Test that a given URI parses correctly when we add a given ref to the end
    508 function do_test_uri_with_hash_suffix(aTest, aSuffix) {
    509  do_info("making sure caller is using suffix that starts with '#'");
    510  Assert.equal(aSuffix[0], "#");
    511 
    512  var origURI = NetUtil.newURI(aTest.spec);
    513  var testURI;
    514 
    515  if (aTest.relativeURI) {
    516    try {
    517      origURI = Services.io.newURI(aTest.relativeURI, null, origURI);
    518    } catch (e) {
    519      do_info(
    520        "Caught error on Relative parse of " +
    521          aTest.spec +
    522          " + " +
    523          aTest.relativeURI +
    524          " Error: " +
    525          e.result
    526      );
    527      return;
    528    }
    529    try {
    530      testURI = Services.io.newURI(aSuffix, null, origURI);
    531    } catch (e) {
    532      do_info(
    533        "Caught error adding suffix to " +
    534          aTest.spec +
    535          " + " +
    536          aTest.relativeURI +
    537          ", suffix " +
    538          aSuffix +
    539          " Error: " +
    540          e.result
    541      );
    542      return;
    543    }
    544  } else {
    545    testURI = NetUtil.newURI(aTest.spec + aSuffix);
    546  }
    547 
    548  do_info(
    549    "testing " +
    550      aTest.spec +
    551      " with '" +
    552      aSuffix +
    553      "' appended " +
    554      "equals a clone of itself"
    555  );
    556  do_check_uri_eq(testURI, testURI.mutate().finalize());
    557 
    558  do_info(
    559    "testing " +
    560      aTest.spec +
    561      " doesn't equal self with '" +
    562      aSuffix +
    563      "' appended"
    564  );
    565 
    566  Assert.ok(!origURI.equals(testURI));
    567 
    568  do_info(
    569    "testing " +
    570      aTest.spec +
    571      " is equalExceptRef to self with '" +
    572      aSuffix +
    573      "' appended"
    574  );
    575  do_check_uri_eqExceptRef(origURI, testURI);
    576 
    577  Assert.equal(testURI.hasRef, true);
    578 
    579  if (!origURI.ref) {
    580    // These tests fail if origURI has a ref
    581    do_info(
    582      "testing setRef('') on " +
    583        testURI.spec +
    584        " is equal to no-ref version but not equal to ref version"
    585    );
    586    var cloneNoRef = testURI.mutate().setRef("").finalize(); // we used to clone here.
    587    do_info("cloneNoRef: " + cloneNoRef.spec + " hasRef: " + cloneNoRef.hasRef);
    588    do_info("testURI: " + testURI.spec + " hasRef: " + testURI.hasRef);
    589    do_check_uri_eq(cloneNoRef, origURI);
    590    Assert.ok(!cloneNoRef.equals(testURI));
    591 
    592    do_info(
    593      "testing cloneWithNewRef on " +
    594        testURI.spec +
    595        " with an empty ref is equal to no-ref version but not equal to ref version"
    596    );
    597    var cloneNewRef = testURI.mutate().setRef("").finalize();
    598    do_check_uri_eq(cloneNewRef, origURI);
    599    do_check_uri_eq(cloneNewRef, cloneNoRef);
    600    Assert.ok(!cloneNewRef.equals(testURI));
    601 
    602    do_info(
    603      "testing cloneWithNewRef on " +
    604        origURI.spec +
    605        " with the same new ref is equal to ref version and not equal to no-ref version"
    606    );
    607    cloneNewRef = origURI.mutate().setRef(aSuffix).finalize();
    608    do_check_uri_eq(cloneNewRef, testURI);
    609    Assert.ok(cloneNewRef.equals(testURI));
    610  }
    611 
    612  do_check_property(aTest, testURI, "scheme");
    613  do_check_property(aTest, testURI, "prePath");
    614  if (!origURI.ref) {
    615    // These don't work if it's a ref already because '+' doesn't give the right result
    616    do_check_property(aTest, testURI, "pathQueryRef", function (aStr) {
    617      return aStr + aSuffix;
    618    });
    619    do_check_property(aTest, testURI, "ref", function () {
    620      return aSuffix.substr(1);
    621    });
    622  }
    623 }
    624 
    625 // Tests various ways of setting & clearing a ref on a URI.
    626 function do_test_mutate_ref(aTest, aSuffix) {
    627  do_info("making sure caller is using suffix that starts with '#'");
    628  Assert.equal(aSuffix[0], "#");
    629 
    630  var refURIWithSuffix = NetUtil.newURI(aTest.spec + aSuffix);
    631  var refURIWithoutSuffix = NetUtil.newURI(aTest.spec);
    632 
    633  var testURI = NetUtil.newURI(aTest.spec);
    634 
    635  // First: Try setting .ref to our suffix
    636  do_info(
    637    "testing that setting .ref on " +
    638      aTest.spec +
    639      " to '" +
    640      aSuffix +
    641      "' does what we expect"
    642  );
    643  testURI = testURI.mutate().setRef(aSuffix).finalize();
    644  do_check_uri_eq(testURI, refURIWithSuffix);
    645  do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
    646 
    647  // Now try setting .ref but leave off the initial hash (expect same result)
    648  var suffixLackingHash = aSuffix.substr(1);
    649  if (suffixLackingHash) {
    650    // (skip this our suffix was *just* a #)
    651    do_info(
    652      "testing that setting .ref on " +
    653        aTest.spec +
    654        " to '" +
    655        suffixLackingHash +
    656        "' does what we expect"
    657    );
    658    testURI = testURI.mutate().setRef(suffixLackingHash).finalize();
    659    do_check_uri_eq(testURI, refURIWithSuffix);
    660    do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
    661  }
    662 
    663  // Now, clear .ref (should get us back the original spec)
    664  do_info(
    665    "testing that clearing .ref on " + testURI.spec + " does what we expect"
    666  );
    667  testURI = testURI.mutate().setRef("").finalize();
    668  do_check_uri_eq(testURI, refURIWithoutSuffix);
    669  do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
    670 
    671  if (!aTest.relativeURI) {
    672    // TODO: These tests don't work as-is for relative URIs.
    673 
    674    // Now try setting .spec directly (including suffix) and then clearing .ref
    675    var specWithSuffix = aTest.spec + aSuffix;
    676    do_info(
    677      "testing that setting spec to " +
    678        specWithSuffix +
    679        " and then clearing ref does what we expect"
    680    );
    681 
    682    testURI = testURI.mutate().setSpec(specWithSuffix).setRef("").finalize();
    683    do_check_uri_eq(testURI, refURIWithoutSuffix);
    684    do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
    685 
    686    // XXX nsIJARURI throws an exception in SetPath(), so skip it for next part.
    687    if (!(testURI instanceof Ci.nsIJARURI)) {
    688      // Now try setting .pathQueryRef directly (including suffix) and then clearing .ref
    689      // (same as above, but with now with .pathQueryRef instead of .spec)
    690      testURI = NetUtil.newURI(aTest.spec);
    691 
    692      var pathWithSuffix = aTest.pathQueryRef + aSuffix;
    693      do_info(
    694        "testing that setting path to " +
    695          pathWithSuffix +
    696          " and then clearing ref does what we expect"
    697      );
    698      testURI = testURI
    699        .mutate()
    700        .setPathQueryRef(pathWithSuffix)
    701        .setRef("")
    702        .finalize();
    703      do_check_uri_eq(testURI, refURIWithoutSuffix);
    704      do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
    705 
    706      // Also: make sure that clearing .pathQueryRef also clears .ref
    707      testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
    708      do_info(
    709        "testing that clearing path from " +
    710          pathWithSuffix +
    711          " also clears .ref"
    712      );
    713      testURI = testURI.mutate().setPathQueryRef("").finalize();
    714      Assert.equal(testURI.ref, "");
    715    }
    716  }
    717 }
    718 
    719 // Check that changing nested/about URIs works correctly.
    720 add_task(function check_nested_mutations() {
    721  // nsNestedAboutURI
    722  let uri1 = Services.io.newURI("about:blank#");
    723  let uri2 = Services.io.newURI("about:blank");
    724  let uri3 = uri1.mutate().setRef("").finalize();
    725  do_check_uri_eq(uri3, uri2);
    726  uri3 = uri2.mutate().setRef("#").finalize();
    727  do_check_uri_eq(uri3, uri1);
    728 
    729  uri1 = Services.io.newURI("about:blank?something");
    730  uri2 = Services.io.newURI("about:blank");
    731  uri3 = uri1.mutate().setQuery("").finalize();
    732  do_check_uri_eq(uri3, uri2);
    733  uri3 = uri2.mutate().setQuery("something").finalize();
    734  do_check_uri_eq(uri3, uri1);
    735 
    736  uri1 = Services.io.newURI("about:blank?query#ref");
    737  uri2 = Services.io.newURI("about:blank");
    738  uri3 = uri1.mutate().setPathQueryRef("blank").finalize();
    739  do_check_uri_eq(uri3, uri2);
    740  uri3 = uri2.mutate().setPathQueryRef("blank?query#ref").finalize();
    741  do_check_uri_eq(uri3, uri1);
    742 
    743  // nsSimpleNestedURI
    744  uri1 = Services.io.newURI("view-source:http://example.com/path#");
    745  uri2 = Services.io.newURI("view-source:http://example.com/path");
    746  uri3 = uri1.mutate().setRef("").finalize();
    747  do_check_uri_eq(uri3, uri2);
    748  uri3 = uri2.mutate().setRef("#").finalize();
    749  do_check_uri_eq(uri3, uri1);
    750 
    751  uri1 = Services.io.newURI("view-source:http://example.com/path?something");
    752  uri2 = Services.io.newURI("view-source:http://example.com/path");
    753  uri3 = uri1.mutate().setQuery("").finalize();
    754  do_check_uri_eq(uri3, uri2);
    755  uri3 = uri2.mutate().setQuery("something").finalize();
    756  do_check_uri_eq(uri3, uri1);
    757 
    758  uri1 = Services.io.newURI("view-source:http://example.com/path?query#ref");
    759  uri2 = Services.io.newURI("view-source:http://example.com/path");
    760  uri3 = uri1.mutate().setPathQueryRef("path").finalize();
    761  do_check_uri_eq(uri3, uri2);
    762  uri3 = uri2.mutate().setPathQueryRef("path?query#ref").finalize();
    763  do_check_uri_eq(uri3, uri1);
    764 
    765  uri1 = Services.io.newURI("view-source:about:blank#");
    766  uri2 = Services.io.newURI("view-source:about:blank");
    767  uri3 = uri1.mutate().setRef("").finalize();
    768  do_check_uri_eq(uri3, uri2);
    769  uri3 = uri2.mutate().setRef("#").finalize();
    770  do_check_uri_eq(uri3, uri1);
    771 
    772  uri1 = Services.io.newURI("view-source:about:blank?something");
    773  uri2 = Services.io.newURI("view-source:about:blank");
    774  uri3 = uri1.mutate().setQuery("").finalize();
    775  do_check_uri_eq(uri3, uri2);
    776  uri3 = uri2.mutate().setQuery("something").finalize();
    777  do_check_uri_eq(uri3, uri1);
    778 
    779  uri1 = Services.io.newURI("view-source:about:blank?query#ref");
    780  uri2 = Services.io.newURI("view-source:about:blank");
    781  uri3 = uri1.mutate().setPathQueryRef("blank").finalize();
    782  do_check_uri_eq(uri3, uri2);
    783  uri3 = uri2.mutate().setPathQueryRef("blank?query#ref").finalize();
    784  do_check_uri_eq(uri3, uri1);
    785 });
    786 
    787 add_task(function check_space_escaping() {
    788  let uri = Services.io.newURI("data:text/plain,hello%20world#space hash");
    789  Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash");
    790  uri = Services.io.newURI("data:text/plain,hello%20world#space%20hash");
    791  Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash");
    792  uri = Services.io.newURI("data:text/plain,hello world#space%20hash");
    793  Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash");
    794  uri = Services.io.newURI("data:text/plain,hello world#space hash");
    795  Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash");
    796  uri = Services.io.newURI("http://example.com/test path#test path");
    797  uri = Services.io.newURI("http://example.com/test%20path#test%20path");
    798 });
    799 
    800 add_task(function check_space_with_query_and_ref() {
    801  let url = Services.io.newURI("data:space");
    802  Assert.equal(url.spec, "data:space");
    803 
    804  url = Services.io.newURI("data:space ?");
    805  Assert.equal(url.spec, "data:space ?");
    806  url = Services.io.newURI("data:space #");
    807  Assert.equal(url.spec, "data:space #");
    808 
    809  url = Services.io.newURI("data:space?");
    810  Assert.equal(url.spec, "data:space?");
    811  url = Services.io.newURI("data:space#");
    812  Assert.equal(url.spec, "data:space#");
    813 
    814  url = Services.io.newURI("data:space ?query");
    815  Assert.equal(url.spec, "data:space ?query");
    816  url = url.mutate().setQuery("").finalize();
    817  Assert.equal(url.spec, "data:space");
    818 
    819  url = Services.io.newURI("data:space #ref");
    820  Assert.equal(url.spec, "data:space #ref");
    821  url = url.mutate().setRef("").finalize();
    822  Assert.equal(url.spec, "data:space");
    823 
    824  url = Services.io.newURI("data:space?query#ref");
    825  Assert.equal(url.spec, "data:space?query#ref");
    826  url = url.mutate().setRef("").finalize();
    827  Assert.equal(url.spec, "data:space?query");
    828  url = url.mutate().setQuery("").finalize();
    829  Assert.equal(url.spec, "data:space");
    830 
    831  url = Services.io.newURI("data:space#ref?query");
    832  Assert.equal(url.spec, "data:space#ref?query");
    833  url = url.mutate().setQuery("").finalize();
    834  Assert.equal(url.spec, "data:space#ref?query");
    835  url = url.mutate().setRef("").finalize();
    836  Assert.equal(url.spec, "data:space");
    837 
    838  url = Services.io.newURI("data:space ?query#ref");
    839  Assert.equal(url.spec, "data:space ?query#ref");
    840  url = url.mutate().setRef("").finalize();
    841  Assert.equal(url.spec, "data:space ?query");
    842  url = url.mutate().setQuery("").finalize();
    843  Assert.equal(url.spec, "data:space");
    844 
    845  url = Services.io.newURI("data:space #ref?query");
    846  Assert.equal(url.spec, "data:space #ref?query");
    847  url = url.mutate().setQuery("").finalize();
    848  Assert.equal(url.spec, "data:space #ref?query");
    849  url = url.mutate().setRef("").finalize();
    850  Assert.equal(url.spec, "data:space");
    851 });
    852 
    853 add_task(function check_schemeIsNull() {
    854  let uri = Services.io.newURI("data:text/plain,aaa");
    855  Assert.ok(!uri.schemeIs(null));
    856  uri = Services.io.newURI("http://example.com");
    857  Assert.ok(!uri.schemeIs(null));
    858  uri = Services.io.newURI("dummyscheme://example.com");
    859  Assert.ok(!uri.schemeIs(null));
    860  uri = Services.io.newURI("jar:resource://gre/chrome.toolkit.jar!/");
    861  Assert.ok(!uri.schemeIs(null));
    862  uri = Services.io.newURI("moz-icon://.unknown?size=32");
    863  Assert.ok(!uri.schemeIs(null));
    864 });
    865 
    866 // Check that characters in the query of moz-extension aren't improperly unescaped (Bug 1547882)
    867 add_task(function check_mozextension_query() {
    868  let uri = Services.io.newURI(
    869    "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html"
    870  );
    871  uri = uri
    872    .mutate()
    873    .setQuery("u=https%3A%2F%2Fnews.ycombinator.com%2F")
    874    .finalize();
    875  Assert.equal(uri.query, "u=https%3A%2F%2Fnews.ycombinator.com%2F");
    876  uri = Services.io.newURI(
    877    "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html?u=https%3A%2F%2Fnews.ycombinator.com%2F"
    878  );
    879  Assert.equal(
    880    uri.spec,
    881    "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html?u=https%3A%2F%2Fnews.ycombinator.com%2F"
    882  );
    883  Assert.equal(uri.query, "u=https%3A%2F%2Fnews.ycombinator.com%2F");
    884 });
    885 
    886 add_task(function check_resolve() {
    887  let base = Services.io.newURI("http://example.com");
    888  let uri = Services.io.newURI("tel::+371 27028456", "utf-8", base);
    889  Assert.equal(uri.spec, "tel::+371 27028456");
    890 });
    891 
    892 add_task(function test_extra_protocols() {
    893  // dweb://
    894  let url = Services.io.newURI("dweb://example.com/test");
    895  Assert.equal(url.host, "example.com");
    896 
    897  // dat://
    898  url = Services.io.newURI(
    899    "dat://41f8a987cfeba80a037e51cc8357d513b62514de36f2f9b3d3eeec7a8fb3b5a5/"
    900  );
    901  Assert.equal(
    902    url.host,
    903    "41f8a987cfeba80a037e51cc8357d513b62514de36f2f9b3d3eeec7a8fb3b5a5"
    904  );
    905  url = Services.io.newURI("dat://example.com/test");
    906  Assert.equal(url.host, "example.com");
    907 
    908  // ipfs://
    909  url = Services.io.newURI(
    910    "ipfs://bafybeiccfclkdtucu6y4yc5cpr6y3yuinr67svmii46v5cfcrkp47ihehy/frontend/license.txt"
    911  );
    912  Assert.equal(url.scheme, "ipfs");
    913  Assert.equal(
    914    url.host,
    915    "bafybeiccfclkdtucu6y4yc5cpr6y3yuinr67svmii46v5cfcrkp47ihehy"
    916  );
    917  Assert.equal(url.filePath, "/frontend/license.txt");
    918 
    919  // ipns://
    920  url = Services.io.newURI("ipns://peerdium.gozala.io/index.html");
    921  Assert.equal(url.scheme, "ipns");
    922  Assert.equal(url.host, "peerdium.gozala.io");
    923  Assert.equal(url.filePath, "/index.html");
    924 
    925  // ssb://
    926  url = Services.io.newURI("ssb://scuttlebutt.nz/index.html");
    927  Assert.equal(url.scheme, "ssb");
    928  Assert.equal(url.host, "scuttlebutt.nz");
    929  Assert.equal(url.filePath, "/index.html");
    930 
    931  // wtp://
    932  url = Services.io.newURI(
    933    "wtp://951ead31d09e4049fc1f21f137e233dd0589fcbd/blog/vim-tips/"
    934  );
    935  Assert.equal(url.scheme, "wtp");
    936  Assert.equal(url.host, "951ead31d09e4049fc1f21f137e233dd0589fcbd");
    937  Assert.equal(url.filePath, "/blog/vim-tips/");
    938 });
    939 
    940 // TEST MAIN FUNCTION
    941 // ------------------
    942 add_task(function mainTest() {
    943  // UTF-8 check - From bug 622981
    944  // ASCII
    945  let base = Services.io.newURI("http://example.org/xenia?");
    946  let resolved = Services.io.newURI("?x", null, base);
    947  let expected = Services.io.newURI("http://example.org/xenia?x");
    948  do_info(
    949    "Bug 662981: ACSII - comparing " + resolved.spec + " and " + expected.spec
    950  );
    951  Assert.ok(resolved.equals(expected));
    952 
    953  // UTF-8 character "è"
    954  // Bug 622981 was triggered by an empty query string
    955  base = Services.io.newURI("http://example.org/xènia?");
    956  resolved = Services.io.newURI("?x", null, base);
    957  expected = Services.io.newURI("http://example.org/xènia?x");
    958  do_info(
    959    "Bug 662981: UTF8 - comparing " + resolved.spec + " and " + expected.spec
    960  );
    961  Assert.ok(resolved.equals(expected));
    962 
    963  gTests.forEach(function (aTest) {
    964    // Check basic URI functionality
    965    do_test_uri_basic(aTest);
    966 
    967    if (!aTest.fail) {
    968      // Try adding various #-prefixed strings to the ends of the URIs
    969      gHashSuffixes.forEach(function (aSuffix) {
    970        do_test_uri_with_hash_suffix(aTest, aSuffix);
    971        if (!aTest.immutable) {
    972          do_test_mutate_ref(aTest, aSuffix);
    973        }
    974      });
    975 
    976      // For URIs that we couldn't mutate above due to them being immutable:
    977      // Now we check that they're actually immutable.
    978      if (aTest.immutable) {
    979        Assert.ok(aTest.immutable);
    980      }
    981    }
    982  });
    983 });
    984 
    985 function check_round_trip_serialization(spec) {
    986  dump(`checking ${spec}\n`);
    987  let uri = Services.io.newURI(spec);
    988  let str = serialize_to_escaped_string(uri);
    989  let other = deserialize_from_escaped_string(str).QueryInterface(Ci.nsIURI);
    990  equal(other.spec, uri.spec);
    991 }
    992 
    993 add_task(function test_iconURI_serialization() {
    994  // URIs taken from test_moz_icon_uri.js
    995 
    996  let tests = [
    997    "moz-icon://foo.html?contentType=bar&size=button&state=normal",
    998    "moz-icon://foo.html?size=3",
    999    "moz-icon://stock/foo",
   1000    "moz-icon:file://foo.txt",
   1001    "moz-icon://file://foo.txt",
   1002  ];
   1003 
   1004  tests.forEach(str => check_round_trip_serialization(str));
   1005 });
   1006 
   1007 add_task(function test_jarURI_serialization() {
   1008  check_round_trip_serialization("jar:http://example.com/bar.jar!/");
   1009 });
   1010 
   1011 add_task(async function round_trip_invalid_ace_label() {
   1012  // This is well-formed punycode, but an invalid ACE label due to hyphens in
   1013  // positions 3 & 4 and trailing hyphen. (Punycode-decode yields "xn--d淾-")
   1014  let uri = Services.io.newURI("http://xn--xn--d--fg4n/");
   1015  Assert.equal(uri.spec, "http://xn--xn--d--fg4n/");
   1016 
   1017  // Entirely invalid punycode will throw a MALFORMED error.
   1018  Assert.throws(() => {
   1019    uri = Services.io.newURI("http://a.b.c.XN--pokxncvks");
   1020  }, /NS_ERROR_MALFORMED_URI/);
   1021 });
   1022 
   1023 add_task(async function test_bug1875119() {
   1024  let uri1 = Services.io.newURI("file:///path");
   1025  let uri2 = Services.io.newURI("resource://test/bla");
   1026  // type of uri2 is still SubstitutingURL which overrides the implementation of EnsureFile,
   1027  // but it's scheme is now file.
   1028  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1876483 to disallow this
   1029  uri2 = uri2.mutate().setSpec("file:///path2").finalize();
   1030  // NOTE: this test was originally expecting `uri1.equals(uri2)` to be throwing
   1031  // a NS_NOINTERFACE error (instead of hitting a crash) when the new test landed
   1032  // as part of Bug 1875119, then as a side-effect of the fix applied by Bug 1926106
   1033  // the expected behavior is for the call to not raise an NS_NOINTERFACE error anymore
   1034  // but to be returning false instead (and so the test has been adjusted accordingly).
   1035  Assert.ok(!uri1.equals(uri2), "Expect uri1.equals(uri2) to be false");
   1036 });
   1037 
   1038 add_task(async function test_bug1843717() {
   1039  // Make sure file path normalization on windows
   1040  // doesn't affect the hash of the URL.
   1041  let base = Services.io.newURI("file:///abc\\def/");
   1042  let uri = Services.io.newURI("foo\\bar#x\\y", null, base);
   1043  Assert.equal(uri.spec, "file:///abc/def/foo/bar#x\\y");
   1044  uri = Services.io.newURI("foo\\bar#xy", null, base);
   1045  Assert.equal(uri.spec, "file:///abc/def/foo/bar#xy");
   1046  uri = Services.io.newURI("foo\\bar#", null, base);
   1047  Assert.equal(uri.spec, "file:///abc/def/foo/bar#");
   1048 });
   1049 
   1050 add_task(async function test_bug1874118() {
   1051  let base = Services.io.newURI("file:///tmp/mock/path");
   1052  let uri = Services.io.newURI("file:c:\\\\foo\\\\bar.html", null, base);
   1053  Assert.equal(uri.spec, "file:///c://foo//bar.html");
   1054 
   1055  base = Services.io.newURI("file:///tmp/mock/path");
   1056  uri = Services.io.newURI("file:c|\\\\foo\\\\bar.html", null, base);
   1057  Assert.equal(uri.spec, "file:///c://foo//bar.html");
   1058 
   1059  base = Services.io.newURI("file:///C:/");
   1060  uri = Services.io.newURI("..", null, base);
   1061  Assert.equal(uri.spec, "file:///C:/");
   1062 
   1063  base = Services.io.newURI("file:///");
   1064  uri = Services.io.newURI("C|/hello/../../", null, base);
   1065  Assert.equal(uri.spec, "file:///C:/");
   1066 
   1067  base = Services.io.newURI("file:///");
   1068  uri = Services.io.newURI("/C:/../../", null, base);
   1069  Assert.equal(uri.spec, "file:///C:/");
   1070 
   1071  base = Services.io.newURI("file:///");
   1072  uri = Services.io.newURI("/C://../../", null, base);
   1073  Assert.equal(uri.spec, "file:///C:/");
   1074 
   1075  base = Services.io.newURI("file:///tmp/mock/path");
   1076  uri = Services.io.newURI("C|/foo/bar", null, base);
   1077  Assert.equal(uri.spec, "file:///C:/foo/bar");
   1078 
   1079  base = Services.io.newURI("file:///tmp/mock/path");
   1080  uri = Services.io.newURI("/C|/foo/bar", null, base);
   1081  Assert.equal(uri.spec, "file:///C:/foo/bar");
   1082 });
   1083 
   1084 add_task(async function test_bug1911529() {
   1085  let testcases = [
   1086    [
   1087      "https://github.com/coder/coder/edit/main/docs/./enterprise.md",
   1088      "https://github.com/coder/coder/edit/main/docs/enterprise.md",
   1089      "enterprise",
   1090    ],
   1091    ["https://domain.com/.", "https://domain.com/", ""],
   1092    ["https://domain.com/%2e", "https://domain.com/", ""],
   1093    ["https://domain.com/%2e%2E", "https://domain.com/", ""],
   1094    ["https://domain.com/%2e%2E/.", "https://domain.com/", ""],
   1095    ["https://domain.com/./test.md", "https://domain.com/test.md", "test"],
   1096    [
   1097      "https://domain.com/dir/sub/%2e%2e/%2e/test.md",
   1098      "https://domain.com/dir/test.md",
   1099      "test",
   1100    ],
   1101    ["https://domain.com/dir/..", "https://domain.com/", ""],
   1102  ];
   1103 
   1104  for (let t of testcases) {
   1105    let uri = Services.io.newURI(t[0]);
   1106    let uri2 = Services.io.newURI(t[1]);
   1107    Assert.ok(uri.equals(uri2), `${uri} must equal ${uri2}`);
   1108    Assert.equal(t[2], uri.QueryInterface(Ci.nsIURL).fileBaseName);
   1109  }
   1110 });
   1111 
   1112 add_task(async function test_bug1939493() {
   1113  let uri = Services.io.newURI("resource:///components/");
   1114  uri = uri
   1115    .mutate()
   1116    .setUserPass("")
   1117    .setUsername("")
   1118    .setPassword("")
   1119    .setPort(-1)
   1120    .finalize();
   1121  Assert.equal(uri.spec, "resource:///components/");
   1122 
   1123  Assert.throws(() => {
   1124    uri = uri.mutate().setUserPass("a:b").finalize();
   1125  }, /NS_ERROR_UNEXPECTED/);
   1126  Assert.throws(() => {
   1127    uri = uri.mutate().setUsername("a").finalize();
   1128  }, /NS_ERROR_UNEXPECTED/);
   1129  Assert.throws(() => {
   1130    uri = uri.mutate().setPassword("b").finalize();
   1131  }, /NS_ERROR_UNEXPECTED/);
   1132  Assert.throws(() => {
   1133    uri = uri.mutate().setPort(10).finalize();
   1134  }, /NS_ERROR_UNEXPECTED/);
   1135 
   1136  // username, password and port doesn't really make sense for a resource URL
   1137  // but they still behave as regular URLs so this should be valid.
   1138  uri = uri.mutate().setHost("gre").finalize();
   1139  Assert.equal(uri.spec, "resource://gre/components/");
   1140  uri = uri.mutate().setUserPass("a:b").finalize();
   1141  Assert.equal(uri.spec, "resource://a:b@gre/components/");
   1142  uri = uri.mutate().setUsername("user").finalize();
   1143  Assert.equal(uri.spec, "resource://user:b@gre/components/");
   1144  uri = uri.mutate().setPassword("pass").finalize();
   1145  Assert.equal(uri.spec, "resource://user:pass@gre/components/");
   1146  uri = uri.mutate().setPort(10).finalize();
   1147  Assert.equal(uri.spec, "resource://user:pass@gre:10/components/");
   1148 
   1149  // Clearing the host should fail, as there are still user, port, pass in play.
   1150  Assert.throws(() => {
   1151    uri = uri.mutate().setHost("").finalize();
   1152  }, /NS_ERROR_MALFORMED_URI/);
   1153 
   1154  uri = uri
   1155    .mutate()
   1156    .setUserPass("")
   1157    .setUsername("")
   1158    .setPassword("")
   1159    .setPort(-1)
   1160    .setHost("")
   1161    .finalize();
   1162 
   1163  Assert.equal(uri.spec, "resource:///components/");
   1164 });