зеркало из https://github.com/mozilla/gecko-dev.git
Do not apply content decoding if the url ends in .gz or .zip. The
content-encoding battle never ends... Bug 128199, r=law, sr=darin, a=asa
This commit is contained in:
Родитель
2059c806a2
Коммит
abe192dbf6
|
@ -61,6 +61,7 @@
|
|||
#define APPLICATION_GZIP "application/x-gzip"
|
||||
#define APPLICATION_GZIP2 "application/gzip"
|
||||
#define APPLICATION_GZIP3 "application/x-gunzip"
|
||||
#define APPLICATION_ZIP "application/zip"
|
||||
#define APPLICATION_HTTP_INDEX_FORMAT "application/http-index-format"
|
||||
#define APPLICATION_JAVASCRIPT "application/x-javascript"
|
||||
#define APPLICATION_NETSCAPE_REVOCATION "application/x-netscape-revocation"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "nsIChannel.idl"
|
||||
|
||||
interface nsIHttpHeaderVisitor;
|
||||
interface nsISimpleEnumerator;
|
||||
|
||||
[scriptable, uuid(d78b53c8-d03f-4fd8-b2ee-7b36fcd150d1)]
|
||||
interface nsIHttpChannel : nsIChannel
|
||||
|
@ -103,6 +104,22 @@ interface nsIHttpChannel : nsIChannel
|
|||
*/
|
||||
readonly attribute string charset;
|
||||
|
||||
/**
|
||||
* This attribute holds the MIME types corresponding to the content
|
||||
* encodings on the channel. The enumerator returns nsISupportsString
|
||||
* objects. The first one corresponds to the outermost encoding on the
|
||||
* channel and then we work our way inward. "identity" is skipped and not
|
||||
* represented on the list. Unknown encodings make the enumeration stop.
|
||||
* If you want the actual Content-Encoding value, use
|
||||
* getResponseHeader("Content-Encoding").
|
||||
*
|
||||
* When there is no Content-Encoding header, this property is null.
|
||||
*
|
||||
* Modifying the Content-Encoding header on the channel will case
|
||||
* this enumerator to have undefined behavior. Don't do it.
|
||||
*/
|
||||
readonly attribute nsISimpleEnumerator contentEncodings;
|
||||
|
||||
/**
|
||||
* This attribute controls whether or not content conversion should be
|
||||
* done per the Content-Encoding response header. Its value is true
|
||||
|
|
|
@ -2473,6 +2473,22 @@ nsHttpChannel::SetRedirectionLimit(PRUint32 value)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetContentEncodings(nsISimpleEnumerator** aEncodings)
|
||||
{
|
||||
NS_PRECONDITION(aEncodings, "Null out param");
|
||||
const char *encoding = mResponseHead->PeekHeader(nsHttp::Content_Encoding);
|
||||
if (!encoding) {
|
||||
*aEncodings = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
nsContentEncodings* enumerator = new nsContentEncodings(this, encoding);
|
||||
if (!enumerator)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return CallQueryInterface(enumerator, aEncodings);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsIRequestObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -2808,3 +2824,151 @@ nsHttpChannel::ClearPasswordManagerEntry(const char *host, PRInt32 port, const c
|
|||
passWordManager->RemoveUser(domain.get(), user);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsContentEncodings <public>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsHttpChannel::nsContentEncodings::nsContentEncodings(nsIHttpChannel* aChannel,
|
||||
const char* aEncodingHeader) :
|
||||
mEncodingHeader(aEncodingHeader), mChannel(aChannel), mReady(PR_FALSE)
|
||||
{
|
||||
mCurEnd = aEncodingHeader + strlen(aEncodingHeader);
|
||||
mCurStart = mCurEnd;
|
||||
NS_INIT_ISUPPORTS();
|
||||
}
|
||||
|
||||
nsHttpChannel::nsContentEncodings::~nsContentEncodings()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsContentEncodings::nsISimpleEnumerator
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::nsContentEncodings::HasMoreElements(PRBool* aMoreEncodings)
|
||||
{
|
||||
if (mReady) {
|
||||
*aMoreEncodings = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = PrepareForNext();
|
||||
*aMoreEncodings = NS_SUCCEEDED(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::nsContentEncodings::GetNext(nsISupports** aNextEncoding)
|
||||
{
|
||||
*aNextEncoding = nsnull;
|
||||
if (!mReady) {
|
||||
nsresult rv = PrepareForNext();
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
const nsACString & encoding = Substring(mCurStart, mCurEnd);
|
||||
|
||||
nsACString::const_iterator start, end;
|
||||
encoding.BeginReading(start);
|
||||
encoding.EndReading(end);
|
||||
|
||||
nsCOMPtr<nsISupportsString> str;
|
||||
str = do_CreateInstance("@mozilla.org/supports-string;1");
|
||||
if (!str)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRBool haveType = PR_FALSE;
|
||||
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("gzip"),
|
||||
start,
|
||||
end)) {
|
||||
str->SetDataWithLength(sizeof(APPLICATION_GZIP) - 1,
|
||||
APPLICATION_GZIP);
|
||||
haveType = PR_TRUE;
|
||||
}
|
||||
|
||||
if (!haveType) {
|
||||
encoding.BeginReading(start);
|
||||
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("compress"),
|
||||
start,
|
||||
end)) {
|
||||
str->SetDataWithLength(sizeof(APPLICATION_COMPRESS) - 1,
|
||||
APPLICATION_COMPRESS);
|
||||
haveType = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (! haveType) {
|
||||
encoding.BeginReading(start);
|
||||
if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("deflate"),
|
||||
start,
|
||||
end)) {
|
||||
str->SetDataWithLength(sizeof(APPLICATION_ZIP) - 1,
|
||||
APPLICATION_ZIP);
|
||||
haveType = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare to fetch the next encoding
|
||||
mCurEnd = mCurStart;
|
||||
mReady = PR_FALSE;
|
||||
|
||||
if (haveType)
|
||||
return CallQueryInterface(str, aNextEncoding);
|
||||
|
||||
NS_WARNING("Unknown encoding type");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsContentEncodings::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsHttpChannel::nsContentEncodings, nsISimpleEnumerator)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsContentEncodings <private>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::nsContentEncodings::PrepareForNext(void)
|
||||
{
|
||||
NS_PRECONDITION(mCurStart == mCurEnd, "Indeterminate state");
|
||||
|
||||
// At this point both mCurStart and mCurEnd point to somewhere
|
||||
// past the end of the next thing we want to return
|
||||
|
||||
while (mCurEnd != mEncodingHeader) {
|
||||
--mCurEnd;
|
||||
if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd))
|
||||
break;
|
||||
}
|
||||
if (mCurEnd == mEncodingHeader)
|
||||
return NS_ERROR_NOT_AVAILABLE; // no more encodings
|
||||
++mCurEnd;
|
||||
|
||||
// At this point mCurEnd points to the first char _after_ the
|
||||
// header we want. Furthermore, mCurEnd - 1 != mEncodingHeader
|
||||
|
||||
mCurStart = mCurEnd - 1;
|
||||
while (mCurStart != mEncodingHeader &&
|
||||
*mCurStart != ',' && !nsCRT::IsAsciiSpace(*mCurStart))
|
||||
--mCurStart;
|
||||
if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart))
|
||||
++mCurStart; // we stopped because of a weird char, so move up one
|
||||
|
||||
// At this point mCurStart and mCurEnd bracket the encoding string
|
||||
// we want. Check that it's not "identity"
|
||||
if (Substring(mCurStart, mCurEnd).Equals("identity",
|
||||
nsCaseInsensitiveCStringComparator())) {
|
||||
mCurEnd = mCurStart;
|
||||
return PrepareForNext();
|
||||
}
|
||||
|
||||
mReady = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsXPIDLString.h"
|
||||
#include "nsHttpConnection.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
class nsHttpTransaction;
|
||||
class nsHttpResponseHead;
|
||||
|
@ -176,6 +177,30 @@ private:
|
|||
PRPackedBool mResponseHeadersModified;
|
||||
PRPackedBool mCanceled;
|
||||
PRPackedBool mUploadStreamHasHeaders;
|
||||
|
||||
class nsContentEncodings : public nsISimpleEnumerator
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISIMPLEENUMERATOR
|
||||
|
||||
nsContentEncodings(nsIHttpChannel* aChannel, const char* aEncodingHeader);
|
||||
virtual ~nsContentEncodings();
|
||||
|
||||
private:
|
||||
nsresult PrepareForNext(void);
|
||||
|
||||
// We do not own the buffer. The channel owns it.
|
||||
const char* mEncodingHeader;
|
||||
const char* mCurStart; // points to start of current header
|
||||
const char* mCurEnd; // points to end of current header
|
||||
|
||||
// Hold a ref to our channel so that it can't go away and take the
|
||||
// header with it.
|
||||
nsCOMPtr<nsIHttpChannel> mChannel;
|
||||
|
||||
PRPackedBool mReady;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // nsHttpChannel_h__
|
||||
|
|
|
@ -143,6 +143,12 @@ static const char* const nonDecodableTypes [] = {
|
|||
0
|
||||
};
|
||||
|
||||
static const char* const nonDecodableExtensions [] = {
|
||||
"gz",
|
||||
"zip",
|
||||
0
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(nsExternalHelperAppService)
|
||||
NS_IMPL_THREADSAFE_RELEASE(nsExternalHelperAppService)
|
||||
|
||||
|
@ -319,6 +325,7 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const char *aMimeContentType
|
|||
|
||||
NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForType(const char *aMimeContentType, PRBool *aApplyDecoding)
|
||||
{
|
||||
NS_PRECONDITION(aMimeContentType, "Null MIME type");
|
||||
*aApplyDecoding = PR_TRUE;
|
||||
PRUint32 index;
|
||||
for (index = 0; nonDecodableTypes[index]; ++index) {
|
||||
|
@ -330,6 +337,20 @@ NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForType(const char *aMime
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(const char *aExtension, PRBool *aApplyDecoding)
|
||||
{
|
||||
NS_PRECONDITION(aExtension, "Null Extension");
|
||||
*aApplyDecoding = PR_TRUE;
|
||||
PRUint32 index;
|
||||
for(index = 0; nonDecodableExtensions[index]; ++index) {
|
||||
if (!PL_strcasecmp(aExtension, nonDecodableExtensions[index])) {
|
||||
*aApplyDecoding = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsExternalHelperAppService::LaunchAppWithTempFile(nsIMIMEInfo * aMimeInfo, nsIFile * aTempFile)
|
||||
{
|
||||
// this method should only be implemented by each OS specific implementation of this service.
|
||||
|
@ -1061,11 +1082,26 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
|
|||
{
|
||||
// Turn off content encoding conversions if needed
|
||||
PRBool applyConversion = PR_TRUE;
|
||||
nsXPIDLCString MIMEType;
|
||||
mMimeInfo->GetMIMEType( getter_Copies( MIMEType ) );
|
||||
|
||||
nsCOMPtr<nsIExternalHelperAppService> extHandler = do_GetService("@mozilla.org/uriloader/external-helper-app-service;1");
|
||||
if (extHandler)
|
||||
if (extHandler) {
|
||||
nsXPIDLCString MIMEType;
|
||||
mMimeInfo->GetMIMEType( getter_Copies( MIMEType ) );
|
||||
extHandler->ApplyDecodingForType(MIMEType, &applyConversion);
|
||||
|
||||
if (applyConversion) {
|
||||
// Now we double-check that it's OK to decode this extension
|
||||
nsCOMPtr<nsIURI> channelURI;
|
||||
aChannel->GetURI(getter_AddRefs(channelURI));
|
||||
nsCOMPtr<nsIURL> channelURL(do_QueryInterface(channelURI));
|
||||
nsCAutoString extension;
|
||||
if (channelURL) {
|
||||
channelURL->GetFileExtension(extension);
|
||||
if (!extension.IsEmpty())
|
||||
extHandler->ApplyDecodingForExtension(extension.get(), &applyConversion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpChannel->SetApplyConversion( applyConversion );
|
||||
}
|
||||
|
|
|
@ -60,6 +60,11 @@ interface nsIExternalHelperAppService : nsISupports
|
|||
// prior to saving or passing off to helper apps,
|
||||
// false otherwise
|
||||
boolean applyDecodingForType (in string aMimeContentType);
|
||||
|
||||
// applyDecodingForExtension: returns false if files with this extension
|
||||
// are not to be decoded prior to saving or
|
||||
// passing off to helper apps, false otherwise
|
||||
boolean applyDecodingForExtension(in string aExtension);
|
||||
};
|
||||
|
||||
// this is a private interface shared between external app handlers and the platform specific
|
||||
|
|
|
@ -197,6 +197,23 @@ function saveInternal(aURL, aDocument,
|
|||
function foundHeaderInfo(aSniffer, aData)
|
||||
{
|
||||
var contentType = aSniffer.contentType;
|
||||
var contentEncodingType = aSniffer.contentEncodingType;
|
||||
|
||||
var shouldDecode = false;
|
||||
// Are we allowed to decode?
|
||||
try {
|
||||
const helperAppService =
|
||||
Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].
|
||||
getService(Components.interfaces.nsIExternalHelperAppService);
|
||||
var url = aSniffer.uri.QueryInterface(Components.interfaces.nsIURL);
|
||||
var urlExt = url.fileExtension;
|
||||
if (helperAppService.applyDecodingForType(contentType) &&
|
||||
(!urlExt || helperAppService.applyDecodingForExtension(urlExt))) {
|
||||
shouldDecode = true;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
var fp = makeFilePicker();
|
||||
var titleKey = aData.filePickerTitle || "SaveLinkTitle";
|
||||
|
@ -206,7 +223,16 @@ function foundHeaderInfo(aSniffer, aData)
|
|||
|
||||
|
||||
var isDocument = aData.document != null && isDocumentType(contentType);
|
||||
appendFiltersForContentType(fp, aSniffer.contentType,
|
||||
if (!isDocument && !shouldDecode && contentEncodingType) {
|
||||
// The data is encoded, we are not going to decode it, and this is not a
|
||||
// document save so we won't be doing a "save as, complete" (which would
|
||||
// break if we reset the type here). So just set our content type to
|
||||
// correspond to the outermost encoding so we get extensions and the like
|
||||
// right.
|
||||
contentType = contentEncodingType;
|
||||
}
|
||||
|
||||
appendFiltersForContentType(fp, contentType,
|
||||
isDocument ? MODE_COMPLETE : MODE_FILEONLY);
|
||||
|
||||
const prefSvcContractID = "@mozilla.org/preferences-service;1";
|
||||
|
@ -278,15 +304,9 @@ function foundHeaderInfo(aSniffer, aData)
|
|||
else
|
||||
persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
|
||||
|
||||
try {
|
||||
const helperAppService =
|
||||
Components.classes["@mozilla.org/uriloader/external-helper-app-service;1"].getService(Components.interfaces.nsIExternalHelperAppService);
|
||||
if (helperAppService.applyDecodingForType(persistArgs.contentType)) {
|
||||
persist.persistFlags &= ~nsIWBP.PERSIST_FLAGS_NO_CONVERSION;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (shouldDecode)
|
||||
persist.persistFlags &= ~nsIWBP.PERSIST_FLAGS_NO_CONVERSION;
|
||||
|
||||
if (isDocument && fp.filterIndex != 1) {
|
||||
// Saving a Document, not a URI:
|
||||
var filesFolder = null;
|
||||
|
@ -403,6 +423,22 @@ nsHeaderSniffer.prototype = {
|
|||
this.contentType = channel.contentType;
|
||||
try {
|
||||
var httpChannel = channel.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||
this.contentEncodingType = null;
|
||||
// There may be content-encodings on the channel. Multiple content
|
||||
// encodings are allowed, eg "Content-Encoding: gzip, uuencode". This
|
||||
// header would mean that the content was first gzipped and then
|
||||
// uuencoded. The encoding enumerator returns MIME types
|
||||
// corresponding to each encoding starting from the end, so the first
|
||||
// thing it returns corresponds to the outermost encoding.
|
||||
var encodingEnumerator = httpChannel.contentEncodings;
|
||||
if (encodingEnumerator && encodingEnumerator.hasMoreElements()) {
|
||||
try {
|
||||
this.contentEncodingType =
|
||||
encodingEnumerator.getNext().
|
||||
QueryInterface(Components.interfaces.nsISupportsString).data;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
this.mContentDisposition = httpChannel.getResponseHeader("content-disposition");
|
||||
}
|
||||
catch (e) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче