Bug 1433958 - Change code that sets nsIURI.pathQueryRef to use nsIURIMutator r=mayhemer
authorValentin Gosu <valentin.gosu@gmail.com>
Mon, 26 Feb 2018 20:43:45 +0100
changeset 457915 26dc46bd3cccc934f5d8070cd8ba9a33d7a60cdf
parent 457914 6c437b464d613af08b898df54fa9dcf056591d2d
child 457916 ac8bd801fea30e1950a787f79def1f8af050b25d
push id8799
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 16:46:23 +0000
treeherdermozilla-beta@15334014dc67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs1433958
milestone60.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 1433958 - Change code that sets nsIURI.pathQueryRef to use nsIURIMutator r=mayhemer MozReview-Commit-ID: HVyZ3E1XuLN
caps/DomainPolicy.cpp
chrome/nsChromeProtocolHandler.cpp
chrome/nsChromeRegistry.cpp
chrome/nsChromeRegistry.h
dom/base/FormData.cpp
dom/base/FormData.h
dom/base/nsDocument.cpp
dom/base/nsGlobalWindowOuter.cpp
dom/html/HTMLFormElement.cpp
dom/html/HTMLFormSubmission.cpp
dom/html/HTMLFormSubmission.h
dom/webbrowserpersist/nsWebBrowserPersist.cpp
dom/webbrowserpersist/nsWebBrowserPersist.h
netwerk/protocol/ftp/nsFTPChannel.h
netwerk/protocol/ftp/nsFtpConnectionThread.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/streamconv/converters/nsIndexedToHTML.cpp
netwerk/test/unit/test_URIs.js
netwerk/test/unit/test_URIs2.js
netwerk/test/unit/test_standardurl.js
toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js
--- a/caps/DomainPolicy.cpp
+++ b/caps/DomainPolicy.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DomainPolicy.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/Unused.h"
 #include "nsIMessageManager.h"
+#include "nsIURIMutator.h"
 #include "nsScriptSecurityManager.h"
 
 namespace mozilla {
 
 using namespace ipc;
 using namespace dom;
 
 NS_IMPL_ISUPPORTS(DomainPolicy, nsIDomainPolicy)
@@ -146,21 +147,20 @@ DomainPolicy::ApplyClone(const DomainPol
     CopyURIs(aClone->superBlacklist(), mSuperBlacklist);
     CopyURIs(aClone->superWhitelist(), mSuperWhitelist);
 }
 
 static already_AddRefed<nsIURI>
 GetCanonicalClone(nsIURI* aURI)
 {
     nsCOMPtr<nsIURI> clone;
-    nsresult rv = aURI->Clone(getter_AddRefs(clone));
-    NS_ENSURE_SUCCESS(rv, nullptr);
-    rv = clone->SetUserPass(EmptyCString());
-    NS_ENSURE_SUCCESS(rv, nullptr);
-    rv = clone->SetPathQueryRef(EmptyCString());
+    nsresult rv = NS_MutateURI(aURI)
+           .SetUserPass(EmptyCString())
+           .SetPathQueryRef(EmptyCString())
+           .Finalize(clone);
     NS_ENSURE_SUCCESS(rv, nullptr);
     return clone.forget();
 }
 
 NS_IMPL_ISUPPORTS(DomainSet, nsIDomainSet)
 
 NS_IMETHODIMP
 DomainSet::Add(nsIURI* aDomain)
--- a/chrome/nsChromeProtocolHandler.cpp
+++ b/chrome/nsChromeProtocolHandler.cpp
@@ -72,17 +72,17 @@ nsChromeProtocolHandler::NewURI(const ns
                                 const char *aCharset,
                                 nsIURI *aBaseURI,
                                 nsIURI **result)
 {
 
     // Chrome: URLs (currently) have no additional structure beyond that provided
     // by standard URLs, so there is no "outer" given to CreateInstance
     nsresult rv;
-    nsCOMPtr<nsIURL> surl;
+    nsCOMPtr<nsIURI> surl;
     nsCOMPtr<nsIURI> base(aBaseURI);
     rv = NS_MutateURI(new mozilla::net::nsStandardURL::Mutator())
            .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init,
                                    nsIStandardURL::URLTYPE_STANDARD,
                                    -1, nsCString(aSpec), aCharset,
                                    base, nullptr))
            .Finalize(surl);
     if (NS_FAILED(rv)) {
@@ -113,27 +113,23 @@ nsChromeProtocolHandler::NewChannel2(nsI
     NS_ENSURE_ARG_POINTER(aURI);
     NS_ENSURE_ARG_POINTER(aLoadInfo);
 
     NS_PRECONDITION(aResult, "Null out param");
 
 #ifdef DEBUG
     // Check that the uri we got is already canonified
     nsresult debug_rv;
-    nsCOMPtr<nsIURI> debugClone;
-    debug_rv = aURI->Clone(getter_AddRefs(debugClone));
+    nsCOMPtr<nsIURI> debugURL = aURI;
+    debug_rv = nsChromeRegistry::Canonify(debugURL);
     if (NS_SUCCEEDED(debug_rv)) {
-        nsCOMPtr<nsIURL> debugURL (do_QueryInterface(debugClone));
-        debug_rv = nsChromeRegistry::Canonify(debugURL);
+        bool same;
+        debug_rv = aURI->Equals(debugURL, &same);
         if (NS_SUCCEEDED(debug_rv)) {
-            bool same;
-            debug_rv = aURI->Equals(debugURL, &same);
-            if (NS_SUCCEEDED(debug_rv)) {
-                NS_ASSERTION(same, "Non-canonified chrome uri passed to nsChromeProtocolHandler::NewChannel!");
-            }
+            NS_ASSERTION(same, "Non-canonified chrome uri passed to nsChromeProtocolHandler::NewChannel!");
         }
     }
 #endif
 
     nsCOMPtr<nsIChannel> result;
 
     if (!nsChromeRegistry::gChromeRegistry) {
         // We don't actually want this ref, we just want the service to
--- a/chrome/nsChromeRegistry.cpp
+++ b/chrome/nsChromeRegistry.cpp
@@ -26,16 +26,17 @@
 #include "nsIScriptError.h"
 #include "nsIWindowMediator.h"
 #include "nsIPrefService.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Printf.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/Location.h"
+#include "nsIURIMutator.h"
 
 #include "unicode/uloc.h"
 
 nsChromeRegistry* nsChromeRegistry::gChromeRegistry;
 
 // DO NOT use namespace mozilla; it'll break due to a naming conflict between
 // mozilla::TextRange and a TextRange in OSX headers.
 using mozilla::StyleSheet;
@@ -147,17 +148,17 @@ nsChromeRegistry::Init()
   gChromeRegistry = this;
 
   mInitialized = true;
 
   return NS_OK;
 }
 
 nsresult
-nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL,
+nsChromeRegistry::GetProviderAndPath(nsIURI* aChromeURL,
                                      nsACString& aProvider, nsACString& aPath)
 {
   nsresult rv;
 
 #ifdef DEBUG
   bool isChrome;
   aChromeURL->SchemeIs("chrome", &isChrome);
   NS_ASSERTION(isChrome, "Non-chrome URI?");
@@ -194,17 +195,17 @@ nsChromeRegistry::GetProviderAndPath(nsI
   }
 
   aProvider.Assign(path.get() + 1, slash);
   return NS_OK;
 }
 
 
 nsresult
-nsChromeRegistry::Canonify(nsIURL* aChromeURL)
+nsChromeRegistry::Canonify(nsCOMPtr<nsIURI>& aChromeURL)
 {
   NS_NAMED_LITERAL_CSTRING(kSlash, "/");
 
   nsresult rv;
 
   nsAutoCString provider, path;
   rv = GetProviderAndPath(aChromeURL, provider, path);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -223,17 +224,19 @@ nsChromeRegistry::Canonify(nsIURL* aChro
       path.AppendLiteral(".dtd");
     }
     else if (provider.EqualsLiteral("skin")) {
       path.AppendLiteral(".css");
     }
     else {
       return NS_ERROR_INVALID_ARG;
     }
-    aChromeURL->SetPathQueryRef(path);
+    return NS_MutateURI(aChromeURL)
+             .SetPathQueryRef(path)
+             .Finalize(aChromeURL);
   }
   else {
     // prevent directory traversals ("..")
     // path is already unescaped once, but uris can get unescaped twice
     const char* pos = path.BeginReading();
     const char* end = path.EndReading();
     // Must start with [a-zA-Z0-9].
     if (!('a' <= *pos && *pos <= 'z') &&
--- a/chrome/nsChromeRegistry.h
+++ b/chrome/nsChromeRegistry.h
@@ -65,17 +65,20 @@ public:
   nsChromeRegistry() : mInitialized(false) { }
 
   virtual nsresult Init();
 
   static already_AddRefed<nsIChromeRegistry> GetService();
 
   static nsChromeRegistry* gChromeRegistry;
 
-  static nsresult Canonify(nsIURL* aChromeURL);
+  // This method can change its parameter, so due to thread safety issues
+  // it should only be called for nsCOMPtr<nsIURI> that is on the stack,
+  // unless you know what you are doing.
+  static nsresult Canonify(nsCOMPtr<nsIURI>& aChromeURL);
 
 protected:
   virtual ~nsChromeRegistry();
 
   void FlushSkinCaches();
   void FlushAllCaches();
 
   static void LogMessage(const char* aMsg, ...)
@@ -86,17 +89,17 @@ protected:
 
   virtual nsIURI* GetBaseURIFromPackage(const nsCString& aPackage,
                                         const nsCString& aProvider,
                                         const nsCString& aPath) = 0;
   virtual nsresult GetFlagsFromPackage(const nsCString& aPackage,
                                        uint32_t* aFlags) = 0;
 
   static nsresult RefreshWindow(nsPIDOMWindowOuter* aWindow);
-  static nsresult GetProviderAndPath(nsIURL* aChromeURL,
+  static nsresult GetProviderAndPath(nsIURI* aChromeURL,
                                      nsACString& aProvider, nsACString& aPath);
 
   bool GetDirectionForLocale(const nsACString& aLocale);
 
   void SanitizeForBCP47(nsACString& aLocale);
 
 public:
   static already_AddRefed<nsChromeRegistry> GetSingleton();
--- a/dom/base/FormData.cpp
+++ b/dom/base/FormData.cpp
@@ -96,17 +96,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 // -------------------------------------------------------------------------
 // HTMLFormSubmission
 nsresult
 FormData::GetEncodedSubmission(nsIURI* aURI,
                                nsIInputStream** aPostDataStream,
-                               int64_t* aPostDataStreamLength)
+                               int64_t* aPostDataStreamLength,
+                               nsCOMPtr<nsIURI>& aOutURI)
 {
   NS_NOTREACHED("Shouldn't call FormData::GetEncodedSubmission");
   return NS_OK;
 }
 
 void
 FormData::Append(const nsAString& aName, const nsAString& aValue,
                  ErrorResult& aRv)
--- a/dom/base/FormData.h
+++ b/dom/base/FormData.h
@@ -102,17 +102,17 @@ public:
 
   const nsAString& GetKeyAtIndex(uint32_t aIndex) const;
 
   const OwningBlobOrDirectoryOrUSVString& GetValueAtIndex(uint32_t aIndex) const;
 
   // HTMLFormSubmission
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength) override;
+                       int64_t* aPostDataStreamLength, nsCOMPtr<nsIURI>& aOutURI) override;
 
   virtual nsresult AddNameValuePair(const nsAString& aName,
                                     const nsAString& aValue) override
   {
     FormDataTuple* data = mFormData.AppendElement();
     SetNameValuePair(data, aName, aValue);
     return NS_OK;
   }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -268,16 +268,17 @@
 
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "nsISpeculativeConnect.h"
 
 #include "mozilla/MediaManager.h"
 
 #include "nsIURIClassifier.h"
+#include "nsIURIMutator.h"
 #include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "nsHTMLTags.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -9116,32 +9117,38 @@ nsDocument::MaybePreLoadImage(nsIURI* ur
   if (NS_SUCCEEDED(rv)) {
     mPreloadingImages.Put(uri, request.forget());
   }
 }
 
 void
 nsDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode)
 {
-  nsCOMPtr<nsIURI> uri;
-  if (NS_FAILED(aOrigURI->Clone(getter_AddRefs(uri)))) {
+  NS_MutateURI mutator(aOrigURI);
+  if (NS_FAILED(mutator.GetStatus())) {
       return;
   }
 
   // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
   // which ignores the path and uses only the origin. The other is for the
   // document mPreloadedPreconnects de-duplication hash. Anonymous vs
   // non-Anonymous preconnects create different connections on the wire and
   // therefore should not be considred duplicates of each other and we
   // normalize the path before putting it in the hash to accomplish that.
 
   if (aCORSMode == CORS_ANONYMOUS) {
-    uri->SetPathQueryRef(NS_LITERAL_CSTRING("/anonymous"));
+    mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/anonymous"));
   } else {
-    uri->SetPathQueryRef(NS_LITERAL_CSTRING("/"));
+    mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/"));
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = mutator.Finalize(uri);
+  if (NS_FAILED(rv)) {
+    return;
   }
 
   auto entry = mPreloadedPreconnects.LookupForAdd(uri);
   if (entry) {
     return; // we found an existing entry
   }
   entry.OrInsert([] () { return true; });
 
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -141,16 +141,17 @@
 #include "nsIControllers.h"
 #include "nsIControllerContext.h"
 #include "nsGlobalWindowCommands.h"
 #include "nsQueryObject.h"
 #include "nsContentUtils.h"
 #include "nsCSSProps.h"
 #include "nsIDOMFileList.h"
 #include "nsIURIFixup.h"
+#include "nsIURIMutator.h"
 #ifndef DEBUG
 #include "nsIAppStartup.h"
 #include "nsToolkitCompsCID.h"
 #endif
 #include "nsCDefaultURIFixup.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "nsIObserverService.h"
@@ -5788,18 +5789,21 @@ nsGlobalWindowOuter::PostMessageMozOuter
   // "*" indicates no specific origin is required.
   else if (!aTargetOrigin.EqualsASCII("*")) {
     nsCOMPtr<nsIURI> originURI;
     if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
       aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
       return;
     }
 
-    if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
-        NS_FAILED(originURI->SetPathQueryRef(EmptyCString()))) {
+    nsresult rv = NS_MutateURI(originURI)
+                    .SetUserPass(EmptyCString())
+                    .SetPathQueryRef(EmptyCString())
+                    .Finalize(originURI);
+    if (NS_FAILED(rv)) {
       return;
     }
 
     OriginAttributes attrs = aSubjectPrincipal.OriginAttributesRef();
     if (aSubjectPrincipal.GetIsSystemPrincipal()) {
       auto principal = BasePrincipal::Cast(GetPrincipal());
 
       if (attrs != principal->OriginAttributesRef()) {
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -772,17 +772,18 @@ HTMLFormElement::SubmitSubmission(HTMLFo
     AutoHandlingUserInputStatePusher userInpStatePusher(
                                        mSubmitInitiatedFromUserInput,
                                        nullptr, doc);
 
     nsCOMPtr<nsIInputStream> postDataStream;
     int64_t postDataStreamLength = -1;
     rv = aFormSubmission->GetEncodedSubmission(actionURI,
                                                getter_AddRefs(postDataStream),
-                                               &postDataStreamLength);
+                                               &postDataStreamLength,
+                                               actionURI);
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
     rv = linkHandler->OnLinkClickSync(this, actionURI,
                                       target.get(),
                                       VoidString(),
                                       postDataStream, postDataStreamLength,
                                       nullptr, false,
                                       getter_AddRefs(docShell),
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -14,16 +14,17 @@
 #include "nsIFormControl.h"
 #include "nsError.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsIFile.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsStringStream.h"
 #include "nsIURI.h"
+#include "nsIURIMutator.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 #include "nsLinebreakConverter.h"
 #include "nsEscape.h"
 #include "nsUnicharUtils.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsIMIMEInputStream.h"
 #include "nsIMIMEService.h"
@@ -108,17 +109,17 @@ public:
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
 
   virtual nsresult
   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
 
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength) override;
+                       int64_t* aPostDataStreamLength, nsCOMPtr<nsIURI>& aOutURI) override;
 
 protected:
 
   /**
    * URL encode a Unicode string by encoding it to bytes, converting linebreaks
    * properly, and then escaping many bytes as %xx.
    *
    * @param aStr the string to encode
@@ -263,19 +264,21 @@ HandleMailtoSubject(nsCString& aPath)
 
     aPath.Append(subjectStrEscaped);
   }
 }
 
 nsresult
 FSURLEncoded::GetEncodedSubmission(nsIURI* aURI,
                                    nsIInputStream** aPostDataStream,
-                                   int64_t* aPostDataStreamLength)
+                                   int64_t* aPostDataStreamLength,
+                                   nsCOMPtr<nsIURI>& aOutURI)
 {
   nsresult rv = NS_OK;
+  aOutURI = aURI;
 
   *aPostDataStream = nullptr;
   *aPostDataStreamLength = -1;
 
   if (mMethod == NS_FORM_METHOD_POST) {
 
     bool isMailto = false;
     aURI->SchemeIs("mailto", &isMailto);
@@ -290,18 +293,19 @@ FSURLEncoded::GetEncodedSubmission(nsIUR
       // Append the body to and force-plain-text args to the mailto line
       nsAutoCString escapedBody;
       if (NS_WARN_IF(!NS_Escape(mQueryString, escapedBody, url_XAlphas))) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
       path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
 
-      rv = aURI->SetPathQueryRef(path);
-
+      return NS_MutateURI(aURI)
+               .SetPathQueryRef(path)
+               .Finalize(aOutURI);
     } else {
 
       nsCOMPtr<nsIInputStream> dataStream;
       // XXX We *really* need to either get the string to disown its data (and
       // not destroy it), or make a string input stream that owns the CString
       // that is passed to it.  Right now this operation does a copy.
       rv = NS_NewCStringInputStream(getter_AddRefs(dataStream), mQueryString);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -326,17 +330,19 @@ FSURLEncoded::GetEncodedSubmission(nsIUR
     rv = aURI->SchemeIs("javascript", &schemeIsJavaScript);
     NS_ENSURE_SUCCESS(rv, rv);
     if (schemeIsJavaScript) {
       return NS_OK;
     }
 
     nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
     if (url) {
-      url->SetQuery(mQueryString);
+      rv = NS_MutateURI(aURI)
+             .SetQuery(mQueryString)
+             .Finalize(aOutURI);
     }
     else {
       nsAutoCString path;
       rv = aURI->GetPathQueryRef(path);
       NS_ENSURE_SUCCESS(rv, rv);
       // Bug 42616: Trim off named anchor and save it to add later
       int32_t namedAnchorPos = path.FindChar('#');
       nsAutoCString namedAnchor;
@@ -351,17 +357,19 @@ FSURLEncoded::GetEncodedSubmission(nsIUR
       if (kNotFound != queryStart) {
         path.Truncate(queryStart);
       }
 
       path.Append('?');
       // Bug 42616: Add named anchor to end after query string
       path.Append(mQueryString + namedAnchor);
 
-      aURI->SetPathQueryRef(path);
+      rv = NS_MutateURI(aURI)
+             .SetPathQueryRef(path)
+             .Finalize(aOutURI);
     }
   }
 
   return rv;
 }
 
 // i18n helper routines
 nsresult
@@ -616,19 +624,21 @@ FSMultipartFormData::AddDataChunk(const 
 
   // CRLF after file
   mPostDataChunk.AppendLiteral(CRLF);
 }
 
 nsresult
 FSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
                                           nsIInputStream** aPostDataStream,
-                                          int64_t* aPostDataStreamLength)
+                                          int64_t* aPostDataStreamLength,
+                                          nsCOMPtr<nsIURI>& aOutURI)
 {
   nsresult rv;
+  aOutURI = aURI;
 
   // Make header
   nsCOMPtr<nsIMIMEInputStream> mimeStream
     = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString contentType;
   GetContentType(contentType);
@@ -681,17 +691,17 @@ public:
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
 
   virtual nsresult
   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
 
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreaLength) override;
+                       int64_t* aPostDataStreaLength, nsCOMPtr<nsIURI>& aOutURI) override;
 
 private:
   nsString mBody;
 };
 
 nsresult
 FSTextPlain::AddNameValuePair(const nsAString& aName, const nsAString& aValue)
 {
@@ -721,19 +731,21 @@ FSTextPlain::AddNameDirectoryPair(const 
   RetrieveDirectoryName(aDirectory, dirname);
   AddNameValuePair(aName, dirname);
   return NS_OK;
 }
 
 nsresult
 FSTextPlain::GetEncodedSubmission(nsIURI* aURI,
                                   nsIInputStream** aPostDataStream,
-                                  int64_t* aPostDataStreamLength)
+                                  int64_t* aPostDataStreamLength,
+                                  nsCOMPtr<nsIURI>& aOutURI)
 {
   nsresult rv = NS_OK;
+  aOutURI = aURI;
 
   *aPostDataStream = nullptr;
   *aPostDataStreamLength = -1;
 
   // XXX HACK We are using the standard URL mechanism to give the body to the
   // mailer instead of passing the post data stream to it, since that sounds
   // hard.
   bool isMailto = false;
@@ -749,18 +761,19 @@ FSTextPlain::GetEncodedSubmission(nsIURI
     nsAutoCString escapedBody;
     if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(mBody), escapedBody,
                               url_XAlphas))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
 
-    rv = aURI->SetPathQueryRef(path);
-
+    rv = NS_MutateURI(aURI)
+           .SetPathQueryRef(path)
+           .Finalize(aOutURI);
   } else {
     // Create data stream.
     // We do want to send the data through the charset encoder and we want to
     // normalize linebreaks to use the "standard net" format (\r\n), but we
     // don't want to perform any other encoding. This means that names and
     // values which contains '=' or newlines are potentially ambigiously
     // encoded, but that how text/plain is specced.
     nsCString cbody;
--- a/dom/html/HTMLFormSubmission.h
+++ b/dom/html/HTMLFormSubmission.h
@@ -77,23 +77,25 @@ public:
    */
   virtual nsresult AddNameDirectoryPair(const nsAString& aName,
                                         Directory* aDirectory) = 0;
 
   /**
    * Given a URI and the current submission, create the final URI and data
    * stream that will be submitted.  Subclasses *must* implement this.
    *
-   * @param aURI the URI being submitted to [INOUT]
+   * @param aURI the URI being submitted to [IN]
    * @param aPostDataStream a data stream for POST data [OUT]
    * @param aPostDataStreamLength a data stream for POST data length [OUT]
+   * @param aOutURI the resulting URI. May be the same as aURI [OUT]
    */
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength) = 0;
+                       int64_t* aPostDataStreamLength,
+                       nsCOMPtr<nsIURI>& aOutURI) = 0;
 
   /**
    * Get the charset that will be used for submission.
    */
   void GetCharset(nsACString& aCharset) { mEncoding->Name(aCharset); }
 
   Element* GetOriginatingElement() const
   {
@@ -163,17 +165,17 @@ public:
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
 
   virtual nsresult
   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
 
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength) override;
+                       int64_t* aPostDataStreamLength, nsCOMPtr<nsIURI>& aOutURI) override;
 
   void GetContentType(nsACString& aContentType)
   {
     aContentType =
       NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary;
   }
 
   nsIInputStream* GetSubmissionBody(uint64_t* aContentLength);
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -620,17 +620,17 @@ nsWebBrowserPersist::SerializeNextFile()
             }
 
             // Make a URI to save the data to.
             nsCOMPtr<nsIURI> fileAsURI;
             rv = data->mDataPath->Clone(getter_AddRefs(fileAsURI));
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
-            rv = AppendPathToURI(fileAsURI, data->mFilename);
+            rv = AppendPathToURI(fileAsURI, data->mFilename, fileAsURI);
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
 
             // The Referrer Policy doesn't matter here since the referrer is
             // nullptr.
             rv = SaveURIInternal(uri, nullptr, nullptr,
                                  mozilla::net::RP_Unset, nullptr, nullptr,
@@ -1300,36 +1300,37 @@ nsWebBrowserPersist::GetLocalFileFromURI
         return rv;
     }
 
     file.forget(aLocalFile);
     return NS_OK;
 }
 
 /* static */ nsresult
-nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath)
+nsWebBrowserPersist::AppendPathToURI(nsIURI *aURI, const nsAString & aPath, nsCOMPtr<nsIURI>& aOutURI)
 {
     NS_ENSURE_ARG_POINTER(aURI);
 
     nsAutoCString newPath;
     nsresult rv = aURI->GetPathQueryRef(newPath);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
     // Append a forward slash if necessary
     int32_t len = newPath.Length();
     if (len > 0 && newPath.CharAt(len - 1) != '/')
     {
         newPath.Append('/');
     }
 
     // Store the path back on the URI
     AppendUTF16toUTF8(aPath, newPath);
-    aURI->SetPathQueryRef(newPath);
-
-    return NS_OK;
+
+    return NS_MutateURI(aURI)
+             .SetPathQueryRef(newPath)
+             .Finalize(aOutURI);
 }
 
 nsresult nsWebBrowserPersist::SaveURIInternal(
     nsIURI *aURI, nsISupports *aCacheKey, nsIURI *aReferrer,
     uint32_t aReferrerPolicy, nsIInputStream *aPostData,
     const char *aExtraHeaders, nsIURI *aFile,
     bool aCalcFileExt, bool aIsPrivate)
 {
@@ -2571,17 +2572,17 @@ nsWebBrowserPersist::URIData::GetLocalUR
     nsresult rv;
     nsCOMPtr<nsIURI> fileAsURI;
     if (mFile) {
         rv = mFile->Clone(getter_AddRefs(fileAsURI));
         NS_ENSURE_SUCCESS(rv, rv);
     } else {
         rv = mDataPath->Clone(getter_AddRefs(fileAsURI));
         NS_ENSURE_SUCCESS(rv, rv);
-        rv = AppendPathToURI(fileAsURI, mFilename);
+        rv = AppendPathToURI(fileAsURI, mFilename, fileAsURI);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // remove username/password if present
     fileAsURI->SetUserPass(EmptyCString());
 
     // reset node attribute
     // Use relative or absolute links
@@ -2694,28 +2695,28 @@ nsWebBrowserPersist::SaveSubframeContent
 
     nsString filenameWithExt = aData->mFilename;
     filenameWithExt.Append(aData->mSubFrameExt);
 
     // Work out the path for the subframe
     nsCOMPtr<nsIURI> frameURI;
     rv = mCurrentDataPath->Clone(getter_AddRefs(frameURI));
     NS_ENSURE_SUCCESS(rv, rv);
-    rv = AppendPathToURI(frameURI, filenameWithExt);
+    rv = AppendPathToURI(frameURI, filenameWithExt, frameURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Work out the path for the subframe data
     nsCOMPtr<nsIURI> frameDataURI;
     rv = mCurrentDataPath->Clone(getter_AddRefs(frameDataURI));
     NS_ENSURE_SUCCESS(rv, rv);
     nsAutoString newFrameDataPath(aData->mFilename);
 
     // Append _data
     newFrameDataPath.AppendLiteral("_data");
-    rv = AppendPathToURI(frameDataURI, newFrameDataPath);
+    rv = AppendPathToURI(frameDataURI, newFrameDataPath, frameDataURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Make frame document & data path conformant and unique
     nsCOMPtr<nsIURI> out;
     rv = CalculateUniqueFilename(frameURI, out);
     NS_ENSURE_SUCCESS(rv, rv);
     frameURI = out;
 
--- a/dom/webbrowserpersist/nsWebBrowserPersist.h
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.h
@@ -86,17 +86,17 @@ private:
     friend class OnWalk;
     friend class OnWrite;
 
     nsresult SaveDocumentDeferred(mozilla::UniquePtr<WalkData>&& aData);
     void Cleanup();
     void CleanupLocalFiles();
     nsresult GetValidURIFromObject(nsISupports *aObject, nsIURI **aURI) const;
     static nsresult GetLocalFileFromURI(nsIURI *aURI, nsIFile **aLocalFile);
-    static nsresult AppendPathToURI(nsIURI *aURI, const nsAString & aPath);
+    static nsresult AppendPathToURI(nsIURI *aURI, const nsAString & aPath, nsCOMPtr<nsIURI>& aOutURI);
     nsresult MakeAndStoreLocalFilenameInURIMap(
         nsIURI *aURI, bool aNeedsPersisting, URIData **aData);
     nsresult MakeOutputStream(
         nsIURI *aFile, nsIOutputStream **aOutputStream);
     nsresult MakeOutputStreamFromFile(
         nsIFile *aFile, nsIOutputStream **aOutputStream);
     nsresult MakeOutputStreamFromURI(nsIURI *aURI, nsIOutputStream  **aOutStream);
     nsresult CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel);
--- a/netwerk/protocol/ftp/nsFTPChannel.h
+++ b/netwerk/protocol/ftp/nsFTPChannel.h
@@ -42,16 +42,21 @@ public:
         , mStartPos(0)
         , mResumeRequested(false)
         , mLastModifiedTime(0)
         , mForcePending(false)
     {
         SetURI(uri);
     }
 
+    void UpdateURI(nsIURI *aURI) {
+        MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(), "Not thread-safe.");
+        mURI = aURI;
+    }
+
     nsIProxyInfo *ProxyInfo() {
         return mProxyInfo;
     }
 
     void SetProxyInfo(nsIProxyInfo *pi)
     {
         mProxyInfo = pi;
     }
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -35,16 +35,17 @@
 #include "nsICancelable.h"
 #include "nsIOutputStream.h"
 #include "nsIPrompt.h"
 #include "nsIProtocolHandler.h"
 #include "nsIProxyInfo.h"
 #include "nsIRunnable.h"
 #include "nsISocketTransportService.h"
 #include "nsIURI.h"
+#include "nsIURIMutator.h"
 #include "nsILoadInfo.h"
 #include "NullPrincipal.h"
 #include "nsIAuthPrompt2.h"
 #include "nsIFTPChannelParentInternal.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
@@ -1640,21 +1641,29 @@ nsFtpState::Init(nsFtpChannel *channel)
     } else {
         rv = mChannel->URI()->GetPathQueryRef(path);
     }
     if (NS_FAILED(rv))
         return rv;
 
     removeParamsFromPath(path);
 
+    nsCOMPtr<nsIURI> outURI;
     // FTP parameters such as type=i are ignored
     if (url) {
-        url->SetFilePath(path);
+        rv = NS_MutateURI(url)
+               .SetFilePath(path)
+               .Finalize(outURI);
     } else {
-        mChannel->URI()->SetPathQueryRef(path);
+        rv = NS_MutateURI(mChannel->URI())
+               .SetPathQueryRef(path)
+               .Finalize(outURI);
+    }
+    if (NS_SUCCEEDED(rv)) {
+        mChannel->UpdateURI(outURI);
     }
 
     // Skip leading slash
     char *fwdPtr = path.BeginWriting();
     if (!fwdPtr)
         return NS_ERROR_OUT_OF_MEMORY;
     if (*fwdPtr == '/')
         fwdPtr++;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1788,17 +1788,19 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     rv = mURI->CloneIgnoringRef(getter_AddRefs(mURIclone));
     if (NS_FAILED(rv)) return rv;
     clone = mURIclone;
     currentHost = referrerHost;
   }
 
   // strip away any userpass; we don't want to be giving out passwords ;-)
   // This is required by Referrer Policy stripping algorithm.
-  rv = clone->SetUserPass(EmptyCString());
+  rv = NS_MutateURI(clone)
+         .SetUserPass(EmptyCString())
+         .Finalize(clone);
   if (NS_FAILED(rv)) return rv;
 
   // 0: full URI
   // 1: scheme+host+port+path
   // 2: scheme+host+port
   int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy();
   int userReferrerXOriginTrimmingPolicy =
     gHttpHandler->ReferrerXOriginTrimmingPolicy();
@@ -1866,31 +1868,34 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     switch (userReferrerTrimmingPolicy) {
       case 1: { // scheme+host+port+path
         nsCOMPtr<nsIURL> url(do_QueryInterface(clone));
         if (url) {
           nsAutoCString path;
           rv = url->GetFilePath(path);
           if (NS_FAILED(rv)) return rv;
           spec.Append(path);
-          rv = url->SetQuery(EmptyCString());
-          if (NS_FAILED(rv)) return rv;
-          rv = url->SetRef(EmptyCString());
+          rv = NS_MutateURI(url)
+                 .SetQuery(EmptyCString())
+                 .SetRef(EmptyCString())
+                 .Finalize(clone);
           if (NS_FAILED(rv)) return rv;
           break;
         }
         // No URL, so fall through to truncating the path and any query/ref off
         // as well.
       }
       MOZ_FALLTHROUGH;
       default: // (Pref limited to [0,2] enforced by clamp, MOZ_CRASH overkill.)
       case 2: // scheme+host+port+/
         spec.AppendLiteral("/");
         // This nukes any query/ref present as well in the case of nsStandardURL
-        rv = clone->SetPathQueryRef(EmptyCString());
+        rv = NS_MutateURI(clone)
+               .SetPathQueryRef(EmptyCString())
+               .Finalize(clone);
         if (NS_FAILED(rv)) return rv;
         break;
     }
   } else {
     // use the full URI
     rv = clone->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) return rv;
   }
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -8,28 +8,30 @@
 #include "DateTimeFormat.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/intl/LocaleService.h"
 #include "nsNetUtil.h"
 #include "netCore.h"
 #include "nsStringStream.h"
 #include "nsIFile.h"
 #include "nsIFileURL.h"
+#include "nsIURIMutator.h"
 #include "nsEscape.h"
 #include "nsIDirIndex.h"
 #include "nsURLHelper.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefLocalizedString.h"
 #include "nsIStringBundle.h"
 #include "nsITextToSubURI.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsString.h"
 #include <algorithm>
 #include "nsIChannel.h"
+#include "mozilla/Unused.h"
 
 using mozilla::intl::LocaleService;
 
 NS_IMPL_ISUPPORTS(nsIndexedToHTML,
                   nsIDirIndexListener,
                   nsIStreamConverter,
                   nsIRequestObserver,
                   nsIStreamListener)
@@ -244,17 +246,19 @@ nsIndexedToHTML::DoOnStartRequest(nsIReq
         // default behavior for other protocols is to assume the channel's
         // URL references a directory ending in '/' -- fixup if necessary.
         nsAutoCString path;
         rv = uri->GetPathQueryRef(path);
         if (NS_FAILED(rv)) return rv;
         if (baseUri.Last() != '/') {
             baseUri.Append('/');
             path.Append('/');
-            uri->SetPathQueryRef(path);
+            mozilla::Unused << NS_MutateURI(uri)
+                                 .SetPathQueryRef(path)
+                                 .Finalize(uri);
         }
         if (!path.EqualsLiteral("/")) {
             rv = uri->Resolve(NS_LITERAL_CSTRING(".."), parentStr);
             if (NS_FAILED(rv)) return rv;
         }
     }
 
     buffer.AppendLiteral("<style type=\"text/css\">\n"
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -497,40 +497,40 @@ 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.pathQueryRef = pathWithSuffix;
+      testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
       testURI.ref = "";
       do_check_uri_eq(testURI, refURIWithoutSuffix);
       do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
       // Also: make sure that clearing .pathQueryRef also clears .ref
-      testURI.pathQueryRef = pathWithSuffix;
+      testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
       do_info("testing that clearing path from " + 
               pathWithSuffix + " also clears .ref");
-      testURI.pathQueryRef = "";
+      testURI = testURI.mutate().setPathQueryRef("").finalize();
       Assert.equal(testURI.ref, "");
     }
   }
 }
 
 // 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",
-                           "host", "port", "pathQueryRef", "query", "ref"];
+                           "host", "port", "query", "ref"];
 
   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
@@ -598,40 +598,40 @@ 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.pathQueryRef = pathWithSuffix;
+      testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
       testURI.ref = "";
       do_check_uri_eq(testURI, refURIWithoutSuffix);
       do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
       // Also: make sure that clearing .pathQueryRef also clears .ref
-      testURI.pathQueryRef = pathWithSuffix;
+      testURI = testURI.mutate().setPathQueryRef(pathWithSuffix).finalize();
       do_info("testing that clearing path from " + 
               pathWithSuffix + " also clears .ref");
-      testURI.pathQueryRef = "";
+      testURI = testURI.mutate().setPathQueryRef("").finalize();
       Assert.equal(testURI.ref, "");
     }
   }
 }
 
 // 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", "userPass", "username", "password",
-                           "host", "port", "pathQueryRef", "query", "ref"];
+                           "host", "port", "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
@@ -50,18 +50,18 @@ add_test(function test_setEmptyPath()
      ["http://example.com/a", "http://example.com/tests/dom/tests"],
      ["http://example.com:80/a", "http://example.com/tests/dom/tests"],
     ].map(pairToURLs);
 
   for (var [provided, target] of pairs)
   {
     symmetricEquality(false, target, provided);
 
-    provided.pathQueryRef = "";
-    target.pathQueryRef = "";
+    provided = provided.mutate().setPathQueryRef("").finalize();
+    target = target.mutate().setPathQueryRef("").finalize();
 
     Assert.equal(provided.spec, target.spec);
     symmetricEquality(true, target, provided);
   }
   run_next_test();
 });
 
 add_test(function test_setQuery()
@@ -297,27 +297,28 @@ 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", "userPass", "username",
-                    "password", "host", "pathQueryRef", "ref",
+                    "password", "host", "ref",
                     "query", "filePath"];
   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: "setHostPort", qi: Ci.nsIURIMutator },
+    { method: "setPathQueryRef", 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/,
--- a/toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_mapURIToAddonID.js
@@ -309,18 +309,19 @@ function run_test_invalidarg() {
 function run_test_provider() {
   restartManager();
 
   const provider = new TestProvider(Components.results.NS_ERROR_NOT_AVAILABLE);
   AddonManagerPrivate.registerProvider(provider);
 
   check_mapping(provider.uri, provider.id);
 
-  let u2 = provider.uri.clone();
-  u2.pathQueryRef = "notmapped";
+  let u2 = provider.uri.mutate()
+                       .setPathQueryRef("notmapped")
+                       .finalize();
   Assert.equal(AddonManager.mapURIToAddonID(u2), null);
 
   AddonManagerPrivate.unregisterProvider(provider);
 
   run_test_provider_nomap();
 }
 
 // Tests that custom providers are correctly handled, even not implementing