Bug 482921 part 2 - Highligh tokenizer-level errors in View Source. r=Olli.Pettay.
authorHenri Sivonen <hsivonen@iki.fi>
Tue, 01 Nov 2011 13:33:11 +0200
changeset 80853 b8fc820633d844f789acd60e5fb3de424963a236
parent 80852 175a0afe3c436b9ac6fe84bab87b5ac8a808b732
child 80854 8a18316c38652ab13fbb4168463c816e2642b293
push idunknown
push userunknown
push dateunknown
reviewersOlli
bugs482921
milestone10.0a1
Bug 482921 part 2 - Highligh tokenizer-level errors in View Source. r=Olli.Pettay.
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
dom/locales/en-US/chrome/layout/htmlparser.properties
dom/locales/jar.mn
parser/html/nsHtml5Highlighter.cpp
parser/html/nsHtml5Highlighter.h
parser/html/nsHtml5Tokenizer.cpp
parser/html/nsHtml5Tokenizer.h
parser/html/nsHtml5TokenizerCppSupplement.h
parser/html/nsHtml5TokenizerHSupplement.h
parser/html/nsHtml5TreeOperation.cpp
parser/html/nsHtml5TreeOperation.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -754,16 +754,17 @@ public:
   enum PropertiesFile {
     eCSS_PROPERTIES,
     eXBL_PROPERTIES,
     eXUL_PROPERTIES,
     eLAYOUT_PROPERTIES,
     eFORMS_PROPERTIES,
     ePRINTING_PROPERTIES,
     eDOM_PROPERTIES,
+    eHTMLPARSER_PROPERTIES,
     eSVG_PROPERTIES,
     eBRAND_PROPERTIES,
     eCOMMON_DIALOG_PROPERTIES,
     PropertiesFile_COUNT
   };
   static nsresult ReportToConsole(PropertiesFile aFile,
                                   const char *aMessageName,
                                   const PRUnichar **aParams,
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2703,16 +2703,17 @@ static const char gPropertiesFiles[nsCon
   // Must line up with the enum values in |PropertiesFile| enum.
   "chrome://global/locale/css.properties",
   "chrome://global/locale/xbl.properties",
   "chrome://global/locale/xul.properties",
   "chrome://global/locale/layout_errors.properties",
   "chrome://global/locale/layout/HtmlForm.properties",
   "chrome://global/locale/printing.properties",
   "chrome://global/locale/dom/dom.properties",
+  "chrome://global/locale/layout/htmlparser.properties",
   "chrome://global/locale/svg/svg.properties",
   "chrome://branding/locale/brand.properties",
   "chrome://global/locale/commonDialogs.properties"
 };
 
 /* static */ nsresult
 nsContentUtils::EnsureStringBundle(PropertiesFile aFile)
 {
new file mode 100644
--- /dev/null
+++ b/dom/locales/en-US/chrome/layout/htmlparser.properties
@@ -0,0 +1,103 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is HTML parser error reporting code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Henri Sivonen <hsivonen@iki.fi>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+# The bulk of the messages in this file are derived from 
+# http://hg.mozilla.org/projects/htmlparser/file/1f633cef7de7/src/nu/validator/htmlparser/impl/ErrorReportingTokenizer.java
+# which is available under the MIT license.
+
+errGarbageAfterLtSlash=Garbage after \u201C</\u201D.
+errLtSlashGt=Saw \u201C</>\u201D. Probable causes: Unescaped \u201C<\u201D (escape as \u201C&lt;\u201D) or mistyped end tag.
+errCharRefLacksSemicolon=Character reference was not terminated by a semicolon.
+errNoDigitsInNCR=No digits in numeric character reference.
+errGtInSystemId=\u201C>\u201D in system identifier.
+errGtInPublicId=\u201C>\u201D in public identifier.
+errNamelessDoctype=Nameless doctype.
+errConsecutiveHyphens=Consecutive hyphens did not terminate a comment. \u201C--\u201D is not permitted inside a comment, but e.g. \u201C- -\u201D is.
+errPrematureEndOfComment=Premature end of comment. Use \u201C-->\u201D to end a comment properly.
+errBogusComment=Bogus comment.
+errUnquotedAttributeLt=\u201C<\u201D in an unquoted attribute value. Probable cause: Missing \u201C>\u201D immediately before.
+errUnquotedAttributeGrave=\u201C`\u201D in an unquoted attribute value. Probable cause: Using the wrong character as a quote.
+errUnquotedAttributeQuote=Quote in an unquoted attribute value. Probable causes: Attributes running together or a URL query string in an unquoted attribute value.
+errUnquotedAttributeEquals=\u201C=\u201D in an unquoted attribute value. Probable causes: Attributes running together or a URL query string in an unquoted attribute value.
+errSlashNotFollowedByGt=A slash was not immediate followed by \u201C>\u201D.
+errNoSpaceBetweenAttributes=No space between attributes.
+errUnquotedAttributeStartLt=\u201C<\u201D at the start of an unquoted attribute value. Probable cause: Missing \u201C>\u201D immediately before
+errUnquotedAttributeStartGrave=\u201C`\u201D at the start of an unquoted attribute value. Probable cause: Using the wrong character as a quote.
+errUnquotedAttributeStartEquals=\u201C=\u201D at the start of an unquoted attribute value. Probable cause: Stray duplicate equals sign.
+errAttributeValueMissing=Attribute value missing.
+errBadCharBeforeAttributeNameLt=Saw \u201C<\u201D when expecting an attribute name. Probable cause: Missing \u201C>\u201D immediately before.
+errEqualsSignBeforeAttributeName=Saw \u201C=\u201D when expecting an attribute name. Probable cause: Attribute name missing.
+errBadCharAfterLt=Bad character after \u201C<\u201D. Probable cause: Unescaped \u201C<\u201D. Try escaping it as \u201C&lt;\u201D.
+errLtGt=Saw \u201C<>\u201D. Probable causes: Unescaped \u201C<\u201D (escape as \u201C&lt;\u201D) or mistyped start tag.
+errProcessingInstruction=Saw \u201C<?\u201D. Probable cause: Attempt to use an XML processing instruction in HTML. (XML processing instructions are not supported in HTML.)
+errUnescapedAmpersandInterpretedAsCharacterReference=The string following \u201C&\u201D was interpreted as a character reference. (\u201C&\u201D probably should have been escaped as \u201C&amp;\u201D.)
+errNotSemicolonTerminated=Named character reference was not terminated by a semicolon. (Or \u201C&\u201D should have been escaped as \u201C&amp;\u201D.)
+errNoNamedCharacterMatch=\u201C&\u201D did not start a character reference. (\u201C&\u201D probably should have been escaped as \u201C&amp;\u201D.)
+errQuoteBeforeAttributeName=Saw a quote when expecting an attribute name. Probable cause: \u201C=\u201D missing immediately before.
+errLtInAttributeName=\u201C<\u201D in attribute name. Probable cause: \u201C>\u201D missing immediately before.
+errQuoteInAttributeName=Quote in attribute name. Probable cause: Matching quote missing somewhere earlier.
+errExpectedPublicId=Expected a public identifier but the doctype ended.
+errBogusDoctype=Bogus doctype.
+maybeErrAttributesOnEndTag=End tag had attributes.
+maybeErrSlashInEndTag=Stray \u201C/\u201D at the end of an end tag.
+errNcrNonCharacter=Character reference expands to a non-character.
+errAstralNonCharacter=Character reference expands to an astral non-character.
+errNcrSurrogate=Character reference expands to a surrogate.
+errNcrControlChar=Character reference expands to a control character.
+errNcrCr=A numeric character reference expanded to carriage return.
+errNcrInC1Range=A numeric character reference expanded to the C1 controls range.
+errEofInPublicId=End of file inside public identifier.
+errEofInComment=End of file inside comment.
+errEofInDoctype=End of file inside doctype.
+errEofInAttributeValue=End of file reached when inside an attribute value. Ignoring tag.
+errEofInAttributeName=End of file occurred in an attribute name. Ignoring tag.
+errEofWithoutGt=Saw end of file without the previous tag ending with \u201C>\u201D. Ignoring tag.
+errEofInTagName=End of file seen when looking for tag name. Ignoring tag.
+errEofInEndTag=End of file inside end tag. Ignoring tag.
+errEofAfterLt=End of file after \u201C<\u201D.
+errNcrOutOfRange=Character reference outside the permissible Unicode range.
+errNcrUnassigned=Character reference expands to a permanently unassigned code point.
+errDuplicateAttribute=Duplicate attribute.
+errEofInSystemId=End of file inside system identifier.
+errExpectedSystemId=Expected a system identifier but the doctype ended.
+errMissingSpaceBeforeDoctypeName=Missing space before doctype name.
+errHyphenHyphenBang=\u201C--!\u201D found in comment.
+errNcrControlChar=Character reference expands to a control character.
+errNcrZero=Character reference expands to zero.
+errNoSpaceBetweenDoctypeSystemKeywordAndQuote=No space between the doctype \u201CSYSTEM\u201D keyword and the quote.
+errNoSpaceBetweenPublicAndSystemIds=No space between the doctype public and system identifiers.
+errNoSpaceBetweenDoctypePublicKeywordAndQuote=No space between the doctype \u201CPUBLIC\u201D keyword and the quote.
\ No newline at end of file
--- a/dom/locales/jar.mn
+++ b/dom/locales/jar.mn
@@ -13,15 +13,16 @@
   locale/@AB_CD@/global/charsetTitles.properties               (%chrome/charsetTitles.properties)
   locale/@AB_CD@/global/global-strres.properties               (%chrome/global-strres.properties)
   locale/@AB_CD@/global/plugins.properties                     (%chrome/plugins.properties)
   locale/@AB_CD@/global/nsWebBrowserPersist.properties         (%chrome/nsWebBrowserPersist.properties)
   locale/@AB_CD@/global/xslt/xslt.properties                   (%chrome/xslt/xslt.properties)
   locale/@AB_CD@/global/dom/dom.properties                     (%chrome/dom/dom.properties)
   locale/@AB_CD@/global/svg/svg.properties                     (%chrome/svg/svg.properties)
   locale/@AB_CD@/global/layout/MediaDocument.properties        (%chrome/layout/MediaDocument.properties)
+  locale/@AB_CD@/global/layout/htmlparser.properties           (%chrome/layout/htmlparser.properties)
   locale/@AB_CD@/global/layout/xmlparser.properties            (%chrome/layout/xmlparser.properties)
   locale/@AB_CD@/global/layout/HtmlForm.properties             (%chrome/layout/HtmlForm.properties)
   locale/@AB_CD@/global/security/caps.properties               (%chrome/security/caps.properties)
   locale/@AB_CD@/global/xml/prettyprint.dtd                    (%chrome/xml/prettyprint.dtd)
   locale/@AB_CD@/global-platform/win/accessible.properties     (%chrome/accessibility/win/accessible.properties)
   locale/@AB_CD@/global-platform/mac/accessible.properties     (%chrome/accessibility/mac/accessible.properties)
   locale/@AB_CD@/global-platform/unix/accessible.properties    (%chrome/accessibility/unix/accessible.properties)
--- a/parser/html/nsHtml5Highlighter.cpp
+++ b/parser/html/nsHtml5Highlighter.cpp
@@ -159,16 +159,23 @@ nsHtml5Highlighter::Transition(PRInt32 a
   switch (mState) {
     case NS_HTML5TOKENIZER_SCRIPT_DATA:
     case NS_HTML5TOKENIZER_RAWTEXT:
     case NS_HTML5TOKENIZER_RCDATA:
     case NS_HTML5TOKENIZER_DATA:
       // We can transition on < and on &. Either way, we don't yet know the
       // role of the token, so open a span without class.
       StartSpan();
+      if (aState == NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE) {
+        // Start another span for highlighting the ampersand
+        StartSpan();
+        mAmpersand = CurrentNode();
+      } else {
+        mMarkupDecl = CurrentNode();
+      }
       break;
     case NS_HTML5TOKENIZER_TAG_OPEN:
       switch (aState) {
         case NS_HTML5TOKENIZER_TAG_NAME:
           StartSpan(sStartTag);
           break;
         case NS_HTML5TOKENIZER_DATA:
           EndInline(); // DATA
@@ -178,44 +185,47 @@ nsHtml5Highlighter::Transition(PRInt32 a
     case NS_HTML5TOKENIZER_TAG_NAME:
       switch (aState) {
         case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
           EndInline(); // NS_HTML5TOKENIZER_TAG_NAME
           break;
         case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
           EndInline(); // NS_HTML5TOKENIZER_TAG_NAME
           StartSpan(); // for highlighting the slash
+          mSlash = CurrentNode();
           break;
         default:
           FinishTag();
           break;
       }
       break;
     case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
       switch (aState) {
         case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
           StartSpan(sAttributeName);
           break;
         case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
           StartSpan(); // for highlighting the slash
+          mSlash = CurrentNode();
           break;
         default:
           FinishTag();
           break;
       }
       break;
     case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
       switch (aState) {
         case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME:
         case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
           EndInline(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME
           break;
         case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
           EndInline(); // NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME
           StartSpan(); // for highlighting the slash
+          mSlash = CurrentNode();
           break;
         default:
           FinishTag();
           break;
       }
       break;
     case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
       switch (aState) {
@@ -235,62 +245,67 @@ nsHtml5Highlighter::Transition(PRInt32 a
     case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED:
     case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED:
       switch (aState) {
         case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED:
           EndInline();
           break;
         case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
           StartSpan();
+          StartSpan(); // for ampersand itself
+          mAmpersand = CurrentNode();
           break;
         default:
           NS_NOTREACHED("Impossible transition.");
           break;
       }
       break;
     case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED:
       switch (aState) {
         case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
           break;
         case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
           StartSpan(); // for highlighting the slash
+          mSlash = CurrentNode();
           break;
         default:
           FinishTag();
           break;
       }
       break;
     case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
+      EndInline(); // end the slash highlight
       switch (aState) {
         case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
-          FlushCurrent();
-          EndInline();
           break;
         default:
           FinishTag();
           break;
       }
       break;
     case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED:
       switch (aState) {
         case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
           EndInline();
           break;
         case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
           StartSpan();
+          StartSpan(); // for ampersand itself
+          mAmpersand = CurrentNode();
           break;
         default:
           FinishTag();
           break;
       }
       break;
     case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME:
       switch (aState) {
         case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
           StartSpan(); // for highlighting the slash
+          mSlash = CurrentNode();
           break;
         case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE:
           break;
         case NS_HTML5TOKENIZER_ATTRIBUTE_NAME:
           StartSpan(sAttributeName);
           break;
         default:
           FinishTag();
@@ -313,16 +328,17 @@ nsHtml5Highlighter::Transition(PRInt32 a
       // highlighting
     case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB:
       if (aState == NS_HTML5TOKENIZER_DATA) {
         AddClass(sCdata);
         FinishTag();
       }
       break;
     case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE:
+      EndInline(); // the span for the ampersand
       switch (aState) {
         case NS_HTML5TOKENIZER_CONSUME_NCR:
         case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP:
           break;
         default:
           // not actually a character reference
           EndInline();
           break;
@@ -331,17 +347,16 @@ nsHtml5Highlighter::Transition(PRInt32 a
     case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP:
       if (aState == NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL) {
         break;
       }
       // not actually a character reference
       EndInline();
       break;
     case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL:
-      // XXX need tokenizer cooperation to set class!
       if (!aReconsume) {
         FlushCurrent();
       }
       EndInline();
       break;
     case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP:
     case NS_HTML5TOKENIZER_HEX_NCR_LOOP:
       switch (aState) {
@@ -378,16 +393,17 @@ nsHtml5Highlighter::Transition(PRInt32 a
         case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
           AddClass(sEndTag);
           EndInline();
           break;
         case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG:
           AddClass(sEndTag);
           EndInline();
           StartSpan(); // for highlighting the slash
+          mSlash = CurrentNode();
           break;
         case NS_HTML5TOKENIZER_DATA: // yes, as a result of emitting the token
           AddClass(sEndTag);
           FinishTag();
           break;
         default:
           FinishTag();
           break;
@@ -470,16 +486,19 @@ nsHtml5Highlighter::End()
     case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
     case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:
     case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:
       AddClass(sDoctype);
       break;
     default:
       break;
   }
+  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+  NS_ASSERTION(treeOp, "Tree op allocation failed.");
+  treeOp->Init(eTreeOpStreamEnded);
   FlushOps();
 }
 
 void
 nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer)
 {
   NS_PRECONDITION(!mBuffer, "Old buffer still here!");
   mBuffer = aBuffer;
@@ -669,8 +688,43 @@ nsHtml5Highlighter::AddViewSourceHref(co
   memcpy(bufferCopy, aValue.get(), aValue.Length() * sizeof(PRUnichar));
   bufferCopy[aValue.Length()] = 0;
 
   mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceHref,
                                  bufferCopy,
                                  aValue.Length(),
                                  CurrentNode());
 }
+
+void
+nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId)
+{
+  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+  NS_ASSERTION(treeOp, "Tree op allocation failed.");
+  treeOp->Init(CurrentNode(), aMsgId);
+}
+
+void
+nsHtml5Highlighter::AddErrorToCurrentMarkupDecl(const char* aMsgId)
+{
+  NS_PRECONDITION(mMarkupDecl, "Adding error to markup decl without one!");
+  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+  NS_ASSERTION(treeOp, "Tree op allocation failed.");
+  treeOp->Init(mMarkupDecl, aMsgId);
+}
+
+void
+nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId)
+{
+  NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!");
+  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+  NS_ASSERTION(treeOp, "Tree op allocation failed.");
+  treeOp->Init(mAmpersand, aMsgId);
+}
+
+void
+nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId)
+{
+  NS_PRECONDITION(mSlash, "Adding error to slash without one!");
+  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
+  NS_ASSERTION(treeOp, "Tree op allocation failed.");
+  treeOp->Init(mSlash, aMsgId);
+}
--- a/parser/html/nsHtml5Highlighter.h
+++ b/parser/html/nsHtml5Highlighter.h
@@ -115,16 +115,40 @@ class nsHtml5Highlighter
                                     nsString* aValue);
 
     /**
      * Inform the highlighter that the tokenizer successfully completed a
      * named character reference.
      */
     void CompletedNamedCharacterReference();
 
+    /**
+     * Adds an error annotation to the node that's currently on top of
+     * mStack.
+     */
+    void AddErrorToCurrentNode(const char* aMsgId);
+
+    /**
+     * Adds an error annotation to the node that corresponds to the most
+     * recently opened markup declaration/tag span.
+     */
+    void AddErrorToCurrentMarkupDecl(const char* aMsgId);
+
+    /**
+     * Adds an error annotation to the node that corresponds to the most
+     * recent potentially character reference-starting ampersand.
+     */
+    void AddErrorToCurrentAmpersand(const char* aMsgId);
+
+    /**
+     * Adds an error annotation to the node that corresponds to the most
+     * recent potentially self-closing slash.
+     */
+    void AddErrorToCurrentSlash(const char* aMsgId);
+
   private:
 
     /**
      * Starts a span with no class.
      */
     void StartSpan();
 
     /**
@@ -281,16 +305,32 @@ class nsHtml5Highlighter
     nsTArray<nsHtml5TreeOperation> mOpQueue;
 
     /**
      * The tree op stage for the tree op executor.
      */
     nsAHtml5TreeOpSink* mOpSink;
 
     /**
+     * The most recently opened markup declaration/tag.
+     */
+    nsIContent** mMarkupDecl;
+
+    /**
+     * The most recent ampersand in a place where character references were
+     * allowed.
+     */
+    nsIContent** mAmpersand;
+
+    /**
+     * The most recent slash that might become a self-closing slash.
+     */
+    nsIContent** mSlash;
+
+    /**
      * Memory for element handles.
      */
     nsAutoArrayPtr<nsIContent*> mHandles;
 
     /**
      * Number of handles used in mHandles
      */
     PRInt32 mHandlesUsed;
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -287,21 +287,21 @@ nsHtml5Tokenizer::strBufToElementNameStr
 {
   tagName = nsHtml5ElementName::elementNameByBuffer(strBuf, 0, strBufLen, interner);
 }
 
 PRInt32 
 nsHtml5Tokenizer::emitCurrentTagToken(bool selfClosing, PRInt32 pos)
 {
   cstart = pos + 1;
-
+  maybeErrSlashInEndTag(selfClosing);
   stateSave = NS_HTML5TOKENIZER_DATA;
   nsHtml5HtmlAttributes* attrs = (!attributes ? nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES : attributes);
   if (endTag) {
-
+    maybeErrAttributesOnEndTag(attrs);
     tokenHandler->endTag(tagName);
     delete attributes;
   } else {
     tokenHandler->startTag(tagName, attrs, selfClosing);
   }
   tagName->release();
   tagName = nsnull;
   resetAttributes();
@@ -311,17 +311,17 @@ nsHtml5Tokenizer::emitCurrentTagToken(bo
 void 
 nsHtml5Tokenizer::attributeNameComplete()
 {
   attributeName = nsHtml5AttributeName::nameByBuffer(strBuf, 0, strBufLen, interner);
   if (!attributes) {
     attributes = new nsHtml5HtmlAttributes(0);
   }
   if (attributes->contains(attributeName)) {
-
+    errDuplicateAttribute();
     attributeName->release();
     attributeName = nsnull;
   }
 }
 
 void 
 nsHtml5Tokenizer::addAttributeWithoutValue()
 {
@@ -3344,30 +3344,30 @@ nsHtml5Tokenizer::stateLoopReportTransit
               state = mViewSource->Transition(NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '/': {
               state = mViewSource->Transition(NS_HTML5TOKENIZER_CLOSE_TAG_OPEN, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\?': {
-
+              errProcessingInstruction();
               clearLongStrBufAndAppend(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errLtGt();
               tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 2);
               cstart = pos + 1;
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
-
+              errBadCharAfterLt(c);
               tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
               cstart = pos;
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
@@ -3458,17 +3458,19 @@ nsHtml5Tokenizer::stateLoopReportTransit
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\0': {
               c = 0xfffd;
             }
             case '\"':
             case '\'':
             case '<':
-            case '=':
+            case '=': {
+              errBadCharBeforeAttributeNameOrNull(c);
+            }
             default: {
               if (c >= 'A' && c <= 'Z') {
                 c += 0x20;
               }
               clearStrBufAndAppend(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_NAME, reconsume, pos);
               NS_HTML5_BREAK(beforeattributenameloop);
             }
@@ -3519,17 +3521,19 @@ nsHtml5Tokenizer::stateLoopReportTransit
               }
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\0': {
               c = 0xfffd;
             }
             case '\"':
             case '\'':
-            case '<':
+            case '<': {
+              errQuoteOrLtInAttributeNameOrNull(c);
+            }
             default: {
               if (c >= 'A' && c <= 'Z') {
                 c += 0x20;
               }
               appendStrBuf(c);
               continue;
             }
           }
@@ -3568,30 +3572,32 @@ nsHtml5Tokenizer::stateLoopReportTransit
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errAttributeValueMissing();
               addAttributeWithoutValue();
               state = mViewSource->Transition(emitCurrentTagToken(false, pos), reconsume, pos);
               if (shouldSuspend) {
                 NS_HTML5_BREAK(stateloop);
               }
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\0': {
               c = 0xfffd;
             }
             case '<':
             case '=':
-            case '`':
+            case '`': {
+              errLtOrEqualsOrGraveInUnquotedAttributeOrNull(c);
+            }
             default: {
               clearLongStrBufAndAppend(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED, reconsume, pos);
 
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
@@ -3667,17 +3673,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
             case '>': {
               state = mViewSource->Transition(emitCurrentTagToken(false, pos), reconsume, pos);
               if (shouldSuspend) {
                 NS_HTML5_BREAK(stateloop);
               }
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
-
+              errNoSpaceBetweenAttributes();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
         afterattributevaluequotedloop_end: ;
       }
@@ -3690,17 +3696,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           case '>': {
             state = mViewSource->Transition(emitCurrentTagToken(true, pos), reconsume, pos);
             if (shouldSuspend) {
               NS_HTML5_BREAK(stateloop);
             }
             NS_HTML5_CONTINUE(stateloop);
           }
           default: {
-
+            errSlashNotFollowedByGt();
             state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
             reconsume = true;
             NS_HTML5_CONTINUE(stateloop);
           }
         }
       }
       case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED: {
         for (; ; ) {
@@ -3746,17 +3752,19 @@ nsHtml5Tokenizer::stateLoopReportTransit
             }
             case '\0': {
               c = 0xfffd;
             }
             case '<':
             case '\"':
             case '\'':
             case '=':
-            case '`':
+            case '`': {
+              errUnquotedAttributeValOrNull(c);
+            }
             default: {
 
               appendLongStrBuf(c);
               continue;
             }
           }
         }
       }
@@ -3796,17 +3804,19 @@ nsHtml5Tokenizer::stateLoopReportTransit
               }
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\0': {
               c = 0xfffd;
             }
             case '\"':
             case '\'':
-            case '<':
+            case '<': {
+              errQuoteOrLtInAttributeNameOrNull(c);
+            }
             default: {
               addAttributeWithoutValue();
               if (c >= 'A' && c <= 'Z') {
                 c += 0x20;
               }
               clearStrBufAndAppend(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_NAME, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
@@ -3837,17 +3847,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               if (tokenHandler->cdataSectionAllowed()) {
                 clearLongStrBufAndAppend(c);
                 index = 0;
                 state = mViewSource->Transition(NS_HTML5TOKENIZER_CDATA_START, reconsume, pos);
                 NS_HTML5_CONTINUE(stateloop);
               }
             }
             default: {
-
+              errBogusComment();
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
         markupdeclarationopenloop_end: ;
@@ -3863,17 +3873,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               NS_HTML5_BREAK(stateloop);
             }
             case '-': {
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_START, reconsume, pos);
               NS_HTML5_BREAK(markupdeclarationhyphenloop);
             }
             default: {
-
+              errBogusComment();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
         markupdeclarationhyphenloop_end: ;
       }
@@ -3885,17 +3895,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           c = checkChar(buf, pos);
           switch(c) {
             case '-': {
               appendLongStrBuf(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_START_DASH, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errPrematureEndOfComment();
               emitComment(0, pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\r': {
               appendLongStrBufCarriageReturn();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
               NS_HTML5_BREAK(stateloop);
@@ -4004,17 +4014,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               NS_HTML5_BREAK(stateloop);
             }
             case '\n': {
               adjustDoubleHyphenAndAppendToLongStrBufLineFeed();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '!': {
-
+              errHyphenHyphenBang();
               appendLongStrBuf(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_END_BANG, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\0': {
               c = 0xfffd;
             }
             default: {
@@ -4069,17 +4079,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
         c = checkChar(buf, pos);
         switch(c) {
           case '-': {
             appendLongStrBuf(c);
             state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_END, reconsume, pos);
             NS_HTML5_CONTINUE(stateloop);
           }
           case '>': {
-
+            errPrematureEndOfComment();
             emitComment(1, pos);
             state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
             NS_HTML5_CONTINUE(stateloop);
           }
           case '\r': {
             appendLongStrBufCarriageReturn();
             state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
             NS_HTML5_BREAK(stateloop);
@@ -4104,17 +4114,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           if (index < 6) {
             if (c == nsHtml5Tokenizer::CDATA_LSQB[index]) {
               appendLongStrBuf(c);
             } else {
-
+              errBogusComment();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
             index++;
             continue;
           } else {
             cstart = pos;
@@ -4278,17 +4288,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
             if (c >= 'a' && c <= 'z') {
               firstCharKey = c - 'a' + 26;
             } else if (c >= 'A' && c <= 'Z') {
               firstCharKey = c - 'A';
             } else {
-
+              errNoNamedCharacterMatch();
               emitOrAppendStrBuf(returnState);
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos;
               }
               state = mViewSource->Transition(returnState, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
@@ -4309,17 +4319,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           PRInt32 hilo = 0;
           if (c <= 'z') {
             const PRInt32* row = nsHtml5NamedCharactersAccel::HILO_ACCEL[c];
             if (row) {
               hilo = row[firstCharKey];
             }
           }
           if (!hilo) {
-
+            errNoNamedCharacterMatch();
             emitOrAppendStrBuf(returnState);
             if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
               cstart = pos;
             }
             state = mViewSource->Transition(returnState, reconsume, pos);
             reconsume = true;
             NS_HTML5_CONTINUE(stateloop);
           }
@@ -4378,17 +4388,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           if (hi < lo) {
             NS_HTML5_BREAK(outer);
           }
           appendStrBuf(c);
           continue;
         }
         outer_end: ;
         if (candidate == -1) {
-
+          errNoNamedCharacterMatch();
           emitOrAppendStrBuf(returnState);
           if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
             cstart = pos;
           }
           state = mViewSource->Transition(returnState, reconsume, pos);
           reconsume = true;
           NS_HTML5_CONTINUE(stateloop);
         } else {
@@ -4397,17 +4407,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
             if ((returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
               PRUnichar ch;
               if (strBufMark == strBufLen) {
                 ch = c;
               } else {
                 ch = strBuf[strBufMark];
               }
               if (ch == '=' || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
-
+                errNoNamedCharacterMatch();
                 appendStrBufToLongStrBuf();
                 state = mViewSource->Transition(returnState, reconsume, pos);
                 reconsume = true;
                 NS_HTML5_CONTINUE(stateloop);
               }
             }
 
           }
@@ -4478,37 +4488,37 @@ nsHtml5Tokenizer::stateLoopReportTransit
           } else if (c == ';') {
             if (seenDigits) {
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos + 1;
               }
               state = mViewSource->Transition(NS_HTML5TOKENIZER_HANDLE_NCR_VALUE, reconsume, pos);
               NS_HTML5_BREAK(decimalloop);
             } else {
-
+              errNoDigitsInNCR();
               appendStrBuf(';');
               emitOrAppendStrBuf(returnState);
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos + 1;
               }
               state = mViewSource->Transition(returnState, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           } else {
             if (!seenDigits) {
-
+              errNoDigitsInNCR();
               emitOrAppendStrBuf(returnState);
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos;
               }
               state = mViewSource->Transition(returnState, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             } else {
-
+              errCharRefLacksSemicolon();
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos;
               }
               state = mViewSource->Transition(NS_HTML5TOKENIZER_HANDLE_NCR_VALUE, reconsume, pos);
               reconsume = true;
               NS_HTML5_BREAK(decimalloop);
             }
           }
@@ -4548,37 +4558,37 @@ nsHtml5Tokenizer::stateLoopReportTransit
           } else if (c == ';') {
             if (seenDigits) {
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos + 1;
               }
               state = mViewSource->Transition(NS_HTML5TOKENIZER_HANDLE_NCR_VALUE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             } else {
-
+              errNoDigitsInNCR();
               appendStrBuf(';');
               emitOrAppendStrBuf(returnState);
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos + 1;
               }
               state = mViewSource->Transition(returnState, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           } else {
             if (!seenDigits) {
-
+              errNoDigitsInNCR();
               emitOrAppendStrBuf(returnState);
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos;
               }
               state = mViewSource->Transition(returnState, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             } else {
-
+              errCharRefLacksSemicolon();
               if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
                 cstart = pos;
               }
               state = mViewSource->Transition(NS_HTML5TOKENIZER_HANDLE_NCR_VALUE, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
           }
@@ -4615,31 +4625,31 @@ nsHtml5Tokenizer::stateLoopReportTransit
       }
       case NS_HTML5TOKENIZER_CLOSE_TAG_OPEN: {
         if (++pos == endPos) {
           NS_HTML5_BREAK(stateloop);
         }
         c = checkChar(buf, pos);
         switch(c) {
           case '>': {
-
+            errLtSlashGt();
             cstart = pos + 1;
             state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
             NS_HTML5_CONTINUE(stateloop);
           }
           case '\r': {
             silentCarriageReturn();
-
+            errGarbageAfterLtSlash();
             clearLongStrBufAndAppend('\n');
             state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
             NS_HTML5_BREAK(stateloop);
           }
           case '\n': {
             silentLineFeed();
-
+            errGarbageAfterLtSlash();
             clearLongStrBufAndAppend('\n');
             state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
             NS_HTML5_CONTINUE(stateloop);
           }
           case '\0': {
             c = 0xfffd;
           }
           default: {
@@ -4647,17 +4657,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               c += 0x20;
             }
             if (c >= 'a' && c <= 'z') {
               endTag = true;
               clearStrBufAndAppend(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             } else {
-
+              errGarbageAfterLtSlash();
               clearLongStrBufAndAppend(c);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
       }
       case NS_HTML5TOKENIZER_RCDATA: {
@@ -5393,17 +5403,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           if (index < 6) {
             PRUnichar folded = c;
             if (c >= 'A' && c <= 'Z') {
               folded += 0x20;
             }
             if (folded == nsHtml5Tokenizer::OCTYPE[index]) {
               appendLongStrBuf(c);
             } else {
-
+              errBogusComment();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
               reconsume = true;
               NS_HTML5_CONTINUE(stateloop);
             }
             index++;
             continue;
           } else {
             state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE, reconsume, pos);
@@ -5435,17 +5445,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
             }
             case ' ':
             case '\t':
             case '\f': {
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME, reconsume, pos);
               NS_HTML5_BREAK(doctypeloop);
             }
             default: {
-
+              errMissingSpaceBeforeDoctypeName();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME, reconsume, pos);
               reconsume = true;
               NS_HTML5_BREAK(doctypeloop);
             }
           }
         }
         doctypeloop_end: ;
       }
@@ -5468,17 +5478,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               silentLineFeed();
             }
             case ' ':
             case '\t':
             case '\f': {
               continue;
             }
             case '>': {
-
+              errNamelessDoctype();
               forceQuirks = true;
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\0': {
               c = 0xfffd;
             }
@@ -5630,29 +5640,29 @@ nsHtml5Tokenizer::stateLoopReportTransit
             }
             case ' ':
             case '\t':
             case '\f': {
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER, reconsume, pos);
               NS_HTML5_BREAK(afterdoctypepublickeywordloop);
             }
             case '\"': {
-
+              errNoSpaceBetweenDoctypePublicKeywordAndQuote();
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
-
+              errNoSpaceBetweenDoctypePublicKeywordAndQuote();
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errExpectedPublicId();
               forceQuirks = true;
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
               bogusDoctype();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
@@ -5687,17 +5697,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               NS_HTML5_BREAK(beforedoctypepublicidentifierloop);
             }
             case '\'': {
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errExpectedPublicId();
               forceQuirks = true;
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
               bogusDoctype();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
@@ -5715,17 +5725,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           c = checkChar(buf, pos);
           switch(c) {
             case '\"': {
               publicIdentifier = longStrBufToString();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER, reconsume, pos);
               NS_HTML5_BREAK(doctypepublicidentifierdoublequotedloop);
             }
             case '>': {
-
+              errGtInPublicId();
               forceQuirks = true;
               publicIdentifier = longStrBufToString();
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\r': {
               appendLongStrBufCarriageReturn();
@@ -5768,23 +5778,23 @@ nsHtml5Tokenizer::stateLoopReportTransit
               NS_HTML5_BREAK(afterdoctypepublicidentifierloop);
             }
             case '>': {
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\"': {
-
+              errNoSpaceBetweenPublicAndSystemIds();
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
-
+              errNoSpaceBetweenPublicAndSystemIds();
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
               bogusDoctype();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
@@ -5844,17 +5854,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           c = checkChar(buf, pos);
           switch(c) {
             case '\"': {
               systemIdentifier = longStrBufToString();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errGtInSystemId();
               forceQuirks = true;
               systemIdentifier = longStrBufToString();
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\r': {
               appendLongStrBufCarriageReturn();
@@ -5985,29 +5995,29 @@ nsHtml5Tokenizer::stateLoopReportTransit
             }
             case ' ':
             case '\t':
             case '\f': {
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER, reconsume, pos);
               NS_HTML5_BREAK(afterdoctypesystemkeywordloop);
             }
             case '\"': {
-
+              errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
-
+              errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errExpectedPublicId();
               forceQuirks = true;
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
               bogusDoctype();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
@@ -6042,17 +6052,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\'': {
               clearLongStrBuf();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
               NS_HTML5_BREAK(beforedoctypesystemidentifierloop);
             }
             case '>': {
-
+              errExpectedSystemId();
               forceQuirks = true;
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             default: {
               bogusDoctype();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
@@ -6070,17 +6080,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           c = checkChar(buf, pos);
           switch(c) {
             case '\'': {
               systemIdentifier = longStrBufToString();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errGtInSystemId();
               forceQuirks = true;
               systemIdentifier = longStrBufToString();
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\r': {
               appendLongStrBufCarriageReturn();
@@ -6108,17 +6118,17 @@ nsHtml5Tokenizer::stateLoopReportTransit
           c = checkChar(buf, pos);
           switch(c) {
             case '\'': {
               publicIdentifier = longStrBufToString();
               state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '>': {
-
+              errGtInPublicId();
               forceQuirks = true;
               publicIdentifier = longStrBufToString();
               emitDoctypeToken(pos);
               state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\r': {
               appendLongStrBufCarriageReturn();
@@ -6191,24 +6201,24 @@ void
 nsHtml5Tokenizer::setAdditionalAndRememberAmpersandLocation(PRUnichar add)
 {
   additional = add;
 }
 
 void 
 nsHtml5Tokenizer::bogusDoctype()
 {
-
+  errBogusDoctype();
   forceQuirks = true;
 }
 
 void 
 nsHtml5Tokenizer::bogusDoctypeWithoutQuirks()
 {
-
+  errBogusDoctype();
   forceQuirks = false;
 }
 
 void 
 nsHtml5Tokenizer::emitOrAppendStrBuf(PRInt32 returnState)
 {
   if ((returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
     appendStrBufToLongStrBuf();
@@ -6217,36 +6227,36 @@ nsHtml5Tokenizer::emitOrAppendStrBuf(PRI
   }
 }
 
 void 
 nsHtml5Tokenizer::handleNcrValue(PRInt32 returnState)
 {
   if (value <= 0xFFFF) {
     if (value >= 0x80 && value <= 0x9f) {
-
+      errNcrInC1Range();
       PRUnichar* val = nsHtml5NamedCharacters::WINDOWS_1252[value - 0x80];
       emitOrAppendOne(val, returnState);
     } else if (value == 0x0) {
-
+      errNcrZero();
       emitOrAppendOne(nsHtml5Tokenizer::REPLACEMENT_CHARACTER, returnState);
     } else if ((value & 0xF800) == 0xD800) {
-
+      errNcrSurrogate();
       emitOrAppendOne(nsHtml5Tokenizer::REPLACEMENT_CHARACTER, returnState);
     } else {
       PRUnichar ch = (PRUnichar) value;
       bmpChar[0] = ch;
       emitOrAppendOne(bmpChar, returnState);
     }
   } else if (value <= 0x10FFFF) {
     astralChar[0] = (PRUnichar) (NS_HTML5TOKENIZER_LEAD_OFFSET + (value >> 10));
     astralChar[1] = (PRUnichar) (0xDC00 + (value & 0x3FF));
     emitOrAppendTwo(astralChar, returnState);
   } else {
-
+    errNcrOutOfRange();
     emitOrAppendOne(nsHtml5Tokenizer::REPLACEMENT_CHARACTER, returnState);
   }
 }
 
 void 
 nsHtml5Tokenizer::eof()
 {
   PRInt32 state = stateSave;
@@ -6254,84 +6264,84 @@ nsHtml5Tokenizer::eof()
   eofloop: for (; ; ) {
     switch(state) {
       case NS_HTML5TOKENIZER_SCRIPT_DATA_LESS_THAN_SIGN:
       case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN: {
         tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_TAG_OPEN: {
-
+        errEofAfterLt();
         tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN: {
         tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME: {
         tokenHandler->characters(nsHtml5Tokenizer::LT_SOLIDUS, 0, 2);
         emitStrBuf();
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_CLOSE_TAG_OPEN: {
-
+        errEofAfterLt();
         tokenHandler->characters(nsHtml5Tokenizer::LT_SOLIDUS, 0, 2);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_TAG_NAME: {
-
+        errEofInTagName();
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME:
       case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED:
       case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: {
-
+        errEofWithoutGt();
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_ATTRIBUTE_NAME: {
-
+        errEofInAttributeName();
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME:
       case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE: {
-
+        errEofWithoutGt();
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED:
       case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED:
       case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED: {
-
+        errEofInAttributeValue();
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_BOGUS_COMMENT: {
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN: {
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN: {
-
+        errBogusComment();
         clearLongStrBuf();
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_MARKUP_DECLARATION_HYPHEN: {
-
+        errBogusComment();
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_MARKUP_DECLARATION_OCTYPE: {
         if (index < 6) {
-
+          errBogusComment();
           emitComment(0, 0);
         } else {
-
+          errEofInDoctype();
           doctypeName = nsHtml5Atoms::emptystring;
           if (systemIdentifier) {
             nsHtml5Portability::releaseString(systemIdentifier);
             systemIdentifier = nsnull;
           }
           if (publicIdentifier) {
             nsHtml5Portability::releaseString(publicIdentifier);
             publicIdentifier = nsnull;
@@ -6339,102 +6349,102 @@ nsHtml5Tokenizer::eof()
           forceQuirks = true;
           emitDoctypeToken(0);
           NS_HTML5_BREAK(eofloop);
         }
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_COMMENT_START:
       case NS_HTML5TOKENIZER_COMMENT: {
-
+        errEofInComment();
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_COMMENT_END: {
-
+        errEofInComment();
         emitComment(2, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_COMMENT_END_DASH:
       case NS_HTML5TOKENIZER_COMMENT_START_DASH: {
-
+        errEofInComment();
         emitComment(1, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_COMMENT_END_BANG: {
-
+        errEofInComment();
         emitComment(3, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_DOCTYPE:
       case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME: {
-
+        errEofInDoctype();
         forceQuirks = true;
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_DOCTYPE_NAME: {
-
+        errEofInDoctype();
         strBufToDoctypeName();
         forceQuirks = true;
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_DOCTYPE_UBLIC:
       case NS_HTML5TOKENIZER_DOCTYPE_YSTEM:
       case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME:
       case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD:
       case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD:
       case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: {
-
+        errEofInDoctype();
         forceQuirks = true;
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:
       case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED: {
-
+        errEofInPublicId();
         forceQuirks = true;
         publicIdentifier = longStrBufToString();
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER:
       case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:
       case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: {
-
+        errEofInDoctype();
         forceQuirks = true;
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:
       case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED: {
-
+        errEofInSystemId();
         forceQuirks = true;
         systemIdentifier = longStrBufToString();
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER: {
-
+        errEofInDoctype();
         forceQuirks = true;
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_BOGUS_DOCTYPE: {
         emitDoctypeToken(0);
         NS_HTML5_BREAK(eofloop);
       }
       case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE: {
         emitOrAppendStrBuf(returnState);
         state = returnState;
         continue;
       }
       case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP: {
-
+        errNoNamedCharacterMatch();
         emitOrAppendStrBuf(returnState);
         state = returnState;
         continue;
       }
       case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL: {
         for (; ; ) {
           PRUnichar c = '\0';
           entCol++;
@@ -6473,32 +6483,32 @@ nsHtml5Tokenizer::eof()
           loloop_end: ;
           if (hi < lo) {
             NS_HTML5_BREAK(outer);
           }
           continue;
         }
         outer_end: ;
         if (candidate == -1) {
-
+          errNoNamedCharacterMatch();
           emitOrAppendStrBuf(returnState);
           state = returnState;
           NS_HTML5_CONTINUE(eofloop);
         } else {
           const nsHtml5CharacterName& candidateName = nsHtml5NamedCharacters::NAMES[candidate];
           if (!candidateName.length() || candidateName.charAt(candidateName.length() - 1) != ';') {
             if ((returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
               PRUnichar ch;
               if (strBufMark == strBufLen) {
                 ch = '\0';
               } else {
                 ch = strBuf[strBufMark];
               }
               if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
-
+                errNoNamedCharacterMatch();
                 appendStrBufToLongStrBuf();
                 state = returnState;
                 NS_HTML5_CONTINUE(eofloop);
               }
             }
 
           }
           const PRUnichar* val = nsHtml5NamedCharacters::VALUES[candidate];
@@ -6519,17 +6529,17 @@ nsHtml5Tokenizer::eof()
           state = returnState;
           NS_HTML5_CONTINUE(eofloop);
         }
       }
       case NS_HTML5TOKENIZER_CONSUME_NCR:
       case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP:
       case NS_HTML5TOKENIZER_HEX_NCR_LOOP: {
         if (!seenDigits) {
-
+          errNoDigitsInNCR();
           emitOrAppendStrBuf(returnState);
           state = returnState;
           continue;
         }
         handleNcrValue(returnState);
         state = returnState;
         continue;
       }
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -185,17 +185,17 @@ class nsHtml5Tokenizer
     void appendLongStrBuf(PRUnichar c);
     inline void appendSecondHyphenToBogusComment()
     {
       appendLongStrBuf('-');
     }
 
     inline void adjustDoubleHyphenAndAppendToLongStrBufAndErr(PRUnichar c)
     {
-
+      errConsecutiveHyphens();
       appendLongStrBuf(c);
     }
 
     void appendLongStrBuf(PRUnichar* buffer, PRInt32 offset, PRInt32 length);
     inline void appendStrBufToLongStrBuf()
     {
       appendLongStrBuf(strBuf, 0, strBufLen);
     }
--- a/parser/html/nsHtml5TokenizerCppSupplement.h
+++ b/parser/html/nsHtml5TokenizerCppSupplement.h
@@ -53,8 +53,445 @@ nsHtml5Tokenizer::StartViewSource()
   mViewSource->Start();
 }
 
 void
 nsHtml5Tokenizer::EndViewSource()
 {
   mViewSource->End();
 }
+
+void
+nsHtml5Tokenizer::errWarnLtSlashInRcdata()
+{
+}
+
+void
+nsHtml5Tokenizer::errUnquotedAttributeValOrNull(PRUnichar c)
+{
+  switch (c) {
+    case '<':
+      mViewSource->AddErrorToCurrentNode("errUnquotedAttributeLt");
+      return;
+    case '`':
+      mViewSource->AddErrorToCurrentNode("errUnquotedAttributeGrave");
+      return;
+    case '\'':
+    case '"':
+      mViewSource->AddErrorToCurrentNode("errUnquotedAttributeQuote");
+      return;
+    case '=':
+      mViewSource->AddErrorToCurrentNode("errUnquotedAttributeEquals");
+      return;
+  }
+}
+
+void
+nsHtml5Tokenizer::errLtOrEqualsOrGraveInUnquotedAttributeOrNull(PRUnichar c)
+{
+  switch (c) {
+    case '=':
+      mViewSource->AddErrorToCurrentNode("errUnquotedAttributeStartEquals");
+      return;
+    case '<':
+      mViewSource->AddErrorToCurrentNode("errUnquotedAttributeStartLt");
+      return;
+    case '`':
+      mViewSource->AddErrorToCurrentNode("errUnquotedAttributeStartGrave");
+      return;
+  }
+}
+
+void
+nsHtml5Tokenizer::errBadCharBeforeAttributeNameOrNull(PRUnichar c)
+{
+  if (c == '<') {
+    mViewSource->AddErrorToCurrentNode("errBadCharBeforeAttributeNameLt");
+  } else if (c == '=') {
+    errEqualsSignBeforeAttributeName();
+  } else if (c != 0xFFFD) {
+    errQuoteBeforeAttributeName(c);
+  }
+}
+
+void
+nsHtml5Tokenizer::errBadCharAfterLt(PRUnichar c)
+{
+  mViewSource->AddErrorToCurrentNode("errBadCharAfterLt");
+}
+
+void
+nsHtml5Tokenizer::errQuoteOrLtInAttributeNameOrNull(PRUnichar c)
+{
+  if (c == '<') {
+    mViewSource->AddErrorToCurrentNode("errLtInAttributeName");
+  } else if (c != 0xFFFD) {
+    mViewSource->AddErrorToCurrentNode("errQuoteInAttributeName");
+  }
+}
+
+void
+nsHtml5Tokenizer::maybeErrAttributesOnEndTag(nsHtml5HtmlAttributes* attrs)
+{
+  if (mViewSource && attrs->getLength() != 0) {
+    /*
+     * When an end tag token is emitted with attributes, that is a parse
+     * error.
+     */
+    mViewSource->AddErrorToCurrentMarkupDecl("maybeErrAttributesOnEndTag");
+  }
+}
+
+void
+nsHtml5Tokenizer::maybeErrSlashInEndTag(bool selfClosing)
+{
+  if (mViewSource && selfClosing && endTag) {
+    mViewSource->AddErrorToCurrentSlash("maybeErrSlashInEndTag");
+  }
+}
+
+PRUnichar
+nsHtml5Tokenizer::errNcrNonCharacter(PRUnichar ch)
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrNonCharacter");
+  }
+  return ch;
+}
+
+void
+nsHtml5Tokenizer::errAstralNonCharacter(int ch)
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errAstralNonCharacter");
+  }
+}
+
+PRUnichar
+nsHtml5Tokenizer::errNcrControlChar(PRUnichar ch)
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrControlChar");
+  }
+  return ch;
+}
+
+void
+nsHtml5Tokenizer::errGarbageAfterLtSlash()
+{
+  mViewSource->AddErrorToCurrentNode("errGarbageAfterLtSlash");
+}
+
+void
+nsHtml5Tokenizer::errLtSlashGt()
+{
+  mViewSource->AddErrorToCurrentNode("errLtSlashGt");
+}
+
+void
+nsHtml5Tokenizer::errCharRefLacksSemicolon()
+{
+  mViewSource->AddErrorToCurrentNode("errCharRefLacksSemicolon");
+}
+
+void
+nsHtml5Tokenizer::errNoDigitsInNCR()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNoDigitsInNCR");
+  }
+}
+
+void
+nsHtml5Tokenizer::errGtInSystemId()
+{
+  mViewSource->AddErrorToCurrentNode("errGtInSystemId");
+}
+
+void
+nsHtml5Tokenizer::errGtInPublicId()
+{
+  mViewSource->AddErrorToCurrentNode("errGtInPublicId");
+}
+
+void
+nsHtml5Tokenizer::errNamelessDoctype()
+{
+  mViewSource->AddErrorToCurrentNode("errNamelessDoctype");
+}
+
+void
+nsHtml5Tokenizer::errConsecutiveHyphens()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errConsecutiveHyphens");
+  }
+}
+
+void
+nsHtml5Tokenizer::errPrematureEndOfComment()
+{
+  mViewSource->AddErrorToCurrentNode("errPrematureEndOfComment");
+}
+
+void
+nsHtml5Tokenizer::errBogusComment()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errBogusComment");
+  }
+}
+
+void
+nsHtml5Tokenizer::errSlashNotFollowedByGt()
+{
+  mViewSource->AddErrorToCurrentSlash("errSlashNotFollowedByGt");
+}
+
+void
+nsHtml5Tokenizer::errNoSpaceBetweenAttributes()
+{
+  mViewSource->AddErrorToCurrentNode("errNoSpaceBetweenAttributes");
+}
+
+void
+nsHtml5Tokenizer::errAttributeValueMissing()
+{
+  mViewSource->AddErrorToCurrentNode("errAttributeValueMissing");
+}
+
+void
+nsHtml5Tokenizer::errEqualsSignBeforeAttributeName()
+{
+  mViewSource->AddErrorToCurrentNode("errEqualsSignBeforeAttributeName");
+}
+
+void
+nsHtml5Tokenizer::errLtGt()
+{
+  mViewSource->AddErrorToCurrentNode("errLtGt");
+}
+
+void
+nsHtml5Tokenizer::errProcessingInstruction()
+{
+  mViewSource->AddErrorToCurrentNode("errProcessingInstruction");
+}
+
+void
+nsHtml5Tokenizer::errUnescapedAmpersandInterpretedAsCharacterReference()
+{
+  mViewSource->AddErrorToCurrentAmpersand("errUnescapedAmpersandInterpretedAsCharacterReference");
+}
+
+void
+nsHtml5Tokenizer::errNotSemicolonTerminated()
+{
+  mViewSource->AddErrorToCurrentNode("errNotSemicolonTerminated");
+}
+
+void
+nsHtml5Tokenizer::errNoNamedCharacterMatch()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentAmpersand("errNoNamedCharacterMatch");
+  }
+}
+
+void
+nsHtml5Tokenizer::errQuoteBeforeAttributeName(PRUnichar c)
+{
+  mViewSource->AddErrorToCurrentNode("errQuoteBeforeAttributeName");
+}
+
+void
+nsHtml5Tokenizer::errExpectedPublicId()
+{
+  mViewSource->AddErrorToCurrentNode("errExpectedPublicId");
+}
+
+void
+nsHtml5Tokenizer::errBogusDoctype()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errBogusDoctype");
+  }
+}
+
+void
+nsHtml5Tokenizer::errNcrSurrogate()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrSurrogate");
+  }
+}
+
+void
+nsHtml5Tokenizer::errNcrCr()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrCr");
+  }
+}
+
+void
+nsHtml5Tokenizer::errNcrInC1Range()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrInC1Range");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInPublicId()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInPublicId");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInComment()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInComment");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInDoctype()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInDoctype");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInAttributeValue()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInAttributeValue");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInAttributeName()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInAttributeName");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofWithoutGt()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofWithoutGt");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInTagName()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInTagName");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInEndTag()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInEndTag");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofAfterLt()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofAfterLt");
+  }
+}
+
+void
+nsHtml5Tokenizer::errNcrOutOfRange()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrOutOfRange");
+  }
+}
+
+void
+nsHtml5Tokenizer::errNcrUnassigned()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrUnassigned");
+  }
+}
+
+void
+nsHtml5Tokenizer::errDuplicateAttribute()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errDuplicateAttribute");
+  }
+}
+
+void
+nsHtml5Tokenizer::errEofInSystemId()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentMarkupDecl("errEofInSystemId");
+  }
+}
+
+void
+nsHtml5Tokenizer::errExpectedSystemId()
+{
+  mViewSource->AddErrorToCurrentNode("errExpectedSystemId");
+}
+
+void
+nsHtml5Tokenizer::errMissingSpaceBeforeDoctypeName()
+{
+  mViewSource->AddErrorToCurrentNode("errMissingSpaceBeforeDoctypeName");
+}
+
+void
+nsHtml5Tokenizer::errHyphenHyphenBang()
+{
+  mViewSource->AddErrorToCurrentNode("errHyphenHyphenBang");
+}
+
+void
+nsHtml5Tokenizer::errNcrControlChar()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrControlChar");
+  }
+}
+
+void
+nsHtml5Tokenizer::errNcrZero()
+{
+  if (mViewSource) {
+    mViewSource->AddErrorToCurrentNode("errNcrZero");
+  }
+}
+
+void
+nsHtml5Tokenizer::errNoSpaceBetweenDoctypeSystemKeywordAndQuote()
+{
+  mViewSource->AddErrorToCurrentNode("errNoSpaceBetweenDoctypeSystemKeywordAndQuote");
+}
+
+void
+nsHtml5Tokenizer::errNoSpaceBetweenPublicAndSystemIds()
+{
+  mViewSource->AddErrorToCurrentNode("errNoSpaceBetweenPublicAndSystemIds");
+}
+
+void
+nsHtml5Tokenizer::errNoSpaceBetweenDoctypePublicKeywordAndQuote()
+{
+  mViewSource->AddErrorToCurrentNode("errNoSpaceBetweenDoctypePublicKeywordAndQuote");
+}
--- a/parser/html/nsHtml5TokenizerHSupplement.h
+++ b/parser/html/nsHtml5TokenizerHSupplement.h
@@ -39,8 +39,122 @@ nsAutoPtr<nsHtml5Highlighter> mViewSourc
 
 void EnableViewSource(nsHtml5Highlighter* aHighlighter);
 
 bool FlushViewSource();
 
 void StartViewSource();
 
 void EndViewSource();
+
+void errGarbageAfterLtSlash();
+
+void errLtSlashGt();
+
+void errWarnLtSlashInRcdata();
+
+void errCharRefLacksSemicolon();
+
+void errNoDigitsInNCR();
+
+void errGtInSystemId();
+
+void errGtInPublicId();
+
+void errNamelessDoctype();
+
+void errConsecutiveHyphens();
+
+void errPrematureEndOfComment();
+
+void errBogusComment();
+
+void errUnquotedAttributeValOrNull(PRUnichar c);
+
+void errSlashNotFollowedByGt();
+
+void errNoSpaceBetweenAttributes();
+
+void errLtOrEqualsOrGraveInUnquotedAttributeOrNull(PRUnichar c);
+
+void errAttributeValueMissing();
+
+void errBadCharBeforeAttributeNameOrNull(PRUnichar c);
+
+void errEqualsSignBeforeAttributeName();
+
+void errBadCharAfterLt(PRUnichar c);
+
+void errLtGt();
+
+void errProcessingInstruction();
+
+void errUnescapedAmpersandInterpretedAsCharacterReference();
+
+void errNotSemicolonTerminated();
+
+void errNoNamedCharacterMatch();
+
+void errQuoteBeforeAttributeName(PRUnichar c);
+
+void errQuoteOrLtInAttributeNameOrNull(PRUnichar c);
+
+void errExpectedPublicId();
+
+void errBogusDoctype();
+
+void maybeErrAttributesOnEndTag(nsHtml5HtmlAttributes* attrs);
+
+void maybeErrSlashInEndTag(bool selfClosing);
+
+PRUnichar errNcrNonCharacter(PRUnichar ch);
+
+void errAstralNonCharacter(PRInt32 ch);
+
+void errNcrSurrogate();
+
+PRUnichar errNcrControlChar(PRUnichar ch);
+
+void errNcrCr();
+
+void errNcrInC1Range();
+
+void errEofInPublicId();
+
+void errEofInComment();
+
+void errEofInDoctype();
+
+void errEofInAttributeValue();
+
+void errEofInAttributeName();
+
+void errEofWithoutGt();
+
+void errEofInTagName();
+
+void errEofInEndTag();
+
+void errEofAfterLt();
+
+void errNcrOutOfRange();
+
+void errNcrUnassigned();
+
+void errDuplicateAttribute();
+
+void errEofInSystemId();
+
+void errExpectedSystemId();
+
+void errMissingSpaceBeforeDoctypeName();
+
+void errHyphenHyphenBang();
+
+void errNcrControlChar();
+
+void errNcrZero();
+
+void errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
+
+void errNoSpaceBetweenPublicAndSystemIds();
+
+void errNoSpaceBetweenDoctypePublicKeywordAndQuote();
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -710,17 +710,25 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
         NS_WARNING("failed to dispatch svg load dispatcher");
       }
       return rv;
     }
     case eTreeOpAddClass: {
       nsIContent* node = *(mOne.node);
       PRUnichar* str = mTwo.unicharPtr;
       nsDependentString depStr(str);
-      node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, depStr, true);
+      nsAutoString klass;
+      node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass);
+      if (!klass.IsEmpty()) {
+        klass.Append(' ');
+        klass.Append(depStr);
+        node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true);
+      } else {
+        node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, depStr, true);
+      }
       return rv;
     }
     case eTreeOpAddViewSourceHref: {
       nsIContent* node = *mOne.node;
       PRUnichar* buffer = mTwo.unicharPtr;
       PRInt32 length = mInt;
 
       nsDependentString relative(buffer, length);
@@ -769,14 +777,45 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
       viewSourceUrl.Append(spec);
 
       nsAutoString utf16;
       CopyUTF8toUTF16(viewSourceUrl, utf16);
 
       node->SetAttr(kNameSpaceID_None, nsGkAtoms::href, utf16, true);
       return rv;
     }
+    case eTreeOpAddError: {
+      nsIContent* node = *(mOne.node);
+      char* msgId = mTwo.charPtr;
+      nsAutoString klass;
+      node->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass);
+      if (!klass.IsEmpty()) {
+        klass.Append(NS_LITERAL_STRING(" error"));
+        node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, klass, true);
+      } else {
+        node->SetAttr(kNameSpaceID_None,
+                      nsGkAtoms::_class,
+                      NS_LITERAL_STRING("error"),
+                      true);
+      }
+
+      nsXPIDLString message;
+      rv = nsContentUtils::GetLocalizedString(
+        nsContentUtils::eHTMLPARSER_PROPERTIES, msgId, message);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsAutoString title;
+      node->GetAttr(kNameSpaceID_None, nsGkAtoms::title, title);
+      if (!title.IsEmpty()) {
+        title.Append(' ');
+        title.Append(message);
+        node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, title, true);
+      } else {
+        node->SetAttr(kNameSpaceID_None, nsGkAtoms::title, message, true);
+      }
+      return rv;
+    }
     default: {
       NS_NOTREACHED("Bogus tree op");
     }
   }
   return rv; // keep compiler happy
 }
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -81,16 +81,17 @@ enum eHtml5TreeOperation {
   eTreeOpProcessOfflineManifest,
   eTreeOpMarkMalformedIfScript,
   eTreeOpStreamEnded,
   eTreeOpSetStyleLineNumber,
   eTreeOpSetScriptLineNumberAndFreeze,
   eTreeOpSvgLoad,
   eTreeOpAddClass,
   eTreeOpAddViewSourceHref,
+  eTreeOpAddError,
   eTreeOpStartLayout
 };
 
 class nsHtml5TreeOperationStringPair {
   private:
     nsString mPublicId;
     nsString mSystemId;
   public:
@@ -270,16 +271,25 @@ class nsHtml5TreeOperation {
                      const nsAString& aSystemId) {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
         "Op code must be uninitialized when initializing.");
       mOpCode = eTreeOpAppendDoctypeToDocument;
       mOne.atom = aName;
       mTwo.stringPair = new nsHtml5TreeOperationStringPair(aPublicId, aSystemId);
     }
     
+    inline void Init(nsIContent** aElement,
+                     const char* aMsgId) {
+      NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+        "Op code must be uninitialized when initializing.");
+      mOpCode = eTreeOpAddError;
+      mOne.node = aElement;
+      mTwo.charPtr = (char*)aMsgId;
+    }
+
     inline void Init(eHtml5TreeOperation aOpCode, const nsAString& aString) {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
         "Op code must be uninitialized when initializing.");
 
       PRUnichar* str = NS_StringCloneData(aString);
       mOpCode = aOpCode;
       mOne.unicharPtr = str;
     }