Bug 1433958 - Change code that sets nsIURI.ref to use nsIURIMutator draft
authorValentin Gosu <valentin.gosu@gmail.com>
Mon, 26 Feb 2018 20:43:46 +0100
changeset 759894 29c33956375b85823763bef839dd1c6dd4a85c5e
parent 759893 0712280f03b96d36d6e0dcfb1d86933347fd801f
child 759895 bb2938bdaa42a1d9331e80edd48289e020cceff0
push id100504
push uservalentin.gosu@gmail.com
push dateMon, 26 Feb 2018 19:44:44 +0000
bugs1433958
milestone60.0a1
Bug 1433958 - Change code that sets nsIURI.ref to use nsIURIMutator MozReview-Commit-ID: 4caicZFBkcQ
browser/extensions/activity-stream/lib/FaviconFeed.jsm
dom/base/Link.cpp
dom/url/URLMainThread.cpp
dom/xbl/nsXBLPrototypeBinding.cpp
layout/style/nsCSSValue.cpp
modules/libjar/nsJARURI.cpp
netwerk/protocol/data/nsDataHandler.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/test/unit/test_URIs.js
netwerk/test/unit/test_URIs2.js
netwerk/test/unit/test_standardurl.js
uriloader/prefetch/nsOfflineCacheUpdate.cpp
--- a/browser/extensions/activity-stream/lib/FaviconFeed.jsm
+++ b/browser/extensions/activity-stream/lib/FaviconFeed.jsm
@@ -119,17 +119,19 @@ var FaviconFeed = class FaviconFeed {
       return;
     }
 
     const sitesByDomain = await this.getSitesByDomain();
     const domain = getDomain(url);
     if (domain in sitesByDomain) {
       let iconUri = Services.io.newURI(sitesByDomain[domain].image_url);
       // The #tippytop is to be able to identify them for telemetry.
-      iconUri.ref = "tippytop";
+      iconUri = iconUri.mutate()
+                       .setRef("tippytop")
+                       .finalize();
       PlacesUtils.favicons.setAndFetchFaviconForPage(
         Services.io.newURI(url),
         iconUri,
         false,
         PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
         null,
         Services.scriptSecurityManager.getSystemPrincipal()
       );
--- a/dom/base/Link.cpp
+++ b/dom/base/Link.cpp
@@ -573,23 +573,29 @@ Link::SetPort(const nsAString &aPort)
 
   (void)uri->SetPort(port);
   SetHrefAttribute(uri);
 }
 
 void
 Link::SetHash(const nsAString &aHash)
 {
-  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
-  (void)uri->SetRef(NS_ConvertUTF16toUTF8(aHash));
+  nsresult rv = NS_MutateURI(uri)
+                  .SetRef(NS_ConvertUTF16toUTF8(aHash))
+                  .Finalize(uri);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   SetHrefAttribute(uri);
 }
 
 void
 Link::GetOrigin(nsAString &aOrigin)
 {
   aOrigin.Truncate();
 
--- a/dom/url/URLMainThread.cpp
+++ b/dom/url/URLMainThread.cpp
@@ -441,17 +441,19 @@ URLMainThread::GetHash(nsAString& aHash,
     aHash.Assign(char16_t('#'));
     AppendUTF8toUTF16(ref, aHash);
   }
 }
 
 void
 URLMainThread::SetHash(const nsAString& aHash, ErrorResult& aRv)
 {
-  mURI->SetRef(NS_ConvertUTF16toUTF8(aHash));
+  Unused << NS_MutateURI(mURI)
+              .SetRef(NS_ConvertUTF16toUTF8(aHash))
+              .Finalize(mURI);
 }
 
 void
 URLMainThread::SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv)
 {
   // Ignore failures to be compatible with NS4.
 
   mURI->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsAtom.h"
 #include "nsIInputStream.h"
 #include "nsNameSpaceManager.h"
 #include "nsIURI.h"
+#include "nsIURIMutator.h"
 #include "nsIURL.h"
 #include "nsIChannel.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIDocument.h"
@@ -127,26 +128,32 @@ nsXBLPrototypeBinding::nsXBLPrototypeBin
 }
 
 nsresult
 nsXBLPrototypeBinding::Init(const nsACString& aID,
                             nsXBLDocumentInfo* aInfo,
                             Element* aElement,
                             bool aFirstBinding)
 {
-  nsresult rv = aInfo->DocumentURI()->Clone(getter_AddRefs(mBindingURI));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv;
+  nsCOMPtr<nsIURI> bindingURI = aInfo->DocumentURI();
 
   // The binding URI might be an immutable URI (e.g. for about: URIs). In that case,
   // we'll fail in SetRef below, but that doesn't matter much for now.
   if (aFirstBinding) {
-    rv = mBindingURI->Clone(getter_AddRefs(mAlternateBindingURI));
+    rv = bindingURI->Clone(getter_AddRefs(mAlternateBindingURI));
     NS_ENSURE_SUCCESS(rv, rv);
   }
-  mBindingURI->SetRef(aID);
+  rv = NS_MutateURI(bindingURI)
+        .SetRef(aID)
+        .Finalize(mBindingURI);
+  if (NS_FAILED(rv)) {
+    // If SetRef failed, mBindingURI should be a clone.
+    bindingURI->Clone(getter_AddRefs(mBindingURI));
+  }
 
   mXBLDocInfoWeak = aInfo;
 
   // aElement will be null when reading from the cache, but the element will
   // still be set later.
   if (aElement) {
     SetBindingElement(aElement);
   }
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/ImageLoader.h"
 #include "CSSCalc.h"
 #include "gfxFontConstants.h"
 #include "imgIRequest.h"
 #include "imgRequestProxy.h"
 #include "nsIDocument.h"
+#include "nsIURIMutator.h"
 #include "nsCSSProps.h"
 #include "nsNetUtil.h"
 #include "nsPresContext.h"
 #include "nsStyleUtil.h"
 #include "nsDeviceContext.h"
 #ifdef MOZ_OLD_STYLE
 #include "nsStyleSet.h"
 #endif
@@ -3008,18 +3009,23 @@ already_AddRefed<nsIURI>
 css::URLValueData::ResolveLocalRef(nsIURI* aURI) const
 {
   nsCOMPtr<nsIURI> result = GetURI();
 
   if (result && IsLocalRef()) {
     nsCString ref;
     mURI->GetRef(ref);
 
-    aURI->Clone(getter_AddRefs(result));
-    result->SetRef(ref);
+    nsresult rv = NS_MutateURI(aURI)
+                    .SetRef(ref)
+                    .Finalize(result);
+    if (NS_FAILED(rv)) {
+      // If setting the ref failed, just return a clone.
+      aURI->Clone(getter_AddRefs(result));
+    }
   }
 
   return result.forget();
 }
 
 already_AddRefed<nsIURI>
 css::URLValueData::ResolveLocalRef(nsIContent* aContent) const
 {
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -694,17 +694,19 @@ NS_IMETHODIMP
 nsJARURI::GetRef(nsACString& ref)
 {
     return mJAREntry->GetRef(ref);
 }
 
 NS_IMETHODIMP
 nsJARURI::SetRef(const nsACString& ref)
 {
-    return mJAREntry->SetRef(ref);
+    return NS_MutateURI(mJAREntry)
+             .SetRef(ref)
+             .Finalize(mJAREntry);
 }
 
 NS_IMETHODIMP
 nsJARURI::GetDirectory(nsACString& directory)
 {
     return mJAREntry->GetDirectory(directory);
 }
 
--- a/netwerk/protocol/data/nsDataHandler.cpp
+++ b/netwerk/protocol/data/nsDataHandler.cpp
@@ -66,20 +66,19 @@ nsDataHandler::NewURI(const nsACString &
     nsresult rv;
     nsCOMPtr<nsIURI> uri;
 
     nsCString spec(aSpec);
 
     if (aBaseURI && !spec.IsEmpty() && spec[0] == '#') {
         // Looks like a reference instead of a fully-specified URI.
         // --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
-        rv = aBaseURI->Clone(getter_AddRefs(uri));
-        if (NS_FAILED(rv))
-            return rv;
-        rv = uri->SetRef(spec);
+        rv = NS_MutateURI(aBaseURI)
+               .SetRef(spec)
+               .Finalize(uri);
     } else {
         // Otherwise, we'll assume |spec| is a fully-specified data URI
         nsAutoCString contentType;
         bool base64;
         rv = ParseURI(spec, contentType, /* contentCharset = */ nullptr,
                       base64, /* dataBuffer = */ nullptr);
         if (NS_FAILED(rv))
             return rv;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3414,27 +3414,29 @@ HttpBaseChannel::IsReferrerSchemeAllowed
       scheme.EqualsIgnoreCase("ftp")) {
     return true;
   }
   return false;
 }
 
 /* static */
 void
-HttpBaseChannel::PropagateReferenceIfNeeded(nsIURI* aURI, nsIURI* aRedirectURI)
+HttpBaseChannel::PropagateReferenceIfNeeded(nsIURI* aURI, nsCOMPtr<nsIURI>& aRedirectURI)
 {
   bool hasRef = false;
   nsresult rv = aRedirectURI->GetHasRef(&hasRef);
   if (NS_SUCCEEDED(rv) && !hasRef) {
     nsAutoCString ref;
     aURI->GetRef(ref);
     if (!ref.IsEmpty()) {
       // NOTE: SetRef will fail if mRedirectURI is immutable
       // (e.g. an about: URI)... Oh well.
-      aRedirectURI->SetRef(ref);
+      Unused << NS_MutateURI(aRedirectURI)
+                  .SetRef(ref)
+                  .Finalize(aRedirectURI);
     }
   }
 }
 
 bool
 HttpBaseChannel::ShouldRewriteRedirectToGET(uint32_t httpStatus,
                                             nsHttpRequestHead::ParsedMethodType method)
 {
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -353,17 +353,17 @@ public:
     MOZ_MUST_USE nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo);
 
 public: /* Necko internal use only... */
     int64_t GetAltDataLength() { return mAltDataLength; }
     bool IsNavigation();
 
     static bool IsReferrerSchemeAllowed(nsIURI *aReferrer);
 
-    static void PropagateReferenceIfNeeded(nsIURI *aURI, nsIURI *aRedirectURI);
+    static void PropagateReferenceIfNeeded(nsIURI *aURI, nsCOMPtr<nsIURI>& aRedirectURI);
 
     // Return whether upon a redirect code of httpStatus for method, the
     // request method should be rewritten to GET.
     static bool ShouldRewriteRedirectToGET(uint32_t httpStatus,
                                            nsHttpRequestHead::ParsedMethodType method);
 
     // Like nsIEncodedChannel::DoApplyConversions except context is set to
     // mListenerContext.
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -452,34 +452,34 @@ function do_test_mutate_ref(aTest, aSuff
   var refURIWithSuffix    = NetUtil.newURI(aTest.spec + aSuffix);
   var refURIWithoutSuffix = NetUtil.newURI(aTest.spec);
 
   var testURI             = NetUtil.newURI(aTest.spec);
 
   // First: Try setting .ref to our suffix
   do_info("testing that setting .ref on " + aTest.spec +
           " to '" + aSuffix + "' does what we expect");
-  testURI.ref = aSuffix;
+  testURI = testURI.mutate().setRef(aSuffix).finalize();
   do_check_uri_eq(testURI, refURIWithSuffix);
   do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
 
   // Now try setting .ref but leave off the initial hash (expect same result)
   var suffixLackingHash = aSuffix.substr(1);
   if (suffixLackingHash) { // (skip this our suffix was *just* a #)
     do_info("testing that setting .ref on " + aTest.spec +
             " to '" + suffixLackingHash + "' does what we expect");
-    testURI.ref = suffixLackingHash;
+    testURI = testURI.mutate().setRef(suffixLackingHash).finalize();
     do_check_uri_eq(testURI, refURIWithSuffix);
     do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
   }
 
   // Now, clear .ref (should get us back the original spec)
   do_info("testing that clearing .ref on " + testURI.spec +
           " does what we expect");
-  testURI.ref = "";
+  testURI = testURI.mutate().setRef("").finalize();
   do_check_uri_eq(testURI, refURIWithoutSuffix);
   do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
   if (!aTest.relativeURI) {
     // TODO: These tests don't work as-is for relative URIs.
 
     // Now try setting .spec directly (including suffix) and then clearing .ref
     var specWithSuffix = aTest.spec + aSuffix;
@@ -497,18 +497,20 @@ function do_test_mutate_ref(aTest, aSuff
     if (!(testURI instanceof Ci.nsIJARURI)) {
       // Now try setting .pathQueryRef directly (including suffix) and then clearing .ref
       // (same as above, but with now with .pathQueryRef instead of .spec)
       testURI = NetUtil.newURI(aTest.spec);
 
       var pathWithSuffix = aTest.pathQueryRef + aSuffix;
       do_info("testing that setting path to " +
               pathWithSuffix + " and then clearing ref does what we expect");
-      testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
-      testURI.ref = "";
+      testURI = testURI.mutate()
+                       .setPathQueryRef(pathWithSuffix)
+                       .setRef("")
+                       .finalize();
       do_check_uri_eq(testURI, refURIWithoutSuffix);
       do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
       // Also: make sure that clearing .pathQueryRef also clears .ref
       testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
       do_info("testing that clearing path from " + 
               pathWithSuffix + " also clears .ref");
       testURI = testURI.mutate().setPathQueryRef("").finalize();
@@ -520,17 +522,17 @@ function do_test_mutate_ref(aTest, aSuff
 // Tests that normally-mutable properties can't be modified on
 // special URIs that are known to be immutable.
 function do_test_immutable(aTest) {
   Assert.ok(aTest.immutable);
 
   var URI = NetUtil.newURI(aTest.spec);
   // All the non-readonly attributes on nsIURI.idl:
   var propertiesToCheck = ["spec", "scheme",
-                           "host", "port", "query", "ref"];
+                           "host", "port", "query"];
 
   propertiesToCheck.forEach(function(aProperty) {
     var threw = false;
     try {
       URI[aProperty] = "anothervalue";
     } catch(e) {
       threw = true;
     }
--- a/netwerk/test/unit/test_URIs2.js
+++ b/netwerk/test/unit/test_URIs2.js
@@ -554,34 +554,34 @@ function do_test_mutate_ref(aTest, aSuff
   var refURIWithSuffix    = NetUtil.newURI(aTest.spec + aSuffix);
   var refURIWithoutSuffix = NetUtil.newURI(aTest.spec);
 
   var testURI             = NetUtil.newURI(aTest.spec);
 
   // First: Try setting .ref to our suffix
   do_info("testing that setting .ref on " + aTest.spec +
           " to '" + aSuffix + "' does what we expect");
-  testURI.ref = aSuffix;
+  testURI = testURI.mutate().setRef(aSuffix).finalize();
   do_check_uri_eq(testURI, refURIWithSuffix);
   do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
 
   // Now try setting .ref but leave off the initial hash (expect same result)
   var suffixLackingHash = aSuffix.substr(1);
   if (suffixLackingHash) { // (skip this our suffix was *just* a #)
     do_info("testing that setting .ref on " + aTest.spec +
             " to '" + suffixLackingHash + "' does what we expect");
-    testURI.ref = suffixLackingHash;
+    testURI = testURI.mutate().setRef(suffixLackingHash).finalize();
     do_check_uri_eq(testURI, refURIWithSuffix);
     do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
   }
 
   // Now, clear .ref (should get us back the original spec)
   do_info("testing that clearing .ref on " + testURI.spec +
           " does what we expect");
-  testURI.ref = "";
+  testURI = testURI.mutate().setRef("").finalize();
   do_check_uri_eq(testURI, refURIWithoutSuffix);
   do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
   if (!aTest.relativeURI) {
     // TODO: These tests don't work as-is for relative URIs.
 
     // Now try setting .spec directly (including suffix) and then clearing .ref
     var specWithSuffix = aTest.spec + aSuffix;
@@ -598,18 +598,20 @@ function do_test_mutate_ref(aTest, aSuff
     if (!(testURI instanceof Ci.nsIJARURI)) {
       // Now try setting .pathQueryRef directly (including suffix) and then clearing .ref
       // (same as above, but with now with .pathQueryRef instead of .spec)
       testURI = NetUtil.newURI(aTest.spec);
 
       var pathWithSuffix = aTest.pathQueryRef + aSuffix;
       do_info("testing that setting path to " +
               pathWithSuffix + " and then clearing ref does what we expect");
-      testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
-      testURI.ref = "";
+      testURI = testURI.mutate()
+                       .setPathQueryRef(pathWithSuffix)
+                       .setRef("")
+                       .finalize();
       do_check_uri_eq(testURI, refURIWithoutSuffix);
       do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
       // Also: make sure that clearing .pathQueryRef also clears .ref
       testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
       do_info("testing that clearing path from " + 
               pathWithSuffix + " also clears .ref");
       testURI = testURI.mutate().setPathQueryRef("").finalize();
@@ -621,17 +623,17 @@ function do_test_mutate_ref(aTest, aSuff
 // Tests that normally-mutable properties can't be modified on
 // special URIs that are known to be immutable.
 function do_test_immutable(aTest) {
   Assert.ok(aTest.immutable);
 
   var URI = NetUtil.newURI(aTest.spec);
   // All the non-readonly attributes on nsIURI.idl:
   var propertiesToCheck = ["scheme",
-                           "host", "port", "query", "ref"];
+                           "host", "port", "query"];
 
   propertiesToCheck.forEach(function(aProperty) {
     var threw = false;
     try {
       URI[aProperty] = "anothervalue";
     } catch(e) {
       threw = true;
     }
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -136,35 +136,35 @@ add_test(function test_setRef()
      ["http://example.com/a",    "xxxxxxxxxxxxxx", "http://example.com/a#xxxxxxxxxxxxxx"],
      ["http://example.com:80/a", "xxxxxxxxxxxxxx", "http://example.com:80/a#xxxxxxxxxxxxxx"],
     ];
 
   for (var [before, ref, result] of tests)
   {
     /* Test1: starting with empty ref */
     var a = stringToURL(before);
-    a.ref = ref;
+    a = a.mutate().setRef(ref).finalize().QueryInterface(Ci.nsIURL);
     var b = stringToURL(result);
 
     Assert.equal(a.spec, b.spec);
     Assert.equal(ref, b.ref);
     symmetricEquality(true, a, b);
 
     /* Test2: starting with non-empty */
-    a.ref = "yyyy";
+    a = a.mutate().setRef("yyyy").finalize().QueryInterface(Ci.nsIURL);
     var c = stringToURL(before);
-    c.ref = "yyyy";
+    c = c.mutate().setRef("yyyy").finalize().QueryInterface(Ci.nsIURL);
     symmetricEquality(true, a, c);
 
     /* Test3: reset the ref */
-    a.ref = "";
+    a = a.mutate().setRef("").finalize().QueryInterface(Ci.nsIURL);
     symmetricEquality(true, a, stringToURL(before));
 
     /* Test4: verify again after reset */
-    a.ref = ref;
+    a = a.mutate().setRef(ref).finalize().QueryInterface(Ci.nsIURL);
     symmetricEquality(true, a, b);
   }
   run_next_test();
 });
 
 // Bug 960014 - Make nsStandardURL::SetHost less magical around IPv6
 add_test(function test_ipv6()
 {
@@ -250,17 +250,17 @@ add_test(function test_escapeBrackets()
   run_next_test();
 });
 
 add_test(function test_escapeQuote()
 {
   var url = stringToURL("http://example.com/#'");
   Assert.equal(url.spec, "http://example.com/#'");
   Assert.equal(url.ref, "'");
-  url.ref = "test'test";
+  url = url.mutate().setRef("test'test").finalize();
   Assert.equal(url.spec, "http://example.com/#test'test");
   Assert.equal(url.ref, "test'test");
   run_next_test();
 });
 
 add_test(function test_apostropheEncoding()
 {
   // For now, single quote is escaped everywhere _except_ the path.
@@ -297,32 +297,33 @@ add_test(function test_hugeStringThrows(
 {
   let prefs = Cc["@mozilla.org/preferences-service;1"]
                 .getService(Ci.nsIPrefService);
   let maxLen = prefs.getIntPref("network.standard-url.max-length");
   let url = stringToURL("http://test:test@example.com");
 
   let hugeString = new Array(maxLen + 1).fill("a").join("");
   let properties = ["scheme",
-                    "host", "ref",
+                    "host",
                     "query"];
   for (let prop of properties) {
     Assert.throws(() => url[prop] = hugeString,
                   /NS_ERROR_MALFORMED_URI/,
                   `Passing a huge string to "${prop}" should throw`);
   }
 
   let setters = [
     { method: "setSpec", qi: Ci.nsIURIMutator },
     { method: "setUsername", qi: Ci.nsIURIMutator },
     { method: "setPassword", qi: Ci.nsIURIMutator },
     { method: "setFilePath", qi: Ci.nsIURIMutator },
     { method: "setHostPort", qi: Ci.nsIURIMutator },
     { method: "setUserPass", qi: Ci.nsIURIMutator },
     { method: "setPathQueryRef", qi: Ci.nsIURIMutator },
+    { method: "setRef", qi: Ci.nsIURIMutator },
     { method: "setFileName", qi: Ci.nsIURLMutator },
     { method: "setFileExtension", qi: Ci.nsIURLMutator },
     { method: "setFileBaseName", qi: Ci.nsIURLMutator },
   ];
 
   for (let prop of setters) {
     Assert.throws(() => url = url.mutate().QueryInterface(prop.qi)[prop.method](hugeString).finalize(),
                   /NS_ERROR_MALFORMED_URI/,
@@ -338,17 +339,17 @@ add_test(function test_filterWhitespace(
   Assert.equal(url.spec, "http://example.com/path/to%20the/file.ext?query#hash");
 
   // These setters should escape \r\n\t, not filter them.
   var url = stringToURL("http://test.com/path?query#hash");
   url = url.mutate().setFilePath("pa\r\n\tth").finalize();
   Assert.equal(url.spec, "http://test.com/pa%0D%0A%09th?query#hash");
   url.query = "qu\r\n\tery";
   Assert.equal(url.spec, "http://test.com/pa%0D%0A%09th?qu%0D%0A%09ery#hash");
-  url.ref = "ha\r\n\tsh";
+  url = url.mutate().setRef("ha\r\n\tsh").finalize();
   Assert.equal(url.spec, "http://test.com/pa%0D%0A%09th?qu%0D%0A%09ery#ha%0D%0A%09sh");
   url = url.mutate().QueryInterface(Ci.nsIURLMutator).setFileName("fi\r\n\tle.name").finalize();
   Assert.equal(url.spec, "http://test.com/fi%0D%0A%09le.name?qu%0D%0A%09ery#ha%0D%0A%09sh");
 
   run_next_test();
 });
 
 add_test(function test_backslashReplacement()
@@ -407,17 +408,17 @@ add_test(function test_encode_C0_and_spa
   }
 
   // Additionally, we need to check the setters.
   var url = stringToURL("http://example.com/path?query#hash");
   url = url.mutate().setFilePath("pa\0th").finalize();
   Assert.equal(url.spec, "http://example.com/pa%00th?query#hash");
   url.query = "qu\0ery";
   Assert.equal(url.spec, "http://example.com/pa%00th?qu%00ery#hash");
-  url.ref = "ha\0sh";
+  url = url.mutate().setRef("ha\0sh").finalize();
   Assert.equal(url.spec, "http://example.com/pa%00th?qu%00ery#ha%00sh");
   url = url.mutate().QueryInterface(Ci.nsIURLMutator).setFileName("fi\0le.name").finalize();
   Assert.equal(url.spec, "http://example.com/fi%00le.name?qu%00ery#ha%00sh");
 
   run_next_test();
 });
 
 add_test(function test_ipv4Normalize()
@@ -551,23 +552,23 @@ add_test(function test_idna_host() {
   equal(url.displayHost, "ält.example.org");
   equal(url.displayHostPort, "ält.example.org:8080");
   equal(url.displaySpec, "http://user:password@ält.example.org:8080/path?query#etc");
 
   equal(url.asciiHost, "xn--lt-uia.example.org");
   equal(url.asciiHostPort, "xn--lt-uia.example.org:8080");
   equal(url.asciiSpec, "http://user:password@xn--lt-uia.example.org:8080/path?query#etc");
 
-  url.ref = ""; // SetRef calls InvalidateCache()
+  url = url.mutate().setRef("").finalize(); // SetRef calls InvalidateCache()
   equal(url.spec, "http://user:password@ält.example.org:8080/path?query");
   equal(url.displaySpec, "http://user:password@ält.example.org:8080/path?query");
   equal(url.asciiSpec, "http://user:password@xn--lt-uia.example.org:8080/path?query");
 
   url = stringToURL("http://user:password@www.ält.com:8080/path?query#etc");
-  url.ref = "";
+  url = url.mutate().setRef("").finalize();
   equal(url.spec, "http://user:password@www.ält.com:8080/path?query");
 
   // We also check that the default behaviour changes once we filp the pref
   gPrefs.setBoolPref("network.standard-url.punycode-host", true);
 
   url = stringToURL("http://user:password@ält.example.org:8080/path?query#etc");
   equal(url.host, "xn--lt-uia.example.org");
   equal(url.hostPort, "xn--lt-uia.example.org:8080");
@@ -579,19 +580,19 @@ add_test(function test_idna_host() {
   equal(url.displayHost, "ält.example.org");
   equal(url.displayHostPort, "ält.example.org:8080");
   equal(url.displaySpec, "http://user:password@ält.example.org:8080/path?query#etc");
 
   equal(url.asciiHost, "xn--lt-uia.example.org");
   equal(url.asciiHostPort, "xn--lt-uia.example.org:8080");
   equal(url.asciiSpec, "http://user:password@xn--lt-uia.example.org:8080/path?query#etc");
 
-  url.ref = ""; // SetRef calls InvalidateCache()
+  url = url.mutate().setRef("").finalize(); // SetRef calls InvalidateCache()
   equal(url.spec, "http://user:password@xn--lt-uia.example.org:8080/path?query");
   equal(url.displaySpec, "http://user:password@ält.example.org:8080/path?query");
   equal(url.asciiSpec, "http://user:password@xn--lt-uia.example.org:8080/path?query");
 
   url = stringToURL("http://user:password@www.ält.com:8080/path?query#etc");
-  url.ref = "";
+  url = url.mutate().setRef("").finalize();
   equal(url.spec, "http://user:password@www.xn--lt-uia.com:8080/path?query");
 
   run_next_test();
 });
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/Element.h"
 #include "nsIDocumentLoader.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIDocument.h"
 #include "nsIObserverService.h"
 #include "nsIURL.h"
+#include "nsIURIMutator.h"
 #include "nsIWebProgress.h"
 #include "nsICryptoHash.h"
 #include "nsICacheEntry.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
@@ -77,21 +78,23 @@ public:
 private:
     uint32_t mCount;
     char **mValues;
 };
 
 namespace {
 
 nsresult
-DropReferenceFromURL(nsIURI * aURI)
+DropReferenceFromURL(nsCOMPtr<nsIURI>& aURI)
 {
     // XXXdholbert If this SetRef fails, callers of this method probably
     // want to call aURI->CloneIgnoringRef() and use the result of that.
-    return aURI->SetRef(EmptyCString());
+    return NS_MutateURI(aURI)
+             .SetRef(EmptyCString())
+             .Finalize(aURI);
 }
 
 void
 LogToConsole(const char * message, nsOfflineCacheUpdateItem * item = nullptr)
 {
     nsCOMPtr<nsIConsoleService> consoleService =
         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (consoleService)