diff --git a/mozilla/htmlparser/src/nsExpatTokenizer.cpp b/mozilla/htmlparser/src/nsExpatTokenizer.cpp
index 897bdee6262..66154226fe6 100644
--- a/mozilla/htmlparser/src/nsExpatTokenizer.cpp
+++ b/mozilla/htmlparser/src/nsExpatTokenizer.cpp
@@ -364,10 +364,11 @@ nsExpatTokenizer::AddErrorMessageTokens(nsParserError* aError)
* in the content sink:
*
*
- * XML Error: "contents of aError->description"
- * Line Number: "contents of aError->lineNumber"
+ * XML Error: [aError->description]
+ * Location: [aError->sourceURL]
+ * Line Number: [aError->lineNumber], Column [aError->colNumber]
*
- * "Contents of aError->sourceLine"
+ * [aError->sourceLine]
* "^ pointing at the error location"
*
*
@@ -387,6 +388,58 @@ nsExpatTokenizer::PushXMLErrorTokens(const char *aBuffer, PRUint32 aLength, PRBo
// Adjust the column number so that it is one based rather than zero based.
error->colNumber = XML_GetCurrentColumnNumber(mExpatParser) + 1;
error->description.AssignWithConversion(XML_ErrorString(error->code));
+ if (error->code==XML_ERROR_TAG_MISMATCH){
+ /*
+ * Certain things can be assumed about the token stream because of
+ * the way expat behaves. eg:
+ *
+ * - data:text/xml, is NOT WELL-FORMED
+ * - data:text/xml, is a TAG_MISMATCH.
+ *
+ * We can assume that there is at least one extra open tag (the one we
+ * want), so balance is initially set to one.
+ *
+ * Then loop through the tokens:
+ * - Each time we see eToken_end () increment balance, because
+ * that means there is another pair of tags we don't care about.
+ * - Each time we see eToken_start () decrement balance because it
+ * matches a close tag (perhaps the MISMATCHed tag in which case
+ * balance should hit 0).
+ * - If balance ever hits zero, exit the loop. Because of the way
+ * balance is adjusted, if balance is zero expected *must* be a start
+ * tag.
+ *
+ * We must check expected in the condition in case expat or nsDeque go
+ * crazy and give us 0 (null) before balance reaches 0.
+ */
+ nsDequeIterator current = mState->tokenDeque->End();
+ CToken *expected = NS_STATIC_CAST(CToken*,--current);
+ PRUint32 balance = 1;
+
+ while (expected) {
+ switch (expected->GetTokenType()) {
+ case eToken_start:
+ --balance;
+ break;
+ case eToken_end:
+ ++balance;
+ break;
+ default:
+ break; // we don't care about newlines or other tokens
+ }
+
+ if (!balance) {
+ // if balance is zero, this must be a start tag
+ CStartToken *startToken=NS_STATIC_CAST(CStartToken*,expected);
+ error->description.Append(NS_LITERAL_STRING(". Expected: "));
+ error->description.Append(startToken->GetStringValue());
+ error->description.Append(NS_LITERAL_STRING(">"));
+ break;
+ }
+
+ expected = NS_STATIC_CAST(CToken*,--current);
+ }
+ }
error->sourceURL.Assign((PRUnichar*)XML_GetBase(mExpatParser));
if (!aIsFinal) {
PRInt32 byteIndexRelativeToFile = 0;
diff --git a/mozilla/parser/htmlparser/src/nsExpatTokenizer.cpp b/mozilla/parser/htmlparser/src/nsExpatTokenizer.cpp
index 897bdee6262..66154226fe6 100644
--- a/mozilla/parser/htmlparser/src/nsExpatTokenizer.cpp
+++ b/mozilla/parser/htmlparser/src/nsExpatTokenizer.cpp
@@ -364,10 +364,11 @@ nsExpatTokenizer::AddErrorMessageTokens(nsParserError* aError)
* in the content sink:
*
*
- * XML Error: "contents of aError->description"
- * Line Number: "contents of aError->lineNumber"
+ * XML Error: [aError->description]
+ * Location: [aError->sourceURL]
+ * Line Number: [aError->lineNumber], Column [aError->colNumber]
*
- * "Contents of aError->sourceLine"
+ * [aError->sourceLine]
* "^ pointing at the error location"
*
*
@@ -387,6 +388,58 @@ nsExpatTokenizer::PushXMLErrorTokens(const char *aBuffer, PRUint32 aLength, PRBo
// Adjust the column number so that it is one based rather than zero based.
error->colNumber = XML_GetCurrentColumnNumber(mExpatParser) + 1;
error->description.AssignWithConversion(XML_ErrorString(error->code));
+ if (error->code==XML_ERROR_TAG_MISMATCH){
+ /*
+ * Certain things can be assumed about the token stream because of
+ * the way expat behaves. eg:
+ *
+ * - data:text/xml, is NOT WELL-FORMED
+ * - data:text/xml, is a TAG_MISMATCH.
+ *
+ * We can assume that there is at least one extra open tag (the one we
+ * want), so balance is initially set to one.
+ *
+ * Then loop through the tokens:
+ * - Each time we see eToken_end () increment balance, because
+ * that means there is another pair of tags we don't care about.
+ * - Each time we see eToken_start () decrement balance because it
+ * matches a close tag (perhaps the MISMATCHed tag in which case
+ * balance should hit 0).
+ * - If balance ever hits zero, exit the loop. Because of the way
+ * balance is adjusted, if balance is zero expected *must* be a start
+ * tag.
+ *
+ * We must check expected in the condition in case expat or nsDeque go
+ * crazy and give us 0 (null) before balance reaches 0.
+ */
+ nsDequeIterator current = mState->tokenDeque->End();
+ CToken *expected = NS_STATIC_CAST(CToken*,--current);
+ PRUint32 balance = 1;
+
+ while (expected) {
+ switch (expected->GetTokenType()) {
+ case eToken_start:
+ --balance;
+ break;
+ case eToken_end:
+ ++balance;
+ break;
+ default:
+ break; // we don't care about newlines or other tokens
+ }
+
+ if (!balance) {
+ // if balance is zero, this must be a start tag
+ CStartToken *startToken=NS_STATIC_CAST(CStartToken*,expected);
+ error->description.Append(NS_LITERAL_STRING(". Expected: "));
+ error->description.Append(startToken->GetStringValue());
+ error->description.Append(NS_LITERAL_STRING(">"));
+ break;
+ }
+
+ expected = NS_STATIC_CAST(CToken*,--current);
+ }
+ }
error->sourceURL.Assign((PRUnichar*)XML_GetBase(mExpatParser));
if (!aIsFinal) {
PRInt32 byteIndexRelativeToFile = 0;