зеркало из https://github.com/mozilla/gecko-dev.git
Bug 497003 - Support delivery of OnDataAvailable on the HTML5 parser thread r=hsivonen r=bholley r=bz
This commit is contained in:
Родитель
1f8d40499c
Коммит
895826d5ed
|
@ -103,6 +103,16 @@ nsStreamListenerTee::CheckListenerChain()
|
|||
if (retargetableListener) {
|
||||
rv = retargetableListener->CheckListenerChain();
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!mObserver) {
|
||||
return rv;
|
||||
}
|
||||
retargetableListener = do_QueryInterface(mObserver, &rv);
|
||||
if (retargetableListener) {
|
||||
rv = retargetableListener->CheckListenerChain();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "nsINestedURI.h"
|
||||
#include "nsCharsetSource.h"
|
||||
#include "nsIWyciwygChannel.h"
|
||||
#include "nsIThreadRetargetableRequest.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
|
||||
|
@ -73,9 +75,10 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5StreamParser)
|
|||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5StreamParser)
|
||||
|
||||
NS_INTERFACE_TABLE_HEAD(nsHtml5StreamParser)
|
||||
NS_INTERFACE_TABLE2(nsHtml5StreamParser,
|
||||
NS_INTERFACE_TABLE3(nsHtml5StreamParser,
|
||||
nsIStreamListener,
|
||||
nsICharsetDetectionObserver)
|
||||
nsICharsetDetectionObserver,
|
||||
nsIThreadRetargetableStreamListener)
|
||||
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5StreamParser)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
@ -926,6 +929,14 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
mReparseForbidden = true;
|
||||
mFeedChardet = false; // can't restart anyway
|
||||
}
|
||||
|
||||
// Attempt to retarget delivery of data (via OnDataAvailable) to the parser
|
||||
// thread, rather than through the main thread.
|
||||
nsCOMPtr<nsIThreadRetargetableRequest> threadRetargetableRequest =
|
||||
do_QueryInterface(mRequest);
|
||||
if (threadRetargetableRequest) {
|
||||
threadRetargetableRequest->RetargetDeliveryTo(mThread);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCharsetSource == kCharsetFromParentFrame) {
|
||||
|
@ -960,6 +971,22 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHtml5StreamParser::CheckListenerChain()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
|
||||
if (!mObserver) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
|
||||
do_QueryInterface(mObserver, &rv);
|
||||
if (NS_SUCCEEDED(rv) && retargetable) {
|
||||
rv = retargetable->CheckListenerChain();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5StreamParser::DoStopRequest()
|
||||
{
|
||||
|
@ -1013,19 +1040,24 @@ nsHtml5StreamParser::OnStopRequest(nsIRequest* aRequest,
|
|||
nsresult status)
|
||||
{
|
||||
NS_ASSERTION(mRequest == aRequest, "Got Stop on wrong stream.");
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
NS_ASSERTION(NS_IsMainThread() || IsParserThread(), "Wrong thread!");
|
||||
if (mObserver) {
|
||||
mObserver->OnStopRequest(aRequest, aContext, status);
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> stopper = new nsHtml5RequestStopper(this);
|
||||
if (NS_FAILED(mThread->Dispatch(stopper, nsIThread::DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatching StopRequest event failed.");
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIRunnable> stopper = new nsHtml5RequestStopper(this);
|
||||
if (NS_FAILED(mThread->Dispatch(stopper, nsIThread::DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatching StopRequest event failed.");
|
||||
}
|
||||
} else {
|
||||
mozilla::MutexAutoLock autoLock(mTokenizerMutex);
|
||||
DoStopRequest();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHtml5StreamParser::DoDataAvailable(uint8_t* aBuffer, uint32_t aLength)
|
||||
nsHtml5StreamParser::DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength)
|
||||
{
|
||||
NS_ASSERTION(IsParserThread(), "Wrong thread!");
|
||||
NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
|
||||
|
@ -1110,24 +1142,58 @@ nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
|
|||
|
||||
NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
|
||||
uint32_t totalRead;
|
||||
const mozilla::fallible_t fallible = mozilla::fallible_t();
|
||||
nsAutoArrayPtr<uint8_t> data(new (fallible) uint8_t[aLength]);
|
||||
if (!data) {
|
||||
return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
|
||||
// Main thread to parser thread dispatch requires copying to buffer first.
|
||||
if (NS_IsMainThread()) {
|
||||
const mozilla::fallible_t fallible = mozilla::fallible_t();
|
||||
nsAutoArrayPtr<uint8_t> data(new (fallible) uint8_t[aLength]);
|
||||
if (!data) {
|
||||
return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
|
||||
aLength, &totalRead);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
|
||||
|
||||
nsCOMPtr<nsIRunnable> dataAvailable = new nsHtml5DataAvailable(this,
|
||||
data.forget(),
|
||||
totalRead);
|
||||
if (NS_FAILED(mThread->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatching DataAvailable event failed.");
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
NS_ASSERTION(IsParserThread(), "Wrong thread!");
|
||||
mozilla::MutexAutoLock autoLock(mTokenizerMutex);
|
||||
|
||||
// Read directly from response buffer.
|
||||
rv = aInStream->ReadSegments(CopySegmentsToParser, this, aLength,
|
||||
&totalRead);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed reading response data to parser");
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
|
||||
aLength, &totalRead);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
|
||||
nsCOMPtr<nsIRunnable> dataAvailable = new nsHtml5DataAvailable(this,
|
||||
data.forget(),
|
||||
totalRead);
|
||||
if (NS_FAILED(mThread->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Dispatching DataAvailable event failed.");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
NS_METHOD
|
||||
nsHtml5StreamParser::CopySegmentsToParser(nsIInputStream *aInStream,
|
||||
void *aClosure,
|
||||
const char *aFromSegment,
|
||||
uint32_t aToOffset,
|
||||
uint32_t aCount,
|
||||
uint32_t *aWriteCount)
|
||||
{
|
||||
nsHtml5StreamParser* parser = static_cast<nsHtml5StreamParser*>(aClosure);
|
||||
|
||||
parser->DoDataAvailable((const uint8_t*)aFromSegment, aCount);
|
||||
// Assume DoDataAvailable consumed all available bytes.
|
||||
*aWriteCount = aCount;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsHtml5StreamParser::PreferredForInternalEncodingDecl(nsACString& aEncoding)
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "nsHtml5Speculation.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsICharsetDetector.h"
|
||||
#include "nsIThreadRetargetableStreamListener.h"
|
||||
|
||||
class nsHtml5Parser;
|
||||
|
||||
|
@ -101,6 +102,7 @@ enum eHtml5StreamState {
|
|||
};
|
||||
|
||||
class nsHtml5StreamParser : public nsIStreamListener,
|
||||
public nsIThreadRetargetableStreamListener,
|
||||
public nsICharsetDetectionObserver {
|
||||
|
||||
friend class nsHtml5RequestStopper;
|
||||
|
@ -125,6 +127,8 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
NS_DECL_NSIREQUESTOBSERVER
|
||||
// nsIStreamListener methods:
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
// nsIThreadRetargetableStreamListener methods:
|
||||
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
|
||||
|
||||
// nsICharsetDetectionObserver
|
||||
/**
|
||||
|
@ -239,7 +243,14 @@ class nsHtml5StreamParser : public nsIStreamListener,
|
|||
|
||||
void DoStopRequest();
|
||||
|
||||
void DoDataAvailable(uint8_t* aBuffer, uint32_t aLength);
|
||||
void DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength);
|
||||
|
||||
static NS_METHOD CopySegmentsToParser(nsIInputStream *aInStream,
|
||||
void *aClosure,
|
||||
const char *aFromSegment,
|
||||
uint32_t aToOffset,
|
||||
uint32_t aCount,
|
||||
uint32_t *aWriteCount);
|
||||
|
||||
bool IsTerminatedOrInterrupted() {
|
||||
mozilla::MutexAutoLock autoLock(mTerminatedMutex);
|
||||
|
|
|
@ -228,7 +228,6 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
|
|||
* value if broken.
|
||||
*/
|
||||
inline nsresult IsBroken() {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
return mBroken;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "nsURILoader.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsIURIContentListener.h"
|
||||
#include "nsIContentHandler.h"
|
||||
#include "nsILoadGroup.h"
|
||||
|
@ -30,10 +31,12 @@
|
|||
#include "nsIDocShell.h"
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
#include "nsIDocShellTreeOwner.h"
|
||||
#include "nsIThreadRetargetableStreamListener.h"
|
||||
|
||||
#include "nsXPIDLString.h"
|
||||
#include "nsString.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsError.h"
|
||||
|
||||
|
@ -63,6 +66,7 @@ PRLogModuleInfo* nsURILoader::mLog = nullptr;
|
|||
* (or aborted).
|
||||
*/
|
||||
class nsDocumentOpenInfo MOZ_FINAL : public nsIStreamListener
|
||||
, public nsIThreadRetargetableStreamListener
|
||||
{
|
||||
public:
|
||||
// Needed for nsCOMPtr to work right... Don't call this!
|
||||
|
@ -110,6 +114,8 @@ public:
|
|||
// nsIStreamListener methods:
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
// nsIThreadRetargetableStreamListener
|
||||
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
|
||||
protected:
|
||||
~nsDocumentOpenInfo();
|
||||
|
||||
|
@ -124,7 +130,7 @@ protected:
|
|||
* The stream listener to forward nsIStreamListener notifications
|
||||
* to. This is set once the load is dispatched.
|
||||
*/
|
||||
nsCOMPtr<nsIStreamListener> m_targetStreamListener;
|
||||
nsMainThreadPtrHandle<nsIStreamListener> m_targetStreamListener;
|
||||
|
||||
/**
|
||||
* A pointer to the entity that originated the load. We depend on getting
|
||||
|
@ -159,6 +165,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
|
|||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
|
||||
NS_INTERFACE_MAP_END_THREADSAFE
|
||||
|
||||
nsDocumentOpenInfo::nsDocumentOpenInfo()
|
||||
|
@ -266,6 +273,22 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest *request, nsISupport
|
|||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocumentOpenInfo::CheckListenerChain()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
|
||||
do_QueryInterface(m_targetStreamListener, &rv);
|
||||
if (retargetableListener) {
|
||||
rv = retargetableListener->CheckListenerChain();
|
||||
}
|
||||
LOG(("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv %x",
|
||||
this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
|
||||
(nsIStreamListener*)m_targetStreamListener, rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocumentOpenInfo::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
|
||||
nsIInputStream * inStr,
|
||||
|
@ -288,7 +311,7 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest *request, nsISupports
|
|||
|
||||
if ( m_targetStreamListener)
|
||||
{
|
||||
nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
|
||||
nsMainThreadPtrHandle<nsIStreamListener> listener = m_targetStreamListener;
|
||||
|
||||
// If this is a multipart stream, we could get another
|
||||
// OnStartRequest after this... reset state.
|
||||
|
@ -514,11 +537,15 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports *
|
|||
aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
rv = helperAppService->DoContent(mContentType,
|
||||
request,
|
||||
m_originalContext,
|
||||
false,
|
||||
getter_AddRefs(m_targetStreamListener));
|
||||
getter_AddRefs(listener));
|
||||
// Passing false here to allow off main thread use.
|
||||
m_targetStreamListener
|
||||
= new nsMainThreadPtrHolder<nsIStreamListener>(listener, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
request->SetLoadFlags(loadFlags);
|
||||
m_targetStreamListener = nullptr;
|
||||
|
@ -558,7 +585,7 @@ nsDocumentOpenInfo::ConvertData(nsIRequest *request,
|
|||
// stream is split up into multiple destination streams. This
|
||||
// intermediate instance is used to target these "decoded" streams...
|
||||
//
|
||||
nsCOMPtr<nsDocumentOpenInfo> nextLink =
|
||||
nsRefPtr<nsDocumentOpenInfo> nextLink =
|
||||
new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
|
||||
if (!nextLink) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
|
@ -581,11 +608,16 @@ nsDocumentOpenInfo::ConvertData(nsIRequest *request,
|
|||
// stream converter and sets the output end of the stream converter to
|
||||
// nextLink. As we pump data into m_targetStreamListener the stream
|
||||
// converter will convert it and pass the converted data to nextLink.
|
||||
return StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(),
|
||||
PromiseFlatCString(aOutContentType).get(),
|
||||
nextLink,
|
||||
request,
|
||||
getter_AddRefs(m_targetStreamListener));
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
rv = StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(),
|
||||
PromiseFlatCString(aOutContentType).get(),
|
||||
nextLink,
|
||||
request,
|
||||
getter_AddRefs(listener));
|
||||
// Passing false here to allow off main thread use.
|
||||
m_targetStreamListener
|
||||
= new nsMainThreadPtrHolder<nsIStreamListener>(listener, false);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -630,7 +662,7 @@ nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
|
|||
// m_targetStreamListener is now the input end of the converter, and we can
|
||||
// just pump the data in there, if it exists. If it does not, we need to
|
||||
// try other nsIURIContentListeners.
|
||||
return m_targetStreamListener != nullptr;
|
||||
return m_targetStreamListener.get() != nullptr;
|
||||
}
|
||||
|
||||
// At this point, aListener wants data of type mContentType. Let 'em have
|
||||
|
@ -652,12 +684,15 @@ nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
|
|||
|
||||
bool abort = false;
|
||||
bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
nsresult rv = aListener->DoContent(mContentType.get(),
|
||||
isPreferred,
|
||||
aChannel,
|
||||
getter_AddRefs(m_targetStreamListener),
|
||||
getter_AddRefs(listener),
|
||||
&abort);
|
||||
|
||||
// Passing false here to allow off main thread use.
|
||||
m_targetStreamListener
|
||||
= new nsMainThreadPtrHolder<nsIStreamListener>(listener, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG_ERROR((" DoContent failed"));
|
||||
|
||||
|
@ -812,7 +847,7 @@ nsresult nsURILoader::OpenChannel(nsIChannel* channel,
|
|||
|
||||
// we need to create a DocumentOpenInfo object which will go ahead and open
|
||||
// the url and discover the content type....
|
||||
nsCOMPtr<nsDocumentOpenInfo> loader =
|
||||
nsRefPtr<nsDocumentOpenInfo> loader =
|
||||
new nsDocumentOpenInfo(aWindowContext, aFlags, this);
|
||||
|
||||
if (!loader) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
|
|
@ -203,6 +203,8 @@ class nsMainThreadPtrHandle
|
|||
operator T*() { return get(); }
|
||||
T* operator->() { return get(); }
|
||||
|
||||
operator bool() { return get(); }
|
||||
|
||||
// These are safe to call on other threads with appropriate external locking.
|
||||
bool operator==(const nsMainThreadPtrHandle<T>& aOther) const {
|
||||
if (!mPtr || !aOther.mPtr)
|
||||
|
|
Загрузка…
Ссылка в новой задаче