Make <object> do the same text/plain sniffing that <iframe> does, and use the content type hint if the type comes back as application/octet-stream or sniffed-binary.
Bug 389677, r+sr=biesi, a=sicking
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -70,16 +70,17 @@
#include "nsCURILoader.h"
#include "nsContentPolicyUtils.h"
#include "nsContentUtils.h"
#include "nsDocShellCID.h"
#include "nsGkAtoms.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsPresShellIterator.h"
+#include "nsMimeTypes.h"
// Concrete classes
#include "nsFrameLoader.h"
#include "nsObjectLoadingContent.h"
static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID);
@@ -368,19 +369,35 @@ nsObjectLoadingContent::OnStartRequest(n
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
NS_ASSERTION(chan, "Why is our request not a channel?");
nsresult rv = NS_ERROR_UNEXPECTED;
// This fallback variable MUST be declared after the notifier variable. Do NOT
// change the order of the declarations!
AutoFallback fallback(this, &rv);
- rv = chan->GetContentType(mContentType);
+ nsCString channelType;
+ rv = chan->GetContentType(channelType);
NS_ENSURE_SUCCESS(rv, rv);
+ if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
+ channelType = APPLICATION_OCTET_STREAM;
+ chan->SetContentType(channelType);
+ }
+
+ if (mContentType.IsEmpty() ||
+ !channelType.EqualsASCII(APPLICATION_OCTET_STREAM)) {
+ mContentType = channelType;
+ } else {
+ // Set the type we'll use for dispatch on the channel. Otherwise we could
+ // end up trying to dispatch to a nsFrameLoader, which will complain that
+ // it couldn't find a way to handle application/octet-stream
+ chan->SetContentType(mContentType);
+ }
+
// Now find out what type the content is
// UnloadContent will set our type to null; need to be sure to only set it to
// the real value on success
ObjectType newType = GetTypeOfContent(mContentType);
LOG(("OBJLC [%p]: OnStartRequest: Content Type=<%s> Old type=%u New Type=%u\n",
this, mContentType.get(), mType, newType));
if (mType != newType) {
UnloadContent();
@@ -1066,17 +1083,18 @@ nsObjectLoadingContent::LoadObject(nsIUR
mType = eType_Plugin;
rv = TryInstantiate(aTypeHint, aURI);
return NS_OK;
}
nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
nsCOMPtr<nsIChannel> chan;
- rv = NS_NewChannel(getter_AddRefs(chan), aURI, nsnull, group, this);
+ rv = NS_NewChannel(getter_AddRefs(chan), aURI, nsnull, group, this,
+ nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
NS_ENSURE_SUCCESS(rv, rv);
// Referrer
nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
if (httpChan) {
httpChan->SetReferrer(doc->GetDocumentURI());
}
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -779,16 +779,24 @@
/**
* General-purpose content sniffer component. Use with CreateInstance.
*
* Implements nsIContentSniffer
*/
#define NS_GENERIC_CONTENT_SNIFFER \
"@mozilla.org/network/content-sniffer;1"
+/**
+ * Detector that can act as either an nsIStreamConverter or an
+ * nsIContentSniffer to decide whether text/plain data is "really" text/plain
+ * or APPLICATION_GUESS_FROM_EXT. Use with CreateInstance.
+ */
+#define NS_BINARYDETECTOR_CONTRACTID \
+ "@mozilla.org/network/binary-detector;1"
+
/******************************************************************************
* netwerk/system classes
*/
// service implementing nsINetworkLinkService
#define NS_NETWORK_LINK_SERVICE_CLASSNAME "Network Link Status"
#define NS_NETWORK_LINK_SERVICE_CID \
{ 0x75a500a2, \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -296,17 +296,16 @@ nsresult NS_NewStreamConv(nsStreamConver
#define FTP_TO_INDEX "?from=text/ftp-dir&to=application/http-index-format"
#define GOPHER_TO_INDEX "?from=text/gopher-dir&to=application/http-index-format"
#define INDEX_TO_HTML "?from=application/http-index-format&to=text/html"
#define MULTI_MIXED_X "?from=multipart/x-mixed-replace&to=*/*"
#define MULTI_MIXED "?from=multipart/mixed&to=*/*"
#define MULTI_BYTERANGES "?from=multipart/byteranges&to=*/*"
#define UNKNOWN_CONTENT "?from=" UNKNOWN_CONTENT_TYPE "&to=*/*"
-#define MAYBE_TEXT "?from=" APPLICATION_MAYBE_TEXT "&to=*/*"
#define GZIP_TO_UNCOMPRESSED "?from=gzip&to=uncompressed"
#define XGZIP_TO_UNCOMPRESSED "?from=x-gzip&to=uncompressed"
#define COMPRESS_TO_UNCOMPRESSED "?from=compress&to=uncompressed"
#define XCOMPRESS_TO_UNCOMPRESSED "?from=x-compress&to=uncompressed"
#define DEFLATE_TO_UNCOMPRESSED "?from=deflate&to=uncompressed"
#define PLAIN_TO_HTML "?from=text/plain&to=text/html"
#ifdef BUILD_BINHEX_DECODER
@@ -316,17 +315,16 @@ nsresult NS_NewStreamConv(nsStreamConver
static const char *const sStreamConverterArray[] = {
FTP_TO_INDEX,
GOPHER_TO_INDEX,
INDEX_TO_HTML,
MULTI_MIXED_X,
MULTI_MIXED,
MULTI_BYTERANGES,
UNKNOWN_CONTENT,
- MAYBE_TEXT,
GZIP_TO_UNCOMPRESSED,
XGZIP_TO_UNCOMPRESSED,
COMPRESS_TO_UNCOMPRESSED,
XCOMPRESS_TO_UNCOMPRESSED,
DEFLATE_TO_UNCOMPRESSED,
#ifdef BUILD_BINHEX_DECODER
BINHEX_TO_WILD,
#endif
@@ -850,18 +848,19 @@ static const nsModuleComponentInfo gNetM
{ "Unknown Content-Type Decoder",
NS_UNKNOWNDECODER_CID,
NS_GENERIC_CONTENT_SNIFFER,
CreateNewUnknownDecoderFactory
},
{ "Binary Detector",
NS_BINARYDETECTOR_CID,
- NS_ISTREAMCONVERTER_KEY MAYBE_TEXT,
- CreateNewBinaryDetectorFactory
+ NS_BINARYDETECTOR_CONTRACTID,
+ CreateNewBinaryDetectorFactory,
+ nsBinaryDetector::Register
},
{ "HttpCompressConverter",
NS_HTTPCOMPRESSCONVERTER_CID,
NS_ISTREAMCONVERTER_KEY GZIP_TO_UNCOMPRESSED,
CreateNewHTTPCompressConvFactory
},
--- a/netwerk/mime/public/nsMimeTypes.h
+++ b/netwerk/mime/public/nsMimeTypes.h
@@ -186,14 +186,13 @@
#define PARAM_MICALG_SHA1_4 "rsa-sha-1"
#define PARAM_MICALG_SHA1_5 "rsa-sha"
#define PARAM_X_MAC_CREATOR "x-mac-creator"
#define PARAM_X_MAC_TYPE "x-mac-type"
#define PARAM_FORMAT "format"
#define UNKNOWN_CONTENT_TYPE "application/x-unknown-content-type"
#define APPLICATION_GUESS_FROM_EXT "application/x-vnd.mozilla.guess-from-ext"
-#define APPLICATION_MAYBE_TEXT "application/x-vnd.mozilla.maybe-text"
#define VIEWSOURCE_CONTENT_TYPE "application/x-view-source"
#define APPLICATION_DIRECTORY "application/directory" /* text/x-vcard is synonym */
#endif /* nsMimeTypes_h_ */
--- a/netwerk/streamconv/converters/nsUnknownDecoder.cpp
+++ b/netwerk/streamconv/converters/nsUnknownDecoder.cpp
@@ -51,27 +51,22 @@
#include "nsISupportsPrimitives.h"
#include "nsIContentSniffer.h"
#include "nsCRT.h"
#include "nsIMIMEService.h"
#include "nsIViewSourceChannel.h"
+#include "nsIHttpChannel.h"
+#include "nsNetCID.h"
-#include "prcpucfg.h" // To get IS_LITTLE_ENDIAN / IS_BIG_ENDIAN
#define MAX_BUFFER_SIZE 1024
-#if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN
-#define LITTLE_TO_NATIVE16(x) ((((x) & 0xFF) << 8) | ((x) >> 8))
-#else
-#define LITTLE_TO_NATIVE16(x) x
-#endif
-
nsUnknownDecoder::nsUnknownDecoder()
: mBuffer(nsnull)
, mBufferLen(0)
, mRequireHTMLsuffix(PR_FALSE)
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
PRBool val;
@@ -182,18 +177,16 @@ nsUnknownDecoder::OnDataAvailable(nsIReq
// Adjust the source offset... The call to FireListenerNotifications(...)
// will make the first OnDataAvailable(...) call with an offset of 0.
// So, this offset needs to be adjusted to reflect that...
//
aSourceOffset += mBufferLen;
DetermineContentType(request);
- NS_ASSERTION(!mContentType.IsEmpty(),
- "Content type should be known by now.");
rv = FireListenerNotifications(request, aCtxt);
}
}
// Must not fire ODA again if it failed once
if (aCount && NS_SUCCEEDED(rv)) {
NS_ASSERTION(!mContentType.IsEmpty(),
"Content type should be known by now.");
@@ -241,18 +234,16 @@ nsUnknownDecoder::OnStopRequest(nsIReque
//
// The total amount of data is less than the size of the sniffer buffer.
// Analyze the buffer now...
//
if (mContentType.IsEmpty()) {
DetermineContentType(request);
- NS_ASSERTION(!mContentType.IsEmpty(),
- "Content type should be known by now.");
rv = FireListenerNotifications(request, aCtxt);
if (NS_FAILED(rv)) {
aStatus = rv;
}
}
rv = mNextListener->OnStopRequest(request, aCtxt, aStatus);
@@ -359,39 +350,51 @@ void nsUnknownDecoder::DetermineContentT
sSnifferEntries[i].mContentTypeSniffer,
"Must have either a type string or a function to set the type");
NS_ASSERTION(sSnifferEntries[i].mMimeType == nsnull ||
sSnifferEntries[i].mContentTypeSniffer == nsnull,
"Both a type string and a type sniffing function set;"
" using type string");
if (sSnifferEntries[i].mMimeType) {
mContentType = sSnifferEntries[i].mMimeType;
+ NS_ASSERTION(!mContentType.IsEmpty(),
+ "Content type should be known by now.");
return;
}
- else if ((this->*(sSnifferEntries[i].mContentTypeSniffer))(aRequest)) {
+ if ((this->*(sSnifferEntries[i].mContentTypeSniffer))(aRequest)) {
+ NS_ASSERTION(!mContentType.IsEmpty(),
+ "Content type should be known by now.");
return;
}
}
}
if (TryContentSniffers(aRequest)) {
+ NS_ASSERTION(!mContentType.IsEmpty(),
+ "Content type should be known by now.");
return;
}
if (SniffForHTML(aRequest)) {
+ NS_ASSERTION(!mContentType.IsEmpty(),
+ "Content type should be known by now.");
return;
}
// We don't know what this is yet. Before we just give up, try
// the URI from the request.
if (SniffURI(aRequest)) {
+ NS_ASSERTION(!mContentType.IsEmpty(),
+ "Content type should be known by now.");
return;
}
LastDitchSniff(aRequest);
+ NS_ASSERTION(!mContentType.IsEmpty(),
+ "Content type should be known by now.");
}
PRBool nsUnknownDecoder::TryContentSniffers(nsIRequest* aRequest)
{
// Enumerate content sniffers
nsCOMPtr<nsICategoryManager> catMan(do_GetService("@mozilla.org/categorymanager;1"));
if (!catMan) {
return PR_FALSE;
@@ -595,34 +598,38 @@ PRBool nsUnknownDecoder::LastDitchSniff(
nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
nsISupports *aCtxt)
{
nsresult rv = NS_OK;
if (!mNextListener) return NS_ERROR_FAILURE;
- nsCOMPtr<nsIViewSourceChannel> viewSourceChannel = do_QueryInterface(request);
- if (viewSourceChannel) {
- rv = viewSourceChannel->SetOriginalContentType(mContentType);
- } else {
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
- if (NS_SUCCEEDED(rv)) {
- // Set the new content type on the channel...
- rv = channel->SetContentType(mContentType);
+ if (!mContentType.IsEmpty()) {
+ nsCOMPtr<nsIViewSourceChannel> viewSourceChannel =
+ do_QueryInterface(request);
+ if (viewSourceChannel) {
+ rv = viewSourceChannel->SetOriginalContentType(mContentType);
+ } else {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ // Set the new content type on the channel...
+ rv = channel->SetContentType(mContentType);
+ }
}
- }
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to set content type on channel!");
- NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to set content type on channel!");
- if (NS_FAILED(rv)) {
- // Cancel the request to make sure it has the correct status if
- // mNextListener looks at it.
- request->Cancel(rv);
- mNextListener->OnStartRequest(request, aCtxt);
- return rv;
+ if (NS_FAILED(rv)) {
+ // Cancel the request to make sure it has the correct status if
+ // mNextListener looks at it.
+ request->Cancel(rv);
+ mNextListener->OnStartRequest(request, aCtxt);
+ return rv;
+ }
}
// Fire the OnStartRequest(...)
rv = mNextListener->OnStartRequest(request, aCtxt);
if (!mBuffer) return NS_ERROR_OUT_OF_MEMORY;
// If the request was canceled, then we need to treat that equivalently
@@ -659,14 +666,68 @@ nsresult nsUnknownDecoder::FireListenerN
mBufferLen = 0;
return rv;
}
void
nsBinaryDetector::DetermineContentType(nsIRequest* aRequest)
{
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
+ if (!httpChannel) {
+ return;
+ }
+
+ // It's an HTTP channel. Check for the text/plain mess
+ nsCAutoString contentTypeHdr;
+ httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
+ contentTypeHdr);
+ nsCAutoString contentType;
+ httpChannel->GetContentType(contentType);
+
+ // Make sure to do a case-sensitive exact match comparison here. Apache
+ // 1.x just sends text/plain for "unknown", while Apache 2.x sends
+ // text/plain with a ISO-8859-1 charset. Debian's Apache version, just to
+ // be different, sends text/plain with iso-8859-1 charset. Don't do
+ // general case-insensitive comparison, since we really want to apply this
+ // crap as rarely as we can.
+ if (!contentType.EqualsLiteral("text/plain") ||
+ (!contentTypeHdr.EqualsLiteral("text/plain") &&
+ !contentTypeHdr.EqualsLiteral("text/plain; charset=ISO-8859-1") &&
+ !contentTypeHdr.EqualsLiteral("text/plain; charset=iso-8859-1"))) {
+ return;
+ }
+
+ // Check whether we have content-encoding. If we do, don't try to
+ // detect the type.
+ // XXXbz we could improve this by doing a local decompress if we
+ // wanted, I'm sure.
+ nsCAutoString contentEncoding;
+ httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
+ contentEncoding);
+ if (!contentEncoding.IsEmpty()) {
+ return;
+ }
+
LastDitchSniff(aRequest);
if (mContentType.Equals(APPLICATION_OCTET_STREAM)) {
// We want to guess at it instead
mContentType = APPLICATION_GUESS_FROM_EXT;
}
}
+
+NS_METHOD
+nsBinaryDetector::Register(nsIComponentManager* compMgr, nsIFile* path,
+ const char* registryLocation,
+ const char* componentType,
+ const nsModuleComponentInfo *info)
+{
+ nsresult rv;
+ nsCOMPtr<nsICategoryManager> catman =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return catman->AddCategoryEntry(NS_CONTENT_SNIFFER_CATEGORY,
+ "Binary Detector",
+ NS_BINARYDETECTOR_CONTRACTID,
+ PR_TRUE, PR_TRUE, nsnull);
+}
--- a/netwerk/streamconv/converters/nsUnknownDecoder.h
+++ b/netwerk/streamconv/converters/nsUnknownDecoder.h
@@ -36,16 +36,17 @@
* ***** END LICENSE BLOCK ***** */
#ifndef nsUnknownDecoder_h__
#define nsUnknownDecoder_h__
#include "nsIStreamConverter.h"
#include "nsIChannel.h"
#include "nsIContentSniffer.h"
+#include "nsIGenericFactory.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#define NS_UNKNOWNDECODER_CID \
{ /* 7d7008a0-c49a-11d3-9b22-0080c7cb1080 */ \
0x7d7008a0, \
0xc49a, \
@@ -153,14 +154,20 @@ protected:
/**
* Class that detects whether a data stream is text or binary. This reuses
* most of nsUnknownDecoder except the actual content-type determination logic
* -- our overridden DetermineContentType simply calls LastDitchSniff and sets
* the type to APPLICATION_GUESS_FROM_EXT if the data is detected as binary.
*/
class nsBinaryDetector : public nsUnknownDecoder
{
+public:
+ static NS_METHOD Register(nsIComponentManager* compMgr, nsIFile* path,
+ const char* registryLocation,
+ const char* componentType,
+ const nsModuleComponentInfo *info);
+
protected:
virtual void DetermineContentType(nsIRequest* aRequest);
};
#endif /* nsUnknownDecoder_h__ */
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -272,72 +272,17 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStar
//
// The transaction has already reported an error - so it will be torn
// down. Therefore, it is not necessary to return an error code...
//
return NS_OK;
}
- if (httpChannel && mContentType.IsEmpty()) {
- // This is our initial dispatch, and this is an HTTP channel. Check for
- // the text/plain mess.
- nsCAutoString contentTypeHdr;
- httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
- contentTypeHdr);
- nsCAutoString contentType;
- httpChannel->GetContentType(contentType);
-
- // Make sure to do a case-sensitive exact match comparison here. Apache
- // 1.x just sends text/plain for "unknown", while Apache 2.x sends
- // text/plain with a ISO-8859-1 charset. Debian's Apache version, just to
- // be different, sends text/plain with iso-8859-1 charset. Don't do
- // general case-insensitive comparison, since we really want to apply this
- // crap as rarely as we can.
- if (contentType.EqualsLiteral("text/plain") &&
- (contentTypeHdr.EqualsLiteral("text/plain") ||
- contentTypeHdr.Equals(
- NS_LITERAL_CSTRING("text/plain; charset=ISO-8859-1")) ||
- contentTypeHdr.Equals(
- NS_LITERAL_CSTRING("text/plain; charset=iso-8859-1")))) {
- // Check whether we have content-encoding. If we do, don't try to detect
- // the type, since that will lead to the content being automatically
- // decompressed....
- nsCAutoString contentEncoding;
- httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
- contentEncoding);
- if (contentEncoding.IsEmpty()) {
- // OK, this is initial dispatch of an HTTP response and its Content-Type
- // header is exactly "text/plain". We need to check whether this is
- // really text.... Note that some of our listeners will actually
- // accept all types, including the APPLICATION_MAYBE_TEXT internal
- // type, so we need to call ConvertData here manually instead of
- // relying on DispatchContent to do it.
- LOG((" Possibly bogus text/plain; trying to sniff for real type"));
- rv = ConvertData(request, m_contentListener,
- NS_LITERAL_CSTRING(APPLICATION_MAYBE_TEXT),
- NS_LITERAL_CSTRING("*/*"));
- if (NS_FAILED(rv)) {
- // We failed to convert. Just go ahead and handle as the original
- // type. If ConvertData happened to set our m_targetStreamListener,
- // we don't want it!
- m_targetStreamListener = nsnull;
- }
- else {
- LOG((APPLICATION_MAYBE_TEXT " converter taking over now"));
- }
- }
- }
- }
-
- // If we sniffed text/plain above, m_targetStreamListener may already be
- // non-null.
- if (!m_targetStreamListener) {
- rv = DispatchContent(request, aCtxt);
- }
+ rv = DispatchContent(request, aCtxt);
LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08X", m_targetStreamListener.get(), rv));
NS_ASSERTION(NS_SUCCEEDED(rv) || !m_targetStreamListener,
"Must not have an m_targetStreamListener with a failure return!");
NS_ENSURE_SUCCESS(rv, rv);