[libxml2/2.9] Fix recovery from invalid HTML start tags



commit 148be64edf88c00dde50cb0ef584a4c7a3d30212
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Tue Feb 22 18:15:53 2022 +0100

    Fix recovery from invalid HTML start tags
    
    Only try to parse a start tag if there's a '<' followed by an ASCII
    letter. This is more in line with HTML5 and the old behavior in
    recovery mode. Emit a literal '<' if the following character is
    invalid.
    
    Fixes #101.
    Fixes #339.

 HTMLparser.c | 44 +++++++++++++++++++++-----------------------
 1 file changed, 21 insertions(+), 23 deletions(-)
---
diff --git a/HTMLparser.c b/HTMLparser.c
index e235f57b..e720bb20 100644
--- a/HTMLparser.c
+++ b/HTMLparser.c
@@ -3961,26 +3961,6 @@ htmlParseStartTag(htmlParserCtxtPtr ctxt) {
        htmlParseErr(ctxt, XML_ERR_NAME_REQUIRED,
                     "htmlParseStartTag: invalid element name\n",
                     NULL, NULL);
-        /*
-         * The recovery code is disabled for now as it can result in
-         * quadratic behavior with the push parser. htmlParseStartTag
-         * must consume all content up to the final '>' in order to avoid
-         * rescanning for this terminator.
-         *
-         * For a proper fix in line with HTML5, htmlParseStartTag and
-         * htmlParseElement should only be called when there's an ASCII
-         * alpha character following the initial '<'. Otherwise, the '<'
-         * should be emitted as text (unless followed by '!', '/' or '?').
-         */
-#if 0
-       /* if recover preserve text on classic misconstructs */
-       if ((ctxt->recovery) && ((IS_BLANK_CH(CUR)) || (CUR == '<') ||
-           (CUR == '=') || (CUR == '>') || (((CUR >= '0') && (CUR <= '9'))))) {
-           htmlParseCharDataInternal(ctxt, '<');
-           return(-1);
-       }
-#endif
-
        /* Dump the bogus tag like browsers do */
        while ((CUR != 0) && (CUR != '>') &&
                (ctxt->instate != XML_PARSER_EOF))
@@ -4433,9 +4413,15 @@ htmlParseContent(htmlParserCtxtPtr ctxt) {
            /*
             * Third case :  a sub-element.
             */
-           else if (CUR == '<') {
+           else if ((CUR == '<') && IS_ASCII_LETTER(NXT(1))) {
                htmlParseElement(ctxt);
            }
+           else if (CUR == '<') {
+                if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
+                    (ctxt->sax->characters != NULL))
+                    ctxt->sax->characters(ctxt->userData, BAD_CAST "<", 1);
+                NEXT;
+           }
 
            /*
             * Fourth case : a reference. If if has not been resolved,
@@ -4832,13 +4818,19 @@ htmlParseContentInternal(htmlParserCtxtPtr ctxt) {
            /*
             * Third case :  a sub-element.
             */
-           else if (CUR == '<') {
+           else if ((CUR == '<') && IS_ASCII_LETTER(NXT(1))) {
                htmlParseElementInternal(ctxt);
                if (currentNode != NULL) xmlFree(currentNode);
 
                currentNode = xmlStrdup(ctxt->name);
                depth = ctxt->nameNr;
            }
+           else if (CUR == '<') {
+                if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
+                    (ctxt->sax->characters != NULL))
+                    ctxt->sax->characters(ctxt->userData, BAD_CAST "<", 1);
+                NEXT;
+            }
 
            /*
             * Fourth case : a reference. If if has not been resolved,
@@ -6005,7 +5997,7 @@ htmlParseTryOrFinish(htmlParserCtxtPtr ctxt, int terminate) {
                                "HPP: entering END_TAG\n");
 #endif
                        break;
-                   } else if (cur == '<') {
+                   } else if ((cur == '<') && IS_ASCII_LETTER(next)) {
                         if ((!terminate) && (next == 0))
                             goto done;
                         ctxt->instate = XML_PARSER_START_TAG;
@@ -6015,6 +6007,12 @@ htmlParseTryOrFinish(htmlParserCtxtPtr ctxt, int terminate) {
                                 "HPP: entering START_TAG\n");
 #endif
                        break;
+                   } else if (cur == '<') {
+                        if ((ctxt->sax != NULL) && (!ctxt->disableSAX) &&
+                            (ctxt->sax->characters != NULL))
+                           ctxt->sax->characters(ctxt->userData,
+                                                 BAD_CAST "<", 1);
+                        NEXT;
                    } else {
                        /*
                         * check that the text sequence is complete


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]