зеркало из https://github.com/mozilla/gecko-dev.git
bug 589292 - add contentDisposition{Filename} props to nsIChannel. r=honzab sr=bzbarsky
This commit is contained in:
Родитель
926c81069f
Коммит
924d548d31
|
@ -129,51 +129,18 @@ StringBeginsWithLowercaseLiteral(nsAString& aString,
|
|||
return StringHead(aString, N).LowerCaseEqualsLiteral(aSubstring);
|
||||
}
|
||||
|
||||
// XXXsayrer put this in here to get on the branch with minimal delay.
|
||||
// Trunk really needs to factor this out. This is the third usage.
|
||||
PRBool
|
||||
HasAttachmentDisposition(nsIHttpChannel* httpChannel)
|
||||
{
|
||||
if (!httpChannel)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCAutoString contentDisposition;
|
||||
nsresult rv =
|
||||
httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"),
|
||||
contentDisposition);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !contentDisposition.IsEmpty()) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
httpChannel->GetURI(getter_AddRefs(uri));
|
||||
nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar =
|
||||
do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
nsCAutoString fallbackCharset;
|
||||
if (uri)
|
||||
uri->GetOriginCharset(fallbackCharset);
|
||||
nsAutoString dispToken;
|
||||
// Get the disposition type
|
||||
rv = mimehdrpar->GetParameter(contentDisposition, "", fallbackCharset,
|
||||
PR_TRUE, nsnull, dispToken);
|
||||
// RFC 2183, section 2.8 says that an unknown disposition
|
||||
// value should be treated as "attachment"
|
||||
// XXXbz this code is duplicated in GetFilenameAndExtensionFromChannel in
|
||||
// nsExternalHelperAppService. Factor it out!
|
||||
if (NS_FAILED(rv) ||
|
||||
(!dispToken.IsEmpty() &&
|
||||
!StringBeginsWithLowercaseLiteral(dispToken, "inline") &&
|
||||
// Broken sites just send
|
||||
// Content-Disposition: filename="file"
|
||||
// without a disposition token... screen those out.
|
||||
!StringBeginsWithLowercaseLiteral(dispToken, "filename") &&
|
||||
// Also in use is Content-Disposition: name="file"
|
||||
!StringBeginsWithLowercaseLiteral(dispToken, "name")))
|
||||
// We have a content-disposition of "attachment" or unknown
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PRUint32 disp;
|
||||
nsresult rv = httpChannel->GetContentDisposition(&disp);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && disp == nsIChannel::DISPOSITION_ATTACHMENT)
|
||||
return PR_TRUE;
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -6749,14 +6749,11 @@ nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
nsCAutoString contentDisp;
|
||||
rv = aChannel->GetContentDispositionHeader(contentDisp);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
SetHeaderData(nsGkAtoms::headerContentDisposition,
|
||||
NS_ConvertASCIItoUTF16(contentDisp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1027,6 +1027,24 @@ nsJSChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return mStreamChannel->SetContentCharset(aContentCharset);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return mStreamChannel->GetContentDisposition(aContentDisposition);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return mStreamChannel->GetContentDispositionFilename(aContentDispositionFilename);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return mStreamChannel->GetContentDispositionHeader(aContentDispositionHeader);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJSChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
|
|
|
@ -666,6 +666,32 @@ nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
if (mContentDispositionHeader.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*aContentDisposition = mContentDisposition;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
if (mContentDispositionHeader.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
aContentDispositionHeader = mContentDispositionHeader;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::GetContentLength(PRInt32 *result)
|
||||
{
|
||||
|
@ -806,7 +832,6 @@ nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
|
|||
}
|
||||
|
||||
if (NS_SUCCEEDED(status) && channel) {
|
||||
nsCAutoString header;
|
||||
// Grab the security info from our base channel
|
||||
channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
|
||||
|
||||
|
@ -815,6 +840,7 @@ nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
|
|||
// 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;
|
||||
|
@ -825,10 +851,6 @@ nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
|
|||
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) {
|
||||
|
@ -836,11 +858,10 @@ nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
|
|||
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->GetContentDispositionHeader(mContentDispositionHeader);
|
||||
mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(status) && mIsUnsafe &&
|
||||
|
|
|
@ -96,6 +96,10 @@ private:
|
|||
nsCOMPtr<nsISupports> mListenerContext;
|
||||
nsCString mContentType;
|
||||
nsCString mContentCharset;
|
||||
nsCString mContentDispositionHeader;
|
||||
/* mContentDisposition is uninitialized if mContentDispositionHeader is
|
||||
* empty */
|
||||
PRUint32 mContentDisposition;
|
||||
PRInt32 mContentLength;
|
||||
PRUint32 mLoadFlags;
|
||||
nsresult mStatus;
|
||||
|
|
Двоичный файл не отображается.
|
@ -0,0 +1,22 @@
|
|||
// Make sure we behave appropriately when asking for content-disposition
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const path = "data/test_bug589292.zip";
|
||||
|
||||
function run_test() {
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
var spec = "jar:" + ios.newFileURI(do_get_file(path)).spec + "!/foo.txt";
|
||||
var channel = ios.newChannel(spec, null, null);
|
||||
instr = channel.open();
|
||||
var val;
|
||||
try {
|
||||
val = channel.contentDisposition;
|
||||
do_check_true(false, "The channel has content disposition?!");
|
||||
} catch (e) {
|
||||
// This is what we want to happen - there's no underlying channel, so no
|
||||
// content-disposition header is available
|
||||
do_check_true(true, "How are you reading this?!");
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ tail =
|
|||
[test_bug407303.js]
|
||||
[test_bug453254.js]
|
||||
[test_bug458158.js]
|
||||
[test_bug589292.js]
|
||||
[test_bug597702.js]
|
||||
[test_bug637286.js]
|
||||
[test_bug658093.js]
|
||||
|
|
|
@ -398,6 +398,24 @@ nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
*aContentLength = mContentLength;
|
||||
|
|
|
@ -635,6 +635,24 @@ nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
*aContentLength = mContentLength;
|
||||
|
|
|
@ -654,6 +654,24 @@ nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIconChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
*aContentLength = mContentLength;
|
||||
|
|
|
@ -994,11 +994,10 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
|
|||
/* 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);
|
||||
|
@ -1041,14 +1040,8 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
|
|||
|
||||
/* set our content disposition as a property */
|
||||
nsCAutoString 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 (chan) {
|
||||
chan->GetContentDispositionHeader(disposition);
|
||||
}
|
||||
if (!disposition.IsEmpty()) {
|
||||
nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
|
||||
|
@ -1114,6 +1107,7 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
|
|||
|
||||
if (imageType == imgIContainer::TYPE_RASTER) {
|
||||
/* Use content-length as a size hint for http channels. */
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
|
||||
if (httpChannel) {
|
||||
nsCAutoString contentLength;
|
||||
rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
|
||||
|
|
|
@ -58,12 +58,6 @@
|
|||
*/
|
||||
#define NS_CHANNEL_PROP_CONTENT_LENGTH_STR "content-length"
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -74,13 +68,10 @@
|
|||
|
||||
#ifdef IMPL_NS_NET
|
||||
#define NS_CHANNEL_PROP_CONTENT_LENGTH gNetStrings->kContentLength
|
||||
#define NS_CHANNEL_PROP_CONTENT_DISPOSITION gNetStrings->kContentDisposition
|
||||
#define NS_CHANNEL_PROP_CHANNEL_POLICY gNetStrings->kChannelPolicy
|
||||
#else
|
||||
#define NS_CHANNEL_PROP_CONTENT_LENGTH \
|
||||
NS_LITERAL_STRING(NS_CHANNEL_PROP_CONTENT_LENGTH_STR)
|
||||
#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
|
||||
|
|
|
@ -56,7 +56,7 @@ interface nsIStreamListener;
|
|||
*
|
||||
* This interface must be used only from the XPCOM main thread.
|
||||
*/
|
||||
[scriptable, uuid(c63a055a-a676-4e71-bf3c-6cfa11082018)]
|
||||
[scriptable, uuid(06f6ada3-7729-4e72-8d3f-bf8ba630ff9b)]
|
||||
interface nsIChannel : nsIRequest
|
||||
{
|
||||
/**
|
||||
|
@ -272,4 +272,39 @@ interface nsIChannel : nsIRequest
|
|||
* the URI when opening the channel.
|
||||
*/
|
||||
const unsigned long LOAD_CLASSIFY_URI = 1 << 22;
|
||||
|
||||
/**
|
||||
* Access to the type implied or stated by the Content-Disposition header
|
||||
* if available and if applicable. This allows determining inline versus
|
||||
* attachment.
|
||||
*
|
||||
* Implementations should throw NS_ERROR_NOT_AVAILABLE if the header either
|
||||
* doesn't exist for this type of channel or is empty, and
|
||||
* DISPOSITION_ATTACHMENT if an invalid/noncompliant value is present.
|
||||
*/
|
||||
readonly attribute unsigned long contentDisposition;
|
||||
const unsigned long DISPOSITION_INLINE = 0;
|
||||
const unsigned long DISPOSITION_ATTACHMENT = 1;
|
||||
|
||||
/**
|
||||
* Access to the filename portion of the Content-Disposition header if
|
||||
* available and if applicable. This allows getting the preferred filename
|
||||
* without having to parse it out yourself.
|
||||
*
|
||||
* Implementations should throw NS_ERROR_NOT_AVAILABLE if the header doesn't
|
||||
* exist for this type of channel, if the header is empty, if the header
|
||||
* doesn't contain a filename portion, or the value of the filename
|
||||
* attribute is empty/missing.
|
||||
*/
|
||||
readonly attribute AString contentDispositionFilename;
|
||||
|
||||
/**
|
||||
* Access to the raw Content-Disposition header if available and applicable.
|
||||
*
|
||||
* Implementations should throw NS_ERROR_NOT_AVAILABLE if the header either
|
||||
* doesn't exist for this type of channel or is empty.
|
||||
*
|
||||
* @deprecated Use contentDisposition/contentDispositionFilename instead.
|
||||
*/
|
||||
readonly attribute ACString contentDispositionHeader;
|
||||
};
|
||||
|
|
|
@ -45,7 +45,7 @@ interface nsIChannel;
|
|||
* associated with a MultiPartChannel.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(ba78db7b-b88c-4b76-baf9-3c2296a585ae)]
|
||||
[scriptable, uuid(4b04e835-d131-42af-8bf0-74289f99374f)]
|
||||
interface nsIMultiPartChannel : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -53,13 +53,6 @@ interface nsIMultiPartChannel : nsISupports
|
|||
*/
|
||||
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.
|
||||
|
|
|
@ -49,7 +49,6 @@ public:
|
|||
|
||||
/** "content-length" */
|
||||
const nsLiteralString kContentLength;
|
||||
const nsLiteralString kContentDisposition;
|
||||
const nsLiteralString kChannelPolicy;
|
||||
};
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@
|
|||
#include "nsIChannelPolicy.h"
|
||||
#include "nsISocketProviderService.h"
|
||||
#include "nsISocketProvider.h"
|
||||
#include "nsIMIMEHeaderParam.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
#include "nsIRedirectChannelRegistrar.h"
|
||||
|
@ -244,19 +245,6 @@ NS_NewChannel(nsIChannel **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
|
||||
|
@ -1907,6 +1895,105 @@ NS_CheckIsJavaCompatibleURLString(nsCString& urlString, PRBool *result)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/** Given the first (disposition) token from a Content-Disposition header,
|
||||
* tell whether it indicates the content is inline or attachment
|
||||
* @param aDispToken the disposition token from the content-disposition header
|
||||
*/
|
||||
inline PRUint32
|
||||
NS_GetContentDispositionFromToken(const nsAString& aDispToken)
|
||||
{
|
||||
// RFC 2183, section 2.8 says that an unknown disposition
|
||||
// value should be treated as "attachment"
|
||||
// If all of these tests eval to false, then we have a content-disposition of
|
||||
// "attachment" or unknown
|
||||
if (aDispToken.IsEmpty() ||
|
||||
aDispToken.LowerCaseEqualsLiteral("inline") ||
|
||||
// Broken sites just send
|
||||
// Content-Disposition: filename="file"
|
||||
// without a disposition token... screen those out.
|
||||
StringHead(aDispToken, 8).LowerCaseEqualsLiteral("filename") ||
|
||||
// Also in use is Content-Disposition: name="file"
|
||||
StringHead(aDispToken, 4).LowerCaseEqualsLiteral("name"))
|
||||
return nsIChannel::DISPOSITION_INLINE;
|
||||
|
||||
return nsIChannel::DISPOSITION_ATTACHMENT;
|
||||
}
|
||||
|
||||
/** Determine the disposition (inline/attachment) of the content based on the
|
||||
* Content-Disposition header
|
||||
* @param aHeader the content-disposition header (full value)
|
||||
* @param aChan the channel the header came from
|
||||
*/
|
||||
inline PRUint32
|
||||
NS_GetContentDispositionFromHeader(const nsACString& aHeader, nsIChannel *aChan = nsnull)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return nsIChannel::DISPOSITION_ATTACHMENT;
|
||||
|
||||
nsCAutoString fallbackCharset;
|
||||
if (aChan) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aChan->GetURI(getter_AddRefs(uri));
|
||||
if (uri)
|
||||
uri->GetOriginCharset(fallbackCharset);
|
||||
}
|
||||
|
||||
nsAutoString dispToken;
|
||||
rv = mimehdrpar->GetParameter(aHeader, "", fallbackCharset, PR_TRUE, nsnull,
|
||||
dispToken);
|
||||
if (NS_FAILED(rv))
|
||||
return nsIChannel::DISPOSITION_ATTACHMENT;
|
||||
|
||||
return NS_GetContentDispositionFromToken(dispToken);
|
||||
}
|
||||
|
||||
/** 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
|
||||
*/
|
||||
inline nsresult
|
||||
NS_GetFilenameFromDisposition(nsAString& aFilename,
|
||||
const nsACString& aDisposition,
|
||||
nsIURI* aURI = nsnull)
|
||||
{
|
||||
aFilename.Truncate();
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar =
|
||||
do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
|
||||
|
||||
nsCAutoString fallbackCharset;
|
||||
if (url)
|
||||
url->GetOriginCharset(fallbackCharset);
|
||||
// Get the value of 'filename' parameter
|
||||
rv = mimehdrpar->GetParameter(aDisposition, "filename",
|
||||
fallbackCharset, PR_TRUE, nsnull,
|
||||
aFilename);
|
||||
if (NS_FAILED(rv) || aFilename.IsEmpty()) {
|
||||
// Try 'name' parameter, instead.
|
||||
rv = mimehdrpar->GetParameter(aDisposition, "name", fallbackCharset,
|
||||
PR_TRUE, nsnull, aFilename);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
aFilename.Truncate();
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aFilename.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure Personal Security Manager is initialized
|
||||
*/
|
||||
|
|
|
@ -519,6 +519,24 @@ nsBaseChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
|
|
|
@ -41,7 +41,6 @@ NS_HIDDEN_(nsNetStrings*) gNetStrings;
|
|||
|
||||
nsNetStrings::nsNetStrings()
|
||||
: NS_LITERAL_STRING_INIT(kContentLength, NS_CHANNEL_PROP_CONTENT_LENGTH_STR),
|
||||
NS_LITERAL_STRING_INIT(kContentDisposition, NS_CHANNEL_PROP_CONTENT_DISPOSITION_STR),
|
||||
NS_LITERAL_STRING_INIT(kChannelPolicy, NS_CHANNEL_PROP_CHANNEL_POLICY_STR)
|
||||
{}
|
||||
|
||||
|
|
|
@ -376,6 +376,51 @@ HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCString header;
|
||||
|
||||
rv = GetContentDispositionHeader(header);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
*aContentDisposition = NS_GetContentDispositionFromHeader(header, this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetContentDispositionFilename(nsAString& aContentDispositionFilename)
|
||||
{
|
||||
aContentDispositionFilename.Truncate();
|
||||
|
||||
nsresult rv;
|
||||
nsCString header;
|
||||
|
||||
rv = GetContentDispositionHeader(header);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
return NS_GetFilenameFromDisposition(aContentDispositionFilename,
|
||||
header, mURI);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetContentDispositionHeader(nsACString& aContentDispositionHeader)
|
||||
{
|
||||
if (!mResponseHead)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Disposition,
|
||||
aContentDispositionHeader);
|
||||
if (NS_FAILED(rv) || aContentDispositionHeader.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
|
|
|
@ -116,6 +116,9 @@ public:
|
|||
NS_IMETHOD SetContentType(const nsACString& aContentType);
|
||||
NS_IMETHOD GetContentCharset(nsACString& aContentCharset);
|
||||
NS_IMETHOD SetContentCharset(const nsACString& aContentCharset);
|
||||
NS_IMETHOD GetContentDisposition(PRUint32 *aContentDisposition);
|
||||
NS_IMETHOD GetContentDispositionFilename(nsAString& aContentDispositionFilename);
|
||||
NS_IMETHOD GetContentDispositionHeader(nsACString& aContentDispositionHeader);
|
||||
NS_IMETHOD GetContentLength(PRInt32 *aContentLength);
|
||||
NS_IMETHOD SetContentLength(PRInt32 aContentLength);
|
||||
NS_IMETHOD Open(nsIInputStream **aResult);
|
||||
|
|
|
@ -371,6 +371,30 @@ nsViewSourceChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return mChannel->SetContentCharset(aContentCharset);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
|
||||
|
||||
return mChannel->GetContentDisposition(aContentDisposition);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
|
||||
|
||||
return mChannel->GetContentDispositionFilename(aContentDispositionFilename);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
|
||||
|
||||
return mChannel->GetContentDispositionHeader(aContentDispositionHeader);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
|
|
|
@ -535,6 +535,24 @@ WyciwygChannelChild::SetContentCharset(const nsACString & aContentCharset)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WyciwygChannelChild::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WyciwygChannelChild::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WyciwygChannelChild::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/* attribute long contentLength; */
|
||||
NS_IMETHODIMP
|
||||
WyciwygChannelChild::GetContentLength(PRInt32 *aContentLength)
|
||||
|
|
|
@ -343,6 +343,24 @@ nsWyciwygChannel::SetContentCharset(const nsACString &aContentCharset)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWyciwygChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWyciwygChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWyciwygChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWyciwygChannel::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
|
|
|
@ -120,6 +120,16 @@ nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext,
|
|||
return listener->OnStopRequest(this, aContext, aStatus);
|
||||
}
|
||||
|
||||
void nsPartChannel::SetContentDisposition(const nsACString& aContentDispositionHeader)
|
||||
{
|
||||
mContentDispositionHeader = aContentDispositionHeader;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
GetURI(getter_AddRefs(uri));
|
||||
NS_GetFilenameFromDisposition(mContentDispositionFilename,
|
||||
mContentDispositionHeader, uri);
|
||||
mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
|
||||
}
|
||||
|
||||
//
|
||||
// nsISupports implementation...
|
||||
//
|
||||
|
@ -337,16 +347,32 @@ nsPartChannel::SetContentLength(PRInt32 aContentLength)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPartChannel::GetContentDisposition(nsACString &aContentDisposition)
|
||||
nsPartChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
aContentDisposition = mContentDisposition;
|
||||
if (mContentDispositionHeader.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*aContentDisposition = mContentDisposition;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPartChannel::SetContentDisposition(const nsACString &aContentDisposition)
|
||||
nsPartChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
mContentDisposition = aContentDisposition;
|
||||
if (mContentDispositionFilename.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
aContentDispositionFilename = mContentDispositionFilename;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPartChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
if (mContentDispositionHeader.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
aContentDispositionHeader = mContentDispositionHeader;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -799,8 +825,7 @@ nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
|
|||
rv = mPartChannel->SetContentLength(mContentLength); // XXX Truncates 64-bit!
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = mPartChannel->SetContentDisposition(mContentDisposition);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
mPartChannel->SetContentDisposition(mContentDisposition);
|
||||
|
||||
nsLoadFlags loadFlags = 0;
|
||||
mPartChannel->GetLoadFlags(&loadFlags);
|
||||
|
|
|
@ -76,6 +76,9 @@ public:
|
|||
nsresult SendOnDataAvailable(nsISupports* aContext, nsIInputStream* aStream,
|
||||
PRUint32 aOffset, PRUint32 aLen);
|
||||
nsresult SendOnStopRequest(nsISupports* aContext, nsresult aStatus);
|
||||
/* SetContentDisposition expects the full value of the Content-Disposition
|
||||
* header */
|
||||
void SetContentDisposition(const nsACString& aContentDispositionHeader);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUEST
|
||||
|
@ -97,7 +100,9 @@ protected:
|
|||
|
||||
nsCString mContentType;
|
||||
nsCString mContentCharset;
|
||||
nsCString mContentDisposition;
|
||||
PRUint32 mContentDisposition;
|
||||
nsString mContentDispositionFilename;
|
||||
nsCString mContentDispositionHeader;
|
||||
PRUint64 mContentLength;
|
||||
|
||||
PRBool mIsByteRangeRequest;
|
||||
|
|
|
@ -349,12 +349,12 @@ function completeTest11(request, data, ctx)
|
|||
do_check_eq(request.status, 0);
|
||||
|
||||
try {
|
||||
// TODO when bug XXX lands, also get channel C-D properties and make sure
|
||||
// they're blank
|
||||
dispo = request.getResponseHeader("Content-Disposition");
|
||||
do_check_eq(dispo, "attachment; filename=foo");
|
||||
var chan = request.QueryInterface(Ci.nsIChannel);
|
||||
do_check_eq(chan.contentDisposition, chan.DISPOSITION_ATTACHMENT);
|
||||
do_check_eq(chan.contentDispositionFilename, "foo");
|
||||
do_check_eq(chan.contentDispositionHeader, "attachment; filename=foo");
|
||||
} catch (ex) {
|
||||
do_throw("Content-Disposition should be present");
|
||||
do_throw("error parsing Content-Disposition: " + ex);
|
||||
}
|
||||
|
||||
endTests();
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// cleaner HTTP header test infrastructure
|
||||
//
|
||||
// tests bugs: 589292, [add more here: see hg log for definitive list]
|
||||
//
|
||||
// TO ADD NEW TESTS:
|
||||
// 1) Increment up 'lastTest' to new number (say, "99")
|
||||
// 2) Add new test 'handler99' and 'completeTest99' functions.
|
||||
// 3) If your test should fail the necko channel, set
|
||||
// test_flags[99] = CL_EXPECT_FAILURE.
|
||||
//
|
||||
// TO DEBUG JUST ONE TEST: temporarily change firstTest and lastTest to equal
|
||||
// the test # you're interested in.
|
||||
//
|
||||
// For tests that need duplicate copies of headers to be sent, see
|
||||
// test_duplicate_headers.js
|
||||
|
||||
var firstTest = 1; // set to test of interest when debugging
|
||||
var lastTest = 4; // set to test of interest when debugging
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Note: sets Cc and Ci variables
|
||||
do_load_httpd_js();
|
||||
|
||||
var httpserver = new nsHttpServer();
|
||||
var index = 0;
|
||||
var nextTest = firstTest;
|
||||
var test_flags = new Array();
|
||||
var testPathBase = "/test_headers";
|
||||
|
||||
function run_test()
|
||||
{
|
||||
httpserver.start(4444);
|
||||
|
||||
do_test_pending();
|
||||
run_test_number(nextTest);
|
||||
}
|
||||
|
||||
function runNextTest()
|
||||
{
|
||||
if (nextTest == lastTest) {
|
||||
endTests();
|
||||
return;
|
||||
}
|
||||
nextTest++;
|
||||
// Make sure test functions exist
|
||||
if (eval("handler" + nextTest) == undefined)
|
||||
do_throw("handler" + nextTest + " undefined!");
|
||||
if (eval("completeTest" + nextTest) == undefined)
|
||||
do_throw("completeTest" + nextTest + " undefined!");
|
||||
|
||||
run_test_number(nextTest);
|
||||
}
|
||||
|
||||
function run_test_number(num)
|
||||
{
|
||||
testPath = testPathBase + num;
|
||||
httpserver.registerPathHandler(testPath, eval("handler" + num));
|
||||
|
||||
var channel = setupChannel(testPath);
|
||||
flags = test_flags[num]; // OK if flags undefined for test
|
||||
channel.asyncOpen(new ChannelListener(eval("completeTest" + num),
|
||||
channel, flags), null);
|
||||
}
|
||||
|
||||
function setupChannel(url)
|
||||
{
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
var chan = ios.newChannel("http://localhost:4444" + url, "", null);
|
||||
var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
return httpChan;
|
||||
}
|
||||
|
||||
function endTests()
|
||||
{
|
||||
httpserver.stop(do_test_finished);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 1: test Content-Disposition channel attributes
|
||||
function handler1(metadata, response)
|
||||
{
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=foo");
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
var body = "foo";
|
||||
}
|
||||
|
||||
function completeTest1(request, data, ctx)
|
||||
{
|
||||
try {
|
||||
var chan = request.QueryInterface(Ci.nsIChannel);
|
||||
do_check_eq(chan.contentDisposition, chan.DISPOSITION_ATTACHMENT);
|
||||
do_check_eq(chan.contentDispositionFilename, "foo");
|
||||
do_check_eq(chan.contentDispositionHeader, "attachment; filename=foo");
|
||||
} catch (ex) {
|
||||
do_throw("error parsing Content-Disposition: " + ex);
|
||||
}
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 2: no filename
|
||||
function handler2(metadata, response)
|
||||
{
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.setHeader("Content-Disposition", "attachment");
|
||||
var body = "foo";
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
function completeTest2(request, data, ctx)
|
||||
{
|
||||
try {
|
||||
var chan = request.QueryInterface(Ci.nsIChannel);
|
||||
do_check_eq(chan.contentDisposition, chan.DISPOSITION_ATTACHMENT);
|
||||
do_check_eq(chan.contentDispositionHeader, "attachment");
|
||||
|
||||
filename = chan.contentDispositionFilename; // should barf
|
||||
do_throw("Should have failed getting Content-Disposition filename");
|
||||
} catch (ex) {
|
||||
do_print("correctly ate exception");
|
||||
}
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 3: filename missing
|
||||
function handler3(metadata, response)
|
||||
{
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.setHeader("Content-Disposition", "attachment; filename=");
|
||||
var body = "foo";
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
function completeTest3(request, data, ctx)
|
||||
{
|
||||
try {
|
||||
var chan = request.QueryInterface(Ci.nsIChannel);
|
||||
do_check_eq(chan.contentDisposition, chan.DISPOSITION_ATTACHMENT);
|
||||
do_check_eq(chan.contentDispositionHeader, "attachment; filename=");
|
||||
|
||||
filename = chan.contentDispositionFilename; // should barf
|
||||
do_throw("Should have failed getting Content-Disposition filename");
|
||||
} catch (ex) {
|
||||
do_print("correctly ate exception");
|
||||
}
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Test 4: inline
|
||||
function handler4(metadata, response)
|
||||
{
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.setHeader("Content-Disposition", "inline");
|
||||
var body = "foo";
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
function completeTest4(request, data, ctx)
|
||||
{
|
||||
try {
|
||||
var chan = request.QueryInterface(Ci.nsIChannel);
|
||||
do_check_eq(chan.contentDisposition, chan.DISPOSITION_INLINE);
|
||||
do_check_eq(chan.contentDispositionHeader, "inline");
|
||||
|
||||
filename = chan.contentDispositionFilename; // should barf
|
||||
do_throw("Should have failed getting Content-Disposition filename");
|
||||
} catch (ex) {
|
||||
do_print("correctly ate exception");
|
||||
}
|
||||
runNextTest();
|
||||
}
|
|
@ -126,6 +126,7 @@ skip-if = os == "android"
|
|||
[test_gre_resources.js]
|
||||
[test_gzipped_206.js]
|
||||
[test_head.js]
|
||||
[test_headers.js]
|
||||
[test_http_headers.js]
|
||||
[test_httpcancel.js]
|
||||
[test_httpsuspend.js]
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
//
|
||||
// Run test script in content process instead of chrome (xpcshell's default)
|
||||
//
|
||||
|
||||
function run_test() {
|
||||
run_test_in_child("../unit/test_headers.js");
|
||||
}
|
|
@ -7,6 +7,7 @@ tail =
|
|||
[test_duplicate_headers_wrap.js]
|
||||
[test_event_sink_wrap.js]
|
||||
[test_head_wrap.js]
|
||||
[test_headers_wrap.js]
|
||||
[test_httpcancel_wrap.js]
|
||||
[test_httpsuspend_wrap.js]
|
||||
[test_post_wrap.js]
|
||||
|
|
|
@ -385,58 +385,10 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports *
|
|||
// future, because the user has specified external handling for the MIME
|
||||
// type.
|
||||
PRBool forceExternalHandling = PR_FALSE;
|
||||
nsCAutoString disposition;
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
|
||||
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;
|
||||
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"
|
||||
// XXXbz this code is duplicated in GetFilenameAndExtensionFromChannel in
|
||||
// nsExternalHelperAppService. Factor it out!
|
||||
if (NS_FAILED(rv) ||
|
||||
(!dispToken.IsEmpty() &&
|
||||
!dispToken.LowerCaseEqualsLiteral("inline") &&
|
||||
// Broken sites just send
|
||||
// Content-Disposition: filename="file"
|
||||
// without a disposition token... screen those out.
|
||||
!dispToken.EqualsIgnoreCase("filename", 8) &&
|
||||
// Also in use is Content-Disposition: name="file"
|
||||
!dispToken.EqualsIgnoreCase("name", 4)))
|
||||
// We have a content-disposition of "attachment" or unknown
|
||||
forceExternalHandling = PR_TRUE;
|
||||
}
|
||||
}
|
||||
PRUint32 disposition;
|
||||
rv = aChannel->GetContentDisposition(&disposition);
|
||||
if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT)
|
||||
forceExternalHandling = PR_TRUE;
|
||||
|
||||
LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
|
||||
|
||||
|
@ -576,6 +528,7 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports *
|
|||
// Before dispatching to the external helper app service, check for an HTTP
|
||||
// error page. If we got one, we don't want to handle it with a helper app,
|
||||
// really.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
|
||||
if (httpChannel) {
|
||||
PRBool requestSucceeded;
|
||||
httpChannel->GetRequestSucceeded(&requestSucceeded);
|
||||
|
|
|
@ -72,7 +72,7 @@ ExternalHelperAppParent::ExternalHelperAppParent(
|
|||
void
|
||||
ExternalHelperAppParent::Init(ContentParent *parent,
|
||||
const nsCString& aMimeContentType,
|
||||
const nsCString& aContentDisposition,
|
||||
const nsCString& aContentDispositionHeader,
|
||||
const PRBool& aForceSave,
|
||||
const IPC::URI& aReferrer)
|
||||
{
|
||||
|
@ -85,7 +85,9 @@ ExternalHelperAppParent::Init(ContentParent *parent,
|
|||
SetPropertyAsInt64(NS_CHANNEL_PROP_CONTENT_LENGTH, mContentLength);
|
||||
if (aReferrer)
|
||||
SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"), aReferrer);
|
||||
SetContentDisposition(aContentDisposition);
|
||||
mContentDispositionHeader = aContentDispositionHeader;
|
||||
NS_GetFilenameFromDisposition(mContentDispositionFilename, mContentDispositionHeader, mURI);
|
||||
mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
|
||||
helperAppService->DoContent(aMimeContentType, this, nsnull,
|
||||
aForceSave, getter_AddRefs(mListener));
|
||||
}
|
||||
|
@ -299,6 +301,36 @@ ExternalHelperAppParent::SetContentCharset(const nsACString& aContentCharset)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExternalHelperAppParent::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
if (mContentDispositionHeader.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
*aContentDisposition = mContentDisposition;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExternalHelperAppParent::GetContentDispositionFilename(nsAString& aContentDispositionFilename)
|
||||
{
|
||||
if (mContentDispositionFilename.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
aContentDispositionFilename = mContentDispositionFilename;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExternalHelperAppParent::GetContentDispositionHeader(nsACString& aContentDispositionHeader)
|
||||
{
|
||||
if (mContentDispositionHeader.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
aContentDispositionHeader = mContentDispositionHeader;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExternalHelperAppParent::GetContentLength(PRInt32 *aContentLength)
|
||||
{
|
||||
|
@ -343,20 +375,6 @@ ExternalHelperAppParent::GetBaseChannel(nsIChannel* *aChannel)
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExternalHelperAppParent::GetContentDisposition(nsACString& aContentDisposition)
|
||||
{
|
||||
aContentDisposition = mContentDisposition;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExternalHelperAppParent::SetContentDisposition(const nsACString& aDisposition)
|
||||
{
|
||||
mContentDisposition = aDisposition;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExternalHelperAppParent::GetPartID(PRUint32* aPartID)
|
||||
{
|
||||
|
|
|
@ -83,7 +83,9 @@ private:
|
|||
nsLoadFlags mLoadFlags;
|
||||
nsresult mStatus;
|
||||
PRInt64 mContentLength;
|
||||
nsCString mContentDisposition;
|
||||
PRUint32 mContentDisposition;
|
||||
nsString mContentDispositionFilename;
|
||||
nsCString mContentDispositionHeader;
|
||||
nsCString mEntityID;
|
||||
};
|
||||
|
||||
|
|
|
@ -227,66 +227,6 @@ static nsresult UnescapeFragment(const nsACString& aFragment, nsIURI* aURI,
|
|||
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.
|
||||
*/
|
||||
static void GetFilenameFromDisposition(nsAString& aFilename,
|
||||
const nsACString& aDisposition,
|
||||
nsIURI* aURI = nsnull,
|
||||
nsIMIMEHeaderParam* aMIMEHeaderParam = nsnull)
|
||||
{
|
||||
aFilename.Truncate();
|
||||
nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar(aMIMEHeaderParam);
|
||||
if (!mimehdrpar) {
|
||||
mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID);
|
||||
if (!mimehdrpar)
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
|
||||
|
||||
nsCAutoString fallbackCharset;
|
||||
if (url)
|
||||
url->GetOriginCharset(fallbackCharset);
|
||||
// Get the value of 'filename' parameter
|
||||
nsresult rv = mimehdrpar->GetParameter(aDisposition, "filename", fallbackCharset,
|
||||
PR_TRUE, nsnull, aFilename);
|
||||
if (NS_FAILED(rv) || aFilename.IsEmpty())
|
||||
// Try 'name' parameter, instead.
|
||||
rv = mimehdrpar->GetParameter(aDisposition, "name", fallbackCharset, PR_TRUE,
|
||||
nsnull, aFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a channel, returns the filename and extension the channel has.
|
||||
* This uses the URL and other sources (nsIMultiPartChannel).
|
||||
|
@ -318,51 +258,19 @@ static PRBool GetFilenameAndExtensionFromChannel(nsIChannel* aChannel,
|
|||
* user. we shouldn't actually use that without their
|
||||
* permission... otherwise just use our temp file
|
||||
*/
|
||||
nsCAutoString 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())
|
||||
PRUint32 disp;
|
||||
nsresult rv = aChannel->GetContentDisposition(&disp);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return PR_FALSE;
|
||||
|
||||
nsCAutoString fallbackCharset;
|
||||
uri->GetOriginCharset(fallbackCharset);
|
||||
// Get the disposition type
|
||||
nsAutoString dispToken;
|
||||
rv = mimehdrpar->GetParameter(disp, "", fallbackCharset, PR_TRUE,
|
||||
nsnull, dispToken);
|
||||
// RFC 2183, section 2.8 says that an unknown disposition
|
||||
// value should be treated as "attachment"
|
||||
// XXXbz this code is duplicated in nsDocumentOpenInfo::DispatchContent.
|
||||
// Factor it out! Maybe store it in the nsDocumentOpenInfo?
|
||||
if (NS_FAILED(rv) ||
|
||||
(!dispToken.IsEmpty() &&
|
||||
!dispToken.LowerCaseEqualsLiteral("inline") &&
|
||||
// Broken sites just send
|
||||
// Content-Disposition: filename="file"
|
||||
// without a disposition token... screen those out.
|
||||
!dispToken.EqualsIgnoreCase("filename", 8) &&
|
||||
// Also in use is Content-Disposition: name="file"
|
||||
!dispToken.EqualsIgnoreCase("name", 4)))
|
||||
{
|
||||
// We have a content-disposition of "attachment" or unknown
|
||||
aChannel->GetContentDispositionFilename(aFileName);
|
||||
if (disp == nsIChannel::DISPOSITION_ATTACHMENT)
|
||||
handleExternally = PR_TRUE;
|
||||
}
|
||||
|
||||
// We may not have a disposition type listed; some servers suck.
|
||||
// But they could have listed a filename anyway.
|
||||
GetFilenameFromDisposition(aFileName, disp, uri, mimehdrpar);
|
||||
|
||||
} // we had a disp header
|
||||
}
|
||||
|
||||
// If the disposition header didn't work, try the filename from nsIURL
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aChannel->GetURI(getter_AddRefs(uri));
|
||||
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
||||
if (url && aFileName.IsEmpty())
|
||||
{
|
||||
|
@ -715,7 +623,7 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeConte
|
|||
|
||||
nsCString disp;
|
||||
if (channel)
|
||||
ExtractDisposition(channel, disp);
|
||||
channel->GetContentDispositionHeader(disp);
|
||||
|
||||
nsCOMPtr<nsIURI> referrer;
|
||||
rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrer));
|
||||
|
|
|
@ -247,6 +247,21 @@ NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(const nsACString &aContent
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(PRUint32 *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(PRInt32 * aContentLength)
|
||||
{
|
||||
*aContentLength = -1;
|
||||
|
|
Загрузка…
Ссылка в новой задаче