Bug 945240 - Make nsIURI.host & variants return ASCII strings r=mcmanus
authorValentin Gosu <valentin.gosu@gmail.com>
Tue, 11 Jul 2017 19:09:10 +0200
changeset 368520 35d7c15f5df292e01bf59b8252e19995a75ea642
parent 368519 5ec303752dae48cf77d2f4e7486c0f80c7cc4e67
child 368521 abd19b1b58599d2d521c72b7cd628760b95065bd
push id32164
push userkwierso@gmail.com
push dateThu, 13 Jul 2017 00:58:33 +0000
treeherdermozilla-central@30ea2905130e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs945240
milestone56.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 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: