Bug 1431204 - Change calls to nsIURI.spec setter to use nsIURIMutator instead draft
authorValentin Gosu <valentin.gosu@gmail.com>
Fri, 19 Jan 2018 15:19:42 +0100
changeset 722678 c31307843debb8d66e203f0d024434412b748a5d
parent 721495 4e429d313fd2e0f9202271ee8f3fb798817ec3e7
child 722679 80f378b91c09bde599473e3a22ac91ac71512156
push id96195
push uservalentin.gosu@gmail.com
push dateFri, 19 Jan 2018 14:28:43 +0000
bugs1431204
milestone59.0a1
Bug 1431204 - Change calls to nsIURI.spec setter to use nsIURIMutator instead * 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.