Bug 1333333 - Label runnables in the HTML parser. r=billm

MozReview-Commit-ID: 6ItIgFSSR2s

--HG--
extra : rebase_source : b7451779a0e2ab8fd427a7a9ab1fb4cb8c0801e4
This commit is contained in:
Henri Sivonen 2017-05-15 15:54:54 +03:00
parent e79de6b6c0
commit 118fc52e41
8 changed files with 377 additions and 523 deletions

View File

@ -35,11 +35,11 @@ EXPORTS += [
'nsHtml5Parser.h',
'nsHtml5PlainTextUtils.h',
'nsHtml5Portability.h',
'nsHtml5RefPtr.h',
'nsHtml5Speculation.h',
'nsHtml5SpeculativeLoad.h',
'nsHtml5StreamListener.h',
'nsHtml5StreamParser.h',
'nsHtml5StreamParserPtr.h',
'nsHtml5String.h',
'nsHtml5StringParser.h',
'nsHtml5SVGLoadDispatcher.h',

View File

@ -1,450 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsHtml5RefPtr_h
#define nsHtml5RefPtr_h
#include "nsThreadUtils.h"
template <class T>
class nsHtml5RefPtrReleaser : public mozilla::Runnable
{
private:
T* mPtr;
public:
explicit nsHtml5RefPtrReleaser(T* aPtr)
: mozilla::Runnable("nsHtml5RefPtrReleaser")
, mPtr(aPtr)
{}
NS_IMETHOD Run() override
{
mPtr->Release();
return NS_OK;
}
};
// template <class T> class nsHtml5RefPtrGetterAddRefs;
/**
* Like nsRefPtr except release is proxied to the main thread. Mostly copied
* from nsRefPtr.
*/
template <class T>
class nsHtml5RefPtr
{
private:
void
assign_with_AddRef( T* rawPtr )
{
if ( rawPtr )
rawPtr->AddRef();
assign_assuming_AddRef(rawPtr);
}
void**
begin_assignment()
{
assign_assuming_AddRef(0);
return reinterpret_cast<void**>(&mRawPtr);
}
void
assign_assuming_AddRef( T* newPtr )
{
T* oldPtr = mRawPtr;
mRawPtr = newPtr;
if ( oldPtr )
release(oldPtr);
}
void
release( T* aPtr )
{
nsCOMPtr<nsIRunnable> releaser = new nsHtml5RefPtrReleaser<T>(aPtr);
if (NS_FAILED(NS_DispatchToMainThread(releaser)))
{
NS_WARNING("Failed to dispatch releaser event.");
}
}
private:
T* mRawPtr;
public:
typedef T element_type;
~nsHtml5RefPtr()
{
if ( mRawPtr )
release(mRawPtr);
}
// Constructors
nsHtml5RefPtr()
: mRawPtr(0)
// default constructor
{
}
nsHtml5RefPtr( const nsHtml5RefPtr<T>& aSmartPtr )
: mRawPtr(aSmartPtr.mRawPtr)
// copy-constructor
{
if ( mRawPtr )
mRawPtr->AddRef();
}
explicit nsHtml5RefPtr( T* aRawPtr )
: mRawPtr(aRawPtr)
// construct from a raw pointer (of the right type)
{
if ( mRawPtr )
mRawPtr->AddRef();
}
explicit nsHtml5RefPtr( const already_AddRefed<T>& aSmartPtr )
: mRawPtr(aSmartPtr.mRawPtr)
// construct from |dont_AddRef(expr)|
{
}
// Assignment operators
nsHtml5RefPtr<T>&
operator=( const nsHtml5RefPtr<T>& rhs )
// copy assignment operator
{
assign_with_AddRef(rhs.mRawPtr);
return *this;
}
nsHtml5RefPtr<T>&
operator=( T* rhs )
// assign from a raw pointer (of the right type)
{
assign_with_AddRef(rhs);
return *this;
}
nsHtml5RefPtr<T>&
operator=( const already_AddRefed<T>& rhs )
// assign from |dont_AddRef(expr)|
{
assign_assuming_AddRef(rhs.mRawPtr);
return *this;
}
// Other pointer operators
void
swap( nsHtml5RefPtr<T>& rhs )
// ...exchange ownership with |rhs|; can save a pair of refcount operations
{
T* temp = rhs.mRawPtr;
rhs.mRawPtr = mRawPtr;
mRawPtr = temp;
}
void
swap( T*& rhs )
// ...exchange ownership with |rhs|; can save a pair of refcount operations
{
T* temp = rhs;
rhs = mRawPtr;
mRawPtr = temp;
}
already_AddRefed<T>
forget()
// return the value of mRawPtr and null out mRawPtr. Useful for
// already_AddRefed return values.
{
T* temp = 0;
swap(temp);
return temp;
}
template <typename I>
void
forget( I** rhs)
// Set the target of rhs to the value of mRawPtr and null out mRawPtr.
// Useful to avoid unnecessary AddRef/Release pairs with "out"
// parameters where rhs bay be a T** or an I** where I is a base class
// of T.
{
NS_ASSERTION(rhs, "Null pointer passed to forget!");
*rhs = mRawPtr;
mRawPtr = 0;
}
T*
get() const
/*
Prefer the implicit conversion provided automatically by |operator T*() const|.
Use |get()| to resolve ambiguity or to get a castable pointer.
*/
{
return const_cast<T*>(mRawPtr);
}
operator T*() const
/*
...makes an |nsHtml5RefPtr| act like its underlying raw pointer type whenever it
is used in a context where a raw pointer is expected. It is this operator
that makes an |nsHtml5RefPtr| substitutable for a raw pointer.
Prefer the implicit use of this operator to calling |get()|, except where
necessary to resolve ambiguity.
*/
{
return get();
}
T*
operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsHtml5RefPtr with operator->().");
return get();
}
nsHtml5RefPtr<T>*
get_address()
// This is not intended to be used by clients. See |address_of|
// below.
{
return this;
}
const nsHtml5RefPtr<T>*
get_address() const
// This is not intended to be used by clients. See |address_of|
// below.
{
return this;
}
public:
T&
operator*() const
{
NS_PRECONDITION(mRawPtr != 0, "You can't dereference a NULL nsHtml5RefPtr with operator*().");
return *get();
}
T**
StartAssignment()
{
#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
return reinterpret_cast<T**>(begin_assignment());
#else
assign_assuming_AddRef(0);
return reinterpret_cast<T**>(&mRawPtr);
#endif
}
};
template <class T>
inline
nsHtml5RefPtr<T>*
address_of( nsHtml5RefPtr<T>& aPtr )
{
return aPtr.get_address();
}
template <class T>
inline
const nsHtml5RefPtr<T>*
address_of( const nsHtml5RefPtr<T>& aPtr )
{
return aPtr.get_address();
}
template <class T>
class nsHtml5RefPtrGetterAddRefs
/*
...
This class is designed to be used for anonymous temporary objects in the
argument list of calls that return COM interface pointers, e.g.,
nsHtml5RefPtr<IFoo> fooP;
...->GetAddRefedPointer(getter_AddRefs(fooP))
DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead.
When initialized with a |nsHtml5RefPtr|, as in the example above, it returns
a |void**|, a |T**|, or an |nsISupports**| as needed, that the
outer call (|GetAddRefedPointer| in this case) can fill in.
This type should be a nested class inside |nsHtml5RefPtr<T>|.
*/
{
public:
explicit
nsHtml5RefPtrGetterAddRefs( nsHtml5RefPtr<T>& aSmartPtr )
: mTargetSmartPtr(aSmartPtr)
{
// nothing else to do
}
operator void**()
{
return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
}
operator T**()
{
return mTargetSmartPtr.StartAssignment();
}
T*&
operator*()
{
return *(mTargetSmartPtr.StartAssignment());
}
private:
nsHtml5RefPtr<T>& mTargetSmartPtr;
};
template <class T>
inline
nsHtml5RefPtrGetterAddRefs<T>
getter_AddRefs( nsHtml5RefPtr<T>& aSmartPtr )
/*
Used around a |nsHtml5RefPtr| when
...makes the class |nsHtml5RefPtrGetterAddRefs<T>| invisible.
*/
{
return nsHtml5RefPtrGetterAddRefs<T>(aSmartPtr);
}
// Comparing two |nsHtml5RefPtr|s
template <class T, class U>
inline
bool
operator==( const nsHtml5RefPtr<T>& lhs, const nsHtml5RefPtr<U>& rhs )
{
return static_cast<const T*>(lhs.get()) == static_cast<const U*>(rhs.get());
}
template <class T, class U>
inline
bool
operator!=( const nsHtml5RefPtr<T>& lhs, const nsHtml5RefPtr<U>& rhs )
{
return static_cast<const T*>(lhs.get()) != static_cast<const U*>(rhs.get());
}
// Comparing an |nsHtml5RefPtr| to a raw pointer
template <class T, class U>
inline
bool
operator==( const nsHtml5RefPtr<T>& lhs, const U* rhs )
{
return static_cast<const T*>(lhs.get()) == static_cast<const U*>(rhs);
}
template <class T, class U>
inline
bool
operator==( const U* lhs, const nsHtml5RefPtr<T>& rhs )
{
return static_cast<const U*>(lhs) == static_cast<const T*>(rhs.get());
}
template <class T, class U>
inline
bool
operator!=( const nsHtml5RefPtr<T>& lhs, const U* rhs )
{
return static_cast<const T*>(lhs.get()) != static_cast<const U*>(rhs);
}
template <class T, class U>
inline
bool
operator!=( const U* lhs, const nsHtml5RefPtr<T>& rhs )
{
return static_cast<const U*>(lhs) != static_cast<const T*>(rhs.get());
}
template <class T, class U>
inline
bool
operator==( const nsHtml5RefPtr<T>& lhs, U* rhs )
{
return static_cast<const T*>(lhs.get()) == const_cast<const U*>(rhs);
}
template <class T, class U>
inline
bool
operator==( U* lhs, const nsHtml5RefPtr<T>& rhs )
{
return const_cast<const U*>(lhs) == static_cast<const T*>(rhs.get());
}
template <class T, class U>
inline
bool
operator!=( const nsHtml5RefPtr<T>& lhs, U* rhs )
{
return static_cast<const T*>(lhs.get()) != const_cast<const U*>(rhs);
}
template <class T, class U>
inline
bool
operator!=( U* lhs, const nsHtml5RefPtr<T>& rhs )
{
return const_cast<const U*>(lhs) != static_cast<const T*>(rhs.get());
}
// Comparing an |nsHtml5RefPtr| to |0|
template <class T>
inline
bool
operator==( const nsHtml5RefPtr<T>& lhs, decltype(nullptr) )
{
return lhs.get() == nullptr;
}
template <class T>
inline
bool
operator==( decltype(nullptr), const nsHtml5RefPtr<T>& rhs )
{
return nullptr == rhs.get();
}
template <class T>
inline
bool
operator!=( const nsHtml5RefPtr<T>& lhs, decltype(nullptr) )
{
return lhs.get() != nullptr;
}
template <class T>
inline
bool
operator!=( decltype(nullptr), const nsHtml5RefPtr<T>& rhs )
{
return nullptr != rhs.get();
}
#endif // !defined(nsHtml5RefPtr_h)

View File

@ -7,7 +7,7 @@
#include "nsIStreamListener.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsHtml5RefPtr.h"
#include "nsHtml5StreamParserPtr.h"
#include "nsHtml5StreamParser.h"
/**
@ -18,14 +18,14 @@
* so nsHtml5StreamParser being an nsIThreadRetargetableStreamListener was
* a memory corruption problem.
*
* mDelegate is an nsHtml5RefPtr, which releases the object that it points
* mDelegate is an nsHtml5StreamParserPtr, which releases the object that it points
* to from a runnable on the main thread. DropDelegate() is only called on
* the main thread. This call will finish before the main-thread derefs the
* nsHtml5StreamListener itself, so there is no risk of another thread making
* the refcount of nsHtml5StreamListener go to zero and running the destructor
* concurrently. Other than that, the thread-safe nsISupports implementation
* takes care of the destructor not running concurrently from different
* threads, so there is no need to have a mutex around nsHtml5RefPtr to
* threads, so there is no need to have a mutex around nsHtml5StreamParserPtr to
* prevent it from double-releasing nsHtml5StreamParser.
*/
class nsHtml5StreamListener : public nsIStreamListener,
@ -49,7 +49,7 @@ public:
private:
virtual ~nsHtml5StreamListener();
nsHtml5RefPtr<nsHtml5StreamParser> mDelegate;
nsHtml5StreamParserPtr mDelegate;
};
#endif // nsHtml5StreamListener_h

View File

@ -14,7 +14,7 @@
#include "nsHtml5TreeBuilder.h"
#include "nsHtml5AtomTable.h"
#include "nsHtml5Module.h"
#include "nsHtml5RefPtr.h"
#include "nsHtml5StreamParserPtr.h"
#include "nsIScriptError.h"
#include "mozilla/Preferences.h"
#include "mozilla/UniquePtrExtensions.h"
@ -54,12 +54,12 @@ nsHtml5StreamParser::InitializeStatics()
*
* To work around this limitation, runnables posted by the main thread to the
* parser thread hold their reference to the stream parser in an
* nsHtml5RefPtr. Upon creation, nsHtml5RefPtr addrefs the object it holds
* nsHtml5StreamParserPtr. Upon creation, nsHtml5StreamParserPtr addrefs the object it holds
* just like a regular nsRefPtr. This is OK, since the creation of the
* runnable and the nsHtml5RefPtr happens on the main thread.
* runnable and the nsHtml5StreamParserPtr happens on the main thread.
*
* When the runnable is done on the parser thread, the destructor of
* nsHtml5RefPtr runs there. It doesn't call Release on the held object
* nsHtml5StreamParserPtr runs there. It doesn't call Release on the held object
* directly. Instead, it posts another runnable back to the main thread where
* that runnable calls Release on the wrapped object.
*
@ -1065,7 +1065,8 @@ nsHtml5StreamParser::DoStopRequest()
class nsHtml5RequestStopper : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
nsHtml5StreamParserPtr mStreamParser;
public:
explicit nsHtml5RequestStopper(nsHtml5StreamParser* aStreamParser)
: Runnable("nsHtml5RequestStopper")
@ -1151,9 +1152,10 @@ nsHtml5StreamParser::DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength)
class nsHtml5DataAvailable : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
UniquePtr<uint8_t[]> mData;
uint32_t mLength;
nsHtml5StreamParserPtr mStreamParser;
UniquePtr<uint8_t[]> mData;
uint32_t mLength;
public:
nsHtml5DataAvailable(nsHtml5StreamParser* aStreamParser,
UniquePtr<uint8_t[]> aData,
@ -1348,9 +1350,7 @@ nsHtml5StreamParser::FlushTreeOpsAndDisarmTimer()
}
mTreeBuilder->Flush();
nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
if (NS_FAILED(mExecutor->GetDocument()->Dispatch("nsHtml5ExecutorFlusher",
TaskCategory::Other,
runnable.forget()))) {
if (NS_FAILED(DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
@ -1381,11 +1381,15 @@ nsHtml5StreamParser::ParseAvailableData()
mFirstBuffer->setEnd(0);
}
mTreeBuilder->FlushLoads();
// Dispatch this runnable unconditionally, because the loads
// that need flushing may have been flushed earlier even if the
// flush right above here did nothing.
if (NS_FAILED(NS_DispatchToMainThread(mLoadFlusher))) {
NS_WARNING("failed to dispatch load flush event");
{
// Dispatch this runnable unconditionally, because the loads
// that need flushing may have been flushed earlier even if the
// flush right above here did nothing.
nsCOMPtr<nsIRunnable> runnable(mLoadFlusher);
if (NS_FAILED(
DispatchToMain("nsHtml5LoadFlusher", runnable.forget()))) {
NS_WARNING("failed to dispatch load flush event");
}
}
return; // no more data for now but expecting more
case STREAM_ENDED:
@ -1482,7 +1486,8 @@ nsHtml5StreamParser::ParseAvailableData()
class nsHtml5StreamParserContinuation : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
nsHtml5StreamParserPtr mStreamParser;
public:
explicit nsHtml5StreamParserContinuation(nsHtml5StreamParser* aStreamParser)
: Runnable("nsHtml5StreamParserContinuation")
@ -1649,7 +1654,8 @@ nsHtml5StreamParser::ContinueAfterFailedCharsetSwitch()
class nsHtml5TimerKungFu : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
nsHtml5StreamParserPtr mStreamParser;
public:
explicit nsHtml5TimerKungFu(nsHtml5StreamParser* aStreamParser)
: Runnable("nsHtml5TimerKungFu")
@ -1682,10 +1688,10 @@ nsHtml5StreamParser::DropTimer()
*
* This DropTimer method addresses these issues. This method must be called
* on the main thread before the destructor of this class is reached.
* The nsHtml5TimerKungFu object has an nsHtml5RefPtr that addrefs this
* The nsHtml5TimerKungFu object has an nsHtml5StreamParserPtr that addrefs this
* stream parser object to keep it alive until the runnable is done.
* The runnable cancels the timer on the parser thread, drops the timer
* and lets nsHtml5RefPtr send a runnable back to the main thread to
* and lets nsHtml5StreamParserPtr send a runnable back to the main thread to
* release the stream parser.
*/
mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
@ -1726,15 +1732,19 @@ nsHtml5StreamParser::TimerFlush()
if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
mTreeBuilder->Flush(); // delete useless ops
if (mTokenizer->FlushViewSource()) {
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
if (NS_FAILED(
DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
} else {
// we aren't speculating and we don't know when new data is
// going to arrive. Send data to the main thread.
if (mTreeBuilder->Flush(true)) {
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
if (NS_FAILED(
DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
@ -1751,7 +1761,20 @@ nsHtml5StreamParser::MarkAsBroken(nsresult aRv)
mTreeBuilder->MarkAsBroken(aRv);
mozilla::DebugOnly<bool> hadOps = mTreeBuilder->Flush(false);
NS_ASSERTION(hadOps, "Should have had the markAsBroken op!");
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher);
if (NS_FAILED(DispatchToMain("nsHtml5ExecutorFlusher", runnable.forget()))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
nsresult
nsHtml5StreamParser::DispatchToMain(const char* aName,
already_AddRefed<nsIRunnable>&& aRunnable)
{
nsCOMPtr<nsIRunnable> runnable(aRunnable);
// mExecutor must outlive this stream parser. Furthermore, dereferencing
// the pointer doesn't change refcounts, so doing this from the parser
// thread is OK.
return mExecutor->GetDocument()->Dispatch(
aName, TaskCategory::Network, runnable.forget());
}

View File

@ -106,61 +106,63 @@ class nsHtml5StreamParser : public nsICharsetDetectionObserver {
friend class nsHtml5DataAvailable;
friend class nsHtml5StreamParserContinuation;
friend class nsHtml5TimerKungFu;
friend class nsHtml5StreamParserPtr;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHtml5StreamParser,
nsICharsetDetectionObserver)
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHtml5StreamParser,
nsICharsetDetectionObserver)
static void InitializeStatics();
static void InitializeStatics();
nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
nsHtml5Parser* aOwner,
eParserMode aMode);
nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
nsHtml5Parser* aOwner,
eParserMode aMode);
// Methods that nsHtml5StreamListener calls
nsresult CheckListenerChain();
// Methods that nsHtml5StreamListener calls
nsresult CheckListenerChain();
nsresult OnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
nsresult OnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
nsresult OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aInStream,
uint64_t aSourceOffset,
uint32_t aLength);
nsresult OnStopRequest(nsIRequest* aRequest,
nsresult OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsresult status);
nsIInputStream* aInStream,
uint64_t aSourceOffset,
uint32_t aLength);
// nsICharsetDetectionObserver
/**
nsresult OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult status);
// nsICharsetDetectionObserver
/**
* Chardet calls this to report the detection result
*/
NS_IMETHOD Notify(const char* aCharset, nsDetectionConfident aConf) override;
NS_IMETHOD Notify(const char* aCharset, nsDetectionConfident aConf) override;
// EncodingDeclarationHandler
// https://hg.mozilla.org/projects/htmlparser/file/tip/src/nu/validator/htmlparser/common/EncodingDeclarationHandler.java
/**
// EncodingDeclarationHandler
// https://hg.mozilla.org/projects/htmlparser/file/tip/src/nu/validator/htmlparser/common/EncodingDeclarationHandler.java
/**
* Tree builder uses this to report a late <meta charset>
*/
bool internalEncodingDeclaration(nsHtml5String aEncoding);
bool internalEncodingDeclaration(nsHtml5String aEncoding);
// Not from an external interface
// Not from an external interface
/**
/**
* Call this method once you've created a parser, and want to instruct it
* about what charset to load
*
* @param aCharset the charset of a document
* @param aCharsetSource the source of the charset
*/
inline void SetDocumentCharset(const nsACString& aCharset, int32_t aSource) {
NS_PRECONDITION(mStreamState == STREAM_NOT_STARTED,
"SetDocumentCharset called too late.");
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mCharset = aCharset;
mCharsetSource = aSource;
inline void SetDocumentCharset(const nsACString& aCharset, int32_t aSource)
{
NS_PRECONDITION(mStreamState == STREAM_NOT_STARTED,
"SetDocumentCharset called too late.");
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mCharset = aCharset;
mCharsetSource = aSource;
}
inline void SetObserver(nsIRequestObserver* aObserver) {
@ -382,6 +384,13 @@ class nsHtml5StreamParser : public nsICharsetDetectionObserver {
return mSpeculationFailureCount < 100;
}
/**
* Dispatch an event to a Quantum DOM main thread-ish thread.
* (Not the parser thread.)
*/
nsresult DispatchToMain(const char* aName,
already_AddRefed<nsIRunnable>&& aRunnable);
nsCOMPtr<nsIRequest> mRequest;
nsCOMPtr<nsIRequestObserver> mObserver;

View File

@ -0,0 +1,268 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsHtml5StreamParserPtr_h
#define nsHtml5StreamParserPtr_h
#include "nsThreadUtils.h"
#include "mozilla/dom/DocGroup.h"
class nsHtml5StreamParserReleaser : public mozilla::Runnable
{
private:
nsHtml5StreamParser* mPtr;
public:
explicit nsHtml5StreamParserReleaser(nsHtml5StreamParser* aPtr)
: mPtr(aPtr)
{
}
NS_IMETHOD Run() override
{
mPtr->Release();
return NS_OK;
}
};
/**
* Like nsRefPtr except release is proxied to the main
* thread. Mostly copied from nsRefPtr.
*/
class nsHtml5StreamParserPtr
{
private:
void assign_with_AddRef(nsHtml5StreamParser* rawPtr)
{
if (rawPtr)
rawPtr->AddRef();
assign_assuming_AddRef(rawPtr);
}
void** begin_assignment()
{
assign_assuming_AddRef(0);
return reinterpret_cast<void**>(&mRawPtr);
}
void assign_assuming_AddRef(nsHtml5StreamParser* newPtr)
{
nsHtml5StreamParser* oldPtr = mRawPtr;
mRawPtr = newPtr;
if (oldPtr)
release(oldPtr);
}
void release(nsHtml5StreamParser* aPtr)
{
nsCOMPtr<nsIRunnable> releaser = new nsHtml5StreamParserReleaser(aPtr);
if (NS_FAILED(aPtr->DispatchToMain("nsHtml5StreamParserReleaser",
releaser.forget()))) {
NS_WARNING("Failed to dispatch releaser event.");
}
}
private:
nsHtml5StreamParser* mRawPtr;
public:
~nsHtml5StreamParserPtr()
{
if (mRawPtr)
release(mRawPtr);
}
// Constructors
nsHtml5StreamParserPtr()
: mRawPtr(0)
// default constructor
{
}
nsHtml5StreamParserPtr(const nsHtml5StreamParserPtr& aSmartPtr)
: mRawPtr(aSmartPtr.mRawPtr)
// copy-constructor
{
if (mRawPtr)
mRawPtr->AddRef();
}
explicit nsHtml5StreamParserPtr(nsHtml5StreamParser* aRawPtr)
: mRawPtr(aRawPtr)
// construct from a raw pointer (of the right type)
{
if (mRawPtr)
mRawPtr->AddRef();
}
// Assignment operators
nsHtml5StreamParserPtr& operator=(const nsHtml5StreamParserPtr& rhs)
// copy assignment operator
{
assign_with_AddRef(rhs.mRawPtr);
return *this;
}
nsHtml5StreamParserPtr& operator=(nsHtml5StreamParser* rhs)
// assign from a raw pointer (of the right type)
{
assign_with_AddRef(rhs);
return *this;
}
// Other pointer operators
void swap(nsHtml5StreamParserPtr& rhs)
// ...exchange ownership with |rhs|; can save a pair of refcount operations
{
nsHtml5StreamParser* temp = rhs.mRawPtr;
rhs.mRawPtr = mRawPtr;
mRawPtr = temp;
}
void swap(nsHtml5StreamParser*& rhs)
// ...exchange ownership with |rhs|; can save a pair of refcount operations
{
nsHtml5StreamParser* temp = rhs;
rhs = mRawPtr;
mRawPtr = temp;
}
template<typename I>
void forget(I** rhs)
// Set the target of rhs to the value of mRawPtr and null out mRawPtr.
// Useful to avoid unnecessary AddRef/Release pairs with "out"
// parameters where rhs bay be a T** or an I** where I is a base class
// of T.
{
NS_ASSERTION(rhs, "Null pointer passed to forget!");
*rhs = mRawPtr;
mRawPtr = 0;
}
nsHtml5StreamParser* get() const
/*
Prefer the implicit conversion provided automatically by |operator nsHtml5StreamParser*() const|.
Use |get()| to resolve ambiguity or to get a castable pointer.
*/
{
return const_cast<nsHtml5StreamParser*>(mRawPtr);
}
operator nsHtml5StreamParser*() const
/*
...makes an |nsHtml5StreamParserPtr| act like its underlying raw pointer type whenever it
is used in a context where a raw pointer is expected. It is this operator
that makes an |nsHtml5StreamParserPtr| substitutable for a raw pointer.
Prefer the implicit use of this operator to calling |get()|, except where
necessary to resolve ambiguity.
*/
{
return get();
}
nsHtml5StreamParser* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
NS_PRECONDITION(
mRawPtr != 0,
"You can't dereference a NULL nsHtml5StreamParserPtr with operator->().");
return get();
}
nsHtml5StreamParserPtr* get_address()
// This is not intended to be used by clients. See |address_of|
// below.
{
return this;
}
const nsHtml5StreamParserPtr* get_address() const
// This is not intended to be used by clients. See |address_of|
// below.
{
return this;
}
public:
nsHtml5StreamParser& operator*() const
{
NS_PRECONDITION(
mRawPtr != 0,
"You can't dereference a NULL nsHtml5StreamParserPtr with operator*().");
return *get();
}
nsHtml5StreamParser** StartAssignment()
{
#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
return reinterpret_cast<nsHtml5StreamParser**>(begin_assignment());
#else
assign_assuming_AddRef(0);
return reinterpret_cast<nsHtml5StreamParser**>(&mRawPtr);
#endif
}
};
inline nsHtml5StreamParserPtr*
address_of(nsHtml5StreamParserPtr& aPtr)
{
return aPtr.get_address();
}
inline const nsHtml5StreamParserPtr*
address_of(const nsHtml5StreamParserPtr& aPtr)
{
return aPtr.get_address();
}
class nsHtml5StreamParserPtrGetterAddRefs
/*
...
This class is designed to be used for anonymous temporary objects in the
argument list of calls that return COM interface pointers, e.g.,
nsHtml5StreamParserPtr<IFoo> fooP;
...->GetAddRefedPointer(getter_AddRefs(fooP))
DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead.
When initialized with a |nsHtml5StreamParserPtr|, as in the example above, it returns
a |void**|, a |T**|, or an |nsISupports**| as needed, that the
outer call (|GetAddRefedPointer| in this case) can fill in.
This type should be a nested class inside |nsHtml5StreamParserPtr<T>|.
*/
{
public:
explicit nsHtml5StreamParserPtrGetterAddRefs(
nsHtml5StreamParserPtr& aSmartPtr)
: mTargetSmartPtr(aSmartPtr)
{
// nothing else to do
}
operator void**()
{
return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
}
operator nsHtml5StreamParser**() { return mTargetSmartPtr.StartAssignment(); }
nsHtml5StreamParser*& operator*()
{
return *(mTargetSmartPtr.StartAssignment());
}
private:
nsHtml5StreamParserPtr& mTargetSmartPtr;
};
inline nsHtml5StreamParserPtrGetterAddRefs
getter_AddRefs(nsHtml5StreamParserPtr& aSmartPtr)
/*
Used around a |nsHtml5StreamParserPtr| when
...makes the class |nsHtml5StreamParserPtrGetterAddRefs| invisible.
*/
{
return nsHtml5StreamParserPtrGetterAddRefs(aSmartPtr);
}
// Comparing an |nsHtml5StreamParserPtr| to |0|
inline bool
operator==(const nsHtml5StreamParserPtr& lhs, decltype(nullptr))
{
return lhs.get() == nullptr;
}
inline bool
operator==(decltype(nullptr), const nsHtml5StreamParserPtr& rhs)
{
return nullptr == rhs.get();
}
inline bool
operator!=(const nsHtml5StreamParserPtr& lhs, decltype(nullptr))
{
return lhs.get() != nullptr;
}
inline bool
operator!=(decltype(nullptr), const nsHtml5StreamParserPtr& rhs)
{
return nullptr != rhs.get();
}
#endif // !defined(nsHtml5StreamParserPtr_h)

View File

@ -238,9 +238,14 @@ nsHtml5TreeOpExecutor::MarkAsBroken(nsresult aReason)
// We are under memory pressure, but let's hope the following allocation
// works out so that we get to terminate and clean up the parser from
// a safer point.
if (mParser) { // can mParser ever be null here?
MOZ_ALWAYS_SUCCEEDS(
NS_DispatchToMainThread(NewRunnableMethod(GetParser(), &nsHtml5Parser::Terminate)));
if (mParser && mDocument) { // can mParser ever be null here?
nsCOMPtr<nsIRunnable> terminator =
NewRunnableMethod(GetParser(), &nsHtml5Parser::Terminate);
if (NS_FAILED(mDocument->Dispatch("nsHtml5Parser::Terminate",
TaskCategory::Network,
terminator.forget()))) {
NS_WARNING("failed to dispatch executor flush event");
}
}
return aReason;
}
@ -264,9 +269,9 @@ void
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync()
{
if (!mDocument || !mDocument->IsInBackgroundWindow()) {
nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this);
if (NS_FAILED(mDocument->Dispatch("nsHtml5ExecutorReflusher",
TaskCategory::Other,
TaskCategory::Network,
flusher.forget()))) {
NS_WARNING("failed to dispatch executor flush event");
}

View File

@ -615,9 +615,8 @@ void
nsHtml5TreeOperation::SvgLoad(nsIContent* aNode)
{
nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(aNode);
if (NS_FAILED(aNode->OwnerDoc()->Dispatch("nsHtml5SVGLoadDispatcher",
TaskCategory::Other,
event.forget()))) {
if (NS_FAILED(aNode->OwnerDoc()->Dispatch(
"nsHtml5SVGLoadDispatcher", TaskCategory::Network, event.forget()))) {
NS_WARNING("failed to dispatch svg load dispatcher");
}
}