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 306774 f4ed38f18dbc97c69c3147c0685f71b6edc4aa96
parent 306773 a878df4314faa5a620aca4b04fbae422f68392d3
child 306775 b212c88a2944a0768231767266460caa5364f514
push id30498
push usercbook@mozilla.com
push dateWed, 27 Jul 2016 14:33:19 +0000
treeherdermozilla-central@fe3a34f70dc0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin
bugs1280584
milestone50.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 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>"