Bug 1280584 - implement cloneWithNewRef, r?bz draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Mon, 18 Jul 2016 16:00:54 +0100
changeset 388977 70356ed02626cb4f400966d286fb45293a488a03
parent 388650 98e3e1a81859a64dfa625d1d40d3d0043a46941c
child 525648 75eda298b9218f71f3fd755d42444e77258b8d45
push id23290
push usergijskruitbosch@gmail.com
push dateMon, 18 Jul 2016 15:01:21 +0000
reviewersbz
bugs1280584
milestone50.0a1
Bug 1280584 - implement cloneWithNewRef, r?bz MozReview-Commit-ID: 8FIyclkJPDp
caps/nsNullPrincipalURI.cpp
dom/base/nsHostObjectURI.cpp
dom/base/nsHostObjectURI.h
dom/jsurl/nsJSProtocolHandler.cpp
dom/jsurl/nsJSProtocolHandler.h
image/decoders/icon/nsIconURI.cpp
modules/libjar/nsJARURI.cpp
netwerk/base/nsIOService.cpp
netwerk/base/nsIURI.idl
netwerk/base/nsSimpleNestedURI.cpp
netwerk/base/nsSimpleNestedURI.h
netwerk/base/nsSimpleURI.cpp
netwerk/base/nsSimpleURI.h
netwerk/base/nsStandardURL.cpp
netwerk/base/nsStandardURL.h
netwerk/protocol/about/nsAboutProtocolHandler.cpp
netwerk/protocol/about/nsAboutProtocolHandler.h
netwerk/test/unit/test_URIs.js
--- a/caps/nsNullPrincipalURI.cpp
+++ b/caps/nsNullPrincipalURI.cpp
@@ -266,16 +266,24 @@ NS_IMETHODIMP
 nsNullPrincipalURI::CloneIgnoringRef(nsIURI **_newURI)
 {
   // GetRef/SetRef not supported by nsNullPrincipalURI, so
   // CloneIgnoringRef() is the same as Clone().
   return Clone(_newURI);
 }
 
 NS_IMETHODIMP
+nsNullPrincipalURI::CloneWithNewRef(const nsACString& newRef, nsIURI **_newURI)
+{
+  // GetRef/SetRef not supported by nsNullPrincipalURI, so
+  // CloneWithNewRef() is the same as Clone().
+  return Clone(_newURI);
+}
+
+NS_IMETHODIMP
 nsNullPrincipalURI::Equals(nsIURI *aOther, bool *_equals)
 {
   *_equals = false;
   RefPtr<nsNullPrincipalURI> otherURI;
   nsresult rv = aOther->QueryInterface(kNullPrincipalURIImplementationCID,
                                        getter_AddRefs(otherURI));
   if (NS_SUCCEEDED(rv)) {
     *_equals = mPath == otherURI->mPath;
--- a/dom/base/nsHostObjectURI.cpp
+++ b/dom/base/nsHostObjectURI.cpp
@@ -141,21 +141,22 @@ nsHostObjectURI::SetScheme(const nsACStr
   // with a different protocol handler that doesn't expect us to be carrying
   // around a principal with nsIURIWithPrincipal.
   return NS_ERROR_FAILURE;
 }
 
 // nsIURI methods:
 nsresult
 nsHostObjectURI::CloneInternal(mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode,
+                               const nsACString& newRef,
                                nsIURI** aClone)
 {
   nsCOMPtr<nsIURI> simpleClone;
   nsresult rv =
-    mozilla::net::nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
+    mozilla::net::nsSimpleURI::CloneInternal(aRefHandlingMode, newRef, getter_AddRefs(simpleClone));
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG
   RefPtr<nsHostObjectURI> uriCheck;
   rv = simpleClone->QueryInterface(kHOSTOBJECTURICID, getter_AddRefs(uriCheck));
   MOZ_ASSERT(NS_SUCCEEDED(rv) && uriCheck);
 #endif
 
--- a/dom/base/nsHostObjectURI.h
+++ b/dom/base/nsHostObjectURI.h
@@ -37,23 +37,25 @@ public:
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
   NS_DECL_NSIIPCSERIALIZABLEURI
 
   NS_IMETHOD SetScheme(const nsACString &aProtocol) override;
 
   // Override CloneInternal() and EqualsInternal()
   virtual nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
+                                 const nsACString& newRef,
                                  nsIURI** aClone) override;
   virtual nsresult EqualsInternal(nsIURI* aOther,
                                   RefHandlingEnum aRefHandlingMode,
                                   bool* aResult) override;
 
   // Override StartClone to hand back a nsHostObjectURI
-  virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum /* unused */) override
+  virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum /* unused */,
+                                                const nsACString& /* unused */) override
   { return new nsHostObjectURI(); }
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
 protected:
   virtual ~nsHostObjectURI() {}
 };
 
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -1357,17 +1357,18 @@ nsJSURI::Deserialize(const mozilla::ipc:
     } else {
         mBaseURI = nullptr;
     }
     return true;
 }
 
 // nsSimpleURI methods:
 /* virtual */ mozilla::net::nsSimpleURI*
-nsJSURI::StartClone(mozilla::net::nsSimpleURI::RefHandlingEnum /* ignored */)
+nsJSURI::StartClone(mozilla::net::nsSimpleURI::RefHandlingEnum /* ignored */,
+                    const nsACString& /* ignored */)
 {
     nsCOMPtr<nsIURI> baseClone;
     if (mBaseURI) {
       // Note: We preserve ref on *base* URI, regardless of ref handling mode.
       nsresult rv = mBaseURI->Clone(getter_AddRefs(baseClone));
       if (NS_FAILED(rv)) {
         return nullptr;
       }
--- a/dom/jsurl/nsJSProtocolHandler.h
+++ b/dom/jsurl/nsJSProtocolHandler.h
@@ -74,17 +74,18 @@ public:
     nsIURI* GetBaseURI() const
     {
         return mBaseURI;
     }
 
     NS_DECL_ISUPPORTS_INHERITED
 
     // nsIURI overrides
-    virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode) override;
+    virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode,
+                                                  const nsACString& newRef) override;
 
     // nsISerializable overrides
     NS_IMETHOD Read(nsIObjectInputStream* aStream) override;
     NS_IMETHOD Write(nsIObjectOutputStream* aStream) override;
 
     // nsIIPCSerializableURI overrides
     NS_DECL_NSIIPCSERIALIZABLEURI
 
--- a/image/decoders/icon/nsIconURI.cpp
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -450,16 +450,25 @@ NS_IMETHODIMP
 nsMozIconURI::CloneIgnoringRef(nsIURI** result)
 {
   // GetRef/SetRef not supported by nsMozIconURI, so
   // CloneIgnoringRef() is the same as Clone().
   return Clone(result);
 }
 
 NS_IMETHODIMP
+nsMozIconURI::CloneWithNewRef(const nsACString& newRef, nsIURI** result)
+{
+  // GetRef/SetRef not supported by nsMozIconURI, so
+  // CloneWithNewRef() is the same as Clone().
+  return Clone(result);
+}
+
+
+NS_IMETHODIMP
 nsMozIconURI::Resolve(const nsACString& relativePath, nsACString& result)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsMozIconURI::GetAsciiSpec(nsACString& aSpecA)
 {
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -523,16 +523,31 @@ nsJARURI::CloneIgnoringRef(nsIURI **resu
     rv = CloneWithJARFileInternal(mJARFile, eIgnoreRef, getter_AddRefs(uri));
     if (NS_FAILED(rv)) return rv;
 
     uri.forget(result);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsJARURI::CloneWithNewRef(const nsACString& newRef, nsIURI **result)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIJARURI> uri;
+    rv = CloneWithJARFileInternal(mJARFile, eIgnoreRef, getter_AddRefs(uri));
+    if (NS_FAILED(rv)) return rv;
+
+    uri->SetRef(newRef);
+
+    uri.forget(result);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsJARURI::Resolve(const nsACString &relativePath, nsACString &result)
 {
     nsresult rv;
 
     nsCOMPtr<nsIIOService> ioServ(do_GetIOService(&rv));
     if (NS_FAILED(rv))
       return rv;
 
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -619,16 +619,22 @@ nsIOService::NewURI(const nsACString &aS
 
     nsAutoCString scheme;
     nsresult rv = ExtractScheme(aSpec, scheme);
     if (NS_FAILED(rv)) {
         // then aSpec is relative
         if (!aBaseURI)
             return NS_ERROR_MALFORMED_URI;
 
+        if (!aSpec.IsEmpty() && aSpec[0] == '#') {
+            // Looks like a reference instead of a fully-specified URI.
+            // --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
+            return aBaseURI->CloneWithNewRef(aSpec, result);
+        }
+
         rv = aBaseURI->GetScheme(scheme);
         if (NS_FAILED(rv)) return rv;
     }
 
     // now get the handler for this scheme
     nsCOMPtr<nsIProtocolHandler> handler;
     rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
     if (NS_FAILED(rv)) return rv;
--- a/netwerk/base/nsIURI.idl
+++ b/netwerk/base/nsIURI.idl
@@ -241,16 +241,22 @@ interface nsIURI : nsISupports
     boolean equalsExceptRef(in nsIURI other);
 
     /**
      * Clones the current URI, clearing the 'ref' attribute in the clone.
      */
     nsIURI cloneIgnoringRef();
 
     /**
+     * Clones the current URI, replacing the 'ref' attribute in the clone with
+     * the ref supplied.
+     */
+    nsIURI cloneWithNewRef(in AUTF8String newRef);
+
+    /**
      * returns a string for the current URI with the ref element cleared.
      */
    readonly attribute AUTF8String specIgnoringRef;
 
     /**
      * Returns if there is a reference portion (the part after the "#") of the URI.
      */
    readonly attribute boolean hasRef;
--- a/netwerk/base/nsSimpleNestedURI.cpp
+++ b/netwerk/base/nsSimpleNestedURI.cpp
@@ -144,24 +144,30 @@ nsSimpleNestedURI::EqualsInternal(nsIURI
             }
         }
     }
 
     return NS_OK;
 }
 
 /* virtual */ nsSimpleURI*
-nsSimpleNestedURI::StartClone(nsSimpleURI::RefHandlingEnum refHandlingMode)
+nsSimpleNestedURI::StartClone(nsSimpleURI::RefHandlingEnum refHandlingMode,
+                              const nsACString& newRef)
 {
     NS_ENSURE_TRUE(mInnerURI, nullptr);
     
     nsCOMPtr<nsIURI> innerClone;
-    nsresult rv = refHandlingMode == eHonorRef ?
-        mInnerURI->Clone(getter_AddRefs(innerClone)) :
-        mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone));
+    nsresult rv;
+    if (refHandlingMode == eHonorRef) {
+        rv = mInnerURI->Clone(getter_AddRefs(innerClone));
+    } else if (refHandlingMode == eReplaceRef) {
+        rv = mInnerURI->CloneWithNewRef(newRef, getter_AddRefs(innerClone));
+    } else {
+        rv = mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone));
+    }
 
     if (NS_FAILED(rv)) {
         return nullptr;
     }
 
     nsSimpleNestedURI* url = new nsSimpleNestedURI(innerClone);
     url->SetMutable(false);
 
--- a/netwerk/base/nsSimpleNestedURI.h
+++ b/netwerk/base/nsSimpleNestedURI.h
@@ -46,17 +46,18 @@ public:
     NS_DECL_NSINESTEDURI
 
     // Overrides for various methods nsSimpleURI implements follow.
   
     // nsSimpleURI overrides
     virtual nsresult EqualsInternal(nsIURI* other,
                                     RefHandlingEnum refHandlingMode,
                                     bool* result) override;
-    virtual nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode) override;
+    virtual nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode,
+                                    const nsACString& newRef) override;
 
     // nsISerializable overrides
     NS_IMETHOD Read(nsIObjectInputStream* aStream) override;
     NS_IMETHOD Write(nsIObjectOutputStream* aStream) override;
 
     // nsIIPCSerializableURI overrides
     NS_DECL_NSIIPCSERIALIZABLEURI
 
--- a/netwerk/base/nsSimpleURI.cpp
+++ b/netwerk/base/nsSimpleURI.cpp
@@ -466,48 +466,63 @@ nsSimpleURI::SchemeIs(const char *i_Sche
     } else {
         *o_Equals = false;
     }
 
     return NS_OK;
 }
 
 /* virtual */ nsSimpleURI*
-nsSimpleURI::StartClone(nsSimpleURI::RefHandlingEnum /* ignored */)
+nsSimpleURI::StartClone(nsSimpleURI::RefHandlingEnum /* ignored */,
+                        const nsACString& /* ignored */)
 {
     return new nsSimpleURI();
 }
 
 NS_IMETHODIMP
 nsSimpleURI::Clone(nsIURI** result)
 {
-    return CloneInternal(eHonorRef, result);
+    return CloneInternal(eHonorRef, EmptyCString(), result);
 }
 
 NS_IMETHODIMP
 nsSimpleURI::CloneIgnoringRef(nsIURI** result)
 {
-    return CloneInternal(eIgnoreRef, result);
+    return CloneInternal(eIgnoreRef, EmptyCString(), result);
+}
+
+NS_IMETHODIMP
+nsSimpleURI::CloneWithNewRef(const nsACString &newRef, nsIURI** result)
+{
+    return CloneInternal(eReplaceRef, newRef, result);
 }
 
 nsresult
 nsSimpleURI::CloneInternal(nsSimpleURI::RefHandlingEnum refHandlingMode,
+                           const nsACString &newRef,
                            nsIURI** result)
 {
-    RefPtr<nsSimpleURI> url = StartClone(refHandlingMode);
+    RefPtr<nsSimpleURI> url = StartClone(refHandlingMode, newRef);
     if (!url)
         return NS_ERROR_OUT_OF_MEMORY;
 
     // Note: |url| may well have mMutable false at this point, so
     // don't call any setter methods.
     url->mScheme = mScheme;
     url->mPath = mPath;
     if (refHandlingMode == eHonorRef) {
         url->mRef = mRef;
         url->mIsRefValid = mIsRefValid;
+    } else if (refHandlingMode == eReplaceRef) {
+        // Cheat to ensure we can use SetRef, to avoid duplicating logic from
+        // there about mIsRefValid and dealing with a leading hash.
+        bool wasMutable = url->mMutable;
+        url->mMutable = true;
+        url->SetRef(newRef);
+        url->mMutable = wasMutable;
     }
 
     url.forget(result);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::Resolve(const nsACString &relativePath, nsACString &result) 
--- a/netwerk/base/nsSimpleURI.h
+++ b/netwerk/base/nsSimpleURI.h
@@ -57,36 +57,39 @@ public:
     // - nsBlobURI: mPrincipal
     virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
     virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
 protected:
     // enum used in a few places to specify how .ref attribute should be handled
     enum RefHandlingEnum {
         eIgnoreRef,
-        eHonorRef
+        eHonorRef,
+        eReplaceRef
     };
 
     // Helper to share code between Equals methods.
     virtual nsresult EqualsInternal(nsIURI* other,
                                     RefHandlingEnum refHandlingMode,
                                     bool* result);
 
     // Helper to be used by inherited classes who want to test
     // equality given an assumed nsSimpleURI.  This must NOT check
     // the passed-in other for QI to our CID.
     bool EqualsInternal(nsSimpleURI* otherUri, RefHandlingEnum refHandlingMode);
 
     // NOTE: This takes the refHandlingMode as an argument because
     // nsSimpleNestedURI's specialized version needs to know how to clone
     // its inner URI.
-    virtual nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode);
+    virtual nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode,
+                                    const nsACString& newRef);
 
     // Helper to share code between Clone methods.
     virtual nsresult CloneInternal(RefHandlingEnum refHandlingMode,
+                                   const nsACString &newRef,
                                    nsIURI** clone);
     
     nsCString mScheme;
     nsCString mPath; // NOTE: mPath does not include ref, as an optimization
     nsCString mRef;  // so that URIs with different refs can share string data.
     bool mMutable;
     bool mIsRefValid; // To distinguish between empty-ref and no-ref.
 };
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -1305,17 +1305,17 @@ nsStandardURL::SetSpec(const nsACString 
     net_FilterURIString(flat, filteredURI);
 
     if (filteredURI.Length() == 0) {
         return NS_ERROR_MALFORMED_URI;
     }
 
     // Make a backup of the curent URL
     nsStandardURL prevURL(false,false);
-    prevURL.CopyMembers(this, eHonorRef);
+    prevURL.CopyMembers(this, eHonorRef, EmptyCString());
     Clear();
 
     if (IsSpecialProtocol(filteredURI)) {
         // Bug 652186: Replace all backslashes with slashes when parsing paths
         // Stop when we reach the query or the hash.
         nsAutoCString::iterator start;
         nsAutoCString::iterator end;
         filteredURI.BeginWriting(start);
@@ -1346,17 +1346,17 @@ nsStandardURL::SetSpec(const nsACString 
     if (mURLType == URLTYPE_AUTHORITY && mHost.mLen == -1) {
         rv = NS_ERROR_MALFORMED_URI;
     }
 
     if (NS_FAILED(rv)) {
         Clear();
         // If parsing the spec has failed, restore the old URL
         // so we don't end up with an empty URL.
-        CopyMembers(&prevURL, eHonorRef);
+        CopyMembers(&prevURL, eHonorRef, EmptyCString());
         return rv;
     }
 
     if (LOG_ENABLED()) {
         LOG((" spec      = %s\n", mSpec.get()));
         LOG((" port      = %d\n", mPort));
         LOG((" scheme    = (%u,%d)\n", mScheme.mPos,    mScheme.mLen));
         LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen));
@@ -2036,45 +2036,53 @@ nsStandardURL::StartClone()
 {
     nsStandardURL *clone = new nsStandardURL();
     return clone;
 }
 
 NS_IMETHODIMP
 nsStandardURL::Clone(nsIURI **result)
 {
-    return CloneInternal(eHonorRef, result);
+    return CloneInternal(eHonorRef, EmptyCString(), result);
 }
 
 
 NS_IMETHODIMP
 nsStandardURL::CloneIgnoringRef(nsIURI **result)
 {
-    return CloneInternal(eIgnoreRef, result);
+    return CloneInternal(eIgnoreRef, EmptyCString(), result);
+}
+
+NS_IMETHODIMP
+nsStandardURL::CloneWithNewRef(const nsACString& newRef, nsIURI **result)
+{
+    return CloneInternal(eReplaceRef, newRef, result);
 }
 
 nsresult
 nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode,
+                             const nsACString& newRef,
                              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
-    clone->CopyMembers(this, refHandlingMode, true);
+    clone->CopyMembers(this, refHandlingMode, newRef, true);
 
     clone.forget(result);
     return NS_OK;
 }
 
 nsresult nsStandardURL::CopyMembers(nsStandardURL * source,
-    nsStandardURL::RefHandlingEnum refHandlingMode, bool copyCached)
+    nsStandardURL::RefHandlingEnum refHandlingMode, const nsACString& newRef,
+    bool copyCached)
 {
     mSpec = source->mSpec;
     mDefaultPort = source->mDefaultPort;
     mPort = source->mPort;
     mScheme = source->mScheme;
     mAuthority = source->mAuthority;
     mUsername = source->mUsername;
     mPassword = source->mPassword;
@@ -2101,16 +2109,18 @@ nsresult nsStandardURL::CopyMembers(nsSt
         // The same state as after calling InvalidateCache()
         mFile = nullptr;
         mHostA = nullptr;
         mSpecEncoding = eEncoding_Unknown;
     }
 
     if (refHandlingMode == eIgnoreRef) {
         SetRef(EmptyCString());
+    } else if (refHandlingMode == eReplaceRef) {
+        SetRef(newRef);
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStandardURL::Resolve(const nsACString &in, nsACString &out)
 {
--- a/netwerk/base/nsStandardURL.h
+++ b/netwerk/base/nsStandardURL.h
@@ -144,33 +144,36 @@ public: /* internal -- HPUX compiler can
         nsCOMPtr<nsIUnicodeEncoder> mEncoder;
     };
     friend class nsSegmentEncoder;
 
 protected:
     // enum used in a few places to specify how .ref attribute should be handled
     enum RefHandlingEnum {
         eIgnoreRef,
-        eHonorRef
+        eHonorRef,
+        eReplaceRef
     };
 
     // Helper to share code between Equals and EqualsExceptRef
     // NOTE: *not* virtual, because no one needs to override this so far...
     nsresult EqualsInternal(nsIURI* unknownOther,
                             RefHandlingEnum refHandlingMode,
                             bool* result);
 
     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
     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
     virtual nsresult EnsureFile();
 
--- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp
@@ -387,27 +387,33 @@ nsNestedAboutURI::Write(nsIObjectOutputS
         if (NS_FAILED(rv)) return rv;
     }
 
     return NS_OK;
 }
 
 // nsSimpleURI
 /* virtual */ nsSimpleURI*
-nsNestedAboutURI::StartClone(nsSimpleURI::RefHandlingEnum aRefHandlingMode)
+nsNestedAboutURI::StartClone(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
+                             const nsACString& aNewRef)
 {
     // Sadly, we can't make use of nsSimpleNestedURI::StartClone here.
     // However, this function is expected to exactly match that function,
     // aside from the "new ns***URI()" call.
     NS_ENSURE_TRUE(mInnerURI, nullptr);
 
     nsCOMPtr<nsIURI> innerClone;
-    nsresult rv = aRefHandlingMode == eHonorRef ?
-        mInnerURI->Clone(getter_AddRefs(innerClone)) :
-        mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone));
+    nsresult rv;
+    if (aRefHandlingMode == eHonorRef) {
+        rv = mInnerURI->Clone(getter_AddRefs(innerClone));
+    } else if (aRefHandlingMode == eReplaceRef) {
+        rv = mInnerURI->CloneWithNewRef(aNewRef, getter_AddRefs(innerClone));
+    } else {
+        rv = mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone));
+    }
 
     if (NS_FAILED(rv)) {
         return nullptr;
     }
 
     nsNestedAboutURI* url = new nsNestedAboutURI(innerClone, mBaseURI);
     url->SetMutable(false);
 
--- a/netwerk/protocol/about/nsAboutProtocolHandler.h
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.h
@@ -65,17 +65,18 @@ public:
     virtual ~nsNestedAboutURI() {}
 
     // Override QI so we can QI to our CID as needed
     NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
 
     // Override StartClone(), the nsISerializable methods, and
     // GetClassIDNoAlloc; this last is needed to make our nsISerializable impl
     // work right.
-    virtual nsSimpleURI* StartClone(RefHandlingEnum aRefHandlingMode);
+    virtual nsSimpleURI* StartClone(RefHandlingEnum aRefHandlingMode,
+                                    const nsACString& newRef);
     NS_IMETHOD Read(nsIObjectInputStream* aStream);
     NS_IMETHOD Write(nsIObjectOutputStream* aStream);
     NS_IMETHOD GetClassIDNoAlloc(nsCID *aClassIDNoAlloc);
 
     nsIURI* GetBaseURI() const {
         return mBaseURI;
     }
 
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -421,16 +421,29 @@ function do_test_uri_with_hash_suffix(aT
 
   if (!origURI.ref) {
     // These tests fail if origURI has a ref
     do_info("testing cloneIgnoringRef on " + testURI.spec +
             " is equal to no-ref version but not equal to ref version");
     var cloneNoRef = testURI.cloneIgnoringRef();
     do_check_uri_eq(cloneNoRef, origURI);
     do_check_false(cloneNoRef.equals(testURI));
+
+    do_info("testing cloneWithNewRef on " + testURI.spec +
+            " with an empty ref is equal to no-ref version but not equal to ref version");
+    var cloneNewRef = testURI.cloneWithNewRef("");
+    do_check_uri_eq(cloneNewRef, origURI);
+    do_check_uri_eq(cloneNewRef, cloneNoRef);
+    do_check_false(cloneNewRef.equals(testURI));
+
+    do_info("testing cloneWithNewRef on " + origURI.spec +
+            " with the same new ref is equal to ref version and not equal to no-ref version");
+    cloneNewRef = origURI.cloneWithNewRef(aSuffix);
+    do_check_uri_eq(cloneNewRef, testURI);
+    do_check_true(cloneNewRef.equals(testURI));
   }
 
   do_check_property(aTest, testURI, "scheme");
   do_check_property(aTest, testURI, "prePath");
   if (!origURI.ref) {
     // These don't work if it's a ref already because '+' doesn't give the right result
     do_check_property(aTest, testURI, "path",
                       function(aStr) { return aStr + aSuffix; });