Bug 1410848 - Use RAII with nsHtml5TreeOpExecutor::mFlushState. r=smaug.

MozReview-Commit-ID: 3a1tyYGT8u5
This commit is contained in:
Henri Sivonen
2017-10-27 10:06:33 +03:00
parent 545efaa3f5
commit b94d63f4e4
8 changed files with 205 additions and 137 deletions

View File

@@ -34,6 +34,7 @@
#include "nsIViewSourceChannel.h"
#include "xpcpublic.h"
#include "mozilla/IdleTaskRunner.h"
#include "nsHtml5AutoPauseUpdate.h"
using namespace mozilla;
@@ -57,6 +58,43 @@ class nsHtml5ExecutorReflusher : public Runnable
}
};
class MOZ_RAII nsHtml5AutoFlush final
{
private:
RefPtr<nsHtml5TreeOpExecutor> mExecutor;
size_t mOpsToRemove;
public:
explicit nsHtml5AutoFlush(nsHtml5TreeOpExecutor* aExecutor)
: mExecutor(aExecutor)
, mOpsToRemove(aExecutor->OpQueueLength())
{
mExecutor->BeginFlush();
mExecutor->BeginDocUpdate();
}
~nsHtml5AutoFlush()
{
if (mExecutor->IsInDocUpdate()) {
mExecutor->EndDocUpdate();
} else {
// We aren't in an update if nsHtml5AutoPauseUpdate
// caused something to terminate the parser.
MOZ_RELEASE_ASSERT(
mExecutor->IsComplete(),
"How do we have mParser but the doc update isn't open?");
}
mExecutor->EndFlush();
mExecutor->RemoveFromStartOfOpQueue(mOpsToRemove);
}
void SetNumberOfOpsToRemove(size_t aOpsToRemove)
{
MOZ_ASSERT(aOpsToRemove < mOpsToRemove,
"Requested partial clearing of op queue but the number to clear "
"wasn't less than the length of the queue.");
mOpsToRemove = aOpsToRemove;
}
};
static mozilla::LinkedList<nsHtml5TreeOpExecutor>* gBackgroundFlushList = nullptr;
StaticRefPtr<IdleTaskRunner> gBackgroundFlushRunner;
@@ -77,7 +115,7 @@ nsHtml5TreeOpExecutor::nsHtml5TreeOpExecutor()
nsHtml5TreeOpExecutor::~nsHtml5TreeOpExecutor()
{
if (gBackgroundFlushList && isInList()) {
mOpQueue.Clear();
ClearOpQueue();
removeFrom(*gBackgroundFlushList);
if (gBackgroundFlushList->isEmpty()) {
delete gBackgroundFlushList;
@@ -298,9 +336,9 @@ nsHtml5TreeOpExecutor::FlushSpeculativeLoads()
{
nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
mStage.MoveSpeculativeLoadsTo(speculativeLoadQueue);
const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
for (nsHtml5SpeculativeLoad* iter = const_cast<nsHtml5SpeculativeLoad*>(start);
nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
for (nsHtml5SpeculativeLoad* iter = start;
iter < end;
++iter) {
if (MOZ_UNLIKELY(!mParser)) {
@@ -368,7 +406,7 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
for (;;) {
if (!mParser) {
// Parse has terminated.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
ClearOpQueue(); // clear in order to be able to assert in destructor
return;
}
@@ -395,18 +433,20 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
if (mReadingFromStage) {
nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
"mOpQueue modified during flush.");
mStage.MoveOpsAndSpeculativeLoadsTo(mOpQueue, speculativeLoadQueue);
// Make sure speculative loads never start after the corresponding
// normal loads for the same URLs.
const nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
const nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
for (nsHtml5SpeculativeLoad* iter = (nsHtml5SpeculativeLoad*)start;
nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
for (nsHtml5SpeculativeLoad* iter = start;
iter < end;
++iter) {
iter->Perform(this);
if (MOZ_UNLIKELY(!mParser)) {
// An extension terminated the parser from a HTTP observer.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
ClearOpQueue(); // clear in order to be able to assert in destructor
return;
}
}
@@ -416,7 +456,7 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
// URLs.
if (MOZ_UNLIKELY(!mParser)) {
// An extension terminated the parser from a HTTP observer.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
ClearOpQueue(); // clear in order to be able to assert in destructor
return;
}
// Not sure if this grip is still needed, but previously, the code
@@ -439,57 +479,46 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
return;
}
mFlushState = eInFlush;
nsIContent* scriptElement = nullptr;
bool interrupted = false;
BeginDocUpdate();
uint32_t numberOfOpsToFlush = mOpQueue.Length();
{
// autoFlush clears mOpQueue in its destructor unless
// SetNumberOfOpsToRemove is called first, in which case only
// some ops from the start of the queue are cleared.
nsHtml5AutoFlush autoFlush(this);
const nsHtml5TreeOperation* first = mOpQueue.Elements();
const nsHtml5TreeOperation* last = first + numberOfOpsToFlush - 1;
for (nsHtml5TreeOperation* iter = const_cast<nsHtml5TreeOperation*>(first);;) {
if (MOZ_UNLIKELY(!mParser)) {
// The previous tree op caused a call to nsIParser::Terminate().
break;
}
NS_ASSERTION(mFlushState == eInDocUpdate,
"Tried to perform tree op outside update batch.");
nsresult rv = iter->Perform(this, &scriptElement, &interrupted);
if (NS_FAILED(rv)) {
MarkAsBroken(rv);
break;
nsHtml5TreeOperation* first = mOpQueue.Elements();
nsHtml5TreeOperation* last = first + mOpQueue.Length() - 1;
for (nsHtml5TreeOperation* iter = first;; ++iter) {
if (MOZ_UNLIKELY(!mParser)) {
// The previous tree op caused a call to nsIParser::Terminate().
return;
}
MOZ_ASSERT(IsInDocUpdate(),
"Tried to perform tree op outside update batch.");
nsresult rv = iter->Perform(this, &scriptElement, &interrupted);
if (NS_FAILED(rv)) {
MarkAsBroken(rv);
break;
}
// Be sure not to check the deadline if the last op was just performed.
if (MOZ_UNLIKELY(iter == last)) {
break;
} else if (MOZ_UNLIKELY(interrupted) ||
MOZ_UNLIKELY(nsContentSink::DidProcessATokenImpl() ==
NS_ERROR_HTMLPARSER_INTERRUPTED)) {
autoFlush.SetNumberOfOpsToRemove((iter - first) + 1);
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
return;
}
}
// Be sure not to check the deadline if the last op was just performed.
if (MOZ_UNLIKELY(iter == last)) {
break;
} else if (MOZ_UNLIKELY(interrupted) ||
MOZ_UNLIKELY(nsContentSink::DidProcessATokenImpl() ==
NS_ERROR_HTMLPARSER_INTERRUPTED)) {
mOpQueue.RemoveElementsAt(0, (iter - first) + 1);
EndDocUpdate();
mFlushState = eNotFlushing;
#ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
printf("REFLUSH SCHEDULED (executing ops): %d\n",
++sTimesFlushLoopInterrupted);
#endif
nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync();
return;
}
++iter;
}
mOpQueue.Clear();
EndDocUpdate();
mFlushState = eNotFlushing;
} // end autoFlush
if (MOZ_UNLIKELY(!mParser)) {
// The parse ended already.
@@ -526,7 +555,7 @@ nsHtml5TreeOpExecutor::FlushDocumentWrite()
if (MOZ_UNLIKELY(!mParser)) {
// The parse has ended.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
ClearOpQueue(); // clear in order to be able to assert in destructor
return rv;
}
@@ -535,15 +564,13 @@ nsHtml5TreeOpExecutor::FlushDocumentWrite()
return rv;
}
mFlushState = eInFlush;
// avoid crashing near EOF
RefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
RefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
mozilla::Unused << parserKungFuDeathGrip; // Intentionally not used within function
NS_ASSERTION(!mReadingFromStage,
"Got doc write flush when reading from stage");
MOZ_RELEASE_ASSERT(!mReadingFromStage,
"Got doc write flush when reading from stage");
#ifdef DEBUG
mStage.AssertEmpty();
@@ -551,34 +578,31 @@ nsHtml5TreeOpExecutor::FlushDocumentWrite()
nsIContent* scriptElement = nullptr;
bool interrupted = false;
BeginDocUpdate();
uint32_t numberOfOpsToFlush = mOpQueue.Length();
{
// autoFlush clears mOpQueue in its destructor.
nsHtml5AutoFlush autoFlush(this);
const nsHtml5TreeOperation* start = mOpQueue.Elements();
const nsHtml5TreeOperation* end = start + numberOfOpsToFlush;
for (nsHtml5TreeOperation* iter = const_cast<nsHtml5TreeOperation*>(start);
nsHtml5TreeOperation* start = mOpQueue.Elements();
nsHtml5TreeOperation* end = start + mOpQueue.Length();
for (nsHtml5TreeOperation* iter = start;
iter < end;
++iter) {
if (MOZ_UNLIKELY(!mParser)) {
// The previous tree op caused a call to nsIParser::Terminate().
break;
if (MOZ_UNLIKELY(!mParser)) {
// The previous tree op caused a call to nsIParser::Terminate().
return rv;
}
NS_ASSERTION(IsInDocUpdate(),
"Tried to perform tree op outside update batch.");
rv = iter->Perform(this, &scriptElement, &interrupted);
if (NS_FAILED(rv)) {
MarkAsBroken(rv);
break;
}
}
NS_ASSERTION(mFlushState == eInDocUpdate,
"Tried to perform tree op outside update batch.");
rv = iter->Perform(this, &scriptElement, &interrupted);
if (NS_FAILED(rv)) {
MarkAsBroken(rv);
break;
}
}
mOpQueue.Clear();
EndDocUpdate();
mFlushState = eNotFlushing;
} // autoFlush
if (MOZ_UNLIKELY(!mParser)) {
// Ending the doc update caused a call to nsIParser::Terminate().
@@ -617,7 +641,7 @@ nsHtml5TreeOpExecutor::StartLayout(bool* aInterrupted) {
return;
}
EndDocUpdate();
nsHtml5AutoPauseUpdate autoPause(this);
if (MOZ_UNLIKELY(!mParser)) {
// got terminate
@@ -628,8 +652,6 @@ nsHtml5TreeOpExecutor::StartLayout(bool* aInterrupted) {
if (mParser) {
*aInterrupted = !GetParser()->IsParserEnabled();
BeginDocUpdate();
}
}
@@ -637,12 +659,10 @@ void
nsHtml5TreeOpExecutor::PauseDocUpdate(bool* aInterrupted) {
// Pausing the document update allows JS to run, and potentially block
// further parsing.
EndDocUpdate();
nsHtml5AutoPauseUpdate autoPause(this);
if (MOZ_LIKELY(mParser)) {
*aInterrupted = !GetParser()->IsParserEnabled();
BeginDocUpdate();
}
}
@@ -687,8 +707,9 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement)
NS_ASSERTION(!block, "Defer or async script tried to block.");
return;
}
NS_ASSERTION(mFlushState == eNotFlushing, "Tried to run script when flushing.");
MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
"Tried to run script while flushing.");
mReadingFromStage = false;
@@ -726,8 +747,7 @@ nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(NotNull<const Encoding*> aEncoding,
int32_t aSource,
uint32_t aLineNumber)
{
EndDocUpdate();
nsHtml5AutoPauseUpdate autoPause(this);
if (MOZ_UNLIKELY(!mParser)) {
// got terminate
return;
@@ -760,8 +780,6 @@ nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(NotNull<const Encoding*> aEncoding,
}
GetParser()->ContinueAfterFailedCharsetSwitch();
BeginDocUpdate();
}
void
@@ -825,12 +843,31 @@ nsHtml5TreeOpExecutor::GetParser()
void
nsHtml5TreeOpExecutor::MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue)
{
NS_PRECONDITION(mFlushState == eNotFlushing, "mOpQueue modified during tree op execution.");
MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
"Ops added to mOpQueue during tree op execution.");
mOpQueue.AppendElements(Move(aOpQueue));
}
void
nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, int32_t aLine)
nsHtml5TreeOpExecutor::ClearOpQueue()
{
MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
"mOpQueue cleared during tree op execution.");
mOpQueue.Clear();
}
void
nsHtml5TreeOpExecutor::RemoveFromStartOfOpQueue(size_t aNumberOfOpsToRemove)
{
MOZ_RELEASE_ASSERT(mFlushState == eNotFlushing,
"Ops removed from mOpQueue during tree op execution.");
mOpQueue.RemoveElementsAt(0, aNumberOfOpsToRemove);
}
void
nsHtml5TreeOpExecutor::InitializeDocWriteParserState(
nsAHtml5TreeBuilderState* aState,
int32_t aLine)
{
GetParser()->InitializeDocWriteParserState(aState, aLine);
}