Bug 482921 part 1 - Implement HTML syntax highlighting using the new parser. r=Olli.Pettay.
authorHenri Sivonen <hsivonen@iki.fi>
Fri, 30 Jul 2010 13:15:38 +0300
changeset 79505 175a0afe3c436b9ac6fe84bab87b5ac8a808b732
parent 79504 a6c58f76edb503841563570d6114f422a5f2f0b3
child 79506 b8fc820633d844f789acd60e5fb3de424963a236
push id21408
push userkhuey@mozilla.com
push dateTue, 01 Nov 2011 14:32:20 +0000
treeherdermozilla-central@cd9add22f090 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersOlli
bugs482921
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 482921 part 1 - Implement HTML syntax highlighting using the new parser. r=Olli.Pettay.
content/html/document/src/nsHTMLDocument.cpp
parser/html/Makefile.in
parser/html/javasrc/Tokenizer.java
parser/html/nsHtml5AttributeName.cpp
parser/html/nsHtml5AttributeName.h
parser/html/nsHtml5ElementName.cpp
parser/html/nsHtml5ElementName.h
parser/html/nsHtml5Highlighter.cpp
parser/html/nsHtml5Highlighter.h
parser/html/nsHtml5HtmlAttributes.cpp
parser/html/nsHtml5HtmlAttributes.h
parser/html/nsHtml5MetaScanner.cpp
parser/html/nsHtml5MetaScanner.h
parser/html/nsHtml5Parser.cpp
parser/html/nsHtml5Parser.h
parser/html/nsHtml5Portability.h
parser/html/nsHtml5StackNode.cpp
parser/html/nsHtml5StackNode.h
parser/html/nsHtml5StateSnapshot.cpp
parser/html/nsHtml5StateSnapshot.h
parser/html/nsHtml5StreamParser.cpp
parser/html/nsHtml5StreamParser.h
parser/html/nsHtml5Tokenizer.cpp
parser/html/nsHtml5Tokenizer.h
parser/html/nsHtml5TokenizerCppSupplement.h
parser/html/nsHtml5TokenizerHSupplement.h
parser/html/nsHtml5TreeBuilder.cpp
parser/html/nsHtml5TreeBuilder.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
parser/html/nsHtml5TreeOperation.cpp
parser/html/nsHtml5TreeOperation.h
parser/html/nsHtml5UTF16Buffer.cpp
parser/html/nsHtml5UTF16Buffer.h
parser/htmlparser/public/nsIParser.h
parser/htmlparser/src/nsParser.cpp
parser/htmlparser/src/nsParser.h
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -571,17 +571,18 @@ nsHTMLDocument::TryDefaultCharset( nsIMa
 
 void
 nsHTMLDocument::StartAutodetection(nsIDocShell *aDocShell, nsACString& aCharset,
                                    const char* aCommand)
 {
   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 <nsIParserFilter> cdetflt;
 
   nsresult rv_detect;
   if(!gInitDetector) {
     const nsAdoptingCString& detector_name =
       Preferences::GetLocalizedCString("intl.charset.detector");
@@ -670,17 +671,18 @@ nsHTMLDocument::StartDocumentLoad(const 
     NS_ASSERTION(mIsRegularHTML,
                  "Hey, someone forgot to reset mIsRegularHTML!!!");
   }
 #endif
   
   if (loadAsHtml5 && 
       !(contentType.EqualsLiteral("text/html") && 
         aCommand && 
-        !nsCRT::strcmp(aCommand, "view"))) {
+        (!nsCRT::strcmp(aCommand, "view") ||
+         !nsCRT::strcmp(aCommand, "view-source")))) {
     loadAsHtml5 = false;
   }
   
   // TODO: Proper about:blank treatment is bug 543435
   if (loadAsHtml5) {
     // mDocumentURI hasn't been set, yet, so get the URI from the channel
     nsCOMPtr<nsIURI> uri;
     aChannel->GetOriginalURI(getter_AddRefs(uri));
@@ -723,17 +725,17 @@ nsHTMLDocument::StartDocumentLoad(const 
     return rv;
   }
 
   nsCOMPtr<nsICachingChannel> cachingChan = do_QueryInterface(aChannel);
 
   if (needsParser) {
     if (loadAsHtml5) {
       mParser = nsHtml5Module::NewHtml5Parser();
-      mParser->MarkAsNotScriptCreated();
+      mParser->MarkAsNotScriptCreated(aCommand);
     } else {
       mParser = do_CreateInstance(kCParserCID, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   PRInt32 textType = GET_BIDI_OPTION_TEXTTYPE(GetBidiOptions());
 
--- a/parser/html/Makefile.in
+++ b/parser/html/Makefile.in
@@ -102,16 +102,17 @@ CPPSRCS		= \
 		nsHtml5TreeOperation.cpp \
 		nsHtml5TreeOpStage.cpp \
 		nsHtml5StateSnapshot.cpp \
 		nsHtml5TreeOpExecutor.cpp \
 		nsHtml5StreamParser.cpp \
 		nsHtml5Speculation.cpp \
 		nsHtml5SpeculativeLoad.cpp \
 		nsHtml5SVGLoadDispatcher.cpp \
+		nsHtml5Highlighter.cpp \
 		$(NULL)
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= \
 		-I$(srcdir)/../../content/base/src \
--- a/parser/html/javasrc/Tokenizer.java
+++ b/parser/html/javasrc/Tokenizer.java
@@ -1215,16 +1215,19 @@ public class Tokenizer implements Locato
         if (metaBoundaryPassed && ElementName.META == tagName
                 && AttributeName.CHARSET == attributeName) {
             err("A \u201Ccharset\u201D attribute on a \u201Cmeta\u201D element found after the first 512 bytes.");
         }
         // ]NOCPP]
         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()) {
                 val = newAsciiLowerCaseStringFromString(val);
             }
             // ]NOCPP]
             attributes.addAttribute(attributeName, val
             // [NOCPP[
@@ -1311,18 +1314,27 @@ public class Tokenizer implements Locato
                 break;
         }
 
         /**
          * The number of <code>char</code>s in <code>buf</code> that have
          * 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);
         } else {
             buffer.setStart(pos + 1);
         }
         return lastCR;
     }
@@ -3144,16 +3156,17 @@ public class Tokenizer implements Locato
                         }
 
                         /*
                          * Otherwise, return a character token for the character
                          * corresponding to the entity name (as given by the
                          * second column of the named character references
                          * table).
                          */
+                        // CPPONLY: mViewSource.CompletedNamedCharacterReference();
                         @Const @NoLength char[] val = NamedCharacters.VALUES[candidate];
                         if (
                         // [NOCPP[
                         val.length == 1
                         // ]NOCPP]
                         // CPPONLY: val[1] == 0
                         ) {
                             emitOrAppendOne(val, returnState);
--- a/parser/html/nsHtml5AttributeName.cpp
+++ b/parser/html/nsHtml5AttributeName.cpp
@@ -28,27 +28,23 @@
 #define nsHtml5AttributeName_cpp__
 
 #include "prtypes.h"
 #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"
 #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"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
--- a/parser/html/nsHtml5AttributeName.h
+++ b/parser/html/nsHtml5AttributeName.h
@@ -29,27 +29,23 @@
 #define nsHtml5AttributeName_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5ElementName;
--- a/parser/html/nsHtml5ElementName.cpp
+++ b/parser/html/nsHtml5ElementName.cpp
@@ -28,27 +28,23 @@
 #define nsHtml5ElementName_cpp__
 
 #include "prtypes.h"
 #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"
 #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"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
--- a/parser/html/nsHtml5ElementName.h
+++ b/parser/html/nsHtml5ElementName.h
@@ -29,27 +29,23 @@
 #define nsHtml5ElementName_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
new file mode 100644
--- /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 <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#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());
+}
new file mode 100644
--- /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 <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#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 <span> 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 <span> or <a>.
+     */
+    void EndInline();
+
+    /**
+     * Starts an <a>.
+     */
+    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 <span> and
+     * <a> 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 <pre>.
+     */
+    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<nsHtml5TreeOperation> mOpQueue;
+
+    /**
+     * The tree op stage for the tree op executor.
+     */
+    nsAHtml5TreeOpSink* mOpSink;
+
+    /**
+     * Memory for element handles.
+     */
+    nsAutoArrayPtr<nsIContent*> mHandles;
+
+    /**
+     * Number of handles used in mHandles
+     */
+    PRInt32 mHandlesUsed;
+
+    /**
+     * A holder for old contents of mHandles
+     */
+    nsTArray<nsAutoArrayPtr<nsIContent*> > mOldHandles;
+
+    /**
+     * The element stack.
+     */
+    nsTArray<nsIContent**> 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_
--- a/parser/html/nsHtml5HtmlAttributes.cpp
+++ b/parser/html/nsHtml5HtmlAttributes.cpp
@@ -29,27 +29,23 @@
 #define nsHtml5HtmlAttributes_cpp__
 
 #include "prtypes.h"
 #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"
 #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"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5StackNode.h"
--- a/parser/html/nsHtml5HtmlAttributes.h
+++ b/parser/html/nsHtml5HtmlAttributes.h
@@ -30,27 +30,23 @@
 #define nsHtml5HtmlAttributes_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
--- a/parser/html/nsHtml5MetaScanner.cpp
+++ b/parser/html/nsHtml5MetaScanner.cpp
@@ -29,27 +29,23 @@
 #define nsHtml5MetaScanner_cpp__
 
 #include "prtypes.h"
 #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"
 #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"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
--- a/parser/html/nsHtml5MetaScanner.h
+++ b/parser/html/nsHtml5MetaScanner.h
@@ -30,27 +30,23 @@
 #define nsHtml5MetaScanner_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -123,17 +123,18 @@ NS_IMETHODIMP_(void)
 nsHtml5Parser::GetCommand(nsCString& aCommand)
 {
   aCommand.Assign("view");
 }
 
 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)
 nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
 {
   NS_ASSERTION(aParserCommand == eViewNormal, 
                "Parser command was not eViewNormal.");
 }
@@ -693,20 +694,31 @@ nsHtml5Parser::BeginEvaluatingParserInse
 
 void
 nsHtml5Parser::EndEvaluatingParserInsertedScript()
 {
   --mParserInsertedScriptsBeingEvaluated;
 }
 
 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
 nsHtml5Parser::IsScriptCreated()
 {
   return !mStreamParser;
 }
 
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -241,18 +241,21 @@ class nsHtml5Parser : public nsIParser,
     /**
      * Call immediately after having evaluated a parser-inserted script.
      */
     virtual void EndEvaluatingParserInsertedScript();
 
     /**
      * 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.
      */
     virtual bool IsScriptCreated();
 
     /* End nsIParser  */
 
--- a/parser/html/nsHtml5Portability.h
+++ b/parser/html/nsHtml5Portability.h
@@ -29,27 +29,23 @@
 #define nsHtml5Portability_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
--- a/parser/html/nsHtml5StackNode.cpp
+++ b/parser/html/nsHtml5StackNode.cpp
@@ -29,27 +29,23 @@
 #define nsHtml5StackNode_cpp__
 
 #include "prtypes.h"
 #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"
 #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"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
--- a/parser/html/nsHtml5StackNode.h
+++ b/parser/html/nsHtml5StackNode.h
@@ -30,27 +30,23 @@
 #define nsHtml5StackNode_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
--- a/parser/html/nsHtml5StateSnapshot.cpp
+++ b/parser/html/nsHtml5StateSnapshot.cpp
@@ -28,27 +28,23 @@
 #define nsHtml5StateSnapshot_cpp__
 
 #include "prtypes.h"
 #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"
 #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"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
--- a/parser/html/nsHtml5StateSnapshot.h
+++ b/parser/html/nsHtml5StateSnapshot.h
@@ -29,27 +29,23 @@
 #define nsHtml5StateSnapshot_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -48,16 +48,17 @@
 #include "nsIHttpChannel.h"
 #include "nsHtml5Parser.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5Module.h"
 #include "nsHtml5RefPtr.h"
 #include "nsIScriptError.h"
 #include "mozilla/Preferences.h"
+#include "nsHtml5Highlighter.h"
 
 using namespace mozilla;
 
 static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
 
 PRInt32 nsHtml5StreamParser::sTimerInitialDelay = 120;
 PRInt32 nsHtml5StreamParser::sTimerSubsequentDelay = 120;
 
@@ -169,41 +170,50 @@ class nsHtml5LoadFlusher : public nsRunn
     NS_IMETHODIMP Run()
     {
       mExecutor->FlushSpeculativeLoads();
       return NS_OK;
     }
 };
 
 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)
   , mSpeculationMutex("nsHtml5StreamParser mSpeculationMutex")
   , mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
   , mThread(nsHtml5Module::GetStreamParserThread())
   , 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);
   mAtomTable.Init(); // we aren't checking for OOM anyway...
 #ifdef DEBUG
   mAtomTable.SetPermittedLookupThread(mThread);
 #endif
   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)
   // on the main thread.
   const nsAdoptingCString& detectorName =
     Preferences::GetLocalizedCString("intl.charset.detector");
   if (!detectorName.IsEmpty()) {
     nsCAutoString detectorContractID;
@@ -680,16 +690,21 @@ nsHtml5StreamParser::OnStartRequest(nsIR
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   if (mObserver) {
     mObserver->OnStartRequest(aRequest, aContext);
   }
   mRequest = aRequest;
 
   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);
   mTokenizer->start();
   mExecutor->Start();
   mExecutor->StartReadingFromStage();
   /*
    * If you move the following line, be very careful not to cause 
@@ -708,17 +723,21 @@ nsHtml5StreamParser::OnStartRequest(nsIR
     return NS_ERROR_OUT_OF_MEMORY;
   }
   NS_ASSERTION(!mFirstBuffer, "How come we have the first buffer set?");
   NS_ASSERTION(!mLastBuffer, "How come we have the last buffer set?");
   mFirstBuffer = mLastBuffer = newBuf;
 
   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<nsIHttpChannel> httpChannel(do_QueryInterface(mRequest, &rv));
   if (NS_SUCCEEDED(rv)) {
     nsCAutoString method;
     httpChannel->GetRequestMethod(method);
     // XXX does Necko have a way to renavigate POST, etc. without hitting
     // the network?
     if (!method.EqualsLiteral("GET")) {
       // This is the old Gecko behavior but the HTML5 spec disagrees.
@@ -1002,16 +1021,19 @@ nsHtml5StreamParser::FlushTreeOpsAndDisa
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   if (mFlushTimerArmed) {
     // avoid calling Cancel if the flush timer isn't armed to avoid acquiring
     // a mutex
     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");
   }
 }
 
 void
 nsHtml5StreamParser::ParseAvailableData()
@@ -1044,16 +1066,19 @@ nsHtml5StreamParser::ParseAvailableData(
             return; // no more data for now but expecting more
           case STREAM_ENDED:
             if (mAtEOF) {
               return;
             }
             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:
             NS_NOTREACHED("It should be impossible to reach this.");
             return;
         }
       }
       mFirstBuffer = mFirstBuffer->next;
@@ -1064,17 +1089,17 @@ nsHtml5StreamParser::ParseAvailableData(
     mFirstBuffer->adjust(mLastWasCR);
     mLastWasCR = false;
     if (mFirstBuffer->hasMore()) {
       mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
       // At this point, internalEncodingDeclaration() may have called 
       // 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,
                                  mFirstBuffer->getStart(),
                                  mTokenizer->getLineNumber(),
                                  mTreeBuilder->newSnapshot());
         mTreeBuilder->AddSnapshotToScript(speculation->GetSnapshot(), 
                                           speculation->GetStartLineNumber());
@@ -1109,16 +1134,18 @@ public:
 };
 
 void
 nsHtml5StreamParser::ContinueAfterScripts(nsHtml5Tokenizer* aTokenizer, 
                                           nsHtml5TreeBuilder* aTreeBuilder,
                                           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;
   }
   #ifdef DEBUG
     mExecutor->AssertStageEmpty();
   #endif
   bool speculationFailed = false;
   {
@@ -1324,21 +1351,30 @@ nsHtml5StreamParser::TimerFlush()
   mFlushTimerArmed = false;
 
   mFlushTimerEverFired = true;
 
   if (IsTerminatedOrInterrupted()) {
     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");
+      }
     }
   }
 }
 
 void
 nsHtml5StreamParser::MarkAsBroken()
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -55,16 +55,38 @@
 #include "nsITimer.h"
 #include "nsICharsetDetector.h"
 
 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.
    */
   BOM_SNIFFING_NOT_STARTED = 0,
 
   /**
    * BOM sniffing is ongoing, and the first byte of an UTF-16LE BOM has been
@@ -113,17 +135,18 @@ class nsHtml5StreamParser : public nsISt
   public:
     NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHtml5StreamParser, nsIStreamListener)
 
     static void InitializeStatics();
 
     nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
-                        nsHtml5Parser* aOwner);
+                        nsHtml5Parser* aOwner,
+                        eParserMode aMode);
                         
     virtual ~nsHtml5StreamParser();
 
     // nsIRequestObserver methods:
     NS_DECL_NSIREQUESTOBSERVER
     // nsIStreamListener methods:
     NS_DECL_NSISTREAMLISTENER
     
@@ -491,16 +514,21 @@ class nsHtml5StreamParser : public nsISt
     bool                          mFlushTimerArmed;
 
     /**
      * False initially and true after the timer has fired at least once.
      */
     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
      * timer hasn't fired previously in this parse.
      */
     static PRInt32                sTimerInitialDelay;
 
     /**
      * The pref html5.flushtimer.subsequentdelay: Time in milliseconds between
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -29,30 +29,27 @@
  */
 
 #define nsHtml5Tokenizer_cpp__
 
 #include "prtypes.h"
 #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"
 #include "nsHtml5ArrayCopy.h"
 #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"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5UTF16Buffer.h"
@@ -335,16 +332,19 @@ nsHtml5Tokenizer::addAttributeWithoutVal
   }
 }
 
 void 
 nsHtml5Tokenizer::addAttributeWithValue()
 {
   if (attributeName) {
     nsString* val = longStrBufToString();
+    if (mViewSource) {
+      mViewSource->MaybeLinkifyAttributeValue(attributeName, val);
+    }
     attributes->addAttribute(attributeName, val);
     attributeName = nsnull;
   }
 }
 
 void 
 nsHtml5Tokenizer::start()
 {
@@ -383,17 +383,23 @@ nsHtml5Tokenizer::tokenizeBuffer(nsHtml5
       cstart = start;
       break;
     }
     default: {
       cstart = PR_INT32_MAX;
       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 {
     buffer->setStart(pos + 1);
   }
   return lastCR;
 }
 
@@ -1528,16 +1534,17 @@ nsHtml5Tokenizer::stateLoop(PRInt32 stat
                 appendStrBufToLongStrBuf();
                 state = returnState;
                 reconsume = true;
                 NS_HTML5_CONTINUE(stateloop);
               }
             }
 
           }
+
           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)) {
@@ -3263,16 +3270,2888 @@ nsHtml5Tokenizer::stateLoop(PRInt32 stat
   }
   stateloop_end: ;
   flushChars(buf, pos);
   stateSave = state;
   returnStateSave = returnState;
   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()
 {
   doctypeName = nsHtml5Atoms::emptystring;
   if (systemIdentifier) {
     nsHtml5Portability::releaseString(systemIdentifier);
     systemIdentifier = nsnull;
   }
@@ -3894,8 +6773,10 @@ nsHtml5Tokenizer::initializeStatics()
 }
 
 void
 nsHtml5Tokenizer::releaseStatics()
 {
 }
 
 
+#include "nsHtml5TokenizerCppSupplement.h"
+
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -30,30 +30,27 @@
 
 #ifndef nsHtml5Tokenizer_h__
 #define nsHtml5Tokenizer_h__
 
 #include "prtypes.h"
 #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"
 #include "nsHtml5ArrayCopy.h"
 #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;
 
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
 class nsHtml5ElementName;
 class nsHtml5HtmlAttributes;
@@ -214,16 +211,17 @@ class nsHtml5Tokenizer
     void attributeNameComplete();
     void addAttributeWithoutValue();
     void addAttributeWithValue();
   public:
     void start();
     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()
     {
       silentCarriageReturn();
       adjustDoubleHyphenAndAppendToLongStrBufAndErr('\n');
     }
 
     inline void adjustDoubleHyphenAndAppendToLongStrBufLineFeed()
@@ -286,16 +284,18 @@ class nsHtml5Tokenizer
     bool isInDataState();
     void resetToDataState();
     void loadState(nsHtml5Tokenizer* other);
     void initializeWithoutStarting();
     void setEncodingDeclarationHandler(nsHtml5StreamParser* encodingDeclarationHandler);
     ~nsHtml5Tokenizer();
     static void initializeStatics();
     static void releaseStatics();
+
+#include "nsHtml5TokenizerHSupplement.h"
 };
 
 #define NS_HTML5TOKENIZER_DATA_AND_RCDATA_MASK ~1
 #define NS_HTML5TOKENIZER_DATA 0
 #define NS_HTML5TOKENIZER_RCDATA 1
 #define NS_HTML5TOKENIZER_SCRIPT_DATA 2
 #define NS_HTML5TOKENIZER_RAWTEXT 3
 #define NS_HTML5TOKENIZER_SCRIPT_DATA_ESCAPED 4
new file mode 100644
--- /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 <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+void
+nsHtml5Tokenizer::EnableViewSource(nsHtml5Highlighter* aHighlighter)
+{
+  mViewSource = aHighlighter;
+}
+
+bool
+nsHtml5Tokenizer::FlushViewSource()
+{
+  return mViewSource->FlushOps();
+}
+
+void
+nsHtml5Tokenizer::StartViewSource()
+{
+  mViewSource->Start();
+}
+
+void
+nsHtml5Tokenizer::EndViewSource()
+{
+  mViewSource->End();
+}
new file mode 100644
--- /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 <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+nsAutoPtr<nsHtml5Highlighter> mViewSource;
+
+void EnableViewSource(nsHtml5Highlighter* aHighlighter);
+
+bool FlushViewSource();
+
+void StartViewSource();
+
+void EndViewSource();
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -39,17 +39,16 @@
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
 #include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
 #include "nsHtml5Parser.h"
 #include "nsHtml5Atoms.h"
-#include "nsHtml5ByteReadable.h"
 #include "nsHtml5TreeOperation.h"
 #include "nsHtml5PendingNotification.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5StreamParser.h"
 #include "nsAHtml5TreeBuilderState.h"
 
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -40,17 +40,16 @@
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsTraceRefcnt.h"
 #include "jArray.h"
 #include "nsHtml5DocumentMode.h"
 #include "nsHtml5ArrayCopy.h"
 #include "nsHtml5Parser.h"
 #include "nsHtml5Atoms.h"
-#include "nsHtml5ByteReadable.h"
 #include "nsHtml5TreeOperation.h"
 #include "nsHtml5PendingNotification.h"
 #include "nsHtml5StateSnapshot.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5StreamParser.h"
 #include "nsAHtml5TreeBuilderState.h"
 
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -888,16 +888,34 @@ nsHtml5TreeOpExecutor::MoveOpsFrom(nsTAr
 }
 
 void
 nsHtml5TreeOpExecutor::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine)
 {
   GetParser()->InitializeDocWriteParserState(aState, aLine);
 }
 
+nsIURI*
+nsHtml5TreeOpExecutor::GetViewSourceBaseURI()
+{
+  if (!mViewSourceBaseURI) {
+    nsCOMPtr<nsIURI> orig = mDocument->GetOriginalURI();
+    bool isViewSource;
+    orig->SchemeIs("view-source", &isViewSource);
+    if (isViewSource) {
+      nsCOMPtr<nsINestedURI> 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<nsIURI>
 nsHtml5TreeOpExecutor::ConvertIfNotPreloadedYet(const nsAString& aURL)
 {
   if (aURL.IsEmpty()) {
     return nsnull;
   }
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -107,16 +107,18 @@ class nsHtml5TreeOpExecutor : public nsC
     
     /**
      * URLs already preloaded/preloading.
      */
     nsCStringHashSet mPreloadedURLs;
 
     nsCOMPtr<nsIURI> mSpeculationBaseURI;
 
+    nsCOMPtr<nsIURI> mViewSourceBaseURI;
+
     /**
      * Whether the parser has started
      */
     bool                          mStarted;
 
     nsHtml5TreeOpStage            mStage;
 
     eHtml5FlushState              mFlushState;
@@ -411,16 +413,18 @@ class nsHtml5TreeOpExecutor : public nsC
     void StreamEnded();
     
 #ifdef DEBUG
     void AssertStageEmpty() {
       mStage.AssertEmpty();
     }
 #endif
 
+    nsIURI* GetViewSourceBaseURI();
+
     void PreloadScript(const nsAString& aURL,
                        const nsAString& aCharset,
                        const nsAString& aType);
 
     void PreloadStyle(const nsAString& aURL, const nsAString& aCharset);
 
     void PreloadImage(const nsAString& aURL, const nsAString& aCrossOrigin);
 
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -60,16 +60,19 @@
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "nsIMutationObserver.h"
 #include "nsIFormProcessor.h"
 #include "nsIServiceManager.h"
 #include "nsEscape.h"
 #include "mozilla/dom/Element.h"
 #include "nsHtml5SVGLoadDispatcher.h"
+#include "nsIURI.h"
+#include "nsIProtocolHandler.h"
+#include "nsNetUtil.h"
 
 namespace dom = mozilla::dom;
 
 static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
 
 /**
  * Helper class that opens a notification batch if the current doc
  * is different from the executor doc.
@@ -120,16 +123,17 @@ nsHtml5TreeOperation::~nsHtml5TreeOperat
       break;
     case eTreeOpAppendDoctypeToDocument:
       delete mTwo.stringPair;
       break;
     case eTreeOpFosterParentText:
     case eTreeOpAppendText:
     case eTreeOpAppendComment:
     case eTreeOpAppendCommentToDocument:
+    case eTreeOpAddViewSourceHref:
       delete[] mTwo.unicharPtr;
       break;
     case eTreeOpSetDocumentCharset:
     case eTreeOpNeedsCharsetSwitchTo:
       delete[] mOne.charPtr;
       break;
     case eTreeOpProcessOfflineManifest:
       nsMemory::Free(mOne.unicharPtr);
@@ -702,14 +706,77 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
     case eTreeOpSvgLoad: {
       nsIContent* node = *(mOne.node);
       nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(node);
       if (NS_FAILED(NS_DispatchToMainThread(event))) {
         NS_WARNING("failed to dispatch svg load dispatcher");
       }
       return rv;
     }
+    case eTreeOpAddClass: {
+      nsIContent* node = *(mOne.node);
+      PRUnichar* str = mTwo.unicharPtr;
+      nsDependentString depStr(str);
+      node->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, depStr, true);
+      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<nsIURI> 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");
     }
   }
   return rv; // keep compiler happy
 }
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -79,16 +79,18 @@ enum eHtml5TreeOperation {
   eTreeOpUpdateStyleSheet,
   eTreeOpProcessMeta,
   eTreeOpProcessOfflineManifest,
   eTreeOpMarkMalformedIfScript,
   eTreeOpStreamEnded,
   eTreeOpSetStyleLineNumber,
   eTreeOpSetScriptLineNumberAndFreeze,
   eTreeOpSvgLoad,
+  eTreeOpAddClass,
+  eTreeOpAddViewSourceHref,
   eTreeOpStartLayout
 };
 
 class nsHtml5TreeOperationStringPair {
   private:
     nsString mPublicId;
     nsString mSystemId;
   public:
@@ -288,16 +290,27 @@ class nsHtml5TreeOperation {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
         "Op code must be uninitialized when initializing.");
       NS_PRECONDITION(aNode, "Initialized tree op with null node.");
       mOpCode = aOpCode;
       mOne.node = aNode;
       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;
     }
     
     inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine) {
       NS_ASSERTION(IsRunScript(), 
         "Setting a snapshot for a tree operation other than eTreeOpRunScript!");
       NS_PRECONDITION(aSnapshot, "Initialized tree op with null snapshot.");
--- a/parser/html/nsHtml5UTF16Buffer.cpp
+++ b/parser/html/nsHtml5UTF16Buffer.cpp
@@ -28,27 +28,23 @@
 #define nsHtml5UTF16Buffer_cpp__
 
 #include "prtypes.h"
 #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"
 #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"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5MetaScanner.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
--- a/parser/html/nsHtml5UTF16Buffer.h
+++ b/parser/html/nsHtml5UTF16Buffer.h
@@ -29,27 +29,23 @@
 #define nsHtml5UTF16Buffer_h__
 
 #include "prtypes.h"
 #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"
 #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;
 
 class nsHtml5Tokenizer;
 class nsHtml5TreeBuilder;
 class nsHtml5MetaScanner;
 class nsHtml5AttributeName;
--- a/parser/htmlparser/public/nsIParser.h
+++ b/parser/htmlparser/public/nsIParser.h
@@ -296,17 +296,17 @@ class nsIParser : public nsISupports {
     /**
      * Call immediately after having evaluated a parser-inserted script.
      */
     virtual void EndEvaluatingParserInsertedScript() = 0;
 
     /**
      * 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.
      */
     virtual bool IsScriptCreated() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIParser, NS_IPARSER_IID)
--- a/parser/htmlparser/src/nsParser.cpp
+++ b/parser/htmlparser/src/nsParser.cpp
@@ -1819,17 +1819,17 @@ nsParser::BeginEvaluatingParserInsertedS
 }
 
 void
 nsParser::EndEvaluatingParserInsertedScript()
 {
 }
 
 void
-nsParser::MarkAsNotScriptCreated()
+nsParser::MarkAsNotScriptCreated(const char* aCommand)
 {
 }
 
 bool
 nsParser::IsScriptCreated()
 {
   return false;
 }
--- a/parser/htmlparser/src/nsParser.h
+++ b/parser/htmlparser/src/nsParser.h
@@ -338,17 +338,17 @@ class nsParser : public nsIParser,
     /**
      * No-op.
      */
     virtual void EndEvaluatingParserInsertedScript();
 
     /**
      * No-op.
      */
-    virtual void MarkAsNotScriptCreated();
+    virtual void MarkAsNotScriptCreated(const char* aCommand);
 
     /**
      * Always false.
      */
     virtual bool IsScriptCreated();
 
     /**  
      *  Set to parser state to indicate whether parsing tokens can be interrupted