Bug 1402944: Part 5 - Move request filtering and permission matching into ChannelWrapper. r=mixedpuppy,ehsan
authorKris Maglione <maglione.k@gmail.com>
Wed, 27 Sep 2017 18:15:12 -0700
changeset 383532 cd219dd09639d421b6ebf2f85ada518dd8a4c0c2
parent 383531 364f04c7fee00054c8af092229ff01ed3a2f7bdc
child 383533 d5dad020fcd48b8294ce54f4e142d66621d24aea
push id32595
push userkwierso@gmail.com
push dateThu, 28 Sep 2017 23:57:40 +0000
treeherdermozilla-central@e6c32278f32c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy, ehsan
bugs1402944
milestone58.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 1402944: Part 5 - Move request filtering and permission matching into ChannelWrapper. r=mixedpuppy,ehsan This allows us to reuse the same URLInfo objects for each permission or extension that we match, and also avoids a lot of XPConnect overhead we wind up incurring when we access URI objects from the JS side. MozReview-Commit-ID: GqgVRjQ3wYQ
dom/base/nsGkAtomList.h
dom/webidl/ChannelWrapper.webidl
toolkit/components/extensions/MatchPattern.cpp
toolkit/components/extensions/MatchPattern.h
toolkit/components/extensions/WebExtensionPolicy.h
toolkit/components/extensions/ext-webRequest.js
toolkit/components/extensions/webrequest/ChannelWrapper.cpp
toolkit/components/extensions/webrequest/ChannelWrapper.h
toolkit/modules/addons/WebRequest.jsm
toolkit/modules/tests/browser/browser_WebRequest_filtering.js
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2072,16 +2072,17 @@ GK_ATOM(moz_extension, "moz-extension")
 GK_ATOM(all_urlsPermission, "<all_urls>")
 GK_ATOM(clipboardRead, "clipboardRead")
 GK_ATOM(clipboardWrite, "clipboardWrite")
 GK_ATOM(debugger, "debugger")
 GK_ATOM(tabs, "tabs")
 GK_ATOM(webRequestBlocking, "webRequestBlocking")
 GK_ATOM(http, "http")
 GK_ATOM(https, "https")
+GK_ATOM(proxy, "proxy")
 
 //---------------------------------------------------------------------------
 // Special atoms
 //---------------------------------------------------------------------------
 
 // Node types
 GK_ATOM(cdataTagName, "#cdata-section")
 GK_ATOM(commentTagName, "#comment")
--- a/dom/webidl/ChannelWrapper.webidl
+++ b/dom/webidl/ChannelWrapper.webidl
@@ -108,25 +108,34 @@ interface ChannelWrapper : EventTarget {
   [Pure, SetterThrows]
   attribute boolean suspended;
 
 
   /**
    * The final URI of the channel (as returned by NS_GetFinalChannelURI) after
    * any redirects have been processed.
    */
-  [Cached, GetterThrows, Pure]
+  [Cached, Pure]
   readonly attribute URI finalURI;
 
   /**
    * The string version of finalURI (but cheaper to access than
    * finalURI.spec).
    */
-  [Cached, GetterThrows, Pure]
-  readonly attribute ByteString finalURL;
+  [Cached, Pure]
+  readonly attribute DOMString finalURL;
+
+
+  /**
+   * Returns true if the request matches the given request filter, and the
+   * given extension has permission to access it.
+   */
+  boolean matches(optional MozRequestFilter filter,
+                  optional WebExtensionPolicy? extension = null,
+                  optional MozRequestMatchOptions options);
 
 
   /**
    * The current HTTP status code of the request. This will be 0 if a response
    * has not yet been received, or if the request is not an HTTP request.
    */
   [Cached, Pure]
   readonly attribute unsigned long statusCode;
@@ -367,8 +376,32 @@ dictionary MozProxyInfo {
  * frameId is the outerWindowID for the parent of the loading window.
  *
  * For further details see nsILoadInfo.idl and nsIDocument::AncestorPrincipals.
  */
 dictionary MozFrameAncestorInfo {
   required ByteString url;
   required unsigned long long frameId;
 };
+
+/**
+ * An object used for filtering requests.
+ */
+dictionary MozRequestFilter {
+  /**
+   * If present, the request only matches if its `type` attribute matches one
+   * of the given types.
+   */
+  sequence<MozContentPolicyType>? types = null;
+
+  /**
+   * If present, the request only matches if its finalURI matches the given
+   * match pattern set.
+   */
+  MatchPatternSet? urls = null;
+};
+
+dictionary MozRequestMatchOptions {
+  /**
+   * True if we're matching for the proxy portion of a proxied request.
+   */
+  boolean isProxy = false;
+};
--- a/toolkit/components/extensions/MatchPattern.cpp
+++ b/toolkit/components/extensions/MatchPattern.cpp
@@ -153,24 +153,30 @@ URLInfo::Path() const
     nsCString path;
     if (NS_SUCCEEDED(URINoRef()->GetPathQueryRef(path))) {
       AppendUTF8toUTF16(path, mPath);
     }
   }
   return mPath;
 }
 
+const nsCString&
+URLInfo::CSpec() const
+{
+  if (mCSpec.IsEmpty()) {
+    Unused << URINoRef()->GetSpec(mCSpec);
+  }
+  return mCSpec;
+}
+
 const nsString&
 URLInfo::Spec() const
 {
   if (mSpec.IsEmpty()) {
-    nsCString spec;
-    if (NS_SUCCEEDED(URINoRef()->GetSpec(spec))) {
-      AppendUTF8toUTF16(spec, mSpec);
-    }
+    AppendUTF8toUTF16(CSpec(), mSpec);
   }
   return mSpec;
 }
 
 nsIURI*
 URLInfo::URINoRef() const
 {
   if (!mURINoRef) {
--- a/toolkit/components/extensions/MatchPattern.h
+++ b/toolkit/components/extensions/MatchPattern.h
@@ -125,51 +125,61 @@ private:
 
   void SortAndUniquify();
 };
 
 
 // A helper class to lazily retrieve, transcode, and atomize certain URI
 // properties the first time they're used, and cache the results, so that they
 // can be used across multiple match operations.
-class MOZ_STACK_CLASS URLInfo final
+class URLInfo final
 {
 public:
   MOZ_IMPLICIT URLInfo(nsIURI* aURI)
     : mURI(aURI)
   {
     mHost.SetIsVoid(true);
   }
 
+  URLInfo(nsIURI* aURI, bool aNoRef)
+    : URLInfo(aURI)
+  {
+    if (aNoRef) {
+      mURINoRef = mURI;
+    }
+  }
+
   URLInfo(const URLInfo& aOther)
     : URLInfo(aOther.mURI.get())
   {}
 
   nsIURI* URI() const { return mURI; }
 
   nsIAtom* Scheme() const;
   const nsCString& Host() const;
   const nsString& Path() const;
   const nsString& FilePath() const;
   const nsString& Spec() const;
+  const nsCString& CSpec() const;
 
   bool InheritsPrincipal() const;
 
 private:
   nsIURI* URINoRef() const;
 
   nsCOMPtr<nsIURI> mURI;
   mutable nsCOMPtr<nsIURI> mURINoRef;
 
   mutable RefPtr<nsIAtom> mScheme;
   mutable nsCString mHost;
 
-  mutable nsAutoString mPath;
-  mutable nsAutoString mFilePath;
-  mutable nsAutoString mSpec;
+  mutable nsString mPath;
+  mutable nsString mFilePath;
+  mutable nsString mSpec;
+  mutable nsCString mCSpec;
 
   mutable Maybe<bool> mInheritsPrincipal;
 };
 
 
 // Similar to URLInfo, but for cookies.
 class MOZ_STACK_CLASS CookieInfo final
 {
--- a/toolkit/components/extensions/WebExtensionPolicy.h
+++ b/toolkit/components/extensions/WebExtensionPolicy.h
@@ -54,17 +54,17 @@ public:
   {
     MOZ_ALWAYS_SUCCEEDS(mBaseURI->GetSpec(aBaseURL));
   }
 
   void GetURL(const nsAString& aPath, nsAString& aURL, ErrorResult& aRv) const;
 
   Result<nsString, nsresult> GetURL(const nsAString& aPath) const;
 
-  bool CanAccessURI(nsIURI* aURI, bool aExplicit = false) const
+  bool CanAccessURI(const URLInfo& aURI, bool aExplicit = false) const
   {
     return mHostPermissions && mHostPermissions->Matches(aURI, aExplicit);
   }
 
   bool IsPathWebAccessible(const nsAString& aPath) const
   {
     return mWebAccessiblePaths.Matches(aPath);
   }
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -12,42 +12,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 // EventManager-like class specifically for WebRequest. Inherits from
 // EventManager. Takes care of converting |details| parameter
 // when invoking listeners.
 function WebRequestEventManager(context, eventName) {
   let name = `webRequest.${eventName}`;
   let register = (fire, filter, info) => {
     let listener = data => {
-      // data.isProxy is only set from the WebRequest AuthRequestor handler,
-      // in which case we know that this is onAuthRequired.  If this is proxy
-      // authorization, we allow without any additional matching/filtering.
-      let isProxyAuth = data.isProxy && context.extension.hasPermission("proxy");
-
-      // Prevent listening in on requests originating from system principal to
-      // prevent tinkering with OCSP, app and addon updates, etc.  However,
-      // proxy addons need to be able to provide auth for any request so we
-      // allow those through.  The exception is for proxy extensions handling
-      // proxy authentication.
-      if (data.isSystemPrincipal && !isProxyAuth) {
-        return;
-      }
-
-      // Check hosts permissions for both the resource being requested,
-      const hosts = context.extension.whiteListedHosts;
-      if (!hosts.matches(data.URI)) {
-        return;
-      }
-      // and the origin that is loading the resource.
-      const origin = data.documentUrl;
-      const own = origin && origin.startsWith(context.extension.getURL());
-      if (origin && !own && !isProxyAuth && !hosts.matches(data.documentURI)) {
-        return;
-      }
-
       let browserData = {tabId: -1, windowId: -1};
       if (data.browser) {
         browserData = tabTracker.getBrowserData(data.browser);
       }
       if (filter.tabId != null && browserData.tabId != filter.tabId) {
         return;
       }
       if (filter.windowId != null && browserData.windowId != filter.windowId) {
@@ -92,16 +66,17 @@ function WebRequestEventManager(context,
         } else {
           info2.push(desc);
         }
       }
     }
 
     let listenerDetails = {
       addonId: context.extension.id,
+      extension: context.extension.policy,
       blockingAllowed,
       tabParent: context.xulBrowser.frameLoader.tabParent,
     };
 
     WebRequest[eventName].addListener(
       listener, filter2, info2,
       listenerDetails);
     return () => {
--- a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
+++ b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
@@ -77,24 +77,25 @@ ChannelWrapper::Get(const GlobalObject& 
   return wrapper.forget();
 }
 
 void
 ChannelWrapper::SetChannel(nsIChannel* aChannel)
 {
   detail::ChannelHolder::SetChannel(aChannel);
   ClearCachedAttributes();
+  ChannelWrapperBinding::ClearCachedFinalURIValue(this);
+  ChannelWrapperBinding::ClearCachedFinalURLValue(this);
+  mFinalURLInfo.reset();
+  ChannelWrapperBinding::ClearCachedProxyInfoValue(this);
 }
 
 void
 ChannelWrapper::ClearCachedAttributes()
 {
-  ChannelWrapperBinding::ClearCachedFinalURIValue(this);
-  ChannelWrapperBinding::ClearCachedFinalURLValue(this);
-  ChannelWrapperBinding::ClearCachedProxyInfoValue(this);
   ChannelWrapperBinding::ClearCachedRemoteAddressValue(this);
   ChannelWrapperBinding::ClearCachedStatusCodeValue(this);
   ChannelWrapperBinding::ClearCachedStatusLineValue(this);
   if (!mFiredErrorEvent) {
     ChannelWrapperBinding::ClearCachedErrorStringValue(this);
   }
 }
 
@@ -365,17 +366,17 @@ ChannelWrapper::IsSystemLoad() const
     }
   }
   return false;
 }
 
 bool
 ChannelWrapper::GetCanModify(ErrorResult& aRv) const
 {
-  nsCOMPtr<nsIURI> uri = GetFinalURI(aRv);
+  nsCOMPtr<nsIURI> uri = FinalURI();
   nsAutoCString spec;
   if (uri) {
     uri->GetSpec(spec);
   }
   if (!uri || AddonManagerWebAPI::IsValidSite(uri)) {
     return false;
   }
 
@@ -435,16 +436,103 @@ ChannelWrapper::GetOriginURL(nsCString& 
 void
 ChannelWrapper::GetDocumentURL(nsCString& aRetVal) const
 {
   if (nsCOMPtr<nsIURI> uri = GetDocumentURI()) {
     Unused << uri->GetSpec(aRetVal);
   }
 }
 
+
+const URLInfo&
+ChannelWrapper::FinalURLInfo() const
+{
+  if (mFinalURLInfo.isNothing()) {
+    ErrorResult rv;
+    nsCOMPtr<nsIURI> uri = FinalURI();
+    MOZ_ASSERT(uri);
+    mFinalURLInfo.emplace(uri.get(), true);
+
+    // If this is a WebSocket request, mangle the URL so that the scheme is
+    // ws: or wss:, as appropriate.
+    auto& url = mFinalURLInfo.ref();
+    if (Type() == MozContentPolicyType::Websocket &&
+        (url.Scheme() == nsGkAtoms::http ||
+         url.Scheme() == nsGkAtoms::https)) {
+      nsAutoCString spec(url.CSpec());
+      spec.Replace(0, 4, NS_LITERAL_CSTRING("ws"));
+
+      Unused << NS_NewURI(getter_AddRefs(uri), spec);
+      MOZ_RELEASE_ASSERT(uri);
+      mFinalURLInfo.reset();
+      mFinalURLInfo.emplace(uri.get(), true);
+    }
+  }
+  return mFinalURLInfo.ref();
+}
+
+const URLInfo*
+ChannelWrapper::DocumentURLInfo() const
+{
+  if (mDocumentURLInfo.isNothing()) {
+    nsCOMPtr<nsIURI> uri = GetDocumentURI();
+    if (!uri) {
+      return nullptr;
+    }
+    mDocumentURLInfo.emplace(uri.get(), true);
+  }
+  return &mDocumentURLInfo.ref();
+}
+
+
+bool
+ChannelWrapper::Matches(const dom::MozRequestFilter& aFilter,
+                        const WebExtensionPolicy* aExtension,
+                        const dom::MozRequestMatchOptions& aOptions) const
+{
+  if (!HaveChannel()) {
+    return false;
+  }
+
+  if (!aFilter.mTypes.IsNull() && !aFilter.mTypes.Value().Contains(Type())) {
+    return false;
+  }
+
+  auto& urlInfo = FinalURLInfo();
+  if (aFilter.mUrls && !aFilter.mUrls->Matches(urlInfo)) {
+    return false;
+  }
+
+  if (aExtension) {
+    if (!aExtension->CanAccessURI(urlInfo)) {
+      return false;
+    }
+
+    bool isProxy = aOptions.mIsProxy && aExtension->HasPermission(nsGkAtoms::proxy);
+    if (!isProxy && IsSystemLoad()) {
+      return false;
+    }
+
+    if (auto origin = DocumentURLInfo()) {
+      nsAutoCString baseURL;
+      aExtension->GetBaseURL(baseURL);
+
+      if (!isProxy &&
+          !StringBeginsWith(origin->CSpec(), baseURL) &&
+          !aExtension->CanAccessURI(*origin)) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+
+
 int64_t
 NormalizeWindowID(nsILoadInfo* aLoadInfo, uint64_t windowID)
 {
   if (windowID == aLoadInfo->GetTopOuterWindowID()) {
     return 0;
   }
   return windowID;
 }
@@ -649,35 +737,30 @@ ChannelWrapper::GetStatusLine(nsCString&
   }
 }
 
 /*****************************************************************************
  * ...
  *****************************************************************************/
 
 already_AddRefed<nsIURI>
-ChannelWrapper::GetFinalURI(ErrorResult& aRv) const
+ChannelWrapper::FinalURI() const
 {
-  nsresult rv = NS_ERROR_UNEXPECTED;
   nsCOMPtr<nsIURI> uri;
   if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
-    rv = NS_GetFinalChannelURI(chan, getter_AddRefs(uri));
-  }
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
+    NS_GetFinalChannelURI(chan, getter_AddRefs(uri));
   }
   return uri.forget();
 }
 
 void
-ChannelWrapper::GetFinalURL(nsCString& aRetVal, ErrorResult& aRv) const
+ChannelWrapper::GetFinalURL(nsString& aRetVal) const
 {
-  nsCOMPtr<nsIURI> uri = GetFinalURI(aRv);
-  if (uri) {
-    Unused << uri->GetSpec(aRetVal);
+  if (HaveChannel()) {
+    aRetVal = FinalURLInfo().Spec();
   }
 }
 
 /*****************************************************************************
  * ...
  *****************************************************************************/
 
 nsresult
--- a/toolkit/components/extensions/webrequest/ChannelWrapper.h
+++ b/toolkit/components/extensions/webrequest/ChannelWrapper.h
@@ -5,16 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_extensions_ChannelWrapper_h
 #define mozilla_extensions_ChannelWrapper_h
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ChannelWrapperBinding.h"
 
+#include "mozilla/extensions/MatchPattern.h"
+#include "mozilla/extensions/WebExtensionPolicy.h"
+
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
@@ -140,19 +143,24 @@ public:
 
   void ErrorCheck();
 
   IMPL_EVENT_HANDLER(error);
   IMPL_EVENT_HANDLER(start);
   IMPL_EVENT_HANDLER(stop);
 
 
-  already_AddRefed<nsIURI> GetFinalURI(ErrorResult& aRv) const;
+  already_AddRefed<nsIURI> FinalURI() const;
+
+  void GetFinalURL(nsString& aRetVal) const;
 
-  void GetFinalURL(nsCString& aRetVal, ErrorResult& aRv) const;
+
+  bool Matches(const dom::MozRequestFilter& aFilter,
+               const WebExtensionPolicy* aExtension,
+               const dom::MozRequestMatchOptions& aOptions) const;
 
 
   already_AddRefed<nsILoadInfo> GetLoadInfo() const
   {
     nsCOMPtr<nsIChannel> chan = MaybeChannel();
     if (chan) {
       return chan->GetLoadInfo();
     }
@@ -224,16 +232,21 @@ private:
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return false;
     }
     return true;
   }
 
   void FireEvent(const nsAString& aType);
 
+
+  const URLInfo& FinalURLInfo() const;
+  const URLInfo* DocumentURLInfo() const;
+
+
   uint64_t WindowId(nsILoadInfo* aLoadInfo) const;
 
   nsresult GetFrameAncestors(nsILoadInfo* aLoadInfo, nsTArray<dom::MozFrameAncestorInfo>& aFrameAncestors) const;
 
   static uint64_t GetNextId()
   {
     static uint64_t sNextId = 1;
     return ++sNextId;
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -235,17 +235,16 @@ var ContentPolicyManager = {
       if (!callback) {
         // It's possible that this listener has been removed and the
         // child hasn't learned yet.
         continue;
       }
       let response = null;
       let listenerKind = "onStop";
       let data = Object.assign({requestId, browser, serialize: serializeRequestData}, msg.data);
-      data.URI = data.url;
 
       delete data.ids;
       try {
         response = callback(data);
         if (response) {
           if (response.cancel) {
             listenerKind = "onError";
             data.error = "NS_ERROR_ABORT";
@@ -258,25 +257,41 @@ var ContentPolicyManager = {
       } finally {
         runLater(() => this.runChannelListener(listenerKind, data));
       }
     }
 
     return {};
   },
 
+  shouldRunListener(policyType, url, opts) {
+    let {filter} = opts;
+
+    if (filter.types && !filter.types.includes(policyType)) {
+      return false;
+    }
+
+    if (filter.urls && !filter.urls.matches(url)) {
+      return false;
+    }
+
+    let {extension} = opts;
+    if (extension && !extension.allowedOrigins.matches(url)) {
+      return false;
+    }
+
+    return true;
+  },
+
   runChannelListener(kind, data) {
     let listeners = HttpObserverManager.listeners[kind];
-    let uri = Services.io.newURI(data.url);
-    let policyType = data.type;
     for (let [callback, opts] of listeners.entries()) {
-      if (!HttpObserverManager.shouldRunListener(policyType, uri, opts.filter)) {
-        continue;
+      if (this.shouldRunListener(data.type, data.url, opts)) {
+        callback(data);
       }
-      callback(data);
     }
   },
 
   addListener(callback, opts) {
     // Clone opts, since we're going to modify them for IPC.
     opts = Object.assign({}, opts);
     let id = this.nextId++;
     opts.id = id;
@@ -656,67 +671,40 @@ HttpObserverManager = {
         }
       });
     } else if (lastActivity !== this.GOOD_LAST_ACTIVITY &&
                lastActivity !== nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE) {
       channel.lastActivity = activitySubtype;
     }
   },
 
-  shouldRunListener(policyType, uri, filter) {
-    // force the protocol to be ws again.
-    if (policyType == "websocket" && ["http", "https"].includes(uri.scheme)) {
-      uri = Services.io.newURI(`ws${uri.spec.substring(4)}`);
-    }
-
-    if (filter.types && !filter.types.includes(policyType)) {
-      return false;
-    }
-
-    return !filter.urls || filter.urls.matches(uri);
-  },
-
   getRequestData(channel, extraData) {
     let data = {
       requestId: String(channel.id),
       url: channel.finalURL,
-      URI: channel.finalURI,
       method: channel.method,
       browser: channel.browserElement,
       type: channel.type,
       fromCache: channel.fromCache,
 
       originUrl: channel.originURL || undefined,
       documentUrl: channel.documentURL || undefined,
-      originURI: channel.originURI,
-      documentURI: channel.documentURI,
-      isSystemPrincipal: channel.isSystemLoad,
 
       windowId: channel.windowId,
       parentWindowId: channel.parentWindowId,
 
+      frameAncestors: channel.frameAncestors || undefined,
+
       ip: channel.remoteAddress,
 
       proxyInfo: channel.proxyInfo,
 
       serialize: serializeRequestData,
     };
 
-    try {
-      let {frameAncestors} = channel;
-      if (frameAncestors !== null) {
-        data.frameAncestors = frameAncestors;
-      }
-    } catch (e) {}
-
-    // force the protocol to be ws again.
-    if (data.type == "websocket" && data.url.startsWith("http")) {
-      data.url = `ws${data.url.substring(4)}`;
-    }
-
     return Object.assign(data, extraData);
   },
 
   registerChannel(channel, opts) {
     if (!opts.blockingAllowed || !opts.addonId) {
       return;
     }
 
@@ -769,20 +757,19 @@ HttpObserverManager = {
       if (kind !== "onError" && channel.errorString) {
         return;
       }
 
       let includeStatus = ["headersReceived", "authRequired", "onRedirect", "onStart", "onStop"].includes(kind);
       let registerFilter = ["opening", "modify", "afterModify", "headersReceived", "authRequired", "onRedirect"].includes(kind);
 
       let commonData = null;
-      let uri = channel.finalURI;
       let requestBody;
       for (let [callback, opts] of this.listeners[kind].entries()) {
-        if (!this.shouldRunListener(channel.type, uri, opts.filter)) {
+        if (!channel.matches(opts.filter, opts.extension, extraData)) {
           continue;
         }
 
         if (!commonData) {
           commonData = this.getRequestData(channel, extraData);
           if (includeStatus) {
             commonData.statusCode = channel.statusCode;
             commonData.statusLine = channel.statusLine;
@@ -897,35 +884,36 @@ HttpObserverManager = {
     }
 
     // Only resume the channel if it was suspended by this call.
     if (shouldResume) {
       channel.suspended = false;
     }
   },
 
-  shouldHookListener(listener, channel) {
+  shouldHookListener(listener, channel, extraData) {
     if (listener.size == 0) {
       return false;
     }
 
     for (let opts of listener.values()) {
-      if (this.shouldRunListener(channel.type, channel.finalURI, opts.filter)) {
+      if (channel.matches(opts.filter, opts.extension, extraData)) {
         return true;
       }
     }
     return false;
   },
 
   examine(channel, topic, data) {
     if (this.listeners.headersReceived.size) {
       this.runChannelListener(channel, "headersReceived");
     }
 
-    if (!channel.hasAuthRequestor && this.shouldHookListener(this.listeners.authRequired, channel)) {
+    if (!channel.hasAuthRequestor &&
+        this.shouldHookListener(this.listeners.authRequired, channel, {isProxy: true})) {
       channel.channel.notificationCallbacks = new AuthRequestor(channel.channel, this);
       channel.hasAuthRequestor = true;
     }
   },
 
   onChannelReplaced(oldChannel, newChannel) {
     let channel = this.getWrapper(oldChannel);
 
--- a/toolkit/modules/tests/browser/browser_WebRequest_filtering.js
+++ b/toolkit/modules/tests/browser/browser_WebRequest_filtering.js
@@ -1,14 +1,13 @@
 "use strict";
 
 var { interfaces: Ci, classes: Cc, utils: Cu, results: Cr } = Components;
 
 var {WebRequest} = Cu.import("resource://gre/modules/WebRequest.jsm", {});
-var {MatchPattern} = Cu.import("resource://gre/modules/MatchPattern.jsm", {});
 
 const BASE = "http://example.com/browser/toolkit/modules/tests/browser";
 const URL = BASE + "/file_WebRequest_page2.html";
 
 var requested = [];
 
 function onBeforeRequest(details) {
   info(`onBeforeRequest ${details.url}`);
@@ -60,17 +59,17 @@ function compareLists(list1, list2, kind
 }
 
 add_task(async function setup() {
   // Disable rcwn to make cache behavior deterministic.
   await SpecialPowers.pushPrefEnv({set: [["network.http.rcwn.enabled", false]]});
 });
 
 add_task(async function filter_urls() {
-  let filter = {urls: new MatchPattern("*://*/*_style_*")};
+  let filter = {urls: new MatchPatternSet(["*://*/*_style_*"])};
 
   WebRequest.onBeforeRequest.addListener(onBeforeRequest, filter, ["blocking"]);
   WebRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, filter, ["blocking"]);
   WebRequest.onResponseStarted.addListener(onResponseStarted, filter);
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, URL);
 
   await waitForLoad();