Bug 1410848 - Use RAII with nsHtml5TreeOpExecutor::mFlushState. r=smaug.
MozReview-Commit-ID: 3a1tyYGT8u5
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user