Bug 436344 - Allow filtering of proxies by channel. r=mcmanus
authorArthur Edelstein <arthuredelstein@gmail.com>
Wed, 21 Jan 2015 21:13:00 +0100
changeset 225110 04c4527643394e15b8fac110ecd9c5cfe4cb07b8
parent 225109 61c35f7b9aaafed3cc72e9a5e196d0b3ca5107ec
child 225111 da5679ebd7590fcb7e985b8940f3d8e3b9a9f9a2
push id10941
push usercbook@mozilla.com
push dateThu, 22 Jan 2015 13:47:01 +0000
treeherderfx-team@dcf91cfa4580 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs436344
milestone38.0a1
Bug 436344 - Allow filtering of proxies by channel. r=mcmanus
dom/plugins/base/moz.build
dom/plugins/base/nsPluginHost.cpp
netwerk/base/nsIOService.cpp
netwerk/base/nsIProtocolProxyCallback.idl
netwerk/base/nsIProtocolProxyFilter.idl
netwerk/base/nsIProtocolProxyService.idl
netwerk/base/nsIProtocolProxyService2.idl
netwerk/base/nsProtocolProxyService.cpp
netwerk/base/nsProtocolProxyService.h
netwerk/protocol/ftp/nsFtpConnectionThread.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsIHttpChannelInternal.idl
netwerk/protocol/websocket/WebSocketChannel.cpp
netwerk/test/unit/test_protocolproxyservice.js
toolkit/components/passwordmgr/test/test_bug_627616.html
toolkit/components/passwordmgr/test/test_prompt.html
toolkit/components/passwordmgr/test/test_prompt_async.html
--- a/dom/plugins/base/moz.build
+++ b/dom/plugins/base/moz.build
@@ -92,16 +92,17 @@ FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/plugins/ipc',
     '/layout/generic',
     '/layout/xul',
+    '/netwerk/base',
     '/widget',
     '/widget/android',
     '/widget/cocoa',
     '/xpcom/base',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     LOCAL_INCLUDES += [
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -25,17 +25,17 @@
 #include "nsIUploadChannel.h"
 #include "nsIByteRangeRequest.h"
 #include "nsIStreamListener.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIURL.h"
 #include "nsTArray.h"
 #include "nsReadableUtils.h"
-#include "nsIProtocolProxyService2.h"
+#include "nsProtocolProxyService.h"
 #include "nsIStreamConverterService.h"
 #include "nsIFile.h"
 #if defined(XP_MACOSX)
 #include "nsILocalFileMac.h"
 #endif
 #include "nsISeekableStream.h"
 #include "nsNetUtil.h"
 #include "nsIProgressEventSink.h"
@@ -601,42 +601,40 @@ nsresult nsPluginHost::PostURL(nsISuppor
 
 nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result)
 {
   if (!url || !result) {
     return NS_ERROR_INVALID_ARG;
   }
   nsresult res;
 
-  nsCOMPtr<nsIURI> uriIn;
-  nsCOMPtr<nsIProtocolProxyService> proxyService;
-  nsCOMPtr<nsIProtocolProxyService2> proxyService2;
-  nsCOMPtr<nsIIOService> ioService;
-
-  proxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
+  nsCOMPtr<nsIProtocolProxyService> proxyService =
+    do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
   if (NS_FAILED(res) || !proxyService)
     return res;
 
-  proxyService2 = do_QueryInterface(proxyService, &res);
-  if (NS_FAILED(res) || !proxyService2)
-    return res;
-
-  ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
+  nsRefPtr<nsProtocolProxyService> rawProxyService = do_QueryObject(proxyService);
+  if (!rawProxyService) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
   if (NS_FAILED(res) || !ioService)
     return res;
 
-  // make an nsURI from the argument url
-  res = ioService->NewURI(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(uriIn));
+  // make a temporary channel from the argument url
+  nsCOMPtr<nsIChannel> tempChannel;
+  res = ioService->NewChannel(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(tempChannel));
   if (NS_FAILED(res))
     return res;
 
   nsCOMPtr<nsIProxyInfo> pi;
 
-  // Remove this with bug 778201
-  res = proxyService2->DeprecatedBlockingResolve(uriIn, 0, getter_AddRefs(pi));
+  // Remove this deprecated call in the future (see Bug 778201):
+  res = rawProxyService->DeprecatedBlockingResolve(tempChannel, 0, getter_AddRefs(pi));
   if (NS_FAILED(res))
     return res;
 
   nsAutoCString host, type;
   int32_t port = -1;
 
   // These won't fail, and even if they do... we'll be ok.
   if (pi) {
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -1502,45 +1502,51 @@ public:
 private:
     nsRefPtr<nsIInterfaceRequestor> mCallbacks;
     nsRefPtr<nsIOService>           mIOService;
 };
 
 NS_IMPL_ISUPPORTS(IOServiceProxyCallback, nsIProtocolProxyCallback)
 
 NS_IMETHODIMP
-IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIURI *aURI,
+IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
                                          nsIProxyInfo *pi, nsresult status)
 {
     // Checking proxy status for speculative connect
     nsAutoCString type;
     if (NS_SUCCEEDED(status) && pi &&
         NS_SUCCEEDED(pi->GetType(type)) &&
         !type.EqualsLiteral("direct")) {
         // proxies dont do speculative connect
         return NS_OK;
     }
 
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = channel->GetURI(getter_AddRefs(uri));
+    if (NS_FAILED(rv)) {
+        return NS_OK;
+    }
+
     nsAutoCString scheme;
-    nsresult rv = aURI->GetScheme(scheme);
+    rv = uri->GetScheme(scheme);
     if (NS_FAILED(rv))
         return NS_OK;
 
     nsCOMPtr<nsIProtocolHandler> handler;
     rv = mIOService->GetProtocolHandler(scheme.get(),
                                         getter_AddRefs(handler));
     if (NS_FAILED(rv))
         return NS_OK;
 
     nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
         do_QueryInterface(handler);
     if (!speculativeHandler)
         return NS_OK;
 
-    speculativeHandler->SpeculativeConnect(aURI,
+    speculativeHandler->SpeculativeConnect(uri,
                                            mCallbacks);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsIOService::SpeculativeConnect(nsIURI *aURI,
                                 nsIInterfaceRequestor *aCallbacks)
 {
@@ -1548,20 +1554,26 @@ nsIOService::SpeculativeConnect(nsIURI *
     // speculative connect should not be performed because the potential
     // reward is slim with tcp peers closely located to the browser.
     nsresult rv;
     nsCOMPtr<nsIProtocolProxyService> pps =
             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
     if (NS_FAILED(rv))
         return rv;
 
+    nsCOMPtr<nsIChannel> channel;
+    rv = NewChannelFromURI(aURI, getter_AddRefs(channel));
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
     nsCOMPtr<nsICancelable> cancelable;
     nsRefPtr<IOServiceProxyCallback> callback =
         new IOServiceProxyCallback(aCallbacks, this);
-    return pps->AsyncResolve(aURI, 0, callback, getter_AddRefs(cancelable));
+    return pps->AsyncResolve(channel, 0, callback, getter_AddRefs(cancelable));
 }
 
 void
 nsIOService::NotifyAppOfflineStatus(uint32_t appId, int32_t state)
 {
     MOZ_RELEASE_ASSERT(NS_IsMainThread(),
             "Should be called on the main thread");
 
--- a/netwerk/base/nsIProtocolProxyCallback.idl
+++ b/netwerk/base/nsIProtocolProxyCallback.idl
@@ -1,42 +1,42 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "nsISupports.idl"
 
-interface nsIURI;
+interface nsIChannel;
 interface nsIProxyInfo;
 interface nsICancelable;
 
 /**
  * This interface serves as a closure for nsIProtocolProxyService's
  * asyncResolve method.
  */
-[scriptable, uuid(a9967200-f95e-45c2-beb3-9b060d874bfd)]
+[scriptable, uuid(fbb6eff6-0cc2-4d99-8d6f-0a12b462bdeb)]
 interface nsIProtocolProxyCallback : nsISupports
 {
   /**
    * This method is called when proxy info is available or when an error
    * in the proxy resolution occurs.
    *
    * @param aRequest
    *        The value returned from asyncResolve.
-   * @param aURI
-   *        The URI passed to asyncResolve.
+   * @param aChannel
+   *        The channel passed to asyncResolve.
    * @param aProxyInfo
    *        The resulting proxy info or null if there is no associated proxy
    *        info for aURI.  As with the result of nsIProtocolProxyService's
    *        resolve method, a null result implies that a direct connection
    *        should be used.
    * @param aStatus
    *        The status of the callback.  This is a failure code if the request
    *        could not be satisfied, in which case the value of aStatus
    *        indicates the reason for the failure and aProxyInfo will be null.
    */
   void onProxyAvailable(in nsICancelable aRequest,
-                        in nsIURI aURI,
+                        in nsIChannel aChannel,
                         in nsIProxyInfo aProxyInfo,
                         in nsresult aStatus);
 };
--- a/netwerk/base/nsIProtocolProxyFilter.idl
+++ b/netwerk/base/nsIProtocolProxyFilter.idl
@@ -1,24 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "nsISupports.idl"
 
+interface nsIChannel;
 interface nsIProtocolProxyService;
 interface nsIProxyInfo;
 interface nsIURI;
 
 /**
  * This interface is used to apply filters to the proxies selected for a given
  * URI.  Use nsIProtocolProxyService::registerFilter to hook up instances of
- * this interface.
+ * this interface. See also nsIProtocolProxyChannelFilter.
  */
 [scriptable, uuid(f424abd3-32b4-456c-9f45-b7e3376cb0d1)]
 interface nsIProtocolProxyFilter : nsISupports
 {
   /**
    * This method is called to apply proxy filter rules for the given URI
    * and proxy object (or list of proxy objects).
    *
@@ -35,8 +36,39 @@ interface nsIProtocolProxyFilter : nsISu
    *         aProxy.  This can be just be aProxy if the filter chooses not to
    *         modify the proxy.  It can also be null to indicate that a direct
    *         connection should be used.  Use aProxyService.newProxyInfo to
    *         construct nsIProxyInfo objects.
    */
   nsIProxyInfo applyFilter(in nsIProtocolProxyService aProxyService,
                            in nsIURI aURI, in nsIProxyInfo aProxy);
 };
+
+/**
+ * This interface is used to apply filters to the proxies selected for a given
+ * channel.  Use nsIProtocolProxyService::registerChannelFilter to hook up instances of
+ * this interface. See also nsIProtocolProxyFilter.
+ */
+[scriptable, uuid(245b0880-82c5-4e6e-be6d-bc586aa55a90)]
+interface nsIProtocolProxyChannelFilter : nsISupports
+{
+  /**
+   * This method is called to apply proxy filter rules for the given channel
+   * and proxy object (or list of proxy objects).
+   *
+   * @param aProxyService
+   *        A reference to the Protocol Proxy Service.  This is passed so that
+   *        implementations may easily access methods such as newProxyInfo.
+   * @param aChannel
+   *        The channel for which these proxy settings apply.
+   * @param aProxy
+   *        The proxy (or list of proxies) that would be used by default for
+   *        the given channel. This may be null.
+   *
+   * @return The proxy (or list of proxies) that should be used in place of
+   *         aProxy. This can be just be aProxy if the filter chooses not to
+   *         modify the proxy. It can also be null to indicate that a direct
+   *         connection should be used.  Use aProxyService.newProxyInfo to
+   *         construct nsIProxyInfo objects.
+   */
+  nsIProxyInfo applyFilter(in nsIProtocolProxyService aProxyService,
+                           in nsIChannel aChannel, in nsIProxyInfo aProxy);
+};
--- a/netwerk/base/nsIProtocolProxyService.idl
+++ b/netwerk/base/nsIProtocolProxyService.idl
@@ -4,25 +4,26 @@
  * 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 "nsISupports.idl"
 
 interface nsICancelable;
 interface nsIProtocolProxyCallback;
 interface nsIProtocolProxyFilter;
+interface nsIProtocolProxyChannelFilter;
 interface nsIProxyInfo;
 interface nsIChannel;
 interface nsIURI;
 
 /**
  * nsIProtocolProxyService provides methods to access information about
  * various network proxies.
  */
-[scriptable, uuid(e77c642b-026f-41ce-9b23-f829a6e3f300)]
+[scriptable, uuid(ab363090-c331-489f-aabb-7fe4481795b8)]
 interface nsIProtocolProxyService : nsISupports
 {
     /** Flag 1 << 0 is unused **/
 
     /**
      * When the proxy configuration is manual this flag may be passed to the
      * resolve and asyncResolve methods to request to prefer the SOCKS proxy
      * to HTTP ones.
@@ -59,21 +60,21 @@ interface nsIProtocolProxyService : nsIS
      * When the proxy configuration is manual this flag may be passed to the
      * resolve and asyncResolve methods to that all methods will be tunneled via
      * CONNECT through the http proxy.
      */
     const unsigned long RESOLVE_ALWAYS_TUNNEL = (1 << 4);
 
     /**
      * This method returns via callback a nsIProxyInfo instance that identifies
-     * a proxy to be used for loading the given URI.  Otherwise, this method returns
+     * a proxy to be used for the given channel.  Otherwise, this method returns
      * null indicating that a direct connection should be used.
      *
-     * @param aURI
-     *        The URI to test.
+     * @param aChannel
+     *        The channel for which a proxy is to be found.
      * @param aFlags
      *        A bit-wise combination of the RESOLVE_ flags defined above.  Pass
      *        0 to specify the default behavior.  Any additional bits that do
      *        not correspond to a RESOLVE_ flag are reserved for future use.
      * @param aCallback
      *        The object to be notified when the result is available.
      *
      * @return An object that can be used to cancel the asychronous operation.
@@ -89,17 +90,17 @@ interface nsIProtocolProxyService : nsIS
      * nsIChannel to the given URI that uses the specified proxy.
      *
      * NOTE: However, if the nsIProxyInfo type is "http", then it means that
      * the given URI should be loaded using the HTTP protocol handler, which
      * also supports nsIProxiedProtocolHandler.
      *
      * @see nsIProxiedProtocolHandler::newProxiedChannel 
      */
-    nsICancelable asyncResolve(in nsIURI aURI, in unsigned long aFlags,
+    nsICancelable asyncResolve(in nsIChannel aChannel, in unsigned long aFlags,
                                in nsIProtocolProxyCallback aCallback);
 
     /**
      * This method may be called to construct a nsIProxyInfo instance from
      * the given parameters.  This method may be useful in conjunction with
      * nsISocketTransportService::createTransport for creating, for example,
      * a SOCKS connection.
      *
@@ -187,24 +188,45 @@ interface nsIProtocolProxyService : nsIS
      * problems.  It is recommended that any extensions that choose to call
      * this method make their position value configurable at runtime (perhaps
      * via the preferences service).
      */
     void registerFilter(in nsIProtocolProxyFilter aFilter,
                         in unsigned long aPosition);
 
     /**
+     * Similar to registerFilter, but accepts an nsIProtocolProxyChannelFilter,
+     * which selects proxies according to channel rather than URI.
+     *
+     * @param aFilter
+     *        The nsIProtocolProxyChannelFilter instance to be registered.
+     * @param aPosition
+     *        The position of the filter.
+     */
+    void registerChannelFilter(in nsIProtocolProxyChannelFilter aFilter,
+                               in unsigned long aPosition);
+
+    /**
      * This method may be used to unregister a proxy filter instance.  All
      * filters will be automatically unregistered at XPCOM shutdown.
      *
      * @param aFilter
      *        The nsIProtocolProxyFilter instance to be unregistered.
      */
     void unregisterFilter(in nsIProtocolProxyFilter aFilter);
 
+    /**
+     * This method may be used to unregister a proxy channel filter instance.  All
+     * filters will be automatically unregistered at XPCOM shutdown.
+     *
+     * @param aFilter
+     *        The nsIProtocolProxyChannelFilter instance to be unregistered.
+     */
+    void unregisterChannelFilter(in nsIProtocolProxyChannelFilter aFilter);
+
      /**
       * These values correspond to the possible integer values for the
       * network.proxy.type preference.
       */ 
      const unsigned long PROXYCONFIG_DIRECT   = 0;
      const unsigned long PROXYCONFIG_MANUAL   = 1;
      const unsigned long PROXYCONFIG_PAC      = 2;
      const unsigned long PROXYCONFIG_WPAD     = 4;
--- a/netwerk/base/nsIProtocolProxyService2.idl
+++ b/netwerk/base/nsIProtocolProxyService2.idl
@@ -4,35 +4,26 @@
  * 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 "nsIProtocolProxyService.idl"
 
 /**
  * An extension of nsIProtocolProxyService
  */
-[scriptable, uuid(bb52e571-4a0e-4363-83d0-52034910dd14)]
+[scriptable, uuid(b2e5b2c0-e21e-4845-b336-be6d60a38951)]
 interface nsIProtocolProxyService2 : nsIProtocolProxyService
 {
   /**
    * Call this method to cause the PAC file (if any is configured) to be
    * reloaded.  The PAC file is loaded asynchronously.
    */
   void reloadPAC();
 
-  /**
-   * This exists so Java(tm) can migrate to an asynchronous interface.
-   * Do not use this unless you are the plugin interface, and even then you
-   * ought to feel horribly guilty because you will create main thread jank.
-   *
-   * No documentation - it is deprecated!
-   **/
-  nsIProxyInfo deprecatedBlockingResolve(in nsIURI aURI, in unsigned long aFlags);
-
     /**
      * This method is identical to asyncResolve() except it may execute the
      * callback function immediately (i.e from the stack of asyncResolve2()) if
      * it is immediately ready to run. The nsICancelable return value will be
      * null in that case.
      */
-  nsICancelable asyncResolve2(in nsIURI aURI, in unsigned long aFlags,
+  nsICancelable asyncResolve2(in nsIChannel aChannel, in unsigned long aFlags,
                               in nsIProtocolProxyCallback aCallback);
 };
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -9,32 +9,34 @@
 
 #include "nsProtocolProxyService.h"
 #include "nsProxyInfo.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIIOService.h"
 #include "nsIObserverService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIProtocolProxyCallback.h"
+#include "nsIChannel.h"
 #include "nsICancelable.h"
 #include "nsIDNSService.h"
 #include "nsPIDNSService.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsThreadUtils.h"
 #include "nsString.h"
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "prnetdb.h"
 #include "nsPACMan.h"
 #include "nsProxyRelease.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/CondVar.h"
 #include "nsISystemProxySettings.h"
 #include "nsINetworkLinkService.h"
+#include "nsIHttpChannelInternal.h"
 
 //----------------------------------------------------------------------------
 
 namespace mozilla {
   extern const char kProxyType_HTTP[];
   extern const char kProxyType_HTTPS[];
   extern const char kProxyType_SOCKS[];
   extern const char kProxyType_SOCKS4[];
@@ -63,54 +65,77 @@ using namespace mozilla;
 struct nsProtocolInfo {
     nsAutoCString scheme;
     uint32_t flags;
     int32_t defaultPort;
 };
 
 //----------------------------------------------------------------------------
 
+// Return the channel's proxy URI, or if it doesn't exist, the
+// channel's main URI.
+static nsresult
+GetProxyURI(nsIChannel *channel, nsIURI **aOut)
+{
+  nsresult rv;
+  nsCOMPtr<nsIURI> proxyURI;
+  nsCOMPtr<nsIHttpChannelInternal> httpChannel(do_QueryInterface(channel));
+  if (httpChannel) {
+    rv = httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
+  }
+  if (!proxyURI) {
+    rv = channel->GetURI(getter_AddRefs(proxyURI));
+  }
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  proxyURI.forget(aOut);
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
 // The nsPACManCallback portion of this implementation should be run
 // on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
 // a true mainThreadResponse parameter.
 class nsAsyncResolveRequest MOZ_FINAL : public nsIRunnable
                                       , public nsPACManCallback
                                       , public nsICancelable
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
 
-    nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIURI *uri,
+    nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
                           uint32_t aResolveFlags,
                           nsIProtocolProxyCallback *callback)
         : mStatus(NS_OK)
         , mDispatched(false)
         , mResolveFlags(aResolveFlags)
         , mPPS(pps)
         , mXPComPPS(pps)
-        , mURI(uri)
+        , mChannel(channel)
         , mCallback(callback)
     {
         NS_ASSERTION(mCallback, "null callback");
     }
 
 private:
     ~nsAsyncResolveRequest()
     {
         if (!NS_IsMainThread()) {
             // these xpcom pointers might need to be proxied back to the
             // main thread to delete safely, but if this request had its
             // callbacks called normally they will all be null and this is a nop
 
             nsCOMPtr<nsIThread> mainThread;
             NS_GetMainThread(getter_AddRefs(mainThread));
 
-            if (mURI) {
-                nsIURI *forgettable;
-                mURI.forget(&forgettable);
+            if (mChannel) {
+                nsIChannel *forgettable;
+                mChannel.forget(&forgettable);
                 NS_ProxyRelease(mainThread, forgettable, false);
             }
 
             if (mCallback) {
                 nsIProtocolProxyCallback *forgettable;
                 mCallback.forget(&forgettable);
                 NS_ProxyRelease(mainThread, forgettable, false);
             }
@@ -206,76 +231,81 @@ private:
             mPACString = NS_LITERAL_CSTRING("DIRECT;");
             mStatus = NS_OK;
         }
 
         // Generate proxy info from the PAC string if appropriate
         if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
             mPPS->ProcessPACString(mPACString, mResolveFlags,
                                    getter_AddRefs(mProxyInfo));
+            nsCOMPtr<nsIURI> proxyURI;
+            GetProxyURI(mChannel, getter_AddRefs(proxyURI));
 
             // Now apply proxy filters
             nsProtocolInfo info;
-            mStatus = mPPS->GetProtocolInfo(mURI, &info);
+            mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
             if (NS_SUCCEEDED(mStatus))
-                mPPS->ApplyFilters(mURI, info, mProxyInfo);
+                mPPS->ApplyFilters(mChannel, info, mProxyInfo);
             else
                 mProxyInfo = nullptr;
 
             LOG(("pac thread callback %s\n", mPACString.get()));
             if (NS_SUCCEEDED(mStatus))
                 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
-            mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus);
+            mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
         }
         else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
             LOG(("pac thread callback indicates new pac file load\n"));
 
+            nsCOMPtr<nsIURI> proxyURI;
+            GetProxyURI(mChannel, getter_AddRefs(proxyURI));
+
             // trigger load of new pac url
             nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
             if (NS_SUCCEEDED(rv)) {
                 // now that the load is triggered, we can resubmit the query
                 nsRefPtr<nsAsyncResolveRequest> newRequest =
-                    new nsAsyncResolveRequest(mPPS, mURI, mResolveFlags, mCallback);
-                rv = mPPS->mPACMan->AsyncGetProxyForURI(mURI, newRequest, true);
+                    new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
+                rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI, newRequest, true);
             }
 
             if (NS_FAILED(rv))
-                mCallback->OnProxyAvailable(this, mURI, nullptr, rv);
+                mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
 
             // do not call onproxyavailable() in SUCCESS case - the newRequest will
             // take care of that
         }
         else {
             LOG(("pac thread callback did not provide information %X\n", mStatus));
             if (NS_SUCCEEDED(mStatus))
                 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
-            mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus);
+            mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
         }
 
         // We are on the main thread now and don't need these any more so
         // release them to avoid having to proxy them back to the main thread
         // in the dtor
         mCallback = nullptr;  // in case the callback holds an owning ref to us
         mPPS = nullptr;
         mXPComPPS = nullptr;
-        mURI = nullptr;
+        mChannel = nullptr;
         mProxyInfo = nullptr;
     }
 
 private:
 
     nsresult  mStatus;
     nsCString mPACString;
     nsCString mPACURL;
     bool      mDispatched;
     uint32_t  mResolveFlags;
 
     nsProtocolProxyService            *mPPS;
     nsCOMPtr<nsIProtocolProxyService>  mXPComPPS;
-    nsCOMPtr<nsIURI>                   mURI;
+    nsCOMPtr<nsIChannel>               mChannel;
     nsCOMPtr<nsIProtocolProxyCallback> mCallback;
     nsCOMPtr<nsIProxyInfo>             mProxyInfo;
 };
 
 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
 
 //----------------------------------------------------------------------------
 
@@ -1093,67 +1123,71 @@ private:
 
     nsresult  mStatus;
     nsCString mPACString;
     nsCString mPACURL;
     bool      mCompleted;
 };
 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
 
-// nsIProtocolProxyService2
-NS_IMETHODIMP
-nsProtocolProxyService::DeprecatedBlockingResolve(nsIURI *aURI,
+// nsProtocolProxyService
+nsresult
+nsProtocolProxyService::DeprecatedBlockingResolve(nsIChannel *aChannel,
                                                   uint32_t aFlags,
                                                   nsIProxyInfo **retval)
 {
-    NS_ENSURE_ARG_POINTER(aURI);
+    NS_ENSURE_ARG_POINTER(aChannel);
+
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = GetProxyURI(aChannel, getter_AddRefs(uri));
+    if (NS_FAILED(rv)) return rv;
 
     nsProtocolInfo info;
-    nsresult rv = GetProtocolInfo(aURI, &info);
+    rv = GetProtocolInfo(uri, &info);
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIProxyInfo> pi;
     bool usePACThread;
 
     // SystemProxySettings and PAC files can block the main thread
     // but if neither of them are in use, we can just do the work
     // right here and directly invoke the callback
 
-    rv = Resolve_Internal(aURI, info, aFlags, &usePACThread, getter_AddRefs(pi));
+    rv = Resolve_Internal(aChannel, info, aFlags, &usePACThread, getter_AddRefs(pi));
     if (NS_FAILED(rv))
         return rv;
 
     if (!usePACThread || !mPACMan) {
-        ApplyFilters(aURI, info, pi);
+        ApplyFilters(aChannel, info, pi);
         pi.forget(retval);
         return NS_OK;
     }
 
     // Use the PAC thread to do the work, so we don't have to reimplement that
     // code, but block this thread on that completion.
     nsRefPtr<nsAsyncBridgeRequest> ctx = new nsAsyncBridgeRequest();
     ctx->Lock();
-    if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForURI(aURI, ctx, false))) {
+    if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForURI(uri, ctx, false))) {
         // this can really block the main thread, so cap it at 3 seconds
        ctx->Wait();
     }
     ctx->Unlock();
     if (!ctx->mCompleted)
         return NS_ERROR_FAILURE;
     if (NS_FAILED(ctx->mStatus))
         return ctx->mStatus;
 
     // pretty much duplicate real DoCallback logic
 
     // Generate proxy info from the PAC string if appropriate
     if (!ctx->mPACString.IsEmpty()) {
         LOG(("sync pac thread callback %s\n", ctx->mPACString.get()));
         ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi));
-        ApplyFilters(aURI, info, pi);
+        ApplyFilters(aChannel, info, pi);
         pi.forget(retval);
         return NS_OK;
     }
 
     if (!ctx->mPACURL.IsEmpty()) {
         NS_WARNING("sync pac thread callback indicates new pac file load\n");
         // This is a problem and is one of the reasons this blocking interface
         // is deprecated. The main loop needs to spin to make this reload happen. So
@@ -1167,47 +1201,51 @@ nsProtocolProxyService::DeprecatedBlocki
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     *retval = nullptr;
     return NS_OK;
 }
 
 nsresult
-nsProtocolProxyService::AsyncResolveInternal(nsIURI *uri, uint32_t flags,
+nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
                                              nsIProtocolProxyCallback *callback,
                                              nsICancelable **result,
                                              bool isSyncOK)
 {
-    NS_ENSURE_ARG_POINTER(uri);
+    NS_ENSURE_ARG_POINTER(channel);
     NS_ENSURE_ARG_POINTER(callback);
 
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
+    if (NS_FAILED(rv)) return rv;
+
     *result = nullptr;
     nsRefPtr<nsAsyncResolveRequest> ctx =
-        new nsAsyncResolveRequest(this, uri, flags, callback);
+        new nsAsyncResolveRequest(this, channel, flags, callback);
 
     nsProtocolInfo info;
-    nsresult rv = GetProtocolInfo(uri, &info);
+    rv = GetProtocolInfo(uri, &info);
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIProxyInfo> pi;
     bool usePACThread;
 
     // SystemProxySettings and PAC files can block the main thread
     // but if neither of them are in use, we can just do the work
     // right here and directly invoke the callback
 
-    rv = Resolve_Internal(uri, info, flags, &usePACThread, getter_AddRefs(pi));
+    rv = Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
     if (NS_FAILED(rv))
         return rv;
 
     if (!usePACThread || !mPACMan) {
         // we can do it locally
-        ApplyFilters(uri, info, pi);
+        ApplyFilters(channel, info, pi);
         ctx->SetResult(NS_OK, pi);
         if (isSyncOK) {
             ctx->Run();
             return NS_OK;
         }
 
         rv = ctx->DispatchCallback();
         if (NS_SUCCEEDED(rv))
@@ -1220,29 +1258,29 @@ nsProtocolProxyService::AsyncResolveInte
     rv = mPACMan->AsyncGetProxyForURI(uri, ctx, true);
     if (NS_SUCCEEDED(rv))
         ctx.forget(result);
     return rv;
 }
 
 // nsIProtocolProxyService
 NS_IMETHODIMP
-nsProtocolProxyService::AsyncResolve2(nsIURI *uri, uint32_t flags,
+nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags,
                                       nsIProtocolProxyCallback *callback,
                                       nsICancelable **result)
 {
-    return AsyncResolveInternal(uri, flags, callback, result, true);
+    return AsyncResolveInternal(channel, flags, callback, result, true);
 }
 
 NS_IMETHODIMP
-nsProtocolProxyService::AsyncResolve(nsIURI *uri, uint32_t flags,
+nsProtocolProxyService::AsyncResolve(nsIChannel *channel, uint32_t flags,
                                      nsIProtocolProxyCallback *callback,
                                      nsICancelable **result)
 {
-    return AsyncResolveInternal(uri, flags, callback, result, false);
+    return AsyncResolveInternal(channel, flags, callback, result, false);
 }
 
 NS_IMETHODIMP
 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
                                      const nsACString &aHost,
                                      int32_t aPort,
                                      uint32_t aFlags,
                                      uint32_t aFailoverTimeout,
@@ -1305,26 +1343,19 @@ nsProtocolProxyService::GetFailoverForPr
     LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
         pi->mType, pi->mHost.get(), pi->mPort,
         pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
 
     NS_ADDREF(*aResult = pi->mNext);
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
-                                       uint32_t position)
+nsresult
+nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position)
 {
-    UnregisterFilter(filter);  // remove this filter if we already have it
-
-    FilterLink *link = new FilterLink(position, filter);
-    if (!link)
-        return NS_ERROR_OUT_OF_MEMORY;
-
     if (!mFilters) {
         mFilters = link;
         return NS_OK;
     }
 
     // insert into mFilters in sorted order
     FilterLink *last = nullptr;
     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
@@ -1342,41 +1373,79 @@ nsProtocolProxyService::RegisterFilter(n
         last = iter;
     }
     // our position is equal to or greater than the last link in the list
     last->next = link;
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter)
+nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
+                                       uint32_t position)
 {
-    // QI to nsISupports so we can safely test object identity.
-    nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
+    UnregisterFilter(filter); // remove this filter if we already have it
+
+    FilterLink *link = new FilterLink(position, filter);
+    if (!link) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+    return InsertFilterLink(link, position);
+}
 
+NS_IMETHODIMP
+nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter,
+                                              uint32_t position)
+{
+    UnregisterChannelFilter(channelFilter);  // remove this filter if we already have it
+
+    FilterLink *link = new FilterLink(position, channelFilter);
+    if (!link) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+    return InsertFilterLink(link, position);
+}
+
+nsresult
+nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject)
+{
     FilterLink *last = nullptr;
     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
         nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
-        if (object == givenObject) {
+        nsCOMPtr<nsISupports> object2 = do_QueryInterface(iter->channelFilter);
+        if (object == givenObject || object2 == givenObject) {
             if (last)
                 last->next = iter->next;
             else
                 mFilters = iter->next;
             iter->next = nullptr;
             delete iter;
             return NS_OK;
         }
         last = iter;
     }
 
     // No need to throw an exception in this case.
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) {
+    // QI to nsISupports so we can safely test object identity.
+    nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
+    return RemoveFilterLink(givenObject);
+}
+
+NS_IMETHODIMP
+nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) {
+    // QI to nsISupports so we can safely test object identity.
+    nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
+    return RemoveFilterLink(givenObject);
+}
+
+NS_IMETHODIMP
 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType)
 {
   *aProxyConfigType = mProxyConfig;
   return NS_OK;
 }
 
 void
 nsProtocolProxyService::LoadHostFilters(const char *filters)
@@ -1573,33 +1642,37 @@ nsProtocolProxyService::NewProxyInfo_Int
         ? mFailedProxyTimeout : aFailoverTimeout;
     failover.swap(proxyInfo->mNext);
 
     NS_ADDREF(*aResult = proxyInfo);
     return NS_OK;
 }
 
 nsresult
-nsProtocolProxyService::Resolve_Internal(nsIURI *uri,
+nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
                                          const nsProtocolInfo &info,
                                          uint32_t flags,
                                          bool *usePACThread,
                                          nsIProxyInfo **result)
 {
-    NS_ENSURE_ARG_POINTER(uri);
+    NS_ENSURE_ARG_POINTER(channel);
     nsresult rv = SetupPACThread();
     if (NS_FAILED(rv))
         return rv;
 
     *usePACThread = false;
     *result = nullptr;
 
     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
         return NS_OK;  // Can't proxy this (filters may not override)
 
+    nsCOMPtr<nsIURI> uri;
+    rv = GetProxyURI(channel, getter_AddRefs(uri));
+    if (NS_FAILED(rv)) return rv;
+
     // See bug #586908.
     // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
     // here means that we will not use a proxy for this connection.
     if (mPACMan && mPACMan->IsPACURI(uri))
         return NS_OK;
 
     bool mainThreadOnly;
     if (mSystemProxySettings &&
@@ -1759,34 +1832,43 @@ nsProtocolProxyService::MaybeDisableDNSP
     if (!pdns)
         return;
 
     // We lose the prefetch optimization for the life of the dns service.
     pdns->SetPrefetchEnabled(false);
 }
 
 void
-nsProtocolProxyService::ApplyFilters(nsIURI *uri, const nsProtocolInfo &info,
+nsProtocolProxyService::ApplyFilters(nsIChannel *channel,
+                                     const nsProtocolInfo &info,
                                      nsIProxyInfo **list)
 {
     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
         return;
 
     // We prune the proxy list prior to invoking each filter.  This may be
     // somewhat inefficient, but it seems like a good idea since we want each
     // filter to "see" a valid proxy list.
 
     nsresult rv;
     nsCOMPtr<nsIProxyInfo> result;
 
     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
         PruneProxyInfo(info, list);
-
-        rv = iter->filter->ApplyFilter(this, uri, *list,
-                                       getter_AddRefs(result));
+        if (iter->filter) {
+          nsCOMPtr<nsIURI> uri;
+          rv = GetProxyURI(channel, getter_AddRefs(uri));
+          if (uri) {
+            rv = iter->filter->ApplyFilter(this, uri, *list,
+                                           getter_AddRefs(result));
+          }
+        } else if (iter->channelFilter) {
+          rv = iter->channelFilter->ApplyFilter(this, channel, *list,
+                                                getter_AddRefs(result));
+        }
         if (NS_FAILED(rv))
             continue;
         result.swap(*list);
     }
 
     PruneProxyInfo(info, list);
 }
 
--- a/netwerk/base/nsProtocolProxyService.h
+++ b/netwerk/base/nsProtocolProxyService.h
@@ -22,28 +22,39 @@
 typedef nsDataHashtable<nsCStringHashKey, uint32_t> nsFailedProxyTable;
 
 class nsProxyInfo;
 struct nsProtocolInfo;
 class nsIPrefBranch;
 class nsISystemProxySettings;
 class nsPACMan;
 
+// CID for the nsProtocolProxyService class
+// 091eedd8-8bae-4fe3-ad62-0c87351e640d
+#define NS_PROTOCOL_PROXY_SERVICE_IMPL_CID        \
+{ 0x091eedd8, 0x8bae, 0x4fe3, \
+        { 0xad, 0x62, 0x0c, 0x87, 0x35, 0x1e, 0x64, 0x0d } }
+
 class nsProtocolProxyService MOZ_FINAL : public nsIProtocolProxyService2
                                        , public nsIObserver
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIPROTOCOLPROXYSERVICE2
     NS_DECL_NSIPROTOCOLPROXYSERVICE
     NS_DECL_NSIOBSERVER
 
+    NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROTOCOL_PROXY_SERVICE_IMPL_CID)
+
     nsProtocolProxyService();
 
     nsresult Init();
+    nsresult DeprecatedBlockingResolve(nsIChannel *aChannel,
+                                       uint32_t aFlags,
+                                       nsIProxyInfo **retval);
 
 protected:
     friend class nsAsyncResolveRequest;
 
     ~nsProtocolProxyService();
 
     /**
      * This method is called whenever a preference may have changed or
@@ -187,58 +198,58 @@ protected:
                                                nsIProxyInfo **result);
 
     /**
      * This method is an internal version of Resolve that does not query PAC.
      * It performs all of the built-in processing, and reports back to the
      * caller with either the proxy info result or a flag to instruct the
      * caller to use PAC instead.
      *
-     * @param uri
-     *        The URI to test.
+     * @param channel
+     *        The channel to test.
      * @param info
      *        Information about the URI's protocol.
      * @param flags
      *        The flags passed to either the resolve or the asyncResolve method.
      * @param usePAC
      *        If this flag is set upon return, then PAC should be queried to
      *        resolve the proxy info.
      * @param result
      *        The resulting proxy info or null.
      */
-    nsresult Resolve_Internal(nsIURI *uri,
+    nsresult Resolve_Internal(nsIChannel *channel,
                                           const nsProtocolInfo &info,
                                           uint32_t flags,
                                           bool *usePAC, 
                                           nsIProxyInfo **result);
 
     /**
      * This method applies the registered filters to the given proxy info
      * list, and returns a possibly modified list.
      *
-     * @param uri
-     *        The URI corresponding to this proxy info list.
+     * @param channel
+     *        The channel corresponding to this proxy info list.
      * @param info
      *        Information about the URI's protocol.
      * @param proxyInfo
      *        The proxy info list to be modified.  This is an inout param.
      */
-    void ApplyFilters(nsIURI *uri, const nsProtocolInfo &info,
+    void ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info,
                                   nsIProxyInfo **proxyInfo);
 
     /**
      * This method is a simple wrapper around ApplyFilters that takes the
      * proxy info list inout param as a nsCOMPtr.
      */
-    inline void ApplyFilters(nsIURI *uri, const nsProtocolInfo &info,
+    inline void ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info,
                              nsCOMPtr<nsIProxyInfo> &proxyInfo)
     {
       nsIProxyInfo *pi = nullptr;
       proxyInfo.swap(pi);
-      ApplyFilters(uri, info, &pi);
+      ApplyFilters(channel, info, &pi);
       proxyInfo.swap(pi);
     }
 
     /**
      * This method prunes out disabled and disallowed proxies from a given
      * proxy info list.
      *
      * @param info
@@ -313,29 +324,37 @@ protected:
             : is_ipaddr(false)
             { /* other members intentionally uninitialized */ }
        ~HostInfo() {
             if (!is_ipaddr && name.host)
                 nsMemory::Free(name.host);
         }
     };
 
-    // This structure is allocated for each registered nsIProtocolProxyFilter.
+    // An instance of this struct is allocated for each registered
+    // nsIProtocolProxyFilter and each nsIProtocolProxyChannelFilter.
     struct FilterLink {
       struct FilterLink                *next;
       uint32_t                          position;
-      nsCOMPtr<nsIProtocolProxyFilter>  filter;
-
+      nsCOMPtr<nsIProtocolProxyFilter> filter;
+      nsCOMPtr<nsIProtocolProxyChannelFilter> channelFilter;
       FilterLink(uint32_t p, nsIProtocolProxyFilter *f)
-        : next(nullptr), position(p), filter(f) {}
-
+        : next(nullptr), position(p), filter(f), channelFilter(nullptr) {}
+      FilterLink(uint32_t p, nsIProtocolProxyChannelFilter *cf)
+        : next(nullptr), position(p), filter(nullptr), channelFilter(cf) {}
       // Chain deletion to simplify cleaning up the filter links
       ~FilterLink() { if (next) delete next; }
     };
 
+private:
+    // Private methods to insert and remove FilterLinks from the FilterLink chain.
+    nsresult InsertFilterLink(FilterLink *link, uint32_t position);
+    nsresult RemoveFilterLink(nsISupports *givenObject);
+
+protected:
     // Indicates if local hosts (plain hostnames, no dots) should use the proxy
     bool mFilterLocalHosts;
 
     // Holds an array of HostInfo objects
     nsTArray<nsAutoPtr<HostInfo> > mHostFiltersArray;
 
     // Points to the start of a sorted by position, singly linked list
     // of FilterLink objects.
@@ -361,16 +380,18 @@ protected:
     nsRefPtr<nsPACMan>           mPACMan;  // non-null if we are using PAC
     nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
 
     PRTime                       mSessionStart;
     nsFailedProxyTable           mFailedProxies;
     int32_t                      mFailedProxyTimeout;
 
 private:
-    nsresult AsyncResolveInternal(nsIURI *uri, uint32_t flags,
+    nsresult AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
                                   nsIProtocolProxyCallback *callback,
                                   nsICancelable **result,
                                   bool isSyncOK);
 
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(nsProtocolProxyService, NS_PROTOCOL_PROXY_SERVICE_IMPL_CID)
+
 #endif // !nsProtocolProxyService_h__
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -1868,17 +1868,17 @@ nsFtpState::Init(nsFtpChannel *channel)
         mPort = port;
 
     // Lookup Proxy information asynchronously if it isn't already set
     // on the channel and if we aren't configured explicitly to go directly
     nsCOMPtr<nsIProtocolProxyService> pps =
         do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
 
     if (pps && !mChannel->ProxyInfo()) {
-        pps->AsyncResolve(mChannel->URI(), 0, this,
+        pps->AsyncResolve(mChannel, 0, this,
                           getter_AddRefs(mProxyRequest));
     }
 
     return NS_OK;
 }
 
 void
 nsFtpState::Connect()
@@ -2356,54 +2356,57 @@ nsFtpState::CloseWithStatus(nsresult sta
     if (mDoomCache && mCacheEntry)
         mCacheEntry->AsyncDoom(nullptr);
     mCacheEntry = nullptr;
 
     return nsBaseContentStream::CloseWithStatus(status);
 }
 
 static nsresult
-CreateHTTPProxiedChannel(nsIURI *uri, nsIProxyInfo *pi, nsIChannel **newChannel)
+CreateHTTPProxiedChannel(nsIChannel *channel, nsIProxyInfo *pi, nsIChannel **newChannel)
 {
     nsresult rv;
     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIProtocolHandler> handler;
     rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler));
     if (NS_FAILED(rv))
         return rv;
 
     nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler, &rv);
     if (NS_FAILED(rv))
         return rv;
 
+    nsCOMPtr<nsIURI> uri;
+    channel->GetURI(getter_AddRefs(uri));
+
     return pph->NewProxiedChannel(uri, pi, 0, nullptr, newChannel);
 }
 
 NS_IMETHODIMP
-nsFtpState::OnProxyAvailable(nsICancelable *request, nsIURI *uri,
+nsFtpState::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
                              nsIProxyInfo *pi, nsresult status)
 {
   mProxyRequest = nullptr;
 
   // failed status code just implies DIRECT processing
 
   if (NS_SUCCEEDED(status)) {
     nsAutoCString type;
     if (pi && NS_SUCCEEDED(pi->GetType(type)) && type.EqualsLiteral("http")) {
         // Proxy the FTP url via HTTP
         // This would have been easier to just return a HTTP channel directly
         // from nsIIOService::NewChannelFromURI(), but the proxy type cannot
         // be reliabliy determined synchronously without jank due to pac, etc..
         LOG(("FTP:(%p) Configured to use a HTTP proxy channel\n", this));
 
         nsCOMPtr<nsIChannel> newChannel;
-        if (NS_SUCCEEDED(CreateHTTPProxiedChannel(uri, pi,
+        if (NS_SUCCEEDED(CreateHTTPProxiedChannel(channel, pi,
                                                   getter_AddRefs(newChannel))) &&
             NS_SUCCEEDED(mChannel->Redirect(newChannel,
                                             nsIChannelEventSink::REDIRECT_INTERNAL,
                                             true))) {
             LOG(("FTP:(%p) Redirected to use a HTTP proxy channel\n", this));
             return NS_OK;
         }
     }
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -68,16 +68,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mTimingEnabled(false)
   , mAllowSpdy(true)
   , mResponseTimeoutEnabled(true)
   , mAllRedirectsSameOrigin(true)
   , mAllRedirectsPassTimingAllowCheck(true)
   , mForceNoIntercept(false)
   , mSuspendCount(0)
   , mProxyResolveFlags(0)
+  , mProxyURI(nullptr)
   , mContentDispositionHint(UINT32_MAX)
   , mHttpHandler(gHttpHandler)
   , mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE)
   , mRedirectCount(0)
   , mForcePending(false)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
@@ -1194,16 +1195,27 @@ HttpBaseChannel::SetReferrerWithPolicy(n
   rv = SetRequestHeader(NS_LITERAL_CSTRING("Referer"), spec, false);
   if (NS_FAILED(rv)) return rv;
 
   mReferrer = clone;
   mReferrerPolicy = referrerPolicy;
   return NS_OK;
 }
 
+// Return the channel's proxy URI, or if it doesn't exist, the
+// channel's main URI.
+NS_IMETHODIMP
+HttpBaseChannel::GetProxyURI(nsIURI **aOut)
+{
+  NS_ENSURE_ARG_POINTER(aOut);
+  nsCOMPtr<nsIURI> result(mProxyURI);
+  result.forget(aOut);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 HttpBaseChannel::GetRequestHeader(const nsACString& aHeader,
                                   nsACString& aValue)
 {
   // XXX might be better to search the header list directly instead of
   // hitting the http atom hash table.
   nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
   if (!atom)
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -185,16 +185,17 @@ public:
   NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable) MOZ_OVERRIDE;
   NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable) MOZ_OVERRIDE;
   NS_IMETHOD AddRedirect(nsIPrincipal *aRedirect) MOZ_OVERRIDE;
   NS_IMETHOD ForcePending(bool aForcePending) MOZ_OVERRIDE;
   NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) MOZ_OVERRIDE;
   NS_IMETHOD ForceNoIntercept() MOZ_OVERRIDE;
   NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) MOZ_OVERRIDE;
   NS_IMETHOD ContinueBeginConnect() MOZ_OVERRIDE;
+  NS_IMETHOD GetProxyURI(nsIURI **proxyURI);
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener) MOZ_OVERRIDE;
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2031,20 +2031,20 @@ nsHttpChannel::ResolveProxy()
     if (NS_FAILED(rv))
         return rv;
 
     // using the nsIProtocolProxyService2 allows a minor performance
     // optimization, but if an add-on has only provided the original interface
     // then it is ok to use that version.
     nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
     if (pps2) {
-        rv = pps2->AsyncResolve2(mProxyURI ? mProxyURI : mURI, mProxyResolveFlags,
+        rv = pps2->AsyncResolve2(this, mProxyResolveFlags,
                                  this, getter_AddRefs(mProxyRequest));
     } else {
-        rv = pps->AsyncResolve(mProxyURI ? mProxyURI : mURI, mProxyResolveFlags,
+        rv = pps->AsyncResolve(this, mProxyResolveFlags,
                                this, getter_AddRefs(mProxyRequest));
     }
 
     return rv;
 }
 
 bool
 nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry) const
@@ -5067,17 +5067,17 @@ nsHttpChannel::ClearClassFlags(uint32_t 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIProtocolProxyCallback
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIURI *uri,
+nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
                                 nsIProxyInfo *pi, nsresult status)
 {
     LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%x mStatus=%x]\n",
          this, pi, status, mStatus));
     mProxyRequest = nullptr;
 
     nsresult rv;
 
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -20,17 +20,17 @@ interface nsIProxyInfo;
 interface nsISecurityConsoleMessage;
 interface nsISocketTransport;
 interface nsIURI;
 
 /**
  * The callback interface for nsIHttpChannelInternal::HTTPUpgrade()
  */
 
-[scriptable, uuid(4b967b6d-cd1c-49ae-a457-23ff76f5a2e8)]
+[scriptable, uuid(7b48d081-1dc1-4d08-b7a5-81491bf28c0e)]
 interface nsIHttpUpgradeListener : nsISupports
 {
     void onTransportAvailable(in nsISocketTransport   aTransport,
                               in nsIAsyncInputStream  aSocketIn,
                               in nsIAsyncOutputStream aSocketOut);
 };
 
 /**
@@ -225,9 +225,15 @@ interface nsIHttpChannelInternal : nsISu
      */
     readonly attribute nsIURI topWindowURI;
 
     /**
      * Used only by nsChannelClassifier to resume connecting or abort the
      * channel after a remote classification verdict.
      */
     void continueBeginConnect();
+
+    /**
+     * Read the proxy URI, which, if non-null, will be used to resolve
+     * proxies for this channel.
+     */
+    readonly attribute nsIURI proxyURI;
 };
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -2557,17 +2557,17 @@ WebSocketChannel::ApplyForAdmission()
     // go straight to DNS
     // expect the callback in ::OnLookupComplete
     LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
     return DoAdmissionDNS();
   }
 
   MOZ_ASSERT(!mCancelable);
 
-  return pps->AsyncResolve(mURI,
+  return pps->AsyncResolve(mHttpChannel,
                            nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
                            nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
                            this, getter_AddRefs(mCancelable));
 }
 
 // Called after both OnStartRequest and OnTransportAvailable have
 // executed. This essentially ends the handshake and starts the websockets
 // protocol state machine.
@@ -2678,17 +2678,17 @@ WebSocketChannel::OnLookupComplete(nsICa
   LOG(("WebSocket OnLookupComplete: Proceeding to ConditionallyConnect\n"));
   nsWSAdmissionManager::ConditionallyConnect(this);
 
   return NS_OK;
 }
 
 // nsIProtocolProxyCallback
 NS_IMETHODIMP
-WebSocketChannel::OnProxyAvailable(nsICancelable *aRequest, nsIURI *aURI,
+WebSocketChannel::OnProxyAvailable(nsICancelable *aRequest, nsIChannel *aChannel,
                                    nsIProxyInfo *pi, nsresult status)
 {
   if (mStopped) {
     LOG(("WebSocketChannel::OnProxyAvailable: [%p] Request Already Stopped\n", this));
     mCancelable = nullptr;
     return NS_OK;
   }
 
--- a/netwerk/test/unit/test_protocolproxyservice.js
+++ b/netwerk/test/unit/test_protocolproxyservice.js
@@ -132,16 +132,30 @@ BasicFilter.prototype = {
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
   applyFilter: function(pps, uri, pi) {
     return pps.newProxyInfo("http", "localhost", 8080, 0, 10,
            pps.newProxyInfo("direct", "", -1, 0, 0, null));
   }
 };
 
+function BasicChannelFilter() {}
+BasicChannelFilter.prototype = {
+  QueryInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIProtocolProxyChannelFilter) ||
+        iid.equals(Components.interfaces.nsISupports))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+  applyFilter: function(pps, channel, pi) {
+    return pps.newProxyInfo("http", channel.URI.host, 7777, 0, 10,
+           pps.newProxyInfo("direct", "", -1, 0, 0, null));
+  }
+};
+
 function resolveCallback() { }
 resolveCallback.prototype = {
   nextFunction: null,
 
   QueryInterface : function (iid) {
     const interfaces = [Components.interfaces.nsIProtocolProxyCallback,
                         Components.interfaces.nsISupports];
     if (!interfaces.some( function(v) { return iid.equals(v) } ))
@@ -150,22 +164,22 @@ resolveCallback.prototype = {
   },
 
   onProxyAvailable : function (req, uri, pi, status) {
     this.nextFunction(pi);
   }
 };
 
 function run_filter_test() {
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   // Verify initial state
   var cb = new resolveCallback();
   cb.nextFunction = filter_test0_1;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 var filter01;
 var filter02;
 
 function filter_test0_1(pi) {
   do_check_eq(pi, null);
 
@@ -173,193 +187,200 @@ function filter_test0_1(pi) {
 
   filter01 = new BasicFilter();
   filter02 = new BasicFilter();
   pps.registerFilter(filter01, 10);
   pps.registerFilter(filter02, 20);
 
   var cb = new resolveCallback();
   cb.nextFunction = filter_test0_2;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function filter_test0_2(pi)
 {
   check_proxy(pi, "http", "localhost", 8080, 0, 10, true);
   check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false);
 
   pps.unregisterFilter(filter02);
 
   var cb = new resolveCallback();
   cb.nextFunction = filter_test0_3;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function filter_test0_3(pi)
 {
   check_proxy(pi, "http", "localhost", 8080, 0, 10, true);
   check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false);
 
   // Remove filter and verify that we return to the initial state
 
   pps.unregisterFilter(filter01);
 
   var cb = new resolveCallback();
   cb.nextFunction = filter_test0_4;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
+var filter03;
+
 function filter_test0_4(pi)
 {
   do_check_eq(pi, null);
+  filter03 = new BasicChannelFilter();
+  pps.registerChannelFilter(filter03, 10);
+  var cb = new resolveCallback();
+  cb.nextFunction = filter_test0_5;
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
+}
+
+function filter_test0_5(pi)
+{
+  pps.unregisterChannelFilter(filter03);
+  check_proxy(pi, "http", "www.mozilla.org", 7777, 0, 10, true);
+  check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false);
   run_filter_test2();
 }
 
 var filter11;
 var filter12;
 
 function run_filter_test2() {
   // Push a filter and verify the results
 
   filter11 = new TestFilter("http", "foo", 8080, 0, 10);
   filter12 = new TestFilter("http", "bar", 8090, 0, 10);
   pps.registerFilter(filter11, 20);
   pps.registerFilter(filter12, 10);
 
   var cb = new resolveCallback();
   cb.nextFunction = filter_test1_1;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function filter_test1_1(pi) {
   check_proxy(pi, "http", "bar", 8090, 0, 10, true);
   check_proxy(pi.failoverProxy, "http", "foo", 8080, 0, 10, false);
 
   pps.unregisterFilter(filter12);
 
   var cb = new resolveCallback();
   cb.nextFunction = filter_test1_2;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function filter_test1_2(pi) {
   check_proxy(pi, "http", "foo", 8080, 0, 10, false);
 
   // Remove filter and verify that we return to the initial state
 
   pps.unregisterFilter(filter11);
 
   var cb = new resolveCallback();
   cb.nextFunction = filter_test1_3;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function filter_test1_3(pi) {
   do_check_eq(pi, null);
   run_filter_test3();
 }
 
 var filter_3_1;
 
 function run_filter_test3() {
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   // Push a filter and verify the results asynchronously
 
   filter_3_1 = new TestFilter("http", "foo", 8080, 0, 10);
   pps.registerFilter(filter_3_1, 20);
 
   var cb = new resolveCallback();
   cb.nextFunction = filter_test3_1;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function filter_test3_1(pi) {
   check_proxy(pi, "http", "foo", 8080, 0, 10, false);
   pps.unregisterFilter(filter_3_1);
   run_pref_test();
 }
 
 function run_pref_test() {
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   // Verify 'direct' setting
 
   prefs.setIntPref("network.proxy.type", 0);
 
   var cb = new resolveCallback();
   cb.nextFunction = pref_test1_1;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function pref_test1_1(pi)
 {
   do_check_eq(pi, null);
 
   // Verify 'manual' setting
   prefs.setIntPref("network.proxy.type", 1);
 
   var cb = new resolveCallback();
   cb.nextFunction = pref_test1_2;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function pref_test1_2(pi)
 {
   // nothing yet configured
   do_check_eq(pi, null);
 
   // try HTTP configuration
   prefs.setCharPref("network.proxy.http", "foopy");
   prefs.setIntPref("network.proxy.http_port", 8080);
 
   var cb = new resolveCallback();
   cb.nextFunction = pref_test1_3;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function pref_test1_3(pi)
 {
   check_proxy(pi, "http", "foopy", 8080, 0, -1, false);
 
   prefs.setCharPref("network.proxy.http", "");
   prefs.setIntPref("network.proxy.http_port", 0);
 
   // try SOCKS configuration
   prefs.setCharPref("network.proxy.socks", "barbar");
   prefs.setIntPref("network.proxy.socks_port", 1203);
 
   var cb = new resolveCallback();
   cb.nextFunction = pref_test1_4;
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-  var req = pps.asyncResolve(uri, 0, cb);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function pref_test1_4(pi)
 {
   check_proxy(pi, "socks", "barbar", 1203, 0, -1, false);
   run_pac_test();
 }
 
-function run_protocol_handler_test() {
-  var uri = ios.newURI("moz-test:foopy", null, null);
-
-  var cb = new resolveCallback();
-  cb.nextFunction = protocol_handler_test_1;
-  var req = pps.asyncResolve(uri, 0, cb);
-}
-
 function protocol_handler_test_1(pi)
 {
   do_check_eq(pi, null);
   prefs.setCharPref("network.proxy.autoconfig_url", "");
   prefs.setIntPref("network.proxy.type", 0);
 
   run_pac_cancel_test();
 }
@@ -398,58 +419,58 @@ TestResolveCallback.prototype = {
 
 var originalTLSProxy;
 
 function run_pac_test() {
   var pac = 'data:text/plain,' +
             'function FindProxyForURL(url, host) {' +
             '  return "PROXY foopy:8080; DIRECT";' +
             '}';
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   // Configure PAC
 
   prefs.setIntPref("network.proxy.type", 2);
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
-  var req = pps.asyncResolve(uri, 0, new TestResolveCallback("http", run_pac2_test));
+  var req = pps.asyncResolve(channel, 0, new TestResolveCallback("http", run_pac2_test));
 }
 
 function run_pac2_test() {
   var pac = 'data:text/plain,' +
             'function FindProxyForURL(url, host) {' +
             '  return "HTTPS foopy:8080; DIRECT";' +
             '}';
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   // Configure PAC
   originalTLSProxy = prefs.getBoolPref("network.proxy.proxy_over_tls");
 
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
   prefs.setBoolPref("network.proxy.proxy_over_tls", true);
 
-  var req = pps.asyncResolve(uri, 0, new TestResolveCallback("https", run_pac3_test));
+  var req = pps.asyncResolve(channel, 0, new TestResolveCallback("https", run_pac3_test));
 }
 
 function run_pac3_test() {
   var pac = 'data:text/plain,' +
             'function FindProxyForURL(url, host) {' +
             '  return "HTTPS foopy:8080; DIRECT";' +
             '}';
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   // Configure PAC
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
   prefs.setBoolPref("network.proxy.proxy_over_tls", false);
 
-  var req = pps.asyncResolve(uri, 0, new TestResolveCallback(null, finish_pac_test));
+  var req = pps.asyncResolve(channel, 0, new TestResolveCallback(null, finish_pac_test));
 }
 
 function finish_pac_test() {
   prefs.setBoolPref("network.proxy.proxy_over_tls", originalTLSProxy);
-  run_protocol_handler_test();
+  run_pac_cancel_test();
 }
 
 function TestResolveCancelationCallback() {
 }
 TestResolveCancelationCallback.prototype = {
   QueryInterface:
   function TestResolveCallback_QueryInterface(iid) {
     if (iid.equals(Components.interfaces.nsIProtocolProxyCallback) ||
@@ -470,27 +491,27 @@ TestResolveCancelationCallback.prototype
     prefs.setCharPref("network.proxy.autoconfig_url", "");
     prefs.setIntPref("network.proxy.type", 0);
 
     run_proxy_host_filters_test();
   }
 };
 
 function run_pac_cancel_test() {
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   // Configure PAC
   var pac = 'data:text/plain,' +
             'function FindProxyForURL(url, host) {' +
             '  return "PROXY foopy:8080; DIRECT";' +
             '}';
   prefs.setIntPref("network.proxy.type", 2);
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
 
-  var req = pps.asyncResolve(uri, 0, new TestResolveCancelationCallback());
+  var req = pps.asyncResolve(channel, 0, new TestResolveCancelationCallback());
   req.cancel(Components.results.NS_ERROR_ABORT);
 }
 
 var hostList;
 var hostIDX;
 var bShouldBeFiltered;
 var hostNextFX;
 
@@ -511,21 +532,21 @@ function check_host_filters_cb()
     check_host_filter(hostIDX);
   else
     hostNextFX();
 }
 
 function check_host_filter(i) {
   var uri;
   dump("*** uri=" + hostList[i] + " bShouldBeFiltered=" + bShouldBeFiltered + "\n");
-  uri = ios.newURI(hostList[i], null, null);
+  var channel = ios.newChannel(hostList[i], null, null);
 
   var cb = new resolveCallback();
   cb.nextFunction = host_filter_cb;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function host_filter_cb(proxy)
 {
   if (bShouldBeFiltered) {
     do_check_eq(proxy, null);
   } else {
     do_check_neq(proxy, null);
@@ -615,24 +636,24 @@ function run_myipaddress_test()
   var pac = 'data:text/plain,' +
             'var pacUseMultihomedDNS = true;\n' +
             'function FindProxyForURL(url, host) {' +
             ' return "PROXY " + myIpAddress() + ":1234";' +
             '}';
 
   // no traffic to this IP is ever sent, it is just a public IP that
   // does not require DNS to determine a route.
-  var uri = ios.newURI("http://192.0.43.10/", null, null);
+  var channel = ios.newChannel("http://192.0.43.10/", null, null);
 
   prefs.setIntPref("network.proxy.type", 2);
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
 
   var cb = new resolveCallback();
   cb.nextFunction = myipaddress_callback;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function myipaddress_callback(pi)
 {
   do_check_neq(pi, null);
   do_check_eq(pi.type, "http");
   do_check_eq(pi.port, 1234);
 
@@ -651,23 +672,23 @@ function run_myipaddress_test_2()
 
   var pac = 'data:text/plain,' +
             'var pacUseMultihomedDNS = true;\n' +
             'var myaddr = myIpAddress(); ' +
             'function FindProxyForURL(url, host) {' +
             ' return "PROXY " + myaddr + ":5678";' +
             '}';
 
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
   prefs.setIntPref("network.proxy.type", 2);
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
 
   var cb = new resolveCallback();
   cb.nextFunction = myipaddress2_callback;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function myipaddress2_callback(pi)
 {
   do_check_neq(pi, null);
   do_check_eq(pi.type, "http");
   do_check_eq(pi.port, 5678);
 
@@ -680,24 +701,24 @@ function myipaddress2_callback(pi)
 }
 
 function run_failed_script_test()
 {
   // test to make sure we go direct with invalid PAC
   var pac = 'data:text/plain,' +
             '\nfor(;\n';
 
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   prefs.setIntPref("network.proxy.type", 2);
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
 
   var cb = new resolveCallback();
   cb.nextFunction = failed_script_callback;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 var directFilter;
 
 function failed_script_callback(pi)
 {
   // we should go direct
   do_check_eq(pi, null);
@@ -760,70 +781,36 @@ function run_isresolvable_test()
 
   var pac = 'data:text/plain,' +
             'function FindProxyForURL(url, host) {' +
             ' if (isResolvable("nonexistant.lan"))' +
             '   return "DIRECT";' +
             ' return "PROXY 127.0.0.1:1234";' +
             '}';
 
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
+  var channel = ios.newChannel("http://www.mozilla.org/", null, null);
 
   prefs.setIntPref("network.proxy.type", 2);
   prefs.setCharPref("network.proxy.autoconfig_url", pac);
 
   var cb = new resolveCallback();
   cb.nextFunction = isresolvable_callback;
-  var req = pps.asyncResolve(uri, 0, cb);
+  var req = pps.asyncResolve(channel, 0, cb);
 }
 
 function isresolvable_callback(pi)
 {
   do_check_neq(pi, null);
   do_check_eq(pi.type, "http");
   do_check_eq(pi.port, 1234);
   do_check_eq(pi.host, "127.0.0.1");
 
   prefs.setIntPref("network.proxy.type", 0);
   do_test_finished();
 }
 
-function run_deprecated_sync_test()
-{
-  var uri = ios.newURI("http://www.mozilla.org/", null, null);
-
-  pps.QueryInterface(Components.interfaces.nsIProtocolProxyService2);
-
-  // Verify initial state
-  var pi = pps.deprecatedBlockingResolve(uri, 0);
-  do_check_eq(pi, null);
-
-  // Push a filter and verify the results
-  var filter1 = new BasicFilter();
-  var filter2 = new BasicFilter();
-  pps.registerFilter(filter1, 10);
-  pps.registerFilter(filter2, 20);
-
-  pi = pps.deprecatedBlockingResolve(uri, 0);
-  check_proxy(pi, "http", "localhost", 8080, 0, 10, true);
-  check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false);
-
-  pps.unregisterFilter(filter2);
-  pi = pps.deprecatedBlockingResolve(uri, 0);
-  check_proxy(pi, "http", "localhost", 8080, 0, 10, true);
-  check_proxy(pi.failoverProxy, "direct", "", -1, 0, 0, false);
-
-  // Remove filter and verify that we return to the initial state
-  pps.unregisterFilter(filter1);
-  pi = pps.deprecatedBlockingResolve(uri, 0);
-  do_check_eq(pi, null);
-}
-
 function run_test() {
   register_test_protocol_handler();
 
-  // any synchronous tests
-  run_deprecated_sync_test();
-
   // start of asynchronous test chain
   run_filter_test();
   do_test_pending();
 }
--- a/toolkit/components/passwordmgr/test/test_bug_627616.html
+++ b/toolkit/components/passwordmgr/test/test_bug_627616.html
@@ -30,18 +30,18 @@
          init2(SpecialPowers.wrap(pi).host, SpecialPowers.wrap(pi).port);
       }
     });
 
     function init1() {
         var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
         var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
 
-        var uri = ios.newURI("http://example.com", null, null);
-        pps.asyncResolve(uri, 0, resolveCallback);
+        var channel = ios.newChannel("http://example.com", null, null);
+        pps.asyncResolve(channel, 0, resolveCallback);
     }
 
     function init2(proxyHost, proxyPort) {
 
         var mozproxy = "moz-proxy://" + proxyHost + ":" + proxyPort;
 
         var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
         login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
--- a/toolkit/components/passwordmgr/test/test_prompt.html
+++ b/toolkit/components/passwordmgr/test/test_prompt.html
@@ -166,18 +166,18 @@ var resolveCallback = SpecialPowers.wrap
 
 function startup() {
   //need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy.
   var ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"].
     getService(SpecialPowers.Ci.nsIIOService);
 
   var pps = SpecialPowers.Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
 
-  var uri = ios.newURI("http://example.com", null, null);
-  pps.asyncResolve(uri, 0, resolveCallback);
+  var channel = ios.newChannel("http://example.com", null, null);
+  pps.asyncResolve(channel, 0, resolveCallback);
 }
 
 function addNotificationCallback(cb) {
   storageObserver.notificationCallbacks.push(cb);
 }
 
 var storageObserver = SpecialPowers.wrapCallbackObject({
   notificationCallbacks: [],
--- a/toolkit/components/passwordmgr/test/test_prompt_async.html
+++ b/toolkit/components/passwordmgr/test/test_prompt_async.html
@@ -141,18 +141,18 @@
 	function startup() {
             //need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy.
             var ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                                    .getService(SpecialPowers.Ci.nsIIOService);
 
             var pps = SpecialPowers.Cc["@mozilla.org/network/protocol-proxy-service;1"]
                                    .getService();
 
-            var uri = ios.newURI("http://example.com", null, null);
-            pps.asyncResolve(uri, 0, resolveCallback);
+            var channel = ios.newChannel("http://example.com", null, null);
+            pps.asyncResolve(channel, 0, resolveCallback);
 	}
 
         // --------------- Test loop spin ----------------
         var testNum = 1;
         var iframe1;
         var iframe2a;
         var iframe2b;
         window.onload = function () {