gecko-dev/netwerk/test/unit/test_URIs.js

1084 lines
32 KiB
JavaScript

/* -*- 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 () {
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_space_with_query_and_ref() {
let url = Services.io.newURI("data:space");
Assert.equal(url.spec, "data:space");
url = Services.io.newURI("data:space ?");
Assert.equal(url.spec, "data:space ?");
url = Services.io.newURI("data:space #");
Assert.equal(url.spec, "data:space #");
url = Services.io.newURI("data:space?");
Assert.equal(url.spec, "data:space?");
url = Services.io.newURI("data:space#");
Assert.equal(url.spec, "data:space#");
url = Services.io.newURI("data:space ?query");
Assert.equal(url.spec, "data:space ?query");
url = url.mutate().setQuery("").finalize();
Assert.equal(url.spec, "data:space");
url = Services.io.newURI("data:space #ref");
Assert.equal(url.spec, "data:space #ref");
url = url.mutate().setRef("").finalize();
Assert.equal(url.spec, "data:space");
url = Services.io.newURI("data:space?query#ref");
Assert.equal(url.spec, "data:space?query#ref");
url = url.mutate().setRef("").finalize();
Assert.equal(url.spec, "data:space?query");
url = url.mutate().setQuery("").finalize();
Assert.equal(url.spec, "data:space");
url = Services.io.newURI("data:space#ref?query");
Assert.equal(url.spec, "data:space#ref?query");
url = url.mutate().setQuery("").finalize();
Assert.equal(url.spec, "data:space#ref?query");
url = url.mutate().setRef("").finalize();
Assert.equal(url.spec, "data:space");
url = Services.io.newURI("data:space ?query#ref");
Assert.equal(url.spec, "data:space ?query#ref");
url = url.mutate().setRef("").finalize();
Assert.equal(url.spec, "data:space ?query");
url = url.mutate().setQuery("").finalize();
Assert.equal(url.spec, "data:space");
url = Services.io.newURI("data:space #ref?query");
Assert.equal(url.spec, "data:space #ref?query");
url = url.mutate().setQuery("").finalize();
Assert.equal(url.spec, "data:space #ref?query");
url = url.mutate().setRef("").finalize();
Assert.equal(url.spec, "data:space");
});
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#");
});
add_task(async function test_bug1874118() {
let base = Services.io.newURI("file:///tmp/mock/path");
let uri = Services.io.newURI("file:c:\\\\foo\\\\bar.html", null, base);
Assert.equal(uri.spec, "file:///c://foo//bar.html");
base = Services.io.newURI("file:///tmp/mock/path");
uri = Services.io.newURI("file:c|\\\\foo\\\\bar.html", null, base);
Assert.equal(uri.spec, "file:///c://foo//bar.html");
base = Services.io.newURI("file:///C:/");
uri = Services.io.newURI("..", null, base);
Assert.equal(uri.spec, "file:///C:/");
base = Services.io.newURI("file:///");
uri = Services.io.newURI("C|/hello/../../", null, base);
Assert.equal(uri.spec, "file:///C:/");
base = Services.io.newURI("file:///");
uri = Services.io.newURI("/C:/../../", null, base);
Assert.equal(uri.spec, "file:///C:/");
base = Services.io.newURI("file:///");
uri = Services.io.newURI("/C://../../", null, base);
Assert.equal(uri.spec, "file:///C:/");
base = Services.io.newURI("file:///tmp/mock/path");
uri = Services.io.newURI("C|/foo/bar", null, base);
Assert.equal(uri.spec, "file:///C:/foo/bar");
base = Services.io.newURI("file:///tmp/mock/path");
uri = Services.io.newURI("/C|/foo/bar", null, base);
Assert.equal(uri.spec, "file:///C:/foo/bar");
});