Bug 1365519 - Make nsUnknownDecoder nsIThreadRetargetableStreamListener. r=mayhemer

This commit is contained in:
Dragana Damjanovic 2017-06-22 10:49:00 -04:00
Родитель 062ca88ee0
Коммит da6f218076
2 изменённых файлов: 164 добавлений и 41 удалений

Просмотреть файл

@ -74,8 +74,15 @@ nsUnknownDecoder::ConvertedStreamListener::
uint32_t count)
{
uint32_t read;
return stream->ReadSegments(AppendDataToString, &mDecoder->mDecodedData, count,
&read);
nsAutoCString decodedData;
nsresult rv = stream->ReadSegments(AppendDataToString, &decodedData, count,
&read);
if (NS_FAILED(rv)) {
return rv;
}
MutexAutoLock lock(mDecoder->mMutex);
mDecoder->mDecodedData = decodedData;
return NS_OK;
}
NS_IMETHODIMP
@ -90,6 +97,7 @@ nsUnknownDecoder::nsUnknownDecoder()
: mBuffer(nullptr)
, mBufferLen(0)
, mRequireHTMLsuffix(false)
, mMutex("nsUnknownDecoder")
, mDecodedData("")
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
@ -122,6 +130,7 @@ NS_INTERFACE_MAP_BEGIN(nsUnknownDecoder)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIContentSniffer)
NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
NS_INTERFACE_MAP_END
@ -153,6 +162,8 @@ nsUnknownDecoder::AsyncConvertData(const char *aFromType,
// hook up our final listener. this guy gets the various On*() calls we want to throw
// at him.
//
MutexAutoLock lock(mMutex);
mNextListener = aListener;
return (aListener) ? NS_OK : NS_ERROR_FAILURE;
}
@ -172,9 +183,15 @@ nsUnknownDecoder::OnDataAvailable(nsIRequest* request,
{
nsresult rv = NS_OK;
if (!mNextListener) return NS_ERROR_FAILURE;
bool contentTypeEmpty;
{
MutexAutoLock lock(mMutex);
if (!mNextListener) return NS_ERROR_FAILURE;
if (mContentType.IsEmpty()) {
contentTypeEmpty = mContentType.IsEmpty();
}
if (contentTypeEmpty) {
uint32_t count, len;
// If the buffer has not been allocated by now, just fail...
@ -185,7 +202,7 @@ nsUnknownDecoder::OnDataAvailable(nsIRequest* request,
// sniffer buffer...
//
if (mBufferLen + aCount >= MAX_BUFFER_SIZE) {
count = MAX_BUFFER_SIZE-mBufferLen;
count = MAX_BUFFER_SIZE - mBufferLen;
} else {
count = aCount;
}
@ -213,8 +230,13 @@ nsUnknownDecoder::OnDataAvailable(nsIRequest* request,
// 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.");
#ifdef DEBUG
{
MutexAutoLock lock(mMutex);
NS_ASSERTION(!mContentType.IsEmpty(),
"Content type should be known by now.");
}
#endif
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
if (divertable) {
@ -225,8 +247,14 @@ nsUnknownDecoder::OnDataAvailable(nsIRequest* request,
return rv;
}
}
rv = mNextListener->OnDataAvailable(request, aCtxt, aStream,
aSourceOffset, aCount);
nsCOMPtr<nsIStreamListener> listener;
{
MutexAutoLock lock(mMutex);
listener = mNextListener;
}
rv = listener->OnDataAvailable(request, aCtxt, aStream,
aSourceOffset, aCount);
}
return rv;
@ -243,7 +271,10 @@ nsUnknownDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt)
{
nsresult rv = NS_OK;
if (!mNextListener) return NS_ERROR_FAILURE;
{
MutexAutoLock lock(mMutex);
if (!mNextListener) return NS_ERROR_FAILURE;
}
// Allocate the sniffer buffer...
if (NS_SUCCEEDED(rv) && !mBuffer) {
@ -269,13 +300,19 @@ nsUnknownDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt,
{
nsresult rv = NS_OK;
if (!mNextListener) return NS_ERROR_FAILURE;
bool contentTypeEmpty;
{
MutexAutoLock lock(mMutex);
if (!mNextListener) return NS_ERROR_FAILURE;
contentTypeEmpty = mContentType.IsEmpty();
}
//
// The total amount of data is less than the size of the sniffer buffer.
// Analyze the buffer now...
//
if (mContentType.IsEmpty()) {
if (contentTypeEmpty) {
DetermineContentType(request);
// Make sure channel listeners see channel as pending while we call
@ -298,8 +335,13 @@ nsUnknownDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt,
}
}
rv = mNextListener->OnStopRequest(request, aCtxt, aStatus);
mNextListener = nullptr;
nsCOMPtr<nsIStreamListener> listener;
{
MutexAutoLock lock(mMutex);
listener = mNextListener;
mNextListener = nullptr;
}
rv = listener->OnStopRequest(request, aCtxt, aStatus);
return rv;
}
@ -315,6 +357,9 @@ nsUnknownDecoder::GetMIMETypeFromContent(nsIRequest* aRequest,
uint32_t aLength,
nsACString& type)
{
// This is only used by sniffer, therefore we do not need to lock anything
// here.
mBuffer = const_cast<char*>(reinterpret_cast<const char*>(aData));
mBufferLen = aLength;
DetermineContentType(aRequest);
@ -388,20 +433,29 @@ uint32_t nsUnknownDecoder::sSnifferEntryNum =
void nsUnknownDecoder::DetermineContentType(nsIRequest* aRequest)
{
NS_ASSERTION(mContentType.IsEmpty(), "Content type is already known.");
if (!mContentType.IsEmpty()) return;
{
MutexAutoLock lock(mMutex);
NS_ASSERTION(mContentType.IsEmpty(), "Content type is already known.");
if (!mContentType.IsEmpty()) return;
}
const char* testData = mBuffer;
uint32_t testDataLen = mBufferLen;
// Check if data are compressed.
nsCOMPtr<nsIHttpChannel> channel(do_QueryInterface(aRequest));
nsAutoCString decodedData;
if (channel) {
// ConvertEncodedData is always called only on a single thread for each
// instance of an object.
nsresult rv = ConvertEncodedData(aRequest, mBuffer, mBufferLen);
if (NS_SUCCEEDED(rv)) {
if (!mDecodedData.IsEmpty()) {
testData = mDecodedData.get();
testDataLen = std::min(mDecodedData.Length(), MAX_BUFFER_SIZE);
}
MutexAutoLock lock(mMutex);
decodedData = mDecodedData;
}
if (!decodedData.IsEmpty()) {
testData = decodedData.get();
testDataLen = std::min(decodedData.Length(), MAX_BUFFER_SIZE);
}
}
@ -419,42 +473,60 @@ void nsUnknownDecoder::DetermineContentType(nsIRequest* aRequest)
"Both a type string and a type sniffing function set;"
" using type string");
if (sSnifferEntries[i].mMimeType) {
MutexAutoLock lock(mMutex);
mContentType = sSnifferEntries[i].mMimeType;
NS_ASSERTION(!mContentType.IsEmpty(),
"Content type should be known by now.");
return;
}
if ((this->*(sSnifferEntries[i].mContentTypeSniffer))(aRequest)) {
#ifdef DEBUG
MutexAutoLock lock(mMutex);
NS_ASSERTION(!mContentType.IsEmpty(),
"Content type should be known by now.");
#endif
return;
}
}
}
nsAutoCString sniffedType;
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest,
(const uint8_t*)testData, testDataLen, mContentType);
if (!mContentType.IsEmpty()) {
return;
(const uint8_t*)testData, testDataLen, sniffedType);
{
MutexAutoLock lock(mMutex);
mContentType = sniffedType;
if (!mContentType.IsEmpty()) {
return;
}
}
if (SniffForHTML(aRequest)) {
#ifdef DEBUG
MutexAutoLock lock(mMutex);
NS_ASSERTION(!mContentType.IsEmpty(),
"Content type should be known by now.");
#endif
return;
}
// We don't know what this is yet. Before we just give up, try
// the URI from the request.
if (SniffURI(aRequest)) {
#ifdef DEBUG
MutexAutoLock lock(mMutex);
NS_ASSERTION(!mContentType.IsEmpty(),
"Content type should be known by now.");
#endif
return;
}
LastDitchSniff(aRequest);
#ifdef DEBUG
MutexAutoLock lock(mMutex);
NS_ASSERTION(!mContentType.IsEmpty(),
"Content type should be known by now.");
#endif
}
bool nsUnknownDecoder::SniffForHTML(nsIRequest* aRequest)
@ -468,6 +540,8 @@ bool nsUnknownDecoder::SniffForHTML(nsIRequest* aRequest)
return false;
}
MutexAutoLock lock(mMutex);
// Now look for HTML.
const char* str;
const char* end;
@ -553,6 +627,7 @@ bool nsUnknownDecoder::SniffForXML(nsIRequest* aRequest)
// First see whether we can glean anything from the uri...
if (!SniffURI(aRequest)) {
// Oh well; just generic XML will have to do
MutexAutoLock lock(mMutex);
mContentType = TEXT_XML;
}
@ -571,6 +646,7 @@ bool nsUnknownDecoder::SniffURI(nsIRequest* aRequest)
nsAutoCString type;
result = mimeService->GetTypeFromURI(uri, type);
if (NS_SUCCEEDED(result)) {
MutexAutoLock lock(mMutex);
mContentType = type;
return true;
}
@ -592,6 +668,8 @@ bool nsUnknownDecoder::LastDitchSniff(nsIRequest* aRequest)
// All we can do now is try to guess whether this is text/plain or
// application/octet-stream
MutexAutoLock lock(mMutex);
const char* testData;
uint32_t testDataLen;
if (mDecodedData.IsEmpty()) {
@ -642,18 +720,26 @@ nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
{
nsresult rv = NS_OK;
if (!mNextListener) return NS_ERROR_FAILURE;
nsCOMPtr<nsIStreamListener> listener;
nsAutoCString contentType;
{
MutexAutoLock lock(mMutex);
if (!mNextListener) return NS_ERROR_FAILURE;
if (!mContentType.IsEmpty()) {
listener = mNextListener;
contentType = mContentType;
}
if (!contentType.IsEmpty()) {
nsCOMPtr<nsIViewSourceChannel> viewSourceChannel =
do_QueryInterface(request);
if (viewSourceChannel) {
rv = viewSourceChannel->SetOriginalContentType(mContentType);
rv = viewSourceChannel->SetOriginalContentType(contentType);
} else {
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
if (NS_SUCCEEDED(rv)) {
// Set the new content type on the channel...
rv = channel->SetContentType(mContentType);
rv = channel->SetContentType(contentType);
}
}
@ -663,7 +749,7 @@ nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
// Cancel the request to make sure it has the correct status if
// mNextListener looks at it.
request->Cancel(rv);
mNextListener->OnStartRequest(request, aCtxt);
listener->OnStartRequest(request, aCtxt);
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
if (divertable) {
@ -675,7 +761,7 @@ nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
}
// Fire the OnStartRequest(...)
rv = mNextListener->OnStartRequest(request, aCtxt);
rv = listener->OnStartRequest(request, aCtxt);
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
if (divertable) {
@ -692,10 +778,12 @@ nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
// install stream converter if required
nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(request);
if (encodedChannel) {
nsCOMPtr<nsIStreamListener> listener;
rv = encodedChannel->DoApplyContentConversions(mNextListener, getter_AddRefs(listener), aCtxt);
if (NS_SUCCEEDED(rv) && listener) {
mNextListener = listener;
nsCOMPtr<nsIStreamListener> listenerNew;
rv = encodedChannel->DoApplyContentConversions(listener, getter_AddRefs(listenerNew), aCtxt);
if (NS_SUCCEEDED(rv) && listenerNew) {
MutexAutoLock lock(mMutex);
mNextListener = listenerNew;
listener = listenerNew;
}
}
}
@ -722,7 +810,7 @@ nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
rv = out->Write(mBuffer, mBufferLen, &len);
if (NS_SUCCEEDED(rv)) {
if (len == mBufferLen) {
rv = mNextListener->OnDataAvailable(request, aCtxt, in, 0, len);
rv = listener->OnDataAvailable(request, aCtxt, in, 0, len);
} else {
NS_ERROR("Unable to write all the data into the pipe.");
rv = NS_ERROR_FAILURE;
@ -746,7 +834,10 @@ nsUnknownDecoder::ConvertEncodedData(nsIRequest* request,
{
nsresult rv = NS_OK;
mDecodedData = "";
{
MutexAutoLock lock(mMutex);
mDecodedData = "";
}
nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(request));
if (encodedChannel) {
@ -783,6 +874,24 @@ nsUnknownDecoder::ConvertEncodedData(nsIRequest* request,
return rv;
}
//
// nsIThreadRetargetableStreamListener methods
//
NS_IMETHODIMP
nsUnknownDecoder::CheckListenerChain()
{
nsCOMPtr<nsIThreadRetargetableStreamListener> listener;
{
MutexAutoLock lock(mMutex);
listener = do_QueryInterface(mNextListener);
}
if (!listener) {
return NS_ERROR_NO_INTERFACE;
}
return listener->CheckListenerChain();
}
void
nsBinaryDetector::DetermineContentType(nsIRequest* aRequest)
{
@ -823,8 +932,9 @@ nsBinaryDetector::DetermineContentType(nsIRequest* aRequest)
if (!contentEncoding.IsEmpty()) {
return;
}
LastDitchSniff(aRequest);
MutexAutoLock lock(mMutex);
if (mContentType.Equals(APPLICATION_OCTET_STREAM)) {
// We want to guess at it instead
mContentType = APPLICATION_GUESS_FROM_EXT;

Просмотреть файл

@ -7,7 +7,10 @@
#define nsUnknownDecoder_h__
#include "nsIStreamConverter.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsIContentSniffer.h"
#include "mozilla/Mutex.h"
#include "mozilla/Atomics.h"
#include "nsCOMPtr.h"
#include "nsString.h"
@ -21,7 +24,9 @@
}
class nsUnknownDecoder : public nsIStreamConverter, public nsIContentSniffer
class nsUnknownDecoder : public nsIStreamConverter
, public nsIContentSniffer
, public nsIThreadRetargetableStreamListener
{
public:
// nsISupports methods
@ -39,6 +44,9 @@ public:
// nsIContentSniffer methods
NS_DECL_NSICONTENTSNIFFER
// nsIThreadRetargetableStreamListener methods
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
nsUnknownDecoder();
protected:
@ -119,13 +127,18 @@ protected:
static nsSnifferEntry sSnifferEntries[];
static uint32_t sSnifferEntryNum;
char *mBuffer;
uint32_t mBufferLen;
bool mRequireHTMLsuffix;
// We guarantee in order delivery of OnStart, OnStop and OnData, therefore
// we do not need proper locking for mBuffer.
mozilla::Atomic<char *>mBuffer;
mozilla::Atomic<uint32_t> mBufferLen;
mozilla::Atomic<bool> mRequireHTMLsuffix;
nsCString mContentType;
// This mutex syncs: mContentType, mDecodedData and mNextListener.
mutable mozilla::Mutex mMutex;
protected:
nsresult ConvertEncodedData(nsIRequest* request, const char* data,
uint32_t length);