Bug 1495313 - Allow password with empty username in URLs r=dragana
authorValentin Gosu <valentin.gosu@gmail.com>
Wed, 14 Nov 2018 19:10:42 +0000
changeset 446409 ef6d81f0b2989624a9918ec5d790dc2745db20ef
parent 446408 06b12fd41ff14bbcba2d2bf268ad5da44496a02a
child 446410 db95e3b861f457a442352ab1e42c451cf3dccb66
push id35041
push useraiakab@mozilla.com
push dateThu, 15 Nov 2018 09:52:43 +0000
treeherdermozilla-central@48720735b142 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1495313
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1495313 - Allow password with empty username in URLs r=dragana Differential Revision: https://phabricator.services.mozilla.com/D11254
netwerk/base/nsStandardURL.cpp
netwerk/base/nsStandardURL.h
netwerk/base/nsURLParsers.cpp
netwerk/test/unit/test_standardurl.js
testing/web-platform/meta/cors/redirect-userinfo.htm.ini
testing/web-platform/meta/url/a-element-origin-xhtml.xhtml.ini
testing/web-platform/meta/url/a-element-origin.html.ini
testing/web-platform/meta/url/a-element-xhtml.xhtml.ini
testing/web-platform/meta/url/a-element.html.ini
testing/web-platform/meta/url/url-constructor.html.ini
testing/web-platform/meta/url/url-origin.html.ini
testing/web-platform/meta/url/url-setters.html.ini
testing/web-platform/meta/xhr/send-authentication-competing-names-passwords.htm.ini
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -709,22 +709,23 @@ nsStandardURL::BuildNormalizedSpec(const
         approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always
 
     // encode URL segments; convert UTF-8 to origin charset and possibly escape.
     // results written to encXXX variables only if |spec| is not already in the
     // appropriate encoding.
     {
         nsSegmentEncoder encoder;
         nsSegmentEncoder queryEncoder(encoding);
-        // Items using an extraLen of 1 don't add anything unless mLen > 0
         // Username@
-        approxLen += encoder.EncodeSegmentCount(spec, mUsername,  esc_Username,      encUsername,  useEncUsername, 1);
+        approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username, encUsername, useEncUsername, 0);
+        approxLen += 1; // reserve length for @
         // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec
-        if (mPassword.mLen >= 0)
+        if (mPassword.mLen > 0) {
             approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword);
+        }
         // mHost is handled differently below due to encoding differences
         MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
         if (mPort != -1 && mPort != mDefaultPort)
         {
             // :port
             portbuf.AppendInt(mPort);
             approxLen += portbuf.Length() + 1;
         }
@@ -826,29 +827,36 @@ nsStandardURL::BuildNormalizedSpec(const
         net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
         i = AppendToBuf(buf, i, "://", 3);
     }
 
     // record authority starting position
     mAuthority.mPos = i;
 
     // append authority
-    if (mUsername.mLen > 0) {
-        i = AppendSegmentToBuf(buf, i, spec, username, mUsername,
-                               &encUsername, useEncUsername, &diff);
-        ShiftFromPassword(diff);
+    if (mUsername.mLen > 0 || mPassword.mLen > 0) {
+        if (mUsername.mLen > 0) {
+            i = AppendSegmentToBuf(buf, i, spec, username, mUsername,
+                                   &encUsername, useEncUsername, &diff);
+            ShiftFromPassword(diff);
+        } else {
+            mUsername.mLen = -1;
+        }
         if (password.mLen > 0) {
             buf[i++] = ':';
             i = AppendSegmentToBuf(buf, i, spec, password, mPassword,
                                    &encPassword, useEncPassword, &diff);
             ShiftFromHost(diff);
         } else {
             mPassword.mLen = -1;
         }
         buf[i++] = '@';
+    } else {
+        mUsername.mLen = -1;
+        mPassword.mLen = -1;
     }
     if (host.mLen > 0) {
         i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost,
                                &diff);
         ShiftFromPath(diff);
 
         net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
         MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
@@ -958,16 +966,17 @@ nsStandardURL::BuildNormalizedSpec(const
         }
         CoalescePath(coalesceFlag, buf + mDirectory.mPos);
     }
     mSpec.Truncate(strlen(buf));
     NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
     MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
                "The spec should never be this long, we missed a check.");
 
+    MOZ_ASSERT(mUsername.mLen != 0 && mPassword.mLen != 0);
     return NS_OK;
 }
 
 bool
 nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase)
 {
     // one or both may be null
     if (!val || mSpec.IsEmpty())
@@ -1258,17 +1267,17 @@ nsStandardURL::GetSpec(nsACString &resul
 // result may contain unescaped UTF-8 characters
 NS_IMETHODIMP
 nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString &result)
 {
     nsresult rv = GetSpec(result);
     if (NS_FAILED(rv)) {
         return rv;
     }
-    if (mPassword.mLen >= 0) {
+    if (mPassword.mLen > 0) {
       result.ReplaceLiteral(mPassword.mPos, mPassword.mLen, "****");
     }
     return NS_OK;
 }
 
 // result may contain unescaped UTF-8 characters
 NS_IMETHODIMP
 nsStandardURL::GetSpecIgnoringRef(nsACString &result)
@@ -1664,46 +1673,30 @@ nsStandardURL::SetUserPass(const nsACStr
     }
 
     if (mSpec.Length() + input.Length() - Userpass(true).Length() > (uint32_t) net_GetURLMaxLength()) {
         return NS_ERROR_MALFORMED_URI;
     }
 
     InvalidateCache();
 
-    if (userpass.IsEmpty()) {
-        // remove user:pass
-        if (mUsername.mLen > 0) {
-            if (mPassword.mLen > 0)
-                mUsername.mLen += (mPassword.mLen + 1);
-            mUsername.mLen++;
-            mSpec.Cut(mUsername.mPos, mUsername.mLen);
-            mAuthority.mLen -= mUsername.mLen;
-            ShiftFromHost(-mUsername.mLen);
-            mUsername.mLen = -1;
-            mPassword.mLen = -1;
-        }
-
-        return NS_OK;
-    }
-
     NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
 
     nsresult rv;
     uint32_t usernamePos, passwordPos;
     int32_t usernameLen, passwordLen;
 
     rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(),
                                 &usernamePos, &usernameLen,
                                 &passwordPos, &passwordLen);
     if (NS_FAILED(rv)) return rv;
 
     // build new user:pass in |buf|
     nsAutoCString buf;
-    if (usernameLen > 0) {
+    if (usernameLen > 0 || passwordLen > 0) {
         nsSegmentEncoder encoder;
         bool ignoredOut;
         usernameLen = encoder.EncodeSegmentCount(userpass.get(),
                                                  URLSegment(usernamePos,
                                                             usernameLen),
                                                  esc_Username | esc_AlwaysCopy,
                                                  buf, ignoredOut);
         if (passwordLen > 0) {
@@ -1712,49 +1705,65 @@ nsStandardURL::SetUserPass(const nsACStr
                                                      URLSegment(passwordPos,
                                                                 passwordLen),
                                                      esc_Password |
                                                      esc_AlwaysCopy, buf,
                                                      ignoredOut);
         } else {
             passwordLen = -1;
         }
-        if (mUsername.mLen < 0)
+        if (mUsername.mLen < 0 && mPassword.mLen < 0) {
             buf.Append('@');
+        }
     }
 
-    uint32_t shift = 0;
-
-    if (mUsername.mLen < 0) {
+    int32_t shift = 0;
+
+    if (mUsername.mLen < 0 && mPassword.mLen < 0) {
         // no existing user:pass
         if (!buf.IsEmpty()) {
             mSpec.Insert(buf, mHost.mPos);
             mUsername.mPos = mHost.mPos;
             shift = buf.Length();
         }
     }
     else {
         // replace existing user:pass
-        uint32_t userpassLen = mUsername.mLen;
-        if (mPassword.mLen >= 0)
+        uint32_t userpassLen = 0;
+        if (mUsername.mLen > 0) {
+            userpassLen += mUsername.mLen;
+        }
+        if (mPassword.mLen > 0) {
             userpassLen += (mPassword.mLen + 1);
-        mSpec.Replace(mUsername.mPos, userpassLen, buf);
+        }
+        if (buf.IsEmpty()) {
+            // remove `@` character too
+            userpassLen++;
+        }
+        mSpec.Replace(mAuthority.mPos, userpassLen, buf);
         shift = buf.Length() - userpassLen;
     }
     if (shift) {
         ShiftFromHost(shift);
+        MOZ_DIAGNOSTIC_ASSERT(mAuthority.mLen >= -shift);
         mAuthority.mLen += shift;
     }
     // update positions and lengths
-    mUsername.mLen = usernameLen;
-    mPassword.mLen = passwordLen;
+    mUsername.mLen = usernameLen > 0 ? usernameLen : -1;
+    mUsername.mPos = mAuthority.mPos;
+    mPassword.mLen = passwordLen > 0 ? passwordLen : -1;
     if (passwordLen > 0) {
-        mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
+        if (mUsername.mLen > 0) {
+            mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
+        } else {
+            mPassword.mPos = mAuthority.mPos + 1;
+        }
     }
 
+    MOZ_ASSERT(mUsername.mLen != 0 && mPassword.mLen != 0);
     return NS_OK;
 }
 
 nsresult
 nsStandardURL::SetUsername(const nsACString &input)
 {
     const nsPromiseFlatCString &username = PromiseFlatCString(input);
 
@@ -1762,47 +1771,58 @@ nsStandardURL::SetUsername(const nsACStr
 
     if (mURLType == URLTYPE_NO_AUTHORITY) {
         if (username.IsEmpty())
             return NS_OK;
         NS_WARNING("cannot set username on no-auth url");
         return NS_ERROR_UNEXPECTED;
     }
 
-    if (username.IsEmpty())
-        return SetUserPass(username);
-
     if (mSpec.Length() + input.Length() - Username().Length() > (uint32_t) net_GetURLMaxLength()) {
         return NS_ERROR_MALFORMED_URI;
     }
 
     InvalidateCache();
 
     // escape username if necessary
     nsAutoCString buf;
     nsSegmentEncoder encoder;
     const nsACString &escUsername =
         encoder.EncodeSegment(username, esc_Username, buf);
 
-    int32_t shift;
-
-    if (mUsername.mLen < 0) {
+    int32_t shift = 0;
+
+    if (mUsername.mLen < 0 && escUsername.IsEmpty()) {
+        return NS_OK;
+    }
+
+    if (mUsername.mLen < 0 && mPassword.mLen < 0) {
+        MOZ_ASSERT(!escUsername.IsEmpty(), "Should not be empty at this point");
         mUsername.mPos = mAuthority.mPos;
         mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
         shift = escUsername.Length() + 1;
+        mUsername.mLen = escUsername.Length() > 0 ? escUsername.Length() : -1;
     }
-    else
-        shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
+    else {
+        uint32_t pos = mUsername.mLen < 0 ? mAuthority.mPos : mUsername.mPos;
+        int32_t len = mUsername.mLen < 0 ? 0 : mUsername.mLen;
+
+        if (mPassword.mLen < 0 && escUsername.IsEmpty()) {
+            len++; // remove the @ character too
+        }
+        shift = ReplaceSegment(pos, len, escUsername);
+        mUsername.mLen = escUsername.Length() > 0 ? escUsername.Length() : -1;
+    }
 
     if (shift) {
-        mUsername.mLen = escUsername.Length();
         mAuthority.mLen += shift;
         ShiftFromPassword(shift);
     }
 
+    MOZ_ASSERT(mUsername.mLen != 0 && mPassword.mLen != 0);
     return NS_OK;
 }
 
 nsresult
 nsStandardURL::SetPassword(const nsACString &input)
 {
     const nsPromiseFlatCString &password = PromiseFlatCString(input);
 
@@ -1818,63 +1838,69 @@ nsStandardURL::SetPassword(const nsACStr
     LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
 
     if (mURLType == URLTYPE_NO_AUTHORITY) {
         if (password.IsEmpty())
             return NS_OK;
         NS_WARNING("cannot set password on no-auth url");
         return NS_ERROR_UNEXPECTED;
     }
-    if (mUsername.mLen <= 0) {
-        if (password.IsEmpty()) {
-            MOZ_DIAGNOSTIC_ASSERT(Password().IsEmpty());
-            return NS_OK;
-        }
-        NS_WARNING("cannot set password without existing username");
-        return NS_ERROR_FAILURE;
-    }
 
     if (mSpec.Length() + input.Length() - Password().Length() > (uint32_t) net_GetURLMaxLength()) {
         return NS_ERROR_MALFORMED_URI;
     }
 
     InvalidateCache();
 
     if (password.IsEmpty()) {
-        if (mPassword.mLen >= 0) {
+        if (mPassword.mLen > 0) {
             // cut(":password")
-            mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
-            ShiftFromHost(-(mPassword.mLen + 1));
-            mAuthority.mLen -= (mPassword.mLen + 1);
+            int32_t len = mPassword.mLen;
+            if (mUsername.mLen < 0) {
+                len++; // also cut the @ character
+            }
+            len++; // for the : character
+            mSpec.Cut(mPassword.mPos - 1, len);
+            ShiftFromHost(-len);
+            mAuthority.mLen -= len;
             mPassword.mLen = -1;
         }
+        MOZ_ASSERT(mUsername.mLen != 0 && mPassword.mLen != 0);
         return NS_OK;
     }
 
     // escape password if necessary
     nsAutoCString buf;
     nsSegmentEncoder encoder;
     const nsACString &escPassword =
         encoder.EncodeSegment(password, esc_Password, buf);
 
     int32_t shift;
 
     if (mPassword.mLen < 0) {
-        mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
-        mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
-        shift = escPassword.Length() + 1;
+        if (mUsername.mLen > 0) {
+            mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
+            mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
+            shift = escPassword.Length() + 1;
+        } else {
+            mPassword.mPos = mAuthority.mPos + 1;
+            mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword + NS_LITERAL_CSTRING("@"), mPassword.mPos - 1);
+            shift = escPassword.Length() + 2;
+        }
     }
     else
         shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
 
     if (shift) {
         mPassword.mLen = escPassword.Length();
         mAuthority.mLen += shift;
         ShiftFromHost(shift);
     }
+
+    MOZ_ASSERT(mUsername.mLen != 0 && mPassword.mLen != 0);
     return NS_OK;
 }
 
 void
 nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart,
                              nsACString::const_iterator& aEnd)
 {
   for (int32_t i = 0; gHostLimitDigits[i]; ++i) {
--- a/netwerk/base/nsStandardURL.h
+++ b/netwerk/base/nsStandardURL.h
@@ -529,22 +529,28 @@ nsStandardURL::Prepath()
         len = mAuthority.mPos + mAuthority.mLen;
     return Substring(mSpec, 0, len);
 }
 
 inline const nsDependentCSubstring
 nsStandardURL::Userpass(bool includeDelim)
 {
     uint32_t pos=0, len=0;
-    // if there is no username, then there can be no password
-    if (mUsername.mLen > 0) {
-        pos = mUsername.mPos;
-        len = mUsername.mLen;
-        if (mPassword.mLen >= 0)
-            len += (mPassword.mLen + 1);
+    if (mUsername.mLen > 0 || mPassword.mLen > 0) {
+        if (mUsername.mLen > 0) {
+            pos = mUsername.mPos;
+            len = mUsername.mLen;
+            if (mPassword.mLen >= 0) {
+                len += (mPassword.mLen + 1);
+            }
+        } else {
+            pos = mPassword.mPos - 1;
+            len = mPassword.mLen + 1;
+        }
+
         if (includeDelim)
             len++;
     }
     return Substring(mSpec, pos, len);
 }
 
 inline const nsDependentCSubstring
 nsStandardURL::Hostport()
--- a/netwerk/base/nsURLParsers.cpp
+++ b/netwerk/base/nsURLParsers.cpp
@@ -530,20 +530,16 @@ nsAuthURLParser::ParseUserInfo(const cha
         SET_RESULT(username, 0, -1);
         SET_RESULT(password, 0, -1);
         return NS_OK;
     }
 
     const char *p = (const char *) memchr(userinfo, ':', userinfoLen);
     if (p) {
         // userinfo = <username:password>
-        if (p == userinfo) {
-            // must have a username!
-            return NS_ERROR_MALFORMED_URI;
-        }
         SET_RESULT(username, 0, p - userinfo);
         SET_RESULT(password, p - userinfo + 1, userinfoLen - (p - userinfo + 1));
     }
     else {
         // userinfo = <username>
         SET_RESULT(username, 0, userinfoLen);
         SET_RESULT(password, 0, -1);
     }
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -539,24 +539,100 @@ add_test(function test_emptyPassword() {
   Assert.equal(url.spec, "http://xxx:zzzz@example.com/");
   url = url.mutate().setUserPass("xxxxx:yyyyyy").finalize();
   Assert.equal(url.spec, "http://xxxxx:yyyyyy@example.com/");
   url = url.mutate().setUserPass("z:").finalize();
   Assert.equal(url.spec, "http://z@example.com/");
   url = url.mutate().setPassword("ppppppppppp").finalize();
   Assert.equal(url.spec, "http://z:ppppppppppp@example.com/");
 
-  url = url.mutate().setUsername("").finalize(); // Should clear password too
-  Assert.equal(url.spec, "http://example.com/");
+  url = stringToURL("http://example.com");
   url = url.mutate().setPassword("").finalize(); // Still empty. Should work.
   Assert.equal(url.spec, "http://example.com/");
 
   run_next_test();
 });
 
+add_test(function test_emptyUser() {
+  let url = stringToURL("http://:a@example.com/path/to/something?query#hash");
+  Assert.equal(url.spec, "http://:a@example.com/path/to/something?query#hash");
+  url = stringToURL("http://:@example.com/path/to/something?query#hash");
+  Assert.equal(url.spec, "http://example.com/path/to/something?query#hash");
+
+  const kurl = stringToURL("http://user:pass@example.com:8888/path/to/something?query#hash");
+  url = kurl.mutate().setUsername("").finalize();
+  Assert.equal(url.spec, "http://:pass@example.com:8888/path/to/something?query#hash");
+  Assert.equal(url.host, "example.com");
+  Assert.equal(url.hostPort, "example.com:8888");
+  Assert.equal(url.filePath, "/path/to/something");
+  Assert.equal(url.query, "query");
+  Assert.equal(url.ref, "hash");
+  url = kurl.mutate().setUserPass(":pass1").finalize();
+  Assert.equal(url.spec, "http://:pass1@example.com:8888/path/to/something?query#hash");
+  Assert.equal(url.host, "example.com");
+  Assert.equal(url.hostPort, "example.com:8888");
+  Assert.equal(url.filePath, "/path/to/something");
+  Assert.equal(url.query, "query");
+  Assert.equal(url.ref, "hash");
+  url = url.mutate().setUsername("user2").finalize();
+  Assert.equal(url.spec, "http://user2:pass1@example.com:8888/path/to/something?query#hash");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setUserPass(":pass234").finalize();
+  Assert.equal(url.spec, "http://:pass234@example.com:8888/path/to/something?query#hash");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setUserPass("").finalize();
+  Assert.equal(url.spec, "http://example.com:8888/path/to/something?query#hash");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setPassword("pa").finalize();
+  Assert.equal(url.spec, "http://:pa@example.com:8888/path/to/something?query#hash");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setUserPass("user:pass").finalize();
+  symmetricEquality(true, url.QueryInterface(Ci.nsIURL), kurl);
+
+  url = stringToURL("http://example.com:8888/path/to/something?query#hash");
+  url = url.mutate().setPassword("pass").finalize();
+  Assert.equal(url.spec, "http://:pass@example.com:8888/path/to/something?query#hash");
+  url = url.mutate().setUsername("").finalize();
+  Assert.equal(url.spec, "http://:pass@example.com:8888/path/to/something?query#hash");
+
+  url = stringToURL("http://example.com:8888");
+  url = url.mutate().setUsername("user").finalize();
+  url = url.mutate().setUsername("").finalize();
+  Assert.equal(url.spec, "http://example.com:8888/");
+
+  url = stringToURL("http://:pass@example.com");
+  Assert.equal(url.spec, "http://:pass@example.com/");
+  url = url.mutate().setPassword("").finalize();
+  Assert.equal(url.spec, "http://example.com/");
+  url = url.mutate().setUserPass("user:pass").finalize();
+  Assert.equal(url.spec, "http://user:pass@example.com/");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setUserPass("u:p").finalize();
+  Assert.equal(url.spec, "http://u:p@example.com/");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setUserPass("u1:p23").finalize();
+  Assert.equal(url.spec, "http://u1:p23@example.com/");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setUsername("u").finalize();
+  Assert.equal(url.spec, "http://u:p23@example.com/");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setPassword("p").finalize();
+  Assert.equal(url.spec, "http://u:p@example.com/");
+  Assert.equal(url.host, "example.com");
+
+  url = url.mutate().setUserPass("u2:p2").finalize();
+  Assert.equal(url.spec, "http://u2:p2@example.com/");
+  Assert.equal(url.host, "example.com");
+  url = url.mutate().setUserPass("u23:p23").finalize();
+  Assert.equal(url.spec, "http://u23:p23@example.com/");
+  Assert.equal(url.host, "example.com");
+
+  run_next_test();
+});
+
 registerCleanupFunction(function () {
   gPrefs.clearUserPref("network.standard-url.punycode-host");
 });
 
 add_test(function test_idna_host() {
   // See bug 945240 - this test makes sure that URLs return a punycode hostname
   // when the pref is set, or unicode otherwise.
 
deleted file mode 100644
--- a/testing/web-platform/meta/cors/redirect-userinfo.htm.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[redirect-userinfo.htm]
-  [Allow redirect without userinfo (:@ is trimmed during URL parsing)]
-    expected: FAIL
-
--- a/testing/web-platform/meta/url/a-element-origin-xhtml.xhtml.ini
+++ b/testing/web-platform/meta/url/a-element-origin-xhtml.xhtml.ini
@@ -1,15 +1,9 @@
 [a-element-origin-xhtml.xhtml]
-  [Parsing origin: <https://:@test> against <about:blank>]
-    expected: FAIL
-
-  [Parsing origin: <http://::@c@d:2> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing origin: <gopher:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing origin: <data:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing origin: <gopher:example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -36,25 +30,16 @@
     expected: FAIL
 
   [Parsing origin: <data:example.com/> against <about:blank>]
     expected: FAIL
 
   [Parsing origin: <http::b@www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing origin: <http:/:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing origin: <http://:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing origin: <http://:@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing origin: <i> against <sc:/pa/pa>]
     expected: FAIL
 
   [Parsing origin: <i> against <sc://ho/pa>]
     expected: FAIL
 
   [Parsing origin: <i> against <sc:///pa/pa>]
     expected: FAIL
--- a/testing/web-platform/meta/url/a-element-origin.html.ini
+++ b/testing/web-platform/meta/url/a-element-origin.html.ini
@@ -1,15 +1,9 @@
 [a-element-origin.html]
-  [Parsing origin: <https://:@test> against <about:blank>]
-    expected: FAIL
-
-  [Parsing origin: <http://::@c@d:2> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing origin: <gopher:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing origin: <data:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing origin: <gopher:example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -36,25 +30,16 @@
     expected: FAIL
 
   [Parsing origin: <data:example.com/> against <about:blank>]
     expected: FAIL
 
   [Parsing origin: <http::b@www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing origin: <http:/:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing origin: <http://:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing origin: <http://:@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing origin: <i> against <sc:/pa/pa>]
     expected: FAIL
 
   [Parsing origin: <i> against <sc://ho/pa>]
     expected: FAIL
 
   [Parsing origin: <i> against <sc:///pa/pa>]
     expected: FAIL
--- a/testing/web-platform/meta/url/a-element-xhtml.xhtml.ini
+++ b/testing/web-platform/meta/url/a-element-xhtml.xhtml.ini
@@ -12,19 +12,16 @@
     expected: FAIL
 
   [Parsing: <http://f: 21 / b ? d # e > against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http::@c:29> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <http://::@c@d:2> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing: <http://foo.com:b@d/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <foo:/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <foo:/bar.com/> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -171,22 +168,16 @@
     expected: FAIL
 
   [Parsing: <mailto:example.com/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http::b@www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http:/:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http://:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <http:/:@/www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://user@/www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http:@/www.example.com> against <about:blank>]
     expected: FAIL
@@ -219,19 +210,16 @@
     expected: FAIL
 
   [Parsing: <http:/@:www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://@:www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http://:@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <file:..> against <http://www.example.com/test>]
     expected: FAIL
 
   [Parsing: <http://example example.com> against <http://other.com/>]
     expected: FAIL
 
   [Parsing: <http://Goo%20 goo%7C|.com> against <http://other.com/>]
     expected: FAIL
@@ -399,19 +387,16 @@
     expected: FAIL
 
   [Parsing: <http:> against <https://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <tel:1234567890> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <https://:@test> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <non-special://test:@test/x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <non-special://:@test/x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://10000000000> against <http://other.com/>]
     expected: FAIL
--- a/testing/web-platform/meta/url/a-element.html.ini
+++ b/testing/web-platform/meta/url/a-element.html.ini
@@ -12,19 +12,16 @@
     expected: FAIL
 
   [Parsing: <http://f: 21 / b ? d # e > against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http::@c:29> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <http://::@c@d:2> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing: <http://foo.com:b@d/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <foo:/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <foo:/bar.com/> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -171,22 +168,16 @@
     expected: FAIL
 
   [Parsing: <mailto:example.com/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http::b@www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http:/:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http://:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <http:/:@/www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://user@/www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http:@/www.example.com> against <about:blank>]
     expected: FAIL
@@ -219,19 +210,16 @@
     expected: FAIL
 
   [Parsing: <http:/@:www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://@:www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http://:@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <http://example example.com> against <http://other.com/>]
     expected: FAIL
 
   [Parsing: <http://Goo%20 goo%7C|.com> against <http://other.com/>]
     expected: FAIL
 
   [Parsing: <http://GOO  goo.com> against <http://other.com/>]
     expected: FAIL
@@ -408,19 +396,16 @@
     expected: FAIL
 
   [Parsing: <http:> against <https://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <tel:1234567890> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <https://:@test> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <non-special://test:@test/x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <non-special://:@test/x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://10000000000> against <http://other.com/>]
     expected: FAIL
--- a/testing/web-platform/meta/url/url-constructor.html.ini
+++ b/testing/web-platform/meta/url/url-constructor.html.ini
@@ -1,18 +1,15 @@
 [url-constructor.html]
   [Parsing: <foo://> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http::@c:29> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <http://::@c@d:2> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing: <http://foo.com:b@d/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <foo://///////> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <foo://///////bar.com/> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -78,28 +75,19 @@
     expected: FAIL
 
   [Parsing: <data:example.com/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http::b@www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http:/:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http://:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <http://www.@pple.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http://:@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <http://﷐zyx.com> against <http://other.com/>]
     expected: FAIL
 
   [Parsing: <http://%ef%b7%90zyx.com> against <http://other.com/>]
     expected: FAIL
 
   [Parsing: <http://%41.com> against <http://other.com/>]
     expected: FAIL
@@ -183,19 +171,16 @@
     expected: FAIL
 
   [Parsing: <sc://ñ.test/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <file:..> against <http://www.example.com/test>]
     expected: FAIL
 
-  [Parsing: <https://:@test> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <non-special://test:@test/x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <non-special://:@test/x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://10000000000> against <http://other.com/>]
     expected: FAIL
--- a/testing/web-platform/meta/url/url-origin.html.ini
+++ b/testing/web-platform/meta/url/url-origin.html.ini
@@ -1,15 +1,9 @@
 [url-origin.html]
-  [Origin parsing: <https://:@test> against <about:blank>]
-    expected: FAIL
-
-  [Origin parsing: <http://::@c@d:2> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Origin parsing: <gopher:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Origin parsing: <data:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Origin parsing: <gopher:example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -36,25 +30,16 @@
     expected: FAIL
 
   [Origin parsing: <data:example.com/> against <about:blank>]
     expected: FAIL
 
   [Origin parsing: <http::b@www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Origin parsing: <http:/:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Origin parsing: <http://:b@www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Origin parsing: <http://:@www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Origin parsing: <i> against <sc:/pa/pa>]
     expected: FAIL
 
   [Origin parsing: <i> against <sc://ho/pa>]
     expected: FAIL
 
   [Origin parsing: <i> against <sc:///pa/pa>]
     expected: FAIL
--- a/testing/web-platform/meta/url/url-setters.html.ini
+++ b/testing/web-platform/meta/url/url-setters.html.ini
@@ -30,37 +30,22 @@
     expected: FAIL
 
   [Setting <mailto:me@example.net>.protocol = 'http' Cannot-be-a-base URL doesn’t have a host, but URL in a special scheme must.]
     expected: FAIL
 
   [Setting <ssh://me@example.net>.protocol = 'http' Can’t switch from non-special scheme to special. Note: this may change, see https://github.com/whatwg/url/issues/104]
     expected: FAIL
 
-  [Setting <http://:secret@example.net>.username = 'me']
-    expected: FAIL
-
-  [Setting <http://me:secret@example.net>.username = '']
-    expected: FAIL
-
   [Setting <http://example.net>.username = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
-  [Setting <http://example.net>.password = 'secret']
-    expected: FAIL
-
-  [Setting <http://:secret@example.net>.password = '']
-    expected: FAIL
-
   [Setting <http://example.net>.password = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
-  [Setting <http://example.net>.password = '%c3%89té' Bytes already percent-encoded are left as-is.]
-    expected: FAIL
-
   [Setting <view-source+http://example.net/foo>.host = '' The empty host is OK for non-special schemes]
     expected: FAIL
 
   [Setting <a:/foo>.host = 'example.net' Path-only URLs can gain a host]
     expected: FAIL
 
   [Setting <http://example.net>.host = '[::0:01\]:2' IPv6 address syntax is normalized]
     expected: FAIL
@@ -141,79 +126,34 @@
     expected: FAIL
 
   [<a>: Setting <ssh://me@example.net>.protocol = 'http' Can’t switch from non-special scheme to special. Note: this may change, see https://github.com/whatwg/url/issues/104]
     expected: FAIL
 
   [<area>: Setting <ssh://me@example.net>.protocol = 'http' Can’t switch from non-special scheme to special. Note: this may change, see https://github.com/whatwg/url/issues/104]
     expected: FAIL
 
-  [URL: Setting <http://:secret@example.net>.username = 'me']
-    expected: FAIL
-
-  [<a>: Setting <http://:secret@example.net>.username = 'me']
-    expected: FAIL
-
-  [<area>: Setting <http://:secret@example.net>.username = 'me']
-    expected: FAIL
-
-  [URL: Setting <http://me:secret@example.net>.username = '']
-    expected: FAIL
-
-  [<a>: Setting <http://me:secret@example.net>.username = '']
-    expected: FAIL
-
-  [<area>: Setting <http://me:secret@example.net>.username = '']
-    expected: FAIL
-
   [URL: Setting <http://example.net>.username = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
   [<a>: Setting <http://example.net>.username = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
   [<area>: Setting <http://example.net>.username = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
-  [URL: Setting <http://example.net>.password = 'secret']
-    expected: FAIL
-
-  [<a>: Setting <http://example.net>.password = 'secret']
-    expected: FAIL
-
-  [<area>: Setting <http://example.net>.password = 'secret']
-    expected: FAIL
-
-  [URL: Setting <http://:secret@example.net>.password = '']
-    expected: FAIL
-
-  [<a>: Setting <http://:secret@example.net>.password = '']
-    expected: FAIL
-
-  [<area>: Setting <http://:secret@example.net>.password = '']
-    expected: FAIL
-
   [URL: Setting <http://example.net>.password = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
   [<a>: Setting <http://example.net>.password = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
   [<area>: Setting <http://example.net>.password = '\x00\x01\t\n\r\x1f !"#$%&'()*+,-./09:;<=>?@AZ[\\\]^_`az{|}~€Éé' UTF-8 percent encoding with the userinfo encode set.]
     expected: FAIL
 
-  [URL: Setting <http://example.net>.password = '%c3%89té' Bytes already percent-encoded are left as-is.]
-    expected: FAIL
-
-  [<a>: Setting <http://example.net>.password = '%c3%89té' Bytes already percent-encoded are left as-is.]
-    expected: FAIL
-
-  [<area>: Setting <http://example.net>.password = '%c3%89té' Bytes already percent-encoded are left as-is.]
-    expected: FAIL
-
   [URL: Setting <view-source+http://example.net/foo>.host = '' The empty host is OK for non-special schemes]
     expected: FAIL
 
   [<a>: Setting <view-source+http://example.net/foo>.host = '' The empty host is OK for non-special schemes]
     expected: FAIL
 
   [<area>: Setting <view-source+http://example.net/foo>.host = '' The empty host is OK for non-special schemes]
     expected: FAIL
--- a/testing/web-platform/meta/xhr/send-authentication-competing-names-passwords.htm.ini
+++ b/testing/web-platform/meta/xhr/send-authentication-competing-names-passwords.htm.ini
@@ -1,13 +1,6 @@
 [send-authentication-competing-names-passwords.htm]
   [XMLHttpRequest: send() - "Basic" authenticated requests with competing user name/password options user name in URL userinfo, password in open() call: user name wins and password is thrown away]
     expected: FAIL
 
   [XMLHttpRequest: send() - "Basic" authenticated requests with competing user name/password options user name and password in URL userinfo, only user name in open() call: user name in open() wins]
     expected: FAIL
-
-  [XMLHttpRequest user/pass options: pass in URL, user in open()]
-    expected: FAIL
-
-  [XMLHttpRequest user/pass options: pass in URL, user/pass in open()]
-    expected: FAIL
-