Back out Bug 589292.
authorDan Witte <dwitte@mozilla.com>
Mon, 30 Aug 2010 13:20:38 -0700
changeset 51738 5ab5bd61c50c5ae2cc7221f628890630c58a9bb6
parent 51737 921aea1613a30c09895345de328cc7d5ae5399c6
child 51739 e78e4b846285ef12bfdedc36aa1216f54d09dbf2
push idunknown
push userunknown
push dateunknown
bugs589292
milestone2.0b5pre
Back out Bug 589292.
content/base/src/nsDocument.cpp
content/base/src/nsWebSocket.cpp
content/html/document/src/nsWyciwygChannel.cpp
dom/src/jsurl/nsJSProtocolHandler.cpp
modules/libjar/nsJARChannel.cpp
modules/libjar/nsJARChannel.h
modules/libpr0n/decoders/icon/beos/nsIconChannel.cpp
modules/libpr0n/decoders/icon/mac/nsIconChannelCocoa.mm
modules/libpr0n/decoders/icon/os2/nsIconChannel.cpp
modules/libpr0n/decoders/icon/win/nsIconChannel.cpp
modules/libpr0n/src/imgRequest.cpp
netwerk/base/public/nsChannelProperties.h
netwerk/base/public/nsIChannel.idl
netwerk/base/public/nsIMultiPartChannel.idl
netwerk/base/public/nsNetStrings.h
netwerk/base/public/nsNetUtil.h
netwerk/base/src/nsBaseChannel.cpp
netwerk/base/src/nsBaseChannel.h
netwerk/base/src/nsNetStrings.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
netwerk/streamconv/converters/nsMultiMixedConv.cpp
netwerk/streamconv/converters/nsMultiMixedConv.h
uriloader/base/nsURILoader.cpp
uriloader/exthandler/ExternalHelperAppParent.cpp
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalProtocolHandler.cpp
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6715,21 +6715,24 @@ nsDocument::RetrieveRelevantHeaders(nsIC
 
         if (NS_SUCCEEDED(rv)) {
           PRInt64 intermediateValue;
           LL_I2L(intermediateValue, PR_USEC_PER_MSEC);
           LL_MUL(modDate, msecs, intermediateValue);
         }
       }
     } else {
-      nsCAutoString contentDisp;
-      rv = aChannel->GetContentDisposition(contentDisp);
-      if (NS_SUCCEEDED(rv) && !contentDisp.IsEmpty()) {
-        SetHeaderData(nsGkAtoms::headerContentDisposition,
-                      NS_ConvertASCIItoUTF16(contentDisp));
+      nsCOMPtr<nsIMultiPartChannel> partChannel = do_QueryInterface(aChannel);
+      if (partChannel) {
+        nsCAutoString contentDisp;
+        rv = partChannel->GetContentDisposition(contentDisp);
+        if (NS_SUCCEEDED(rv) && !contentDisp.IsEmpty()) {
+          SetHeaderData(nsGkAtoms::headerContentDisposition,
+                        NS_ConvertASCIItoUTF16(contentDisp));
+        }
       }
     }
   }
 
   if (LL_IS_ZERO(modDate)) {
     // We got nothing from our attempt to ask nsIFileChannel and
     // nsIHttpChannel for the last modified time. Return the current
     // time.
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -2402,17 +2402,16 @@ NOT_IMPLEMENTED_IF_FUNC_1(GetOwner, nsIS
 NOT_IMPLEMENTED_IF_FUNC_1(SetOwner, nsISupports *owner)
 NOT_IMPLEMENTED_IF_FUNC_1(SetNotificationCallbacks,
                           nsIInterfaceRequestor *callbacks)
 NOT_IMPLEMENTED_IF_FUNC_1(GetSecurityInfo, nsISupports **securityInfo)
 NOT_IMPLEMENTED_IF_FUNC_1(GetContentType, nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(SetContentType, const nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(GetContentCharset, nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(SetContentCharset, const nsACString &value)
-NOT_IMPLEMENTED_IF_FUNC_1(GetContentDisposition, nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(GetContentLength, PRInt64 *value)
 NOT_IMPLEMENTED_IF_FUNC_1(SetContentLength, PRInt64 value)
 NOT_IMPLEMENTED_IF_FUNC_1(Open, nsIInputStream **_retval)
 NOT_IMPLEMENTED_IF_FUNC_2(AsyncOpen, nsIStreamListener *listener,
                           nsISupports *context)
 
 //-----------------------------------------------------------------------------
 // nsWebSocketEstablishedConnection::nsIHttpAuthenticableChannel
--- a/content/html/document/src/nsWyciwygChannel.cpp
+++ b/content/html/document/src/nsWyciwygChannel.cpp
@@ -260,23 +260,16 @@ nsWyciwygChannel::GetContentCharset(nsAC
 
 NS_IMETHODIMP
 nsWyciwygChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsWyciwygChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-  aContentDisposition.Truncate();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsWyciwygChannel::GetContentLength(PRInt64 *aContentLength)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsWyciwygChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -1007,22 +1007,16 @@ nsJSChannel::GetContentCharset(nsACStrin
 
 NS_IMETHODIMP
 nsJSChannel::SetContentCharset(const nsACString &aContentCharset)
 {
     return mStreamChannel->SetContentCharset(aContentCharset);
 }
 
 NS_IMETHODIMP
-nsJSChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-    return mStreamChannel->GetContentDisposition(aContentDisposition);
-}
-
-NS_IMETHODIMP
 nsJSChannel::GetContentLength(PRInt64 *aContentLength)
 {
     return mStreamChannel->GetContentLength(aContentLength);
 }
 
 NS_IMETHODIMP
 nsJSChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -627,23 +627,16 @@ nsJARChannel::GetContentCharset(nsACStri
 NS_IMETHODIMP
 nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
 {
     mContentCharset = aContentCharset;
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsJARChannel::GetContentDisposition(nsACString &result)
-{
-    result = mContentDisposition;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
 nsJARChannel::GetContentLength(PRInt64 *result)
 {
     // if content length is unknown, query mJarInput...
     if (mContentLength < 0 && mJarInput)
         mContentLength = mJarInput->GetContentLength();
 
     *result = mContentLength;
     return NS_OK;
@@ -773,45 +766,51 @@ nsJARChannel::OnDownloadComplete(nsIDown
             }
             if (NS_SUCCEEDED(status)) {
                 status = rv;
             }
         }
     }
 
     if (NS_SUCCEEDED(status) && channel) {
+        nsCAutoString header;
         // Grab the security info from our base channel
         channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
 
         nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
         if (httpChannel) {
             // We only want to run scripts if the server really intended to
             // send us a JAR file.  Check the server-supplied content type for
             // a JAR type.
-            nsCAutoString header;
             httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
                                            header);
             nsCAutoString contentType;
             nsCAutoString charset;
             NS_ParseContentType(header, contentType, charset);
             nsCAutoString channelContentType;
             channel->GetContentType(channelContentType);
             mIsUnsafe = !(contentType.Equals(channelContentType) &&
                           (contentType.EqualsLiteral("application/java-archive") ||
                            contentType.EqualsLiteral("application/x-jar")));
+            rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Disposition"),
+                                                header);
+            if (NS_SUCCEEDED(rv))
+                SetPropertyAsACString(NS_CHANNEL_PROP_CONTENT_DISPOSITION, header);
         } else {
             nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel));
             if (innerJARChannel) {
                 PRBool unsafe;
                 innerJARChannel->GetIsUnsafe(&unsafe);
                 mIsUnsafe = unsafe;
             }
+            // Soon-to-be common way to get Disposition: right now only nsIJARChannel
+            rv = NS_GetContentDisposition(request, header);
+            if (NS_SUCCEEDED(rv))
+                SetPropertyAsACString(NS_CHANNEL_PROP_CONTENT_DISPOSITION, header);
         }
-
-        channel->GetContentDisposition(mContentDisposition);
     }
 
     if (NS_SUCCEEDED(status) && mIsUnsafe) {
         PRBool allowUnpack = PR_FALSE;
 
         nsCOMPtr<nsIPrefBranch> prefs =
             do_GetService(NS_PREFSERVICE_CONTRACTID);
         if (prefs) {
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -91,17 +91,16 @@ private:
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsISupports>           mSecurityInfo;
     nsCOMPtr<nsIProgressEventSink>  mProgressSink;
     nsCOMPtr<nsILoadGroup>          mLoadGroup;
     nsCOMPtr<nsIStreamListener>     mListener;
     nsCOMPtr<nsISupports>           mListenerContext;
     nsCString                       mContentType;
     nsCString                       mContentCharset;
-    nsCString                       mContentDisposition;
     PRInt64                         mContentLength;
     PRUint32                        mLoadFlags;
     nsresult                        mStatus;
     PRPackedBool                    mIsPending;
     PRPackedBool                    mIsUnsafe;
 
     nsJARInputThunk                *mJarInput;
     nsCOMPtr<nsIStreamListener>     mDownloader;
--- a/modules/libpr0n/decoders/icon/beos/nsIconChannel.cpp
+++ b/modules/libpr0n/decoders/icon/beos/nsIconChannel.cpp
@@ -402,23 +402,16 @@ NS_IMETHODIMP nsIconChannel::GetContentC
 NS_IMETHODIMP
 nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   // It doesn't make sense to set the content-charset on this type
   // of channel...
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-nsIconChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-  aContentDisposition.Truncate();
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt64 *aContentLength)
 {
   *aContentLength = mContentLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsIconChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/modules/libpr0n/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/modules/libpr0n/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -394,23 +394,16 @@ NS_IMETHODIMP nsIconChannel::GetContentC
 NS_IMETHODIMP
 nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   //It doesn't make sense to set the content-type on this type
   // of channel...
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-nsIconChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-  aContentDisposition.Truncate();
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt64 *aContentLength)
 {
   *aContentLength = mContentLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsIconChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/modules/libpr0n/decoders/icon/os2/nsIconChannel.cpp
+++ b/modules/libpr0n/decoders/icon/os2/nsIconChannel.cpp
@@ -631,23 +631,16 @@ NS_IMETHODIMP nsIconChannel::GetContentC
 NS_IMETHODIMP
 nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   // It doesn't make sense to set the content-charset on this type
   // of channel...
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-nsIconChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-  aContentDisposition.Truncate();
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt64 *aContentLength)
 {
   *aContentLength = mContentLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsIconChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/modules/libpr0n/decoders/icon/win/nsIconChannel.cpp
+++ b/modules/libpr0n/decoders/icon/win/nsIconChannel.cpp
@@ -688,23 +688,16 @@ NS_IMETHODIMP nsIconChannel::GetContentC
 NS_IMETHODIMP
 nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   // It doesn't make sense to set the content-charset on this type
   // of channel...
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-nsIconChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-  aContentDisposition.Truncate();
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt64 *aContentLength)
 {
   *aContentLength = mContentLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsIconChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -983,20 +983,21 @@ NS_IMETHODIMP imgRequest::OnDataAvailabl
      */
     PRUint32 out;
     inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
 
 #ifdef NS_DEBUG
     /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
 #endif
 
-    nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
     if (mContentType.IsEmpty()) {
       LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
 
+      nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
+
       rv = NS_ERROR_FAILURE;
       if (chan) {
         rv = chan->GetContentType(mContentType);
       }
 
       if (NS_FAILED(rv)) {
         PR_LOG(gImgLog, PR_LOG_ERROR,
                ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
@@ -1026,18 +1027,24 @@ NS_IMETHODIMP imgRequest::OnDataAvailabl
     nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
     if (contentType) {
       contentType->SetData(mContentType);
       mProperties->Set("type", contentType);
     }
 
     /* set our content disposition as a property */
     nsCAutoString disposition;
-    if (chan) {
-      chan->GetContentDisposition(disposition);
+    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
+    if (httpChannel) {
+      httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"), disposition);
+    } else {
+      nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
+      if (multiPartChannel) {
+        multiPartChannel->GetContentDisposition(disposition);
+      }
     }
     if (!disposition.IsEmpty()) {
       nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
       if (contentDisposition) {
         contentDisposition->SetData(disposition);
         mProperties->Set("content-disposition", contentDisposition);
       }
     }
@@ -1087,17 +1094,16 @@ NS_IMETHODIMP imgRequest::OnDataAvailabl
     if (NS_FAILED(rv)) { // Probably bad mimetype
 
       this->Cancel(rv);
       return NS_BINDING_ABORTED;
     }
 
     if (imageType == imgIContainer::TYPE_RASTER) {
       /* Use content-length as a size hint for http channels. */
-      nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
       if (httpChannel) {
         PRInt64 contentLength;
         rv = httpChannel->GetContentLength(&contentLength);
         if (NS_SUCCEEDED(rv)) {
           // Pass anything usable on so that the RasterImage can preallocate
           // its source buffer
           if (contentLength > 0) {
             PRUint32 sizeHint = (PRUint32) contentLength;
--- a/netwerk/base/public/nsChannelProperties.h
+++ b/netwerk/base/public/nsChannelProperties.h
@@ -46,22 +46,32 @@
  * @file
  * This file contains constants for properties channels can expose.
  * They can be accessed by using QueryInterface to access the nsIPropertyBag
  * or nsIPropertyBag2 interface on a channel and reading the value.
  */
 
 
 /**
+ * MIME Content-Disposition header of channel.  
+ * Not available before onStartRequest. 
+ * Type: nsACString
+ */
+#define NS_CHANNEL_PROP_CONTENT_DISPOSITION_STR "content-disposition"
+
+/**
  * Exists to allow content policy mechanism to function properly during channel
  * redirects.  Contains security contextual information about the load.
  * Type: nsIChannelPolicy
  */
 #define NS_CHANNEL_PROP_CHANNEL_POLICY_STR "channel-policy"
 
 #ifdef IMPL_NS_NET
+#define NS_CHANNEL_PROP_CONTENT_DISPOSITION gNetStrings->kContentDisposition
 #define NS_CHANNEL_PROP_CHANNEL_POLICY gNetStrings->kChannelPolicy
 #else
+#define NS_CHANNEL_PROP_CONTENT_DISPOSITION \
+  NS_LITERAL_STRING(NS_CHANNEL_PROP_CONTENT_DISPOSITION_STR)
 #define NS_CHANNEL_PROP_CHANNEL_POLICY \
   NS_LITERAL_STRING(NS_CHANNEL_PROP_CHANNEL_POLICY_STR)
 #endif
 
 #endif
--- a/netwerk/base/public/nsIChannel.idl
+++ b/netwerk/base/public/nsIChannel.idl
@@ -51,17 +51,17 @@ interface nsIStreamListener;
  * by calling nsIChannel::open or nsIChannel::asyncOpen.
  *
  * After a request has been completed, the channel is still valid for accessing
  * protocol-specific results.  For example, QI'ing to nsIHttpChannel allows
  * response headers to be retrieved for the corresponding http transaction.
  *
  * This interface must be used only from the XPCOM main thread.
  */
-[scriptable, uuid(3906f857-3d79-4716-be55-ed2455d666f4)]
+[scriptable, uuid(e0bb5c49-c54e-4efb-8f0d-6a7edd926fab)]
 interface nsIChannel : nsIRequest
 {
     /**
      * The original URI used to construct the channel. This is used in
      * the case of a redirect or URI "resolution" (e.g. resolving a
      * resource: URI to a file: URI) so that the original pre-redirect
      * URI can still be obtained.  This is never null.  Attempts to
      * set it to null must throw.
@@ -153,23 +153,16 @@ interface nsIChannel : nsIRequest
      * The length of the data associated with the channel if available.  A value
      * of -1 indicates that the content length is unknown. Note that this
      * is a 64-bit value and obsoletes the "content-length" property used on
      * some channels.
      */
     attribute PRInt64 contentLength;
 
     /**
-     * Access to the Content-Disposition header if available and if applicable.
-     * This allows getting the preferred handling method, preferred filename,
-     * etc.  See RFC 2183.
-     */
-    readonly attribute ACString contentDisposition;
-
-    /**
      * Synchronously open the channel.
      *
      * @return blocking input stream to the channel's data.
      *
      * NOTE: nsIChannel implementations are not required to implement this
      * method.  Moreover, since this method may block the calling thread, it
      * should not be called on a thread that processes UI events.  Like any
      * other nsIChannel method it must not be called on any thread other
--- a/netwerk/base/public/nsIMultiPartChannel.idl
+++ b/netwerk/base/public/nsIMultiPartChannel.idl
@@ -40,25 +40,32 @@
 
 interface nsIChannel;
 
 /**
  * An interface to access the the base channel 
  * associated with a MultiPartChannel.
  */
 
-[scriptable, uuid(51698f28-c975-4bce-a951-25130cda0113)]
+[scriptable, uuid(ba78db7b-b88c-4b76-baf9-3c2296a585ae)]
 interface nsIMultiPartChannel : nsISupports
 {
     /**
      * readonly attribute to access the underlying channel
      */
     readonly attribute nsIChannel baseChannel;
 
     /**
+     * Access to the Content-Disposition header field of this part of
+     * a multipart message.  This allows getting the preferred
+     * handling method, preferred filename, etc.  See RFC 2183.
+     */
+    attribute ACString contentDisposition;
+
+    /**
      * Attribute guaranteed to be different for different parts of
      * the same multipart document.
      */
     readonly attribute PRUint32 partID;
 
     /**
      * Set to true when onStopRequest is received from the base channel.
      * The listener can check this from its onStopRequest to determine
--- a/netwerk/base/public/nsNetStrings.h
+++ b/netwerk/base/public/nsNetStrings.h
@@ -42,15 +42,16 @@
 /**
  * Class on which wide strings are available, to avoid constructing strings
  * wherever these strings are used.
  */
 class nsNetStrings {
 public:
   nsNetStrings();
 
+  const nsLiteralString kContentDisposition;
   const nsLiteralString kChannelPolicy;
 };
 
 extern NS_HIDDEN_(nsNetStrings*) gNetStrings;
 
 
 #endif
--- a/netwerk/base/public/nsNetUtil.h
+++ b/netwerk/base/public/nsNetUtil.h
@@ -233,16 +233,29 @@ NS_NewChannel(nsIChannel           **res
             }
             if (NS_SUCCEEDED(rv))
                 chan.forget(result);
         }
     }
     return rv;
 }
 
+// For now, works only with JARChannel.  Future: with all channels that may
+// have Content-Disposition header (JAR, nsIHttpChannel, and nsIMultiPartChannel).
+inline nsresult
+NS_GetContentDisposition(nsIRequest     *channel,
+                         nsACString     &result)
+{
+    nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(channel));
+    if (props)
+        return props->GetPropertyAsACString(NS_CHANNEL_PROP_CONTENT_DISPOSITION,
+                                            result);
+    return NS_ERROR_NOT_AVAILABLE;
+}
+
 // Use this function with CAUTION. It creates a stream that blocks when you
 // Read() from it and blocking the UI thread is a bad idea. If you don't want
 // to implement a full blown asynchronous consumer (via nsIStreamListener) look
 // at nsIStreamLoader instead.
 inline nsresult
 NS_OpenURI(nsIInputStream       **result,
            nsIURI                *uri,
            nsIIOService          *ioService = nsnull,     // pass in nsIIOService to optimize callers
--- a/netwerk/base/src/nsBaseChannel.cpp
+++ b/netwerk/base/src/nsBaseChannel.cpp
@@ -499,23 +499,16 @@ nsBaseChannel::GetContentCharset(nsACStr
 NS_IMETHODIMP
 nsBaseChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   mContentCharset = aContentCharset;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsBaseChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-  aContentDisposition = mContentDisposition;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsBaseChannel::GetContentLength(PRInt64 *aContentLength)
 {
   *aContentLength = mContentLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBaseChannel::SetContentLength(PRInt64 aContentLength)
--- a/netwerk/base/src/nsBaseChannel.h
+++ b/netwerk/base/src/nsBaseChannel.h
@@ -285,17 +285,16 @@ private:
   nsCOMPtr<nsILoadGroup>              mLoadGroup;
   nsCOMPtr<nsISupports>               mOwner;
   nsCOMPtr<nsISupports>               mSecurityInfo;
   nsCOMPtr<nsIStreamListener>         mListener;
   nsCOMPtr<nsISupports>               mListenerContext;
   nsCOMPtr<nsIChannel>                mRedirectChannel;
   nsCString                           mContentType;
   nsCString                           mContentCharset;
-  nsCString                           mContentDisposition;
   PRInt64                             mContentLength;
   PRUint32                            mLoadFlags;
   nsresult                            mStatus;
   PRPackedBool                        mQueriedProgressSink;
   PRPackedBool                        mSynthProgressEvents;
   PRPackedBool                        mWasOpened;
   PRPackedBool                        mWaitingOnAsyncRedirect;
   PRPackedBool                        mOpenRedirectChannel;
--- a/netwerk/base/src/nsNetStrings.cpp
+++ b/netwerk/base/src/nsNetStrings.cpp
@@ -35,12 +35,13 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsNetStrings.h"
 #include "nsChannelProperties.h"
 
 NS_HIDDEN_(nsNetStrings*) gNetStrings;
 
 nsNetStrings::nsNetStrings()
-  : NS_LITERAL_STRING_INIT(kChannelPolicy, NS_CHANNEL_PROP_CHANNEL_POLICY_STR)
+  : NS_LITERAL_STRING_INIT(kContentDisposition, NS_CHANNEL_PROP_CONTENT_DISPOSITION_STR),
+    NS_LITERAL_STRING_INIT(kChannelPolicy, NS_CHANNEL_PROP_CHANNEL_POLICY_STR)
 {}
 
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -354,28 +354,16 @@ HttpBaseChannel::SetContentCharset(const
   } else {
     // Charset hint
     mContentCharsetHint = aContentCharset;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpBaseChannel::GetContentDisposition(nsACString& aContentDisposition)
-{
-  aContentDisposition.Truncate();
-
-  if (!mResponseHead)
-    return NS_ERROR_NOT_AVAILABLE;
-
-  mResponseHead->GetHeader(nsHttp::Content_Disposition, aContentDisposition);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 HttpBaseChannel::GetContentLength(PRInt64 *aContentLength)
 {
   if (!mResponseHead)
     return NS_ERROR_NOT_AVAILABLE;
 
   *aContentLength = mResponseHead->ContentLength();
   return NS_OK;
 }
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -124,17 +124,16 @@ public:
   NS_IMETHOD GetOwner(nsISupports **aOwner);
   NS_IMETHOD SetOwner(nsISupports *aOwner);
   NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks);
   NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks);
   NS_IMETHOD GetContentType(nsACString& aContentType);
   NS_IMETHOD SetContentType(const nsACString& aContentType);
   NS_IMETHOD GetContentCharset(nsACString& aContentCharset);
   NS_IMETHOD SetContentCharset(const nsACString& aContentCharset);
-  NS_IMETHOD GetContentDisposition(nsACString& aContentDisposition);
   NS_IMETHOD GetContentLength(PRInt64 *aContentLength);
   NS_IMETHOD SetContentLength(PRInt64 aContentLength);
   NS_IMETHOD Open(nsIInputStream **aResult);
 
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD GetRequestMethod(nsACString& aMethod);
   NS_IMETHOD SetRequestMethod(const nsACString& aMethod);
   NS_IMETHOD GetReferrer(nsIURI **referrer);
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -365,24 +365,16 @@ NS_IMETHODIMP
 nsViewSourceChannel::SetContentCharset(const nsACString &aContentCharset)
 {
     NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
 
     return mChannel->SetContentCharset(aContentCharset);
 }
 
 NS_IMETHODIMP
-nsViewSourceChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-    NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
-
-    return mChannel->GetContentDisposition(aContentDisposition);
-}
-
-NS_IMETHODIMP
 nsViewSourceChannel::GetContentLength(PRInt64 *aContentLength)
 {
     NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
 
     return mChannel->GetContentLength(aContentLength);
 }
 
 NS_IMETHODIMP
--- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp
@@ -340,16 +340,23 @@ nsPartChannel::SetContentLength(PRInt64 
 NS_IMETHODIMP
 nsPartChannel::GetContentDisposition(nsACString &aContentDisposition)
 {
     aContentDisposition = mContentDisposition;
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPartChannel::SetContentDisposition(const nsACString &aContentDisposition)
+{
+    mContentDisposition = aContentDisposition;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPartChannel::GetPartID(PRUint32 *aPartID)
 {
     *aPartID = mPartID;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPartChannel::GetIsLastPart(PRBool *aIsLastPart)
@@ -788,17 +795,18 @@ nsMultiMixedConv::SendStart(nsIChannel *
     mPartChannel = newChannel;
 
     rv = mPartChannel->SetContentType(mContentType);
     if (NS_FAILED(rv)) return rv;
 
     rv = mPartChannel->SetContentLength(mContentLength);
     if (NS_FAILED(rv)) return rv;
 
-    mPartChannel->SetContentDisposition(mContentDisposition);
+    rv = mPartChannel->SetContentDisposition(mContentDisposition);
+    if (NS_FAILED(rv)) return rv;
 
     nsLoadFlags loadFlags = 0;
     mPartChannel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_REPLACE;
     mPartChannel->SetLoadFlags(loadFlags);
 
     nsCOMPtr<nsILoadGroup> loadGroup;
     (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
--- a/netwerk/streamconv/converters/nsMultiMixedConv.h
+++ b/netwerk/streamconv/converters/nsMultiMixedConv.h
@@ -72,21 +72,16 @@ public:
 
   void InitializeByteRange(PRInt64 aStart, PRInt64 aEnd);
   void SetIsLastPart() { mIsLastPart = PR_TRUE; }
   nsresult SendOnStartRequest(nsISupports* aContext);
   nsresult SendOnDataAvailable(nsISupports* aContext, nsIInputStream* aStream,
                                PRUint32 aOffset, PRUint32 aLen);
   nsresult SendOnStopRequest(nsISupports* aContext, nsresult aStatus);
 
-  void SetContentDisposition(const nsACString& aDisposition)
-  {
-    mContentDisposition = aDisposition;
-  }
-
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUEST
   NS_DECL_NSICHANNEL
   NS_DECL_NSIBYTERANGEREQUEST
   NS_DECL_NSIMULTIPARTCHANNEL
 
 protected:
   ~nsPartChannel();
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -381,31 +381,45 @@ nsresult nsDocumentOpenInfo::DispatchCon
     aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
   }
 
   // Check whether the data should be forced to be handled externally.  This
   // could happen because the Content-Disposition header is set so, or, in the
   // future, because the user has specified external handling for the MIME
   // type.
   PRBool forceExternalHandling = PR_FALSE;
+  nsCAutoString disposition;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
-  nsCAutoString disposition;
-  rv = aChannel->GetContentDisposition(disposition);
+  nsCOMPtr<nsIURI> uri;
+  if (httpChannel)
+  {
+    rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"),
+                                        disposition);
+    httpChannel->GetURI(getter_AddRefs(uri));
+  }
+  else
+  {
+    nsCOMPtr<nsIMultiPartChannel> multipartChannel(do_QueryInterface(request));
+    if (multipartChannel)
+    {
+      rv = multipartChannel->GetContentDisposition(disposition);
+    } else {
+      // Soon-to-be common way to get Disposition: right now only JARChannel
+      rv = NS_GetContentDisposition(request, disposition);
+    }
+  }
 
   LOG(("  Disposition header: '%s'", disposition.get()));
 
   if (NS_SUCCEEDED(rv) && !disposition.IsEmpty())
   {
     nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
     if (NS_SUCCEEDED(rv))
     {
       nsCAutoString fallbackCharset;
-      nsCOMPtr<nsIURI> uri;
-      if (httpChannel)
-        httpChannel->GetURI(getter_AddRefs(uri));
       if (uri)
         uri->GetOriginCharset(fallbackCharset);
       nsAutoString dispToken;
       // Get the disposition type
       rv = mimehdrpar->GetParameter(disposition, "", fallbackCharset,
                                     PR_TRUE, nsnull, dispToken);
       // RFC 2183, section 2.8 says that an unknown disposition
       // value should be treated as "attachment"
--- a/uriloader/exthandler/ExternalHelperAppParent.cpp
+++ b/uriloader/exthandler/ExternalHelperAppParent.cpp
@@ -291,23 +291,16 @@ ExternalHelperAppParent::GetContentChars
 
 NS_IMETHODIMP
 ExternalHelperAppParent::SetContentCharset(const nsACString& aContentCharset)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-ExternalHelperAppParent::GetContentDisposition(nsACString& aContentDisposition)
-{
-  aContentDisposition.Truncate();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 ExternalHelperAppParent::GetContentLength(PRInt64 *aContentLength)
 {
   *aContentLength = mContentLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ExternalHelperAppParent::SetContentLength(PRInt64 aContentLength)
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -217,16 +217,41 @@ static nsresult UnescapeFragment(const n
 {
   nsAutoString result;
   nsresult rv = UnescapeFragment(aFragment, aURI, result);
   if (NS_SUCCEEDED(rv))
     CopyUTF16toUTF8(result, aResult);
   return rv;
 }
 
+/** Gets the content-disposition header from a channel, using nsIHttpChannel
+ * or nsIMultipartChannel if available
+ * @param aChannel The channel to extract the disposition header from
+ * @param aDisposition Reference to a string where the header is to be stored
+ */
+static void ExtractDisposition(nsIChannel* aChannel, nsACString& aDisposition)
+{
+  aDisposition.Truncate();
+  // First see whether this is an http channel
+  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+  if (httpChannel) 
+  {
+    httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"), aDisposition);
+  }
+  if (aDisposition.IsEmpty())
+  {
+    nsCOMPtr<nsIMultiPartChannel> multipartChannel(do_QueryInterface(aChannel));
+    if (multipartChannel)
+    {
+      multipartChannel->GetContentDisposition(aDisposition);
+    }
+  }
+
+}
+
 /** Extracts the filename out of a content-disposition header
  * @param aFilename [out] The filename. Can be empty on error.
  * @param aDisposition Value of a Content-Disposition header
  * @param aURI Optional. Will be used to get a fallback charset for the
  *        filename, if it is QI'able to nsIURL
  * @param aMIMEHeaderParam Optional. Pointer to a nsIMIMEHeaderParam class, so
  *        that it doesn't need to be fetched by this function.
  */
@@ -284,17 +309,17 @@ static PRBool GetFilenameAndExtensionFro
   /*
    * If the channel is an http or part of a multipart channel and we
    * have a content disposition header set, then use the file name
    * suggested there as the preferred file name to SUGGEST to the
    * user.  we shouldn't actually use that without their
    * permission... otherwise just use our temp file
    */
   nsCAutoString disp;
-  aChannel->GetContentDisposition(disp);
+  ExtractDisposition(aChannel, disp);
   PRBool handleExternally = PR_FALSE;
   nsCOMPtr<nsIURI> uri;
   nsresult rv;
   aChannel->GetURI(getter_AddRefs(uri));
   // content-disposition: has format:
   // disposition-type < ; name=value >* < ; filename=value > < ; name=value >*
   if (!disp.IsEmpty()) 
   {
--- a/uriloader/exthandler/nsExternalProtocolHandler.cpp
+++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp
@@ -242,22 +242,16 @@ NS_IMETHODIMP nsExtProtocolChannel::GetC
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(nsACString &aContentDisposition)
-{
-  aContentDisposition.Truncate();
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(PRInt64 * aContentLength)
 {
   *aContentLength = -1;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsExtProtocolChannel::SetContentLength(PRInt64 aContentLength)