Bug 516406 - Make document.write() parser and stream parser have distinct tokenizers in the HTML5 parser. r=bnewman.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user