Bug 1431204 - Change calls to nsIURI.spec setter to use nsIURIMutator instead r=mayhemer
authorValentin Gosu <valentin.gosu@gmail.com>
Fri, 19 Jan 2018 15:19:42 +0100
changeset 454360 24e867a67e1fa9818438094b31d1fdd6411486a5
parent 454359 a36c57e9279da697b7c71e349c408ee14ba82510
child 454361 4070ee4b8315abe9753f239128d8242c80db9d63
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs1431204
milestone59.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 1431204 - Change calls to nsIURI.spec setter to use nsIURIMutator instead r=mayhemer * changes call to use nsIURIMutator.setSpec() * Add new NS_MutateURI constructor that takes new Mutator object * Make nsSimpleNestedURI::Mutate() and nsNestedAboutURI::Mutate() return mutable URIs * Make the finalizers for nsSimpleNestedURI and nsNestedAboutURI make the returned URIs immutable MozReview-Commit-ID: 1kcv6zMxnv7
dom/file/nsHostObjectProtocolHandler.cpp
dom/jsurl/nsJSProtocolHandler.cpp
dom/url/URLWorker.cpp
image/decoders/icon/nsIconProtocolHandler.cpp
netwerk/base/nsIURIMutator.idl
netwerk/base/nsSimpleNestedURI.cpp
netwerk/base/nsSimpleNestedURI.h
netwerk/protocol/about/nsAboutProtocolHandler.cpp
netwerk/protocol/about/nsAboutProtocolHandler.h
netwerk/protocol/data/nsDataHandler.cpp
netwerk/protocol/viewsource/nsViewSourceHandler.cpp
netwerk/test/gtest/TestHttpAuthUtils.cpp
netwerk/test/gtest/TestProtocolProxyService.cpp
netwerk/test/gtest/TestStandardURL.cpp
netwerk/test/unit/test_URIs2.js
netwerk/test/unit/test_standardurl.js
startupcache/test/TestStartupCache.cpp
--- a/dom/file/nsHostObjectProtocolHandler.cpp
+++ b/dom/file/nsHostObjectProtocolHandler.cpp
@@ -887,28 +887,30 @@ nsHostObjectProtocolHandler::NewURI(cons
                                     nsIURI *aBaseURI,
                                     nsIURI **aResult)
 {
   *aResult = nullptr;
   nsresult rv;
 
   DataInfo* info = GetDataInfo(aSpec);
 
-  RefPtr<nsHostObjectURI> uri;
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_MutateURI(new nsHostObjectURI::Mutator())
+         .SetSpec(aSpec)
+         .Finalize(uri);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  RefPtr<nsHostObjectURI> hostURI = static_cast<nsHostObjectURI*>(uri.get());
   if (info && info->mObjectType == DataInfo::eBlobImpl) {
     MOZ_ASSERT(info->mBlobImpl);
-    uri = new nsHostObjectURI(info->mPrincipal, info->mBlobImpl);
-  } else {
-    uri = new nsHostObjectURI(nullptr, nullptr);
+    hostURI->mPrincipal = info->mPrincipal;
+    hostURI->mBlobImpl = info->mBlobImpl;
   }
 
-  rv = uri->SetSpec(aSpec);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  NS_TryToSetImmutable(uri);
+  NS_TryToSetImmutable(hostURI);
   uri.forget(aResult);
 
   if (info && info->mObjectType == DataInfo::eBlobImpl) {
     info->mURIs.AppendElement(do_GetWeakReference(*aResult));
   }
 
   return NS_OK;
 }
@@ -1115,31 +1117,33 @@ NS_GetStreamForMediaStreamURI(nsIURI* aU
 }
 
 NS_IMETHODIMP
 nsFontTableProtocolHandler::NewURI(const nsACString& aSpec,
                                    const char *aCharset,
                                    nsIURI *aBaseURI,
                                    nsIURI **aResult)
 {
-  RefPtr<nsIURI> uri;
+  nsresult rv;
+  nsCOMPtr<nsIURI> uri;
 
   // Either you got here via a ref or a fonttable: uri
   if (aSpec.Length() && aSpec.CharAt(0) == '#') {
-    nsresult rv = aBaseURI->CloneIgnoringRef(getter_AddRefs(uri));
+    rv = NS_MutateURI(aBaseURI)
+           .SetRef(aSpec)
+           .Finalize(uri);
     NS_ENSURE_SUCCESS(rv, rv);
-
-    uri->SetRef(aSpec);
   } else {
     // Relative URIs (other than #ref) are not meaningful within the
     // fonttable: scheme.
     // If aSpec is a relative URI -other- than a bare #ref,
     // this will leave uri empty, and we'll return a failure code below.
-    uri = new mozilla::net::nsSimpleURI();
-    nsresult rv = uri->SetSpec(aSpec);
+    rv = NS_MutateURI(new mozilla::net::nsSimpleURI::Mutator())
+           .SetSpec(aSpec)
+           .Finalize(uri);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   bool schemeIs;
   if (NS_FAILED(uri->SchemeIs(FONTTABLEURI_SCHEME, &schemeIs)) || !schemeIs) {
     NS_WARNING("Non-fonttable spec in nsFontTableProtocolHander");
     return NS_ERROR_NOT_AVAILABLE;
   }
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -1191,30 +1191,32 @@ nsJSProtocolHandler::NewURI(const nsACSt
 {
     nsresult rv;
 
     // javascript: URLs (currently) have no additional structure beyond that
     // provided by standard URLs, so there is no "outer" object given to
     // CreateInstance.
 
     nsCOMPtr<nsIURI> url = new nsJSURI(aBaseURI);
-
-    if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset))
-      rv = url->SetSpec(aSpec);
-    else {
+    NS_MutateURI mutator(url);
+    if (!aCharset || !nsCRT::strcasecmp("UTF-8", aCharset)) {
+      mutator.SetSpec(aSpec);
+    } else {
       nsAutoCString utf8Spec;
       rv = EnsureUTF8Spec(PromiseFlatCString(aSpec), aCharset, utf8Spec);
       if (NS_SUCCEEDED(rv)) {
-        if (utf8Spec.IsEmpty())
-          rv = url->SetSpec(aSpec);
-        else
-          rv = url->SetSpec(utf8Spec);
+        if (utf8Spec.IsEmpty()) {
+          mutator.SetSpec(aSpec);
+        } else {
+          mutator.SetSpec(utf8Spec);
+        }
       }
     }
 
+    rv = mutator.Finalize(url);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     url.forget(result);
     return rv;
 }
 
--- a/dom/url/URLWorker.cpp
+++ b/dom/url/URLWorker.cpp
@@ -625,26 +625,30 @@ URLWorker::Init(const nsAString& aURL, c
     rv = net_ExtractURLScheme(NS_ConvertUTF16toUTF8(aBase.Value()), scheme);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aRv.ThrowTypeError<MSG_INVALID_URL>(aURL);
       return;
     }
   }
 
   if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) {
-    RefPtr<nsStandardURL> baseURL;
+    nsCOMPtr<nsIURI> baseURL;
     if (aBase.WasPassed()) {
       baseURL = new nsStandardURL();
 
       // XXXcatalinb: SetSpec only writes a warning to the console on urls
       // without a valid scheme. I can't fix that because we've come to rely
       // on that behaviour in a bunch of different places.
-      nsresult rv = baseURL->SetSpec(NS_ConvertUTF16toUTF8(aBase.Value()));
+      nsresult rv = NS_MutateURI(new nsStandardURL::Mutator())
+        .SetSpec(NS_ConvertUTF16toUTF8(aBase.Value()))
+        .Finalize(baseURL);
       nsAutoCString baseScheme;
-      baseURL->GetScheme(baseScheme);
+      if (baseURL) {
+        baseURL->GetScheme(baseScheme);
+      }
       if (NS_WARN_IF(NS_FAILED(rv)) || baseScheme.IsEmpty()) {
         aRv.ThrowTypeError<MSG_INVALID_URL>(aBase.Value());
         return;
       }
     }
     mStdURL = new nsStandardURL();
     aRv = mStdURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1,
                         NS_ConvertUTF16toUTF8(aURL), nullptr, baseURL);
@@ -702,18 +706,21 @@ URLWorker::SetHref(const nsAString& aHre
   nsAutoCString scheme;
   nsresult rv = net_ExtractURLScheme(NS_ConvertUTF16toUTF8(aHref), scheme);
   if (NS_FAILED(rv)) {
     aRv.ThrowTypeError<MSG_INVALID_URL>(aHref);
     return;
   }
 
   if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) {
-    mStdURL = new nsStandardURL();
-    aRv = mStdURL->SetSpec(NS_ConvertUTF16toUTF8(aHref));
+    nsCOMPtr<nsIURI> uri;
+    aRv = NS_MutateURI(new nsStandardURL::Mutator())
+            .SetSpec(NS_ConvertUTF16toUTF8(aHref))
+            .Finalize(uri);
+    mStdURL = static_cast<net::nsStandardURL*>(uri.get());
     if (mURLProxy) {
       mWorkerPrivate->AssertIsOnWorkerThread();
 
       RefPtr<TeardownURLRunnable> runnable =
         new TeardownURLRunnable(mURLProxy);
       mURLProxy = nullptr;
 
       if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable)))) {
--- a/image/decoders/icon/nsIconProtocolHandler.cpp
+++ b/image/decoders/icon/nsIconProtocolHandler.cpp
@@ -63,24 +63,19 @@ nsIconProtocolHandler::GetProtocolFlags(
 }
 
 NS_IMETHODIMP
 nsIconProtocolHandler::NewURI(const nsACString& aSpec,
                               const char* aOriginCharset, // ignored
                               nsIURI* aBaseURI,
                               nsIURI** result)
 {
-  nsCOMPtr<nsIMozIconURI> uri = new nsMozIconURI();
-  if (!uri) return NS_ERROR_OUT_OF_MEMORY;
-
-  nsresult rv = uri->SetSpec(aSpec);
-  if (NS_FAILED(rv)) return rv;
-
-  NS_ADDREF(*result = uri);
-  return NS_OK;
+  return NS_MutateURI(new nsMozIconURI::Mutator())
+    .SetSpec(aSpec)
+    .Finalize(result);
 }
 
 NS_IMETHODIMP
 nsIconProtocolHandler::NewChannel2(nsIURI* url,
                                    nsILoadInfo* aLoadInfo,
                                    nsIChannel** result)
 {
   NS_ENSURE_ARG_POINTER(url);
--- a/netwerk/base/nsIURIMutator.idl
+++ b/netwerk/base/nsIURIMutator.idl
@@ -273,16 +273,22 @@ interface nsIURIMutator : nsIURISetters
 
 // This class provides a useful helper that allows chaining of setter operations
 class MOZ_STACK_CLASS NS_MutateURI
 {
 public:
   explicit NS_MutateURI(nsIURI* aURI);
   explicit NS_MutateURI(const char * aContractID);
 
+  explicit NS_MutateURI(nsIURIMutator* m)
+  {
+    mStatus = m ? NS_OK : NS_ERROR_NULL_POINTER;
+    mMutator = m;
+  }
+
   NS_MutateURI& SetSpec(const nsACString& aSpec)
   {
     NS_ENSURE_SUCCESS(mStatus, *this);
     mStatus = mMutator->SetSpec(aSpec, nullptr);
     return *this;
   }
   NS_MutateURI& SetScheme(const nsACString& aScheme)
   {
--- a/netwerk/base/nsSimpleNestedURI.cpp
+++ b/netwerk/base/nsSimpleNestedURI.cpp
@@ -191,14 +191,17 @@ NS_IMPL_ISUPPORTS(nsSimpleNestedURI::Mut
 NS_IMETHODIMP
 nsSimpleNestedURI::Mutate(nsIURIMutator** aMutator)
 {
     RefPtr<nsSimpleNestedURI::Mutator> mutator = new nsSimpleNestedURI::Mutator();
     nsresult rv = mutator->InitFromURI(this);
     if (NS_FAILED(rv)) {
         return rv;
     }
+    // StartClone calls SetMutable(false) but we need the mutator clone
+    // to be mutable
+    mutator->ResetMutable();
     mutator.forget(aMutator);
     return NS_OK;
 }
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/base/nsSimpleNestedURI.h
+++ b/netwerk/base/nsSimpleNestedURI.h
@@ -72,23 +72,58 @@ protected:
 
 
 public:
     class Mutator
         : public nsIURIMutator
         , public BaseURIMutator<nsSimpleNestedURI>
     {
         NS_DECL_ISUPPORTS
-        NS_DEFINE_NSIMUTATOR_COMMON
         NS_FORWARD_SAFE_NSIURISETTERS_RET(mURI)
 
         explicit Mutator() { }
     private:
         virtual ~Mutator() { }
 
+        MOZ_MUST_USE NS_IMETHOD
+        Deserialize(const mozilla::ipc::URIParams& aParams) override
+        {
+            return InitFromIPCParams(aParams);
+        }
+
+        MOZ_MUST_USE NS_IMETHOD
+        Read(nsIObjectInputStream* aStream) override
+        {
+            return InitFromInputStream(aStream);
+        }
+
+        MOZ_MUST_USE NS_IMETHOD
+        Finalize(nsIURI** aURI) override
+        {
+            mURI->mMutable = false;
+            mURI.forget(aURI);
+            return NS_OK;
+        }
+
+        MOZ_MUST_USE NS_IMETHOD
+        SetSpec(const nsACString& aSpec, nsIURIMutator** aMutator) override
+        {
+            if (aMutator) {
+                NS_ADDREF(*aMutator = this);
+            }
+            return InitFromSpec(aSpec);
+        }
+
+        void ResetMutable()
+        {
+            if (mURI) {
+                mURI->mMutable = true;
+            }
+        }
+
         friend class nsSimpleNestedURI;
     };
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif /* nsSimpleNestedURI_h__ */
--- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp
@@ -108,21 +108,23 @@ NS_IMETHODIMP
 nsAboutProtocolHandler::NewURI(const nsACString &aSpec,
                                const char *aCharset, // ignore charset info
                                nsIURI *aBaseURI,
                                nsIURI **result)
 {
     *result = nullptr;
     nsresult rv;
 
+
     // Use a simple URI to parse out some stuff first
-    nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv);
-    if (NS_FAILED(rv)) return rv;
+    nsCOMPtr<nsIURI> url;
+    rv = NS_MutateURI(new nsSimpleURI::Mutator())
+           .SetSpec(aSpec)
+           .Finalize(url);
 
-    rv = url->SetSpec(aSpec);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     // Unfortunately, people create random about: URIs that don't correspond to
     // about: modules...  Since those URIs will never open a channel, might as
     // well consider them unsafe for better perf, and just in case.
     bool isSafe = false;
@@ -143,23 +145,20 @@ nsAboutProtocolHandler::NewURI(const nsA
         NS_ENSURE_SUCCESS(rv, rv);
 
         spec.InsertLiteral("moz-safe-about:", 0);
 
         nsCOMPtr<nsIURI> inner;
         rv = NS_NewURI(getter_AddRefs(inner), spec);
         NS_ENSURE_SUCCESS(rv, rv);
 
-        nsSimpleNestedURI* outer = new nsNestedAboutURI(inner, aBaseURI);
-        NS_ENSURE_TRUE(outer, NS_ERROR_OUT_OF_MEMORY);
-
-        // Take a ref to it in the COMPtr we plan to return
-        url = outer;
-
-        rv = outer->SetSpec(aSpec);
+        RefPtr<nsSimpleNestedURI> outer = new nsNestedAboutURI(inner, aBaseURI);
+        rv = NS_MutateURI(outer)
+               .SetSpec(aSpec)
+               .Finalize(url);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // We don't want to allow mutation, since it would allow safe and
     // unsafe URIs to change into each other...
     NS_TryToSetImmutable(url);
     url.swap(*result);
     return NS_OK;
@@ -293,31 +292,25 @@ nsSafeAboutProtocolHandler::GetProtocolF
 }
 
 NS_IMETHODIMP
 nsSafeAboutProtocolHandler::NewURI(const nsACString &aSpec,
                                    const char *aCharset, // ignore charset info
                                    nsIURI *aBaseURI,
                                    nsIURI **result)
 {
-    nsresult rv;
-
-    nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv);
-    if (NS_FAILED(rv)) return rv;
-
-    rv = url->SetSpec(aSpec);
+    nsresult rv = NS_MutateURI(new nsSimpleURI::Mutator())
+                    .SetSpec(aSpec)
+                    .Finalize(result);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
-    NS_TryToSetImmutable(url);
-
-    *result = nullptr;
-    url.swap(*result);
-    return rv;
+    NS_TryToSetImmutable(*result);
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSafeAboutProtocolHandler::NewChannel2(nsIURI* uri,
                                         nsILoadInfo* aLoadInfo,
                                         nsIChannel** result)
 {
     *result = nullptr;
@@ -432,16 +425,19 @@ NS_IMPL_ISUPPORTS(nsNestedAboutURI::Muta
 NS_IMETHODIMP
 nsNestedAboutURI::Mutate(nsIURIMutator** aMutator)
 {
     RefPtr<nsNestedAboutURI::Mutator> mutator = new nsNestedAboutURI::Mutator();
     nsresult rv = mutator->InitFromURI(this);
     if (NS_FAILED(rv)) {
         return rv;
     }
+    // StartClone calls SetMutable(false) but we need the mutator clone
+    // to be mutable
+    mutator->ResetMutable();
     mutator.forget(aMutator);
     return NS_OK;
 }
 
 // nsIClassInfo
 NS_IMETHODIMP
 nsNestedAboutURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
 {
--- a/netwerk/protocol/about/nsAboutProtocolHandler.h
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.h
@@ -88,22 +88,57 @@ protected:
 
 public:
     class Mutator
         : public nsIURIMutator
         , public BaseURIMutator<nsNestedAboutURI>
     {
         NS_DECL_ISUPPORTS
         NS_FORWARD_SAFE_NSIURISETTERS_RET(mURI)
-        NS_DEFINE_NSIMUTATOR_COMMON
 
         explicit Mutator() { }
     private:
         virtual ~Mutator() { }
 
+        MOZ_MUST_USE NS_IMETHOD
+        Deserialize(const mozilla::ipc::URIParams& aParams) override
+        {
+            return InitFromIPCParams(aParams);
+        }
+
+        MOZ_MUST_USE NS_IMETHOD
+        Read(nsIObjectInputStream* aStream) override
+        {
+            return InitFromInputStream(aStream);
+        }
+
+        MOZ_MUST_USE NS_IMETHOD
+        Finalize(nsIURI** aURI) override
+        {
+            mURI->mMutable = false;
+            mURI.forget(aURI);
+            return NS_OK;
+        }
+
+        MOZ_MUST_USE NS_IMETHOD
+        SetSpec(const nsACString& aSpec, nsIURIMutator** aMutator) override
+        {
+            if (aMutator) {
+                NS_ADDREF(*aMutator = this);
+            }
+            return InitFromSpec(aSpec);
+        }
+
+        void ResetMutable()
+        {
+            if (mURI) {
+                mURI->mMutable = true;
+            }
+        }
+
         friend class nsNestedAboutURI;
     };
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif /* nsAboutProtocolHandler_h___ */
--- a/netwerk/protocol/data/nsDataHandler.cpp
+++ b/netwerk/protocol/data/nsDataHandler.cpp
@@ -4,18 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDataChannel.h"
 #include "nsDataHandler.h"
 #include "nsNetCID.h"
 #include "nsError.h"
 #include "DataChannelChild.h"
 #include "plstr.h"
-
-static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
+#include "nsSimpleURI.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 
 nsDataHandler::nsDataHandler() {
 }
 
 nsDataHandler::~nsDataHandler() {
 }
@@ -59,17 +58,17 @@ nsDataHandler::GetProtocolFlags(uint32_t
 }
 
 NS_IMETHODIMP
 nsDataHandler::NewURI(const nsACString &aSpec,
                       const char *aCharset, // ignore charset info
                       nsIURI *aBaseURI,
                       nsIURI **result) {
     nsresult rv;
-    RefPtr<nsIURI> uri;
+    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))
@@ -89,20 +88,19 @@ nsDataHandler::NewURI(const nsACString &
         if (base64 || (strncmp(contentType.get(),"text/",5) != 0 &&
                        contentType.Find("xml") == kNotFound)) {
             // it's ascii encoded binary, don't let any spaces in
             if (!spec.StripWhitespace(mozilla::fallible)) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
         }
 
-        uri = do_CreateInstance(kSimpleURICID, &rv);
-        if (NS_FAILED(rv))
-            return rv;
-        rv = uri->SetSpec(spec);
+        rv = NS_MutateURI(new nsSimpleURI::Mutator())
+               .SetSpec(spec)
+               .Finalize(uri);
     }
 
     if (NS_FAILED(rv))
         return rv;
 
     uri.forget(result);
     return rv;
 }
--- a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp
@@ -70,28 +70,29 @@ nsViewSourceHandler::NewURI(const nsACSt
         return rv;
 
     // put back our scheme and construct a simple-uri wrapper
 
     asciiSpec.Insert(VIEW_SOURCE ":", 0);
 
     // We can't swap() from an RefPtr<nsSimpleNestedURI> to an nsIURI**,
     // sadly.
-    nsSimpleNestedURI* ourURI = new nsSimpleNestedURI(innerURI);
-    nsCOMPtr<nsIURI> uri = ourURI;
-    if (!uri)
-        return NS_ERROR_OUT_OF_MEMORY;
+    RefPtr<nsSimpleNestedURI> ourURI = new nsSimpleNestedURI(innerURI);
 
-    rv = ourURI->SetSpec(asciiSpec);
-    if (NS_FAILED(rv))
+    nsCOMPtr<nsIURI> uri;
+    rv = NS_MutateURI(ourURI)
+           .SetSpec(asciiSpec)
+           .Finalize(uri);
+    if (NS_FAILED(rv)) {
         return rv;
+    }
 
     // Make the URI immutable so it's impossible to get it out of sync
     // with its inner URI.
-    ourURI->SetMutable(false);
+    NS_TryToSetImmutable(uri);
 
     uri.swap(*aResult);
     return rv;
 }
 
 NS_IMETHODIMP
 nsViewSourceHandler::NewChannel2(nsIURI* uri,
                                  nsILoadInfo* aLoadInfo,
--- a/netwerk/test/gtest/TestHttpAuthUtils.cpp
+++ b/netwerk/test/gtest/TestHttpAuthUtils.cpp
@@ -1,46 +1,42 @@
 #include "gtest/gtest.h"
 
 #include "mozilla/net/HttpAuthUtils.h"
 #include "mozilla/Preferences.h"
-#include "nsIURL.h"
-#include "nsNetCID.h"
-#include "nsComponentManagerUtils.h"
+#include "nsNetUtil.h"
 
 namespace mozilla {
 namespace net {
 
 #define TEST_PREF "network.http_test.auth_utils"
 
 TEST(TestHttpAuthUtils, Bug1351301) {
-  nsCOMPtr<nsIURL> url( do_CreateInstance(NS_STANDARDURL_CONTRACTID) );
-  ASSERT_TRUE(url) << "couldn't create URL";
-
+  nsCOMPtr<nsIURI> url;
   nsAutoCString spec;
 
   ASSERT_EQ(Preferences::SetCString(TEST_PREF, "bar.com"), NS_OK);
   spec = "http://bar.com";
-  ASSERT_EQ(url->SetSpec(spec), NS_OK);
+  ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
   ASSERT_EQ(auth::URIMatchesPrefPattern(url,TEST_PREF), true);
 
   spec = "http://foo.bar.com";
-  ASSERT_EQ(url->SetSpec(spec), NS_OK);
+  ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
   ASSERT_EQ(auth::URIMatchesPrefPattern(url,TEST_PREF), true);
 
   spec = "http://foobar.com";
-  ASSERT_EQ(url->SetSpec(spec), NS_OK);
+  ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
   ASSERT_EQ(auth::URIMatchesPrefPattern(url,TEST_PREF), false);
 
   ASSERT_EQ(Preferences::SetCString(TEST_PREF, ".bar.com"), NS_OK);
   spec = "http://foo.bar.com";
-  ASSERT_EQ(url->SetSpec(spec), NS_OK);
+  ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
   ASSERT_EQ(auth::URIMatchesPrefPattern(url,TEST_PREF), true);
 
   spec = "http://bar.com";
-  ASSERT_EQ(url->SetSpec(spec), NS_OK);
+  ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
   ASSERT_EQ(auth::URIMatchesPrefPattern(url,TEST_PREF), false);
 
   ASSERT_EQ(Preferences::ClearUser(TEST_PREF), NS_OK);
 }
 
 } // namespace net
 } // namespace mozila
--- a/netwerk/test/gtest/TestProtocolProxyService.cpp
+++ b/netwerk/test/gtest/TestProtocolProxyService.cpp
@@ -1,89 +1,87 @@
 #include "gtest/gtest.h"
 
 #include "nsCOMPtr.h"
 #include "nsNetCID.h"
-#include "nsIURL.h"
 #include "nsString.h"
 #include "nsComponentManagerUtils.h"
 #include "../../base/nsProtocolProxyService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/Preferences.h"
+#include "nsNetUtil.h"
 
 namespace mozilla {
 namespace net {
 
 TEST(TestProtocolProxyService, LoadHostFilters) {
   nsCOMPtr<nsIProtocolProxyService2> ps = do_GetService(NS_PROTOCOLPROXYSERVICE_CID);
   ASSERT_TRUE(ps);
   mozilla::net::nsProtocolProxyService* pps = static_cast<mozilla::net::nsProtocolProxyService*>(ps.get());
 
-  nsCOMPtr<nsIURL> url( do_CreateInstance(NS_STANDARDURL_CONTRACTID) );
-  ASSERT_TRUE(url) << "couldn't create URL";
-
+  nsCOMPtr<nsIURI> url;
   nsAutoCString spec;
 
   auto CheckLoopbackURLs = [&](bool expected)
   {
     // loopback IPs are always filtered
     spec = "http://127.0.0.1";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
     spec = "http://[::1]";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
   };
 
   auto CheckURLs = [&](bool expected)
   {
     spec = "http://example.com";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
 
     spec = "https://10.2.3.4";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 443), expected);
 
     spec = "http://1.2.3.4";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
 
     spec = "http://1.2.3.4:8080";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
 
     spec = "http://[2001::1]";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
 
     spec = "http://2.3.4.5:7777";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
 
     spec = "http://[abcd::2]:123";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
 
     spec = "http://bla.test.com";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
   };
 
   auto CheckPortDomain = [&](bool expected)
   {
     spec = "http://blabla.com:10";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
   };
 
   auto CheckLocalDomain = [&](bool expected)
   {
     spec = "http://test";
-    ASSERT_EQ(url->SetSpec(spec), NS_OK);
+    ASSERT_EQ(NS_NewURI(getter_AddRefs(url), spec), NS_OK);
     ASSERT_EQ(pps->CanUseProxy(url, 80), expected);
   };
 
   // --------------------------------------------------------------------------
 
   nsAutoCString filter;
 
   // Anything is allowed when there are no filters set
--- a/netwerk/test/gtest/TestStandardURL.cpp
+++ b/netwerk/test/gtest/TestStandardURL.cpp
@@ -11,17 +11,17 @@
 
 // In nsStandardURL.cpp
 extern nsresult Test_NormalizeIPv4(const nsACString& host, nsCString& result);
 
 
 TEST(TestStandardURL, Simple) {
     nsCOMPtr<nsIURL> url( do_CreateInstance(NS_STANDARDURL_CONTRACTID) );
     ASSERT_TRUE(url);
-    ASSERT_EQ(url->SetSpec(NS_LITERAL_CSTRING("http://example.com")), NS_OK);
+    ASSERT_EQ(url->SetSpecInternal(NS_LITERAL_CSTRING("http://example.com")), NS_OK);
 
     nsAutoCString out;
 
     ASSERT_EQ(url->GetSpec(out), NS_OK);
     ASSERT_TRUE(out == NS_LITERAL_CSTRING("http://example.com/"));
 
     ASSERT_EQ(url->Resolve(NS_LITERAL_CSTRING("foo.html?q=45"), out), NS_OK);
     ASSERT_TRUE(out == NS_LITERAL_CSTRING("http://example.com/foo.html?q=45"));
@@ -178,17 +178,17 @@ TEST(TestStandardURL, From_test_standard
 #define COUNT 10000
 
 MOZ_GTEST_BENCH(TestStandardURL, Perf, [] {
     nsCOMPtr<nsIURL> url( do_CreateInstance(NS_STANDARDURL_CONTRACTID) );
     ASSERT_TRUE(url);
     nsAutoCString out;
 
     for (int i = COUNT; i; --i) {
-        ASSERT_EQ(url->SetSpec(NS_LITERAL_CSTRING("http://example.com")), NS_OK);
+        ASSERT_EQ(url->SetSpecInternal(NS_LITERAL_CSTRING("http://example.com")), NS_OK);
         ASSERT_EQ(url->GetSpec(out), NS_OK);
         url->Resolve(NS_LITERAL_CSTRING("foo.html?q=45"), out);
         url->SetScheme(NS_LITERAL_CSTRING("foo"));
         url->GetScheme(out);
         url->SetHost(NS_LITERAL_CSTRING("www.yahoo.com"));
         url->GetHost(out);
         url->SetPathQueryRef(NS_LITERAL_CSTRING("/some-path/one-the-net/about.html?with-a-query#for-you"));
         url->GetPathQueryRef(out);
--- a/netwerk/test/unit/test_URIs2.js
+++ b/netwerk/test/unit/test_URIs2.js
@@ -620,17 +620,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", "userPass", "username", "password",
+  var propertiesToCheck = ["scheme", "userPass", "username", "password",
                            "hostPort", "host", "port", "pathQueryRef", "query", "ref"];
 
   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
@@ -298,25 +298,29 @@ add_test(function test_percentDecoding()
 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 = ["spec", "scheme", "userPass", "username",
+  let properties = ["scheme", "userPass", "username",
                     "password", "hostPort", "host", "pathQueryRef", "ref",
                     "query", "fileName", "filePath", "fileBaseName", "fileExtension"];
   for (let prop of properties) {
     Assert.throws(() => url[prop] = hugeString,
                   /NS_ERROR_MALFORMED_URI/,
                   `Passing a huge string to "${prop}" should throw`);
   }
 
+  Assert.throws(() => { url = url.mutate().setSpec(hugeString).finalize(); },
+                /NS_ERROR_MALFORMED_URI/,
+                "Passing a huge string to setSpec should throw");
+
   run_next_test();
 });
 
 add_test(function test_filterWhitespace()
 {
   var url = stringToURL(" \r\n\th\nt\rt\tp://ex\r\n\tample.com/path\r\n\t/\r\n\tto the/fil\r\n\te.e\r\n\txt?que\r\n\try#ha\r\n\tsh \r\n\t ");
   Assert.equal(url.spec, "http://example.com/path/to%20the/file.ext?query#hash");
 
--- a/startupcache/test/TestStartupCache.cpp
+++ b/startupcache/test/TestStartupCache.cpp
@@ -24,16 +24,18 @@
 #include "nsIXPConnect.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "prio.h"
 #include "prprf.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Printf.h"
 #include "mozilla/UniquePtr.h"
+#include "nsNetCID.h"
+#include "nsIURIMutator.h"
 
 using namespace JS;
 
 using namespace mozilla::scache;
 using mozilla::UniquePtr;
 
 void
 WaitForStartupTimer()
@@ -122,22 +124,22 @@ TEST_F(TestStartupCache, WriteInvalidate
   rv = sc->GetBuffer(id, &outbuf, &len);
   EXPECT_EQ(rv, NS_ERROR_NOT_AVAILABLE);
 }
 
 TEST_F(TestStartupCache, WriteObject)
 {
   nsresult rv;
 
-  nsCOMPtr<nsIURI> obj
-    = do_CreateInstance("@mozilla.org/network/simple-uri;1");
-  ASSERT_TRUE(obj);
+  nsCOMPtr<nsIURI> obj;
 
   NS_NAMED_LITERAL_CSTRING(spec, "http://www.mozilla.org");
-  rv = obj->SetSpec(spec);
+  rv = NS_MutateURI(NS_SIMPLEURIMUTATOR_CONTRACTID)
+         .SetSpec(spec)
+         .Finalize(obj);
   EXPECT_TRUE(NS_SUCCEEDED(rv));
 
   StartupCache* sc = StartupCache::GetSingleton();
 
   // Create an object stream. Usually this is done with
   // NewObjectOutputWrappedStorageStream, but that uses
   // StartupCache::GetSingleton in debug builds, and we
   // don't have access to that here. Obviously.