From 2b0cb4fef45cc6bf700c903f16734d48f09fb8a0 Mon Sep 17 00:00:00 2001 From: Henri Sivonen Date: Wed, 28 Oct 2009 15:48:37 +0200 Subject: [PATCH] Bug 483015 - Expose HTML line number to JS and CSS parsers in the HTML5 parser. r=bnewman, a=beltzner. --- parser/html/nsHtml5Parser.cpp | 36 ++++++++++++++-- parser/html/nsHtml5Parser.h | 7 +++- parser/html/nsHtml5Speculation.cpp | 2 + parser/html/nsHtml5Speculation.h | 10 +++++ parser/html/nsHtml5StreamParser.cpp | 5 ++- parser/html/nsHtml5TreeBuilderCppSupplement.h | 42 +++++++++++++++---- parser/html/nsHtml5TreeBuilderHSupplement.h | 2 +- parser/html/nsHtml5TreeOpExecutor.cpp | 4 +- parser/html/nsHtml5TreeOpExecutor.h | 2 +- parser/html/nsHtml5TreeOperation.cpp | 16 ++++++- parser/html/nsHtml5TreeOperation.h | 16 ++++++- 11 files changed, 123 insertions(+), 19 deletions(-) diff --git a/parser/html/nsHtml5Parser.cpp b/parser/html/nsHtml5Parser.cpp index be110d181db..8b88077e86a 100644 --- a/parser/html/nsHtml5Parser.cpp +++ b/parser/html/nsHtml5Parser.cpp @@ -87,6 +87,7 @@ nsHtml5Parser::nsHtml5Parser() , mExecutor(new nsHtml5TreeOpExecutor()) , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor)) , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder)) + , mRootContextLineNumber(1) { mAtomTable.Init(); // we aren't checking for OOM anyway... mTokenizer->setInterner(&mAtomTable); @@ -293,12 +294,12 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer, NS_PRECONDITION(IsInsertionPointDefined(), "Document.write called when insertion point not defined."); + NS_PRECONDITION(!(mStreamParser && !aKey), "Got a null key in a non-script-created parser"); + if (aSourceBuffer.IsEmpty()) { return NS_OK; } - PRInt32 lineNumberSave = mTokenizer->getLineNumber(); - nsRefPtr buffer = new nsHtml5UTF16Buffer(aSourceBuffer.Length()); memcpy(buffer->getBuffer(), aSourceBuffer.BeginReading(), aSourceBuffer.Length() * sizeof(PRUnichar)); buffer->setEnd(aSourceBuffer.Length()); @@ -346,7 +347,25 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer, buffer->adjust(mLastWasCR); mLastWasCR = PR_FALSE; if (buffer->hasMore()) { + + PRInt32 lineNumberSave; + PRBool inRootContext = (!mStreamParser && (aKey == mRootContextKey)); + if (inRootContext) { + mTokenizer->setLineNumber(mRootContextLineNumber); + } else { + // we aren't the root context, so save the line number on the + // *stack* so that we can restore it. + lineNumberSave = mTokenizer->getLineNumber(); + } + mLastWasCR = mTokenizer->tokenizeBuffer(buffer); + + if (inRootContext) { + mRootContextLineNumber = mTokenizer->getLineNumber(); + } else { + mTokenizer->setLineNumber(lineNumberSave); + } + if (mTreeBuilder->HasScript()) { // No need to flush characters, because an end tag was tokenized last mTreeBuilder->Flush(); // Move ops to the executor @@ -362,13 +381,13 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer, } if (!mBlocked) { // buffer was tokenized to completion + NS_ASSERTION(!buffer->hasMore(), "Buffer wasn't tokenized to completion?"); // Scripting semantics require a forced tree builder flush here mTreeBuilder->flushCharacters(); // Flush trailing characters mTreeBuilder->Flush(); // Move ops to the executor mExecutor->Flush(); // run the ops } - mTokenizer->setLineNumber(lineNumberSave); return NS_OK; } @@ -492,6 +511,7 @@ nsHtml5Parser::Reset() UnblockParser(); mDocumentClosed = PR_FALSE; mStreamParser = nsnull; + mRootContextLineNumber = 1; mParserInsertedScriptsBeingEvaluated = 0; mRootContextKey = nsnull; mAtomTable.Clear(); // should be already cleared in the fragment case anyway @@ -603,7 +623,14 @@ nsHtml5Parser::ParseUntilBlocked() mFirstBuffer->adjust(mLastWasCR); mLastWasCR = PR_FALSE; if (mFirstBuffer->hasMore()) { + PRBool inRootContext = (!mStreamParser && (mFirstBuffer->key == mRootContextKey)); + if (inRootContext) { + mTokenizer->setLineNumber(mRootContextLineNumber); + } mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer); + if (inRootContext) { + mRootContextLineNumber = mTokenizer->getLineNumber(); + } if (mTreeBuilder->HasScript()) { mTreeBuilder->Flush(); mExecutor->Flush(); @@ -636,9 +663,10 @@ nsHtml5Parser::StartTokenizer(PRBool aScriptingEnabled) { } void -nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState) +nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine) { mTokenizer->resetToDataState(); + mTokenizer->setLineNumber(aLine); mTreeBuilder->loadState(aState, &mAtomTable); mLastWasCR = PR_FALSE; mReturnToStreamParserPermitted = PR_TRUE; diff --git a/parser/html/nsHtml5Parser.h b/parser/html/nsHtml5Parser.h index 391c0cd3bd1..8202a21d5d7 100644 --- a/parser/html/nsHtml5Parser.h +++ b/parser/html/nsHtml5Parser.h @@ -294,7 +294,7 @@ class nsHtml5Parser : public nsIParser, return mTokenizer; } - void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState); + void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine); void DropStreamParser() { mStreamParser = nsnull; @@ -378,6 +378,11 @@ class nsHtml5Parser : public nsIParser, * The stream parser. */ nsRefPtr mStreamParser; + + /** + * + */ + PRInt32 mRootContextLineNumber; /** * Whether it's OK to transfer parsing back to the stream parser diff --git a/parser/html/nsHtml5Speculation.cpp b/parser/html/nsHtml5Speculation.cpp index d1ce52482dd..14afa1b8a0e 100644 --- a/parser/html/nsHtml5Speculation.cpp +++ b/parser/html/nsHtml5Speculation.cpp @@ -39,9 +39,11 @@ nsHtml5Speculation::nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer, PRInt32 aStart, + PRInt32 aStartLineNumber, nsAHtml5TreeBuilderState* aSnapshot) : mBuffer(aBuffer) , mStart(aStart) + , mStartLineNumber(aStartLineNumber) , mSnapshot(aSnapshot) { MOZ_COUNT_CTOR(nsHtml5Speculation); diff --git a/parser/html/nsHtml5Speculation.h b/parser/html/nsHtml5Speculation.h index 3d84b92dc6b..07b3b7d867f 100644 --- a/parser/html/nsHtml5Speculation.h +++ b/parser/html/nsHtml5Speculation.h @@ -50,6 +50,7 @@ class nsHtml5Speculation : public nsAHtml5TreeOpSink public: nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer, PRInt32 aStart, + PRInt32 aStartLineNumber, nsAHtml5TreeBuilderState* aSnapshot); ~nsHtml5Speculation(); @@ -61,6 +62,10 @@ class nsHtml5Speculation : public nsAHtml5TreeOpSink PRInt32 GetStart() { return mStart; } + + PRInt32 GetStartLineNumber() { + return mStartLineNumber; + } nsAHtml5TreeBuilderState* GetSnapshot() { return mSnapshot; @@ -89,6 +94,11 @@ class nsHtml5Speculation : public nsAHtml5TreeOpSink * The start index of this speculation in the first buffer */ PRInt32 mStart; + + /** + * The current line number at the start of the speculation + */ + PRInt32 mStartLineNumber; nsAutoPtr mSnapshot; diff --git a/parser/html/nsHtml5StreamParser.cpp b/parser/html/nsHtml5StreamParser.cpp index f5eaa41a126..2f0b7792e05 100644 --- a/parser/html/nsHtml5StreamParser.cpp +++ b/parser/html/nsHtml5StreamParser.cpp @@ -757,8 +757,10 @@ nsHtml5StreamParser::ParseAvailableData() nsHtml5Speculation* speculation = new nsHtml5Speculation(mFirstBuffer, mFirstBuffer->getStart(), + mTokenizer->getLineNumber(), mTreeBuilder->newSnapshot()); - mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot()); + mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot(), + speculation->GetStartLineNumber()); mTreeBuilder->Flush(); if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) { NS_WARNING("failed to dispatch executor flush event"); @@ -855,6 +857,7 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer, nsHtml5Speculation* speculation = mSpeculations.ElementAt(0); mFirstBuffer = speculation->GetBuffer(); mFirstBuffer->setStart(speculation->GetStart()); + mTokenizer->setLineNumber(speculation->GetStartLineNumber()); nsHtml5UTF16Buffer* buffer = mFirstBuffer->next; while (buffer) { buffer->setStart(0); diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h index 576f5dd0625..0f64ae09a3f 100644 --- a/parser/html/nsHtml5TreeBuilderCppSupplement.h +++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h @@ -140,8 +140,13 @@ nsIContent** nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes) { NS_PRECONDITION(aAttributes, "Got null attributes."); + + nsIContent** content = AllocateContentHandle(); + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + // XXX if null, OOM! + treeOp->Init(aNamespace, aName, aAttributes, content); - // Start wall of code for speculative loading + // Start wall of code for speculative loading and line numbers if (mSpeculativeLoader) { switch (aNamespace) { @@ -152,6 +157,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url)); } } else if (nsHtml5Atoms::script == aName) { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + // XXX if null, OOM! + treeOp->Init(eTreeOpSetScriptLineNumber, content, tokenizer->getLineNumber()); + nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC); if (url) { nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); @@ -179,6 +188,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm if (url) { Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url)); } + } else if (nsHtml5Atoms::style == aName) { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + // XXX if null, OOM! + treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); } break; case kNameSpaceID_SVG: @@ -188,6 +201,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url)); } } else if (nsHtml5Atoms::script == aName) { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + // XXX if null, OOM! + treeOp->Init(eTreeOpSetScriptLineNumber, content, tokenizer->getLineNumber()); + nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); if (url) { nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); @@ -197,6 +214,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm (type) ? *type : EmptyString())); } } else if (nsHtml5Atoms::style == aName) { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + // XXX if null, OOM! + treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); + nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF); if (url) { Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader, @@ -206,14 +227,21 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm } break; } + } else if (aNamespace != kNameSpaceID_MathML) { + // No speculative loader--just line numbers + if (nsHtml5Atoms::style == aName) { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + // XXX if null, OOM! + treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber()); + } else if (nsHtml5Atoms::script == aName) { + nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); + // XXX if null, OOM! + treeOp->Init(eTreeOpSetScriptLineNumber, content, tokenizer->getLineNumber()); + } } // End wall of code for speculative loading - nsIContent** content = AllocateContentHandle(); - nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); - // XXX if null, OOM! - treeOp->Init(aNamespace, aName, aAttributes, content); return content; } @@ -584,11 +612,11 @@ nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset) } void -nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot) +nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine) { NS_PRECONDITION(HasScript(), "No script to add a snapshot to!"); NS_PRECONDITION(aSnapshot, "Got null snapshot."); - mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot); + mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine); } void diff --git a/parser/html/nsHtml5TreeBuilderHSupplement.h b/parser/html/nsHtml5TreeBuilderHSupplement.h index d1c734f8541..9bb5480fe61 100644 --- a/parser/html/nsHtml5TreeBuilderHSupplement.h +++ b/parser/html/nsHtml5TreeBuilderHSupplement.h @@ -83,7 +83,7 @@ void NeedsCharsetSwitchTo(const nsACString& aEncoding); - void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot); + void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine); inline void Dispatch(nsIRunnable* aEvent) { if (NS_FAILED(NS_DispatchToMainThread(aEvent))) { diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index 230fb36af8e..dbc9ce245d9 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -585,9 +585,9 @@ nsHtml5TreeOpExecutor::ForcedFlush(nsTArray& aOpQueue) } void -nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState) +nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine) { - static_cast (mParser.get())->InitializeDocWriteParserState(aState); + static_cast (mParser.get())->InitializeDocWriteParserState(aState, aLine); } void diff --git a/parser/html/nsHtml5TreeOpExecutor.h b/parser/html/nsHtml5TreeOpExecutor.h index a88d0752bab..26749e9797c 100644 --- a/parser/html/nsHtml5TreeOpExecutor.h +++ b/parser/html/nsHtml5TreeOpExecutor.h @@ -224,7 +224,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink, mScriptElement = aScript; } - void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState); + void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine); PRBool IsScriptEnabled(); diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index a268246a9ce..b471c07ef69 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -324,7 +324,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder) nsIContent* node = *(mOne.node); nsAHtml5TreeBuilderState* snapshot = mTwo.state; if (snapshot) { - aBuilder->InitializeDocWriteParserState(snapshot); + aBuilder->InitializeDocWriteParserState(snapshot, mInt); } aBuilder->SetScriptElement(node); return rv; @@ -392,6 +392,20 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder) aBuilder->DocumentMode(mOne.mode); return rv; } + case eTreeOpSetStyleLineNumber: { + nsIContent* node = *(mOne.node); + nsCOMPtr ssle = do_QueryInterface(node); + NS_ASSERTION(ssle, "Node didn't QI to style."); + ssle->SetLineNumber(mInt); + return rv; + } + case eTreeOpSetScriptLineNumber: { + nsIContent* node = *(mOne.node); + nsCOMPtr sele = do_QueryInterface(node); + NS_ASSERTION(sele, "Node didn't QI to script."); + sele->SetScriptLineNumber(mInt); + return rv; + } default: { NS_NOTREACHED("Bogus tree op"); } diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h index 88d0f15be07..f5a4cd97cb4 100644 --- a/parser/html/nsHtml5TreeOperation.h +++ b/parser/html/nsHtml5TreeOperation.h @@ -74,6 +74,8 @@ enum eHtml5TreeOperation { eTreeOpProcessOfflineManifest, eTreeOpMarkMalformedIfScript, eTreeOpStreamEnded, + eTreeOpSetStyleLineNumber, + eTreeOpSetScriptLineNumber, eTreeOpStartLayout }; @@ -221,14 +223,26 @@ class nsHtml5TreeOperation { mOne.charPtr = str; } + inline void Init(eHtml5TreeOperation aOpCode, + nsIContent** aNode, + PRInt32 aInt) { + NS_PRECONDITION(mOpCode == eTreeOpUninitialized, + "Op code must be uninitialized when initializing."); + + mOpCode = aOpCode; + mOne.node = aNode; + mInt = aInt; + } + inline PRBool IsRunScript() { return mOpCode == eTreeOpRunScript; } - inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot) { + inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine) { NS_ASSERTION(IsRunScript(), "Setting a snapshot for a tree operation other than eTreeOpRunScript!"); mTwo.state = aSnapshot; + mInt = aLine; } nsresult Perform(nsHtml5TreeOpExecutor* aBuilder);