Bug 1012917 - Send HttpBaseChannel::mApplyConversion with ChannelDiverter constructor in IPDL r=reuben r=mayhemer
☠☠ backed out by a25309077ac7 ☠ ☠
authorSteve Workman <sworkman@mozilla.com>
Thu, 28 Aug 2014 10:58:20 -0700
changeset 223870 2f94e060bac3b2e4b22b047e10f993418edbb0f8
parent 223868 c105712ba3e0d0257e6de4ea51d803fcefb7bfea
child 223871 fe22fccea4aea9abcf32727cc9b94c2606847250
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersreuben, mayhemer
bugs1012917
milestone34.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 1012917 - Send HttpBaseChannel::mApplyConversion with ChannelDiverter constructor in IPDL r=reuben r=mayhemer
netwerk/base/src/ChannelDiverterParent.cpp
netwerk/base/src/ChannelDiverterParent.h
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/nsHttpChannel.cpp
uriloader/exthandler/ExternalHelperAppChild.cpp
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
--- a/netwerk/base/src/ChannelDiverterParent.cpp
+++ b/netwerk/base/src/ChannelDiverterParent.cpp
@@ -19,29 +19,33 @@ ChannelDiverterParent::ChannelDiverterPa
 {
 }
 
 ChannelDiverterParent::~ChannelDiverterParent()
 {
 }
 
 bool
-ChannelDiverterParent::Init(const ChannelDiverterArgs& aChannel)
+ChannelDiverterParent::Init(const ChannelDiverterArgs& aArgs)
 {
-  switch (aChannel.type()) {
-  case ChannelDiverterArgs::TPHttpChannelParent:
+  switch (aArgs.type()) {
+  case ChannelDiverterArgs::THttpChannelDiverterArgs:
   {
-    mDivertableChannelParent = static_cast<ADivertableParentChannel*>(
-      static_cast<HttpChannelParent*>(aChannel.get_PHttpChannelParent()));
+    auto httpParent = static_cast<HttpChannelParent*>(
+      aArgs.get_HttpChannelDiverterArgs().mChannelParent());
+    httpParent->SetApplyConversion(aArgs.get_HttpChannelDiverterArgs().mApplyConversion());
+
+    mDivertableChannelParent =
+      static_cast<ADivertableParentChannel*>(httpParent);
     break;
   }
   case ChannelDiverterArgs::TPFTPChannelParent:
   {
     mDivertableChannelParent = static_cast<ADivertableParentChannel*>(
-      static_cast<FTPChannelParent*>(aChannel.get_PFTPChannelParent()));
+      static_cast<FTPChannelParent*>(aArgs.get_PFTPChannelParent()));
     break;
   }
   default:
     NS_NOTREACHED("unknown ChannelDiverterArgs type");
     return false;
   }
   MOZ_ASSERT(mDivertableChannelParent);
 
--- a/netwerk/base/src/ChannelDiverterParent.h
+++ b/netwerk/base/src/ChannelDiverterParent.h
@@ -19,17 +19,17 @@ class ADivertableParentChannel;
 
 class ChannelDiverterParent :
   public PChannelDiverterParent
 {
 public:
   ChannelDiverterParent();
   virtual ~ChannelDiverterParent();
 
-  bool Init(const ChannelDiverterArgs& aChannel);
+  bool Init(const ChannelDiverterArgs& aArgs);
 
   void DivertTo(nsIStreamListener* newListener);
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
 private:
   nsRefPtr<ADivertableParentChannel> mDivertableChannelParent;
 };
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -80,19 +80,25 @@ struct FTPChannelConnectArgs
 };
 
 union FTPChannelCreationArgs
 {
   FTPChannelOpenArgs;      // For AsyncOpen: the common case.
   FTPChannelConnectArgs;   // Used for redirected-to channels
 };
 
+struct HttpChannelDiverterArgs
+{
+  PHttpChannel mChannel;
+  bool mApplyConversion;
+};
+
 union ChannelDiverterArgs
 {
-  PHttpChannel;
+  HttpChannelDiverterArgs;
   PFTPChannel;
 };
 
 //-----------------------------------------------------------------------------
 // RTSP IPDL structs
 //-----------------------------------------------------------------------------
 
 struct RtspChannelConnectArgs
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -599,16 +599,25 @@ HttpBaseChannel::GetApplyConversion(bool
 NS_IMETHODIMP
 HttpBaseChannel::SetApplyConversion(bool value)
 {
   LOG(("HttpBaseChannel::SetApplyConversion [this=%p value=%d]\n", this, value));
   mApplyConversion = value;
   return NS_OK;
 }
 
+nsresult
+HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener,
+                                           nsIStreamListener** aNewNextListener)
+{
+  return DoApplyContentConversions(aNextListener,
+                                   aNewNextListener,
+                                   mListenerContext);
+}
+
 NS_IMETHODIMP
 HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener,
                                            nsIStreamListener** aNewNextListener,
                                            nsISupports *aCtxt)
 {
   *aNewNextListener = nullptr;
   nsCOMPtr<nsIStreamListener> nextListener = aNextListener;
   if (!mResponseHead)
@@ -617,32 +626,29 @@ HttpBaseChannel::DoApplyContentConversio
   LOG(("HttpBaseChannel::DoApplyContentConversions [this=%p]\n", this));
 
   if (!mApplyConversion) {
     LOG(("not applying conversion per mApplyConversion\n"));
     return NS_OK;
   }
 
   nsAutoCString contentEncoding;
-  char *cePtr, *val;
-  nsresult rv;
-
-  rv = mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
+  nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
   if (NS_FAILED(rv) || contentEncoding.IsEmpty())
     return NS_OK;
 
   // The encodings are listed in the order they were applied
   // (see rfc 2616 section 14.11), so they need to removed in reverse
   // order. This is accomplished because the converter chain ends up
   // being a stack with the last converter created being the first one
   // to accept the raw network data.
 
-  cePtr = contentEncoding.BeginWriting();
+  char* cePtr = contentEncoding.BeginWriting();
   uint32_t count = 0;
-  while ((val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr))) {
+  while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) {
     if (++count > 16) {
       // That's ridiculous. We only understand 2 different ones :)
       // but for compatibility with old code, we will just carry on without
       // removing the encodings
       LOG(("Too many Content-Encodings. Ignoring remainder.\n"));
       break;
     }
 
@@ -667,25 +673,24 @@ HttpBaseChannel::DoApplyContentConversio
                                   aCtxt,
                                   getter_AddRefs(converter));
       if (NS_FAILED(rv)) {
         LOG(("Unexpected failure of AsyncConvertData %s\n", val));
         return rv;
       }
 
       LOG(("converter removed '%s' content-encoding\n", val));
-      nextListener = converter;
+      nextListener = converter.forget();
     }
     else {
       if (val)
         LOG(("Unknown content encoding '%s', ignoring\n", val));
     }
   }
-  *aNewNextListener = nextListener;
-  NS_ADDREF(*aNewNextListener);
+  nextListener.forget(aNewNextListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings)
 {
   if (!mResponseHead) {
     *aEncodings = nullptr;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -114,16 +114,19 @@ public:
   NS_IMETHOD GetContentLength(int64_t *aContentLength);
   NS_IMETHOD SetContentLength(int64_t aContentLength);
   NS_IMETHOD Open(nsIInputStream **aResult);
 
   // nsIEncodedChannel
   NS_IMETHOD GetApplyConversion(bool *value);
   NS_IMETHOD SetApplyConversion(bool value);
   NS_IMETHOD GetContentEncodings(nsIUTF8StringEnumerator** aEncodings);
+  NS_IMETHOD DoApplyContentConversions(nsIStreamListener *aNextListener,
+                                       nsIStreamListener **aNewNextListener,
+                                       nsISupports *aCtxt);
 
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD GetRequestMethod(nsACString& aMethod);
   NS_IMETHOD SetRequestMethod(const nsACString& aMethod);
   NS_IMETHOD GetReferrer(nsIURI **referrer);
   NS_IMETHOD SetReferrer(nsIURI *referrer);
   NS_IMETHOD GetRequestHeader(const nsACString& aHeader, nsACString& aValue);
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
@@ -225,30 +228,31 @@ public:
 public: /* Necko internal use only... */
 
 
     // Return whether upon a redirect code of httpStatus for method, the
     // request method should be rewritten to GET.
     static bool ShouldRewriteRedirectToGET(uint32_t httpStatus,
                                            nsHttpRequestHead::ParsedMethodType method);
 
+    // Like nsIEncodedChannel::DoApplyConversions except context is set to
+    // mListenerContext.
+    nsresult DoApplyContentConversions(nsIStreamListener *aNextListener,
+                                       nsIStreamListener **aNewNextListener);
+
 protected:
   nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;
 
   // Handle notifying listener, removing from loadgroup if request failed.
   void     DoNotifyListener();
   virtual void DoNotifyListenerCleanup() = 0;
 
   // drop reference to listener, its callbacks, and the progress sink
   void ReleaseListeners();
 
-  NS_IMETHOD DoApplyContentConversions(nsIStreamListener *aNextListener,
-                                     nsIStreamListener **aNewNextListener,
-                                     nsISupports *aCtxt);
-
   void AddCookiesToRequest();
   virtual nsresult SetupReplacementChannel(nsIURI *,
                                            nsIChannel *,
                                            bool preserveMethod);
 
   // bundle calling OMR observers and marking flag into one function
   inline void CallOnModifyRequestObservers() {
     gHttpHandler->OnModifyRequest(this);
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -333,18 +333,17 @@ HttpChannelChild::OnStartRequest(const n
       mLoadGroup->RemoveRequest(this, nullptr, mStatus);
     }
   }
 
   if (mResponseHead)
     SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
 
   nsCOMPtr<nsIStreamListener> listener;
-  rv = DoApplyContentConversions(mListener, getter_AddRefs(listener),
-                                 mListenerContext);
+  rv = DoApplyContentConversions(mListener, getter_AddRefs(listener));
   if (NS_FAILED(rv)) {
     Cancel(rv);
   } else if (listener) {
     mListener = listener;
   }
 
   mSelfAddr = selfAddr;
   mPeerAddr = peerAddr;
@@ -1667,18 +1666,22 @@ HttpChannelChild::DivertToParent(Channel
   nsresult rv = Suspend();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Once this is set, it should not be unset before the child is taken down.
   mDivertingToParent = true;
 
+  HttpChannelDiverterArgs args;
+  args.mChannelChild() = this;
+  args.mApplyConversion() = mApplyConversion;
+
   PChannelDiverterChild* diverter =
-    gNeckoChild->SendPChannelDiverterConstructor(this);
+    gNeckoChild->SendPChannelDiverterConstructor(args);
   MOZ_RELEASE_ASSERT(diverter);
 
   *aChild = static_cast<ChannelDiverterChild*>(diverter);
 
   return NS_OK;
 }
 
 }} // mozilla::net
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -493,17 +493,24 @@ HttpChannelParent::RecvDivertOnDataAvail
   if (NS_FAILED(rv)) {
     if (mChannel) {
       mChannel->Cancel(rv);
     }
     mStatus = rv;
     return true;
   }
 
-  rv = mParentListener->OnDataAvailable(mChannel, nullptr, stringStream,
+  nsCOMPtr<nsIStreamListener> listener;
+  if (mConverterListener) {
+    listener = mConverterListener;
+  } else {
+    listener = mParentListener;
+    MOZ_ASSERT(listener);
+  }
+  rv = listener->OnDataAvailable(mChannel, nullptr, stringStream,
                                         offset, count);
   stringStream->Close();
   if (NS_FAILED(rv)) {
     if (mChannel) {
       mChannel->Cancel(rv);
     }
     mStatus = rv;
     return true;
@@ -525,25 +532,33 @@ HttpChannelParent::RecvDivertOnStopReque
   // Honor the channel's status even if the underlying transaction completed.
   nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
 
   // Reset fake pending status in case OnStopRequest has already been called.
   if (mChannel) {
     mChannel->ForcePending(false);
   }
 
-  mParentListener->OnStopRequest(mChannel, nullptr, status);
+  nsCOMPtr<nsIStreamListener> listener;
+  if (mConverterListener) {
+    listener = mConverterListener;
+  } else {
+    listener = mParentListener;
+    MOZ_ASSERT(listener);
+  }
+  listener->OnStopRequest(mChannel, nullptr, status);
   return true;
 }
 
 bool
 HttpChannelParent::RecvDivertComplete()
 {
   MOZ_ASSERT(mParentListener);
   mParentListener = nullptr;
+  mConverterListener = nullptr;
   if (NS_WARN_IF(!mDivertingFromChild)) {
     MOZ_ASSERT(mDivertingFromChild,
                "Cannot RecvDivertComplete if diverting is not set!");
     FailDiversion(NS_ERROR_UNEXPECTED);
     return false;
   }
 
   nsresult rv = ResumeForDiversion();
@@ -910,18 +925,40 @@ HttpChannelParent::StartDiversion()
   if (NS_FAILED(rv)) {
     if (mChannel) {
       mChannel->Cancel(rv);
     }
     mStatus = rv;
   }
   mDivertedOnStartRequest = true;
 
-  // After OnStartRequest has been called, tell HttpChannelChild to divert the
-  // OnDataAvailables and OnStopRequest to this HttpChannelParent.
+  // After OnStartRequest has been called, setup content decoders if needed.
+  //
+  // We want to use the same decoders that the nsHttpChannel might use. There
+  // are two possible scenarios depending on whether OnStopRequest has been
+  // called or not.
+  //
+  // 1. nsHttpChannel has not called OnStopRequest yet.
+  // Create content conversion chain based on nsHttpChannel::mListener
+  // Get ptr to final listener and set that in mContentConverter, as well as
+  // nsHttpChannel::mListener.
+  //
+  // 2. nsHttpChannel has called OnStopRequest.
+  // Create a content conversion chain based on mParentListener.
+  // Get ptr to final listener and set that in mContentConverter.
+
+  nsCOMPtr<nsIStreamListener> converterListener;
+  mChannel->DoApplyContentConversions(mParentListener,
+                                      getter_AddRefs(converterListener));
+  if (converterListener) {
+    mConverterListener = converterListener.forget();
+  }
+
+  // The listener chain should now be setup; tell HttpChannelChild to divert
+  // the OnDataAvailables and OnStopRequest to this HttpChannelParent.
   if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
     FailDiversion(NS_ERROR_UNEXPECTED);
     return;
   }
 }
 
 class HTTPFailDiversionEvent : public nsRunnable
 {
@@ -989,16 +1026,17 @@ HttpChannelParent::NotifyDiversionFailed
     mChannel->ForcePending(false);
   }
   // If the channel is pending, it will call OnStopRequest itself; otherwise, do
   // it here.
   if (!isPending) {
     mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode);
   }
   mParentListener = nullptr;
+  mConverterListener = nullptr;
   mChannel = nullptr;
 
   if (!mIPCClosed) {
     unused << SendDeleteSelf();
   }
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -65,16 +65,23 @@ public:
   // that it should divert OnDataAvailable and OnStopRequest calls to this
   // parent channel.
   void StartDiversion();
 
   // Handles calling OnStart/Stop if there are errors during diversion.
   // Called asynchronously from FailDiversion.
   void NotifyDiversionFailed(nsresult aErrorCode, bool aSkipResume = true);
 
+  // Forwarded to nsHttpChannel::SetApplyConversion.
+  void SetApplyConversion(bool aApplyConversion) {
+    if (mChannel) {
+      mChannel->SetApplyConversion(aApplyConversion);
+    }
+  }
+
 protected:
   // used to connect redirected-to channel in parent with just created
   // ChildChannel.  Used during redirects.
   bool ConnectChannel(const uint32_t& channelId);
 
   bool DoAsyncOpen(const URIParams&           uri,
                    const OptionalURIParams&   originalUri,
                    const OptionalURIParams&   docUri,
@@ -148,16 +155,18 @@ private:
   bool mReceivedRedirect2Verify     : 1;
 
   PBOverrideStatus mPBOverride;
 
   nsCOMPtr<nsILoadContext> mLoadContext;
   nsRefPtr<nsHttpHandler>  mHttpHandler;
 
   nsRefPtr<HttpChannelParentListener> mParentListener;
+  // The first listener in the decode chain if channel decoding is applied.
+  nsCOMPtr<nsIStreamListener> mConverterListener;
   // Set to the canceled status value if the main channel was canceled.
   nsresult mStatus;
   // Once set, no OnStart/OnData/OnStop calls should be accepted; conversely, it
   // must be set when RecvDivertOnData/~DivertOnStop/~DivertComplete are
   // received from the child channel.
   bool mDivertingFromChild;
 
   // Set if OnStart|StopRequest was called during a diversion from the child.
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -917,18 +917,17 @@ nsHttpChannel::CallOnStartRequest()
         NS_WARNING("OnStartRequest skipped because of null listener");
     }
 
     // Install stream converter if required.
     // If we use unknownDecoder, stream converters will be installed later (in
     // nsUnknownDecoder) after OnStartRequest is called for the real listener.
     if (!unknownDecoderStarted) {
       nsCOMPtr<nsIStreamListener> listener;
-      nsISupports *ctxt = mListenerContext;
-      rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), ctxt);
+      rv = DoApplyContentConversions(mListener, getter_AddRefs(listener));
       if (NS_FAILED(rv)) {
         return rv;
       }
       if (listener) {
         mListener = listener;
       }
     }
 
--- a/uriloader/exthandler/ExternalHelperAppChild.cpp
+++ b/uriloader/exthandler/ExternalHelperAppChild.cpp
@@ -88,25 +88,30 @@ ExternalHelperAppChild::OnStopRequest(ns
     SendOnStopRequest(status);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
   }
 
   return NS_OK;
 }
 
 nsresult
-ExternalHelperAppChild::DivertToParent(nsIDivertableChannel *divertable, nsIRequest *request)
+ExternalHelperAppChild::DivertToParent(nsIDivertableChannel *divertable,
+                                       nsIRequest *request)
 {
+  // nsIDivertable must know about content conversions before being diverted.
+  MOZ_ASSERT(mHandler);
+  mHandler->MaybeApplyDecodingForExtension(request);
+
   mozilla::net::ChannelDiverterChild *diverter = nullptr;
   nsresult rv = divertable->DivertToParent(&diverter);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+  MOZ_ASSERT(diverter);
 
-  MOZ_ASSERT(diverter);
   if (SendDivertToParentUsing(diverter)) {
     mHandler->DidDivertRequest(request);
     mHandler = nullptr;
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -1491,16 +1491,61 @@ nsresult nsExternalAppHandler::SetUpTemp
   LOG(("Enabled hashing and signature verification"));
 
   rv = mSaver->SetTarget(mTempFile, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return rv;
 }
 
+void
+nsExternalAppHandler::MaybeApplyDecodingForExtension(nsIRequest *aRequest)
+{
+  MOZ_ASSERT(aRequest);
+
+  nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface(aRequest);
+  if (!encChannel) {
+    return;
+  }
+
+  // Turn off content encoding conversions if needed
+  bool applyConversion = true;
+
+  nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(mSourceUrl));
+  if (sourceURL)
+  {
+    nsAutoCString extension;
+    sourceURL->GetFileExtension(extension);
+    if (!extension.IsEmpty())
+    {
+      nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
+      encChannel->GetContentEncodings(getter_AddRefs(encEnum));
+      if (encEnum)
+      {
+        bool hasMore;
+        nsresult rv = encEnum->HasMore(&hasMore);
+        if (NS_SUCCEEDED(rv) && hasMore)
+        {
+          nsAutoCString encType;
+          rv = encEnum->GetNext(encType);
+          if (NS_SUCCEEDED(rv) && !encType.IsEmpty())
+          {
+            MOZ_ASSERT(mExtProtSvc);
+            mExtProtSvc->ApplyDecodingForExtension(extension, encType,
+                                                   &applyConversion);
+          }
+        }
+      }
+    }
+  }
+
+  encChannel->SetApplyConversion( applyConversion );
+  return;
+}
+
 NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
 {
   NS_PRECONDITION(request, "OnStartRequest without request?");
 
   // Set mTimeDownloadStarted here as the download has already started and
   // we want to record the start time before showing the filepicker.
   mTimeDownloadStarted = PR_Now();
 
@@ -1553,45 +1598,17 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt
   MaybeCloseWindow();
 
   // In an IPC setting, we're allowing the child process, here, to make
   // decisions about decoding the channel (e.g. decompression).  It will
   // still forward the decoded (uncompressed) data back to the parent.
   // Con: Uncompressed data means more IPC overhead.
   // Pros: ExternalHelperAppParent doesn't need to implement nsIEncodedChannel.
   //       Parent process doesn't need to expect CPU time on decompression.
-  nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface( aChannel );
-  if (encChannel) {
-    // Turn off content encoding conversions if needed
-    bool applyConversion = true;
-
-    nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(mSourceUrl));
-    if (sourceURL) {
-      nsAutoCString extension;
-      sourceURL->GetFileExtension(extension);
-      if (!extension.IsEmpty()) {
-        nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
-        encChannel->GetContentEncodings(getter_AddRefs(encEnum));
-        if (encEnum) {
-          bool hasMore;
-          rv = encEnum->HasMore(&hasMore);
-          if (NS_SUCCEEDED(rv) && hasMore) {
-            nsAutoCString encType;
-            rv = encEnum->GetNext(encType);
-            if (NS_SUCCEEDED(rv) && !encType.IsEmpty()) {
-              mExtProtSvc->ApplyDecodingForExtension(extension, encType,
-                                                     &applyConversion);
-            }
-          }
-        }
-      }    
-    }
-
-    encChannel->SetApplyConversion( applyConversion );
-  }
+  MaybeApplyDecodingForExtension(aChannel);
 
   // At this point, the child process has done everything it can usefully do
   // for OnStartRequest.
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     return NS_OK;
   }
 
   rv = SetUpTempFile(aChannel);
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -240,16 +240,21 @@ public:
                        const nsAString& aFilename,
                        uint32_t aReason, bool aForceSave);
 
   /**
    * Clean up after the request was diverted to the parent process.
    */
   void DidDivertRequest(nsIRequest *request);
 
+  /**
+   * Apply content conversions if needed.
+   */
+  void MaybeApplyDecodingForExtension(nsIRequest *request);
+
 protected:
   ~nsExternalAppHandler();
 
   nsIInterfaceRequestor* GetDialogParent() {
     return mWindowContext ? mWindowContext : mContentContext;
   }
 
   nsCOMPtr<nsIFile> mTempFile;