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

MozReview-Commit-ID: 1Z89LSr46dN
This commit is contained in:
Henri Sivonen
2017-06-27 10:38:30 -07:00
parent 15be754192
commit c3862d6f4b
8 changed files with 384 additions and 518 deletions

View File

@@ -15,7 +15,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"
@@ -29,6 +29,7 @@
#include "nsPrintfCString.h"
#include "nsNetUtil.h"
#include "nsXULAppAPI.h"
#include "mozilla/SchedulerGroup.h"
using namespace mozilla;
@@ -52,12 +53,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.
*
@@ -955,8 +956,14 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
// previous normal load in the history.
mReparseForbidden = !(mMode == NORMAL || mMode == PLAIN_TEXT);
mDocGroup = mExecutor->GetDocument()->GetDocGroup();
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mRequest, &rv));
if (NS_SUCCEEDED(rv)) {
// Non-HTTP channels are bogus enough that we let them work with unlabeled
// runnables for now. Asserting for HTTP channels only.
MOZ_ASSERT(mDocGroup || mMode == LOAD_AS_DATA, "How come the doc group is still null?");
nsAutoCString method;
Unused << httpChannel->GetRequestMethod(method);
// XXX does Necko have a way to renavigate POST, etc. without hitting
@@ -1059,7 +1066,8 @@ nsHtml5StreamParser::DoStopRequest()
class nsHtml5RequestStopper : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
nsHtml5StreamParserPtr mStreamParser;
public:
explicit nsHtml5RequestStopper(nsHtml5StreamParser* aStreamParser)
: Runnable("nsHtml5RequestStopper")
@@ -1145,9 +1153,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,
@@ -1342,9 +1351,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");
}
}
@@ -1375,11 +1382,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:
@@ -1476,7 +1487,8 @@ nsHtml5StreamParser::ParseAvailableData()
class nsHtml5StreamParserContinuation : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
nsHtml5StreamParserPtr mStreamParser;
public:
explicit nsHtml5StreamParserContinuation(nsHtml5StreamParser* aStreamParser)
: Runnable("nsHtml5StreamParserContinuation")
@@ -1640,7 +1652,8 @@ nsHtml5StreamParser::ContinueAfterFailedCharsetSwitch()
class nsHtml5TimerKungFu : public Runnable
{
private:
nsHtml5RefPtr<nsHtml5StreamParser> mStreamParser;
nsHtml5StreamParserPtr mStreamParser;
public:
explicit nsHtml5TimerKungFu(nsHtml5StreamParser* aStreamParser)
: Runnable("nsHtml5TimerKungFu")
@@ -1673,10 +1686,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);
@@ -1717,15 +1730,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");
}
}
@@ -1742,7 +1759,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);
if (mDocGroup) {
return mDocGroup->Dispatch(aName, TaskCategory::Network, runnable.forget());
}
return SchedulerGroup::UnlabeledDispatch(
aName, TaskCategory::Network, runnable.forget());
}