From aa8cafb38c5f13a48ac8bf557e28d1ced99b0cd2 Mon Sep 17 00:00:00 2001 From: Henri Sivonen Date: Fri, 30 Jul 2010 13:15:38 +0300 Subject: [PATCH] Bug 482921 part 1 - Implement HTML syntax highlighting using the new parser. r=Olli.Pettay. --- content/html/document/src/nsHTMLDocument.cpp | 8 +- parser/html/Makefile.in | 1 + parser/html/javasrc/Tokenizer.java | 13 + parser/html/nsHtml5AttributeName.cpp | 6 +- parser/html/nsHtml5AttributeName.h | 6 +- parser/html/nsHtml5ElementName.cpp | 6 +- parser/html/nsHtml5ElementName.h | 6 +- parser/html/nsHtml5Highlighter.cpp | 676 ++++ parser/html/nsHtml5Highlighter.h | 349 +++ parser/html/nsHtml5HtmlAttributes.cpp | 6 +- parser/html/nsHtml5HtmlAttributes.h | 6 +- parser/html/nsHtml5MetaScanner.cpp | 6 +- parser/html/nsHtml5MetaScanner.h | 6 +- parser/html/nsHtml5Parser.cpp | 18 +- parser/html/nsHtml5Parser.h | 5 +- parser/html/nsHtml5Portability.h | 6 +- parser/html/nsHtml5StackNode.cpp | 6 +- parser/html/nsHtml5StackNode.h | 6 +- parser/html/nsHtml5StateSnapshot.cpp | 6 +- parser/html/nsHtml5StateSnapshot.h | 6 +- parser/html/nsHtml5StreamParser.cpp | 56 +- parser/html/nsHtml5StreamParser.h | 30 +- parser/html/nsHtml5Tokenizer.cpp | 2891 +++++++++++++++++- parser/html/nsHtml5Tokenizer.h | 8 +- parser/html/nsHtml5TokenizerCppSupplement.h | 60 + parser/html/nsHtml5TokenizerHSupplement.h | 46 + parser/html/nsHtml5TreeBuilder.cpp | 1 - parser/html/nsHtml5TreeBuilder.h | 1 - parser/html/nsHtml5TreeOpExecutor.cpp | 18 + parser/html/nsHtml5TreeOpExecutor.h | 4 + parser/html/nsHtml5TreeOperation.cpp | 67 + parser/html/nsHtml5TreeOperation.h | 13 + parser/html/nsHtml5UTF16Buffer.cpp | 6 +- parser/html/nsHtml5UTF16Buffer.h | 6 +- parser/htmlparser/public/nsIParser.h | 2 +- parser/htmlparser/src/nsParser.cpp | 2 +- parser/htmlparser/src/nsParser.h | 2 +- 37 files changed, 4254 insertions(+), 107 deletions(-) create mode 100644 parser/html/nsHtml5Highlighter.cpp create mode 100644 parser/html/nsHtml5Highlighter.h create mode 100644 parser/html/nsHtml5TokenizerCppSupplement.h create mode 100644 parser/html/nsHtml5TokenizerHSupplement.h diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index 88345f848bb..683172860fa 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -576,7 +576,8 @@ nsHTMLDocument::StartAutodetection(nsIDocShell *aDocShell, nsACString& aCharset, if (mIsRegularHTML && nsHtml5Module::sEnabled && aCommand && - !nsCRT::strcmp(aCommand, "view")) { + (!nsCRT::strcmp(aCommand, "view") || + !nsCRT::strcmp(aCommand, "view-source"))) { return; // the HTML5 parser uses chardet directly } nsCOMPtr cdetflt; @@ -675,7 +676,8 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand, if (loadAsHtml5 && !(contentType.EqualsLiteral("text/html") && aCommand && - !nsCRT::strcmp(aCommand, "view"))) { + (!nsCRT::strcmp(aCommand, "view") || + !nsCRT::strcmp(aCommand, "view-source")))) { loadAsHtml5 = false; } @@ -728,7 +730,7 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand, if (needsParser) { if (loadAsHtml5) { mParser = nsHtml5Module::NewHtml5Parser(); - mParser->MarkAsNotScriptCreated(); + mParser->MarkAsNotScriptCreated(aCommand); } else { mParser = do_CreateInstance(kCParserCID, &rv); NS_ENSURE_SUCCESS(rv, rv); diff --git a/parser/html/Makefile.in b/parser/html/Makefile.in index 2b90a7fc738..c5959eddfc2 100644 --- a/parser/html/Makefile.in +++ b/parser/html/Makefile.in @@ -107,6 +107,7 @@ CPPSRCS = \ nsHtml5Speculation.cpp \ nsHtml5SpeculativeLoad.cpp \ nsHtml5SVGLoadDispatcher.cpp \ + nsHtml5Highlighter.cpp \ $(NULL) FORCE_STATIC_LIB = 1 diff --git a/parser/html/javasrc/Tokenizer.java b/parser/html/javasrc/Tokenizer.java index 690f9425467..6e352074023 100644 --- a/parser/html/javasrc/Tokenizer.java +++ b/parser/html/javasrc/Tokenizer.java @@ -1220,6 +1220,9 @@ public class Tokenizer implements Locator { if (attributeName != null) { String val = longStrBufToString(); // Ownership transferred to // HtmlAttributes + // CPPONLY: if (mViewSource) { + // CPPONLY: mViewSource.MaybeLinkifyAttributeValue(attributeName, val); + // CPPONLY: } // [NOCPP[ if (!endTag && html4 && html4ModeCompatibleWithXhtml1Schemata && attributeName.isCaseFolded()) { @@ -1316,8 +1319,17 @@ public class Tokenizer implements Locator { * meaning. (The rest of the array is garbage and should not be * examined.) */ + // CPPONLY: if (mViewSource) { + // CPPONLY: mViewSource.SetBuffer(buffer); + // CPPONLY: pos = stateLoopReportTransitions(state, c, pos, buffer.getBuffer(), false, returnState, buffer.getEnd()); + // CPPONLY: mViewSource.DropBuffer((pos == buffer.getEnd()) ? pos : pos + 1); + // CPPONLY: } else { + // CPPONLY: pos = stateLoop(state, c, pos, buffer.getBuffer(), false, returnState, buffer.getEnd()); + // CPPONLY: } + // [NOCPP[ pos = stateLoop(state, c, pos, buffer.getBuffer(), false, returnState, buffer.getEnd()); + // ]NOCPP] if (pos == buffer.getEnd()) { // exiting due to end of buffer buffer.setStart(pos); @@ -3149,6 +3161,7 @@ public class Tokenizer implements Locator { * second column of the named character references * table). */ + // CPPONLY: mViewSource.CompletedNamedCharacterReference(); @Const @NoLength char[] val = NamedCharacters.VALUES[candidate]; if ( // [NOCPP[ diff --git a/parser/html/nsHtml5AttributeName.cpp b/parser/html/nsHtml5AttributeName.cpp index ae2e0655548..c911fa23579 100644 --- a/parser/html/nsHtml5AttributeName.cpp +++ b/parser/html/nsHtml5AttributeName.cpp @@ -33,17 +33,13 @@ #include "nsString.h" #include "nsINameSpaceManager.h" #include "nsIContent.h" -#include "nsIDocument.h" #include "nsTraceRefcnt.h" #include "jArray.h" -#include "nsHtml5DocumentMode.h" #include "nsHtml5ArrayCopy.h" -#include "nsHtml5NamedCharacters.h" -#include "nsHtml5NamedCharactersAccel.h" +#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Atoms.h" #include "nsHtml5ByteReadable.h" #include "nsIUnicodeDecoder.h" -#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Macros.h" #include "nsHtml5Tokenizer.h" diff --git a/parser/html/nsHtml5AttributeName.h b/parser/html/nsHtml5AttributeName.h index ab244bb7e90..e3dfabc1496 100644 --- a/parser/html/nsHtml5AttributeName.h +++ b/parser/html/nsHtml5AttributeName.h @@ -34,17 +34,13 @@ #include "nsString.h" #include "nsINameSpaceManager.h" #include "nsIContent.h" -#include "nsIDocument.h" #include "nsTraceRefcnt.h" #include "jArray.h" -#include "nsHtml5DocumentMode.h" #include "nsHtml5ArrayCopy.h" -#include "nsHtml5NamedCharacters.h" -#include "nsHtml5NamedCharactersAccel.h" +#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Atoms.h" #include "nsHtml5ByteReadable.h" #include "nsIUnicodeDecoder.h" -#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Macros.h" class nsHtml5StreamParser; diff --git a/parser/html/nsHtml5ElementName.cpp b/parser/html/nsHtml5ElementName.cpp index 0d37a22b33f..68f236dfdee 100644 --- a/parser/html/nsHtml5ElementName.cpp +++ b/parser/html/nsHtml5ElementName.cpp @@ -33,17 +33,13 @@ #include "nsString.h" #include "nsINameSpaceManager.h" #include "nsIContent.h" -#include "nsIDocument.h" #include "nsTraceRefcnt.h" #include "jArray.h" -#include "nsHtml5DocumentMode.h" #include "nsHtml5ArrayCopy.h" -#include "nsHtml5NamedCharacters.h" -#include "nsHtml5NamedCharactersAccel.h" +#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Atoms.h" #include "nsHtml5ByteReadable.h" #include "nsIUnicodeDecoder.h" -#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Macros.h" #include "nsHtml5Tokenizer.h" diff --git a/parser/html/nsHtml5ElementName.h b/parser/html/nsHtml5ElementName.h index dd775a6e2c6..2b2d9b010d6 100644 --- a/parser/html/nsHtml5ElementName.h +++ b/parser/html/nsHtml5ElementName.h @@ -34,17 +34,13 @@ #include "nsString.h" #include "nsINameSpaceManager.h" #include "nsIContent.h" -#include "nsIDocument.h" #include "nsTraceRefcnt.h" #include "jArray.h" -#include "nsHtml5DocumentMode.h" #include "nsHtml5ArrayCopy.h" -#include "nsHtml5NamedCharacters.h" -#include "nsHtml5NamedCharactersAccel.h" +#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Atoms.h" #include "nsHtml5ByteReadable.h" #include "nsIUnicodeDecoder.h" -#include "nsAHtml5TreeBuilderState.h" #include "nsHtml5Macros.h" class nsHtml5StreamParser; diff --git a/parser/html/nsHtml5Highlighter.cpp b/parser/html/nsHtml5Highlighter.cpp new file mode 100644 index 00000000000..92fcf9a20f9 --- /dev/null +++ b/parser/html/nsHtml5Highlighter.cpp @@ -0,0 +1,676 @@ +/* ***** 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 HTML5 View Source code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Henri Sivonen + * + * 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 ***** */ + +#include "nsHtml5Highlighter.h" +#include "nsDebug.h" +#include "nsHtml5Tokenizer.h" +#include "nsHtml5AttributeName.h" +#include "nsString.h" +#include "nsThreadUtils.h" +#include "mozilla/Preferences.h" + +using namespace mozilla; + +PRUnichar nsHtml5Highlighter::sComment[] = + { 'c', 'o', 'm', 'm', 'e', 'n', 't', 0 }; + +PRUnichar nsHtml5Highlighter::sCdata[] = + { 'c', 'd', 'a', 't', 'a', 0 }; + +PRUnichar nsHtml5Highlighter::sEntity[] = + { 'e', 'n', 't', 'i', 't', 'y', 0 }; + +PRUnichar nsHtml5Highlighter::sEndTag[] = + { 'e', 'n', 'd', '-', 't', 'a', 'g', 0 }; + +PRUnichar nsHtml5Highlighter::sStartTag[] = + { 's', 't', 'a', 'r', 't', '-', 't', 'a', 'g', 0 }; + +PRUnichar nsHtml5Highlighter::sAttributeName[] = + { 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', 'n', 'a', 'm', 'e', 0 }; + +PRUnichar nsHtml5Highlighter::sAttributeValue[] = + { 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', '-', + 'v', 'a', 'l', 'u', 'e', 0 }; + +PRUnichar nsHtml5Highlighter::sDoctype[] = + { 'd', 'o', 'c', 't', 'y', 'p', 'e', 0 }; + +nsHtml5Highlighter::nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink) + : mState(NS_HTML5TOKENIZER_DATA) + , mCStart(PR_INT32_MAX) + , mPos(0) + , mInlinesOpen(0) + , mBuffer(nsnull) + , mSyntaxHighlight(Preferences::GetBool("view_source.syntax_highlight", + true)) + , mWrapLongLines(Preferences::GetBool("view_source.wrap_long_lines", true)) + , mTabSize(Preferences::GetInt("view_source.tab_size", 4)) + , mOpSink(aOpSink) + , mHandles(new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH]) + , mHandlesUsed(0) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +} + +nsHtml5Highlighter::~nsHtml5Highlighter() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +} + +void +nsHtml5Highlighter::Start() +{ + // Doctype + mOpQueue.AppendElement()->Init(nsGkAtoms::html, EmptyString(), EmptyString()); + + mOpQueue.AppendElement()->Init(STANDARDS_MODE); + + nsIContent** root = CreateElement(nsHtml5Atoms::html, nsnull); + mOpQueue.AppendElement()->Init(eTreeOpAppendToDocument, root); + mStack.AppendElement(root); + + Push(nsGkAtoms::head, nsnull); + + Push(nsGkAtoms::title, nsnull); + // XUL will add the "Source of: " prefix. + AppendCharacters(mURL.get(), 0, mURL.Length()); + Pop(); // title + + nsHtml5HtmlAttributes* linkAttrs = new nsHtml5HtmlAttributes(0); + nsString* rel = new nsString(NS_LITERAL_STRING("stylesheet")); + linkAttrs->addAttribute(nsHtml5AttributeName::ATTR_REL, rel); + nsString* type = new nsString(NS_LITERAL_STRING("text/css")); + linkAttrs->addAttribute(nsHtml5AttributeName::ATTR_TYPE, type); + nsString* href = new nsString( + NS_LITERAL_STRING("resource://gre-resources/viewsource.css")); + linkAttrs->addAttribute(nsHtml5AttributeName::ATTR_HREF, href); + Push(nsGkAtoms::link, linkAttrs); + + mOpQueue.AppendElement()->Init(eTreeOpUpdateStyleSheet, CurrentNode()); + + Pop(); // link + + Pop(); // head + + nsHtml5HtmlAttributes* bodyAttrs = new nsHtml5HtmlAttributes(0); + nsString* id = new nsString(NS_LITERAL_STRING("viewsource")); + bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, id); + + if (mWrapLongLines) { + nsString* klass = new nsString(NS_LITERAL_STRING("wrap")); + bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_CLASS, klass); + } + + if (mTabSize > 0) { + nsString* style = new nsString(NS_LITERAL_STRING("-moz-tab-size: ")); + style->AppendInt(mTabSize); + bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_CLASS, style); + } + + Push(nsGkAtoms::body, bodyAttrs); + + nsHtml5HtmlAttributes* preAttrs = new nsHtml5HtmlAttributes(0); + nsString* preId = new nsString(NS_LITERAL_STRING("line1")); + preAttrs->addAttribute(nsHtml5AttributeName::ATTR_ID, preId); + Push(nsGkAtoms::pre, preAttrs); + + mOpQueue.AppendElement()->Init(eTreeOpStartLayout); +} + +PRInt32 +nsHtml5Highlighter::Transition(PRInt32 aState, bool aReconsume, PRInt32 aPos) +{ + mPos = aPos; + 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(); + break; + case NS_HTML5TOKENIZER_TAG_OPEN: + switch (aState) { + case NS_HTML5TOKENIZER_TAG_NAME: + StartSpan(sStartTag); + break; + case NS_HTML5TOKENIZER_DATA: + EndInline(); // DATA + break; + } + break; + 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 + 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 + 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 + break; + default: + FinishTag(); + break; + } + break; + case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE: + switch (aState) { + case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED: + case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED: + FlushCurrent(); + StartA(); + break; + case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED: + StartA(); + break; + default: + FinishTag(); + break; + } + break; + 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(); + 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 + break; + default: + FinishTag(); + break; + } + break; + case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: + 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(); + 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 + break; + case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE: + break; + case NS_HTML5TOKENIZER_ATTRIBUTE_NAME: + StartSpan(sAttributeName); + break; + default: + FinishTag(); + break; + } + break; + // most comment states are omitted, because they don't matter to + // highlighting + case NS_HTML5TOKENIZER_COMMENT_END: + case NS_HTML5TOKENIZER_COMMENT_END_BANG: + case NS_HTML5TOKENIZER_COMMENT_START_DASH: + case NS_HTML5TOKENIZER_BOGUS_COMMENT: + case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN: + if (aState == NS_HTML5TOKENIZER_DATA) { + AddClass(sComment); + FinishTag(); + } + break; + // most cdata states are omitted, because they don't matter to + // highlighting + case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB: + if (aState == NS_HTML5TOKENIZER_DATA) { + AddClass(sCdata); + FinishTag(); + } + break; + case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE: + switch (aState) { + case NS_HTML5TOKENIZER_CONSUME_NCR: + case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP: + break; + default: + // not actually a character reference + EndInline(); + break; + } + break; + 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) { + case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE: + AddClass(sEntity); + FlushCurrent(); + break; + case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE_RECONSUME: + AddClass(sEntity); + break; + } + EndInline(); + break; + case NS_HTML5TOKENIZER_CLOSE_TAG_OPEN: + switch (aState) { + case NS_HTML5TOKENIZER_DATA: + FinishTag(); + break; + case NS_HTML5TOKENIZER_TAG_NAME: + StartSpan(sEndTag); + break; + } + break; + case NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN: + if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) { + FlushCurrent(); + StartSpan(); // don't know if it is "end-tag" yet :-( + break; + } + EndInline(); + break; + case NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME: + switch (aState) { + 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 + break; + case NS_HTML5TOKENIZER_DATA: // yes, as a result of emitting the token + AddClass(sEndTag); + FinishTag(); + break; + default: + FinishTag(); + break; + } + break; + case NS_HTML5TOKENIZER_SCRIPT_DATA_LESS_THAN_SIGN: + case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN: + if (aState == NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME) { + break; + } + FinishTag(); + break; + case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH_DASH: + case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED: + case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH: + if (aState == NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN) { + StartSpan(); + } + break; + // Lots of double escape states omitted, because they don't highlight. + // Likewise, only doctype states that can emit the doctype are of + // interest. Otherwise, the transition out of bogus comment deals. + case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME: + case NS_HTML5TOKENIZER_DOCTYPE_NAME: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD: + case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: + case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER: + case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: + case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER: + case NS_HTML5TOKENIZER_BOGUS_DOCTYPE: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD: + case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER: + case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED: + case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED: + if (aState == NS_HTML5TOKENIZER_DATA) { + AddClass(sDoctype); + FinishTag(); + } + break; + default: + break; + } + mState = aState; + return aState; +} + +void +nsHtml5Highlighter::End() +{ + switch (mState) { + case NS_HTML5TOKENIZER_COMMENT_END: + case NS_HTML5TOKENIZER_COMMENT_END_BANG: + case NS_HTML5TOKENIZER_COMMENT_START_DASH: + case NS_HTML5TOKENIZER_BOGUS_COMMENT: + case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN: + AddClass(sComment); + break; + case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB: + AddClass(sCdata); + break; + case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP: + case NS_HTML5TOKENIZER_HEX_NCR_LOOP: + // XXX need tokenizer help here + break; + case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME: + case NS_HTML5TOKENIZER_DOCTYPE_NAME: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD: + case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: + case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER: + case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: + case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER: + case NS_HTML5TOKENIZER_BOGUS_DOCTYPE: + case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD: + 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; + } + FlushOps(); +} + +void +nsHtml5Highlighter::SetBuffer(nsHtml5UTF16Buffer* aBuffer) +{ + NS_PRECONDITION(!mBuffer, "Old buffer still here!"); + mBuffer = aBuffer; + mCStart = aBuffer->getStart(); +} + +void +nsHtml5Highlighter::DropBuffer(PRInt32 aPos) +{ + NS_PRECONDITION(mBuffer, "No buffer to drop!"); + mPos = aPos; + FlushChars(); + mBuffer = nsnull; +} + +void +nsHtml5Highlighter::StartSpan() +{ + FlushChars(); + Push(nsGkAtoms::span, nsnull); + ++mInlinesOpen; +} + +void +nsHtml5Highlighter::StartSpan(const PRUnichar* aClass) +{ + StartSpan(); + AddClass(aClass); +} + +void +nsHtml5Highlighter::EndInline() +{ + FlushChars(); + Pop(); + --mInlinesOpen; +} + +void +nsHtml5Highlighter::StartA() +{ + FlushChars(); + Push(nsGkAtoms::a, nsnull); + AddClass(sAttributeValue); + ++mInlinesOpen; +} + +void +nsHtml5Highlighter::FinishTag() +{ + while (mInlinesOpen > 1) { + EndInline(); + } + FlushCurrent(); // > + EndInline(); // DATA + NS_ASSERTION(!mInlinesOpen, "mInlinesOpen got out of sync!"); +} + +void +nsHtml5Highlighter::FlushChars() +{ + if (mPos > mCStart) { + AppendCharacters(mBuffer->getBuffer(), mCStart, mPos - mCStart); + mCStart = mPos; + } +} + +void +nsHtml5Highlighter::FlushCurrent() +{ + mPos++; + FlushChars(); +} + +bool +nsHtml5Highlighter::FlushOps() +{ + bool hasOps = !mOpQueue.IsEmpty(); + if (hasOps) { + mOpSink->MoveOpsFrom(mOpQueue); + } + return hasOps; +} + +void +nsHtml5Highlighter::MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName, + nsString* aValue) +{ + if (!(nsHtml5AttributeName::ATTR_HREF == aName + || nsHtml5AttributeName::ATTR_SRC == aName + || nsHtml5AttributeName::ATTR_ACTION == aName + || nsHtml5AttributeName::ATTR_CITE == aName + || nsHtml5AttributeName::ATTR_BACKGROUND == aName + || nsHtml5AttributeName::ATTR_LONGDESC == aName + || nsHtml5AttributeName::ATTR_XLINK_HREF == aName + || nsHtml5AttributeName::ATTR_DEFINITIONURL == aName)) { + return; + } + AddViewSourceHref(*aValue); +} + +void +nsHtml5Highlighter::CompletedNamedCharacterReference() +{ + AddClass(sEntity); +} + +nsIContent** +nsHtml5Highlighter::AllocateContentHandle() +{ + if (mHandlesUsed == NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH) { + mOldHandles.AppendElement(mHandles.forget()); + mHandles = new nsIContent*[NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH]; + mHandlesUsed = 0; + } +#ifdef DEBUG + mHandles[mHandlesUsed] = (nsIContent*)0xC0DEDBAD; +#endif + return &mHandles[mHandlesUsed++]; +} + +nsIContent** +nsHtml5Highlighter::CreateElement(nsIAtom* aName, + nsHtml5HtmlAttributes* aAttributes) +{ + NS_PRECONDITION(aName, "Got null name."); + nsIContent** content = AllocateContentHandle(); + mOpQueue.AppendElement()->Init(kNameSpaceID_XHTML, + aName, + aAttributes, + content, + true); + return content; +} + +nsIContent** +nsHtml5Highlighter::CurrentNode() +{ + NS_PRECONDITION(mStack.Length() >= 1, "Must have something on stack."); + return mStack[mStack.Length() - 1]; +} + +void +nsHtml5Highlighter::Push(nsIAtom* aName, + nsHtml5HtmlAttributes* aAttributes) +{ + NS_PRECONDITION(mStack.Length() >= 1, "Pushing without root."); + nsIContent** elt = CreateElement(aName, aAttributes); // Don't inline below! + mOpQueue.AppendElement()->Init(eTreeOpAppend, elt, CurrentNode()); + mStack.AppendElement(elt); +} + +void +nsHtml5Highlighter::Pop() +{ + NS_PRECONDITION(mStack.Length() >= 2, "Popping when stack too short."); + mStack.RemoveElementAt(mStack.Length() - 1); +} + +void +nsHtml5Highlighter::AppendCharacters(const PRUnichar* aBuffer, + PRInt32 aStart, + PRInt32 aLength) +{ + NS_PRECONDITION(aBuffer, "Null buffer"); + + PRUnichar* bufferCopy = new PRUnichar[aLength]; + memcpy(bufferCopy, aBuffer + aStart, aLength * sizeof(PRUnichar)); + + mOpQueue.AppendElement()->Init(eTreeOpAppendText, + bufferCopy, + aLength, + CurrentNode()); +} + + +void +nsHtml5Highlighter::AddClass(const PRUnichar* aClass) +{ + mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass); +} + +void +nsHtml5Highlighter::AddViewSourceHref(const nsString& aValue) +{ + PRUnichar* bufferCopy = new PRUnichar[aValue.Length() + 1]; + memcpy(bufferCopy, aValue.get(), aValue.Length() * sizeof(PRUnichar)); + bufferCopy[aValue.Length()] = 0; + + mOpQueue.AppendElement()->Init(eTreeOpAddViewSourceHref, + bufferCopy, + aValue.Length(), + CurrentNode()); +} diff --git a/parser/html/nsHtml5Highlighter.h b/parser/html/nsHtml5Highlighter.h new file mode 100644 index 00000000000..123431423b5 --- /dev/null +++ b/parser/html/nsHtml5Highlighter.h @@ -0,0 +1,349 @@ +/* ***** 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 HTML5 View Source code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Henri Sivonen + * + * 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 ***** */ +#ifndef nsHtml5Highlighter_h_ +#define nsHtml5Highlighter_h_ + +#include "prtypes.h" +#include "nsCOMPtr.h" +#include "nsHtml5TreeOperation.h" +#include "nsHtml5UTF16Buffer.h" +#include "nsHtml5TreeOperation.h" +#include "nsAHtml5TreeOpSink.h" + +#define NS_HTML5_HIGHLIGHTER_HANDLE_ARRAY_LENGTH 512 + +/** + * A state machine for generating HTML for display in View Source based on + * the transitions the tokenizer makes on the source being viewed. + */ +class nsHtml5Highlighter +{ + public: + /** + * The constructor. + * + * @param aOpSink the sink for the tree ops generated by this highlighter + */ + nsHtml5Highlighter(nsAHtml5TreeOpSink* aOpSink); + + /** + * The destructor. + */ + ~nsHtml5Highlighter(); + + /** + * Starts the generated document. + */ + void Start(); + + /** + * Report a tokenizer state transition. + * + * @param aState the state being transitioned to + * @param aReconsume whether this is a reconsuming transition + * @param aPos the tokenizer's current position into the buffer + */ + PRInt32 Transition(PRInt32 aState, bool aReconsume, PRInt32 aPos); + + /** + * Report end of file. + */ + void End(); + + /** + * Set the current buffer being tokenized + */ + void SetBuffer(nsHtml5UTF16Buffer* aBuffer); + + /** + * Let go of the buffer being tokenized but first, flush text from it. + * + * @param aPos the first UTF-16 code unit not to flush + */ + void DropBuffer(PRInt32 aPos); + + /** + * Flush the tree ops into the sink. + * + * @return true if there were ops to flush + */ + bool FlushOps(); + + /** + * Linkify the current attribute value if the attribute name is one of + * known URL attributes. (When executing tree ops, javascript: URLs will + * not be linkified, though.) + * + * @param aName the name of the attribute + * @param aValue the value of the attribute + */ + void MaybeLinkifyAttributeValue(nsHtml5AttributeName* aName, + nsString* aValue); + + /** + * Inform the highlighter that the tokenizer successfully completed a + * named character reference. + */ + void CompletedNamedCharacterReference(); + + private: + + /** + * Starts a span with no class. + */ + void StartSpan(); + + /** + * Starts a and sets the class attribute on it. + * + * @param aClass the class to set (MUST be a static string that does not + * need to be released!) + */ + void StartSpan(const PRUnichar* aClass); + + /** + * End the current or . + */ + void EndInline(); + + /** + * Starts an . + */ + void StartA(); + + /** + * Flushes characters up to but not including the current one. + */ + void FlushChars(); + + /** + * Flushes characters up to and including the current one. + */ + void FlushCurrent(); + + /** + * Finishes a source tag being highlighted by closing the open and + * elements. + */ + void FinishTag(); + + /** + * Adds a class attribute to the current node. + * + * @param aClass the class to set (MUST be a static string that does not + * need to be released!) + */ + void AddClass(const PRUnichar* aClass); + + /** + * Allocates a handle for an element. + * + * @return the handle + */ + nsIContent** AllocateContentHandle(); + + /** + * Enqueues an element creation tree operation. + * + * @param aName the name of the element + * @param aAttributes the attribute holder (ownership will be taken) or + * nsnull for no attributes + * @return the handle for the element that will be created + */ + nsIContent** CreateElement(nsIAtom* aName, + nsHtml5HtmlAttributes* aAttributes); + + /** + * Gets the handle for the current node. May be called only after the + * root element has been set. + * + * @return the handle for the current node + */ + nsIContent** CurrentNode(); + + /** + * Create an element and push it (its handle) on the stack. + * + * @param aName the name of the element + * @param aAttributes the attribute holder (ownership will be taken) or + * nsnull for no attributes + */ + void Push(nsIAtom* aName, nsHtml5HtmlAttributes* aAttributes); + + /** + * Pops the current node off the stack. + */ + void Pop(); + + /** + * Appends text content to the current node. + * + * @param aBuffer the buffer to copy from + * @param aStart the index of the first code unit to copy + * @param aLength the number of code units to copy + */ + void AppendCharacters(const PRUnichar* aBuffer, + PRInt32 aStart, + PRInt32 aLength); + + /** + * Enqueues a tree op for adding an href attribute with the view-source: + * URL scheme to the current node. + * + * @param aValue the (potentially relative) URL to link to + */ + void AddViewSourceHref(const nsString& aValue); + + /** + * The state we are transitioning away from. + */ + PRInt32 mState; + + /** + * The index of the first UTF-16 code unit in mBuffer that hasn't been + * flushed yet. + */ + PRInt32 mCStart; + + /** + * The position of the code unit in mBuffer that caused the current + * transition. + */ + PRInt32 mPos; + + /** + * The number of inline elements open inside the
.
+     */
+    PRInt32 mInlinesOpen;
+
+    /**
+     * The current buffer being tokenized.
+     */
+    nsHtml5UTF16Buffer* mBuffer;
+
+    /**
+     * The URL of the document to be shown in the page title.
+     */
+    nsString mURL;
+
+    /**
+     * Whether to highlight syntax visibly initially.
+     */
+    bool mSyntaxHighlight;
+
+    /**
+     * Whether to wrap long lines.
+     */
+    bool mWrapLongLines;
+
+    /**
+     * The tab size pref.
+     */
+    PRInt32 mTabSize;
+
+    /**
+     * The outgoing tree op queue.
+     */
+    nsTArray mOpQueue;
+
+    /**
+     * The tree op stage for the tree op executor.
+     */
+    nsAHtml5TreeOpSink* mOpSink;
+
+    /**
+     * Memory for element handles.
+     */
+    nsAutoArrayPtr mHandles;
+
+    /**
+     * Number of handles used in mHandles
+     */
+    PRInt32 mHandlesUsed;
+
+    /**
+     * A holder for old contents of mHandles
+     */
+    nsTArray > mOldHandles;
+
+    /**
+     * The element stack.
+     */
+    nsTArray mStack;
+
+    /**
+     * The string "comment"
+     */
+    static PRUnichar sComment[];
+
+    /**
+     * The string "cdata"
+     */
+    static PRUnichar sCdata[];
+
+    /**
+     * The string "start-tag"
+     */
+    static PRUnichar sStartTag[];
+
+    /**
+     * The string "attribute-name"
+     */
+    static PRUnichar sAttributeName[];
+
+    /**
+     * The string "attribute-value"
+     */
+    static PRUnichar sAttributeValue[];
+
+    /**
+     * The string "end-tag"
+     */
+    static PRUnichar sEndTag[];
+
+    /**
+     * The string "doctype"
+     */
+    static PRUnichar sDoctype[];
+
+    /**
+     * The string "entity"
+     */
+    static PRUnichar sEntity[];
+};
+
+#endif // nsHtml5Highlighter_h_
diff --git a/parser/html/nsHtml5HtmlAttributes.cpp b/parser/html/nsHtml5HtmlAttributes.cpp
index 62dd640e765..f13ff096517 100644
--- a/parser/html/nsHtml5HtmlAttributes.cpp
+++ b/parser/html/nsHtml5HtmlAttributes.cpp
@@ -34,17 +34,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 #include "nsHtml5Tokenizer.h"
diff --git a/parser/html/nsHtml5HtmlAttributes.h b/parser/html/nsHtml5HtmlAttributes.h
index 01c33ab30a8..4f5ad93c992 100644
--- a/parser/html/nsHtml5HtmlAttributes.h
+++ b/parser/html/nsHtml5HtmlAttributes.h
@@ -35,17 +35,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 class nsHtml5StreamParser;
diff --git a/parser/html/nsHtml5MetaScanner.cpp b/parser/html/nsHtml5MetaScanner.cpp
index dc3d94a0260..7ec0f5d72d8 100644
--- a/parser/html/nsHtml5MetaScanner.cpp
+++ b/parser/html/nsHtml5MetaScanner.cpp
@@ -34,17 +34,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 #include "nsHtml5Tokenizer.h"
diff --git a/parser/html/nsHtml5MetaScanner.h b/parser/html/nsHtml5MetaScanner.h
index 9cd1015dafc..e7f5d1507e6 100644
--- a/parser/html/nsHtml5MetaScanner.h
+++ b/parser/html/nsHtml5MetaScanner.h
@@ -35,17 +35,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 class nsHtml5StreamParser;
diff --git a/parser/html/nsHtml5Parser.cpp b/parser/html/nsHtml5Parser.cpp
index 2139f781def..9ddd25c74e4 100644
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -128,7 +128,8 @@ nsHtml5Parser::GetCommand(nsCString& aCommand)
 NS_IMETHODIMP_(void)
 nsHtml5Parser::SetCommand(const char* aCommand)
 {
-  NS_ASSERTION(!strcmp(aCommand, "view"), "Parser command was not view");
+  NS_ASSERTION(!strcmp(aCommand, "view") || !strcmp(aCommand, "view-source"),
+      "Parser command was not view");
 }
 
 NS_IMETHODIMP_(void)
@@ -698,10 +699,21 @@ nsHtml5Parser::EndEvaluatingParserInsertedScript()
 }
 
 void
-nsHtml5Parser::MarkAsNotScriptCreated()
+nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand)
 {
   NS_PRECONDITION(!mStreamParser, "Must not call this twice.");
-  mStreamParser = new nsHtml5StreamParser(mExecutor, this);
+  eParserMode mode = NORMAL;
+  if (!nsCRT::strcmp(aCommand, "view-source")) {
+    mode = VIEW_SOURCE_HTML;
+    // XXX XML view source not implemented yet
+  }
+#ifdef DEBUG
+  else {
+    NS_ASSERTION(!nsCRT::strcmp(aCommand, "view"),
+        "Unsupported parser command!");
+  }
+#endif
+  mStreamParser = new nsHtml5StreamParser(mExecutor, this, mode);
 }
 
 bool
diff --git a/parser/html/nsHtml5Parser.h b/parser/html/nsHtml5Parser.h
index f1cda84be76..9bc69183889 100644
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -246,8 +246,11 @@ class nsHtml5Parser : public nsIParser,
     /**
      * Marks the HTML5 parser as not a script-created parser: Prepares the 
      * parser to be able to read a stream.
+     *
+     * @param aCommand the parser command (Yeah, this is bad API design. Let's
+     * make this better when retiring nsIParser)
      */
-    virtual void MarkAsNotScriptCreated();
+    virtual void MarkAsNotScriptCreated(const char* aCommand);
 
     /**
      * True if this is a script-created HTML5 parser.
diff --git a/parser/html/nsHtml5Portability.h b/parser/html/nsHtml5Portability.h
index d0d80774696..60cef2d3c78 100644
--- a/parser/html/nsHtml5Portability.h
+++ b/parser/html/nsHtml5Portability.h
@@ -34,17 +34,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 class nsHtml5StreamParser;
diff --git a/parser/html/nsHtml5StackNode.cpp b/parser/html/nsHtml5StackNode.cpp
index 2addf91f8b2..b3cc4756ada 100644
--- a/parser/html/nsHtml5StackNode.cpp
+++ b/parser/html/nsHtml5StackNode.cpp
@@ -34,17 +34,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 #include "nsHtml5Tokenizer.h"
diff --git a/parser/html/nsHtml5StackNode.h b/parser/html/nsHtml5StackNode.h
index 85e09f47c33..f1603b822c0 100644
--- a/parser/html/nsHtml5StackNode.h
+++ b/parser/html/nsHtml5StackNode.h
@@ -35,17 +35,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 class nsHtml5StreamParser;
diff --git a/parser/html/nsHtml5StateSnapshot.cpp b/parser/html/nsHtml5StateSnapshot.cpp
index 02138ce8758..f80bfce81af 100644
--- a/parser/html/nsHtml5StateSnapshot.cpp
+++ b/parser/html/nsHtml5StateSnapshot.cpp
@@ -33,17 +33,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 #include "nsHtml5Tokenizer.h"
diff --git a/parser/html/nsHtml5StateSnapshot.h b/parser/html/nsHtml5StateSnapshot.h
index cff408bcef3..0da8744c2e6 100644
--- a/parser/html/nsHtml5StateSnapshot.h
+++ b/parser/html/nsHtml5StateSnapshot.h
@@ -34,17 +34,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 class nsHtml5StreamParser;
diff --git a/parser/html/nsHtml5StreamParser.cpp b/parser/html/nsHtml5StreamParser.cpp
index e811cd14f6f..5fbcb0311b5 100644
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -53,6 +53,7 @@
 #include "nsHtml5RefPtr.h"
 #include "nsIScriptError.h"
 #include "mozilla/Preferences.h"
+#include "nsHtml5Highlighter.h"
 
 using namespace mozilla;
 
@@ -174,12 +175,16 @@ class nsHtml5LoadFlusher : public nsRunnable
 };
 
 nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
-                                         nsHtml5Parser* aOwner)
+                                         nsHtml5Parser* aOwner,
+                                         eParserMode aMode)
   : mFirstBuffer(nsnull) // Will be filled when starting
   , mLastBuffer(nsnull) // Will be filled when starting
   , mExecutor(aExecutor)
-  , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor->GetStage(),
-                                        mExecutor->GetStage()))
+  , mTreeBuilder(new nsHtml5TreeBuilder((aMode == VIEW_SOURCE_HTML ||
+                                         aMode == VIEW_SOURCE_XML) ?
+                                             nsnull : mExecutor->GetStage(),
+                                         aMode == NORMAL ?
+                                             mExecutor->GetStage() : nsnull))
   , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
   , mTokenizerMutex("nsHtml5StreamParser mTokenizerMutex")
   , mOwner(aOwner)
@@ -189,6 +194,7 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
   , mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
   , mLoadFlusher(new nsHtml5LoadFlusher(aExecutor))
   , mFlushTimer(do_CreateInstance("@mozilla.org/timer;1"))
+  , mMode(aMode)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   mFlushTimer->SetTarget(mThread);
@@ -199,6 +205,10 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
   mTokenizer->setInterner(&mAtomTable);
   mTokenizer->setEncodingDeclarationHandler(this);
 
+  if (aMode == VIEW_SOURCE_HTML || aMode == VIEW_SOURCE_XML) {
+    mTokenizer->EnableViewSource(new nsHtml5Highlighter(mExecutor->GetStage()));
+  }
+
   // Chardet instantiation adapted from nsDOMFile.
   // Chardet is initialized here even if it turns out to be useless
   // to make the chardet refcount its observer (nsHtml5StreamParser)
@@ -685,6 +695,11 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 
   mStreamState = STREAM_BEING_READ;
 
+  if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
+    mTokenizer->StartViewSource();
+  }
+  // For View Source, the parser should run with scripts "enabled" if a normal
+  // load would have scripts enabled.
   bool scriptingEnabled = mExecutor->IsScriptEnabled();
   mOwner->StartTokenizer(scriptingEnabled);
   mTreeBuilder->setScriptingEnabled(scriptingEnabled);
@@ -713,7 +728,11 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 
   nsresult rv = NS_OK;
 
-  mReparseForbidden = false;
+  // The line below means that the encoding can end up being wrong if
+  // a view-source URL is loaded without having the encoding hint from a
+  // previous normal load in the history.
+  mReparseForbidden = !(mMode == NORMAL);
+
   nsCOMPtr httpChannel(do_QueryInterface(mRequest, &rv));
   if (NS_SUCCEEDED(rv)) {
     nsCAutoString method;
@@ -1007,6 +1026,9 @@ nsHtml5StreamParser::FlushTreeOpsAndDisarmTimer()
     mFlushTimer->Cancel();
     mFlushTimerArmed = false;
   }
+  if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
+    mTokenizer->FlushViewSource();
+  }
   mTreeBuilder->Flush();
   if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
     NS_WARNING("failed to dispatch executor flush event");
@@ -1049,6 +1071,9 @@ nsHtml5StreamParser::ParseAvailableData()
             mAtEOF = true;
             mTokenizer->eof();
             mTreeBuilder->StreamEnded();
+            if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
+              mTokenizer->EndViewSource();
+            }
             FlushTreeOpsAndDisarmTimer();
             return; // no more data and not expecting more
           default:
@@ -1069,7 +1094,7 @@ nsHtml5StreamParser::ParseAvailableData()
       // Terminate, but that never happens together with script.
       // Can't assert that here, though, because it's possible that the main
       // thread has called Terminate() while this thread was parsing.
-      if (mTreeBuilder->HasScript()) {
+      if (mMode == NORMAL && mTreeBuilder->HasScript()) {
         mozilla::MutexAutoLock speculationAutoLock(mSpeculationMutex);
         nsHtml5Speculation* speculation = 
           new nsHtml5Speculation(mFirstBuffer,
@@ -1114,6 +1139,8 @@ nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer,
                                           bool aLastWasCR)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!(mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML),
+      "ContinueAfterScripts called in view source mode!");
   if (mExecutor->IsBroken()) {
     return;
   }
@@ -1329,11 +1356,20 @@ nsHtml5StreamParser::TimerFlush()
     return;
   }
 
-  // we aren't speculating and we don't know when new data is
-  // going to arrive. Send data to the main thread.
-  if (mTreeBuilder->Flush(true)) {
-    if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
-      NS_WARNING("failed to dispatch executor flush event");
+  if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) {
+    mTreeBuilder->Flush(); // delete useless ops
+    if (mTokenizer->FlushViewSource()) {
+       if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
+         NS_WARNING("failed to dispatch executor flush event");
+       }
+     }
+  } else {
+    // we aren't speculating and we don't know when new data is
+    // going to arrive. Send data to the main thread.
+    if (mTreeBuilder->Flush(true)) {
+      if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
+        NS_WARNING("failed to dispatch executor flush event");
+      }
     }
   }
 }
diff --git a/parser/html/nsHtml5StreamParser.h b/parser/html/nsHtml5StreamParser.h
index 24261426a92..18c1567b5a8 100644
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -60,6 +60,28 @@ class nsHtml5Parser;
 #define NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE 1024
 #define NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE 1024
 
+enum eParserMode {
+  /**
+   * Parse a document normally as HTML.
+   */
+  NORMAL,
+
+  /**
+   * View document as HTML source.
+   */
+  VIEW_SOURCE_HTML,
+
+  /**
+   * View document as XML source
+   */
+  VIEW_SOURCE_XML,
+
+  /**
+   * View document as plain text
+   */
+  PLAIN_TEXT
+};
+
 enum eBomState {
   /**
    * BOM sniffing hasn't started.
@@ -118,7 +140,8 @@ class nsHtml5StreamParser : public nsIStreamListener,
     static void InitializeStatics();
 
     nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
-                        nsHtml5Parser* aOwner);
+                        nsHtml5Parser* aOwner,
+                        eParserMode aMode);
                         
     virtual ~nsHtml5StreamParser();
 
@@ -495,6 +518,11 @@ class nsHtml5StreamParser : public nsIStreamListener,
      */
     bool                          mFlushTimerEverFired;
 
+    /**
+     * Whether the parser is doing a normal parse, view source or plain text.
+     */
+    eParserMode                   mMode;
+
     /**
      * The pref html5.flushtimer.initialdelay: Time in milliseconds between
      * the time a network buffer is seen and the timer firing when the
diff --git a/parser/html/nsHtml5Tokenizer.cpp b/parser/html/nsHtml5Tokenizer.cpp
index 8354ec85f1a..f6d21d37e63 100644
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -34,9 +34,7 @@
 #include "nsIAtom.h"
 #include "nsHtml5AtomTable.h"
 #include "nsString.h"
-#include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
 #include "nsHtml5DocumentMode.h"
@@ -44,10 +42,9 @@
 #include "nsHtml5NamedCharacters.h"
 #include "nsHtml5NamedCharactersAccel.h"
 #include "nsHtml5Atoms.h"
-#include "nsHtml5ByteReadable.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
+#include "nsHtml5Highlighter.h"
 
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
@@ -340,6 +337,9 @@ nsHtml5Tokenizer::addAttributeWithValue()
 {
   if (attributeName) {
     nsString* val = longStrBufToString();
+    if (mViewSource) {
+      mViewSource->MaybeLinkifyAttributeValue(attributeName, val);
+    }
     attributes->addAttribute(attributeName, val);
     attributeName = nsnull;
   }
@@ -388,7 +388,13 @@ nsHtml5Tokenizer::tokenizeBuffer(nsHtml5UTF16Buffer* buffer)
       break;
     }
   }
-  pos = stateLoop(state, c, pos, buffer->getBuffer(), false, returnState, buffer->getEnd());
+  if (mViewSource) {
+    mViewSource->SetBuffer(buffer);
+    pos = stateLoopReportTransitions(state, c, pos, buffer->getBuffer(), false, returnState, buffer->getEnd());
+    mViewSource->DropBuffer((pos == buffer->getEnd()) ? pos : pos + 1);
+  } else {
+    pos = stateLoop(state, c, pos, buffer->getBuffer(), false, returnState, buffer->getEnd());
+  }
   if (pos == buffer->getEnd()) {
     buffer->setStart(pos);
   } else {
@@ -1533,6 +1539,7 @@ nsHtml5Tokenizer::stateLoop(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar*
             }
 
           }
+
           const PRUnichar* val = nsHtml5NamedCharacters::VALUES[candidate];
           if (!val[1]) {
             emitOrAppendOne(val, returnState);
@@ -3268,6 +3275,2878 @@ nsHtml5Tokenizer::stateLoop(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar*
   return pos;
 }
 
+PRInt32 
+nsHtml5Tokenizer::stateLoopReportTransitions(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar* buf, bool reconsume, PRInt32 returnState, PRInt32 endPos)
+{
+  stateloop: for (; ; ) {
+    switch(state) {
+      case NS_HTML5TOKENIZER_DATA: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '&': {
+              flushChars(buf, pos);
+              clearStrBufAndAppend(c);
+              setAdditionalAndRememberAmpersandLocation('\0');
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '<': {
+              flushChars(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_TAG_OPEN, reconsume, pos);
+              NS_HTML5_BREAK(dataloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+        dataloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_TAG_OPEN: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (c >= 'A' && c <= 'Z') {
+            endTag = false;
+            clearStrBufAndAppend((PRUnichar) (c + 0x20));
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
+            NS_HTML5_BREAK(tagopenloop);
+          } else if (c >= 'a' && c <= 'z') {
+            endTag = false;
+            clearStrBufAndAppend(c);
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
+            NS_HTML5_BREAK(tagopenloop);
+          }
+          switch(c) {
+            case '!': {
+              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 '\?': {
+
+              clearLongStrBufAndAppend(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+
+              tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 2);
+              cstart = pos + 1;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            default: {
+
+              tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
+              cstart = pos;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        tagopenloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_TAG_NAME: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              strBufToElementNameString();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              strBufToElementNameString();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(tagnameloop);
+            }
+            case '/': {
+              strBufToElementNameString();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+              strBufToElementNameString();
+              state = mViewSource->Transition(emitCurrentTagToken(false, pos), reconsume, pos);
+              if (shouldSuspend) {
+                NS_HTML5_BREAK(stateloop);
+              }
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              if (c >= 'A' && c <= 'Z') {
+                c += 0x20;
+              }
+              appendStrBuf(c);
+              continue;
+            }
+          }
+        }
+        tagnameloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '/': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+              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 '=':
+            default: {
+              if (c >= 'A' && c <= 'Z') {
+                c += 0x20;
+              }
+              clearStrBufAndAppend(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(beforeattributenameloop);
+            }
+          }
+        }
+        beforeattributenameloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_ATTRIBUTE_NAME: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              attributeNameComplete();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              attributeNameComplete();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '/': {
+              attributeNameComplete();
+              addAttributeWithoutValue();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '=': {
+              attributeNameComplete();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE, reconsume, pos);
+              NS_HTML5_BREAK(attributenameloop);
+            }
+            case '>': {
+              attributeNameComplete();
+              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 '<':
+            default: {
+              if (c >= 'A' && c <= 'Z') {
+                c += 0x20;
+              }
+              appendStrBuf(c);
+              continue;
+            }
+          }
+        }
+        attributenameloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '\"': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED, reconsume, pos);
+              NS_HTML5_BREAK(beforeattributevalueloop);
+            }
+            case '&': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED, reconsume, pos);
+
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\'': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+
+              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 '`':
+            default: {
+              clearLongStrBufAndAppend(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED, reconsume, pos);
+
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        beforeattributevalueloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_DOUBLE_QUOTED: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\"': {
+              addAttributeWithValue();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED, reconsume, pos);
+              NS_HTML5_BREAK(attributevaluedoublequotedloop);
+            }
+            case '&': {
+              clearStrBufAndAppend(c);
+              setAdditionalAndRememberAmpersandLocation('\"');
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+        attributevaluedoublequotedloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '/': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG, reconsume, pos);
+              NS_HTML5_BREAK(afterattributevaluequotedloop);
+            }
+            case '>': {
+              state = mViewSource->Transition(emitCurrentTagToken(false, pos), reconsume, pos);
+              if (shouldSuspend) {
+                NS_HTML5_BREAK(stateloop);
+              }
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            default: {
+
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        afterattributevaluequotedloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG: {
+        if (++pos == endPos) {
+          NS_HTML5_BREAK(stateloop);
+        }
+        c = checkChar(buf, pos);
+        switch(c) {
+          case '>': {
+            state = mViewSource->Transition(emitCurrentTagToken(true, pos), reconsume, pos);
+            if (shouldSuspend) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          default: {
+
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+            reconsume = true;
+            NS_HTML5_CONTINUE(stateloop);
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_UNQUOTED: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              addAttributeWithValue();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              addAttributeWithValue();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '&': {
+              clearStrBufAndAppend(c);
+              setAdditionalAndRememberAmpersandLocation('>');
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+              addAttributeWithValue();
+              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 '=':
+            case '`':
+            default: {
+
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_NAME: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '/': {
+              addAttributeWithoutValue();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '=': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_VALUE, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+              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 '<':
+            default: {
+              addAttributeWithoutValue();
+              if (c >= 'A' && c <= 'Z') {
+                c += 0x20;
+              }
+              clearStrBufAndAppend(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_ATTRIBUTE_NAME, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              clearLongStrBufAndAppend(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_MARKUP_DECLARATION_HYPHEN, reconsume, pos);
+              NS_HTML5_BREAK(markupdeclarationopenloop);
+            }
+            case 'd':
+            case 'D': {
+              clearLongStrBufAndAppend(c);
+              index = 0;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_MARKUP_DECLARATION_OCTYPE, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '[': {
+              if (tokenHandler->cdataSectionAllowed()) {
+                clearLongStrBufAndAppend(c);
+                index = 0;
+                state = mViewSource->Transition(NS_HTML5TOKENIZER_CDATA_START, reconsume, pos);
+                NS_HTML5_CONTINUE(stateloop);
+              }
+            }
+            default: {
+
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        markupdeclarationopenloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_MARKUP_DECLARATION_HYPHEN: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\0': {
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '-': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_START, reconsume, pos);
+              NS_HTML5_BREAK(markupdeclarationhyphenloop);
+            }
+            default: {
+
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        markupdeclarationhyphenloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_COMMENT_START: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_START_DASH, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+
+              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);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_BREAK(commentstartloop);
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_BREAK(commentstartloop);
+            }
+          }
+        }
+        commentstartloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_COMMENT: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_END_DASH, reconsume, pos);
+              NS_HTML5_BREAK(commentloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+        commentloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_COMMENT_END_DASH: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_END, reconsume, pos);
+              NS_HTML5_BREAK(commentenddashloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        commentenddashloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_COMMENT_END: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '>': {
+              emitComment(2, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '-': {
+              adjustDoubleHyphenAndAppendToLongStrBufAndErr(c);
+              continue;
+            }
+            case '\r': {
+              adjustDoubleHyphenAndAppendToLongStrBufCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              adjustDoubleHyphenAndAppendToLongStrBufLineFeed();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '!': {
+
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_END_BANG, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              adjustDoubleHyphenAndAppendToLongStrBufAndErr(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+
+      }
+      case NS_HTML5TOKENIZER_COMMENT_END_BANG: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '>': {
+              emitComment(3, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '-': {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_END_DASH, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_COMMENT_START_DASH: {
+        if (++pos == endPos) {
+          NS_HTML5_BREAK(stateloop);
+        }
+        c = checkChar(buf, pos);
+        switch(c) {
+          case '-': {
+            appendLongStrBuf(c);
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT_END, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          case '>': {
+
+            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);
+          }
+          case '\n': {
+            appendLongStrBufLineFeed();
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          case '\0': {
+            c = 0xfffd;
+          }
+          default: {
+            appendLongStrBuf(c);
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_COMMENT, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_CDATA_START: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (index < 6) {
+            if (c == nsHtml5Tokenizer::CDATA_LSQB[index]) {
+              appendLongStrBuf(c);
+            } else {
+
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            index++;
+            continue;
+          } else {
+            cstart = pos;
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_CDATA_SECTION, reconsume, pos);
+            reconsume = true;
+            break;
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_CDATA_SECTION: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case ']': {
+              flushChars(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CDATA_RSQB, reconsume, pos);
+              NS_HTML5_BREAK(cdatasectionloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+        cdatasectionloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_CDATA_RSQB: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case ']': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CDATA_RSQB_RSQB, reconsume, pos);
+              NS_HTML5_BREAK(cdatarsqb);
+            }
+            default: {
+              tokenHandler->characters(nsHtml5Tokenizer::RSQB_RSQB, 0, 1);
+              cstart = pos;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CDATA_SECTION, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        cdatarsqb_end: ;
+      }
+      case NS_HTML5TOKENIZER_CDATA_RSQB_RSQB: {
+        if (++pos == endPos) {
+          NS_HTML5_BREAK(stateloop);
+        }
+        c = checkChar(buf, pos);
+        switch(c) {
+          case '>': {
+            cstart = pos + 1;
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          default: {
+            tokenHandler->characters(nsHtml5Tokenizer::RSQB_RSQB, 0, 2);
+            cstart = pos;
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_CDATA_SECTION, reconsume, pos);
+            reconsume = true;
+            NS_HTML5_CONTINUE(stateloop);
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_ATTRIBUTE_VALUE_SINGLE_QUOTED: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\'': {
+              addAttributeWithValue();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_ATTRIBUTE_VALUE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '&': {
+              clearStrBufAndAppend(c);
+              setAdditionalAndRememberAmpersandLocation('\'');
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
+              NS_HTML5_BREAK(attributevaluesinglequotedloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+        attributevaluesinglequotedloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE: {
+        if (++pos == endPos) {
+          NS_HTML5_BREAK(stateloop);
+        }
+        c = checkChar(buf, pos);
+        if (c == '\0') {
+          NS_HTML5_BREAK(stateloop);
+        }
+        switch(c) {
+          case ' ':
+          case '\t':
+          case '\n':
+          case '\r':
+          case '\f':
+          case '<':
+          case '&': {
+            emitOrAppendStrBuf(returnState);
+            if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
+              cstart = pos;
+            }
+            state = mViewSource->Transition(returnState, reconsume, pos);
+            reconsume = true;
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          case '#': {
+            appendStrBuf('#');
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_CONSUME_NCR, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          default: {
+            if (c == additional) {
+              emitOrAppendStrBuf(returnState);
+              state = mViewSource->Transition(returnState, reconsume, pos);
+              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 {
+
+              emitOrAppendStrBuf(returnState);
+              if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
+                cstart = pos;
+              }
+              state = mViewSource->Transition(returnState, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            appendStrBuf(c);
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP, reconsume, pos);
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_HILO_LOOKUP: {
+        {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (c == '\0') {
+            NS_HTML5_BREAK(stateloop);
+          }
+          PRInt32 hilo = 0;
+          if (c <= 'z') {
+            const PRInt32* row = nsHtml5NamedCharactersAccel::HILO_ACCEL[c];
+            if (row) {
+              hilo = row[firstCharKey];
+            }
+          }
+          if (!hilo) {
+
+            emitOrAppendStrBuf(returnState);
+            if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
+              cstart = pos;
+            }
+            state = mViewSource->Transition(returnState, reconsume, pos);
+            reconsume = true;
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          appendStrBuf(c);
+          lo = hilo & 0xFFFF;
+          hi = hilo >> 16;
+          entCol = -1;
+          candidate = -1;
+          strBufMark = 0;
+          state = mViewSource->Transition(NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL, reconsume, pos);
+        }
+      }
+      case NS_HTML5TOKENIZER_CHARACTER_REFERENCE_TAIL: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (c == '\0') {
+            NS_HTML5_BREAK(stateloop);
+          }
+          entCol++;
+          for (; ; ) {
+            if (hi < lo) {
+              NS_HTML5_BREAK(outer);
+            }
+            if (entCol == nsHtml5NamedCharacters::NAMES[lo].length()) {
+              candidate = lo;
+              strBufMark = strBufLen;
+              lo++;
+            } else if (entCol > nsHtml5NamedCharacters::NAMES[lo].length()) {
+              NS_HTML5_BREAK(outer);
+            } else if (c > nsHtml5NamedCharacters::NAMES[lo].charAt(entCol)) {
+              lo++;
+            } else {
+              NS_HTML5_BREAK(loloop);
+            }
+          }
+          loloop_end: ;
+          for (; ; ) {
+            if (hi < lo) {
+              NS_HTML5_BREAK(outer);
+            }
+            if (entCol == nsHtml5NamedCharacters::NAMES[hi].length()) {
+              NS_HTML5_BREAK(hiloop);
+            }
+            if (entCol > nsHtml5NamedCharacters::NAMES[hi].length()) {
+              NS_HTML5_BREAK(outer);
+            } else if (c < nsHtml5NamedCharacters::NAMES[hi].charAt(entCol)) {
+              hi--;
+            } else {
+              NS_HTML5_BREAK(hiloop);
+            }
+          }
+          hiloop_end: ;
+          if (hi < lo) {
+            NS_HTML5_BREAK(outer);
+          }
+          appendStrBuf(c);
+          continue;
+        }
+        outer_end: ;
+        if (candidate == -1) {
+
+          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 {
+          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 = c;
+              } else {
+                ch = strBuf[strBufMark];
+              }
+              if (ch == '=' || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
+
+                appendStrBufToLongStrBuf();
+                state = mViewSource->Transition(returnState, reconsume, pos);
+                reconsume = true;
+                NS_HTML5_CONTINUE(stateloop);
+              }
+            }
+
+          }
+          mViewSource->CompletedNamedCharacterReference();
+          const PRUnichar* val = nsHtml5NamedCharacters::VALUES[candidate];
+          if (!val[1]) {
+            emitOrAppendOne(val, returnState);
+          } else {
+            emitOrAppendTwo(val, returnState);
+          }
+          if (strBufMark < strBufLen) {
+            if ((returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
+              for (PRInt32 i = strBufMark; i < strBufLen; i++) {
+                appendLongStrBuf(strBuf[i]);
+              }
+            } else {
+              tokenHandler->characters(strBuf, strBufMark, strBufLen - strBufMark);
+            }
+          }
+          if (!(returnState & NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK)) {
+            cstart = pos;
+          }
+          state = mViewSource->Transition(returnState, reconsume, pos);
+          reconsume = true;
+          NS_HTML5_CONTINUE(stateloop);
+        }
+      }
+      case NS_HTML5TOKENIZER_CONSUME_NCR: {
+        if (++pos == endPos) {
+          NS_HTML5_BREAK(stateloop);
+        }
+        c = checkChar(buf, pos);
+        prevValue = -1;
+        value = 0;
+        seenDigits = false;
+        switch(c) {
+          case 'x':
+          case 'X': {
+            appendStrBuf(c);
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_HEX_NCR_LOOP, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          default: {
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP, reconsume, pos);
+            reconsume = true;
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_DECIMAL_NRC_LOOP: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          if (value < prevValue) {
+            value = 0x110000;
+          }
+          prevValue = value;
+          if (c >= '0' && c <= '9') {
+            seenDigits = true;
+            value *= 10;
+            value += c - '0';
+            continue;
+          } 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 {
+
+              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) {
+
+              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 {
+
+              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);
+            }
+          }
+        }
+        decimalloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_HANDLE_NCR_VALUE: {
+        handleNcrValue(returnState);
+        state = mViewSource->Transition(returnState, reconsume, pos);
+        NS_HTML5_CONTINUE(stateloop);
+      }
+      case NS_HTML5TOKENIZER_HEX_NCR_LOOP: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (value < prevValue) {
+            value = 0x110000;
+          }
+          prevValue = value;
+          if (c >= '0' && c <= '9') {
+            seenDigits = true;
+            value *= 16;
+            value += c - '0';
+            continue;
+          } else if (c >= 'A' && c <= 'F') {
+            seenDigits = true;
+            value *= 16;
+            value += c - 'A' + 10;
+            continue;
+          } else if (c >= 'a' && c <= 'f') {
+            seenDigits = true;
+            value *= 16;
+            value += c - 'a' + 10;
+            continue;
+          } 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 {
+
+              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) {
+
+              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 {
+
+              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);
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_PLAINTEXT: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\0': {
+              emitPlaintextReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+
+      }
+      case NS_HTML5TOKENIZER_CLOSE_TAG_OPEN: {
+        if (++pos == endPos) {
+          NS_HTML5_BREAK(stateloop);
+        }
+        c = checkChar(buf, pos);
+        switch(c) {
+          case '>': {
+
+            cstart = pos + 1;
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          case '\r': {
+            silentCarriageReturn();
+
+            clearLongStrBufAndAppend('\n');
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+            NS_HTML5_BREAK(stateloop);
+          }
+          case '\n': {
+            silentLineFeed();
+
+            clearLongStrBufAndAppend('\n');
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
+          case '\0': {
+            c = 0xfffd;
+          }
+          default: {
+            if (c >= 'A' && c <= 'Z') {
+              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 {
+
+              clearLongStrBufAndAppend(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_RCDATA: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '&': {
+              flushChars(buf, pos);
+              clearStrBufAndAppend(c);
+              additional = '\0';
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_CONSUME_CHARACTER_REFERENCE, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '<': {
+              flushChars(buf, pos);
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+
+      }
+      case NS_HTML5TOKENIZER_RAWTEXT: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '<': {
+              flushChars(buf, pos);
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_BREAK(rawtextloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+        rawtextloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_RAWTEXT_RCDATA_LESS_THAN_SIGN: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '/': {
+              index = 0;
+              clearStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME, reconsume, pos);
+              NS_HTML5_BREAK(rawtextrcdatalessthansignloop);
+            }
+            default: {
+              tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
+              cstart = pos;
+              state = mViewSource->Transition(returnState, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        rawtextrcdatalessthansignloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (index < endTagExpectationAsArray.length) {
+            PRUnichar e = endTagExpectationAsArray[index];
+            PRUnichar folded = c;
+            if (c >= 'A' && c <= 'Z') {
+              folded += 0x20;
+            }
+            if (folded != e) {
+              tokenHandler->characters(nsHtml5Tokenizer::LT_SOLIDUS, 0, 2);
+              emitStrBuf();
+              cstart = pos;
+              state = mViewSource->Transition(returnState, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            appendStrBuf(c);
+            index++;
+            continue;
+          } else {
+            endTag = true;
+            tagName = endTagExpectation;
+            switch(c) {
+              case '\r': {
+                silentCarriageReturn();
+                state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+                NS_HTML5_BREAK(stateloop);
+              }
+              case '\n': {
+                silentLineFeed();
+              }
+              case ' ':
+              case '\t':
+              case '\f': {
+                state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_ATTRIBUTE_NAME, reconsume, pos);
+                NS_HTML5_CONTINUE(stateloop);
+              }
+              case '/': {
+                state = mViewSource->Transition(NS_HTML5TOKENIZER_SELF_CLOSING_START_TAG, reconsume, pos);
+                NS_HTML5_CONTINUE(stateloop);
+              }
+              case '>': {
+                state = mViewSource->Transition(emitCurrentTagToken(false, pos), reconsume, pos);
+                if (shouldSuspend) {
+                  NS_HTML5_BREAK(stateloop);
+                }
+                NS_HTML5_CONTINUE(stateloop);
+              }
+              default: {
+                tokenHandler->characters(nsHtml5Tokenizer::LT_SOLIDUS, 0, 2);
+                emitStrBuf();
+                if (c == '\0') {
+                  emitReplacementCharacter(buf, pos);
+                } else {
+                  cstart = pos;
+                }
+                state = mViewSource->Transition(returnState, reconsume, pos);
+                NS_HTML5_CONTINUE(stateloop);
+              }
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_BOGUS_COMMENT: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '>': {
+              emitComment(0, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '-': {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN, reconsume, pos);
+              NS_HTML5_BREAK(boguscommentloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+        boguscommentloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BOGUS_COMMENT_HYPHEN: {
+        boguscommenthyphenloop: for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '>': {
+              emitComment(0, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '-': {
+              appendSecondHyphenToBogusComment();
+              NS_HTML5_CONTINUE(boguscommenthyphenloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '<': {
+              flushChars(buf, pos);
+              returnState = state;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+        scriptdataloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_LESS_THAN_SIGN: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '/': {
+              index = 0;
+              clearStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '!': {
+              tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
+              cstart = pos;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPE_START, reconsume, pos);
+              NS_HTML5_BREAK(scriptdatalessthansignloop);
+            }
+            default: {
+              tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
+              cstart = pos;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdatalessthansignloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPE_START: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPE_START_DASH, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataescapestartloop);
+            }
+            default: {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdataescapestartloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPE_START_DASH: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH_DASH, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataescapestartdashloop);
+            }
+            default: {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdataescapestartdashloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH_DASH: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              continue;
+            }
+            case '<': {
+              flushChars(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataescapeddashdashloop);
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataescapeddashdashloop);
+            }
+          }
+        }
+        scriptdataescapeddashdashloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '-': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataescapedloop);
+            }
+            case '<': {
+              flushChars(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+        scriptdataescapedloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_DASH_DASH, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '<': {
+              flushChars(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataescapeddashloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdataescapeddashloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '/': {
+              index = 0;
+              clearStrBuf();
+              returnState = NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_NON_DATA_END_TAG_NAME, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case 'S':
+            case 's': {
+              tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
+              cstart = pos;
+              index = 1;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPE_START, reconsume, pos);
+              NS_HTML5_BREAK(scriptdataescapedlessthanloop);
+            }
+            default: {
+              tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
+              cstart = pos;
+              reconsume = true;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdataescapedlessthanloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPE_START: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+
+          if (index < 6) {
+            PRUnichar folded = c;
+            if (c >= 'A' && c <= 'Z') {
+              folded += 0x20;
+            }
+            if (folded != nsHtml5Tokenizer::SCRIPT_ARR[index]) {
+              reconsume = true;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            index++;
+            continue;
+          }
+          switch(c) {
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f':
+            case '/':
+            case '>': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(scriptdatadoubleescapestartloop);
+            }
+            default: {
+              reconsume = true;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdatadoubleescapestartloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '-': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_DASH, reconsume, pos);
+              NS_HTML5_BREAK(scriptdatadoubleescapedloop);
+            }
+            case '<': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              continue;
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+        scriptdatadoubleescapedloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_DASH: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH, reconsume, pos);
+              NS_HTML5_BREAK(scriptdatadoubleescapeddashloop);
+            }
+            case '<': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdatadoubleescapeddashloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '-': {
+              continue;
+            }
+            case '<': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN, reconsume, pos);
+              NS_HTML5_BREAK(scriptdatadoubleescapeddashdashloop);
+            }
+            case '>': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              emitReplacementCharacter(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdatadoubleescapeddashdashloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '/': {
+              index = 0;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPE_END, reconsume, pos);
+              NS_HTML5_BREAK(scriptdatadoubleescapedlessthanloop);
+            }
+            default: {
+              reconsume = true;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        scriptdatadoubleescapedlessthanloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPE_END: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (index < 6) {
+            PRUnichar folded = c;
+            if (c >= 'A' && c <= 'Z') {
+              folded += 0x20;
+            }
+            if (folded != nsHtml5Tokenizer::SCRIPT_ARR[index]) {
+              reconsume = true;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            index++;
+            continue;
+          }
+          switch(c) {
+            case '\r': {
+              emitCarriageReturn(buf, pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f':
+            case '/':
+            case '>': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            default: {
+              reconsume = true;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_SCRIPT_DATA_DOUBLE_ESCAPED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+
+      }
+      case NS_HTML5TOKENIZER_MARKUP_DECLARATION_OCTYPE: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (index < 6) {
+            PRUnichar folded = c;
+            if (c >= 'A' && c <= 'Z') {
+              folded += 0x20;
+            }
+            if (folded == nsHtml5Tokenizer::OCTYPE[index]) {
+              appendLongStrBuf(c);
+            } else {
+
+              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);
+            reconsume = true;
+            NS_HTML5_BREAK(markupdeclarationdoctypeloop);
+          }
+        }
+        markupdeclarationdoctypeloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          initDoctypeFields();
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(doctypeloop);
+            }
+            default: {
+
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_BREAK(doctypeloop);
+            }
+          }
+        }
+        doctypeloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_NAME: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '>': {
+
+              forceQuirks = true;
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              if (c >= 'A' && c <= 'Z') {
+                c += 0x20;
+              }
+              clearStrBufAndAppend(c);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(beforedoctypenameloop);
+            }
+          }
+        }
+        beforedoctypenameloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE_NAME: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              strBufToDoctypeName();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              strBufToDoctypeName();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME, reconsume, pos);
+              NS_HTML5_BREAK(doctypenameloop);
+            }
+            case '>': {
+              strBufToDoctypeName();
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              if (c >= 'A' && c <= 'Z') {
+                c += 0x0020;
+              }
+              appendStrBuf(c);
+              continue;
+            }
+          }
+        }
+        doctypenameloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_AFTER_DOCTYPE_NAME: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '>': {
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case 'p':
+            case 'P': {
+              index = 0;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_UBLIC, reconsume, pos);
+              NS_HTML5_BREAK(afterdoctypenameloop);
+            }
+            case 's':
+            case 'S': {
+              index = 0;
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_YSTEM, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            default: {
+              bogusDoctype();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        afterdoctypenameloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE_UBLIC: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (index < 5) {
+            PRUnichar folded = c;
+            if (c >= 'A' && c <= 'Z') {
+              folded += 0x20;
+            }
+            if (folded != nsHtml5Tokenizer::UBLIC[index]) {
+              bogusDoctype();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            index++;
+            continue;
+          } else {
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD, reconsume, pos);
+            reconsume = true;
+            NS_HTML5_BREAK(doctypeublicloop);
+          }
+        }
+        doctypeublicloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_KEYWORD: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER, reconsume, pos);
+              NS_HTML5_BREAK(afterdoctypepublickeywordloop);
+            }
+            case '\"': {
+
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\'': {
+
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+
+              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);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        afterdoctypepublickeywordloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '\"': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
+              NS_HTML5_BREAK(beforedoctypepublicidentifierloop);
+            }
+            case '\'': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+
+              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);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        beforedoctypepublicidentifierloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          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 '>': {
+
+              forceQuirks = true;
+              publicIdentifier = longStrBufToString();
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+        doctypepublicidentifierdoublequotedloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_AFTER_DOCTYPE_PUBLIC_IDENTIFIER: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS, reconsume, pos);
+              NS_HTML5_BREAK(afterdoctypepublicidentifierloop);
+            }
+            case '>': {
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\"': {
+
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\'': {
+
+              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);
+            }
+          }
+        }
+        afterdoctypepublicidentifierloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '>': {
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\"': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
+              NS_HTML5_BREAK(betweendoctypepublicandsystemidentifiersloop);
+            }
+            case '\'': {
+              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);
+            }
+          }
+        }
+        betweendoctypepublicandsystemidentifiersloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          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 '>': {
+
+              forceQuirks = true;
+              systemIdentifier = longStrBufToString();
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+
+      }
+      case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_IDENTIFIER: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '>': {
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            default: {
+              bogusDoctypeWithoutQuirks();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
+              NS_HTML5_BREAK(afterdoctypesystemidentifierloop);
+            }
+          }
+        }
+        afterdoctypesystemidentifierloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BOGUS_DOCTYPE: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '>': {
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            default: {
+              continue;
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE_YSTEM: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          if (index < 5) {
+            PRUnichar folded = c;
+            if (c >= 'A' && c <= 'Z') {
+              folded += 0x20;
+            }
+            if (folded != nsHtml5Tokenizer::YSTEM[index]) {
+              bogusDoctype();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BOGUS_DOCTYPE, reconsume, pos);
+              reconsume = true;
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            index++;
+            NS_HTML5_CONTINUE(stateloop);
+          } else {
+            state = mViewSource->Transition(NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD, reconsume, pos);
+            reconsume = true;
+            NS_HTML5_BREAK(doctypeystemloop);
+          }
+        }
+        doctypeystemloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_AFTER_DOCTYPE_SYSTEM_KEYWORD: {
+        for (; ; ) {
+          if (reconsume) {
+            reconsume = false;
+          } else {
+            if (++pos == endPos) {
+              NS_HTML5_BREAK(stateloop);
+            }
+            c = checkChar(buf, pos);
+          }
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER, reconsume, pos);
+              NS_HTML5_BREAK(afterdoctypesystemkeywordloop);
+            }
+            case '\"': {
+
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\'': {
+
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '>': {
+
+              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);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        afterdoctypesystemkeywordloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_BEFORE_DOCTYPE_SYSTEM_IDENTIFIER: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch(c) {
+            case '\r': {
+              silentCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              silentLineFeed();
+            }
+            case ' ':
+            case '\t':
+            case '\f': {
+              continue;
+            }
+            case '\"': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\'': {
+              clearLongStrBuf();
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED, reconsume, pos);
+              NS_HTML5_BREAK(beforedoctypesystemidentifierloop);
+            }
+            case '>': {
+
+              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);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+        beforedoctypesystemidentifierloop_end: ;
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          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 '>': {
+
+              forceQuirks = true;
+              systemIdentifier = longStrBufToString();
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+      }
+      case NS_HTML5TOKENIZER_DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED: {
+        for (; ; ) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          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 '>': {
+
+              forceQuirks = true;
+              publicIdentifier = longStrBufToString();
+              emitDoctypeToken(pos);
+              state = mViewSource->Transition(NS_HTML5TOKENIZER_DATA, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendLongStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendLongStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendLongStrBuf(c);
+              continue;
+            }
+          }
+        }
+      }
+    }
+  }
+  stateloop_end: ;
+  flushChars(buf, pos);
+  stateSave = state;
+  returnStateSave = returnState;
+  return pos;
+}
+
 void 
 nsHtml5Tokenizer::initDoctypeFields()
 {
@@ -3899,3 +6778,5 @@ nsHtml5Tokenizer::releaseStatics()
 }
 
 
+#include "nsHtml5TokenizerCppSupplement.h"
+
diff --git a/parser/html/nsHtml5Tokenizer.h b/parser/html/nsHtml5Tokenizer.h
index 3ccec56fdd4..6bfbfb040eb 100644
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -35,9 +35,7 @@
 #include "nsIAtom.h"
 #include "nsHtml5AtomTable.h"
 #include "nsString.h"
-#include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
 #include "nsHtml5DocumentMode.h"
@@ -45,10 +43,9 @@
 #include "nsHtml5NamedCharacters.h"
 #include "nsHtml5NamedCharactersAccel.h"
 #include "nsHtml5Atoms.h"
-#include "nsHtml5ByteReadable.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
+#include "nsHtml5Highlighter.h"
 
 class nsHtml5StreamParser;
 
@@ -219,6 +216,7 @@ class nsHtml5Tokenizer
     bool tokenizeBuffer(nsHtml5UTF16Buffer* buffer);
   private:
     PRInt32 stateLoop(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar* buf, bool reconsume, PRInt32 returnState, PRInt32 endPos);
+    PRInt32 stateLoopReportTransitions(PRInt32 state, PRUnichar c, PRInt32 pos, PRUnichar* buf, bool reconsume, PRInt32 returnState, PRInt32 endPos);
     void initDoctypeFields();
     inline void adjustDoubleHyphenAndAppendToLongStrBufCarriageReturn()
     {
@@ -291,6 +289,8 @@ class nsHtml5Tokenizer
     ~nsHtml5Tokenizer();
     static void initializeStatics();
     static void releaseStatics();
+
+#include "nsHtml5TokenizerHSupplement.h"
 };
 
 #define NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK ~1
diff --git a/parser/html/nsHtml5TokenizerCppSupplement.h b/parser/html/nsHtml5TokenizerCppSupplement.h
new file mode 100644
index 00000000000..71285af2509
--- /dev/null
+++ b/parser/html/nsHtml5TokenizerCppSupplement.h
@@ -0,0 +1,60 @@
+/* ***** 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 HTML5 View Source 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 
+ *
+ * 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 ***** */
+
+void
+nsHtml5Tokenizer::EnableViewSource(nsHtml5Highlighter* aHighlighter)
+{
+  mViewSource = aHighlighter;
+}
+
+bool
+nsHtml5Tokenizer::FlushViewSource()
+{
+  return mViewSource->FlushOps();
+}
+
+void
+nsHtml5Tokenizer::StartViewSource()
+{
+  mViewSource->Start();
+}
+
+void
+nsHtml5Tokenizer::EndViewSource()
+{
+  mViewSource->End();
+}
diff --git a/parser/html/nsHtml5TokenizerHSupplement.h b/parser/html/nsHtml5TokenizerHSupplement.h
new file mode 100644
index 00000000000..12cc93016e4
--- /dev/null
+++ b/parser/html/nsHtml5TokenizerHSupplement.h
@@ -0,0 +1,46 @@
+/* ***** 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 HTML5 View Source code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen 
+ *
+ * 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 ***** */
+
+nsAutoPtr mViewSource;
+
+void EnableViewSource(nsHtml5Highlighter* aHighlighter);
+
+bool FlushViewSource();
+
+void StartViewSource();
+
+void EndViewSource();
diff --git a/parser/html/nsHtml5TreeBuilder.cpp b/parser/html/nsHtml5TreeBuilder.cpp
index f0667f9a8a4..19b8bf4900d 100644
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -44,7 +44,6 @@
 #include "nsHtml5ArrayCopy.h"
 #include "nsHtml5Parser.h"
 #include "nsHtml5Atoms.h"
-#include "nsHtml5ByteReadable.h"
 #include "nsHtml5TreeOperation.h"
 #include "nsHtml5PendingNotification.h"
 #include "nsHtml5StateSnapshot.h"
diff --git a/parser/html/nsHtml5TreeBuilder.h b/parser/html/nsHtml5TreeBuilder.h
index c275884143c..f337af83896 100644
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -45,7 +45,6 @@
 #include "nsHtml5ArrayCopy.h"
 #include "nsHtml5Parser.h"
 #include "nsHtml5Atoms.h"
-#include "nsHtml5ByteReadable.h"
 #include "nsHtml5TreeOperation.h"
 #include "nsHtml5PendingNotification.h"
 #include "nsHtml5StateSnapshot.h"
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index 6584e50b277..df3c0447473 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -893,6 +893,24 @@ nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* a
   GetParser()->InitializeDocWriteParserState(aState, aLine);
 }
 
+nsIURI*
+nsHtml5TreeOpExecutor::GetViewSourceBaseURI()
+{
+  if (!mViewSourceBaseURI) {
+    nsCOMPtr orig = mDocument->GetOriginalURI();
+    bool isViewSource;
+    orig->SchemeIs("view-source", &isViewSource);
+    if (isViewSource) {
+      nsCOMPtr nested = do_QueryInterface(orig);
+      NS_ASSERTION(nested, "URI with scheme view-source didn't QI to nested!");
+      nested->GetInnerURI(getter_AddRefs(mViewSourceBaseURI));
+    } else {
+      mViewSourceBaseURI = orig;
+    }
+  }
+  return mViewSourceBaseURI;
+}
+
 // Speculative loading
 
 already_AddRefed
diff --git a/parser/html/nsHtml5TreeOpExecutor.h b/parser/html/nsHtml5TreeOpExecutor.h
index e440e430823..d8db889dfaf 100644
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -112,6 +112,8 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
 
     nsCOMPtr mSpeculationBaseURI;
 
+    nsCOMPtr mViewSourceBaseURI;
+
     /**
      * Whether the parser has started
      */
@@ -416,6 +418,8 @@ class nsHtml5TreeOpExecutor : public nsContentSink,
     }
 #endif
 
+    nsIURI* GetViewSourceBaseURI();
+
     void PreloadScript(const nsAString& aURL,
                        const nsAString& aCharset,
                        const nsAString& aType);
diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp
index f86c5bc45c8..c03d2fd5fbe 100644
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -65,6 +65,9 @@
 #include "nsEscape.h"
 #include "mozilla/dom/Element.h"
 #include "nsHtml5SVGLoadDispatcher.h"
+#include "nsIURI.h"
+#include "nsIProtocolHandler.h"
+#include "nsNetUtil.h"
 
 namespace dom = mozilla::dom;
 
@@ -125,6 +128,7 @@ nsHtml5TreeOperation::~nsHtml5TreeOperation()
     case eTreeOpAppendText:
     case eTreeOpAppendComment:
     case eTreeOpAppendCommentToDocument:
+    case eTreeOpAddViewSourceHref:
       delete[] mTwo.unicharPtr;
       break;
     case eTreeOpSetDocumentCharset:
@@ -707,6 +711,69 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
       }
       return rv;
     }
+    case eTreeOpAddClass: {
+      nsIContent* node = *(mOne.node);
+      PRUnichar* str = mTwo.unicharPtr;
+      nsDependentString depStr(str);
+      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);
+
+      nsIDocument* doc = aBuilder->GetDocument();
+
+      const nsCString& charset = doc->GetDocumentCharacterSet();
+      nsCOMPtr uri;
+      rv = NS_NewURI(getter_AddRefs(uri),
+                     relative,
+                     charset.get(),
+                     aBuilder->GetViewSourceBaseURI());
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Reuse the fix for bug 467852
+      // URLs that execute script (e.g. "javascript:" URLs) should just be
+      // ignored.  There's nothing reasonable we can do with them, and allowing
+      // them to execute in the context of the view-source window presents a
+      // security risk.  Just return the empty string in this case.
+      bool openingExecutesScript = false;
+      rv = NS_URIChainHasFlags(uri,
+                               nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT,
+                               &openingExecutesScript);
+      NS_ENSURE_SUCCESS(rv, NS_OK);
+      if (openingExecutesScript) {
+        return NS_OK;
+      }
+
+      nsCAutoString viewSourceUrl;
+
+      // URLs that return data (e.g. "http:" URLs) should be prefixed with
+      // "view-source:".  URLs that don't return data should just be returned
+      // undecorated.
+      bool doesNotReturnData = false;
+      rv = NS_URIChainHasFlags(uri,
+                               nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
+                               &doesNotReturnData);
+      NS_ENSURE_SUCCESS(rv, NS_OK);
+      if (!doesNotReturnData) {
+        viewSourceUrl.AssignLiteral("view-source:");
+      }
+
+      nsCAutoString spec;
+      uri->GetSpec(spec);
+
+      viewSourceUrl.Append(spec);
+
+      nsAutoString utf16;
+      CopyUTF8toUTF16(viewSourceUrl, utf16);
+
+      node->SetAttr(kNameSpaceID_None, nsGkAtoms::href, utf16, true);
+      return rv;
+    }
     default: {
       NS_NOTREACHED("Bogus tree op");
     }
diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h
index c2839b9fa6c..9b4ee000d49 100644
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -84,6 +84,8 @@ enum eHtml5TreeOperation {
   eTreeOpSetStyleLineNumber,
   eTreeOpSetScriptLineNumberAndFreeze,
   eTreeOpSvgLoad,
+  eTreeOpAddClass,
+  eTreeOpAddViewSourceHref,
   eTreeOpStartLayout
 };
 
@@ -293,6 +295,17 @@ class nsHtml5TreeOperation {
       mInt = aInt;
     }
 
+    inline void InitAddClass(nsIContent** aNode, const PRUnichar* aClass) {
+      NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+        "Op code must be uninitialized when initializing.");
+      NS_PRECONDITION(aNode, "Initialized tree op with null node.");
+      NS_PRECONDITION(aClass, "Initialized tree op with null string.");
+      // aClass must be a literal string that does not need freeing
+      mOpCode = eTreeOpAddClass;
+      mOne.node = aNode;
+      mTwo.unicharPtr = (PRUnichar*)aClass;
+    }
+
     inline bool IsRunScript() {
       return mOpCode == eTreeOpRunScript;
     }
diff --git a/parser/html/nsHtml5UTF16Buffer.cpp b/parser/html/nsHtml5UTF16Buffer.cpp
index 1639039e0fc..b7cb0484893 100644
--- a/parser/html/nsHtml5UTF16Buffer.cpp
+++ b/parser/html/nsHtml5UTF16Buffer.cpp
@@ -33,17 +33,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 #include "nsHtml5Tokenizer.h"
diff --git a/parser/html/nsHtml5UTF16Buffer.h b/parser/html/nsHtml5UTF16Buffer.h
index 891cc232fec..72d7413778a 100644
--- a/parser/html/nsHtml5UTF16Buffer.h
+++ b/parser/html/nsHtml5UTF16Buffer.h
@@ -34,17 +34,13 @@
 #include "nsString.h"
 #include "nsINameSpaceManager.h"
 #include "nsIContent.h"
-#include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
-#include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
-#include "nsHtml5NamedCharacters.h"
-#include "nsHtml5NamedCharactersAccel.h"
+#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Atoms.h"
 #include "nsHtml5ByteReadable.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsAHtml5TreeBuilderState.h"
 #include "nsHtml5Macros.h"
 
 class nsHtml5StreamParser;
diff --git a/parser/htmlparser/public/nsIParser.h b/parser/htmlparser/public/nsIParser.h
index a407c56f6c3..4e232b36f29 100644
--- a/parser/htmlparser/public/nsIParser.h
+++ b/parser/htmlparser/public/nsIParser.h
@@ -301,7 +301,7 @@ class nsIParser : public nsISupports {
     /**
      * Marks the HTML5 parser as not a script-created parser.
      */
-    virtual void MarkAsNotScriptCreated() = 0;
+    virtual void MarkAsNotScriptCreated(const char* aCommand) = 0;
 
     /**
      * True if this is a script-created HTML5 parser.
diff --git a/parser/htmlparser/src/nsParser.cpp b/parser/htmlparser/src/nsParser.cpp
index ff194a30457..005acf9b318 100644
--- a/parser/htmlparser/src/nsParser.cpp
+++ b/parser/htmlparser/src/nsParser.cpp
@@ -1824,7 +1824,7 @@ nsParser::EndEvaluatingParserInsertedScript()
 }
 
 void
-nsParser::MarkAsNotScriptCreated()
+nsParser::MarkAsNotScriptCreated(const char* aCommand)
 {
 }
 
diff --git a/parser/htmlparser/src/nsParser.h b/parser/htmlparser/src/nsParser.h
index 41351e1c9d2..7cdb13e3e40 100644
--- a/parser/htmlparser/src/nsParser.h
+++ b/parser/htmlparser/src/nsParser.h
@@ -343,7 +343,7 @@ class nsParser : public nsIParser,
     /**
      * No-op.
      */
-    virtual void MarkAsNotScriptCreated();
+    virtual void MarkAsNotScriptCreated(const char* aCommand);
 
     /**
      * Always false.