Bug 436344 - Allow filtering of proxies by channel. r=mcmanus
authorArthur Edelstein <arthuredelstein@gmail.com>
Wed, 21 Jan 2015 21:13:00 +0100
changeset 225108 04c4527643394e15b8fac110ecd9c5cfe4cb07b8
parent 225107 61c35f7b9aaafed3cc72e9a5e196d0b3ca5107ec
child 225109 da5679ebd7590fcb7e985b8940f3d8e3b9a9f9a2
push id28152
push usercbook@mozilla.com
push dateThu, 22 Jan 2015 13:37:12 +0000
treeherdermozilla-central@86f9d0128ccf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs436344
milestone38.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 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 () {