Bug 1280584 - implement cloneWithNewRef and thereby make hash/ref links use a simple unified codepath in the IO service, r=valentin
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 26 Jul 2016 23:38:46 +0100
changeset 393218 f4ed38f18dbc97c69c3147c0685f71b6edc4aa96
parent 393217 a878df4314faa5a620aca4b04fbae422f68392d3
child 393219 b212c88a2944a0768231767266460caa5364f514
push id24261
push usergijskruitbosch@gmail.com
push dateWed, 27 Jul 2016 09:59:13 +0000
reviewersvalentin
bugs1280584
milestone50.0a1
Bug 1280584 - implement cloneWithNewRef and thereby make hash/ref links use a simple unified codepath in the IO service, r=valentin 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
modules/libjar/nsJARURI.h
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
parser/xml/test/unit/results.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
@@ -156,21 +156,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
@@ -44,24 +44,30 @@ 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
-  { return new nsHostObjectURI(); }
+  virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode,
+                                                const nsACString& newRef) override
+  {
+    nsHostObjectURI* url = new nsHostObjectURI();
+    SetRefOnClone(url, refHandlingMode, newRef);
+    return url;
+  }
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   RefPtr<mozilla::dom::BlobImpl> mBlobImpl;
 
 protected:
   virtual ~nsHostObjectURI() {}
 };
 
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -1357,28 +1357,31 @@ 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 refHandlingMode,
+                    const nsACString& newRef)
 {
     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;
       }
     }
 
-    return new nsJSURI(baseClone);
+    nsJSURI* url = new nsJSURI(baseClone);
+    SetRefOnClone(url, refHandlingMode, newRef);
+    return url;
 }
 
 /* virtual */ nsresult
 nsJSURI::EqualsInternal(nsIURI* aOther,
                         mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode,
                         bool* aResult)
 {
     NS_ENSURE_ARG_POINTER(aOther);
--- 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,30 @@ 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, eReplaceRef, newRef,
+                                  getter_AddRefs(uri));
+    if (NS_FAILED(rv)) return rv;
+
+    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;
 
@@ -779,33 +793,45 @@ nsJARURI::CloneWithJARFile(nsIURI *jarFi
     return CloneWithJARFileInternal(jarFile, eHonorRef, result);
 }
 
 nsresult
 nsJARURI::CloneWithJARFileInternal(nsIURI *jarFile,
                                    nsJARURI::RefHandlingEnum refHandlingMode,
                                    nsIJARURI **result)
 {
+  return CloneWithJARFileInternal(jarFile, refHandlingMode, EmptyCString(), result);
+}
+
+nsresult
+nsJARURI::CloneWithJARFileInternal(nsIURI *jarFile,
+                                   nsJARURI::RefHandlingEnum refHandlingMode,
+                                   const nsACString& newRef,
+                                   nsIJARURI **result)
+{
     if (!jarFile) {
         return NS_ERROR_INVALID_ARG;
     }
 
     nsresult rv;
 
     nsCOMPtr<nsIURI> newJARFile;
     rv = jarFile->Clone(getter_AddRefs(newJARFile));
     if (NS_FAILED(rv)) return rv;
 
     NS_TryToSetImmutable(newJARFile);
 
     nsCOMPtr<nsIURI> newJAREntryURI;
-    rv = refHandlingMode == eHonorRef ?
-        mJAREntry->Clone(getter_AddRefs(newJAREntryURI)) :
-        mJAREntry->CloneIgnoringRef(getter_AddRefs(newJAREntryURI));
-
+    if (refHandlingMode == eHonorRef) {
+      rv = mJAREntry->Clone(getter_AddRefs(newJAREntryURI));
+    } else if (refHandlingMode == eReplaceRef) {
+      rv = mJAREntry->CloneWithNewRef(newRef, getter_AddRefs(newJAREntryURI));
+    } else {
+      rv = mJAREntry->CloneIgnoringRef(getter_AddRefs(newJAREntryURI));
+    }
     if (NS_FAILED(rv)) return rv;
 
     nsCOMPtr<nsIURL> newJAREntry(do_QueryInterface(newJAREntryURI));
     NS_ASSERTION(newJAREntry, "This had better QI to nsIURL!");
     
     nsJARURI* uri = new nsJARURI();
     NS_ADDREF(uri);
     uri->mJARFile = newJARFile;
--- a/modules/libjar/nsJARURI.h
+++ b/modules/libjar/nsJARURI.h
@@ -62,28 +62,33 @@ public:
     nsresult SetSpecWithBase(const nsACString& aSpec, nsIURI* aBaseURL);
 
 protected:
     virtual ~nsJARURI();
 
     // 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 share code between Clone methods.
+    // Helpers to share code between Clone methods.
     nsresult CloneWithJARFileInternal(nsIURI *jarFile,
                                       RefHandlingEnum refHandlingMode,
                                       nsIJARURI **result);
+    nsresult CloneWithJARFileInternal(nsIURI *jarFile,
+                                      RefHandlingEnum refHandlingMode,
+                                      const nsACString& newRef,
+                                      nsIJARURI **result);
     nsCOMPtr<nsIURI> mJARFile;
     // mJarEntry stored as a URL so that we can easily access things
     // like extensions, refs, etc.
     nsCOMPtr<nsIURL> mJAREntry;
     nsCString        mCharsetHint;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsJARURI, NS_THIS_JARURI_IMPL_CID)
--- 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,30 +144,37 @@ 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);
+    SetRefOnClone(url, refHandlingMode, newRef);
     url->SetMutable(false);
 
     return url;
 }
 
 // nsIClassInfo overrides
 
 NS_IMETHODIMP 
--- 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,49 +466,68 @@ nsSimpleURI::SchemeIs(const char *i_Sche
     } else {
         *o_Equals = false;
     }
 
     return NS_OK;
 }
 
 /* virtual */ nsSimpleURI*
-nsSimpleURI::StartClone(nsSimpleURI::RefHandlingEnum /* ignored */)
+nsSimpleURI::StartClone(nsSimpleURI::RefHandlingEnum refHandlingMode,
+                        const nsACString& newRef)
 {
-    return new nsSimpleURI();
+    nsSimpleURI* url = new nsSimpleURI();
+    SetRefOnClone(url, refHandlingMode, newRef);
+    return url;
+}
+
+/* virtual */ void
+nsSimpleURI::SetRefOnClone(nsSimpleURI* url,
+                           nsSimpleURI::RefHandlingEnum refHandlingMode,
+                           const nsACString& newRef)
+{
+    if (refHandlingMode == eHonorRef) {
+        url->mRef = mRef;
+        url->mIsRefValid = mIsRefValid;
+    } else if (refHandlingMode == eReplaceRef) {
+        url->SetRef(newRef);
+    }
 }
 
 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;
-    }
 
     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,44 @@ 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);
 
+    // Used by StartClone (and versions of StartClone in subclasses) to
+    // handle the ref in the right way for clones.
+    void SetRefOnClone(nsSimpleURI* url, RefHandlingEnum refHandlingMode,
+                       const nsACString& newRef);
+
     // 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,33 +387,40 @@ 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);
+    SetRefOnClone(url, aRefHandlingMode, aNewRef);
     url->SetMutable(false);
 
     return url;
 }
 
 // nsIClassInfo
 NS_IMETHODIMP
 nsNestedAboutURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
--- 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; });
--- a/parser/xml/test/unit/results.js
+++ b/parser/xml/test/unit/results.js
@@ -134,17 +134,17 @@ var vectors = [
     "sanitized": "<html><head></head><body><a></a><a>XXX</a><a>XXX</a></body></html>"
   },
   {
     "data": "1<vmlframe xmlns=urn:schemas-microsoft-com:vml style=behavior:url(#default#vml);position:absolute;width:100%;height:100% src=test.vml#xss></vmlframe>",
     "sanitized": "<html><head></head><body>1</body></html>"
   },
   {
     "data": "1<a href=#><line xmlns=urn:schemas-microsoft-com:vml style=behavior:url(#default#vml);position:absolute href=javascript:alert(1) strokecolor=white strokeweight=1000px from=0 to=1000 /></a>",
-    "sanitized": "<html><head></head><body>1<a></a></body></html>"
+    "sanitized": "<html><head></head><body>1<a href=\"#\"></a></body></html>"
   },
   {
     "data": "<a style=\"behavior:url(#default#AnchorClick);\" folder=\"javascript:alert(1)\">XXX</a>",
     "sanitized": "<html><head></head><body><a>XXX</a></body></html>"
   },
   {
     "data": "<!--<img src=\"--><img src=x onerror=alert(1)//\">",
     "sanitized": "<html><head></head><body><img></body></html>"
@@ -474,17 +474,17 @@ var vectors = [
     "sanitized": "<html><head></head><body><div draggable=\"true\">\n\t<h1>Drop me</h1>\n</div>\n\n</body></html>"
   },
   {
     "data": "<iframe src=\"view-source:http://www.example.org/\" frameborder=\"0\" style=\"width:400px;height:180px\"></iframe>\n\n<textarea type=\"text\" cols=\"50\" rows=\"10\"></textarea>",
     "sanitized": "<html><head></head><body>\n\n<textarea type=\"text\" cols=\"50\" rows=\"10\"></textarea></body></html>"
   },
   {
     "data": "<script>\nfunction makePopups(){\n\tfor (i=1;i<6;i++) {\n\t\twindow.open('popup.html','spam'+i,'width=50,height=50');\n\t}\n}\n</script>\n\n<body>\n<a href=\"#\" onclick=\"makePopups()\">Spam</a>",
-    "sanitized": "<html><head>\n\n</head><body>\n<a>Spam</a></body></html>"
+    "sanitized": "<html><head>\n\n</head><body>\n<a href=\"#\">Spam</a></body></html>"
   },
   {
     "data": "<html xmlns=\"http://www.w3.org/1999/xhtml\"\nxmlns:svg=\"http://www.w3.org/2000/svg\">\n<body style=\"background:gray\">\n<iframe src=\"http://example.com/\" style=\"width:800px; height:350px; border:none; mask: url(#maskForClickjacking);\"/>\n<svg:svg>\n<svg:mask id=\"maskForClickjacking\" maskUnits=\"objectBoundingBox\" maskContentUnits=\"objectBoundingBox\">\n\t<svg:rect x=\"0.0\" y=\"0.0\" width=\"0.373\" height=\"0.3\" fill=\"white\"/>\n\t<svg:circle cx=\"0.45\" cy=\"0.7\" r=\"0.075\" fill=\"white\"/>\n</svg:mask>\n</svg:svg>\n</body>\n</html>",
     "sanitized": "<html><head></head><body>\n\n&lt;svg:svg&gt;\n&lt;svg:mask id=\"maskForClickjacking\" maskUnits=\"objectBoundingBox\" maskContentUnits=\"objectBoundingBox\"&gt;\n\t&lt;svg:rect x=\"0.0\" y=\"0.0\" width=\"0.373\" height=\"0.3\" fill=\"white\"/&gt;\n\t&lt;svg:circle cx=\"0.45\" cy=\"0.7\" r=\"0.075\" fill=\"white\"/&gt;\n&lt;/svg:mask&gt;\n&lt;/svg:svg&gt;\n&lt;/body&gt;\n&lt;/html&gt;</body></html>"
   },
   {
     "data": "<iframe sandbox=\"allow-same-origin allow-forms allow-scripts\" src=\"http://example.org/\"></iframe>",
     "sanitized": "<html><head></head><body></body></html>"