Bug 516406 - Make document.write() parser and stream parser have distinct tokenizers in the HTML5 parser. r=bnewman.

This commit is contained in:
Henri Sivonen
2009-09-21 16:18:20 +03:00
parent 4384f2fdf0
commit 2cf48a3ad5
39 changed files with 1546 additions and 614 deletions

View File

@@ -110,16 +110,15 @@ nsHtml5Parser::nsHtml5Parser()
, mExecutor(new nsHtml5TreeOpExecutor())
, mTreeBuilder(new nsHtml5TreeBuilder(mExecutor))
, mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
, mAtomTable(new nsHtml5AtomTable())
{
mExecutor->SetTreeBuilder(mTreeBuilder);
mAtomTable->Init(); // we aren't checking for OOM anyway...
mTokenizer->setInterner(mAtomTable);
mAtomTable.Init(); // we aren't checking for OOM anyway...
mTokenizer->setInterner(&mAtomTable);
// There's a zeroing operator new for everything else
}
nsHtml5Parser::~nsHtml5Parser()
{
mTokenizer->end();
while (mFirstBuffer) {
nsHtml5UTF16Buffer* old = mFirstBuffer;
mFirstBuffer = mFirstBuffer->next;
@@ -196,7 +195,7 @@ NS_IMETHODIMP
nsHtml5Parser::GetStreamListener(nsIStreamListener** aListener)
{
if (!mStreamParser) {
mStreamParser = new nsHtml5StreamParser(mTokenizer, mExecutor, this);
mStreamParser = new nsHtml5StreamParser(mExecutor, this);
}
NS_ADDREF(*aListener = mStreamParser);
return NS_OK;
@@ -225,9 +224,7 @@ nsHtml5Parser::ContinueInterruptedParsing()
nsCOMPtr<nsIParser> kungFuDeathGrip(this);
nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
// XXX Stop speculative script thread but why?
mExecutor->MaybeFlush();
ParseUntilSuspend();
ParseUntilScript();
return NS_OK;
}
@@ -235,18 +232,12 @@ NS_IMETHODIMP_(void)
nsHtml5Parser::BlockParser()
{
mBlocked = PR_TRUE;
if (mStreamParser) {
mStreamParser->Block();
}
}
NS_IMETHODIMP_(void)
nsHtml5Parser::UnblockParser()
{
mBlocked = PR_FALSE;
if (mStreamParser) {
mStreamParser->Unblock();
}
}
NS_IMETHODIMP_(PRBool)
@@ -274,16 +265,12 @@ nsHtml5Parser::Parse(nsIURI* aURL, // legacy parameter; ignored
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED,
"Tried to start parse without initializing the parser properly.");
if (!mStreamParser) {
mStreamParser = new nsHtml5StreamParser(mTokenizer, mExecutor, this);
mStreamParser = new nsHtml5StreamParser(mExecutor, this);
}
mStreamParser->SetObserver(aObserver);
mTokenizer->setEncodingDeclarationHandler(mStreamParser);
mExecutor->SetStreamParser(mStreamParser);
mExecutor->SetParser(this);
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
mExecutor->AllowInterrupts();
mRootContextKey = aKey;
mExecutor->SetParser(this);
return NS_OK;
}
@@ -310,10 +297,18 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
case TERMINATED:
return NS_OK;
case NOT_STARTED:
NS_ASSERTION(!mStreamParser,
"Had stream parser but document.write started life cycle.");
mExecutor->SetParser(this);
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
mTokenizer->start();
mExecutor->SetLifeCycle(PARSING);
mExecutor->Start();
/*
* If you move the following line, be very careful not to cause
* WillBuildModel to be called before the document has had its
* script global object set.
*/
mExecutor->WillBuildModel(eDTDMode_unknown);
break;
default:
break;
@@ -321,13 +316,13 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
if (aLastCall && aSourceBuffer.IsEmpty() && aKey == GetRootContextKey()) {
// document.close()
mExecutor->SetLifeCycle(STREAM_ENDING);
NS_ASSERTION(!mStreamParser,
"Had stream parser but got document.close().");
mDocumentClosed = PR_TRUE;
MaybePostContinueEvent();
return NS_OK;
}
// XXX stop speculative script thread here
PRInt32 lineNumberSave = mTokenizer->getLineNumber();
if (!aSourceBuffer.IsEmpty()) {
@@ -335,16 +330,20 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
memcpy(buffer->getBuffer(), aSourceBuffer.BeginReading(), aSourceBuffer.Length() * sizeof(PRUnichar));
buffer->setEnd(aSourceBuffer.Length());
if (!mBlocked) {
mExecutor->WillResume();
// mExecutor->WillResume();
while (buffer->hasMore()) {
buffer->adjust(mLastWasCR);
mLastWasCR = PR_FALSE;
if (buffer->hasMore()) {
mLastWasCR = mTokenizer->tokenizeBuffer(buffer);
mExecutor->MaybeExecuteScript();
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush(); // moves ops to executor queue
mExecutor->Flush(); // executes the queue
// Is mBlocked always true here?
}
if (mBlocked) {
// XXX is the tail insertion and script exec in the wrong order?
mExecutor->WillInterrupt();
// mExecutor->WillInterrupt();
break;
}
// Ignore suspension requests
@@ -396,6 +395,8 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
}
// Scripting semantics require a forced tree builder flush here
// TODO: Also flush the pending text node from tree builder
mTreeBuilder->Flush();
mExecutor->Flush();
mTokenizer->setLineNumber(lineNumberSave);
return NS_OK;
@@ -426,7 +427,9 @@ nsHtml5Parser::Terminate(void)
// CancelParsingEvents must be called to avoid leaking the nsParser object
// @see bug 108049
CancelParsingEvents();
if (mStreamParser) {
mStreamParser->Terminate();
}
return mExecutor->DidBuildModel(PR_TRUE);
}
@@ -464,7 +467,6 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
// Initialize() doesn't deal with base URI
mExecutor->SetBaseUriFromDocument();
mExecutor->ProhibitInterrupts();
mExecutor->SetParser(this);
mExecutor->SetNodeInfoManager(target->GetOwnerDoc()->NodeInfoManager());
@@ -475,7 +477,7 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
NS_PRECONDITION(mExecutor->GetLifeCycle() == NOT_STARTED, "Tried to start parse without initializing the parser properly.");
mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
mTokenizer->start();
mExecutor->SetLifeCycle(PARSING);
mExecutor->Start(); // Don't call WillBuildModel in fragment case
if (!aSourceBuffer.IsEmpty()) {
PRBool lastWasCR = PR_FALSE;
nsHtml5UTF16Buffer buffer(aSourceBuffer.Length());
@@ -490,21 +492,22 @@ nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
}
}
}
mExecutor->SetLifeCycle(TERMINATED);
mTokenizer->eof();
mTreeBuilder->StreamEnded();
mTreeBuilder->Flush();
mExecutor->Flush();
mTokenizer->end();
mExecutor->SetLifeCycle(TERMINATED);
mExecutor->DropParserAndPerfHint();
mAtomTable->Clear();
mAtomTable.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsHtml5Parser::BuildModel(void)
{
// XXX who calls this? Should this be a no-op?
ParseUntilSuspend();
return NS_OK;
NS_NOTREACHED("Don't call this!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
@@ -521,11 +524,11 @@ nsHtml5Parser::Reset()
mLastWasCR = PR_FALSE;
mFragmentMode = PR_FALSE;
UnblockParser();
mSuspending = PR_FALSE;
mDocumentClosed = PR_FALSE;
mStreamParser = nsnull;
mRootContextKey = nsnull;
mContinueEvent = nsnull; // weak ref
mAtomTable->Clear(); // should be already cleared in the fragment case anyway
mAtomTable.Clear(); // should be already cleared in the fragment case anyway
// Portable parser objects
while (mFirstBuffer->next) {
nsHtml5UTF16Buffer* oldBuf = mFirstBuffer;
@@ -557,10 +560,9 @@ nsHtml5Parser::HandleParserContinueEvent(nsHtml5ParserContinueEvent* ev)
}
void
nsHtml5Parser::ParseUntilSuspend()
nsHtml5Parser::ParseUntilScript()
{
NS_PRECONDITION(!mFragmentMode, "ParseUntilSuspend called in fragment mode.");
NS_PRECONDITION(!mExecutor->NeedsCharsetSwitch(), "ParseUntilSuspend called when charset switch needed.");
NS_PRECONDITION(!mFragmentMode, "ParseUntilScript called in fragment mode.");
if (mBlocked) {
return;
@@ -577,7 +579,6 @@ nsHtml5Parser::ParseUntilSuspend()
}
mExecutor->WillResume();
mSuspending = PR_FALSE;
for (;;) {
if (!mFirstBuffer->hasMore()) {
if (mFirstBuffer == mLastBuffer) {
@@ -586,21 +587,26 @@ nsHtml5Parser::ParseUntilSuspend()
// something like cache manisfests stopped the parse in mid-flight
return;
case PARSING:
// never release the last buffer. instead just zero its indeces for refill
mFirstBuffer->setStart(0);
mFirstBuffer->setEnd(0);
if (mStreamParser) {
mStreamParser->ParseUntilSuspend();
}
return; // no more data for now but expecting more
case STREAM_ENDING:
if (mStreamParser && !mStreamParser->IsDone()) { // may still have stream data left
mStreamParser->ParseUntilSuspend();
if (mDocumentClosed) {
NS_ASSERTION(!mStreamParser,
"This should only happen with script-created parser.");
mTokenizer->eof();
mTreeBuilder->StreamEnded();
mTreeBuilder->Flush();
mExecutor->Flush();
mTokenizer->end();
return;
} else {
// no more data and not expecting more
mExecutor->DidBuildModel(PR_FALSE);
// never release the last buffer. instead just zero its indeces for refill
mFirstBuffer->setStart(0);
mFirstBuffer->setEnd(0);
if (mStreamParser) {
mStreamParser->ContinueAfterScripts(mTokenizer,
mTreeBuilder,
mLastWasCR);
}
return; // no more data for now but expecting more
}
return;
default:
NS_NOTREACHED("It should be impossible to reach this.");
return;
@@ -622,16 +628,12 @@ nsHtml5Parser::ParseUntilSuspend()
mLastWasCR = PR_FALSE;
if (mFirstBuffer->hasMore()) {
mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
NS_ASSERTION(!(mExecutor->HasScriptElement() && mExecutor->NeedsCharsetSwitch()), "Can't have both script and charset switch.");
mExecutor->IgnoreCharsetSwitch();
mExecutor->MaybeExecuteScript();
if (mBlocked) {
mExecutor->WillInterrupt();
return;
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush();
mExecutor->Flush();
}
if (mSuspending) {
MaybePostContinueEvent();
mExecutor->WillInterrupt();
if (mBlocked) {
// mExecutor->WillInterrupt();
return;
}
}
@@ -657,15 +659,6 @@ nsHtml5Parser::MaybePostContinueEvent()
}
}
void
nsHtml5Parser::Suspend()
{
mSuspending = PR_TRUE;
if (mStreamParser) {
mStreamParser->Suspend();
}
}
nsresult
nsHtml5Parser::Initialize(nsIDocument* aDoc,
nsIURI* aURI,
@@ -675,3 +668,16 @@ nsHtml5Parser::Initialize(nsIDocument* aDoc,
return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
}
void
nsHtml5Parser::StartTokenizer(PRBool aScriptingEnabled) {
mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
mTokenizer->start();
}
void
nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState)
{
mTokenizer->resetToDataState();
mTreeBuilder->loadState(aState, &mAtomTable);
mLastWasCR = PR_FALSE;
}