Bug 1857386 [wpt PR 42383] - Update wpt metadata, a=testonly
[gecko.git] / netwerk / test / unit / test_URIs.js
blob464ecd39136914b5b46005566d480bfd44f72934
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/. */
6 "use strict";
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
11 // See also test_URIs2.js.
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/
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:",
181     pathQueryRef: "//mozilla.org/",
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   },
322   // Adding more? Consider adding to test_URIs2.js instead, so that neither
323   // test runs for *too* long, risking timeouts on slow platforms.
326 var gHashSuffixes = ["#", "#myRef", "#myRef?a=b", "#myRef#", "#myRef#x:yz"];
328 // TEST HELPER FUNCTIONS
329 // ---------------------
330 function do_info(text, stack) {
331   if (!stack) {
332     stack = Components.stack.caller;
333   }
335   dump(
336     "\n" +
337       "TEST-INFO | " +
338       stack.filename +
339       " | [" +
340       stack.name +
341       " : " +
342       stack.lineNumber +
343       "] " +
344       text +
345       "\n"
346   );
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.
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));
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   }
369 // Checks that the URIs satisfy equalsExceptRef(), in both possible orderings.
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));
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];
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   }
407 // Test that a given URI parses correctly into its various components.
408 function do_test_uri_basic(aTest) {
409   var URI;
411   do_info(
412     "Basic tests for " +
413       aTest.spec +
414       " relative URI: " +
415       (aTest.relativeURI === undefined ? "(none)" : aTest.relativeURI)
416   );
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   }
429   if (aTest.relativeURI) {
430     var relURI;
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   }
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);
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);
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   if ("hasRef" in aTest) {
497     do_info("testing hasref: " + aTest.hasRef + " vs " + URI.hasRef);
498     Assert.equal(aTest.hasRef, URI.hasRef);
499   }
502 // Test that a given URI parses correctly when we add a given ref to the end
503 function do_test_uri_with_hash_suffix(aTest, aSuffix) {
504   do_info("making sure caller is using suffix that starts with '#'");
505   Assert.equal(aSuffix[0], "#");
507   var origURI = NetUtil.newURI(aTest.spec);
508   var testURI;
510   if (aTest.relativeURI) {
511     try {
512       origURI = Services.io.newURI(aTest.relativeURI, null, origURI);
513     } catch (e) {
514       do_info(
515         "Caught error on Relative parse of " +
516           aTest.spec +
517           " + " +
518           aTest.relativeURI +
519           " Error: " +
520           e.result
521       );
522       return;
523     }
524     try {
525       testURI = Services.io.newURI(aSuffix, null, origURI);
526     } catch (e) {
527       do_info(
528         "Caught error adding suffix to " +
529           aTest.spec +
530           " + " +
531           aTest.relativeURI +
532           ", suffix " +
533           aSuffix +
534           " Error: " +
535           e.result
536       );
537       return;
538     }
539   } else {
540     testURI = NetUtil.newURI(aTest.spec + aSuffix);
541   }
543   do_info(
544     "testing " +
545       aTest.spec +
546       " with '" +
547       aSuffix +
548       "' appended " +
549       "equals a clone of itself"
550   );
551   do_check_uri_eq(testURI, testURI.mutate().finalize());
553   do_info(
554     "testing " +
555       aTest.spec +
556       " doesn't equal self with '" +
557       aSuffix +
558       "' appended"
559   );
561   Assert.ok(!origURI.equals(testURI));
563   do_info(
564     "testing " +
565       aTest.spec +
566       " is equalExceptRef to self with '" +
567       aSuffix +
568       "' appended"
569   );
570   do_check_uri_eqExceptRef(origURI, testURI);
572   Assert.equal(testURI.hasRef, true);
574   if (!origURI.ref) {
575     // These tests fail if origURI has a ref
576     do_info(
577       "testing setRef('') on " +
578         testURI.spec +
579         " is equal to no-ref version but not equal to ref version"
580     );
581     var cloneNoRef = testURI.mutate().setRef("").finalize(); // we used to clone here.
582     do_info("cloneNoRef: " + cloneNoRef.spec + " hasRef: " + cloneNoRef.hasRef);
583     do_info("testURI: " + testURI.spec + " hasRef: " + testURI.hasRef);
584     do_check_uri_eq(cloneNoRef, origURI);
585     Assert.ok(!cloneNoRef.equals(testURI));
587     do_info(
588       "testing cloneWithNewRef on " +
589         testURI.spec +
590         " with an empty ref is equal to no-ref version but not equal to ref version"
591     );
592     var cloneNewRef = testURI.mutate().setRef("").finalize();
593     do_check_uri_eq(cloneNewRef, origURI);
594     do_check_uri_eq(cloneNewRef, cloneNoRef);
595     Assert.ok(!cloneNewRef.equals(testURI));
597     do_info(
598       "testing cloneWithNewRef on " +
599         origURI.spec +
600         " with the same new ref is equal to ref version and not equal to no-ref version"
601     );
602     cloneNewRef = origURI.mutate().setRef(aSuffix).finalize();
603     do_check_uri_eq(cloneNewRef, testURI);
604     Assert.ok(cloneNewRef.equals(testURI));
605   }
607   do_check_property(aTest, testURI, "scheme");
608   do_check_property(aTest, testURI, "prePath");
609   if (!origURI.ref) {
610     // These don't work if it's a ref already because '+' doesn't give the right result
611     do_check_property(aTest, testURI, "pathQueryRef", function (aStr) {
612       return aStr + aSuffix;
613     });
614     do_check_property(aTest, testURI, "ref", function (aStr) {
615       return aSuffix.substr(1);
616     });
617   }
620 // Tests various ways of setting & clearing a ref on a URI.
621 function do_test_mutate_ref(aTest, aSuffix) {
622   do_info("making sure caller is using suffix that starts with '#'");
623   Assert.equal(aSuffix[0], "#");
625   var refURIWithSuffix = NetUtil.newURI(aTest.spec + aSuffix);
626   var refURIWithoutSuffix = NetUtil.newURI(aTest.spec);
628   var testURI = NetUtil.newURI(aTest.spec);
630   // First: Try setting .ref to our suffix
631   do_info(
632     "testing that setting .ref on " +
633       aTest.spec +
634       " to '" +
635       aSuffix +
636       "' does what we expect"
637   );
638   testURI = testURI.mutate().setRef(aSuffix).finalize();
639   do_check_uri_eq(testURI, refURIWithSuffix);
640   do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
642   // Now try setting .ref but leave off the initial hash (expect same result)
643   var suffixLackingHash = aSuffix.substr(1);
644   if (suffixLackingHash) {
645     // (skip this our suffix was *just* a #)
646     do_info(
647       "testing that setting .ref on " +
648         aTest.spec +
649         " to '" +
650         suffixLackingHash +
651         "' does what we expect"
652     );
653     testURI = testURI.mutate().setRef(suffixLackingHash).finalize();
654     do_check_uri_eq(testURI, refURIWithSuffix);
655     do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
656   }
658   // Now, clear .ref (should get us back the original spec)
659   do_info(
660     "testing that clearing .ref on " + testURI.spec + " does what we expect"
661   );
662   testURI = testURI.mutate().setRef("").finalize();
663   do_check_uri_eq(testURI, refURIWithoutSuffix);
664   do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
666   if (!aTest.relativeURI) {
667     // TODO: These tests don't work as-is for relative URIs.
669     // Now try setting .spec directly (including suffix) and then clearing .ref
670     var specWithSuffix = aTest.spec + aSuffix;
671     do_info(
672       "testing that setting spec to " +
673         specWithSuffix +
674         " and then clearing ref does what we expect"
675     );
677     testURI = testURI.mutate().setSpec(specWithSuffix).setRef("").finalize();
678     do_check_uri_eq(testURI, refURIWithoutSuffix);
679     do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
681     // XXX nsIJARURI throws an exception in SetPath(), so skip it for next part.
682     if (!(testURI instanceof Ci.nsIJARURI)) {
683       // Now try setting .pathQueryRef directly (including suffix) and then clearing .ref
684       // (same as above, but with now with .pathQueryRef instead of .spec)
685       testURI = NetUtil.newURI(aTest.spec);
687       var pathWithSuffix = aTest.pathQueryRef + aSuffix;
688       do_info(
689         "testing that setting path to " +
690           pathWithSuffix +
691           " and then clearing ref does what we expect"
692       );
693       testURI = testURI
694         .mutate()
695         .setPathQueryRef(pathWithSuffix)
696         .setRef("")
697         .finalize();
698       do_check_uri_eq(testURI, refURIWithoutSuffix);
699       do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
701       // Also: make sure that clearing .pathQueryRef also clears .ref
702       testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
703       do_info(
704         "testing that clearing path from " +
705           pathWithSuffix +
706           " also clears .ref"
707       );
708       testURI = testURI.mutate().setPathQueryRef("").finalize();
709       Assert.equal(testURI.ref, "");
710     }
711   }
714 // Check that changing nested/about URIs works correctly.
715 add_task(function check_nested_mutations() {
716   // nsNestedAboutURI
717   let uri1 = Services.io.newURI("about:blank#");
718   let uri2 = Services.io.newURI("about:blank");
719   let uri3 = uri1.mutate().setRef("").finalize();
720   do_check_uri_eq(uri3, uri2);
721   uri3 = uri2.mutate().setRef("#").finalize();
722   do_check_uri_eq(uri3, uri1);
724   uri1 = Services.io.newURI("about:blank?something");
725   uri2 = Services.io.newURI("about:blank");
726   uri3 = uri1.mutate().setQuery("").finalize();
727   do_check_uri_eq(uri3, uri2);
728   uri3 = uri2.mutate().setQuery("something").finalize();
729   do_check_uri_eq(uri3, uri1);
731   uri1 = Services.io.newURI("about:blank?query#ref");
732   uri2 = Services.io.newURI("about:blank");
733   uri3 = uri1.mutate().setPathQueryRef("blank").finalize();
734   do_check_uri_eq(uri3, uri2);
735   uri3 = uri2.mutate().setPathQueryRef("blank?query#ref").finalize();
736   do_check_uri_eq(uri3, uri1);
738   // nsSimpleNestedURI
739   uri1 = Services.io.newURI("view-source:http://example.com/path#");
740   uri2 = Services.io.newURI("view-source:http://example.com/path");
741   uri3 = uri1.mutate().setRef("").finalize();
742   do_check_uri_eq(uri3, uri2);
743   uri3 = uri2.mutate().setRef("#").finalize();
744   do_check_uri_eq(uri3, uri1);
746   uri1 = Services.io.newURI("view-source:http://example.com/path?something");
747   uri2 = Services.io.newURI("view-source:http://example.com/path");
748   uri3 = uri1.mutate().setQuery("").finalize();
749   do_check_uri_eq(uri3, uri2);
750   uri3 = uri2.mutate().setQuery("something").finalize();
751   do_check_uri_eq(uri3, uri1);
753   uri1 = Services.io.newURI("view-source:http://example.com/path?query#ref");
754   uri2 = Services.io.newURI("view-source:http://example.com/path");
755   uri3 = uri1.mutate().setPathQueryRef("path").finalize();
756   do_check_uri_eq(uri3, uri2);
757   uri3 = uri2.mutate().setPathQueryRef("path?query#ref").finalize();
758   do_check_uri_eq(uri3, uri1);
760   uri1 = Services.io.newURI("view-source:about:blank#");
761   uri2 = Services.io.newURI("view-source:about:blank");
762   uri3 = uri1.mutate().setRef("").finalize();
763   do_check_uri_eq(uri3, uri2);
764   uri3 = uri2.mutate().setRef("#").finalize();
765   do_check_uri_eq(uri3, uri1);
767   uri1 = Services.io.newURI("view-source:about:blank?something");
768   uri2 = Services.io.newURI("view-source:about:blank");
769   uri3 = uri1.mutate().setQuery("").finalize();
770   do_check_uri_eq(uri3, uri2);
771   uri3 = uri2.mutate().setQuery("something").finalize();
772   do_check_uri_eq(uri3, uri1);
774   uri1 = Services.io.newURI("view-source:about:blank?query#ref");
775   uri2 = Services.io.newURI("view-source:about:blank");
776   uri3 = uri1.mutate().setPathQueryRef("blank").finalize();
777   do_check_uri_eq(uri3, uri2);
778   uri3 = uri2.mutate().setPathQueryRef("blank?query#ref").finalize();
779   do_check_uri_eq(uri3, uri1);
782 add_task(function check_space_escaping() {
783   let uri = Services.io.newURI("data:text/plain,hello%20world#space hash");
784   Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash");
785   uri = Services.io.newURI("data:text/plain,hello%20world#space%20hash");
786   Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash");
787   uri = Services.io.newURI("data:text/plain,hello world#space%20hash");
788   Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash");
789   uri = Services.io.newURI("data:text/plain,hello world#space hash");
790   Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash");
791   uri = Services.io.newURI("http://example.com/test path#test path");
792   uri = Services.io.newURI("http://example.com/test%20path#test%20path");
795 add_task(function check_schemeIsNull() {
796   let uri = Services.io.newURI("data:text/plain,aaa");
797   Assert.ok(!uri.schemeIs(null));
798   uri = Services.io.newURI("http://example.com");
799   Assert.ok(!uri.schemeIs(null));
800   uri = Services.io.newURI("dummyscheme://example.com");
801   Assert.ok(!uri.schemeIs(null));
802   uri = Services.io.newURI("jar:resource://gre/chrome.toolkit.jar!/");
803   Assert.ok(!uri.schemeIs(null));
804   uri = Services.io.newURI("moz-icon://.unknown?size=32");
805   Assert.ok(!uri.schemeIs(null));
808 // Check that characters in the query of moz-extension aren't improperly unescaped (Bug 1547882)
809 add_task(function check_mozextension_query() {
810   let uri = Services.io.newURI(
811     "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html"
812   );
813   uri = uri
814     .mutate()
815     .setQuery("u=https%3A%2F%2Fnews.ycombinator.com%2F")
816     .finalize();
817   Assert.equal(uri.query, "u=https%3A%2F%2Fnews.ycombinator.com%2F");
818   uri = Services.io.newURI(
819     "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html?u=https%3A%2F%2Fnews.ycombinator.com%2F"
820   );
821   Assert.equal(
822     uri.spec,
823     "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html?u=https%3A%2F%2Fnews.ycombinator.com%2F"
824   );
825   Assert.equal(uri.query, "u=https%3A%2F%2Fnews.ycombinator.com%2F");
828 add_task(function check_resolve() {
829   let base = Services.io.newURI("http://example.com");
830   let uri = Services.io.newURI("tel::+371 27028456", "utf-8", base);
831   Assert.equal(uri.spec, "tel::+371 27028456");
834 add_task(function test_extra_protocols() {
835   // dweb://
836   let url = Services.io.newURI("dweb://example.com/test");
837   Assert.equal(url.host, "example.com");
839   // dat://
840   url = Services.io.newURI(
841     "dat://41f8a987cfeba80a037e51cc8357d513b62514de36f2f9b3d3eeec7a8fb3b5a5/"
842   );
843   Assert.equal(
844     url.host,
845     "41f8a987cfeba80a037e51cc8357d513b62514de36f2f9b3d3eeec7a8fb3b5a5"
846   );
847   url = Services.io.newURI("dat://example.com/test");
848   Assert.equal(url.host, "example.com");
850   // ipfs://
851   url = Services.io.newURI(
852     "ipfs://bafybeiccfclkdtucu6y4yc5cpr6y3yuinr67svmii46v5cfcrkp47ihehy/frontend/license.txt"
853   );
854   Assert.equal(url.scheme, "ipfs");
855   Assert.equal(
856     url.host,
857     "bafybeiccfclkdtucu6y4yc5cpr6y3yuinr67svmii46v5cfcrkp47ihehy"
858   );
859   Assert.equal(url.filePath, "/frontend/license.txt");
861   // ipns://
862   url = Services.io.newURI("ipns://peerdium.gozala.io/index.html");
863   Assert.equal(url.scheme, "ipns");
864   Assert.equal(url.host, "peerdium.gozala.io");
865   Assert.equal(url.filePath, "/index.html");
867   // ssb://
868   url = Services.io.newURI("ssb://scuttlebutt.nz/index.html");
869   Assert.equal(url.scheme, "ssb");
870   Assert.equal(url.host, "scuttlebutt.nz");
871   Assert.equal(url.filePath, "/index.html");
873   // wtp://
874   url = Services.io.newURI(
875     "wtp://951ead31d09e4049fc1f21f137e233dd0589fcbd/blog/vim-tips/"
876   );
877   Assert.equal(url.scheme, "wtp");
878   Assert.equal(url.host, "951ead31d09e4049fc1f21f137e233dd0589fcbd");
879   Assert.equal(url.filePath, "/blog/vim-tips/");
882 // TEST MAIN FUNCTION
883 // ------------------
884 add_task(function mainTest() {
885   // UTF-8 check - From bug 622981
886   // ASCII
887   let base = Services.io.newURI("http://example.org/xenia?");
888   let resolved = Services.io.newURI("?x", null, base);
889   let expected = Services.io.newURI("http://example.org/xenia?x");
890   do_info(
891     "Bug 662981: ACSII - comparing " + resolved.spec + " and " + expected.spec
892   );
893   Assert.ok(resolved.equals(expected));
895   // UTF-8 character "è"
896   // Bug 622981 was triggered by an empty query string
897   base = Services.io.newURI("http://example.org/xènia?");
898   resolved = Services.io.newURI("?x", null, base);
899   expected = Services.io.newURI("http://example.org/xènia?x");
900   do_info(
901     "Bug 662981: UTF8 - comparing " + resolved.spec + " and " + expected.spec
902   );
903   Assert.ok(resolved.equals(expected));
905   gTests.forEach(function (aTest) {
906     // Check basic URI functionality
907     do_test_uri_basic(aTest);
909     if (!aTest.fail) {
910       // Try adding various #-prefixed strings to the ends of the URIs
911       gHashSuffixes.forEach(function (aSuffix) {
912         do_test_uri_with_hash_suffix(aTest, aSuffix);
913         if (!aTest.immutable) {
914           do_test_mutate_ref(aTest, aSuffix);
915         }
916       });
918       // For URIs that we couldn't mutate above due to them being immutable:
919       // Now we check that they're actually immutable.
920       if (aTest.immutable) {
921         Assert.ok(aTest.immutable);
922       }
923     }
924   });
927 function check_round_trip_serialization(spec) {
928   dump(`checking ${spec}\n`);
929   let uri = Services.io.newURI(spec);
930   let str = serialize_to_escaped_string(uri);
931   let other = deserialize_from_escaped_string(str).QueryInterface(Ci.nsIURI);
932   equal(other.spec, uri.spec);
935 add_task(function test_iconURI_serialization() {
936   // URIs taken from test_moz_icon_uri.js
938   let tests = [
939     "moz-icon://foo.html?contentType=bar&size=button&state=normal",
940     "moz-icon://foo.html?size=3",
941     "moz-icon://stock/foo",
942     "moz-icon:file://foo.txt",
943     "moz-icon://file://foo.txt",
944   ];
946   tests.forEach(str => check_round_trip_serialization(str));
949 add_task(function test_jarURI_serialization() {
950   check_round_trip_serialization("jar:http://example.com/bar.jar!/");
953 add_task(async function round_trip_invalid_ace_label() {
954   // This is well-formed punycode, but an invalid ACE label due to hyphens in
955   // positions 3 & 4 and trailing hyphen. (Punycode-decode yields "xn--d淾-")
956   let uri = Services.io.newURI("http://xn--xn--d--fg4n/");
957   Assert.equal(uri.spec, "http://xn--xn--d--fg4n/");
959   // Entirely invalid punycode will throw a MALFORMED error.
960   Assert.throws(() => {
961     uri = Services.io.newURI("http://a.b.c.XN--pokxncvks");
962   }, /NS_ERROR_MALFORMED_URI/);