diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/test/unit/test_URIs.js | 996 |
1 files changed, 996 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_URIs.js b/netwerk/test/unit/test_URIs.js new file mode 100644 index 0000000000..548ecd5535 --- /dev/null +++ b/netwerk/test/unit/test_URIs.js @@ -0,0 +1,996 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Run by: cd objdir; make -C netwerk/test/ xpcshell-tests +// or: cd objdir; make SOLO_FILE="test_URIs.js" -C netwerk/test/ check-one + +// See also test_URIs2.js. + +// Relevant RFCs: 1738, 1808, 2396, 3986 (newer than the code) +// http://greenbytes.de/tech/webdav/rfc3986.html#rfc.section.5.4 +// http://greenbytes.de/tech/tc/uris/ + +Services.prefs.setBoolPref("network.url.useDefaultURI", true); + +// TEST DATA +// --------- +var gTests = [ + { + spec: "about:blank", + scheme: "about", + prePath: "about:", + pathQueryRef: "blank", + ref: "", + nsIURL: false, + nsINestedURI: true, + immutable: true, + }, + { + spec: "about:foobar", + scheme: "about", + prePath: "about:", + pathQueryRef: "foobar", + ref: "", + nsIURL: false, + nsINestedURI: false, + immutable: true, + }, + { + spec: "chrome://foobar/somedir/somefile.xml", + scheme: "chrome", + prePath: "chrome://foobar", + pathQueryRef: "/somedir/somefile.xml", + ref: "", + nsIURL: true, + nsINestedURI: false, + immutable: true, + }, + { + spec: "data:text/html;charset=utf-8,<html></html>", + scheme: "data", + prePath: "data:", + pathQueryRef: "text/html;charset=utf-8,<html></html>", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "data:text/html;charset=utf-8,<html>\r\n\t</html>", + scheme: "data", + prePath: "data:", + pathQueryRef: "text/html;charset=utf-8,<html></html>", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "data:text/plain,hello%20world", + scheme: "data", + prePath: "data:", + pathQueryRef: "text/plain,hello%20world", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "data:text/plain,hello world", + scheme: "data", + prePath: "data:", + pathQueryRef: "text/plain,hello world", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "file:///dir/afile", + scheme: "data", + prePath: "data:", + pathQueryRef: "text/plain,2", + ref: "", + relativeURI: "data:te\nxt/plain,2", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "file://", + scheme: "file", + prePath: "file://", + pathQueryRef: "/", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "file:///", + scheme: "file", + prePath: "file://", + pathQueryRef: "/", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "file:///myFile.html", + scheme: "file", + prePath: "file://", + pathQueryRef: "/myFile.html", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "file:///dir/afile", + scheme: "file", + prePath: "file://", + pathQueryRef: "/dir/data/text/plain,2", + ref: "", + relativeURI: "data/text/plain,2", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "file:///dir/dir2/", + scheme: "file", + prePath: "file://", + pathQueryRef: "/dir/dir2/data/text/plain,2", + ref: "", + relativeURI: "data/text/plain,2", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "ftp://ftp.mozilla.org/pub/mozilla.org/README", + scheme: "ftp", + prePath: "ftp://ftp.mozilla.org", + pathQueryRef: "/pub/mozilla.org/README", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "ftp://foo:bar@ftp.mozilla.org:100/pub/mozilla.org/README", + scheme: "ftp", + prePath: "ftp://foo:bar@ftp.mozilla.org:100", + port: 100, + username: "foo", + password: "bar", + pathQueryRef: "/pub/mozilla.org/README", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "ftp://foo:@ftp.mozilla.org:100/pub/mozilla.org/README", + scheme: "ftp", + prePath: "ftp://foo@ftp.mozilla.org:100", + port: 100, + username: "foo", + password: "", + pathQueryRef: "/pub/mozilla.org/README", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + //Bug 706249 + { + spec: "gopher://mozilla.org/", + scheme: "gopher", + prePath: "gopher://mozilla.org", + pathQueryRef: "/", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "http://www.example.com/", + scheme: "http", + prePath: "http://www.example.com", + pathQueryRef: "/", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "http://www.exa\nmple.com/", + scheme: "http", + prePath: "http://www.example.com", + pathQueryRef: "/", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "http://10.32.4.239/", + scheme: "http", + prePath: "http://10.32.4.239", + host: "10.32.4.239", + pathQueryRef: "/", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "http://[::192.9.5.5]/ipng", + scheme: "http", + prePath: "http://[::c009:505]", + host: "::c009:505", + pathQueryRef: "/ipng", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:8888/index.html", + scheme: "http", + prePath: "http://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:8888", + host: "fedc:ba98:7654:3210:fedc:ba98:7654:3210", + port: 8888, + pathQueryRef: "/index.html", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "http://bar:foo@www.mozilla.org:8080/pub/mozilla.org/README.html", + scheme: "http", + prePath: "http://bar:foo@www.mozilla.org:8080", + port: 8080, + username: "bar", + password: "foo", + host: "www.mozilla.org", + pathQueryRef: "/pub/mozilla.org/README.html", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "jar:resource://!/", + scheme: "jar", + prePath: "jar:", + pathQueryRef: "resource:///!/", + ref: "", + nsIURL: true, + nsINestedURI: true, + }, + { + spec: "jar:resource://gre/chrome.toolkit.jar!/", + scheme: "jar", + prePath: "jar:", + pathQueryRef: "resource://gre/chrome.toolkit.jar!/", + ref: "", + nsIURL: true, + nsINestedURI: true, + }, + { + spec: "mailto:webmaster@mozilla.com", + scheme: "mailto", + prePath: "mailto:", + pathQueryRef: "webmaster@mozilla.com", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "javascript:new Date()", + scheme: "javascript", + prePath: "javascript:", + pathQueryRef: "new Date()", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "blob:123456", + scheme: "blob", + prePath: "blob:", + pathQueryRef: "123456", + ref: "", + nsIURL: false, + nsINestedURI: false, + immutable: true, + }, + { + spec: "place:sort=8&maxResults=10", + scheme: "place", + prePath: "place:", + pathQueryRef: "sort=8&maxResults=10", + ref: "", + nsIURL: false, + nsINestedURI: false, + }, + { + spec: "resource://gre/", + scheme: "resource", + prePath: "resource://gre", + pathQueryRef: "/", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + { + spec: "resource://gre/components/", + scheme: "resource", + prePath: "resource://gre", + pathQueryRef: "/components/", + ref: "", + nsIURL: true, + nsINestedURI: false, + }, + + // Adding more? Consider adding to test_URIs2.js instead, so that neither + // test runs for *too* long, risking timeouts on slow platforms. +]; + +var gHashSuffixes = ["#", "#myRef", "#myRef?a=b", "#myRef#", "#myRef#x:yz"]; + +// TEST HELPER FUNCTIONS +// --------------------- +function do_info(text, stack) { + if (!stack) { + stack = Components.stack.caller; + } + + dump( + "\n" + + "TEST-INFO | " + + stack.filename + + " | [" + + stack.name + + " : " + + stack.lineNumber + + "] " + + text + + "\n" + ); +} + +// Checks that the URIs satisfy equals(), in both possible orderings. +// Also checks URI.equalsExceptRef(), because equal URIs should also be equal +// when we ignore the ref. +// +// The third argument is optional. If the client passes a third argument +// (e.g. todo_check_true), we'll use that in lieu of ok. +function do_check_uri_eq(aURI1, aURI2, aCheckTrueFunc = ok) { + do_info("(uri equals check: '" + aURI1.spec + "' == '" + aURI2.spec + "')"); + aCheckTrueFunc(aURI1.equals(aURI2)); + do_info("(uri equals check: '" + aURI2.spec + "' == '" + aURI1.spec + "')"); + aCheckTrueFunc(aURI2.equals(aURI1)); + + // (Only take the extra step of testing 'equalsExceptRef' when we expect the + // URIs to really be equal. In 'todo' cases, the URIs may or may not be + // equal when refs are ignored - there's no way of knowing in general.) + if (aCheckTrueFunc == ok) { + do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc); + } +} + +// Checks that the URIs satisfy equalsExceptRef(), in both possible orderings. +// +// The third argument is optional. If the client passes a third argument +// (e.g. todo_check_true), we'll use that in lieu of ok. +function do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc = ok) { + do_info( + "(uri equalsExceptRef check: '" + aURI1.spec + "' == '" + aURI2.spec + "')" + ); + aCheckTrueFunc(aURI1.equalsExceptRef(aURI2)); + do_info( + "(uri equalsExceptRef check: '" + aURI2.spec + "' == '" + aURI1.spec + "')" + ); + aCheckTrueFunc(aURI2.equalsExceptRef(aURI1)); +} + +// Checks that the given property on aURI matches the corresponding property +// in the test bundle (or matches some function of that corresponding property, +// if aTestFunctor is passed in). +function do_check_property(aTest, aURI, aPropertyName, aTestFunctor) { + if (aTest[aPropertyName]) { + var expectedVal = aTestFunctor + ? aTestFunctor(aTest[aPropertyName]) + : aTest[aPropertyName]; + + do_info( + "testing " + + aPropertyName + + " of " + + (aTestFunctor ? "modified '" : "'") + + aTest.spec + + "' is '" + + expectedVal + + "'" + ); + Assert.equal(aURI[aPropertyName], expectedVal); + } +} + +// Test that a given URI parses correctly into its various components. +function do_test_uri_basic(aTest) { + var URI; + + do_info( + "Basic tests for " + + aTest.spec + + " relative URI: " + + (aTest.relativeURI === undefined ? "(none)" : aTest.relativeURI) + ); + + try { + URI = NetUtil.newURI(aTest.spec); + } catch (e) { + do_info("Caught error on parse of" + aTest.spec + " Error: " + e.result); + if (aTest.fail) { + Assert.equal(e.result, aTest.result); + return; + } + do_throw(e.result); + } + + if (aTest.relativeURI) { + var relURI; + + try { + relURI = Services.io.newURI(aTest.relativeURI, null, URI); + } catch (e) { + do_info( + "Caught error on Relative parse of " + + aTest.spec + + " + " + + aTest.relativeURI + + " Error: " + + e.result + ); + if (aTest.relativeFail) { + Assert.equal(e.result, aTest.relativeFail); + return; + } + do_throw(e.result); + } + do_info( + "relURI.pathQueryRef = " + + relURI.pathQueryRef + + ", was " + + URI.pathQueryRef + ); + URI = relURI; + do_info("URI.pathQueryRef now = " + URI.pathQueryRef); + } + + // Sanity-check + do_info("testing " + aTest.spec + " equals a clone of itself"); + do_check_uri_eq(URI, URI.mutate().finalize()); + do_check_uri_eqExceptRef(URI, URI.mutate().setRef("").finalize()); + do_info("testing " + aTest.spec + " instanceof nsIURL"); + Assert.equal(URI instanceof Ci.nsIURL, aTest.nsIURL); + do_info("testing " + aTest.spec + " instanceof nsINestedURI"); + Assert.equal(URI instanceof Ci.nsINestedURI, aTest.nsINestedURI); + + do_info( + "testing that " + + aTest.spec + + " throws or returns false " + + "from equals(null)" + ); + // XXXdholbert At some point it'd probably be worth making this behavior + // (throwing vs. returning false) consistent across URI implementations. + var threw = false; + var isEqualToNull; + try { + isEqualToNull = URI.equals(null); + } catch (e) { + threw = true; + } + Assert.ok(threw || !isEqualToNull); + + // Check the various components + do_check_property(aTest, URI, "scheme"); + do_check_property(aTest, URI, "prePath"); + do_check_property(aTest, URI, "pathQueryRef"); + do_check_property(aTest, URI, "query"); + do_check_property(aTest, URI, "ref"); + do_check_property(aTest, URI, "port"); + do_check_property(aTest, URI, "username"); + do_check_property(aTest, URI, "password"); + do_check_property(aTest, URI, "host"); + do_check_property(aTest, URI, "specIgnoringRef"); + + do_info("testing hasRef"); + Assert.equal(URI.hasRef, !!aTest.ref, "URI.hasRef is correct"); + do_info("testing hasUserPass"); + Assert.equal( + URI.hasUserPass, + !!aTest.username || !!aTest.password, + "URI.hasUserPass is correct" + ); +} + +// Test that a given URI parses correctly when we add a given ref to the end +function do_test_uri_with_hash_suffix(aTest, aSuffix) { + do_info("making sure caller is using suffix that starts with '#'"); + Assert.equal(aSuffix[0], "#"); + + var origURI = NetUtil.newURI(aTest.spec); + var testURI; + + if (aTest.relativeURI) { + try { + origURI = Services.io.newURI(aTest.relativeURI, null, origURI); + } catch (e) { + do_info( + "Caught error on Relative parse of " + + aTest.spec + + " + " + + aTest.relativeURI + + " Error: " + + e.result + ); + return; + } + try { + testURI = Services.io.newURI(aSuffix, null, origURI); + } catch (e) { + do_info( + "Caught error adding suffix to " + + aTest.spec + + " + " + + aTest.relativeURI + + ", suffix " + + aSuffix + + " Error: " + + e.result + ); + return; + } + } else { + testURI = NetUtil.newURI(aTest.spec + aSuffix); + } + + do_info( + "testing " + + aTest.spec + + " with '" + + aSuffix + + "' appended " + + "equals a clone of itself" + ); + do_check_uri_eq(testURI, testURI.mutate().finalize()); + + do_info( + "testing " + + aTest.spec + + " doesn't equal self with '" + + aSuffix + + "' appended" + ); + + Assert.ok(!origURI.equals(testURI)); + + do_info( + "testing " + + aTest.spec + + " is equalExceptRef to self with '" + + aSuffix + + "' appended" + ); + do_check_uri_eqExceptRef(origURI, testURI); + + Assert.equal(testURI.hasRef, true); + + if (!origURI.ref) { + // These tests fail if origURI has a ref + do_info( + "testing setRef('') on " + + testURI.spec + + " is equal to no-ref version but not equal to ref version" + ); + var cloneNoRef = testURI.mutate().setRef("").finalize(); // we used to clone here. + do_info("cloneNoRef: " + cloneNoRef.spec + " hasRef: " + cloneNoRef.hasRef); + do_info("testURI: " + testURI.spec + " hasRef: " + testURI.hasRef); + do_check_uri_eq(cloneNoRef, origURI); + Assert.ok(!cloneNoRef.equals(testURI)); + + do_info( + "testing cloneWithNewRef on " + + testURI.spec + + " with an empty ref is equal to no-ref version but not equal to ref version" + ); + var cloneNewRef = testURI.mutate().setRef("").finalize(); + do_check_uri_eq(cloneNewRef, origURI); + do_check_uri_eq(cloneNewRef, cloneNoRef); + Assert.ok(!cloneNewRef.equals(testURI)); + + do_info( + "testing cloneWithNewRef on " + + origURI.spec + + " with the same new ref is equal to ref version and not equal to no-ref version" + ); + cloneNewRef = origURI.mutate().setRef(aSuffix).finalize(); + do_check_uri_eq(cloneNewRef, testURI); + Assert.ok(cloneNewRef.equals(testURI)); + } + + do_check_property(aTest, testURI, "scheme"); + do_check_property(aTest, testURI, "prePath"); + if (!origURI.ref) { + // These don't work if it's a ref already because '+' doesn't give the right result + do_check_property(aTest, testURI, "pathQueryRef", function (aStr) { + return aStr + aSuffix; + }); + do_check_property(aTest, testURI, "ref", function (aStr) { + return aSuffix.substr(1); + }); + } +} + +// Tests various ways of setting & clearing a ref on a URI. +function do_test_mutate_ref(aTest, aSuffix) { + do_info("making sure caller is using suffix that starts with '#'"); + Assert.equal(aSuffix[0], "#"); + + var refURIWithSuffix = NetUtil.newURI(aTest.spec + aSuffix); + var refURIWithoutSuffix = NetUtil.newURI(aTest.spec); + + var testURI = NetUtil.newURI(aTest.spec); + + // First: Try setting .ref to our suffix + do_info( + "testing that setting .ref on " + + aTest.spec + + " to '" + + aSuffix + + "' does what we expect" + ); + testURI = testURI.mutate().setRef(aSuffix).finalize(); + do_check_uri_eq(testURI, refURIWithSuffix); + do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix); + + // Now try setting .ref but leave off the initial hash (expect same result) + var suffixLackingHash = aSuffix.substr(1); + if (suffixLackingHash) { + // (skip this our suffix was *just* a #) + do_info( + "testing that setting .ref on " + + aTest.spec + + " to '" + + suffixLackingHash + + "' does what we expect" + ); + testURI = testURI.mutate().setRef(suffixLackingHash).finalize(); + do_check_uri_eq(testURI, refURIWithSuffix); + do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix); + } + + // Now, clear .ref (should get us back the original spec) + do_info( + "testing that clearing .ref on " + testURI.spec + " does what we expect" + ); + testURI = testURI.mutate().setRef("").finalize(); + do_check_uri_eq(testURI, refURIWithoutSuffix); + do_check_uri_eqExceptRef(testURI, refURIWithSuffix); + + if (!aTest.relativeURI) { + // TODO: These tests don't work as-is for relative URIs. + + // Now try setting .spec directly (including suffix) and then clearing .ref + var specWithSuffix = aTest.spec + aSuffix; + do_info( + "testing that setting spec to " + + specWithSuffix + + " and then clearing ref does what we expect" + ); + + testURI = testURI.mutate().setSpec(specWithSuffix).setRef("").finalize(); + do_check_uri_eq(testURI, refURIWithoutSuffix); + do_check_uri_eqExceptRef(testURI, refURIWithSuffix); + + // XXX nsIJARURI throws an exception in SetPath(), so skip it for next part. + if (!(testURI instanceof Ci.nsIJARURI)) { + // Now try setting .pathQueryRef directly (including suffix) and then clearing .ref + // (same as above, but with now with .pathQueryRef instead of .spec) + testURI = NetUtil.newURI(aTest.spec); + + var pathWithSuffix = aTest.pathQueryRef + aSuffix; + do_info( + "testing that setting path to " + + pathWithSuffix + + " and then clearing ref does what we expect" + ); + testURI = testURI + .mutate() + .setPathQueryRef(pathWithSuffix) + .setRef("") + .finalize(); + do_check_uri_eq(testURI, refURIWithoutSuffix); + do_check_uri_eqExceptRef(testURI, refURIWithSuffix); + + // Also: make sure that clearing .pathQueryRef also clears .ref + testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize(); + do_info( + "testing that clearing path from " + + pathWithSuffix + + " also clears .ref" + ); + testURI = testURI.mutate().setPathQueryRef("").finalize(); + Assert.equal(testURI.ref, ""); + } + } +} + +// Check that changing nested/about URIs works correctly. +add_task(function check_nested_mutations() { + // nsNestedAboutURI + let uri1 = Services.io.newURI("about:blank#"); + let uri2 = Services.io.newURI("about:blank"); + let uri3 = uri1.mutate().setRef("").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setRef("#").finalize(); + do_check_uri_eq(uri3, uri1); + + uri1 = Services.io.newURI("about:blank?something"); + uri2 = Services.io.newURI("about:blank"); + uri3 = uri1.mutate().setQuery("").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setQuery("something").finalize(); + do_check_uri_eq(uri3, uri1); + + uri1 = Services.io.newURI("about:blank?query#ref"); + uri2 = Services.io.newURI("about:blank"); + uri3 = uri1.mutate().setPathQueryRef("blank").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setPathQueryRef("blank?query#ref").finalize(); + do_check_uri_eq(uri3, uri1); + + // nsSimpleNestedURI + uri1 = Services.io.newURI("view-source:http://example.com/path#"); + uri2 = Services.io.newURI("view-source:http://example.com/path"); + uri3 = uri1.mutate().setRef("").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setRef("#").finalize(); + do_check_uri_eq(uri3, uri1); + + uri1 = Services.io.newURI("view-source:http://example.com/path?something"); + uri2 = Services.io.newURI("view-source:http://example.com/path"); + uri3 = uri1.mutate().setQuery("").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setQuery("something").finalize(); + do_check_uri_eq(uri3, uri1); + + uri1 = Services.io.newURI("view-source:http://example.com/path?query#ref"); + uri2 = Services.io.newURI("view-source:http://example.com/path"); + uri3 = uri1.mutate().setPathQueryRef("path").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setPathQueryRef("path?query#ref").finalize(); + do_check_uri_eq(uri3, uri1); + + uri1 = Services.io.newURI("view-source:about:blank#"); + uri2 = Services.io.newURI("view-source:about:blank"); + uri3 = uri1.mutate().setRef("").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setRef("#").finalize(); + do_check_uri_eq(uri3, uri1); + + uri1 = Services.io.newURI("view-source:about:blank?something"); + uri2 = Services.io.newURI("view-source:about:blank"); + uri3 = uri1.mutate().setQuery("").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setQuery("something").finalize(); + do_check_uri_eq(uri3, uri1); + + uri1 = Services.io.newURI("view-source:about:blank?query#ref"); + uri2 = Services.io.newURI("view-source:about:blank"); + uri3 = uri1.mutate().setPathQueryRef("blank").finalize(); + do_check_uri_eq(uri3, uri2); + uri3 = uri2.mutate().setPathQueryRef("blank?query#ref").finalize(); + do_check_uri_eq(uri3, uri1); +}); + +add_task(function check_space_escaping() { + let uri = Services.io.newURI("data:text/plain,hello%20world#space hash"); + Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash"); + uri = Services.io.newURI("data:text/plain,hello%20world#space%20hash"); + Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash"); + uri = Services.io.newURI("data:text/plain,hello world#space%20hash"); + Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash"); + uri = Services.io.newURI("data:text/plain,hello world#space hash"); + Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash"); + uri = Services.io.newURI("http://example.com/test path#test path"); + uri = Services.io.newURI("http://example.com/test%20path#test%20path"); +}); + +add_task(function check_schemeIsNull() { + let uri = Services.io.newURI("data:text/plain,aaa"); + Assert.ok(!uri.schemeIs(null)); + uri = Services.io.newURI("http://example.com"); + Assert.ok(!uri.schemeIs(null)); + uri = Services.io.newURI("dummyscheme://example.com"); + Assert.ok(!uri.schemeIs(null)); + uri = Services.io.newURI("jar:resource://gre/chrome.toolkit.jar!/"); + Assert.ok(!uri.schemeIs(null)); + uri = Services.io.newURI("moz-icon://.unknown?size=32"); + Assert.ok(!uri.schemeIs(null)); +}); + +// Check that characters in the query of moz-extension aren't improperly unescaped (Bug 1547882) +add_task(function check_mozextension_query() { + let uri = Services.io.newURI( + "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html" + ); + uri = uri + .mutate() + .setQuery("u=https%3A%2F%2Fnews.ycombinator.com%2F") + .finalize(); + Assert.equal(uri.query, "u=https%3A%2F%2Fnews.ycombinator.com%2F"); + uri = Services.io.newURI( + "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html?u=https%3A%2F%2Fnews.ycombinator.com%2F" + ); + Assert.equal( + uri.spec, + "moz-extension://a7d1572e-3beb-4d93-a920-c408fa09e8ea/_source/holding.html?u=https%3A%2F%2Fnews.ycombinator.com%2F" + ); + Assert.equal(uri.query, "u=https%3A%2F%2Fnews.ycombinator.com%2F"); +}); + +add_task(function check_resolve() { + let base = Services.io.newURI("http://example.com"); + let uri = Services.io.newURI("tel::+371 27028456", "utf-8", base); + Assert.equal(uri.spec, "tel::+371 27028456"); +}); + +add_task(function test_extra_protocols() { + // dweb:// + let url = Services.io.newURI("dweb://example.com/test"); + Assert.equal(url.host, "example.com"); + + // dat:// + url = Services.io.newURI( + "dat://41f8a987cfeba80a037e51cc8357d513b62514de36f2f9b3d3eeec7a8fb3b5a5/" + ); + Assert.equal( + url.host, + "41f8a987cfeba80a037e51cc8357d513b62514de36f2f9b3d3eeec7a8fb3b5a5" + ); + url = Services.io.newURI("dat://example.com/test"); + Assert.equal(url.host, "example.com"); + + // ipfs:// + url = Services.io.newURI( + "ipfs://bafybeiccfclkdtucu6y4yc5cpr6y3yuinr67svmii46v5cfcrkp47ihehy/frontend/license.txt" + ); + Assert.equal(url.scheme, "ipfs"); + Assert.equal( + url.host, + "bafybeiccfclkdtucu6y4yc5cpr6y3yuinr67svmii46v5cfcrkp47ihehy" + ); + Assert.equal(url.filePath, "/frontend/license.txt"); + + // ipns:// + url = Services.io.newURI("ipns://peerdium.gozala.io/index.html"); + Assert.equal(url.scheme, "ipns"); + Assert.equal(url.host, "peerdium.gozala.io"); + Assert.equal(url.filePath, "/index.html"); + + // ssb:// + url = Services.io.newURI("ssb://scuttlebutt.nz/index.html"); + Assert.equal(url.scheme, "ssb"); + Assert.equal(url.host, "scuttlebutt.nz"); + Assert.equal(url.filePath, "/index.html"); + + // wtp:// + url = Services.io.newURI( + "wtp://951ead31d09e4049fc1f21f137e233dd0589fcbd/blog/vim-tips/" + ); + Assert.equal(url.scheme, "wtp"); + Assert.equal(url.host, "951ead31d09e4049fc1f21f137e233dd0589fcbd"); + Assert.equal(url.filePath, "/blog/vim-tips/"); +}); + +// TEST MAIN FUNCTION +// ------------------ +add_task(function mainTest() { + // UTF-8 check - From bug 622981 + // ASCII + let base = Services.io.newURI("http://example.org/xenia?"); + let resolved = Services.io.newURI("?x", null, base); + let expected = Services.io.newURI("http://example.org/xenia?x"); + do_info( + "Bug 662981: ACSII - comparing " + resolved.spec + " and " + expected.spec + ); + Assert.ok(resolved.equals(expected)); + + // UTF-8 character "è" + // Bug 622981 was triggered by an empty query string + base = Services.io.newURI("http://example.org/xènia?"); + resolved = Services.io.newURI("?x", null, base); + expected = Services.io.newURI("http://example.org/xènia?x"); + do_info( + "Bug 662981: UTF8 - comparing " + resolved.spec + " and " + expected.spec + ); + Assert.ok(resolved.equals(expected)); + + gTests.forEach(function (aTest) { + // Check basic URI functionality + do_test_uri_basic(aTest); + + if (!aTest.fail) { + // Try adding various #-prefixed strings to the ends of the URIs + gHashSuffixes.forEach(function (aSuffix) { + do_test_uri_with_hash_suffix(aTest, aSuffix); + if (!aTest.immutable) { + do_test_mutate_ref(aTest, aSuffix); + } + }); + + // For URIs that we couldn't mutate above due to them being immutable: + // Now we check that they're actually immutable. + if (aTest.immutable) { + Assert.ok(aTest.immutable); + } + } + }); +}); + +function check_round_trip_serialization(spec) { + dump(`checking ${spec}\n`); + let uri = Services.io.newURI(spec); + let str = serialize_to_escaped_string(uri); + let other = deserialize_from_escaped_string(str).QueryInterface(Ci.nsIURI); + equal(other.spec, uri.spec); +} + +add_task(function test_iconURI_serialization() { + // URIs taken from test_moz_icon_uri.js + + let tests = [ + "moz-icon://foo.html?contentType=bar&size=button&state=normal", + "moz-icon://foo.html?size=3", + "moz-icon://stock/foo", + "moz-icon:file://foo.txt", + "moz-icon://file://foo.txt", + ]; + + tests.forEach(str => check_round_trip_serialization(str)); +}); + +add_task(function test_jarURI_serialization() { + check_round_trip_serialization("jar:http://example.com/bar.jar!/"); +}); + +add_task(async function round_trip_invalid_ace_label() { + // This is well-formed punycode, but an invalid ACE label due to hyphens in + // positions 3 & 4 and trailing hyphen. (Punycode-decode yields "xn--d淾-") + let uri = Services.io.newURI("http://xn--xn--d--fg4n/"); + Assert.equal(uri.spec, "http://xn--xn--d--fg4n/"); + + // Entirely invalid punycode will throw a MALFORMED error. + Assert.throws(() => { + uri = Services.io.newURI("http://a.b.c.XN--pokxncvks"); + }, /NS_ERROR_MALFORMED_URI/); +}); + +add_task(async function test_bug1875119() { + let uri1 = Services.io.newURI("file:///path"); + let uri2 = Services.io.newURI("resource://test/bla"); + // type of uri2 is still SubstitutingURL which overrides the implementation of EnsureFile, + // but it's scheme is now file. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1876483 to disallow this + uri2 = uri2.mutate().setSpec("file:///path2").finalize(); + Assert.throws( + () => uri1.equals(uri2), + /(NS_NOINTERFACE)|(NS_ERROR_FILE_UNRECOGNIZED_PATH)/, + "uri2 is in an invalid state and should throw" + ); +}); + +add_task(async function test_bug1843717() { + // Make sure file path normalization on windows + // doesn't affect the hash of the URL. + let base = Services.io.newURI("file:///abc\\def/"); + let uri = Services.io.newURI("foo\\bar#x\\y", null, base); + Assert.equal(uri.spec, "file:///abc/def/foo/bar#x\\y"); + uri = Services.io.newURI("foo\\bar#xy", null, base); + Assert.equal(uri.spec, "file:///abc/def/foo/bar#xy"); + uri = Services.io.newURI("foo\\bar#", null, base); + Assert.equal(uri.spec, "file:///abc/def/foo/bar#"); +}); |