Bug 945240 - Make nsIURI.host & variants return ASCII strings r=mcmanus draft
authorValentin Gosu <valentin.gosu@gmail.com>
Tue, 11 Jul 2017 19:09:10 +0200
changeset 607138 d8ae8bf774eb22b549370ca96565bafc930faf51
parent 605863 a418121d46250f91728b86d9eea331029c264c30
child 607139 2461e4a1b33bd1a24e75f174b55db30ce033cfc7
push id67902
push uservalentin.gosu@gmail.com
push dateTue, 11 Jul 2017 21:59:36 +0000
reviewersmcmanus
bugs945240
milestone56.0a1
Bug 945240 - Make nsIURI.host & variants return ASCII strings r=mcmanus * nsStandardURL::GetHost/GetHostPort/GetSpec contain an punycode encoded hostname. * Added nsIURI::GetDisplayHost/GetDisplayHostPort/GetDisplaySpec which have unicode hostnames, depending on the hostname, character blacklist and the network.IDN_show_punycode pref * remove mHostEncoding since it's not needed anymore (the hostname is always ASCII encoded) * Add mCheckedIfHostA to know when GetDisplayHost can return the regular host, or when we need to use the cached mDisplayHost MozReview-Commit-ID: 4qV9Ynhr2Jl * * * Bug 945240 - Make sure nsIURI.specIgnoringRef/.getSensitiveInfoHiddenSpec/.prePath contain unicode hosts when network.standard-url.punycode-host is set to false r=mcmanus MozReview-Commit-ID: F6bZuHOWEsj
caps/NullPrincipalURI.cpp
image/decoders/icon/nsIconURI.cpp
ipc/glue/URIParams.ipdlh
modules/libjar/nsJARURI.cpp
netwerk/base/nsIURI.idl
netwerk/base/nsSimpleURI.cpp
netwerk/base/nsStandardURL.cpp
netwerk/base/nsStandardURL.h
--- a/caps/NullPrincipalURI.cpp
+++ b/caps/NullPrincipalURI.cpp
@@ -344,16 +344,34 @@ NullPrincipalURI::Resolve(const nsACStri
 
 NS_IMETHODIMP
 NullPrincipalURI::SchemeIs(const char* aScheme, bool* _schemeIs)
 {
   *_schemeIs = (0 == nsCRT::strcasecmp(NS_NULLPRINCIPAL_SCHEME, aScheme));
   return NS_OK;
 }
 
+NS_IMETHODIMP
+NullPrincipalURI::GetDisplaySpec(nsACString &aUnicodeSpec)
+{
+  return GetSpec(aUnicodeSpec);
+}
+
+NS_IMETHODIMP
+NullPrincipalURI::GetDisplayHostPort(nsACString &aUnicodeHostPort)
+{
+  return GetHostPort(aUnicodeHostPort);
+}
+
+NS_IMETHODIMP
+NullPrincipalURI::GetDisplayHost(nsACString &aUnicodeHost)
+{
+  return GetHost(aUnicodeHost);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIIPCSerializableURI
 
 void
 NullPrincipalURI::Serialize(mozilla::ipc::URIParams& aParams)
 {
   aParams = mozilla::ipc::NullPrincipalURIParams();
 }
--- a/image/decoders/icon/nsIconURI.cpp
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -113,16 +113,34 @@ nsMozIconURI::GetSpec(nsACString& aSpec)
 
 NS_IMETHODIMP
 nsMozIconURI::GetSpecIgnoringRef(nsACString& result)
 {
   return GetSpec(result);
 }
 
 NS_IMETHODIMP
+nsMozIconURI::GetDisplaySpec(nsACString& aUnicodeSpec)
+{
+  return GetSpec(aUnicodeSpec);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetDisplayHostPort(nsACString& aUnicodeHostPort)
+{
+  return GetHostPort(aUnicodeHostPort);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetDisplayHost(nsACString& aUnicodeHost)
+{
+  return GetHost(aUnicodeHost);
+}
+
+NS_IMETHODIMP
 nsMozIconURI::GetHasRef(bool* result)
 {
   *result = false;
   return NS_OK;
 }
 
 // takes a string like ?size=32&contentType=text/html and returns a new string
 // containing just the attribute value. i.e you could pass in this string with
--- a/ipc/glue/URIParams.ipdlh
+++ b/ipc/glue/URIParams.ipdlh
@@ -41,17 +41,16 @@ struct StandardURLParams
   StandardURLSegment directory;
   StandardURLSegment baseName;
   StandardURLSegment extension;
   StandardURLSegment query;
   StandardURLSegment ref;
   nsCString originCharset;
   bool isMutable;
   bool supportsFileURL;
-  uint32_t hostEncoding;
 };
 
 struct JARURIParams
 {
   URIParams jarFile;
   URIParams jarEntry;
   nsCString charset;
 };
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -224,16 +224,34 @@ NS_IMETHODIMP
 nsJARURI::GetSpecIgnoringRef(nsACString &aSpec)
 {
     nsAutoCString entrySpec;
     mJAREntry->GetSpecIgnoringRef(entrySpec);
     return FormatSpec(entrySpec, aSpec);
 }
 
 NS_IMETHODIMP
+nsJARURI::GetDisplaySpec(nsACString &aUnicodeSpec)
+{
+    return GetSpec(aUnicodeSpec);
+}
+
+NS_IMETHODIMP
+nsJARURI::GetDisplayHostPort(nsACString &aUnicodeHostPort)
+{
+    return GetHostPort(aUnicodeHostPort);
+}
+
+NS_IMETHODIMP
+nsJARURI::GetDisplayHost(nsACString &aUnicodeHost)
+{
+    return GetHost(aUnicodeHost);
+}
+
+NS_IMETHODIMP
 nsJARURI::GetHasRef(bool *result)
 {
     return mJAREntry->GetHasRef(result);
 }
 
 NS_IMETHODIMP
 nsJARURI::SetSpec(const nsACString& aSpec)
 {
--- a/netwerk/base/nsIURI.idl
+++ b/netwerk/base/nsIURI.idl
@@ -134,33 +134,29 @@ interface nsIURI : nsISupports
     attribute AUTF8String username;
     attribute AUTF8String password;
 
     /**
      * The host:port (or simply the host, if port == -1).
      *
      * If this attribute is set to a value that only has a host part, the port
      * will not be reset. To reset the port as well use setHostAndPort.
-     *
-     * Characters are NOT escaped.
      */
     attribute AUTF8String hostPort;
 
     /**
      * This function will always set a host and a port. If the port part is
      * empty, the value of the port will be set to the default value.
      */
     void setHostAndPort(in AUTF8String hostport);
 
     /**
      * The host is the internet domain name to which this URI refers.  It could
-     * be an IPv4 (or IPv6) address literal.  If supported, it could be a
-     * non-ASCII internationalized domain name.
-     *
-     * Characters are NOT escaped.
+     * be an IPv4 (or IPv6) address literal. Otherwise it is an ASCII or punycode
+     * encoded string.
      */
     attribute AUTF8String host;
 
     /**
      * A port value of -1 corresponds to the protocol's default port (eg. -1
      * implies port 80 for http URIs).
      */
     attribute long port;
@@ -302,9 +298,29 @@ interface nsIURI : nsISupports
 
     /**
      * Returns the query portion (the part after the "?") of the URL.
      * If there isn't one, an empty string is returned.
      *
      * Some characters may be escaped.
      */
     attribute AUTF8String query;
+
+    /**
+     * If the URI has a punycode encoded hostname, this will hold the UTF8
+     * representation of that hostname (if that representation doesn't contain
+     * blacklisted characters, and the network.IDN_show_punycode pref is false)
+     * Otherwise, if the hostname is ASCII, it will return the same as .asciiHost
+     */
+    readonly attribute AUTF8String displayHost;
+
+    /**
+     * The displayHost:port (or simply the displayHost, if port == -1).
+     */
+    readonly attribute AUTF8String displayHostPort;
+
+    /**
+     * Returns the same as calling .spec, only with a UTF8 encoded hostname
+     * (if that hostname doesn't contain blacklisted characters, and
+     * the network.IDN_show_punycode pref is false)
+     */
+    readonly attribute AUTF8String displaySpec;
 };
--- a/netwerk/base/nsSimpleURI.cpp
+++ b/netwerk/base/nsSimpleURI.cpp
@@ -238,16 +238,34 @@ nsSimpleURI::GetSpecIgnoringRef(nsACStri
     result = mScheme + NS_LITERAL_CSTRING(":") + mPath;
     if (mIsQueryValid) {
         result += NS_LITERAL_CSTRING("?") + mQuery;
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSimpleURI::GetDisplaySpec(nsACString &aUnicodeSpec)
+{
+    return GetSpec(aUnicodeSpec);
+}
+
+NS_IMETHODIMP
+nsSimpleURI::GetDisplayHostPort(nsACString &aUnicodeHostPort)
+{
+    return GetHostPort(aUnicodeHostPort);
+}
+
+NS_IMETHODIMP
+nsSimpleURI::GetDisplayHost(nsACString &aUnicodeHost)
+{
+    return GetHost(aUnicodeHost);
+}
+
+NS_IMETHODIMP
 nsSimpleURI::GetHasRef(bool *result)
 {
     *result = mIsRefValid;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::SetSpec(const nsACString &aSpec)
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -287,22 +287,22 @@ nsSegmentEncoder::EncodeSegment(const ns
 
 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
 static LinkedList<nsStandardURL> gAllURLs;
 #endif
 
 nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL)
     : mDefaultPort(-1)
     , mPort(-1)
-    , mHostA(nullptr)
-    , mHostEncoding(eEncoding_ASCII)
+    , mDisplayHost(nullptr)
     , mSpecEncoding(eEncoding_Unknown)
     , mURLType(URLTYPE_STANDARD)
     , mMutable(true)
     , mSupportsFileURL(aSupportsFileURL)
+    , mCheckedIfHostA(false)
 {
     LOG(("Creating nsStandardURL @%p\n", this));
 
     if (!gInitialized) {
         gInitialized = true;
         InitGlobalObjects();
     }
 
@@ -322,20 +322,16 @@ nsStandardURL::nsStandardURL(bool aSuppo
         mRustURL = new RustURL();
     }
 #endif
 }
 
 nsStandardURL::~nsStandardURL()
 {
     LOG(("Destroying nsStandardURL @%p\n", this));
-
-    if (mHostA) {
-        free(mHostA);
-    }
 }
 
 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
 struct DumpLeakedURLs {
     DumpLeakedURLs() {}
     ~DumpLeakedURLs();
 };
 
@@ -391,39 +387,37 @@ nsStandardURL::Clear()
 
     mPort = -1;
 
     mScheme.Reset();
     mAuthority.Reset();
     mUsername.Reset();
     mPassword.Reset();
     mHost.Reset();
-    mHostEncoding = eEncoding_ASCII;
 
     mPath.Reset();
     mFilepath.Reset();
     mDirectory.Reset();
     mBasename.Reset();
 
     mExtension.Reset();
     mQuery.Reset();
     mRef.Reset();
 
     InvalidateCache();
 }
 
 void
 nsStandardURL::InvalidateCache(bool invalidateCachedFile)
 {
-    if (invalidateCachedFile)
+    if (invalidateCachedFile) {
         mFile = nullptr;
-    if (mHostA) {
-        free(mHostA);
-        mHostA = nullptr;
     }
+    mDisplayHost.Truncate();
+    mCheckedIfHostA = false;
     mSpecEncoding = eEncoding_Unknown;
 }
 
 // Return the number of "dots" in the string, or -1 if invalid.  Note that the
 // number of relevant entries in the bases/starts/ends arrays is number of
 // dots + 1.
 // Since the trailing dot is allowed, we pass and adjust "length".
 //
@@ -635,42 +629,54 @@ nsStandardURL::NormalizeIPv4(const nsACS
 nsresult
 nsStandardURL::NormalizeIDN(const nsACString& host, nsCString& result)
 {
     // If host is ACE, then convert to UTF-8.  Else, if host is already UTF-8,
     // then make sure it is normalized per IDN.
 
     // this function returns true if normalization succeeds.
 
-    // NOTE: As a side-effect this function sets mHostEncoding.  While it would
-    // be nice to avoid side-effects in this function, the implementation of
-    // this function is already somewhat bound to the behavior of the
-    // callsites.  Anyways, this function exists to avoid code duplication, so
-    // side-effects abound :-/
-
-    NS_ASSERTION(mHostEncoding == eEncoding_ASCII, "unexpected default encoding");
-
-    bool isASCII;
     if (!gIDN) {
         nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
         if (serv) {
             NS_ADDREF(gIDN = serv.get());
         }
     }
 
     result.Truncate();
-    nsresult rv = NS_ERROR_UNEXPECTED;
-    if (gIDN) {
-        rv = gIDN->ConvertToDisplayIDN(host, &isASCII, result);
-        if (NS_SUCCEEDED(rv) && !isASCII) {
-          mHostEncoding = eEncoding_UTF8;
-        }
+    nsresult rv;
+
+    if (!gIDN) {
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    bool isAscii;
+    nsAutoCString normalized;
+    rv = gIDN->ConvertToDisplayIDN(host, &isAscii, normalized);
+    if (NS_FAILED(rv)) {
+        return rv;
     }
 
-    return rv;
+    // The result is ASCII. No need to convert to ACE.
+    if (isAscii) {
+        result = normalized;
+        mCheckedIfHostA = true;
+        mDisplayHost.Truncate();
+        return NS_OK;
+    }
+
+    rv = gIDN->ConvertUTF8toACE(normalized, result);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
+    mCheckedIfHostA = true;
+    mDisplayHost = normalized;
+
+    return NS_OK;
 }
 
 bool
 nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length)
 {
     if (!host || !*host) {
         // Should not be NULL or empty string
         return false;
@@ -820,17 +826,16 @@ nsStandardURL::BuildNormalizedSpec(const
             approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref,
                                                         encRef, useEncRef);
         }
     }
 
     // do not escape the hostname, if IPv6 address literal, mHost will
     // already point to a [ ] delimited IPv6 address literal.
     // However, perform Unicode normalization on it, as IDN does.
-    mHostEncoding = eEncoding_ASCII;
     // Note that we don't disallow URLs without a host - file:, etc
     if (mHost.mLen > 0) {
         nsAutoCString tempHost;
         NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host, tempHost);
         if (tempHost.Contains('\0'))
             return NS_ERROR_MALFORMED_URI;  // null embedded in hostname
         if (tempHost.Contains(' '))
             return NS_ERROR_MALFORMED_URI;  // don't allow spaces in the hostname
@@ -1340,45 +1345,110 @@ nsStandardURL::GetSpec(nsACString &resul
     CALL_RUST_GETTER_STR(result, GetSpec, result);
     return NS_OK;
 }
 
 // result may contain unescaped UTF-8 characters
 NS_IMETHODIMP
 nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString &result)
 {
-    result = mSpec;
+    nsresult rv = GetSpec(result);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
     if (mPassword.mLen >= 0) {
       result.Replace(mPassword.mPos, mPassword.mLen, "****");
     }
     CALL_RUST_GETTER_STR(result, GetSensitiveInfoHiddenSpec, result);
     return NS_OK;
 }
 
 // result may contain unescaped UTF-8 characters
 NS_IMETHODIMP
 nsStandardURL::GetSpecIgnoringRef(nsACString &result)
 {
     // URI without ref is 0 to one char before ref
-    if (mRef.mLen >= 0) {
-        URLSegment noRef(0, mRef.mPos - 1);
-
-        result = Segment(noRef);
-    } else {
-        result = mSpec;
+    if (mRef.mLen < 0) {
+        return GetSpec(result);
     }
+
+    URLSegment noRef(0, mRef.mPos - 1);
+    result = Segment(noRef);
+
+    if (!gPunycodeHost && mCheckedIfHostA && !mDisplayHost.IsEmpty()) {
+        result.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
+    }
+
     CALL_RUST_GETTER_STR(result, GetSpecIgnoringRef, result);
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsStandardURL::GetDisplaySpec(nsACString &aUnicodeSpec)
+{
+    aUnicodeSpec.Assign(mSpec);
+    if (mCheckedIfHostA && !mDisplayHost.IsEmpty()) {
+        aUnicodeSpec.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStandardURL::GetDisplayHostPort(nsACString &aUnicodeHostPort)
+{
+    nsresult rv = GetDisplayHost(aUnicodeHostPort);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+    uint32_t pos = mHost.mPos + mHost.mLen;
+    if (pos < mPath.mPos)
+        aUnicodeHostPort += Substring(mSpec, pos, mPath.mPos - pos);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStandardURL::GetDisplayHost(nsACString &aUnicodeHost)
+{
+    if (mCheckedIfHostA) {
+        if (mDisplayHost.IsEmpty()) {
+            return GetAsciiHost(aUnicodeHost);
+        } else {
+            aUnicodeHost = mDisplayHost;
+            return NS_OK;
+        }
+    }
+
+    if (!gIDN) {
+        return NS_ERROR_NOT_INITIALIZED;
+    }
+
+    nsresult rv = gIDN->ConvertACEtoUTF8(Host(), aUnicodeHost);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
+    mCheckedIfHostA = true;
+    if (aUnicodeHost != Host()) {
+        mDisplayHost = aUnicodeHost;
+    }
+
+    return NS_OK;
+}
+
+
 // result may contain unescaped UTF-8 characters
 NS_IMETHODIMP
 nsStandardURL::GetPrePath(nsACString &result)
 {
     result = Prepath();
+    if (!gPunycodeHost && mCheckedIfHostA && !mDisplayHost.IsEmpty()) {
+        result.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
+    }
     CALL_RUST_GETTER_STR(result, GetPrePath, result);
     return NS_OK;
 }
 
 // result is strictly US-ASCII
 NS_IMETHODIMP
 nsStandardURL::GetScheme(nsACString &result)
 {
@@ -1412,27 +1482,27 @@ nsStandardURL::GetPassword(nsACString &r
     result = Password();
     CALL_RUST_GETTER_STR(result, GetPassword, result);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStandardURL::GetHostPort(nsACString &result)
 {
-    result = Hostport();
+    nsresult rv = GetAsciiHostPort(result);
     CALL_RUST_GETTER_STR(result, GetHostPort, result);
-    return NS_OK;
+    return rv;
 }
 
 NS_IMETHODIMP
 nsStandardURL::GetHost(nsACString &result)
 {
-    result = Host();
+    nsresult rv = GetAsciiHost(result);
     CALL_RUST_GETTER_STR(result, GetHost, result);
-    return NS_OK;
+    return rv;
 }
 
 NS_IMETHODIMP
 nsStandardURL::GetPort(int32_t *result)
 {
     // should never be more than 16 bit
     MOZ_ASSERT(mPort <= std::numeric_limits<uint16_t>::max());
     *result = mPort;
@@ -1486,67 +1556,26 @@ nsStandardURL::GetAsciiSpec(nsACString &
     CALL_RUST_GETTER_STR(result, GetAsciiSpec, result);
     return NS_OK;
 }
 
 // result is ASCII
 NS_IMETHODIMP
 nsStandardURL::GetAsciiHostPort(nsACString &result)
 {
-    if (mHostEncoding == eEncoding_ASCII) {
-        result = Hostport();
-        CALL_RUST_GETTER_STR(result, GetAsciiHostPort, result);
-        return NS_OK;
-    }
-
-    MOZ_ALWAYS_SUCCEEDS(GetAsciiHost(result));
-
-    // As our mHostEncoding is not eEncoding_ASCII, we know that
-    // the our host is not ipv6, and we can avoid looking at it.
-    MOZ_ASSERT(result.FindChar(':') == -1, "The host must not be ipv6");
-
-    // hostport = "hostA" + ":port"
-    uint32_t pos = mHost.mPos + mHost.mLen;
-    if (pos < mPath.mPos)
-        result += Substring(mSpec, pos, mPath.mPos - pos);
-
+    result = Hostport();
     CALL_RUST_GETTER_STR(result, GetAsciiHostPort, result);
     return NS_OK;
 }
 
 // result is ASCII
 NS_IMETHODIMP
 nsStandardURL::GetAsciiHost(nsACString &result)
 {
-    if (mHostEncoding == eEncoding_ASCII) {
-        result = Host();
-        CALL_RUST_GETTER_STR(result, GetAsciiHost, result);
-        return NS_OK;
-    }
-
-    // perhaps we have it cached...
-    if (mHostA) {
-        result = mHostA;
-        CALL_RUST_GETTER_STR(result, GetAsciiHost, result);
-        return NS_OK;
-    }
-
-    if (gIDN) {
-        nsresult rv;
-        rv = gIDN->ConvertUTF8toACE(Host(), result);
-        CALL_RUST_GETTER_STR(result, GetAsciiHost, result);
-        if (NS_SUCCEEDED(rv)) {
-            mHostA = ToNewCString(result);
-            return NS_OK;
-        }
-        NS_WARNING("nsIDNService::ConvertUTF8toACE failed");
-    }
-
-    // something went wrong... guess all we can do is URL escape :-/
-    NS_EscapeURL(Host(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
+    result = Host();
     CALL_RUST_GETTER_STR(result, GetAsciiHost, result);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStandardURL::GetOriginCharset(nsACString &result)
 {
     if (mOriginCharset.IsEmpty())
@@ -2079,17 +2108,16 @@ nsStandardURL::SetHost(const nsACString 
     if (strchr(host, ' '))
         return NS_ERROR_MALFORMED_URI;
 
     if (mSpec.Length() + strlen(host) - Host().Length() > (uint32_t) net_GetURLMaxLength()) {
         return NS_ERROR_MALFORMED_URI;
     }
 
     InvalidateCache();
-    mHostEncoding = eEncoding_ASCII;
 
     uint32_t len;
     nsAutoCString hostBuf;
     nsresult rv = NormalizeIDN(flat, hostBuf);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
@@ -2402,17 +2430,17 @@ nsStandardURL::CloneInternal(nsStandardU
                              nsIURI **result)
 
 {
     RefPtr<nsStandardURL> clone = StartClone();
     if (!clone)
         return NS_ERROR_OUT_OF_MEMORY;
 
     // Copy local members into clone.
-    // Also copies the cached members mFile, mHostA
+    // Also copies the cached members mFile, mDisplayHost
     clone->CopyMembers(this, refHandlingMode, newRef, true);
 
     clone.forget(result);
     return NS_OK;
 }
 
 nsresult nsStandardURL::CopyMembers(nsStandardURL * source,
     nsStandardURL::RefHandlingEnum refHandlingMode, const nsACString& newRef,
@@ -2433,28 +2461,25 @@ nsresult nsStandardURL::CopyMembers(nsSt
     mExtension = source->mExtension;
     mQuery = source->mQuery;
     mRef = source->mRef;
     mOriginCharset = source->mOriginCharset;
     mURLType = source->mURLType;
     mParser = source->mParser;
     mMutable = true;
     mSupportsFileURL = source->mSupportsFileURL;
-    mHostEncoding = source->mHostEncoding;
 
     COPY_RUST_MEMBER;
     if (copyCached) {
         mFile = source->mFile;
-        mHostA = source->mHostA ? strdup(source->mHostA) : nullptr;
+        mCheckedIfHostA = source->mCheckedIfHostA;
+        mDisplayHost = source->mDisplayHost;
         mSpecEncoding = source->mSpecEncoding;
     } else {
-        // The same state as after calling InvalidateCache()
-        mFile = nullptr;
-        mHostA = nullptr;
-        mSpecEncoding = eEncoding_Unknown;
+        InvalidateCache(true);
     }
 
     if (refHandlingMode == eIgnoreRef) {
         SetRef(EmptyCString());
     } else if (refHandlingMode == eReplaceRef) {
         SetRef(newRef);
     }
 
@@ -3424,17 +3449,17 @@ nsStandardURL::SetMutable(bool value)
 
 //----------------------------------------------------------------------------
 // nsStandardURL::nsISerializable
 //----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsStandardURL::Read(nsIObjectInputStream *stream)
 {
-    NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
+    NS_PRECONDITION(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host");
     NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
                     "Shouldn't have spec encoding here");
 
     nsresult rv;
 
     uint32_t urlType;
     rv = stream->Read32(&urlType);
     if (NS_FAILED(rv)) return rv;
@@ -3512,25 +3537,16 @@ nsStandardURL::Read(nsIObjectInputStream
     if (NS_FAILED(rv)) return rv;
     mMutable = isMutable;
 
     bool supportsFileURL;
     rv = stream->ReadBoolean(&supportsFileURL);
     if (NS_FAILED(rv)) return rv;
     mSupportsFileURL = supportsFileURL;
 
-    uint32_t hostEncoding;
-    rv = stream->Read32(&hostEncoding);
-    if (NS_FAILED(rv)) return rv;
-    if (hostEncoding != eEncoding_ASCII && hostEncoding != eEncoding_UTF8) {
-        NS_WARNING("Unexpected host encoding");
-        return NS_ERROR_UNEXPECTED;
-    }
-    mHostEncoding = hostEncoding;
-
     // wait until object is set up, then modify path to include the param
     if (old_param.mLen >= 0) {  // note that mLen=0 is ";"
         // If this wasn't empty, it marks characters between the end of the
         // file and start of the query - mPath should include the param,
         // query and ref already.  Bump the mFilePath and
         // directory/basename/extension components to include this.
         mFilepath.Merge(mSpec,  ';', old_param);
         mDirectory.Merge(mSpec, ';', old_param);
@@ -3609,20 +3625,17 @@ nsStandardURL::Write(nsIObjectOutputStre
     if (NS_FAILED(rv)) return rv;
 
     rv = stream->WriteBoolean(mMutable);
     if (NS_FAILED(rv)) return rv;
 
     rv = stream->WriteBoolean(mSupportsFileURL);
     if (NS_FAILED(rv)) return rv;
 
-    rv = stream->Write32(mHostEncoding);
-    if (NS_FAILED(rv)) return rv;
-
-    // mSpecEncoding and mHostA are just caches that can be recovered as needed.
+    // mSpecEncoding and mDisplayHost are just caches that can be recovered as needed.
 
     return NS_OK;
 }
 
 //---------------------------------------------------------------------------
 // nsStandardURL::nsIIPCSerializableURI
 //---------------------------------------------------------------------------
 
@@ -3661,26 +3674,25 @@ nsStandardURL::Serialize(URIParams& aPar
     params.directory() = ToIPCSegment(mDirectory);
     params.baseName() = ToIPCSegment(mBasename);
     params.extension() = ToIPCSegment(mExtension);
     params.query() = ToIPCSegment(mQuery);
     params.ref() = ToIPCSegment(mRef);
     params.originCharset() = mOriginCharset;
     params.isMutable() = !!mMutable;
     params.supportsFileURL() = !!mSupportsFileURL;
-    params.hostEncoding() = mHostEncoding;
-    // mSpecEncoding and mHostA are just caches that can be recovered as needed.
+    // mSpecEncoding and mDisplayHost are just caches that can be recovered as needed.
 
     aParams = params;
 }
 
 bool
 nsStandardURL::Deserialize(const URIParams& aParams)
 {
-    NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
+    NS_PRECONDITION(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host");
     NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
                     "Shouldn't have spec encoding here");
     NS_PRECONDITION(!mFile, "Shouldn't have cached file");
 
     if (aParams.type() != URIParams::TStandardURLParams) {
         NS_ERROR("Received unknown parameters from the other process!");
         return false;
     }
@@ -3698,22 +3710,16 @@ nsStandardURL::Deserialize(const URIPara
         case URLTYPE_NO_AUTHORITY:
             mParser = net_GetNoAuthURLParser();
             break;
         default:
             NS_NOTREACHED("bad urlType");
             return false;
     }
 
-    if (params.hostEncoding() != eEncoding_ASCII &&
-        params.hostEncoding() != eEncoding_UTF8) {
-        NS_WARNING("Unexpected host encoding");
-        return false;
-    }
-
     mPort = params.port();
     mDefaultPort = params.defaultPort();
     mSpec = params.spec();
     mScheme = FromIPCSegment(params.scheme());
     mAuthority = FromIPCSegment(params.authority());
     mUsername = FromIPCSegment(params.username());
     mPassword = FromIPCSegment(params.password());
     mHost = FromIPCSegment(params.host());
@@ -3722,21 +3728,20 @@ nsStandardURL::Deserialize(const URIPara
     mDirectory = FromIPCSegment(params.directory());
     mBasename = FromIPCSegment(params.baseName());
     mExtension = FromIPCSegment(params.extension());
     mQuery = FromIPCSegment(params.query());
     mRef = FromIPCSegment(params.ref());
     mOriginCharset = params.originCharset();
     mMutable = params.isMutable();
     mSupportsFileURL = params.supportsFileURL();
-    mHostEncoding = params.hostEncoding();
 
     CALL_RUST_SYNC;
 
-    // mSpecEncoding and mHostA are just caches that can be recovered as needed.
+    // mSpecEncoding and mDisplayHost are just caches that can be recovered as needed.
     return true;
 }
 
 //----------------------------------------------------------------------------
 // nsStandardURL::nsIClassInfo
 //----------------------------------------------------------------------------
 
 NS_IMETHODIMP
@@ -3795,17 +3800,17 @@ nsStandardURL::GetClassIDNoAlloc(nsCID *
 // nsStandardURL::nsISizeOf
 //----------------------------------------------------------------------------
 
 size_t
 nsStandardURL::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
          mOriginCharset.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
-         aMallocSizeOf(mHostA);
+         mDisplayHost.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mParser
   // - mFile
 }
 
 size_t
--- a/netwerk/base/nsStandardURL.h
+++ b/netwerk/base/nsStandardURL.h
@@ -163,17 +163,17 @@ protected:
 
     virtual nsStandardURL* StartClone();
 
     // Helper to share code between Clone methods.
     nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
                            const nsACString& newRef,
                            nsIURI** aClone);
     // Helper method that copies member variables from the source StandardURL
-    // if copyCached = true, it will also copy mFile and mHostA
+    // if copyCached = true, it will also copy mFile and mDisplayHost
     nsresult CopyMembers(nsStandardURL * source, RefHandlingEnum mode,
                          const nsACString& newRef,
                          bool copyCached = false);
 
     // Helper for subclass implementation of GetFile().  Subclasses that map
     // URIs to files in a special way should implement this method.  It should
     // ensure that our mFile is initialized, if it's possible.
     // returns NS_ERROR_NO_INTERFACE if the url does not map to a file
@@ -276,29 +276,32 @@ private:
     nsCString              mOriginCharset;
     nsCOMPtr<nsIURLParser> mParser;
 
     // mFile is protected so subclasses can access it directly
 protected:
     nsCOMPtr<nsIFile>      mFile;  // cached result for nsIFileURL::GetFile
 
 private:
-    char                  *mHostA; // cached result for nsIURI::GetHostA
+    // cached result for nsIURI::GetDisplayHost
+    nsCString              mDisplayHost;
 
     enum {
         eEncoding_Unknown,
         eEncoding_ASCII,
         eEncoding_UTF8
     };
 
-    uint32_t mHostEncoding    : 2; // eEncoding_xxx
     uint32_t mSpecEncoding    : 2; // eEncoding_xxx
     uint32_t mURLType         : 2; // nsIStandardURL::URLTYPE_xxx
     uint32_t mMutable         : 1; // nsIStandardURL::mutable
     uint32_t mSupportsFileURL : 1; // QI to nsIFileURL?
+    uint32_t mCheckedIfHostA  : 1; // If set to true, it means either that
+                                   // mDisplayHost has a been initialized, or
+                                   // that the hostname is not punycode
 
     // global objects.  don't use COMPtr as its destructor will cause a
     // coredump if we leak it.
     static nsIIDNService               *gIDN;
     static char                         gHostLimitDigits[];
     static bool                         gInitialized;
 
 public: