Bug 1319410 - Make consecutive hyphens in comments a non-error r=smaug
authorMichael[tm] Smith <mike@w3.org>
Mon, 16 Aug 2021 05:01:09 +0000
changeset 589017 5b33e3f54b589b5935d8d3d573cbff239f90f5be
parent 589016 36974366b0fbc17ac7e2ee7e6a39ce6cead207fa
child 589018 0b241b7f07e9e2a38841a86f044575fef4397fc9
push id148082
push userhsivonen@mozilla.com
push dateMon, 16 Aug 2021 05:03:43 +0000
treeherderautoland@d75852f907a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1319410
milestone93.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 1319410 - Make consecutive hyphens in comments a non-error r=smaug Also allow `<!-->` at (IE conditional) comment end See https://github.com/whatwg/html/pull/1356 See https://github.com/whatwg/html/pull/1456 Differential Revision: https://phabricator.services.mozilla.com/D82000
dom/locales/en-US/chrome/layout/htmlparser.properties
parser/html/javasrc/Tokenizer.java
parser/html/nsHtml5Tokenizer.cpp
parser/html/nsHtml5Tokenizer.h
parser/html/nsHtml5TokenizerCppSupplement.h
parser/html/nsHtml5TokenizerHSupplement.h
--- a/dom/locales/en-US/chrome/layout/htmlparser.properties
+++ b/dom/locales/en-US/chrome/layout/htmlparser.properties
@@ -71,17 +71,17 @@ errEofInTagName=End of file seen when lo
 errEofInEndTag=End of file inside end tag. Ignoring tag.
 errEofAfterLt=End of file after “<”.
 errNcrOutOfRange=Character reference outside the permissible Unicode range.
 errNcrUnassigned=Character reference expands to a permanently unassigned code point.
 errDuplicateAttribute=Duplicate attribute.
 errEofInSystemId=End of file inside system identifier.
 errExpectedSystemId=Expected a system identifier but the doctype ended.
 errMissingSpaceBeforeDoctypeName=Missing space before doctype name.
-errHyphenHyphenBang=“--!” found in comment.
+errNestedComment=Saw “<!--” within a comment. Probable cause: Nested comment (not allowed).
 errNcrZero=Character reference expands to zero.
 errNoSpaceBetweenDoctypeSystemKeywordAndQuote=No space between the doctype “SYSTEM” keyword and the quote.
 errNoSpaceBetweenPublicAndSystemIds=No space between the doctype public and system identifiers.
 errNoSpaceBetweenDoctypePublicKeywordAndQuote=No space between the doctype “PUBLIC” keyword and the quote.
 
 # Tree builder errors
 errDeepTree=The document tree is too deep. The tree will be flattened to be 513 elements deep.
 errStrayStartTag2=Stray start tag “%1$S”.
--- a/parser/html/javasrc/Tokenizer.java
+++ b/parser/html/javasrc/Tokenizer.java
@@ -216,16 +216,24 @@ public class Tokenizer implements Locato
     public static final int SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH = 71;
 
     public static final int SCRIPT_DATA_DOUBLE_ESCAPE_END = 72;
 
     public static final int PROCESSING_INSTRUCTION = 73;
 
     public static final int PROCESSING_INSTRUCTION_QUESTION_MARK = 74;
 
+    public static final int COMMENT_LESSTHAN = 76;
+
+    public static final int COMMENT_LESSTHAN_BANG = 77;
+
+    public static final int COMMENT_LESSTHAN_BANG_DASH = 78;
+
+    public static final int COMMENT_LESSTHAN_BANG_DASH_DASH = 79;
+
     /**
      * Magic value for UTF-16 operations.
      */
     private static final int LEAD_OFFSET = (0xD800 - (0x10000 >> 10));
 
     /**
      * UTF-16 code unit array containing less than and greater than for emitting
      * those characters on certain parse errors.
@@ -1024,30 +1032,31 @@ public class Tokenizer implements Locato
             case FATAL:
                 fatal("The document is not mappable to XML 1.0 due to a trailing hyphen in a comment.");
                 break;
         }
     }
 
     // ]NOCPP]
 
-    @Inline private void adjustDoubleHyphenAndAppendToStrBufAndErr(char c)
+    @Inline private void adjustDoubleHyphenAndAppendToStrBufAndErr(char c, boolean reportedConsecutiveHyphens)
             throws SAXException {
-        errConsecutiveHyphens();
         // [NOCPP[
         switch (commentPolicy) {
             case ALTER_INFOSET:
                 strBufLen--;
                 // WARNING!!! This expands the worst case of the buffer length
                 // given the length of input!
                 appendStrBuf(' ');
                 appendStrBuf('-');
                 // CPPONLY: MOZ_FALLTHROUGH;
             case ALLOW:
-                warn("The document is not mappable to XML 1.0 due to two consecutive hyphens in a comment.");
+                if (!reportedConsecutiveHyphens) {
+                    warn("The document is not mappable to XML 1.0 due to two consecutive hyphens in a comment.");
+                }
                 // ]NOCPP]
                 appendStrBuf(c);
                 // [NOCPP[
                 break;
             case FATAL:
                 fatal("The document is not mappable to XML 1.0 due to two consecutive hyphens in a comment.");
                 break;
         }
@@ -1459,16 +1468,17 @@ public class Tokenizer implements Locato
             strBuf = newBuf;
         }
     }
     // ]NOCPP]
 
     @SuppressWarnings("unused") private int stateLoop(int state, char c,
             int pos, @NoLength char[] buf, boolean reconsume, int returnState,
             int endPos) throws SAXException {
+        boolean reportedConsecutiveHyphens = false;
         /*
          * Idioms used in this code:
          *
          *
          * Consuming the next input character
          *
          * To consume the next input character, the code does this: if (++pos ==
          * endPos) { break stateloop; } c = checkChar(buf, pos);
@@ -2535,16 +2545,17 @@ public class Tokenizer implements Locato
                                 errBogusComment();
                                 reconsume = true;
                                 state = transition(state, Tokenizer.BOGUS_COMMENT, reconsume, pos);
                                 continue stateloop;
                         }
                     }
                     // CPPONLY: MOZ_FALLTHROUGH;
                 case COMMENT_START:
+                    reportedConsecutiveHyphens = false;
                     commentstartloop: for (;;) {
                         if (++pos == endPos) {
                             break stateloop;
                         }
                         c = checkChar(buf, pos);
                         /*
                          * Comment start state
                          *
@@ -2567,16 +2578,20 @@ public class Tokenizer implements Locato
                                 errPrematureEndOfComment();
                                 /* Emit the comment token. */
                                 emitComment(0, pos);
                                 /*
                                  * Switch to the data state.
                                  */
                                 state = transition(state, Tokenizer.DATA, reconsume, pos);
                                 continue stateloop;
+                            case '<':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                                continue stateloop;
                             case '\r':
                                 appendStrBufCarriageReturn();
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 break stateloop;
                             case '\n':
                                 appendStrBufLineFeed();
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 break commentstartloop;
@@ -2612,16 +2627,20 @@ public class Tokenizer implements Locato
                                 /*
                                  * U+002D HYPHEN-MINUS (-) Switch to the comment
                                  * end dash state
                                  */
                                 appendStrBuf(c);
                                 state = transition(state, Tokenizer.COMMENT_END_DASH, reconsume, pos);
                                 break commentloop;
                             // continue stateloop;
+                            case '<':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                                continue stateloop;
                             case '\r':
                                 appendStrBufCarriageReturn();
                                 break stateloop;
                             case '\n':
                                 appendStrBufLineFeed();
                                 continue;
                             case '\u0000':
                                 c = '\uFFFD';
@@ -2654,16 +2673,20 @@ public class Tokenizer implements Locato
                                 /*
                                  * U+002D HYPHEN-MINUS (-) Switch to the comment
                                  * end state
                                  */
                                 appendStrBuf(c);
                                 state = transition(state, Tokenizer.COMMENT_END, reconsume, pos);
                                 break commentenddashloop;
                             // continue stateloop;
+                            case '<':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                                continue stateloop;
                             case '\r':
                                 appendStrBufCarriageReturn();
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 break stateloop;
                             case '\n':
                                 appendStrBufLineFeed();
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 continue stateloop;
@@ -2708,44 +2731,49 @@ public class Tokenizer implements Locato
                                 state = transition(state, Tokenizer.DATA, reconsume, pos);
                                 continue stateloop;
                             case '-':
                                 /* U+002D HYPHEN-MINUS (-) Parse error. */
                                 /*
                                  * Append a U+002D HYPHEN-MINUS (-) character to
                                  * the comment token's data.
                                  */
-                                adjustDoubleHyphenAndAppendToStrBufAndErr(c);
+                                adjustDoubleHyphenAndAppendToStrBufAndErr(c, reportedConsecutiveHyphens);
+                                reportedConsecutiveHyphens = true;
                                 /*
                                  * Stay in the comment end state.
                                  */
                                 continue;
+                            case '<':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                                continue stateloop;
                             case '\r':
                                 adjustDoubleHyphenAndAppendToStrBufCarriageReturn();
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 break stateloop;
                             case '\n':
                                 adjustDoubleHyphenAndAppendToStrBufLineFeed();
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 continue stateloop;
                             case '!':
-                                errHyphenHyphenBang();
                                 appendStrBuf(c);
                                 state = transition(state, Tokenizer.COMMENT_END_BANG, reconsume, pos);
                                 continue stateloop;
                             case '\u0000':
                                 c = '\uFFFD';
                                 // CPPONLY: MOZ_FALLTHROUGH;
                             default:
                                 /*
                                  * Append two U+002D HYPHEN-MINUS (-) characters
                                  * and the input character to the comment
                                  * token's data.
                                  */
-                                adjustDoubleHyphenAndAppendToStrBufAndErr(c);
+                                adjustDoubleHyphenAndAppendToStrBufAndErr(c, reportedConsecutiveHyphens);
+                                reportedConsecutiveHyphens = true;
                                 /*
                                  * Switch to the comment state.
                                  */
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 continue stateloop;
                         }
                     }
                 case COMMENT_END_BANG:
@@ -2805,16 +2833,158 @@ public class Tokenizer implements Locato
                                 appendStrBuf(c);
                                 /*
                                  * Switch to the comment state.
                                  */
                                 state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                                 continue stateloop;
                         }
                     }
+                case COMMENT_LESSTHAN:
+                    for (;;) {
+                        if (++pos == endPos) {
+                            break stateloop;
+                        }
+                        c = checkChar(buf, pos);
+                        switch (c) {
+                            case '!':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN_BANG, reconsume, pos);
+                                continue stateloop;
+                            case '<':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                                continue stateloop;
+                            case '-':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_END_DASH, reconsume, pos);
+                                continue stateloop;
+                            case '\r':
+                                appendStrBufCarriageReturn();
+                                break stateloop;
+                            case '\n':
+                                appendStrBufLineFeed();
+                                continue;
+                            case '\u0000':
+                                c = '\uFFFD';
+                                // fall thru
+                            default:
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT, reconsume, pos);
+                                continue stateloop;
+                        }
+                    }
+                case COMMENT_LESSTHAN_BANG:
+                    for (;;) {
+                        if (++pos == endPos) {
+                            break stateloop;
+                        }
+                        c = checkChar(buf, pos);
+                        switch (c) {
+                            case '-':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN_BANG_DASH, reconsume, pos);
+                                continue stateloop;
+                            case '<':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                                continue stateloop;
+                            case '\r':
+                                appendStrBufCarriageReturn();
+                                break stateloop;
+                            case '\n':
+                                appendStrBufLineFeed();
+                                continue;
+                            case '\u0000':
+                                c = '\uFFFD';
+                                // fall thru
+                            default:
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT, reconsume, pos);
+                                continue stateloop;
+                        }
+                    }
+                case COMMENT_LESSTHAN_BANG_DASH:
+                    for (;;) {
+                        if (++pos == endPos) {
+                            break stateloop;
+                        }
+                        c = checkChar(buf, pos);
+                        switch (c) {
+                            case '-':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN_BANG_DASH_DASH, reconsume, pos);
+                                continue stateloop;
+                            case '<':
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                                continue stateloop;
+                            case '\r':
+                                appendStrBufCarriageReturn();
+                                break stateloop;
+                            case '\n':
+                                appendStrBufLineFeed();
+                                continue;
+                            case '\u0000':
+                                c = '\uFFFD';
+                                // fall thru
+                            default:
+                                appendStrBuf(c);
+                                state = transition(state, Tokenizer.COMMENT, reconsume, pos);
+                                continue stateloop;
+                        }
+                    }
+                case COMMENT_LESSTHAN_BANG_DASH_DASH:
+                    for (;;) {
+                        if (++pos == endPos) {
+                            break stateloop;
+                        }
+                        c = checkChar(buf, pos);
+                        switch (c) {
+                            case '>':
+                                appendStrBuf(c);
+                                emitComment(3, pos);
+                                state = transition(state, Tokenizer.DATA, reconsume, pos);
+                                continue stateloop;
+                            case '-':
+                                errNestedComment();
+                                adjustDoubleHyphenAndAppendToStrBufAndErr(c, reportedConsecutiveHyphens);
+                                reportedConsecutiveHyphens = true;
+                                state = transition(state, Tokenizer.COMMENT_END, reconsume, pos);
+                                continue stateloop;
+                            case '\r':
+                                errNestedComment();
+                                adjustDoubleHyphenAndAppendToStrBufAndErr(c, reportedConsecutiveHyphens);
+                                reportedConsecutiveHyphens = true;
+                                state = transition(state, Tokenizer.COMMENT, reconsume, pos);
+                                break stateloop;
+                            case '\n':
+                                errNestedComment();
+                                adjustDoubleHyphenAndAppendToStrBufAndErr(c, reportedConsecutiveHyphens);
+                                reportedConsecutiveHyphens = true;
+                                state = transition(state, Tokenizer.COMMENT, reconsume, pos);
+                                continue;
+                            case '\u0000':
+                                c = '\uFFFD';
+                                // fall thru
+                            case '!':
+                                errNestedComment();
+                                adjustDoubleHyphenAndAppendToStrBufAndErr(c, reportedConsecutiveHyphens);
+                                reportedConsecutiveHyphens = true;
+                                state = transition(state, Tokenizer.COMMENT_END_BANG, reconsume, pos);
+                                continue stateloop;
+                            default:
+                                errNestedComment();
+                                adjustDoubleHyphenAndAppendToStrBufAndErr(c, reportedConsecutiveHyphens);
+                                reportedConsecutiveHyphens = true;
+                                state = transition(state, Tokenizer.COMMENT_END, reconsume, pos);
+                                continue stateloop;
+                        }
+                    }
+                    // XXX reorder point
                 case COMMENT_START_DASH:
                     if (++pos == endPos) {
                         break stateloop;
                     }
                     c = checkChar(buf, pos);
                     /*
                      * Comment start dash state
                      *
@@ -2833,16 +3003,20 @@ public class Tokenizer implements Locato
                             errPrematureEndOfComment();
                             /* Emit the comment token. */
                             emitComment(1, pos);
                             /*
                              * Switch to the data state.
                              */
                             state = transition(state, Tokenizer.DATA, reconsume, pos);
                             continue stateloop;
+                        case '<':
+                            appendStrBuf(c);
+                            state = transition(state, Tokenizer.COMMENT_LESSTHAN, reconsume, pos);
+                            continue stateloop;
                         case '\r':
                             appendStrBufCarriageReturn();
                             state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                             break stateloop;
                         case '\n':
                             appendStrBufLineFeed();
                             state = transition(state, Tokenizer.COMMENT, reconsume, pos);
                             continue stateloop;
@@ -5952,23 +6126,23 @@ public class Tokenizer implements Locato
             publicIdentifier = null;
         }
         forceQuirks = false;
     }
 
     @Inline private void adjustDoubleHyphenAndAppendToStrBufCarriageReturn()
             throws SAXException {
         silentCarriageReturn();
-        adjustDoubleHyphenAndAppendToStrBufAndErr('\n');
+        adjustDoubleHyphenAndAppendToStrBufAndErr('\n', false);
     }
 
     @Inline private void adjustDoubleHyphenAndAppendToStrBufLineFeed()
             throws SAXException {
         silentLineFeed();
-        adjustDoubleHyphenAndAppendToStrBufAndErr('\n');
+        adjustDoubleHyphenAndAppendToStrBufAndErr('\n', false);
     }
 
     @Inline private void appendStrBufLineFeed() {
         silentLineFeed();
         appendStrBuf('\n');
     }
 
     @Inline private void appendStrBufCarriageReturn() {
@@ -6263,36 +6437,40 @@ public class Tokenizer implements Locato
                         /*
                          * Reconsume the EOF character in the data state.
                          */
                         break eofloop;
                     }
                     break eofloop;
                 case COMMENT_START:
                 case COMMENT:
+                case COMMENT_LESSTHAN:
+                case COMMENT_LESSTHAN_BANG:
                     /*
                      * EOF Parse error.
                      */
                     errEofInComment();
                     /* Emit the comment token. */
                     emitComment(0, 0);
                     /*
                      * Reconsume the EOF character in the data state.
                      */
                     break eofloop;
                 case COMMENT_END:
+                case COMMENT_LESSTHAN_BANG_DASH_DASH:
                     errEofInComment();
                     /* Emit the comment token. */
                     emitComment(2, 0);
                     /*
                      * Reconsume the EOF character in the data state.
                      */
                     break eofloop;
                 case COMMENT_END_DASH:
                 case COMMENT_START_DASH:
+                case COMMENT_LESSTHAN_BANG_DASH:
                     errEofInComment();
                     /* Emit the comment token. */
                     emitComment(1, 0);
                     /*
                      * Reconsume the EOF character in the data state.
                      */
                     break eofloop;
                 case COMMENT_END_BANG:
@@ -6912,17 +7090,17 @@ public class Tokenizer implements Locato
     }
 
     protected void errGtInPublicId() throws SAXException {
     }
 
     protected void errNamelessDoctype() throws SAXException {
     }
 
-    protected void errConsecutiveHyphens() throws SAXException {
+    protected void errNestedComment() throws SAXException {
     }
 
     protected void errPrematureEndOfComment() throws SAXException {
     }
 
     protected void errBogusComment() throws SAXException {
     }
 
@@ -7055,19 +7233,16 @@ public class Tokenizer implements Locato
     }
 
     protected void errExpectedSystemId() throws SAXException {
     }
 
     protected void errMissingSpaceBeforeDoctypeName() throws SAXException {
     }
 
-    protected void errHyphenHyphenBang() throws SAXException {
-    }
-
     protected void errNcrControlChar() throws SAXException {
     }
 
     protected void errNcrZero() throws SAXException {
     }
 
     protected void errNoSpaceBetweenDoctypeSystemKeywordAndQuote()
             throws SAXException {
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -450,16 +450,17 @@ bool nsHtml5Tokenizer::tokenizeBuffer(ns
   }
   return lastCR;
 }
 
 template <class P>
 int32_t nsHtml5Tokenizer::stateLoop(int32_t state, char16_t c, int32_t pos,
                                     char16_t* buf, bool reconsume,
                                     int32_t returnState, int32_t endPos) {
+  bool reportedConsecutiveHyphens = false;
 stateloop:
   for (;;) {
     switch (state) {
       case DATA: {
         for (;;) {
           if (reconsume) {
             reconsume = false;
           } else {
@@ -1243,16 +1244,17 @@ stateloop:
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
       markupdeclarationhyphenloop_end:;
         [[fallthrough]];
       }
       case COMMENT_START: {
+        reportedConsecutiveHyphens = false;
         for (;;) {
           if (++pos == endPos) {
             NS_HTML5_BREAK(stateloop);
           }
           c = checkChar(buf, pos);
           switch (c) {
             case '-': {
               appendStrBuf(c);
@@ -1265,16 +1267,23 @@ stateloop:
               if (P::reportErrors) {
                 errPrematureEndOfComment();
               }
               emitComment(0, pos);
               state = P::transition(mViewSource.get(), nsHtml5Tokenizer::DATA,
                                     reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
+            case '<': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
             case '\r': {
               appendStrBufCarriageReturn();
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT, reconsume, pos);
               NS_HTML5_BREAK(stateloop);
             }
             case '\n': {
               appendStrBufLineFeed();
@@ -1306,16 +1315,23 @@ stateloop:
           switch (c) {
             case '-': {
               appendStrBuf(c);
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT_END_DASH,
                                     reconsume, pos);
               NS_HTML5_BREAK(commentloop);
             }
+            case '<': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
             case '\r': {
               appendStrBufCarriageReturn();
               NS_HTML5_BREAK(stateloop);
             }
             case '\n': {
               appendStrBufLineFeed();
               continue;
             }
@@ -1341,16 +1357,23 @@ stateloop:
           switch (c) {
             case '-': {
               appendStrBuf(c);
               state =
                   P::transition(mViewSource.get(),
                                 nsHtml5Tokenizer::COMMENT_END, reconsume, pos);
               NS_HTML5_BREAK(commentenddashloop);
             }
+            case '<': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
             case '\r': {
               appendStrBufCarriageReturn();
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT, reconsume, pos);
               NS_HTML5_BREAK(stateloop);
             }
             case '\n': {
               appendStrBufLineFeed();
@@ -1382,47 +1405,55 @@ stateloop:
           switch (c) {
             case '>': {
               emitComment(2, pos);
               state = P::transition(mViewSource.get(), nsHtml5Tokenizer::DATA,
                                     reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '-': {
-              adjustDoubleHyphenAndAppendToStrBufAndErr(c);
+              adjustDoubleHyphenAndAppendToStrBufAndErr(
+                  c, reportedConsecutiveHyphens);
+              reportedConsecutiveHyphens = true;
               continue;
             }
+            case '<': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
             case '\r': {
               adjustDoubleHyphenAndAppendToStrBufCarriageReturn();
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT, reconsume, pos);
               NS_HTML5_BREAK(stateloop);
             }
             case '\n': {
               adjustDoubleHyphenAndAppendToStrBufLineFeed();
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '!': {
-              if (P::reportErrors) {
-                errHyphenHyphenBang();
-              }
               appendStrBuf(c);
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT_END_BANG,
                                     reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
             case '\0': {
               c = 0xfffd;
               [[fallthrough]];
             }
             default: {
-              adjustDoubleHyphenAndAppendToStrBufAndErr(c);
+              adjustDoubleHyphenAndAppendToStrBufAndErr(
+                  c, reportedConsecutiveHyphens);
+              reportedConsecutiveHyphens = true;
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
       }
       case COMMENT_END_BANG: {
@@ -1465,16 +1496,225 @@ stateloop:
               appendStrBuf(c);
               state = P::transition(mViewSource.get(),
                                     nsHtml5Tokenizer::COMMENT, reconsume, pos);
               NS_HTML5_CONTINUE(stateloop);
             }
           }
         }
       }
+      case COMMENT_LESSTHAN: {
+        for (;;) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch (c) {
+            case '!': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN_BANG,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '<': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '-': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_END_DASH,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+      }
+      case COMMENT_LESSTHAN_BANG: {
+        for (;;) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch (c) {
+            case '-': {
+              appendStrBuf(c);
+              state = P::transition(
+                  mViewSource.get(),
+                  nsHtml5Tokenizer::COMMENT_LESSTHAN_BANG_DASH, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '<': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+      }
+      case COMMENT_LESSTHAN_BANG_DASH: {
+        for (;;) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch (c) {
+            case '-': {
+              appendStrBuf(c);
+              state = P::transition(
+                  mViewSource.get(),
+                  nsHtml5Tokenizer::COMMENT_LESSTHAN_BANG_DASH_DASH, reconsume,
+                  pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '<': {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_LESSTHAN,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              appendStrBufCarriageReturn();
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              appendStrBufLineFeed();
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            default: {
+              appendStrBuf(c);
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+      }
+      case COMMENT_LESSTHAN_BANG_DASH_DASH: {
+        for (;;) {
+          if (++pos == endPos) {
+            NS_HTML5_BREAK(stateloop);
+          }
+          c = checkChar(buf, pos);
+          switch (c) {
+            case '>': {
+              appendStrBuf(c);
+              emitComment(3, pos);
+              state = P::transition(mViewSource.get(), nsHtml5Tokenizer::DATA,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '-': {
+              if (P::reportErrors) {
+                errNestedComment();
+              }
+              adjustDoubleHyphenAndAppendToStrBufAndErr(
+                  c, reportedConsecutiveHyphens);
+              reportedConsecutiveHyphens = true;
+              state =
+                  P::transition(mViewSource.get(),
+                                nsHtml5Tokenizer::COMMENT_END, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            case '\r': {
+              if (P::reportErrors) {
+                errNestedComment();
+              }
+              adjustDoubleHyphenAndAppendToStrBufAndErr(
+                  c, reportedConsecutiveHyphens);
+              reportedConsecutiveHyphens = true;
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT, reconsume, pos);
+              NS_HTML5_BREAK(stateloop);
+            }
+            case '\n': {
+              if (P::reportErrors) {
+                errNestedComment();
+              }
+              adjustDoubleHyphenAndAppendToStrBufAndErr(
+                  c, reportedConsecutiveHyphens);
+              reportedConsecutiveHyphens = true;
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT, reconsume, pos);
+              continue;
+            }
+            case '\0': {
+              c = 0xfffd;
+            }
+            case '!': {
+              if (P::reportErrors) {
+                errNestedComment();
+              }
+              adjustDoubleHyphenAndAppendToStrBufAndErr(
+                  c, reportedConsecutiveHyphens);
+              reportedConsecutiveHyphens = true;
+              state = P::transition(mViewSource.get(),
+                                    nsHtml5Tokenizer::COMMENT_END_BANG,
+                                    reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+            default: {
+              if (P::reportErrors) {
+                errNestedComment();
+              }
+              adjustDoubleHyphenAndAppendToStrBufAndErr(
+                  c, reportedConsecutiveHyphens);
+              reportedConsecutiveHyphens = true;
+              state =
+                  P::transition(mViewSource.get(),
+                                nsHtml5Tokenizer::COMMENT_END, reconsume, pos);
+              NS_HTML5_CONTINUE(stateloop);
+            }
+          }
+        }
+      }
       case COMMENT_START_DASH: {
         if (++pos == endPos) {
           NS_HTML5_BREAK(stateloop);
         }
         c = checkChar(buf, pos);
         switch (c) {
           case '-': {
             appendStrBuf(c);
@@ -1487,16 +1727,23 @@ stateloop:
             if (P::reportErrors) {
               errPrematureEndOfComment();
             }
             emitComment(1, pos);
             state = P::transition(mViewSource.get(), nsHtml5Tokenizer::DATA,
                                   reconsume, pos);
             NS_HTML5_CONTINUE(stateloop);
           }
+          case '<': {
+            appendStrBuf(c);
+            state = P::transition(mViewSource.get(),
+                                  nsHtml5Tokenizer::COMMENT_LESSTHAN, reconsume,
+                                  pos);
+            NS_HTML5_CONTINUE(stateloop);
+          }
           case '\r': {
             appendStrBufCarriageReturn();
             state = P::transition(mViewSource.get(), nsHtml5Tokenizer::COMMENT,
                                   reconsume, pos);
             NS_HTML5_BREAK(stateloop);
           }
           case '\n': {
             appendStrBufLineFeed();
@@ -4260,28 +4507,32 @@ eofloop:
           }
           forceQuirks = true;
           emitDoctypeToken(0);
           NS_HTML5_BREAK(eofloop);
         }
         NS_HTML5_BREAK(eofloop);
       }
       case COMMENT_START:
-      case COMMENT: {
+      case COMMENT:
+      case COMMENT_LESSTHAN:
+      case COMMENT_LESSTHAN_BANG: {
         errEofInComment();
         emitComment(0, 0);
         NS_HTML5_BREAK(eofloop);
       }
-      case COMMENT_END: {
+      case COMMENT_END:
+      case COMMENT_LESSTHAN_BANG_DASH_DASH: {
         errEofInComment();
         emitComment(2, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case COMMENT_END_DASH:
-      case COMMENT_START_DASH: {
+      case COMMENT_START_DASH:
+      case COMMENT_LESSTHAN_BANG_DASH: {
         errEofInComment();
         emitComment(1, 0);
         NS_HTML5_BREAK(eofloop);
       }
       case COMMENT_END_BANG: {
         errEofInComment();
         emitComment(3, 0);
         NS_HTML5_BREAK(eofloop);
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -207,16 +207,24 @@ class nsHtml5Tokenizer {
   static const int32_t SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH = 71;
 
   static const int32_t SCRIPT_DATA_DOUBLE_ESCAPE_END = 72;
 
   static const int32_t PROCESSING_INSTRUCTION = 73;
 
   static const int32_t PROCESSING_INSTRUCTION_QUESTION_MARK = 74;
 
+  static const int32_t COMMENT_LESSTHAN = 76;
+
+  static const int32_t COMMENT_LESSTHAN_BANG = 77;
+
+  static const int32_t COMMENT_LESSTHAN_BANG_DASH = 78;
+
+  static const int32_t COMMENT_LESSTHAN_BANG_DASH_DASH = 79;
+
  private:
   static const int32_t LEAD_OFFSET = (0xD800 - (0x10000 >> 10));
 
   static char16_t LT_GT[];
   static char16_t LT_SOLIDUS[];
   static char16_t RSQB_RSQB[];
   static char16_t REPLACEMENT_CHARACTER[];
   static char16_t LF[];
@@ -364,18 +372,18 @@ class nsHtml5Tokenizer {
  protected:
   nsHtml5String strBufToString();
 
  private:
   void strBufToDoctypeName();
   void emitStrBuf();
   inline void appendSecondHyphenToBogusComment() { appendStrBuf('-'); }
 
-  inline void adjustDoubleHyphenAndAppendToStrBufAndErr(char16_t c) {
-    errConsecutiveHyphens();
+  inline void adjustDoubleHyphenAndAppendToStrBufAndErr(
+      char16_t c, bool reportedConsecutiveHyphens) {
     appendStrBuf(c);
   }
 
   void appendStrBuf(char16_t* buffer, int32_t offset, int32_t length);
   inline void appendCharRefBufToStrBuf() {
     appendStrBuf(charRefBuf, 0, charRefBufLen);
     charRefBufLen = 0;
   }
@@ -398,22 +406,22 @@ class nsHtml5Tokenizer {
 
  private:
   template <class P>
   int32_t stateLoop(int32_t state, char16_t c, int32_t pos, char16_t* buf,
                     bool reconsume, int32_t returnState, int32_t endPos);
   void initDoctypeFields();
   inline void adjustDoubleHyphenAndAppendToStrBufCarriageReturn() {
     silentCarriageReturn();
-    adjustDoubleHyphenAndAppendToStrBufAndErr('\n');
+    adjustDoubleHyphenAndAppendToStrBufAndErr('\n', false);
   }
 
   inline void adjustDoubleHyphenAndAppendToStrBufLineFeed() {
     silentLineFeed();
-    adjustDoubleHyphenAndAppendToStrBufAndErr('\n');
+    adjustDoubleHyphenAndAppendToStrBufAndErr('\n', false);
   }
 
   inline void appendStrBufLineFeed() {
     silentLineFeed();
     appendStrBuf('\n');
   }
 
   inline void appendStrBufCarriageReturn() {
--- a/parser/html/nsHtml5TokenizerCppSupplement.h
+++ b/parser/html/nsHtml5TokenizerCppSupplement.h
@@ -419,19 +419,19 @@ void nsHtml5Tokenizer::errExpectedSystem
 }
 
 void nsHtml5Tokenizer::errMissingSpaceBeforeDoctypeName() {
   if (MOZ_LIKELY(mViewSource)) {
     mViewSource->AddErrorToCurrentNode("errMissingSpaceBeforeDoctypeName");
   }
 }
 
-void nsHtml5Tokenizer::errHyphenHyphenBang() {
+void nsHtml5Tokenizer::errNestedComment() {
   if (MOZ_LIKELY(mViewSource)) {
-    mViewSource->AddErrorToCurrentNode("errHyphenHyphenBang");
+    mViewSource->AddErrorToCurrentNode("errNestedComment");
   }
 }
 
 void nsHtml5Tokenizer::errNcrControlChar() {
   if (MOZ_UNLIKELY(mViewSource)) {
     mViewSource->AddErrorToCurrentNode("errNcrControlChar");
   }
 }
--- a/parser/html/nsHtml5TokenizerHSupplement.h
+++ b/parser/html/nsHtml5TokenizerHSupplement.h
@@ -127,17 +127,17 @@ void errNcrUnassigned();
 void errDuplicateAttribute();
 
 void errEofInSystemId();
 
 void errExpectedSystemId();
 
 void errMissingSpaceBeforeDoctypeName();
 
-void errHyphenHyphenBang();
+void errNestedComment();
 
 void errNcrControlChar();
 
 void errNcrZero();
 
 void errNoSpaceBetweenDoctypeSystemKeywordAndQuote();
 
 void errNoSpaceBetweenPublicAndSystemIds();