diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 91ea378b971a..53cad12b2c00 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -107,7 +107,72 @@ static const char NEVER_ASK_FOR_OPEN_FILE_PREF[] = "openFile"; static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kPluginManagerCID, NS_PLUGINMANAGER_CID); -// The following static table lists all of the "default" content type mappings we are going to use. +// Helper functions for Content-Disposition headers + +/** 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 httpChannel(do_QueryInterface(aChannel)); + if (httpChannel) + { + httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"), aDisposition); + } + if (aDisposition.IsEmpty()) + { + nsCOMPtr 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 mimehdrpar(aMIMEHeaderParam); + if (!mimehdrpar) { + mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID); + if (!mimehdrpar) + return; + } + + nsCOMPtr 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); + + return; +} + + +// The following static table lists all of the "default" content type mappings we are going to use struct nsDefaultMimeTypeEntry { const char* mMimeType; const char* mFileExtension; @@ -314,9 +379,29 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const char *aMimeContentType nsCOMPtr uri; channel->GetURI(getter_AddRefs(uri)); + // Firstly, get the content-disposition header + nsCAutoString disp; + ExtractDisposition(channel, disp); + if (!disp.IsEmpty()) { + nsAutoString filename; + GetFilenameFromDisposition(filename, disp, uri); + if (!filename.IsEmpty()) { + LOG(("Getting filename from disposition: Disp='%s', filename='%s'\n", + disp.get(), NS_ConvertUTF16toUTF8(filename).get())); + PRInt32 pointPos = filename.RFindChar('.'); + if (pointPos != kNotFound) { + const nsAString & ext = Substring(filename, pointPos + 1, filename.Length() - pointPos); + CopyUTF16toUTF8(ext, fileExtension); + LOG(("...found extension '%s'\n", fileExtension.get())); + } + } + } + + // If the method is post, don't bother getting the file extension; // it will belong to a CGI script anyway - if (uri && !methodIsPost) { + // Also, if we already have an extension, don't bother + if (uri && !methodIsPost && fileExtension.IsEmpty()) { url = do_QueryInterface(uri); if (url) { @@ -915,25 +1000,12 @@ void nsExternalAppHandler::ExtractSuggestedFileNameFromChannel(nsIChannel* aChan * permission...o.t. just use our temp file */ nsCAutoString disp; - nsresult rv = NS_OK; - // First see whether this is an http channel - nsCOMPtr httpChannel( do_QueryInterface( aChannel ) ); - if ( httpChannel ) - { - rv = httpChannel->GetResponseHeader( NS_LITERAL_CSTRING("content-disposition"), disp ); - } - if ( NS_FAILED(rv) || disp.IsEmpty() ) - { - nsCOMPtr multipartChannel( do_QueryInterface( aChannel ) ); - if ( multipartChannel ) - { - rv = multipartChannel->GetContentDisposition( disp ); - } - } + ExtractDisposition(aChannel, disp); // content-disposition: has format: // disposition-type < ; name=value >* < ; filename=value > < ; name=value >* - if ( NS_SUCCEEDED( rv ) && !disp.IsEmpty() ) + if (!disp.IsEmpty()) { + nsresult rv; nsCOMPtr mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); if (NS_FAILED(rv)) return; @@ -956,25 +1028,7 @@ void nsExternalAppHandler::ExtractSuggestedFileNameFromChannel(nsIChannel* aChan // We may not have a disposition type listed; some servers suck. // But they could have listed a filename anyway. - nsCOMPtr srcUri; - GetSource(getter_AddRefs(srcUri)); - nsCOMPtr url = do_QueryInterface(srcUri); - - nsCAutoString fallbackCharset; - nsAutoString fileName; - if (url) - url->GetOriginCharset(fallbackCharset); - // Get the value of 'filename' parameter - rv = mimehdrpar->GetParameter(disp, "filename", fallbackCharset, - PR_TRUE, nsnull, fileName); - if (NS_FAILED(rv) || fileName.IsEmpty()) - // Try 'name' parameter, instead. - rv = mimehdrpar->GetParameter(disp, "name",fallbackCharset, PR_TRUE, - nsnull, fileName); - if (NS_FAILED(rv) || fileName.IsEmpty()) - return; - - mSuggestedFileName = fileName; + GetFilenameFromDisposition(mSuggestedFileName, disp, mSourceUrl, mimehdrpar); #ifdef XP_WIN // Make sure extension is still correct. @@ -1207,7 +1261,7 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo // ignore failure... ExtractSuggestedFileNameFromChannel(aChannel); nsCOMPtr httpChannel = do_QueryInterface( aChannel ); - if ( httpChannel ) + if (httpChannel) { // Turn off content encoding conversions if needed PRBool applyConversion = PR_TRUE; diff --git a/uriloader/exthandler/win/nsOSHelperAppService.cpp b/uriloader/exthandler/win/nsOSHelperAppService.cpp index c1734e70043b..fc7dce079e7c 100644 --- a/uriloader/exthandler/win/nsOSHelperAppService.cpp +++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp @@ -401,23 +401,16 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const char *a already_AddRefed nsOSHelperAppService::GetMIMEInfoFromOS(const char *aMIMEType, const char *aFileExt) { - if (PL_strcasecmp(aMIMEType, APPLICATION_OCTET_STREAM) == 0) { - /* XXX Gross hack to wallpaper over the most common Win32 - * extension issues caused by the fix for bug 116938. See bug - * 120327, comment 271 for why this is needed. Not even sure we - * want to remove this once we have fixed all this stuff to work - * right; any info we get from the OS on this type is pretty much - * useless.... - * Just lookup by extension for this filetype. - */ - aMIMEType = nsnull; - // If we now have nothing to lookup from, return - if (!aFileExt || !*aFileExt) - return nsnull; - } - nsCAutoString fileExtension; - if (aMIMEType && *aMIMEType) { + /* XXX The strcasecmp is a gross hack to wallpaper over the most common Win32 + * extension issues caused by the fix for bug 116938. See bug + * 120327, comment 271 for why this is needed. Not even sure we + * want to remove this once we have fixed all this stuff to work + * right; any info we get from the OS on this type is pretty much + * useless.... + * We'll do extension-based lookup for this type later in this function. + */ + if (aMIMEType && *aMIMEType && PL_strcasecmp(aMIMEType, APPLICATION_OCTET_STREAM) != 0) { // (1) try to use the windows mime database to see if there is a mapping to a file extension // (2) try to see if we have some left over 4.x registry info we can peek at... GetExtensionFromWindowsMimeDatabase(aMIMEType, fileExtension);