Bug 483015 - Expose HTML line number to JS and CSS parsers in the HTML5 parser. r=bnewman, a=beltzner.

This commit is contained in:
Henri Sivonen 2009-10-28 15:48:37 +02:00
Родитель 75191b2cf1
Коммит 2b0cb4fef4
11 изменённых файлов: 123 добавлений и 19 удалений

Просмотреть файл

@ -87,6 +87,7 @@ nsHtml5Parser::nsHtml5Parser()
, mExecutor(new nsHtml5TreeOpExecutor()) , mExecutor(new nsHtml5TreeOpExecutor())
, mTreeBuilder(new nsHtml5TreeBuilder(mExecutor)) , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor))
, mTokenizer(new nsHtml5Tokenizer(mTreeBuilder)) , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
, mRootContextLineNumber(1)
{ {
mAtomTable.Init(); // we aren't checking for OOM anyway... mAtomTable.Init(); // we aren't checking for OOM anyway...
mTokenizer->setInterner(&mAtomTable); mTokenizer->setInterner(&mAtomTable);
@ -293,12 +294,12 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
NS_PRECONDITION(IsInsertionPointDefined(), NS_PRECONDITION(IsInsertionPointDefined(),
"Document.write called when insertion point not defined."); "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()) { if (aSourceBuffer.IsEmpty()) {
return NS_OK; return NS_OK;
} }
PRInt32 lineNumberSave = mTokenizer->getLineNumber();
nsRefPtr<nsHtml5UTF16Buffer> buffer = new nsHtml5UTF16Buffer(aSourceBuffer.Length()); nsRefPtr<nsHtml5UTF16Buffer> buffer = new nsHtml5UTF16Buffer(aSourceBuffer.Length());
memcpy(buffer->getBuffer(), aSourceBuffer.BeginReading(), aSourceBuffer.Length() * sizeof(PRUnichar)); memcpy(buffer->getBuffer(), aSourceBuffer.BeginReading(), aSourceBuffer.Length() * sizeof(PRUnichar));
buffer->setEnd(aSourceBuffer.Length()); buffer->setEnd(aSourceBuffer.Length());
@ -346,7 +347,25 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
buffer->adjust(mLastWasCR); buffer->adjust(mLastWasCR);
mLastWasCR = PR_FALSE; mLastWasCR = PR_FALSE;
if (buffer->hasMore()) { 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); mLastWasCR = mTokenizer->tokenizeBuffer(buffer);
if (inRootContext) {
mRootContextLineNumber = mTokenizer->getLineNumber();
} else {
mTokenizer->setLineNumber(lineNumberSave);
}
if (mTreeBuilder->HasScript()) { if (mTreeBuilder->HasScript()) {
// No need to flush characters, because an end tag was tokenized last // No need to flush characters, because an end tag was tokenized last
mTreeBuilder->Flush(); // Move ops to the executor mTreeBuilder->Flush(); // Move ops to the executor
@ -362,13 +381,13 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
} }
if (!mBlocked) { // buffer was tokenized to completion 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 // Scripting semantics require a forced tree builder flush here
mTreeBuilder->flushCharacters(); // Flush trailing characters mTreeBuilder->flushCharacters(); // Flush trailing characters
mTreeBuilder->Flush(); // Move ops to the executor mTreeBuilder->Flush(); // Move ops to the executor
mExecutor->Flush(); // run the ops mExecutor->Flush(); // run the ops
} }
mTokenizer->setLineNumber(lineNumberSave);
return NS_OK; return NS_OK;
} }
@ -492,6 +511,7 @@ nsHtml5Parser::Reset()
UnblockParser(); UnblockParser();
mDocumentClosed = PR_FALSE; mDocumentClosed = PR_FALSE;
mStreamParser = nsnull; mStreamParser = nsnull;
mRootContextLineNumber = 1;
mParserInsertedScriptsBeingEvaluated = 0; mParserInsertedScriptsBeingEvaluated = 0;
mRootContextKey = nsnull; mRootContextKey = nsnull;
mAtomTable.Clear(); // should be already cleared in the fragment case anyway mAtomTable.Clear(); // should be already cleared in the fragment case anyway
@ -603,7 +623,14 @@ nsHtml5Parser::ParseUntilBlocked()
mFirstBuffer->adjust(mLastWasCR); mFirstBuffer->adjust(mLastWasCR);
mLastWasCR = PR_FALSE; mLastWasCR = PR_FALSE;
if (mFirstBuffer->hasMore()) { if (mFirstBuffer->hasMore()) {
PRBool inRootContext = (!mStreamParser && (mFirstBuffer->key == mRootContextKey));
if (inRootContext) {
mTokenizer->setLineNumber(mRootContextLineNumber);
}
mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer); mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
if (inRootContext) {
mRootContextLineNumber = mTokenizer->getLineNumber();
}
if (mTreeBuilder->HasScript()) { if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush(); mTreeBuilder->Flush();
mExecutor->Flush(); mExecutor->Flush();
@ -636,9 +663,10 @@ nsHtml5Parser::StartTokenizer(PRBool aScriptingEnabled) {
} }
void void
nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState) nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine)
{ {
mTokenizer->resetToDataState(); mTokenizer->resetToDataState();
mTokenizer->setLineNumber(aLine);
mTreeBuilder->loadState(aState, &mAtomTable); mTreeBuilder->loadState(aState, &mAtomTable);
mLastWasCR = PR_FALSE; mLastWasCR = PR_FALSE;
mReturnToStreamParserPermitted = PR_TRUE; mReturnToStreamParserPermitted = PR_TRUE;

Просмотреть файл

@ -294,7 +294,7 @@ class nsHtml5Parser : public nsIParser,
return mTokenizer; return mTokenizer;
} }
void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState); void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine);
void DropStreamParser() { void DropStreamParser() {
mStreamParser = nsnull; mStreamParser = nsnull;
@ -378,6 +378,11 @@ class nsHtml5Parser : public nsIParser,
* The stream parser. * The stream parser.
*/ */
nsRefPtr<nsHtml5StreamParser> mStreamParser; nsRefPtr<nsHtml5StreamParser> mStreamParser;
/**
*
*/
PRInt32 mRootContextLineNumber;
/** /**
* Whether it's OK to transfer parsing back to the stream parser * Whether it's OK to transfer parsing back to the stream parser

Просмотреть файл

@ -39,9 +39,11 @@
nsHtml5Speculation::nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer, nsHtml5Speculation::nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer,
PRInt32 aStart, PRInt32 aStart,
PRInt32 aStartLineNumber,
nsAHtml5TreeBuilderState* aSnapshot) nsAHtml5TreeBuilderState* aSnapshot)
: mBuffer(aBuffer) : mBuffer(aBuffer)
, mStart(aStart) , mStart(aStart)
, mStartLineNumber(aStartLineNumber)
, mSnapshot(aSnapshot) , mSnapshot(aSnapshot)
{ {
MOZ_COUNT_CTOR(nsHtml5Speculation); MOZ_COUNT_CTOR(nsHtml5Speculation);

Просмотреть файл

@ -50,6 +50,7 @@ class nsHtml5Speculation : public nsAHtml5TreeOpSink
public: public:
nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer, nsHtml5Speculation(nsHtml5UTF16Buffer* aBuffer,
PRInt32 aStart, PRInt32 aStart,
PRInt32 aStartLineNumber,
nsAHtml5TreeBuilderState* aSnapshot); nsAHtml5TreeBuilderState* aSnapshot);
~nsHtml5Speculation(); ~nsHtml5Speculation();
@ -61,6 +62,10 @@ class nsHtml5Speculation : public nsAHtml5TreeOpSink
PRInt32 GetStart() { PRInt32 GetStart() {
return mStart; return mStart;
} }
PRInt32 GetStartLineNumber() {
return mStartLineNumber;
}
nsAHtml5TreeBuilderState* GetSnapshot() { nsAHtml5TreeBuilderState* GetSnapshot() {
return mSnapshot; return mSnapshot;
@ -89,6 +94,11 @@ class nsHtml5Speculation : public nsAHtml5TreeOpSink
* The start index of this speculation in the first buffer * The start index of this speculation in the first buffer
*/ */
PRInt32 mStart; PRInt32 mStart;
/**
* The current line number at the start of the speculation
*/
PRInt32 mStartLineNumber;
nsAutoPtr<nsAHtml5TreeBuilderState> mSnapshot; nsAutoPtr<nsAHtml5TreeBuilderState> mSnapshot;

Просмотреть файл

@ -757,8 +757,10 @@ nsHtml5StreamParser::ParseAvailableData()
nsHtml5Speculation* speculation = nsHtml5Speculation* speculation =
new nsHtml5Speculation(mFirstBuffer, new nsHtml5Speculation(mFirstBuffer,
mFirstBuffer->getStart(), mFirstBuffer->getStart(),
mTokenizer->getLineNumber(),
mTreeBuilder->newSnapshot()); mTreeBuilder->newSnapshot());
mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot()); mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot(),
speculation->GetStartLineNumber());
mTreeBuilder->Flush(); mTreeBuilder->Flush();
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) { if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
NS_WARNING("failed to dispatch executor flush event"); NS_WARNING("failed to dispatch executor flush event");
@ -855,6 +857,7 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
nsHtml5Speculation* speculation = mSpeculations.ElementAt(0); nsHtml5Speculation* speculation = mSpeculations.ElementAt(0);
mFirstBuffer = speculation->GetBuffer(); mFirstBuffer = speculation->GetBuffer();
mFirstBuffer->setStart(speculation->GetStart()); mFirstBuffer->setStart(speculation->GetStart());
mTokenizer->setLineNumber(speculation->GetStartLineNumber());
nsHtml5UTF16Buffer* buffer = mFirstBuffer->next; nsHtml5UTF16Buffer* buffer = mFirstBuffer->next;
while (buffer) { while (buffer) {
buffer->setStart(0); buffer->setStart(0);

Просмотреть файл

@ -140,8 +140,13 @@ nsIContent**
nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes) nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes)
{ {
NS_PRECONDITION(aAttributes, "Got null attributes."); 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) { if (mSpeculativeLoader) {
switch (aNamespace) { switch (aNamespace) {
@ -152,6 +157,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url)); Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
} }
} else if (nsHtml5Atoms::script == aName) { } 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); nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
if (url) { if (url) {
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET); nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
@ -179,6 +188,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
if (url) { if (url) {
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *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; break;
case kNameSpaceID_SVG: case kNameSpaceID_SVG:
@ -188,6 +201,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url)); Dispatch(new nsHtml5SpeculativeImage(mSpeculativeLoader, *url));
} }
} else if (nsHtml5Atoms::script == aName) { } 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); nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
if (url) { if (url) {
nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE); nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
@ -197,6 +214,10 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
(type) ? *type : EmptyString())); (type) ? *type : EmptyString()));
} }
} else if (nsHtml5Atoms::style == aName) { } 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); nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
if (url) { if (url) {
Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader, Dispatch(new nsHtml5SpeculativeStyle(mSpeculativeLoader,
@ -206,14 +227,21 @@ nsHtml5TreeBuilder::createElement(PRInt32 aNamespace, nsIAtom* aName, nsHtml5Htm
} }
break; 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 // 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; return content;
} }
@ -584,11 +612,11 @@ nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset)
} }
void void
nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot) nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine)
{ {
NS_PRECONDITION(HasScript(), "No script to add a snapshot to!"); NS_PRECONDITION(HasScript(), "No script to add a snapshot to!");
NS_PRECONDITION(aSnapshot, "Got null snapshot."); NS_PRECONDITION(aSnapshot, "Got null snapshot.");
mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot); mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine);
} }
void void

Просмотреть файл

@ -83,7 +83,7 @@
void NeedsCharsetSwitchTo(const nsACString& aEncoding); void NeedsCharsetSwitchTo(const nsACString& aEncoding);
void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot); void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine);
inline void Dispatch(nsIRunnable* aEvent) { inline void Dispatch(nsIRunnable* aEvent) {
if (NS_FAILED(NS_DispatchToMainThread(aEvent))) { if (NS_FAILED(NS_DispatchToMainThread(aEvent))) {

Просмотреть файл

@ -585,9 +585,9 @@ nsHtml5TreeOpExecutor::ForcedFlush(nsTArray<nsHtml5TreeOperation>& aOpQueue)
} }
void void
nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState) nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine)
{ {
static_cast<nsHtml5Parser*> (mParser.get())->InitializeDocWriteParserState(aState); static_cast<nsHtml5Parser*> (mParser.get())->InitializeDocWriteParserState(aState, aLine);
} }
void void

Просмотреть файл

@ -224,7 +224,7 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
mScriptElement = aScript; mScriptElement = aScript;
} }
void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState); void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine);
PRBool IsScriptEnabled(); PRBool IsScriptEnabled();

Просмотреть файл

@ -324,7 +324,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder)
nsIContent* node = *(mOne.node); nsIContent* node = *(mOne.node);
nsAHtml5TreeBuilderState* snapshot = mTwo.state; nsAHtml5TreeBuilderState* snapshot = mTwo.state;
if (snapshot) { if (snapshot) {
aBuilder->InitializeDocWriteParserState(snapshot); aBuilder->InitializeDocWriteParserState(snapshot, mInt);
} }
aBuilder->SetScriptElement(node); aBuilder->SetScriptElement(node);
return rv; return rv;
@ -392,6 +392,20 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder)
aBuilder->DocumentMode(mOne.mode); aBuilder->DocumentMode(mOne.mode);
return rv; return rv;
} }
case eTreeOpSetStyleLineNumber: {
nsIContent* node = *(mOne.node);
nsCOMPtr<nsIStyleSheetLinkingElement> 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<nsIScriptElement> sele = do_QueryInterface(node);
NS_ASSERTION(sele, "Node didn't QI to script.");
sele->SetScriptLineNumber(mInt);
return rv;
}
default: { default: {
NS_NOTREACHED("Bogus tree op"); NS_NOTREACHED("Bogus tree op");
} }

Просмотреть файл

@ -74,6 +74,8 @@ enum eHtml5TreeOperation {
eTreeOpProcessOfflineManifest, eTreeOpProcessOfflineManifest,
eTreeOpMarkMalformedIfScript, eTreeOpMarkMalformedIfScript,
eTreeOpStreamEnded, eTreeOpStreamEnded,
eTreeOpSetStyleLineNumber,
eTreeOpSetScriptLineNumber,
eTreeOpStartLayout eTreeOpStartLayout
}; };
@ -221,14 +223,26 @@ class nsHtml5TreeOperation {
mOne.charPtr = str; 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() { inline PRBool IsRunScript() {
return mOpCode == eTreeOpRunScript; return mOpCode == eTreeOpRunScript;
} }
inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot) { inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine) {
NS_ASSERTION(IsRunScript(), NS_ASSERTION(IsRunScript(),
"Setting a snapshot for a tree operation other than eTreeOpRunScript!"); "Setting a snapshot for a tree operation other than eTreeOpRunScript!");
mTwo.state = aSnapshot; mTwo.state = aSnapshot;
mInt = aLine;
} }
nsresult Perform(nsHtml5TreeOpExecutor* aBuilder); nsresult Perform(nsHtml5TreeOpExecutor* aBuilder);