Bug 1147695 - Enable interception of beacons through service workers; r=nsm
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 25 Mar 2015 21:23:52 -0400
changeset 266692 256a54bc4d9663e8ac7741ec9e95b99d89352607
parent 266691 d2f789b621773ae9fa3aa27044d68c67a0993adc
child 266693 84792fb8d1bd85d04c49f7f12489871d4757daf5
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnsm
bugs1147695, 1147699
milestone39.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 1147695 - Enable interception of beacons through service workers; r=nsm Currently when sending a beacon, HttpBaseChannel::ShouldIntercept tries to get access to the nsINetworkInterceptController interface through the channel's notification callbacks, but in this case the notification callback is the nsCORSListenerProxy object (thanks to nsCORSListenerProxy::Init). nsCORSListenerProxy already knows how to forward calls to nsIInterfaceRequestor::GetInterface to mOuterNotificationCallbacks, and ShouldIntercept calls GetInterfce. But mOuterNotificationCallbacks is set by default to the callbacks of the channel at the time nsCORSListenerProxy is called, and the callbacks on this channel is intentionally null, so ShouldIntercept bails out and the beacon never gets intercepted. This patch extends nsCORSListenerProxy to make it aware of nsINetworkInterceptController, and have it route the request for nsINetworkInterceptController correctly to the docshell without the need to mess with the notification callbacks. This will be tested in bug 1147699.
dom/base/Navigator.cpp
dom/security/nsCORSListenerProxy.cpp
dom/security/nsCORSListenerProxy.h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -65,16 +65,17 @@
 #include "TimeManager.h"
 #include "DeviceStorage.h"
 #include "nsIDOMNavigatorSystemMessages.h"
 #include "nsStreamUtils.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "WidgetUtils.h"
 #include "mozIThirdPartyUtil.h"
+#include "nsINetworkInterceptController.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 #include "mozilla/dom/MediaDevices.h"
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #endif
@@ -1066,19 +1067,19 @@ Navigator::SendBeacon(const nsAString& a
                      nsILoadInfo::SEC_NORMAL,
                      nsIContentPolicy::TYPE_BEACON);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
 
+  nsIDocShell* docShell = mWindow->GetDocShell();
   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
   if (pbChannel) {
-    nsIDocShell* docShell = mWindow->GetDocShell();
     nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
     if (loadContext) {
       rv = pbChannel->SetPrivate(loadContext->UsePrivateBrowsing());
       if (NS_FAILED(rv)) {
         NS_WARNING("Setting the privacy status on the beacon channel failed");
       }
     }
   }
@@ -1202,16 +1203,19 @@ Navigator::SendBeacon(const nsAString& a
 
   nsRefPtr<nsCORSListenerProxy> cors = new nsCORSListenerProxy(new BeaconStreamListener(),
                                                                principal,
                                                                true);
 
   rv = cors->Init(channel, true);
   NS_ENSURE_SUCCESS(rv, false);
 
+  nsCOMPtr<nsINetworkInterceptController> interceptController = do_QueryInterface(docShell);
+  cors->SetInterceptController(interceptController);
+
   // Start a preflight if cross-origin and content type is not whitelisted
   rv = secMan->CheckSameOriginURI(documentURI, uri, false);
   bool crossOrigin = NS_FAILED(rv);
   nsAutoCString contentType, parsedCharset;
   rv = NS_ParseContentType(mimeType, contentType, parsedCharset);
   if (crossOrigin &&
       contentType.Length() > 0 &&
       !contentType.Equals(APPLICATION_WWW_FORM_URLENCODED) &&
--- a/dom/security/nsCORSListenerProxy.cpp
+++ b/dom/security/nsCORSListenerProxy.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/Preferences.h"
 #include "nsIScriptError.h"
 #include "nsILoadGroup.h"
 #include "nsILoadContext.h"
 #include "nsIConsoleService.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIDOMWindow.h"
+#include "nsINetworkInterceptController.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 #define PREFLIGHT_CACHE_SIZE 100
 
 static bool gDisableCORS = false;
 static bool gDisableCORSPrivateData = false;
@@ -672,26 +673,40 @@ nsCORSListenerProxy::OnDataAvailable(nsI
   MOZ_ASSERT(mInited, "nsCORSListenerProxy has not been initialized properly");
   if (!mRequestApproved) {
     return NS_ERROR_DOM_BAD_URI;
   }
   return mOuterListener->OnDataAvailable(aRequest, aContext, aInputStream,
                                          aOffset, aCount);
 }
 
+void
+nsCORSListenerProxy::SetInterceptController(nsINetworkInterceptController* aInterceptController)
+{
+  mInterceptController = aInterceptController;
+}
+
 NS_IMETHODIMP
 nsCORSListenerProxy::GetInterface(const nsIID & aIID, void **aResult)
 {
   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
     *aResult = static_cast<nsIChannelEventSink*>(this);
     NS_ADDREF_THIS();
 
     return NS_OK;
   }
 
+  if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
+      mInterceptController) {
+    nsCOMPtr<nsINetworkInterceptController> copy(mInterceptController);
+    *aResult = copy.forget().take();
+
+    return NS_OK;
+  }
+
   return mOuterNotificationCallbacks ?
     mOuterNotificationCallbacks->GetInterface(aIID, aResult) :
     NS_ERROR_NO_INTERFACE;
 }
 
 NS_IMETHODIMP
 nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
                                             nsIChannel *aNewChannel,
--- a/dom/security/nsCORSListenerProxy.h
+++ b/dom/security/nsCORSListenerProxy.h
@@ -15,16 +15,17 @@
 #include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/Attributes.h"
 
 class nsIURI;
 class nsIParser;
 class nsIPrincipal;
+class nsINetworkInterceptController;
 
 nsresult
 NS_StartCORSPreflight(nsIChannel* aRequestChannel,
                       nsIStreamListener* aListener,
                       nsIPrincipal* aPrincipal,
                       bool aWithCredentials,
                       nsTArray<nsCString>& aACUnsafeHeaders,
                       nsIChannel** aPreflightChannel);
@@ -53,29 +54,32 @@ public:
 
   // Must be called at startup.
   static void Startup();
 
   static void Shutdown();
 
   nsresult Init(nsIChannel* aChannel, bool aAllowDataURI = false);
 
+  void SetInterceptController(nsINetworkInterceptController* aInterceptController);
+
 private:
   ~nsCORSListenerProxy();
 
   nsresult UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI = false);
   nsresult CheckRequestApproved(nsIRequest* aRequest);
 
   nsCOMPtr<nsIStreamListener> mOuterListener;
   // The principal that originally kicked off the request
   nsCOMPtr<nsIPrincipal> mRequestingPrincipal;
   // The principal to use for our Origin header ("source origin" in spec terms).
   // This can get changed during redirects, unlike mRequestingPrincipal.
   nsCOMPtr<nsIPrincipal> mOriginHeaderPrincipal;
   nsCOMPtr<nsIInterfaceRequestor> mOuterNotificationCallbacks;
+  nsCOMPtr<nsINetworkInterceptController> mInterceptController;
   bool mWithCredentials;
   bool mRequestApproved;
   bool mHasBeenCrossSite;
   bool mIsPreflight;
 #ifdef DEBUG
   bool mInited;
 #endif
   nsCString mPreflightMethod;