1 /*
2  * Copyright (c) 2007 Henri Sivonen
3  * Copyright (c) 2007-2017 Mozilla Foundation
4  * Portions of comments Copyright 2004-2008 Apple Computer, Inc., Mozilla
5  * Foundation, and Opera Software ASA.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25 
26 /*
27  * The comments following this one that use the same comment syntax as this
28  * comment are quotes from the WHATWG HTML 5 spec as of 27 June 2007
29  * amended as of June 28 2007.
30  * That document came with this statement:
31  * "© Copyright 2004-2007 Apple Computer, Inc., Mozilla Foundation, and
32  * Opera Software ASA. You are granted a license to use, reproduce and
33  * create derivative works of this document."
34  */
35 
36 package nu.validator.htmlparser.impl;
37 
38 import java.util.Arrays;
39 import java.util.HashMap;
40 import java.util.Map;
41 
42 import org.xml.sax.ErrorHandler;
43 import org.xml.sax.Locator;
44 import org.xml.sax.SAXException;
45 import org.xml.sax.SAXParseException;
46 
47 import nu.validator.htmlparser.annotation.Auto;
48 import nu.validator.htmlparser.annotation.Const;
49 import nu.validator.htmlparser.annotation.IdType;
50 import nu.validator.htmlparser.annotation.Inline;
51 import nu.validator.htmlparser.annotation.Literal;
52 import nu.validator.htmlparser.annotation.Local;
53 import nu.validator.htmlparser.annotation.NoLength;
54 import nu.validator.htmlparser.annotation.NsUri;
55 import nu.validator.htmlparser.common.DocumentMode;
56 import nu.validator.htmlparser.common.DocumentModeHandler;
57 import nu.validator.htmlparser.common.Interner;
58 import nu.validator.htmlparser.common.TokenHandler;
59 import nu.validator.htmlparser.common.XmlViolationPolicy;
60 
61 public abstract class TreeBuilder<T> implements TokenHandler,
62         TreeBuilderState<T> {
63 
64     /**
65      * Array version of U+FFFD.
66      */
67     private static final @NoLength char[] REPLACEMENT_CHARACTER = { '\uFFFD' };
68 
69     // Start dispatch groups
70 
71     final static int OTHER = 0;
72 
73     final static int A = 1;
74 
75     final static int BASE = 2;
76 
77     final static int BODY = 3;
78 
79     final static int BR = 4;
80 
81     final static int BUTTON = 5;
82 
83     final static int CAPTION = 6;
84 
85     final static int COL = 7;
86 
87     final static int COLGROUP = 8;
88 
89     final static int FORM = 9;
90 
91     final static int FRAME = 10;
92 
93     final static int FRAMESET = 11;
94 
95     final static int IMAGE = 12;
96 
97     final static int INPUT = 13;
98 
99     final static int RT_OR_RP = 14;
100 
101     final static int LI = 15;
102 
103     final static int LINK_OR_BASEFONT_OR_BGSOUND = 16;
104 
105     final static int MATH = 17;
106 
107     final static int META = 18;
108 
109     final static int SVG = 19;
110 
111     final static int HEAD = 20;
112 
113     final static int HR = 22;
114 
115     final static int HTML = 23;
116 
117     final static int NOBR = 24;
118 
119     final static int NOFRAMES = 25;
120 
121     final static int NOSCRIPT = 26;
122 
123     final static int OPTGROUP = 27;
124 
125     final static int OPTION = 28;
126 
127     final static int P = 29;
128 
129     final static int PLAINTEXT = 30;
130 
131     final static int SCRIPT = 31;
132 
133     final static int SELECT = 32;
134 
135     final static int STYLE = 33;
136 
137     final static int TABLE = 34;
138 
139     final static int TEXTAREA = 35;
140 
141     final static int TITLE = 36;
142 
143     final static int TR = 37;
144 
145     final static int XMP = 38;
146 
147     final static int TBODY_OR_THEAD_OR_TFOOT = 39;
148 
149     final static int TD_OR_TH = 40;
150 
151     final static int DD_OR_DT = 41;
152 
153     final static int H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6 = 42;
154 
155     final static int MARQUEE_OR_APPLET = 43;
156 
157     final static int PRE_OR_LISTING = 44;
158 
159     final static int B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U = 45;
160 
161     final static int UL_OR_OL_OR_DL = 46;
162 
163     final static int IFRAME = 47;
164 
165     final static int EMBED = 48;
166 
167     final static int AREA_OR_WBR = 49;
168 
169     final static int DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU = 50;
170 
171     final static int ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY = 51;
172 
173     final static int RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR = 52;
174 
175     final static int RB_OR_RTC = 53;
176 
177     final static int PARAM_OR_SOURCE_OR_TRACK = 55;
178 
179     final static int MGLYPH_OR_MALIGNMARK = 56;
180 
181     final static int MI_MO_MN_MS_MTEXT = 57;
182 
183     final static int ANNOTATION_XML = 58;
184 
185     final static int FOREIGNOBJECT_OR_DESC = 59;
186 
187     final static int NOEMBED = 60;
188 
189     final static int FIELDSET = 61;
190 
191     final static int OUTPUT = 62;
192 
193     final static int OBJECT = 63;
194 
195     final static int FONT = 64;
196 
197     final static int KEYGEN = 65;
198 
199     final static int MENUITEM = 66;
200 
201     final static int TEMPLATE = 67;
202 
203     final static int IMG = 68;
204 
205     // start insertion modes
206 
207     private static final int IN_ROW = 0;
208 
209     private static final int IN_TABLE_BODY = 1;
210 
211     private static final int IN_TABLE = 2;
212 
213     private static final int IN_CAPTION = 3;
214 
215     private static final int IN_CELL = 4;
216 
217     private static final int FRAMESET_OK = 5;
218 
219     private static final int IN_BODY = 6;
220 
221     private static final int IN_HEAD = 7;
222 
223     private static final int IN_HEAD_NOSCRIPT = 8;
224 
225     // no fall-through
226 
227     private static final int IN_COLUMN_GROUP = 9;
228 
229     // no fall-through
230 
231     private static final int IN_SELECT_IN_TABLE = 10;
232 
233     private static final int IN_SELECT = 11;
234 
235     // no fall-through
236 
237     private static final int AFTER_BODY = 12;
238 
239     // no fall-through
240 
241     private static final int IN_FRAMESET = 13;
242 
243     private static final int AFTER_FRAMESET = 14;
244 
245     // no fall-through
246 
247     private static final int INITIAL = 15;
248 
249     // could add fall-through
250 
251     private static final int BEFORE_HTML = 16;
252 
253     // could add fall-through
254 
255     private static final int BEFORE_HEAD = 17;
256 
257     // no fall-through
258 
259     private static final int AFTER_HEAD = 18;
260 
261     // no fall-through
262 
263     private static final int AFTER_AFTER_BODY = 19;
264 
265     // no fall-through
266 
267     private static final int AFTER_AFTER_FRAMESET = 20;
268 
269     // no fall-through
270 
271     private static final int TEXT = 21;
272 
273     private static final int IN_TEMPLATE = 22;
274 
275     // start charset states
276 
277     private static final int CHARSET_INITIAL = 0;
278 
279     private static final int CHARSET_C = 1;
280 
281     private static final int CHARSET_H = 2;
282 
283     private static final int CHARSET_A = 3;
284 
285     private static final int CHARSET_R = 4;
286 
287     private static final int CHARSET_S = 5;
288 
289     private static final int CHARSET_E = 6;
290 
291     private static final int CHARSET_T = 7;
292 
293     private static final int CHARSET_EQUALS = 8;
294 
295     private static final int CHARSET_SINGLE_QUOTED = 9;
296 
297     private static final int CHARSET_DOUBLE_QUOTED = 10;
298 
299     private static final int CHARSET_UNQUOTED = 11;
300 
301     // end pseudo enums
302 
303     @Literal private final static String[] QUIRKY_PUBLIC_IDS = {
304             "+//silmaril//dtd html pro v0r11 19970101//",
305             "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
306             "-//as//dtd html 3.0 aswedit + extensions//",
307             "-//ietf//dtd html 2.0 level 1//",
308             "-//ietf//dtd html 2.0 level 2//",
309             "-//ietf//dtd html 2.0 strict level 1//",
310             "-//ietf//dtd html 2.0 strict level 2//",
311             "-//ietf//dtd html 2.0 strict//",
312             "-//ietf//dtd html 2.0//",
313             "-//ietf//dtd html 2.1e//",
314             "-//ietf//dtd html 3.0//",
315             "-//ietf//dtd html 3.2 final//",
316             "-//ietf//dtd html 3.2//",
317             "-//ietf//dtd html 3//",
318             "-//ietf//dtd html level 0//",
319             "-//ietf//dtd html level 1//",
320             "-//ietf//dtd html level 2//",
321             "-//ietf//dtd html level 3//",
322             "-//ietf//dtd html strict level 0//",
323             "-//ietf//dtd html strict level 1//",
324             "-//ietf//dtd html strict level 2//",
325             "-//ietf//dtd html strict level 3//",
326             "-//ietf//dtd html strict//",
327             "-//ietf//dtd html//",
328             "-//metrius//dtd metrius presentational//",
329             "-//microsoft//dtd internet explorer 2.0 html strict//",
330             "-//microsoft//dtd internet explorer 2.0 html//",
331             "-//microsoft//dtd internet explorer 2.0 tables//",
332             "-//microsoft//dtd internet explorer 3.0 html strict//",
333             "-//microsoft//dtd internet explorer 3.0 html//",
334             "-//microsoft//dtd internet explorer 3.0 tables//",
335             "-//netscape comm. corp.//dtd html//",
336             "-//netscape comm. corp.//dtd strict html//",
337             "-//o'reilly and associates//dtd html 2.0//",
338             "-//o'reilly and associates//dtd html extended 1.0//",
339             "-//o'reilly and associates//dtd html extended relaxed 1.0//",
340             "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
341             "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
342             "-//spyglass//dtd html 2.0 extended//",
343             "-//sq//dtd html 2.0 hotmetal + extensions//",
344             "-//sun microsystems corp.//dtd hotjava html//",
345             "-//sun microsystems corp.//dtd hotjava strict html//",
346             "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//",
347             "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//",
348             "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//",
349             "-//w3c//dtd html 4.0 transitional//",
350             "-//w3c//dtd html experimental 19960712//",
351             "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//",
352             "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//",
353             "-//webtechs//dtd mozilla html//" };
354 
355     private static final int NOT_FOUND_ON_STACK = Integer.MAX_VALUE;
356 
357     // [NOCPP[
358 
359     private static final @Local String HTML_LOCAL = "html";
360 
361     // ]NOCPP]
362 
363     private int mode = INITIAL;
364 
365     private int originalMode = INITIAL;
366 
367     /**
368      * Used only when moving back to IN_BODY.
369      */
370     private boolean framesetOk = true;
371 
372     protected Tokenizer tokenizer;
373 
374     // [NOCPP[
375 
376     protected ErrorHandler errorHandler;
377 
378     private DocumentModeHandler documentModeHandler;
379 
380     // ]NOCPP]
381 
382     private boolean scriptingEnabled = false;
383 
384     private boolean needToDropLF;
385 
386     // [NOCPP[
387 
388     private boolean wantingComments;
389 
390     // ]NOCPP]
391 
392     private boolean fragment;
393 
394     private @Local String contextName;
395 
396     private @NsUri String contextNamespace;
397 
398     private T contextNode;
399 
400     /**
401      * Stack of template insertion modes
402      */
403     private @Auto int[] templateModeStack;
404 
405     /**
406      * Current template mode stack pointer.
407      */
408     private int templateModePtr = -1;
409 
410     private @Auto StackNode<T>[] stackNodes;
411 
412     /**
413      * Index of the earliest possible unused or empty element in stackNodes.
414      */
415     private int stackNodesIdx = -1;
416 
417     private int numStackNodes = 0;
418 
419     private @Auto StackNode<T>[] stack;
420 
421     private int currentPtr = -1;
422 
423     private @Auto StackNode<T>[] listOfActiveFormattingElements;
424 
425     private int listPtr = -1;
426 
427     private T formPointer;
428 
429     private T headPointer;
430 
431     protected @Auto char[] charBuffer;
432 
433     protected int charBufferLen = 0;
434 
435     private boolean quirks = false;
436 
437     private boolean isSrcdocDocument = false;
438 
439     // [NOCPP[
440 
441     private boolean reportingDoctype = true;
442 
443     private XmlViolationPolicy namePolicy = XmlViolationPolicy.ALTER_INFOSET;
444 
445     private final Map<String, LocatorImpl> idLocations = new HashMap<String, LocatorImpl>();
446 
447     // ]NOCPP]
448 
TreeBuilder()449     protected TreeBuilder() {
450         fragment = false;
451     }
452 
453     /**
454      * Reports an condition that would make the infoset incompatible with XML
455      * 1.0 as fatal.
456      *
457      * @throws SAXException
458      * @throws SAXParseException
459      */
fatal()460     protected void fatal() throws SAXException {
461     }
462 
463     // CPPONLY: @Inline private @Creator Object htmlCreator(@HtmlCreator Object htmlCreator) {
464     // CPPONLY:     @Creator Object creator;
465     // CPPONLY:     creator.html = htmlCreator;
466     // CPPONLY:     return creator;
467     // CPPONLY: }
468     // CPPONLY:
469     // CPPONLY: @Inline private @Creator Object svgCreator(@SvgCreator Object svgCreator) {
470     // CPPONLY:     @Creator Object creator;
471     // CPPONLY:     creator.svg = svgCreator;
472     // CPPONLY:     return creator;
473     // CPPONLY: }
474 
475     // [NOCPP[
476 
fatal(Exception e)477     protected final void fatal(Exception e) throws SAXException {
478         SAXParseException spe = new SAXParseException(e.getMessage(),
479                 tokenizer, e);
480         if (errorHandler != null) {
481             errorHandler.fatalError(spe);
482         }
483         throw spe;
484     }
485 
fatal(String s)486     final void fatal(String s) throws SAXException {
487         SAXParseException spe = new SAXParseException(s, tokenizer);
488         if (errorHandler != null) {
489             errorHandler.fatalError(spe);
490         }
491         throw spe;
492     }
493 
494     /**
495      * Reports a Parse Error.
496      *
497      * @param message
498      *            the message
499      * @throws SAXException
500      */
err(String message)501     final void err(String message) throws SAXException {
502         if (errorHandler == null) {
503             return;
504         }
505         errNoCheck(message);
506     }
507 
508     /**
509      * Reports a Parse Error without checking if an error handler is present.
510      *
511      * @param message
512      *            the message
513      * @throws SAXException
514      */
errNoCheck(String message)515     final void errNoCheck(String message) throws SAXException {
516         SAXParseException spe = new SAXParseException(message, tokenizer);
517         errorHandler.error(spe);
518     }
519 
errListUnclosedStartTags(int eltPos)520     private void errListUnclosedStartTags(int eltPos) throws SAXException {
521         if (currentPtr != -1) {
522             for (int i = currentPtr; i > eltPos; i--) {
523                 reportUnclosedElementNameAndLocation(i);
524             }
525         }
526     }
527 
528     /**
529      * Reports the name and location of an unclosed element.
530      *
531      * @throws SAXException
532      */
reportUnclosedElementNameAndLocation(int pos)533     private final void reportUnclosedElementNameAndLocation(int pos) throws SAXException {
534         StackNode<T> node = stack[pos];
535         if (node.isOptionalEndTag()) {
536             return;
537         }
538         TaintableLocatorImpl locator = node.getLocator();
539         if (locator.isTainted()) {
540             return;
541         }
542         locator.markTainted();
543         SAXParseException spe = new SAXParseException(
544                 "Unclosed element \u201C" + node.popName + "\u201D.", locator);
545         errorHandler.error(spe);
546     }
547 
548     /**
549      * Reports a warning
550      *
551      * @param message
552      *            the message
553      * @throws SAXException
554      */
warn(String message)555     final void warn(String message) throws SAXException {
556         if (errorHandler == null) {
557             return;
558         }
559         SAXParseException spe = new SAXParseException(message, tokenizer);
560         errorHandler.warning(spe);
561     }
562 
563     /**
564      * Reports a warning with an explicit locator
565      *
566      * @param message
567      *            the message
568      * @throws SAXException
569      */
warn(String message, Locator locator)570     final void warn(String message, Locator locator) throws SAXException {
571         if (errorHandler == null) {
572             return;
573         }
574         SAXParseException spe = new SAXParseException(message, locator);
575         errorHandler.warning(spe);
576     }
577 
578     // ]NOCPP]
579 
startTokenization(Tokenizer self)580     @SuppressWarnings("unchecked") public final void startTokenization(Tokenizer self) throws SAXException {
581         tokenizer = self;
582         stackNodes = new StackNode[64];
583         stack = new StackNode[64];
584         templateModeStack = new int[64];
585         listOfActiveFormattingElements = new StackNode[64];
586         needToDropLF = false;
587         originalMode = INITIAL;
588         templateModePtr = -1;
589         stackNodesIdx = 0;
590         numStackNodes = 0;
591         currentPtr = -1;
592         listPtr = -1;
593         formPointer = null;
594         headPointer = null;
595         // [NOCPP[
596         idLocations.clear();
597         wantingComments = wantsComments();
598         // ]NOCPP]
599         start(fragment);
600         charBufferLen = 0;
601         charBuffer = null;
602         framesetOk = true;
603         if (fragment) {
604             T elt;
605             if (contextNode != null) {
606                 elt = contextNode;
607             } else {
608                 elt = createHtmlElementSetAsRoot(tokenizer.emptyAttributes());
609             }
610             // When the context node is not in the HTML namespace, contrary
611             // to the spec, the first node on the stack is not set to "html"
612             // in the HTML namespace. Instead, it is set to a node that has
613             // the characteristics of the appropriate "adjusted current node".
614             // This way, there is no need to perform "adjusted current node"
615             // checks during tree construction. Instead, it's sufficient to
616             // just look at the current node. However, this also means that it
617             // is not safe to treat "html" in the HTML namespace as a sentinel
618             // that ends stack popping. Instead, stack popping loops that are
619             // meant not to pop the first element on the stack need to check
620             // for currentPos becoming zero.
621             if (contextNamespace == "http://www.w3.org/2000/svg") {
622                 ElementName elementName = ElementName.SVG;
623                 if ("title" == contextName || "desc" == contextName
624                         || "foreignObject" == contextName) {
625                     // These elements are all alike and we don't care about
626                     // the exact name.
627                     elementName = ElementName.FOREIGNOBJECT;
628                 }
629                 // This is the SVG variant of the StackNode constructor.
630                 StackNode<T> node = createStackNode(elementName,
631                         elementName.getCamelCaseName(), elt
632                         // [NOCPP[
633                         , errorHandler == null ? null
634                                 : new TaintableLocatorImpl(tokenizer)
635                 // ]NOCPP]
636                 );
637                 currentPtr++;
638                 stack[currentPtr] = node;
639                 tokenizer.setState(Tokenizer.DATA);
640                 // The frameset-ok flag is set even though <frameset> never
641                 // ends up being allowed as HTML frameset in the fragment case.
642                 mode = FRAMESET_OK;
643             } else if (contextNamespace == "http://www.w3.org/1998/Math/MathML") {
644                 ElementName elementName = ElementName.MATH;
645                 if ("mi" == contextName || "mo" == contextName
646                         || "mn" == contextName || "ms" == contextName
647                         || "mtext" == contextName) {
648                     // These elements are all alike and we don't care about
649                     // the exact name.
650                     elementName = ElementName.MTEXT;
651                 } else if ("annotation-xml" == contextName) {
652                     elementName = ElementName.ANNOTATION_XML;
653                     // Blink does not check the encoding attribute of the
654                     // annotation-xml element innerHTML is being set on.
655                     // Let's do the same at least until
656                     // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26783
657                     // is resolved.
658                 }
659                 // This is the MathML variant of the StackNode constructor.
660                 StackNode<T> node = createStackNode(elementName, elt,
661                         elementName.getName(), false
662                         // [NOCPP[
663                         , errorHandler == null ? null
664                                 : new TaintableLocatorImpl(tokenizer)
665                 // ]NOCPP]
666                 );
667                 currentPtr++;
668                 stack[currentPtr] = node;
669                 tokenizer.setState(Tokenizer.DATA);
670                 // The frameset-ok flag is set even though <frameset> never
671                 // ends up being allowed as HTML frameset in the fragment case.
672                 mode = FRAMESET_OK;
673             } else { // html
674                 StackNode<T> node = createStackNode(ElementName.HTML, elt
675                 // [NOCPP[
676                         , errorHandler == null ? null
677                                 : new TaintableLocatorImpl(tokenizer)
678                 // ]NOCPP]
679                 );
680                 currentPtr++;
681                 stack[currentPtr] = node;
682                 if ("template" == contextName) {
683                     pushTemplateMode(IN_TEMPLATE);
684                 }
685                 resetTheInsertionMode();
686                 formPointer = getFormPointerForContext(contextNode);
687                 if ("title" == contextName || "textarea" == contextName) {
688                     tokenizer.setState(Tokenizer.RCDATA);
689                 } else if ("style" == contextName || "xmp" == contextName
690                         || "iframe" == contextName || "noembed" == contextName
691                         || "noframes" == contextName
692                         || (scriptingEnabled && "noscript" == contextName)) {
693                     tokenizer.setState(Tokenizer.RAWTEXT);
694                 } else if ("plaintext" == contextName) {
695                     tokenizer.setState(Tokenizer.PLAINTEXT);
696                 } else if ("script" == contextName) {
697                     tokenizer.setState(Tokenizer.SCRIPT_DATA);
698                 } else {
699                     tokenizer.setState(Tokenizer.DATA);
700                 }
701             }
702         } else {
703             mode = INITIAL;
704             // If we are viewing XML source, put a foreign element permanently
705             // on the stack so that cdataSectionAllowed() returns true.
706             // CPPONLY: if (tokenizer.isViewingXmlSource()) {
707             // CPPONLY: T elt = createElement("http://www.w3.org/2000/svg",
708             // CPPONLY: "svg",
709             // CPPONLY: tokenizer.emptyAttributes(), null,
710             // CPPONLY: svgCreator(NS_NewSVGSVGElement));
711             // CPPONLY: StackNode<T> node = createStackNode(ElementName.SVG,
712             // CPPONLY: "svg",
713             // CPPONLY: elt);
714             // CPPONLY: currentPtr++;
715             // CPPONLY: stack[currentPtr] = node;
716             // CPPONLY: }
717         }
718     }
719 
doctype(@ocal String name, String publicIdentifier, String systemIdentifier, boolean forceQuirks)720     public final void doctype(@Local String name, String publicIdentifier,
721             String systemIdentifier, boolean forceQuirks) throws SAXException {
722         needToDropLF = false;
723         if (!isInForeign() && mode == INITIAL) {
724             // [NOCPP[
725             if (reportingDoctype) {
726                 // ]NOCPP]
727                 String emptyString = Portability.newEmptyString();
728                 appendDoctypeToDocument(name == null ? "" : name,
729                         publicIdentifier == null ? emptyString
730                                 : publicIdentifier,
731                         systemIdentifier == null ? emptyString
732                                 : systemIdentifier);
733                 Portability.releaseString(emptyString);
734                 // [NOCPP[
735             }
736             // ]NOCPP]
737             if (isQuirky(name, publicIdentifier, systemIdentifier,
738                     forceQuirks)) {
739                 errQuirkyDoctype();
740                 documentModeInternal(DocumentMode.QUIRKS_MODE,
741                         publicIdentifier, systemIdentifier);
742             } else if (isAlmostStandards(publicIdentifier,
743                     systemIdentifier)) {
744                 errAlmostStandardsDoctype();
745                 documentModeInternal(
746                         DocumentMode.ALMOST_STANDARDS_MODE,
747                         publicIdentifier, systemIdentifier);
748             } else {
749                 // [NOCPP[
750                 if ((Portability.literalEqualsString(
751                         "-//W3C//DTD HTML 4.0//EN", publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString(
752                         "http://www.w3.org/TR/REC-html40/strict.dtd",
753                         systemIdentifier)))
754                         || (Portability.literalEqualsString(
755                                 "-//W3C//DTD HTML 4.01//EN",
756                                 publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString(
757                                 "http://www.w3.org/TR/html4/strict.dtd",
758                                 systemIdentifier)))
759                         || (Portability.literalEqualsString(
760                                 "-//W3C//DTD XHTML 1.0 Strict//EN",
761                                 publicIdentifier) && Portability.literalEqualsString(
762                                 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd",
763                                 systemIdentifier))
764                         || (Portability.literalEqualsString(
765                                 "-//W3C//DTD XHTML 1.1//EN",
766                                 publicIdentifier) && Portability.literalEqualsString(
767                                 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd",
768                                 systemIdentifier))
769 
770                 ) {
771                     err("Obsolete doctype. Expected \u201C<!DOCTYPE html>\u201D.");
772                 } else if (!((systemIdentifier == null || Portability.literalEqualsString(
773                         "about:legacy-compat", systemIdentifier)) && publicIdentifier == null)) {
774                     err("Legacy doctype. Expected \u201C<!DOCTYPE html>\u201D.");
775                 }
776                 // ]NOCPP]
777                 documentModeInternal(DocumentMode.STANDARDS_MODE,
778                         publicIdentifier, systemIdentifier);
779             }
780 
781             /*
782              *
783              * Then, switch to the root element mode of the tree construction
784              * stage.
785              */
786             mode = BEFORE_HTML;
787             return;
788         }
789         /*
790          * A DOCTYPE token Parse error.
791          */
792         errStrayDoctype();
793         /*
794          * Ignore the token.
795          */
796         return;
797     }
798 
comment(@oLength char[] buf, int start, int length)799     public final void comment(@NoLength char[] buf, int start, int length)
800             throws SAXException {
801         needToDropLF = false;
802         // [NOCPP[
803         if (!wantingComments) {
804             return;
805         }
806         // ]NOCPP]
807         if (!isInForeign()) {
808             switch (mode) {
809                 case INITIAL:
810                 case BEFORE_HTML:
811                 case AFTER_AFTER_BODY:
812                 case AFTER_AFTER_FRAMESET:
813                     /*
814                      * A comment token Append a Comment node to the Document
815                      * object with the data attribute set to the data given in
816                      * the comment token.
817                      */
818                     appendCommentToDocument(buf, start, length);
819                     return;
820                 case AFTER_BODY:
821                     /*
822                      * A comment token Append a Comment node to the first
823                      * element in the stack of open elements (the html element),
824                      * with the data attribute set to the data given in the
825                      * comment token.
826                      */
827                     flushCharacters();
828                     appendComment(stack[0].node, buf, start, length);
829                     return;
830                 default:
831                     break;
832             }
833         }
834         /*
835          * A comment token Append a Comment node to the current node with the
836          * data attribute set to the data given in the comment token.
837          */
838         flushCharacters();
839         appendComment(stack[currentPtr].node, buf, start, length);
840         return;
841     }
842 
843     /**
844      * @see nu.validator.htmlparser.common.TokenHandler#characters(char[], int,
845      *      int)
846      */
characters(@onst @oLength char[] buf, int start, int length)847     public final void characters(@Const @NoLength char[] buf, int start, int length)
848             throws SAXException {
849         // Note: Can't attach error messages to EOF in C++ yet
850 
851         // CPPONLY: if (tokenizer.isViewingXmlSource()) {
852         // CPPONLY: return;
853         // CPPONLY: }
854         if (needToDropLF) {
855             needToDropLF = false;
856             if (buf[start] == '\n') {
857                 start++;
858                 length--;
859                 if (length == 0) {
860                     return;
861                 }
862             }
863         }
864 
865         // optimize the most common case
866         switch (mode) {
867             case IN_BODY:
868             case IN_CELL:
869             case IN_CAPTION:
870                 if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
871                     reconstructTheActiveFormattingElements();
872                 }
873                 // CPPONLY: MOZ_FALLTHROUGH;
874             case TEXT:
875                 accumulateCharacters(buf, start, length);
876                 return;
877             case IN_TABLE:
878             case IN_TABLE_BODY:
879             case IN_ROW:
880                 accumulateCharactersForced(buf, start, length);
881                 return;
882             default:
883                 int end = start + length;
884                 charactersloop: for (int i = start; i < end; i++) {
885                     switch (buf[i]) {
886                         case ' ':
887                         case '\t':
888                         case '\n':
889                         case '\r':
890                         case '\u000C':
891                             /*
892                              * A character token that is one of one of U+0009
893                              * CHARACTER TABULATION, U+000A LINE FEED (LF),
894                              * U+000C FORM FEED (FF), or U+0020 SPACE
895                              */
896                             switch (mode) {
897                                 case INITIAL:
898                                 case BEFORE_HTML:
899                                 case BEFORE_HEAD:
900                                     /*
901                                      * Ignore the token.
902                                      */
903                                     start = i + 1;
904                                     continue;
905                                 case IN_HEAD:
906                                 case IN_HEAD_NOSCRIPT:
907                                 case AFTER_HEAD:
908                                 case IN_COLUMN_GROUP:
909                                 case IN_FRAMESET:
910                                 case AFTER_FRAMESET:
911                                     /*
912                                      * Append the character to the current node.
913                                      */
914                                     continue;
915                                 case FRAMESET_OK:
916                                 case IN_TEMPLATE:
917                                 case IN_BODY:
918                                 case IN_CELL:
919                                 case IN_CAPTION:
920                                     if (start < i) {
921                                         accumulateCharacters(buf, start, i
922                                                 - start);
923                                         start = i;
924                                     }
925 
926                                     /*
927                                      * Reconstruct the active formatting
928                                      * elements, if any.
929                                      */
930                                     if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
931                                         flushCharacters();
932                                         reconstructTheActiveFormattingElements();
933                                     }
934                                     /*
935                                      * Append the token's character to the
936                                      * current node.
937                                      */
938                                     break charactersloop;
939                                 case IN_SELECT:
940                                 case IN_SELECT_IN_TABLE:
941                                     break charactersloop;
942                                 case IN_TABLE:
943                                 case IN_TABLE_BODY:
944                                 case IN_ROW:
945                                     accumulateCharactersForced(buf, i, 1);
946                                     start = i + 1;
947                                     continue;
948                                 case AFTER_BODY:
949                                 case AFTER_AFTER_BODY:
950                                 case AFTER_AFTER_FRAMESET:
951                                     if (start < i) {
952                                         accumulateCharacters(buf, start, i
953                                                 - start);
954                                         start = i;
955                                     }
956                                     /*
957                                      * Reconstruct the active formatting
958                                      * elements, if any.
959                                      */
960                                     flushCharacters();
961                                     reconstructTheActiveFormattingElements();
962                                     /*
963                                      * Append the token's character to the
964                                      * current node.
965                                      */
966                                     continue;
967                             }
968                             // CPPONLY: MOZ_FALLTHROUGH_ASSERT();
969                         default:
970                             /*
971                              * A character token that is not one of one of
972                              * U+0009 CHARACTER TABULATION, U+000A LINE FEED
973                              * (LF), U+000C FORM FEED (FF), or U+0020 SPACE
974                              */
975                             switch (mode) {
976                                 case INITIAL:
977                                     /*
978                                      * Parse error.
979                                      */
980                                     // [NOCPP[
981                                     // XXX figure out a way to report this in the Gecko View Source case
982                                     err("Non-space characters found without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
983                                     // ]NOCPP]
984                                     /*
985                                      *
986                                      * Set the document to quirks mode.
987                                      */
988                                     documentModeInternal(
989                                             DocumentMode.QUIRKS_MODE, null,
990                                             null);
991                                     /*
992                                      * Then, switch to the root element mode of
993                                      * the tree construction stage
994                                      */
995                                     mode = BEFORE_HTML;
996                                     /*
997                                      * and reprocess the current token.
998                                      */
999                                     i--;
1000                                     continue;
1001                                 case BEFORE_HTML:
1002                                     /*
1003                                      * Create an HTMLElement node with the tag
1004                                      * name html, in the HTML namespace. Append
1005                                      * it to the Document object.
1006                                      */
1007                                     // No need to flush characters here,
1008                                     // because there's nothing to flush.
1009                                     appendHtmlElementToDocumentAndPush();
1010                                     /* Switch to the main mode */
1011                                     mode = BEFORE_HEAD;
1012                                     /*
1013                                      * reprocess the current token.
1014                                      */
1015                                     i--;
1016                                     continue;
1017                                 case BEFORE_HEAD:
1018                                     if (start < i) {
1019                                         accumulateCharacters(buf, start, i
1020                                                 - start);
1021                                         start = i;
1022                                     }
1023                                     /*
1024                                      * /Act as if a start tag token with the tag
1025                                      * name "head" and no attributes had been
1026                                      * seen,
1027                                      */
1028                                     flushCharacters();
1029                                     appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
1030                                     mode = IN_HEAD;
1031                                     /*
1032                                      * then reprocess the current token.
1033                                      *
1034                                      * This will result in an empty head element
1035                                      * being generated, with the current token
1036                                      * being reprocessed in the "after head"
1037                                      * insertion mode.
1038                                      */
1039                                     i--;
1040                                     continue;
1041                                 case IN_HEAD:
1042                                     if (start < i) {
1043                                         accumulateCharacters(buf, start, i
1044                                                 - start);
1045                                         start = i;
1046                                     }
1047                                     /*
1048                                      * Act as if an end tag token with the tag
1049                                      * name "head" had been seen,
1050                                      */
1051                                     flushCharacters();
1052                                     pop();
1053                                     mode = AFTER_HEAD;
1054                                     /*
1055                                      * and reprocess the current token.
1056                                      */
1057                                     i--;
1058                                     continue;
1059                                 case IN_HEAD_NOSCRIPT:
1060                                     if (start < i) {
1061                                         accumulateCharacters(buf, start, i
1062                                                 - start);
1063                                         start = i;
1064                                     }
1065                                     /*
1066                                      * Parse error. Act as if an end tag with
1067                                      * the tag name "noscript" had been seen
1068                                      */
1069                                     errNonSpaceInNoscriptInHead();
1070                                     flushCharacters();
1071                                     pop();
1072                                     mode = IN_HEAD;
1073                                     /*
1074                                      * and reprocess the current token.
1075                                      */
1076                                     i--;
1077                                     continue;
1078                                 case AFTER_HEAD:
1079                                     if (start < i) {
1080                                         accumulateCharacters(buf, start, i
1081                                                 - start);
1082                                         start = i;
1083                                     }
1084                                     /*
1085                                      * Act as if a start tag token with the tag
1086                                      * name "body" and no attributes had been
1087                                      * seen,
1088                                      */
1089                                     flushCharacters();
1090                                     appendToCurrentNodeAndPushBodyElement();
1091                                     mode = FRAMESET_OK;
1092                                     /*
1093                                      * and then reprocess the current token.
1094                                      */
1095                                     i--;
1096                                     continue;
1097                                 case FRAMESET_OK:
1098                                     framesetOk = false;
1099                                     mode = IN_BODY;
1100                                     i--;
1101                                     continue;
1102                                 case IN_TEMPLATE:
1103                                 case IN_BODY:
1104                                 case IN_CELL:
1105                                 case IN_CAPTION:
1106                                     if (start < i) {
1107                                         accumulateCharacters(buf, start, i
1108                                                 - start);
1109                                         start = i;
1110                                     }
1111                                     /*
1112                                      * Reconstruct the active formatting
1113                                      * elements, if any.
1114                                      */
1115                                     if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
1116                                         flushCharacters();
1117                                         reconstructTheActiveFormattingElements();
1118                                     }
1119                                     /*
1120                                      * Append the token's character to the
1121                                      * current node.
1122                                      */
1123                                     break charactersloop;
1124                                 case IN_TABLE:
1125                                 case IN_TABLE_BODY:
1126                                 case IN_ROW:
1127                                     accumulateCharactersForced(buf, i, 1);
1128                                     start = i + 1;
1129                                     continue;
1130                                 case IN_COLUMN_GROUP:
1131                                     if (start < i) {
1132                                         accumulateCharacters(buf, start, i
1133                                                 - start);
1134                                         start = i;
1135                                     }
1136                                     /*
1137                                      * Act as if an end tag with the tag name
1138                                      * "colgroup" had been seen, and then, if
1139                                      * that token wasn't ignored, reprocess the
1140                                      * current token.
1141                                      */
1142                                     if (currentPtr == 0 || stack[currentPtr].getGroup() ==
1143                                             TreeBuilder.TEMPLATE) {
1144                                         errNonSpaceInColgroupInFragment();
1145                                         start = i + 1;
1146                                         continue;
1147                                     }
1148                                     flushCharacters();
1149                                     pop();
1150                                     mode = IN_TABLE;
1151                                     i--;
1152                                     continue;
1153                                 case IN_SELECT:
1154                                 case IN_SELECT_IN_TABLE:
1155                                     break charactersloop;
1156                                 case AFTER_BODY:
1157                                     errNonSpaceAfterBody();
1158                                     fatal();
1159                                     mode = framesetOk ? FRAMESET_OK : IN_BODY;
1160                                     i--;
1161                                     continue;
1162                                 case IN_FRAMESET:
1163                                     if (start < i) {
1164                                         accumulateCharacters(buf, start, i
1165                                                 - start);
1166                                         // start index is adjusted below.
1167                                     }
1168                                     /*
1169                                      * Parse error.
1170                                      */
1171                                     errNonSpaceInFrameset();
1172                                     /*
1173                                      * Ignore the token.
1174                                      */
1175                                     start = i + 1;
1176                                     continue;
1177                                 case AFTER_FRAMESET:
1178                                     if (start < i) {
1179                                         accumulateCharacters(buf, start, i
1180                                                 - start);
1181                                         // start index is adjusted below.
1182                                     }
1183                                     /*
1184                                      * Parse error.
1185                                      */
1186                                     errNonSpaceAfterFrameset();
1187                                     /*
1188                                      * Ignore the token.
1189                                      */
1190                                     start = i + 1;
1191                                     continue;
1192                                 case AFTER_AFTER_BODY:
1193                                     /*
1194                                      * Parse error.
1195                                      */
1196                                     errNonSpaceInTrailer();
1197                                     /*
1198                                      * Switch back to the main mode and
1199                                      * reprocess the token.
1200                                      */
1201                                     mode = framesetOk ? FRAMESET_OK : IN_BODY;
1202                                     i--;
1203                                     continue;
1204                                 case AFTER_AFTER_FRAMESET:
1205                                     if (start < i) {
1206                                         accumulateCharacters(buf, start, i
1207                                                 - start);
1208                                         // start index is adjusted below.
1209                                     }
1210                                     /*
1211                                      * Parse error.
1212                                      */
1213                                     errNonSpaceInTrailer();
1214                                     /*
1215                                      * Ignore the token.
1216                                      */
1217                                     start = i + 1;
1218                                     continue;
1219                             }
1220                     }
1221                 }
1222                 if (start < end) {
1223                     accumulateCharacters(buf, start, end - start);
1224                 }
1225         }
1226     }
1227 
1228     /**
1229      * @see nu.validator.htmlparser.common.TokenHandler#zeroOriginatingReplacementCharacter()
1230      */
zeroOriginatingReplacementCharacter()1231     public void zeroOriginatingReplacementCharacter() throws SAXException {
1232         if (mode == TEXT) {
1233             accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
1234             return;
1235         }
1236         if (currentPtr >= 0) {
1237             if (isSpecialParentInForeign(stack[currentPtr])) {
1238                 return;
1239             }
1240             accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
1241         }
1242     }
1243 
eof()1244     public final void eof() throws SAXException {
1245         flushCharacters();
1246         // Note: Can't attach error messages to EOF in C++ yet
1247         eofloop: for (;;) {
1248             switch (mode) {
1249                 case INITIAL:
1250                     /*
1251                      * Parse error.
1252                      */
1253                     // [NOCPP[
1254                     err("End of file seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
1255                     // ]NOCPP]
1256                     /*
1257                      *
1258                      * Set the document to quirks mode.
1259                      */
1260                     documentModeInternal(DocumentMode.QUIRKS_MODE, null, null);
1261                     /*
1262                      * Then, switch to the root element mode of the tree
1263                      * construction stage
1264                      */
1265                     mode = BEFORE_HTML;
1266                     /*
1267                      * and reprocess the current token.
1268                      */
1269                     continue;
1270                 case BEFORE_HTML:
1271                     /*
1272                      * Create an HTMLElement node with the tag name html, in the
1273                      * HTML namespace. Append it to the Document object.
1274                      */
1275                     appendHtmlElementToDocumentAndPush();
1276                     // XXX application cache manifest
1277                     /* Switch to the main mode */
1278                     mode = BEFORE_HEAD;
1279                     /*
1280                      * reprocess the current token.
1281                      */
1282                     continue;
1283                 case BEFORE_HEAD:
1284                     appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
1285                     mode = IN_HEAD;
1286                     continue;
1287                 case IN_HEAD:
1288                     // [NOCPP[
1289                     if (errorHandler != null && currentPtr > 1) {
1290                         errEofWithUnclosedElements();
1291                     }
1292                     // ]NOCPP]
1293                     while (currentPtr > 0) {
1294                         popOnEof();
1295                     }
1296                     mode = AFTER_HEAD;
1297                     continue;
1298                 case IN_HEAD_NOSCRIPT:
1299                     // [NOCPP[
1300                     errEofWithUnclosedElements();
1301                     // ]NOCPP]
1302                     while (currentPtr > 1) {
1303                         popOnEof();
1304                     }
1305                     mode = IN_HEAD;
1306                     continue;
1307                 case AFTER_HEAD:
1308                     appendToCurrentNodeAndPushBodyElement();
1309                     mode = IN_BODY;
1310                     continue;
1311                 case IN_TABLE_BODY:
1312                 case IN_ROW:
1313                 case IN_TABLE:
1314                 case IN_SELECT_IN_TABLE:
1315                 case IN_SELECT:
1316                 case IN_COLUMN_GROUP:
1317                 case FRAMESET_OK:
1318                 case IN_CAPTION:
1319                 case IN_CELL:
1320                 case IN_BODY:
1321                     // [NOCPP[
1322                     // i > 0 to stop in time in the foreign fragment case.
1323                     openelementloop: for (int i = currentPtr; i > 0; i--) {
1324                         int group = stack[i].getGroup();
1325                         switch (group) {
1326                             case DD_OR_DT:
1327                             case LI:
1328                             case P:
1329                             case TBODY_OR_THEAD_OR_TFOOT:
1330                             case TD_OR_TH:
1331                             case BODY:
1332                             case HTML:
1333                                 break;
1334                             default:
1335                                 errEofWithUnclosedElements();
1336                                 break openelementloop;
1337                         }
1338                     }
1339                     // ]NOCPP]
1340 
1341                     if (isTemplateModeStackEmpty()) {
1342                         break eofloop;
1343                     }
1344 
1345                     // fall through to IN_TEMPLATE
1346                     // CPPONLY: MOZ_FALLTHROUGH;
1347                 case IN_TEMPLATE:
1348                     int eltPos = findLast("template");
1349                     if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1350                         assert fragment;
1351                         break eofloop;
1352                     }
1353                     if (errorHandler != null) {
1354                         errUnclosedElements(eltPos, "template");
1355                     }
1356                     while (currentPtr >= eltPos) {
1357                         pop();
1358                     }
1359                     clearTheListOfActiveFormattingElementsUpToTheLastMarker();
1360                     popTemplateMode();
1361                     resetTheInsertionMode();
1362 
1363                     // Reprocess token.
1364                     continue;
1365                 case TEXT:
1366                     // [NOCPP[
1367                     if (errorHandler != null) {
1368                         errNoCheck("End of file seen when expecting text or an end tag.");
1369                         errListUnclosedStartTags(0);
1370                     }
1371                     // ]NOCPP]
1372                     // XXX mark script as already executed
1373                     if (originalMode == AFTER_HEAD) {
1374                         popOnEof();
1375                     }
1376                     popOnEof();
1377                     mode = originalMode;
1378                     continue;
1379                 case IN_FRAMESET:
1380                     // [NOCPP[
1381                     if (errorHandler != null && currentPtr > 0) {
1382                         errEofWithUnclosedElements();
1383                     }
1384                     // ]NOCPP]
1385                     break eofloop;
1386                 case AFTER_BODY:
1387                 case AFTER_FRAMESET:
1388                 case AFTER_AFTER_BODY:
1389                 case AFTER_AFTER_FRAMESET:
1390                 default:
1391                     // [NOCPP[
1392                     if (currentPtr == 0) { // This silliness is here to poison
1393                         // buggy compiler optimizations in
1394                         // GWT
1395                         System.currentTimeMillis();
1396                     }
1397                     // ]NOCPP]
1398                     break eofloop;
1399             }
1400         }
1401         while (currentPtr > 0) {
1402             popOnEof();
1403         }
1404         if (!fragment) {
1405             popOnEof();
1406         }
1407         /* Stop parsing. */
1408     }
1409 
1410     /**
1411      * @see nu.validator.htmlparser.common.TokenHandler#endTokenization()
1412      */
endTokenization()1413     public final void endTokenization() throws SAXException {
1414         formPointer = null;
1415         headPointer = null;
1416         contextName = null;
1417         contextNode = null;
1418         templateModeStack = null;
1419         if (stack != null) {
1420             while (currentPtr > -1) {
1421                 stack[currentPtr].release(this);
1422                 currentPtr--;
1423             }
1424             stack = null;
1425         }
1426         if (listOfActiveFormattingElements != null) {
1427             while (listPtr > -1) {
1428                 if (listOfActiveFormattingElements[listPtr] != null) {
1429                     listOfActiveFormattingElements[listPtr].release(this);
1430                 }
1431                 listPtr--;
1432             }
1433             listOfActiveFormattingElements = null;
1434         }
1435         if (stackNodes != null) {
1436             for (int i = 0; i < numStackNodes; i++) {
1437                 assert stackNodes[i].isUnused();
1438                 Portability.delete(stackNodes[i]);
1439             }
1440             numStackNodes = 0;
1441             stackNodesIdx = 0;
1442             stackNodes = null;
1443         }
1444         // [NOCPP[
1445         idLocations.clear();
1446         // ]NOCPP]
1447         charBuffer = null;
1448         end();
1449     }
1450 
startTag(ElementName elementName, HtmlAttributes attributes, boolean selfClosing)1451     public final void startTag(ElementName elementName,
1452             HtmlAttributes attributes, boolean selfClosing) throws SAXException {
1453         flushCharacters();
1454 
1455         // [NOCPP[
1456         if (errorHandler != null) {
1457             // ID uniqueness
1458             @IdType String id = attributes.getId();
1459             if (id != null) {
1460                 LocatorImpl oldLoc = idLocations.get(id);
1461                 if (oldLoc != null) {
1462                     err("Duplicate ID \u201C" + id + "\u201D.");
1463                     errorHandler.warning(new SAXParseException(
1464                             "The first occurrence of ID \u201C" + id
1465                             + "\u201D was here.", oldLoc));
1466                 } else {
1467                     idLocations.put(id, new LocatorImpl(tokenizer));
1468                 }
1469             }
1470         }
1471         // ]NOCPP]
1472 
1473         int eltPos;
1474         needToDropLF = false;
1475         starttagloop: for (;;) {
1476             int group = elementName.getGroup();
1477             @Local String name = elementName.getName();
1478             if (isInForeign()) {
1479                 StackNode<T> currentNode = stack[currentPtr];
1480                 @NsUri String currNs = currentNode.ns;
1481                 if (!(currentNode.isHtmlIntegrationPoint() || (currNs == "http://www.w3.org/1998/Math/MathML" && ((currentNode.getGroup() == MI_MO_MN_MS_MTEXT && group != MGLYPH_OR_MALIGNMARK) || (currentNode.getGroup() == ANNOTATION_XML && group == SVG))))) {
1482                     switch (group) {
1483                         case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
1484                         case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
1485                         case BODY:
1486                         case BR:
1487                         case RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR:
1488                         case DD_OR_DT:
1489                         case UL_OR_OL_OR_DL:
1490                         case EMBED:
1491                         case IMG:
1492                         case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
1493                         case HEAD:
1494                         case HR:
1495                         case LI:
1496                         case META:
1497                         case NOBR:
1498                         case P:
1499                         case PRE_OR_LISTING:
1500                         case TABLE:
1501                         case FONT:
1502                             // re-check FONT to deal with the special case
1503                             if (!(group == FONT && !(attributes.contains(AttributeName.COLOR)
1504                                     || attributes.contains(AttributeName.FACE) || attributes.contains(AttributeName.SIZE)))) {
1505                                 errHtmlStartTagInForeignContext(name);
1506                                 if (!fragment) {
1507                                     while (!isSpecialParentInForeign(stack[currentPtr])) {
1508                                         popForeign(-1, -1);
1509                                     }
1510                                     continue starttagloop;
1511                                 } // else fall thru
1512                             }
1513                             // CPPONLY: MOZ_FALLTHROUGH;
1514                         default:
1515                             if ("http://www.w3.org/2000/svg" == currNs) {
1516                                 attributes.adjustForSvg();
1517                                 if (selfClosing) {
1518                                     appendVoidElementToCurrentMayFosterSVG(
1519                                             elementName, attributes);
1520                                     selfClosing = false;
1521                                 } else {
1522                                     appendToCurrentNodeAndPushElementMayFosterSVG(
1523                                             elementName, attributes);
1524                                 }
1525                                 attributes = null; // CPP
1526                                 break starttagloop;
1527                             } else {
1528                                 attributes.adjustForMath();
1529                                 if (selfClosing) {
1530                                     appendVoidElementToCurrentMayFosterMathML(
1531                                             elementName, attributes);
1532                                     selfClosing = false;
1533                                 } else {
1534                                     appendToCurrentNodeAndPushElementMayFosterMathML(
1535                                             elementName, attributes);
1536                                 }
1537                                 attributes = null; // CPP
1538                                 break starttagloop;
1539                             }
1540                     } // switch
1541                 } // foreignObject / annotation-xml
1542             }
1543             switch (mode) {
1544                 case IN_TEMPLATE:
1545                     switch (group) {
1546                         case COL:
1547                             popTemplateMode();
1548                             pushTemplateMode(IN_COLUMN_GROUP);
1549                             mode = IN_COLUMN_GROUP;
1550                             // Reprocess token.
1551                             continue;
1552                         case CAPTION:
1553                         case COLGROUP:
1554                         case TBODY_OR_THEAD_OR_TFOOT:
1555                             popTemplateMode();
1556                             pushTemplateMode(IN_TABLE);
1557                             mode = IN_TABLE;
1558                             // Reprocess token.
1559                             continue;
1560                         case TR:
1561                             popTemplateMode();
1562                             pushTemplateMode(IN_TABLE_BODY);
1563                             mode = IN_TABLE_BODY;
1564                             // Reprocess token.
1565                             continue;
1566                         case TD_OR_TH:
1567                             popTemplateMode();
1568                             pushTemplateMode(IN_ROW);
1569                             mode = IN_ROW;
1570                             // Reprocess token.
1571                             continue;
1572                         case META:
1573                             checkMetaCharset(attributes);
1574                             appendVoidElementToCurrentMayFoster(
1575                                     elementName,
1576                                     attributes);
1577                             selfClosing = false;
1578                             attributes = null; // CPP
1579                             break starttagloop;
1580                         case TITLE:
1581                             startTagTitleInHead(elementName, attributes);
1582                             attributes = null; // CPP
1583                             break starttagloop;
1584                         case BASE:
1585                         case LINK_OR_BASEFONT_OR_BGSOUND:
1586                             appendVoidElementToCurrentMayFoster(
1587                                     elementName,
1588                                     attributes);
1589                             selfClosing = false;
1590                             attributes = null; // CPP
1591                             break starttagloop;
1592                         case SCRIPT:
1593                             startTagScriptInHead(elementName, attributes);
1594                             attributes = null; // CPP
1595                             break starttagloop;
1596                         case NOFRAMES:
1597                         case STYLE:
1598                             startTagGenericRawText(elementName, attributes);
1599                             attributes = null; // CPP
1600                             break starttagloop;
1601                         case TEMPLATE:
1602                             startTagTemplateInHead(elementName, attributes);
1603                             attributes = null; // CPP
1604                             break starttagloop;
1605                         default:
1606                             popTemplateMode();
1607                             pushTemplateMode(IN_BODY);
1608                             mode = IN_BODY;
1609                             // Reprocess token.
1610                             continue;
1611                     }
1612                 case IN_ROW:
1613                     switch (group) {
1614                         case TD_OR_TH:
1615                             clearStackBackTo(findLastOrRoot(TreeBuilder.TR));
1616                             appendToCurrentNodeAndPushElement(
1617                                     elementName,
1618                                     attributes);
1619                             mode = IN_CELL;
1620                             insertMarker();
1621                             attributes = null; // CPP
1622                             break starttagloop;
1623                         case CAPTION:
1624                         case COL:
1625                         case COLGROUP:
1626                         case TBODY_OR_THEAD_OR_TFOOT:
1627                         case TR:
1628                             eltPos = findLastOrRoot(TreeBuilder.TR);
1629                             if (eltPos == 0) {
1630                                 assert fragment || isTemplateContents();
1631                                 errNoTableRowToClose();
1632                                 break starttagloop;
1633                             }
1634                             clearStackBackTo(eltPos);
1635                             pop();
1636                             mode = IN_TABLE_BODY;
1637                             continue;
1638                         default:
1639                             // fall through to IN_TABLE
1640                     }
1641                     // CPPONLY: MOZ_FALLTHROUGH;
1642                 case IN_TABLE_BODY:
1643                     switch (group) {
1644                         case TR:
1645                             clearStackBackTo(findLastInTableScopeOrRootTemplateTbodyTheadTfoot());
1646                             appendToCurrentNodeAndPushElement(
1647                                     elementName,
1648                                     attributes);
1649                             mode = IN_ROW;
1650                             attributes = null; // CPP
1651                             break starttagloop;
1652                         case TD_OR_TH:
1653                             errStartTagInTableBody(name);
1654                             clearStackBackTo(findLastInTableScopeOrRootTemplateTbodyTheadTfoot());
1655                             appendToCurrentNodeAndPushElement(
1656                                     ElementName.TR,
1657                                     HtmlAttributes.EMPTY_ATTRIBUTES);
1658                             mode = IN_ROW;
1659                             continue;
1660                         case CAPTION:
1661                         case COL:
1662                         case COLGROUP:
1663                         case TBODY_OR_THEAD_OR_TFOOT:
1664                             eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot();
1665                             if (eltPos == 0 || stack[eltPos].getGroup() == TEMPLATE) {
1666                                 assert fragment || isTemplateContents();
1667                                 errStrayStartTag(name);
1668                                 break starttagloop;
1669                             } else {
1670                                 clearStackBackTo(eltPos);
1671                                 pop();
1672                                 mode = IN_TABLE;
1673                                 continue;
1674                             }
1675                         default:
1676                             // fall through to IN_TABLE
1677                     }
1678                     // CPPONLY: MOZ_FALLTHROUGH;
1679                 case IN_TABLE:
1680                     intableloop: for (;;) {
1681                         switch (group) {
1682                             case CAPTION:
1683                                 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1684                                 insertMarker();
1685                                 appendToCurrentNodeAndPushElement(
1686                                         elementName,
1687                                         attributes);
1688                                 mode = IN_CAPTION;
1689                                 attributes = null; // CPP
1690                                 break starttagloop;
1691                             case COLGROUP:
1692                                 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1693                                 appendToCurrentNodeAndPushElement(
1694                                         elementName,
1695                                         attributes);
1696                                 mode = IN_COLUMN_GROUP;
1697                                 attributes = null; // CPP
1698                                 break starttagloop;
1699                             case COL:
1700                                 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1701                                 appendToCurrentNodeAndPushElement(
1702                                         ElementName.COLGROUP,
1703                                         HtmlAttributes.EMPTY_ATTRIBUTES);
1704                                 mode = IN_COLUMN_GROUP;
1705                                 continue starttagloop;
1706                             case TBODY_OR_THEAD_OR_TFOOT:
1707                                 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1708                                 appendToCurrentNodeAndPushElement(
1709                                         elementName,
1710                                         attributes);
1711                                 mode = IN_TABLE_BODY;
1712                                 attributes = null; // CPP
1713                                 break starttagloop;
1714                             case TR:
1715                             case TD_OR_TH:
1716                                 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1717                                 appendToCurrentNodeAndPushElement(
1718                                         ElementName.TBODY,
1719                                         HtmlAttributes.EMPTY_ATTRIBUTES);
1720                                 mode = IN_TABLE_BODY;
1721                                 continue starttagloop;
1722                             case TEMPLATE:
1723                                 // fall through to IN_HEAD
1724                                 break intableloop;
1725                             case TABLE:
1726                                 errTableSeenWhileTableOpen();
1727                                 eltPos = findLastInTableScope(name);
1728                                 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1729                                     assert fragment || isTemplateContents();
1730                                     break starttagloop;
1731                                 }
1732                                 generateImpliedEndTags();
1733                                 if (errorHandler != null && !isCurrent("table")) {
1734                                     errNoCheckUnclosedElementsOnStack();
1735                                 }
1736                                 while (currentPtr >= eltPos) {
1737                                     pop();
1738                                 }
1739                                 resetTheInsertionMode();
1740                                 continue starttagloop;
1741                             case SCRIPT:
1742                                 // XXX need to manage much more stuff
1743                                 // here if
1744                                 // supporting
1745                                 // document.write()
1746                                 appendToCurrentNodeAndPushElement(
1747                                         elementName,
1748                                         attributes);
1749                                 originalMode = mode;
1750                                 mode = TEXT;
1751                                 tokenizer.setStateAndEndTagExpectation(
1752                                         Tokenizer.SCRIPT_DATA, elementName);
1753                                 attributes = null; // CPP
1754                                 break starttagloop;
1755                             case STYLE:
1756                                 appendToCurrentNodeAndPushElement(
1757                                         elementName,
1758                                         attributes);
1759                                 originalMode = mode;
1760                                 mode = TEXT;
1761                                 tokenizer.setStateAndEndTagExpectation(
1762                                         Tokenizer.RAWTEXT, elementName);
1763                                 attributes = null; // CPP
1764                                 break starttagloop;
1765                             case INPUT:
1766                                 errStartTagInTable(name);
1767                                 if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
1768                                         "hidden",
1769                                         attributes.getValue(AttributeName.TYPE))) {
1770                                     break intableloop;
1771                                 }
1772                                 appendVoidInputToCurrent(
1773                                         attributes,
1774                                         formPointer);
1775                                 selfClosing = false;
1776                                 attributes = null; // CPP
1777                                 break starttagloop;
1778                             case FORM:
1779                                 if (formPointer != null || isTemplateContents()) {
1780                                     errFormWhenFormOpen();
1781                                     break starttagloop;
1782                                 } else {
1783                                     errStartTagInTable(name);
1784                                     appendVoidFormToCurrent(attributes);
1785                                     attributes = null; // CPP
1786                                     break starttagloop;
1787                                 }
1788                             default:
1789                                 errStartTagInTable(name);
1790                                 // fall through to IN_BODY
1791                                 break intableloop;
1792                         }
1793                     }
1794                     // CPPONLY: MOZ_FALLTHROUGH;
1795                 case IN_CAPTION:
1796                     switch (group) {
1797                         case CAPTION:
1798                         case COL:
1799                         case COLGROUP:
1800                         case TBODY_OR_THEAD_OR_TFOOT:
1801                         case TR:
1802                         case TD_OR_TH:
1803                             eltPos = findLastInTableScope("caption");
1804                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1805                                 assert fragment || isTemplateContents();
1806                                 errStrayStartTag(name);
1807                                 break starttagloop;
1808                             }
1809                             generateImpliedEndTags();
1810                             if (errorHandler != null && currentPtr != eltPos) {
1811                                 errNoCheckUnclosedElementsOnStack();
1812                             }
1813                             while (currentPtr >= eltPos) {
1814                                 pop();
1815                             }
1816                             clearTheListOfActiveFormattingElementsUpToTheLastMarker();
1817                             mode = IN_TABLE;
1818                             continue;
1819                         default:
1820                             // fall through to IN_BODY
1821                     }
1822                     // CPPONLY: MOZ_FALLTHROUGH;
1823                 case IN_CELL:
1824                     switch (group) {
1825                         case CAPTION:
1826                         case COL:
1827                         case COLGROUP:
1828                         case TBODY_OR_THEAD_OR_TFOOT:
1829                         case TR:
1830                         case TD_OR_TH:
1831                             eltPos = findLastInTableScopeTdTh();
1832                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1833                                 errNoCellToClose();
1834                                 break starttagloop;
1835                             } else {
1836                                 closeTheCell(eltPos);
1837                                 continue;
1838                             }
1839                         default:
1840                             // fall through to IN_BODY
1841                     }
1842                     // CPPONLY: MOZ_FALLTHROUGH;
1843                 case FRAMESET_OK:
1844                     switch (group) {
1845                         case FRAMESET:
1846                             if (mode == FRAMESET_OK) {
1847                                 if (currentPtr == 0 || stack[1].getGroup() != BODY) {
1848                                     assert fragment || isTemplateContents();
1849                                     errStrayStartTag(name);
1850                                     break starttagloop;
1851                                 } else {
1852                                     errFramesetStart();
1853                                     detachFromParent(stack[1].node);
1854                                     while (currentPtr > 0) {
1855                                         pop();
1856                                     }
1857                                     appendToCurrentNodeAndPushElement(
1858                                             elementName,
1859                                             attributes);
1860                                     mode = IN_FRAMESET;
1861                                     attributes = null; // CPP
1862                                     break starttagloop;
1863                                 }
1864                             } else {
1865                                 errStrayStartTag(name);
1866                                 break starttagloop;
1867                             }
1868                             // NOT falling through!
1869                         case PRE_OR_LISTING:
1870                         case LI:
1871                         case DD_OR_DT:
1872                         case BUTTON:
1873                         case MARQUEE_OR_APPLET:
1874                         case OBJECT:
1875                         case TABLE:
1876                         case AREA_OR_WBR:
1877                         case KEYGEN:
1878                         case BR:
1879                         case EMBED:
1880                         case IMG:
1881                         case INPUT:
1882                         case HR:
1883                         case TEXTAREA:
1884                         case XMP:
1885                         case IFRAME:
1886                         case SELECT:
1887                             if (mode == FRAMESET_OK
1888                                     && !(group == INPUT && Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
1889                                             "hidden",
1890                                             attributes.getValue(AttributeName.TYPE)))) {
1891                                 framesetOk = false;
1892                                 mode = IN_BODY;
1893                             }
1894                             // CPPONLY: MOZ_FALLTHROUGH;
1895                         default:
1896                             // fall through to IN_BODY
1897                     }
1898                     // CPPONLY: MOZ_FALLTHROUGH;
1899                 case IN_BODY:
1900                     inbodyloop: for (;;) {
1901                         switch (group) {
1902                             case HTML:
1903                                 errStrayStartTag(name);
1904                                 if (!fragment && !isTemplateContents()) {
1905                                     addAttributesToHtml(attributes);
1906                                     attributes = null; // CPP
1907                                 }
1908                                 break starttagloop;
1909                             case BASE:
1910                             case LINK_OR_BASEFONT_OR_BGSOUND:
1911                             case META:
1912                             case STYLE:
1913                             case SCRIPT:
1914                             case TITLE:
1915                             case TEMPLATE:
1916                                 // Fall through to IN_HEAD
1917                                 break inbodyloop;
1918                             case BODY:
1919                                 if (currentPtr == 0 || stack[1].getGroup() != BODY || isTemplateContents()) {
1920                                     assert fragment || isTemplateContents();
1921                                     errStrayStartTag(name);
1922                                     break starttagloop;
1923                                 }
1924                                 errFooSeenWhenFooOpen(name);
1925                                 framesetOk = false;
1926                                 if (mode == FRAMESET_OK) {
1927                                     mode = IN_BODY;
1928                                 }
1929                                 if (addAttributesToBody(attributes)) {
1930                                     attributes = null; // CPP
1931                                 }
1932                                 break starttagloop;
1933                             case P:
1934                             case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
1935                             case UL_OR_OL_OR_DL:
1936                             case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY:
1937                                 implicitlyCloseP();
1938                                 appendToCurrentNodeAndPushElementMayFoster(
1939                                         elementName,
1940                                         attributes);
1941                                 attributes = null; // CPP
1942                                 break starttagloop;
1943                             case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
1944                                 implicitlyCloseP();
1945                                 if (stack[currentPtr].getGroup() == H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) {
1946                                     errHeadingWhenHeadingOpen();
1947                                     pop();
1948                                 }
1949                                 appendToCurrentNodeAndPushElementMayFoster(
1950                                         elementName,
1951                                         attributes);
1952                                 attributes = null; // CPP
1953                                 break starttagloop;
1954                             case FIELDSET:
1955                                 implicitlyCloseP();
1956                                 appendToCurrentNodeAndPushElementMayFoster(
1957                                         elementName,
1958                                         attributes, formPointer);
1959                                 attributes = null; // CPP
1960                                 break starttagloop;
1961                             case PRE_OR_LISTING:
1962                                 implicitlyCloseP();
1963                                 appendToCurrentNodeAndPushElementMayFoster(
1964                                         elementName,
1965                                         attributes);
1966                                 needToDropLF = true;
1967                                 attributes = null; // CPP
1968                                 break starttagloop;
1969                             case FORM:
1970                                 if (formPointer != null && !isTemplateContents()) {
1971                                     errFormWhenFormOpen();
1972                                     break starttagloop;
1973                                 } else {
1974                                     implicitlyCloseP();
1975                                     appendToCurrentNodeAndPushFormElementMayFoster(attributes);
1976                                     attributes = null; // CPP
1977                                     break starttagloop;
1978                                 }
1979                             case LI:
1980                             case DD_OR_DT:
1981                                 eltPos = currentPtr;
1982                                 for (;;) {
1983                                     StackNode<T> node = stack[eltPos]; // weak
1984                                     // ref
1985                                     if (node.getGroup() == group) { // LI or
1986                                         // DD_OR_DT
1987                                         generateImpliedEndTagsExceptFor(node.name);
1988                                         if (errorHandler != null
1989                                                 && eltPos != currentPtr) {
1990                                             errUnclosedElementsImplied(eltPos, name);
1991                                         }
1992                                         while (currentPtr >= eltPos) {
1993                                             pop();
1994                                         }
1995                                         break;
1996                                     } else if (eltPos == 0 || (node.isSpecial()
1997                                             && (node.ns != "http://www.w3.org/1999/xhtml"
1998                                                     || (node.name != "p"
1999                                                             && node.name != "address"
2000                                                             && node.name != "div")))) {
2001                                         break;
2002                                     }
2003                                     eltPos--;
2004                                 }
2005                                 implicitlyCloseP();
2006                                 appendToCurrentNodeAndPushElementMayFoster(
2007                                         elementName,
2008                                         attributes);
2009                                 attributes = null; // CPP
2010                                 break starttagloop;
2011                             case PLAINTEXT:
2012                                 implicitlyCloseP();
2013                                 appendToCurrentNodeAndPushElementMayFoster(
2014                                         elementName,
2015                                         attributes);
2016                                 tokenizer.setStateAndEndTagExpectation(
2017                                         Tokenizer.PLAINTEXT, elementName);
2018                                 attributes = null; // CPP
2019                                 break starttagloop;
2020                             case A:
2021                                 int activeAPos = findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker("a");
2022                                 if (activeAPos != -1) {
2023                                     errFooSeenWhenFooOpen(name);
2024                                     StackNode<T> activeA = listOfActiveFormattingElements[activeAPos];
2025                                     activeA.retain();
2026                                     adoptionAgencyEndTag("a");
2027                                     removeFromStack(activeA);
2028                                     activeAPos = findInListOfActiveFormattingElements(activeA);
2029                                     if (activeAPos != -1) {
2030                                         removeFromListOfActiveFormattingElements(activeAPos);
2031                                     }
2032                                     activeA.release(this);
2033                                 }
2034                                 reconstructTheActiveFormattingElements();
2035                                 appendToCurrentNodeAndPushFormattingElementMayFoster(
2036                                         elementName,
2037                                         attributes);
2038                                 attributes = null; // CPP
2039                                 break starttagloop;
2040                             case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
2041                             case FONT:
2042                                 reconstructTheActiveFormattingElements();
2043                                 maybeForgetEarlierDuplicateFormattingElement(elementName.getName(), attributes);
2044                                 appendToCurrentNodeAndPushFormattingElementMayFoster(
2045                                         elementName,
2046                                         attributes);
2047                                 attributes = null; // CPP
2048                                 break starttagloop;
2049                             case NOBR:
2050                                 reconstructTheActiveFormattingElements();
2051                                 if (TreeBuilder.NOT_FOUND_ON_STACK != findLastInScope("nobr")) {
2052                                     errFooSeenWhenFooOpen(name);
2053                                     adoptionAgencyEndTag("nobr");
2054                                     reconstructTheActiveFormattingElements();
2055                                 }
2056                                 appendToCurrentNodeAndPushFormattingElementMayFoster(
2057                                         elementName,
2058                                         attributes);
2059                                 attributes = null; // CPP
2060                                 break starttagloop;
2061                             case BUTTON:
2062                                 eltPos = findLastInScope(name);
2063                                 if (eltPos != TreeBuilder.NOT_FOUND_ON_STACK) {
2064                                     errFooSeenWhenFooOpen(name);
2065                                     generateImpliedEndTags();
2066                                     if (errorHandler != null
2067                                             && !isCurrent(name)) {
2068                                         errUnclosedElementsImplied(eltPos, name);
2069                                     }
2070                                     while (currentPtr >= eltPos) {
2071                                         pop();
2072                                     }
2073                                     continue starttagloop;
2074                                 } else {
2075                                     reconstructTheActiveFormattingElements();
2076                                     appendToCurrentNodeAndPushElementMayFoster(
2077                                             elementName,
2078                                             attributes, formPointer);
2079                                     attributes = null; // CPP
2080                                     break starttagloop;
2081                                 }
2082                             case OBJECT:
2083                                 reconstructTheActiveFormattingElements();
2084                                 appendToCurrentNodeAndPushElementMayFoster(
2085                                         elementName,
2086                                         attributes, formPointer);
2087                                 insertMarker();
2088                                 attributes = null; // CPP
2089                                 break starttagloop;
2090                             case MARQUEE_OR_APPLET:
2091                                 reconstructTheActiveFormattingElements();
2092                                 appendToCurrentNodeAndPushElementMayFoster(
2093                                         elementName,
2094                                         attributes);
2095                                 insertMarker();
2096                                 attributes = null; // CPP
2097                                 break starttagloop;
2098                             case TABLE:
2099                                 // The only quirk. Blame Hixie and
2100                                 // Acid2.
2101                                 if (!quirks) {
2102                                     implicitlyCloseP();
2103                                 }
2104                                 appendToCurrentNodeAndPushElementMayFoster(
2105                                         elementName,
2106                                         attributes);
2107                                 mode = IN_TABLE;
2108                                 attributes = null; // CPP
2109                                 break starttagloop;
2110                             case BR:
2111                             case EMBED:
2112                             case AREA_OR_WBR:
2113                             case KEYGEN:
2114                                 reconstructTheActiveFormattingElements();
2115                                 // FALL THROUGH to PARAM_OR_SOURCE_OR_TRACK
2116                                 // CPPONLY: MOZ_FALLTHROUGH;
2117                             // CPPONLY: case MENUITEM:
2118                             case PARAM_OR_SOURCE_OR_TRACK:
2119                                 appendVoidElementToCurrentMayFoster(
2120                                         elementName,
2121                                         attributes);
2122                                 selfClosing = false;
2123                                 attributes = null; // CPP
2124                                 break starttagloop;
2125                             case HR:
2126                                 implicitlyCloseP();
2127                                 appendVoidElementToCurrentMayFoster(
2128                                         elementName,
2129                                         attributes);
2130                                 selfClosing = false;
2131                                 attributes = null; // CPP
2132                                 break starttagloop;
2133                             case IMAGE:
2134                                 errImage();
2135                                 elementName = ElementName.IMG;
2136                                 continue starttagloop;
2137                             case IMG:
2138                             case INPUT:
2139                                 reconstructTheActiveFormattingElements();
2140                                 appendVoidElementToCurrentMayFoster(
2141                                         elementName, attributes,
2142                                         formPointer);
2143                                 selfClosing = false;
2144                                 attributes = null; // CPP
2145                                 break starttagloop;
2146                             case TEXTAREA:
2147                                 appendToCurrentNodeAndPushElementMayFoster(
2148                                         elementName,
2149                                         attributes, formPointer);
2150                                 tokenizer.setStateAndEndTagExpectation(
2151                                         Tokenizer.RCDATA, elementName);
2152                                 originalMode = mode;
2153                                 mode = TEXT;
2154                                 needToDropLF = true;
2155                                 attributes = null; // CPP
2156                                 break starttagloop;
2157                             case XMP:
2158                                 implicitlyCloseP();
2159                                 reconstructTheActiveFormattingElements();
2160                                 appendToCurrentNodeAndPushElementMayFoster(
2161                                         elementName,
2162                                         attributes);
2163                                 originalMode = mode;
2164                                 mode = TEXT;
2165                                 tokenizer.setStateAndEndTagExpectation(
2166                                         Tokenizer.RAWTEXT, elementName);
2167                                 attributes = null; // CPP
2168                                 break starttagloop;
2169                             case NOSCRIPT:
2170                                 if (!scriptingEnabled) {
2171                                     reconstructTheActiveFormattingElements();
2172                                     appendToCurrentNodeAndPushElementMayFoster(
2173                                             elementName,
2174                                             attributes);
2175                                     attributes = null; // CPP
2176                                     break starttagloop;
2177                                 }
2178                                 // CPPONLY: MOZ_FALLTHROUGH;
2179                             case NOFRAMES:
2180                             case IFRAME:
2181                             case NOEMBED:
2182                                 startTagGenericRawText(elementName, attributes);
2183                                 attributes = null; // CPP
2184                                 break starttagloop;
2185                             case SELECT:
2186                                 reconstructTheActiveFormattingElements();
2187                                 appendToCurrentNodeAndPushElementMayFoster(
2188                                         elementName,
2189                                         attributes, formPointer);
2190                                 switch (mode) {
2191                                     case IN_TABLE:
2192                                     case IN_CAPTION:
2193                                     case IN_COLUMN_GROUP:
2194                                     case IN_TABLE_BODY:
2195                                     case IN_ROW:
2196                                     case IN_CELL:
2197                                         mode = IN_SELECT_IN_TABLE;
2198                                         break;
2199                                     default:
2200                                         mode = IN_SELECT;
2201                                         break;
2202                                 }
2203                                 attributes = null; // CPP
2204                                 break starttagloop;
2205                             case OPTGROUP:
2206                             case OPTION:
2207                                 if (isCurrent("option")) {
2208                                     pop();
2209                                 }
2210                                 reconstructTheActiveFormattingElements();
2211                                 appendToCurrentNodeAndPushElementMayFoster(
2212                                         elementName,
2213                                         attributes);
2214                                 attributes = null; // CPP
2215                                 break starttagloop;
2216                             case RB_OR_RTC:
2217                                 eltPos = findLastInScope("ruby");
2218                                 if (eltPos != NOT_FOUND_ON_STACK) {
2219                                     generateImpliedEndTags();
2220                                 }
2221                                 if (eltPos != currentPtr) {
2222                                     if (eltPos == NOT_FOUND_ON_STACK) {
2223                                         errStartTagSeenWithoutRuby(name);
2224                                     } else {
2225                                         errUnclosedChildrenInRuby();
2226                                     }
2227                                 }
2228                                 appendToCurrentNodeAndPushElementMayFoster(
2229                                         elementName,
2230                                         attributes);
2231                                 attributes = null; // CPP
2232                                 break starttagloop;
2233                             case RT_OR_RP:
2234                                 eltPos = findLastInScope("ruby");
2235                                 if (eltPos != NOT_FOUND_ON_STACK) {
2236                                     generateImpliedEndTagsExceptFor("rtc");
2237                                 }
2238                                 if (eltPos != currentPtr) {
2239                                     if (!isCurrent("rtc")) {
2240                                         if (eltPos == NOT_FOUND_ON_STACK) {
2241                                             errStartTagSeenWithoutRuby(name);
2242                                         } else {
2243                                             errUnclosedChildrenInRuby();
2244                                         }
2245                                     }
2246                                 }
2247                                 appendToCurrentNodeAndPushElementMayFoster(
2248                                         elementName,
2249                                         attributes);
2250                                 attributes = null; // CPP
2251                                 break starttagloop;
2252                             case MATH:
2253                                 reconstructTheActiveFormattingElements();
2254                                 attributes.adjustForMath();
2255                                 if (selfClosing) {
2256                                     appendVoidElementToCurrentMayFosterMathML(
2257                                             elementName, attributes);
2258                                     selfClosing = false;
2259                                 } else {
2260                                     appendToCurrentNodeAndPushElementMayFosterMathML(
2261                                             elementName, attributes);
2262                                 }
2263                                 attributes = null; // CPP
2264                                 break starttagloop;
2265                             case SVG:
2266                                 reconstructTheActiveFormattingElements();
2267                                 attributes.adjustForSvg();
2268                                 if (selfClosing) {
2269                                     appendVoidElementToCurrentMayFosterSVG(
2270                                             elementName,
2271                                             attributes);
2272                                     selfClosing = false;
2273                                 } else {
2274                                     appendToCurrentNodeAndPushElementMayFosterSVG(
2275                                             elementName, attributes);
2276                                 }
2277                                 attributes = null; // CPP
2278                                 break starttagloop;
2279                             case CAPTION:
2280                             case COL:
2281                             case COLGROUP:
2282                             case TBODY_OR_THEAD_OR_TFOOT:
2283                             case TR:
2284                             case TD_OR_TH:
2285                             case FRAME:
2286                             case FRAMESET:
2287                             case HEAD:
2288                                 errStrayStartTag(name);
2289                                 break starttagloop;
2290                             case OUTPUT:
2291                                 reconstructTheActiveFormattingElements();
2292                                 appendToCurrentNodeAndPushElementMayFoster(
2293                                         elementName,
2294                                         attributes, formPointer);
2295                                 attributes = null; // CPP
2296                                 break starttagloop;
2297                             default:
2298                                 reconstructTheActiveFormattingElements();
2299                                 appendToCurrentNodeAndPushElementMayFoster(
2300                                         elementName,
2301                                         attributes);
2302                                 attributes = null; // CPP
2303                                 break starttagloop;
2304                         }
2305                     }
2306                     // CPPONLY: MOZ_FALLTHROUGH;
2307                 case IN_HEAD:
2308                     inheadloop: for (;;) {
2309                         switch (group) {
2310                             case HTML:
2311                                 errStrayStartTag(name);
2312                                 if (!fragment && !isTemplateContents()) {
2313                                     addAttributesToHtml(attributes);
2314                                     attributes = null; // CPP
2315                                 }
2316                                 break starttagloop;
2317                             case BASE:
2318                             case LINK_OR_BASEFONT_OR_BGSOUND:
2319                                 appendVoidElementToCurrentMayFoster(
2320                                         elementName,
2321                                         attributes);
2322                                 selfClosing = false;
2323                                 attributes = null; // CPP
2324                                 break starttagloop;
2325                             case META:
2326                                 // Fall through to IN_HEAD_NOSCRIPT
2327                                 break inheadloop;
2328                             case TITLE:
2329                                 startTagTitleInHead(elementName, attributes);
2330                                 attributes = null; // CPP
2331                                 break starttagloop;
2332                             case NOSCRIPT:
2333                                 if (scriptingEnabled) {
2334                                     appendToCurrentNodeAndPushElement(
2335                                             elementName,
2336                                             attributes);
2337                                     originalMode = mode;
2338                                     mode = TEXT;
2339                                     tokenizer.setStateAndEndTagExpectation(
2340                                             Tokenizer.RAWTEXT, elementName);
2341                                 } else {
2342                                     appendToCurrentNodeAndPushElementMayFoster(
2343                                             elementName,
2344                                             attributes);
2345                                     mode = IN_HEAD_NOSCRIPT;
2346                                 }
2347                                 attributes = null; // CPP
2348                                 break starttagloop;
2349                             case SCRIPT:
2350                                 startTagScriptInHead(elementName, attributes);
2351                                 attributes = null; // CPP
2352                                 break starttagloop;
2353                             case STYLE:
2354                             case NOFRAMES:
2355                                 startTagGenericRawText(elementName, attributes);
2356                                 attributes = null; // CPP
2357                                 break starttagloop;
2358                             case HEAD:
2359                                 /* Parse error. */
2360                                 errFooSeenWhenFooOpen(name);
2361                                 /* Ignore the token. */
2362                                 break starttagloop;
2363                             case TEMPLATE:
2364                                 startTagTemplateInHead(elementName, attributes);
2365                                 attributes = null; // CPP
2366                                 break starttagloop;
2367                             default:
2368                                 pop();
2369                                 mode = AFTER_HEAD;
2370                                 continue starttagloop;
2371                         }
2372                     }
2373                     // CPPONLY: MOZ_FALLTHROUGH;
2374                 case IN_HEAD_NOSCRIPT:
2375                     switch (group) {
2376                         case HTML:
2377                             // XXX did Hixie really mean to omit "base"
2378                             // here?
2379                             errStrayStartTag(name);
2380                             if (!fragment && !isTemplateContents()) {
2381                                 addAttributesToHtml(attributes);
2382                                 attributes = null; // CPP
2383                             }
2384                             break starttagloop;
2385                         case LINK_OR_BASEFONT_OR_BGSOUND:
2386                             appendVoidElementToCurrentMayFoster(
2387                                     elementName,
2388                                     attributes);
2389                             selfClosing = false;
2390                             attributes = null; // CPP
2391                             break starttagloop;
2392                         case META:
2393                             checkMetaCharset(attributes);
2394                             appendVoidElementToCurrentMayFoster(
2395                                     elementName,
2396                                     attributes);
2397                             selfClosing = false;
2398                             attributes = null; // CPP
2399                             break starttagloop;
2400                         case STYLE:
2401                         case NOFRAMES:
2402                             appendToCurrentNodeAndPushElement(
2403                                     elementName,
2404                                     attributes);
2405                             originalMode = mode;
2406                             mode = TEXT;
2407                             tokenizer.setStateAndEndTagExpectation(
2408                                     Tokenizer.RAWTEXT, elementName);
2409                             attributes = null; // CPP
2410                             break starttagloop;
2411                         case HEAD:
2412                             errFooSeenWhenFooOpen(name);
2413                             break starttagloop;
2414                         case NOSCRIPT:
2415                             errFooSeenWhenFooOpen(name);
2416                             break starttagloop;
2417                         default:
2418                             errBadStartTagInNoscriptInHead(name);
2419                             pop();
2420                             mode = IN_HEAD;
2421                             continue;
2422                     }
2423                 case IN_COLUMN_GROUP:
2424                     switch (group) {
2425                         case HTML:
2426                             errStrayStartTag(name);
2427                             if (!fragment && !isTemplateContents()) {
2428                                 addAttributesToHtml(attributes);
2429                                 attributes = null; // CPP
2430                             }
2431                             break starttagloop;
2432                         case COL:
2433                             appendVoidElementToCurrentMayFoster(
2434                                     elementName,
2435                                     attributes);
2436                             selfClosing = false;
2437                             attributes = null; // CPP
2438                             break starttagloop;
2439                         case TEMPLATE:
2440                             startTagTemplateInHead(elementName, attributes);
2441                             attributes = null; // CPP
2442                             break starttagloop;
2443                         default:
2444                             if (currentPtr == 0 || stack[currentPtr].getGroup() == TEMPLATE) {
2445                                 assert fragment || isTemplateContents();
2446                                 errGarbageInColgroup();
2447                                 break starttagloop;
2448                             }
2449                             pop();
2450                             mode = IN_TABLE;
2451                             continue;
2452                     }
2453                 case IN_SELECT_IN_TABLE:
2454                     switch (group) {
2455                         case CAPTION:
2456                         case TBODY_OR_THEAD_OR_TFOOT:
2457                         case TR:
2458                         case TD_OR_TH:
2459                         case TABLE:
2460                             errStartTagWithSelectOpen(name);
2461                             eltPos = findLastInTableScope("select");
2462                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2463                                 assert fragment;
2464                                 break starttagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375
2465                             }
2466                             while (currentPtr >= eltPos) {
2467                                 pop();
2468                             }
2469                             resetTheInsertionMode();
2470                             continue;
2471                         default:
2472                             // fall through to IN_SELECT
2473                     }
2474                     // CPPONLY: MOZ_FALLTHROUGH;
2475                 case IN_SELECT:
2476                     switch (group) {
2477                         case HTML:
2478                             errStrayStartTag(name);
2479                             if (!fragment) {
2480                                 addAttributesToHtml(attributes);
2481                                 attributes = null; // CPP
2482                             }
2483                             break starttagloop;
2484                         case OPTION:
2485                             if (isCurrent("option")) {
2486                                 pop();
2487                             }
2488                             appendToCurrentNodeAndPushElement(
2489                                     elementName,
2490                                     attributes);
2491                             attributes = null; // CPP
2492                             break starttagloop;
2493                         case OPTGROUP:
2494                             if (isCurrent("option")) {
2495                                 pop();
2496                             }
2497                             if (isCurrent("optgroup")) {
2498                                 pop();
2499                             }
2500                             appendToCurrentNodeAndPushElement(
2501                                     elementName,
2502                                     attributes);
2503                             attributes = null; // CPP
2504                             break starttagloop;
2505                         case SELECT:
2506                             errStartSelectWhereEndSelectExpected();
2507                             eltPos = findLastInTableScope(name);
2508                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2509                                 assert fragment;
2510                                 errNoSelectInTableScope();
2511                                 break starttagloop;
2512                             } else {
2513                                 while (currentPtr >= eltPos) {
2514                                     pop();
2515                                 }
2516                                 resetTheInsertionMode();
2517                                 break starttagloop;
2518                             }
2519                         case INPUT:
2520                         case TEXTAREA:
2521                             errStartTagWithSelectOpen(name);
2522                             eltPos = findLastInTableScope("select");
2523                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2524                                 assert fragment;
2525                                 break starttagloop;
2526                             }
2527                             while (currentPtr >= eltPos) {
2528                                 pop();
2529                             }
2530                             resetTheInsertionMode();
2531                             continue;
2532                         case SCRIPT:
2533                             startTagScriptInHead(elementName, attributes);
2534                             attributes = null; // CPP
2535                             break starttagloop;
2536                         case TEMPLATE:
2537                             startTagTemplateInHead(elementName, attributes);
2538                             attributes = null; // CPP
2539                             break starttagloop;
2540                         default:
2541                             errStrayStartTag(name);
2542                             break starttagloop;
2543                     }
2544                 case AFTER_BODY:
2545                     switch (group) {
2546                         case HTML:
2547                             errStrayStartTag(name);
2548                             if (!fragment && !isTemplateContents()) {
2549                                 addAttributesToHtml(attributes);
2550                                 attributes = null; // CPP
2551                             }
2552                             break starttagloop;
2553                         default:
2554                             errStrayStartTag(name);
2555                             mode = framesetOk ? FRAMESET_OK : IN_BODY;
2556                             continue;
2557                     }
2558                 case IN_FRAMESET:
2559                     switch (group) {
2560                         case FRAMESET:
2561                             appendToCurrentNodeAndPushElement(
2562                                     elementName,
2563                                     attributes);
2564                             attributes = null; // CPP
2565                             break starttagloop;
2566                         case FRAME:
2567                             appendVoidElementToCurrentMayFoster(
2568                                     elementName,
2569                                     attributes);
2570                             selfClosing = false;
2571                             attributes = null; // CPP
2572                             break starttagloop;
2573                         default:
2574                             // fall through to AFTER_FRAMESET
2575                     }
2576                     // CPPONLY: MOZ_FALLTHROUGH;
2577                 case AFTER_FRAMESET:
2578                     switch (group) {
2579                         case HTML:
2580                             errStrayStartTag(name);
2581                             if (!fragment && !isTemplateContents()) {
2582                                 addAttributesToHtml(attributes);
2583                                 attributes = null; // CPP
2584                             }
2585                             break starttagloop;
2586                         case NOFRAMES:
2587                             appendToCurrentNodeAndPushElement(
2588                                     elementName,
2589                                     attributes);
2590                             originalMode = mode;
2591                             mode = TEXT;
2592                             tokenizer.setStateAndEndTagExpectation(
2593                                     Tokenizer.RAWTEXT, elementName);
2594                             attributes = null; // CPP
2595                             break starttagloop;
2596                         default:
2597                             errStrayStartTag(name);
2598                             break starttagloop;
2599                     }
2600                 case INITIAL:
2601                     /*
2602                      * Parse error.
2603                      */
2604                     errStartTagWithoutDoctype();
2605                     /*
2606                      *
2607                      * Set the document to quirks mode.
2608                      */
2609                     documentModeInternal(DocumentMode.QUIRKS_MODE, null, null);
2610                     /*
2611                      * Then, switch to the root element mode of the tree
2612                      * construction stage
2613                      */
2614                     mode = BEFORE_HTML;
2615                     /*
2616                      * and reprocess the current token.
2617                      */
2618                     continue;
2619                 case BEFORE_HTML:
2620                     switch (group) {
2621                         case HTML:
2622                             // optimize error check and streaming SAX by
2623                             // hoisting
2624                             // "html" handling here.
2625                             if (attributes == HtmlAttributes.EMPTY_ATTRIBUTES) {
2626                                 // This has the right magic side effect
2627                                 // that
2628                                 // it
2629                                 // makes attributes in SAX Tree mutable.
2630                                 appendHtmlElementToDocumentAndPush();
2631                             } else {
2632                                 appendHtmlElementToDocumentAndPush(attributes);
2633                             }
2634                             // XXX application cache should fire here
2635                             mode = BEFORE_HEAD;
2636                             attributes = null; // CPP
2637                             break starttagloop;
2638                         default:
2639                             /*
2640                              * Create an HTMLElement node with the tag name
2641                              * html, in the HTML namespace. Append it to the
2642                              * Document object.
2643                              */
2644                             appendHtmlElementToDocumentAndPush();
2645                             /* Switch to the main mode */
2646                             mode = BEFORE_HEAD;
2647                             /*
2648                              * reprocess the current token.
2649                              */
2650                             continue;
2651                     }
2652                 case BEFORE_HEAD:
2653                     switch (group) {
2654                         case HTML:
2655                             errStrayStartTag(name);
2656                             if (!fragment && !isTemplateContents()) {
2657                                 addAttributesToHtml(attributes);
2658                                 attributes = null; // CPP
2659                             }
2660                             break starttagloop;
2661                         case HEAD:
2662                             /*
2663                              * A start tag whose tag name is "head"
2664                              *
2665                              * Create an element for the token.
2666                              *
2667                              * Set the head element pointer to this new element
2668                              * node.
2669                              *
2670                              * Append the new element to the current node and
2671                              * push it onto the stack of open elements.
2672                              */
2673                             appendToCurrentNodeAndPushHeadElement(attributes);
2674                             /*
2675                              * Change the insertion mode to "in head".
2676                              */
2677                             mode = IN_HEAD;
2678                             attributes = null; // CPP
2679                             break starttagloop;
2680                         default:
2681                             /*
2682                              * Any other start tag token
2683                              *
2684                              * Act as if a start tag token with the tag name
2685                              * "head" and no attributes had been seen,
2686                              */
2687                             appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
2688                             mode = IN_HEAD;
2689                             /*
2690                              * then reprocess the current token.
2691                              *
2692                              * This will result in an empty head element being
2693                              * generated, with the current token being
2694                              * reprocessed in the "after head" insertion mode.
2695                              */
2696                             continue;
2697                     }
2698                 case AFTER_HEAD:
2699                     switch (group) {
2700                         case HTML:
2701                             errStrayStartTag(name);
2702                             if (!fragment && !isTemplateContents()) {
2703                                 addAttributesToHtml(attributes);
2704                                 attributes = null; // CPP
2705                             }
2706                             break starttagloop;
2707                         case BODY:
2708                             if (attributes.getLength() == 0) {
2709                                 // This has the right magic side effect
2710                                 // that
2711                                 // it
2712                                 // makes attributes in SAX Tree mutable.
2713                                 appendToCurrentNodeAndPushBodyElement();
2714                             } else {
2715                                 appendToCurrentNodeAndPushBodyElement(attributes);
2716                             }
2717                             framesetOk = false;
2718                             mode = IN_BODY;
2719                             attributes = null; // CPP
2720                             break starttagloop;
2721                         case FRAMESET:
2722                             appendToCurrentNodeAndPushElement(
2723                                     elementName,
2724                                     attributes);
2725                             mode = IN_FRAMESET;
2726                             attributes = null; // CPP
2727                             break starttagloop;
2728                         case TEMPLATE:
2729                             errFooBetweenHeadAndBody(name);
2730                             pushHeadPointerOntoStack();
2731                             StackNode<T> headOnStack = stack[currentPtr];
2732                             startTagTemplateInHead(elementName, attributes);
2733                             removeFromStack(headOnStack);
2734                             attributes = null; // CPP
2735                             break starttagloop;
2736                         case BASE:
2737                         case LINK_OR_BASEFONT_OR_BGSOUND:
2738                             errFooBetweenHeadAndBody(name);
2739                             pushHeadPointerOntoStack();
2740                             appendVoidElementToCurrentMayFoster(
2741                                     elementName,
2742                                     attributes);
2743                             selfClosing = false;
2744                             pop(); // head
2745                             attributes = null; // CPP
2746                             break starttagloop;
2747                         case META:
2748                             errFooBetweenHeadAndBody(name);
2749                             checkMetaCharset(attributes);
2750                             pushHeadPointerOntoStack();
2751                             appendVoidElementToCurrentMayFoster(
2752                                     elementName,
2753                                     attributes);
2754                             selfClosing = false;
2755                             pop(); // head
2756                             attributes = null; // CPP
2757                             break starttagloop;
2758                         case SCRIPT:
2759                             errFooBetweenHeadAndBody(name);
2760                             pushHeadPointerOntoStack();
2761                             appendToCurrentNodeAndPushElement(
2762                                     elementName,
2763                                     attributes);
2764                             originalMode = mode;
2765                             mode = TEXT;
2766                             tokenizer.setStateAndEndTagExpectation(
2767                                     Tokenizer.SCRIPT_DATA, elementName);
2768                             attributes = null; // CPP
2769                             break starttagloop;
2770                         case STYLE:
2771                         case NOFRAMES:
2772                             errFooBetweenHeadAndBody(name);
2773                             pushHeadPointerOntoStack();
2774                             appendToCurrentNodeAndPushElement(
2775                                     elementName,
2776                                     attributes);
2777                             originalMode = mode;
2778                             mode = TEXT;
2779                             tokenizer.setStateAndEndTagExpectation(
2780                                     Tokenizer.RAWTEXT, elementName);
2781                             attributes = null; // CPP
2782                             break starttagloop;
2783                         case TITLE:
2784                             errFooBetweenHeadAndBody(name);
2785                             pushHeadPointerOntoStack();
2786                             appendToCurrentNodeAndPushElement(
2787                                     elementName,
2788                                     attributes);
2789                             originalMode = mode;
2790                             mode = TEXT;
2791                             tokenizer.setStateAndEndTagExpectation(
2792                                     Tokenizer.RCDATA, elementName);
2793                             attributes = null; // CPP
2794                             break starttagloop;
2795                         case HEAD:
2796                             errStrayStartTag(name);
2797                             break starttagloop;
2798                         default:
2799                             appendToCurrentNodeAndPushBodyElement();
2800                             mode = FRAMESET_OK;
2801                             continue;
2802                     }
2803                 case AFTER_AFTER_BODY:
2804                     switch (group) {
2805                         case HTML:
2806                             errStrayStartTag(name);
2807                             if (!fragment && !isTemplateContents()) {
2808                                 addAttributesToHtml(attributes);
2809                                 attributes = null; // CPP
2810                             }
2811                             break starttagloop;
2812                         default:
2813                             errStrayStartTag(name);
2814                             fatal();
2815                             mode = framesetOk ? FRAMESET_OK : IN_BODY;
2816                             continue;
2817                     }
2818                 case AFTER_AFTER_FRAMESET:
2819                     switch (group) {
2820                         case HTML:
2821                             errStrayStartTag(name);
2822                             if (!fragment && !isTemplateContents()) {
2823                                 addAttributesToHtml(attributes);
2824                                 attributes = null; // CPP
2825                             }
2826                             break starttagloop;
2827                         case NOFRAMES:
2828                             startTagGenericRawText(elementName, attributes);
2829                             attributes = null; // CPP
2830                             break starttagloop;
2831                         default:
2832                             errStrayStartTag(name);
2833                             break starttagloop;
2834                     }
2835                 case TEXT:
2836                     assert false;
2837                     break starttagloop; // Avoid infinite loop if the assertion
2838                                         // fails
2839             }
2840         }
2841         if (selfClosing) {
2842             errSelfClosing();
2843         }
2844         // CPPONLY: if (mBuilder == null && attributes != HtmlAttributes.EMPTY_ATTRIBUTES) {
2845         // CPPONLY:    Portability.delete(attributes);
2846         // CPPONLY: }
2847     }
2848 
startTagTitleInHead(ElementName elementName, HtmlAttributes attributes)2849     private void startTagTitleInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2850         appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
2851         originalMode = mode;
2852         mode = TEXT;
2853         tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA, elementName);
2854     }
2855 
startTagGenericRawText(ElementName elementName, HtmlAttributes attributes)2856     private void startTagGenericRawText(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2857         appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
2858         originalMode = mode;
2859         mode = TEXT;
2860         tokenizer.setStateAndEndTagExpectation(Tokenizer.RAWTEXT, elementName);
2861     }
2862 
startTagScriptInHead(ElementName elementName, HtmlAttributes attributes)2863     private void startTagScriptInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2864         // XXX need to manage much more stuff here if supporting document.write()
2865         appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
2866         originalMode = mode;
2867         mode = TEXT;
2868         tokenizer.setStateAndEndTagExpectation(Tokenizer.SCRIPT_DATA, elementName);
2869     }
2870 
startTagTemplateInHead(ElementName elementName, HtmlAttributes attributes)2871     private void startTagTemplateInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2872         appendToCurrentNodeAndPushElement(elementName, attributes);
2873         insertMarker();
2874         framesetOk = false;
2875         originalMode = mode;
2876         mode = IN_TEMPLATE;
2877         pushTemplateMode(IN_TEMPLATE);
2878     }
2879 
isTemplateContents()2880     private boolean isTemplateContents() {
2881         return TreeBuilder.NOT_FOUND_ON_STACK != findLast("template");
2882     }
2883 
isTemplateModeStackEmpty()2884     private boolean isTemplateModeStackEmpty() {
2885         return templateModePtr == -1;
2886     }
2887 
isSpecialParentInForeign(StackNode<T> stackNode)2888     private boolean isSpecialParentInForeign(StackNode<T> stackNode) {
2889         @NsUri String ns = stackNode.ns;
2890         return ("http://www.w3.org/1999/xhtml" == ns)
2891                 || (stackNode.isHtmlIntegrationPoint())
2892                 || (("http://www.w3.org/1998/Math/MathML" == ns) && (stackNode.getGroup() == MI_MO_MN_MS_MTEXT));
2893     }
2894 
2895     /**
2896      *
2897      * <p>
2898      * C++ memory note: The return value must be released.
2899      *
2900      * @return
2901      * @throws SAXException
2902      * @throws StopSniffingException
2903      */
extractCharsetFromContent(String attributeValue )2904     public static String extractCharsetFromContent(String attributeValue
2905         // CPPONLY: , TreeBuilder tb
2906     ) {
2907         // This is a bit ugly. Converting the string to char array in order to
2908         // make the portability layer smaller.
2909         int charsetState = CHARSET_INITIAL;
2910         int start = -1;
2911         int end = -1;
2912         @Auto char[] buffer = Portability.newCharArrayFromString(attributeValue);
2913 
2914         charsetloop: for (int i = 0; i < buffer.length; i++) {
2915             char c = buffer[i];
2916             switch (charsetState) {
2917                 case CHARSET_INITIAL:
2918                     switch (c) {
2919                         case 'c':
2920                         case 'C':
2921                             charsetState = CHARSET_C;
2922                             continue;
2923                         default:
2924                             continue;
2925                     }
2926                 case CHARSET_C:
2927                     switch (c) {
2928                         case 'h':
2929                         case 'H':
2930                             charsetState = CHARSET_H;
2931                             continue;
2932                         default:
2933                             charsetState = CHARSET_INITIAL;
2934                             continue;
2935                     }
2936                 case CHARSET_H:
2937                     switch (c) {
2938                         case 'a':
2939                         case 'A':
2940                             charsetState = CHARSET_A;
2941                             continue;
2942                         default:
2943                             charsetState = CHARSET_INITIAL;
2944                             continue;
2945                     }
2946                 case CHARSET_A:
2947                     switch (c) {
2948                         case 'r':
2949                         case 'R':
2950                             charsetState = CHARSET_R;
2951                             continue;
2952                         default:
2953                             charsetState = CHARSET_INITIAL;
2954                             continue;
2955                     }
2956                 case CHARSET_R:
2957                     switch (c) {
2958                         case 's':
2959                         case 'S':
2960                             charsetState = CHARSET_S;
2961                             continue;
2962                         default:
2963                             charsetState = CHARSET_INITIAL;
2964                             continue;
2965                     }
2966                 case CHARSET_S:
2967                     switch (c) {
2968                         case 'e':
2969                         case 'E':
2970                             charsetState = CHARSET_E;
2971                             continue;
2972                         default:
2973                             charsetState = CHARSET_INITIAL;
2974                             continue;
2975                     }
2976                 case CHARSET_E:
2977                     switch (c) {
2978                         case 't':
2979                         case 'T':
2980                             charsetState = CHARSET_T;
2981                             continue;
2982                         default:
2983                             charsetState = CHARSET_INITIAL;
2984                             continue;
2985                     }
2986                 case CHARSET_T:
2987                     switch (c) {
2988                         case '\t':
2989                         case '\n':
2990                         case '\u000C':
2991                         case '\r':
2992                         case ' ':
2993                             continue;
2994                         case '=':
2995                             charsetState = CHARSET_EQUALS;
2996                             continue;
2997                         default:
2998                             return null;
2999                     }
3000                 case CHARSET_EQUALS:
3001                     switch (c) {
3002                         case '\t':
3003                         case '\n':
3004                         case '\u000C':
3005                         case '\r':
3006                         case ' ':
3007                             continue;
3008                         case '\'':
3009                             start = i + 1;
3010                             charsetState = CHARSET_SINGLE_QUOTED;
3011                             continue;
3012                         case '\"':
3013                             start = i + 1;
3014                             charsetState = CHARSET_DOUBLE_QUOTED;
3015                             continue;
3016                         default:
3017                             start = i;
3018                             charsetState = CHARSET_UNQUOTED;
3019                             continue;
3020                     }
3021                 case CHARSET_SINGLE_QUOTED:
3022                     switch (c) {
3023                         case '\'':
3024                             end = i;
3025                             break charsetloop;
3026                         default:
3027                             continue;
3028                     }
3029                 case CHARSET_DOUBLE_QUOTED:
3030                     switch (c) {
3031                         case '\"':
3032                             end = i;
3033                             break charsetloop;
3034                         default:
3035                             continue;
3036                     }
3037                 case CHARSET_UNQUOTED:
3038                     switch (c) {
3039                         case '\t':
3040                         case '\n':
3041                         case '\u000C':
3042                         case '\r':
3043                         case ' ':
3044                         case ';':
3045                             end = i;
3046                             break charsetloop;
3047                         default:
3048                             continue;
3049                     }
3050             }
3051         }
3052         if (start != -1) {
3053             if (end == -1) {
3054                 if (charsetState == CHARSET_UNQUOTED) {
3055                     end = buffer.length;
3056                 } else {
3057                     return null;
3058                 }
3059             }
3060             return Portability.newStringFromBuffer(buffer, start, end
3061                     - start
3062                 // CPPONLY: , tb, false
3063             );
3064         }
3065         return null;
3066     }
3067 
checkMetaCharset(HtmlAttributes attributes)3068     private void checkMetaCharset(HtmlAttributes attributes)
3069             throws SAXException {
3070         String charset = attributes.getValue(AttributeName.CHARSET);
3071         if (charset != null) {
3072             if (tokenizer.internalEncodingDeclaration(charset)) {
3073                 requestSuspension();
3074                 return;
3075             }
3076             return;
3077         }
3078         if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
3079                 "content-type",
3080                 attributes.getValue(AttributeName.HTTP_EQUIV))) {
3081             return;
3082         }
3083         String content = attributes.getValue(AttributeName.CONTENT);
3084         if (content != null) {
3085             String extract = TreeBuilder.extractCharsetFromContent(content
3086                 // CPPONLY: , this
3087             );
3088             // remember not to return early without releasing the string
3089             if (extract != null) {
3090                 if (tokenizer.internalEncodingDeclaration(extract)) {
3091                     requestSuspension();
3092                 }
3093             }
3094             Portability.releaseString(extract);
3095         }
3096     }
3097 
endTag(ElementName elementName)3098     public final void endTag(ElementName elementName) throws SAXException {
3099         flushCharacters();
3100         needToDropLF = false;
3101         int eltPos;
3102         int group = elementName.getGroup();
3103         @Local String name = elementName.getName();
3104         endtagloop: for (;;) {
3105             if (isInForeign()) {
3106                 if (stack[currentPtr].name != name) {
3107                     if (currentPtr == 0) {
3108                         errStrayEndTag(name);
3109                     } else {
3110                         errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr].popName);
3111                     }
3112                 }
3113                 eltPos = currentPtr;
3114                 int origPos = currentPtr;
3115                 for (;;) {
3116                     if (eltPos == 0) {
3117                         assert fragment: "We can get this close to the root of the stack in foreign content only in the fragment case.";
3118                         break endtagloop;
3119                     }
3120                     if (stack[eltPos].name == name) {
3121                         while (currentPtr >= eltPos) {
3122                             popForeign(origPos, eltPos);
3123                         }
3124                         break endtagloop;
3125                     }
3126                     if (stack[--eltPos].ns == "http://www.w3.org/1999/xhtml") {
3127                         break;
3128                     }
3129                 }
3130             }
3131             switch (mode) {
3132                 case IN_TEMPLATE:
3133                     switch (group) {
3134                         case TEMPLATE:
3135                             // fall through to IN_HEAD
3136                             break;
3137                         default:
3138                             errStrayEndTag(name);
3139                             break endtagloop;
3140                     }
3141                     // CPPONLY: MOZ_FALLTHROUGH;
3142                 case IN_ROW:
3143                     switch (group) {
3144                         case TR:
3145                             eltPos = findLastOrRoot(TreeBuilder.TR);
3146                             if (eltPos == 0) {
3147                                 assert fragment || isTemplateContents();
3148                                 errNoTableRowToClose();
3149                                 break endtagloop;
3150                             }
3151                             clearStackBackTo(eltPos);
3152                             pop();
3153                             mode = IN_TABLE_BODY;
3154                             break endtagloop;
3155                         case TABLE:
3156                             eltPos = findLastOrRoot(TreeBuilder.TR);
3157                             if (eltPos == 0) {
3158                                 assert fragment || isTemplateContents();
3159                                 errNoTableRowToClose();
3160                                 break endtagloop;
3161                             }
3162                             clearStackBackTo(eltPos);
3163                             pop();
3164                             mode = IN_TABLE_BODY;
3165                             continue;
3166                         case TBODY_OR_THEAD_OR_TFOOT:
3167                             if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
3168                                 errStrayEndTag(name);
3169                                 break endtagloop;
3170                             }
3171                             eltPos = findLastOrRoot(TreeBuilder.TR);
3172                             if (eltPos == 0) {
3173                                 assert fragment || isTemplateContents();
3174                                 errNoTableRowToClose();
3175                                 break endtagloop;
3176                             }
3177                             clearStackBackTo(eltPos);
3178                             pop();
3179                             mode = IN_TABLE_BODY;
3180                             continue;
3181                         case BODY:
3182                         case CAPTION:
3183                         case COL:
3184                         case COLGROUP:
3185                         case HTML:
3186                         case TD_OR_TH:
3187                             errStrayEndTag(name);
3188                             break endtagloop;
3189                         default:
3190                             // fall through to IN_TABLE
3191                     }
3192                     // CPPONLY: MOZ_FALLTHROUGH;
3193                 case IN_TABLE_BODY:
3194                     switch (group) {
3195                         case TBODY_OR_THEAD_OR_TFOOT:
3196                             eltPos = findLastOrRoot(name);
3197                             if (eltPos == 0) {
3198                                 errStrayEndTag(name);
3199                                 break endtagloop;
3200                             }
3201                             clearStackBackTo(eltPos);
3202                             pop();
3203                             mode = IN_TABLE;
3204                             break endtagloop;
3205                         case TABLE:
3206                             eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot();
3207                             if (eltPos == 0 || stack[eltPos].getGroup() == TEMPLATE) {
3208                                 assert fragment || isTemplateContents();
3209                                 errStrayEndTag(name);
3210                                 break endtagloop;
3211                             }
3212                             clearStackBackTo(eltPos);
3213                             pop();
3214                             mode = IN_TABLE;
3215                             continue;
3216                         case BODY:
3217                         case CAPTION:
3218                         case COL:
3219                         case COLGROUP:
3220                         case HTML:
3221                         case TD_OR_TH:
3222                         case TR:
3223                             errStrayEndTag(name);
3224                             break endtagloop;
3225                         default:
3226                             // fall through to IN_TABLE
3227                     }
3228                     // CPPONLY: MOZ_FALLTHROUGH;
3229                 case IN_TABLE:
3230                     switch (group) {
3231                         case TABLE:
3232                             eltPos = findLast("table");
3233                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3234                                 assert fragment || isTemplateContents();
3235                                 errStrayEndTag(name);
3236                                 break endtagloop;
3237                             }
3238                             while (currentPtr >= eltPos) {
3239                                 pop();
3240                             }
3241                             resetTheInsertionMode();
3242                             break endtagloop;
3243                         case BODY:
3244                         case CAPTION:
3245                         case COL:
3246                         case COLGROUP:
3247                         case HTML:
3248                         case TBODY_OR_THEAD_OR_TFOOT:
3249                         case TD_OR_TH:
3250                         case TR:
3251                             errStrayEndTag(name);
3252                             break endtagloop;
3253                         case TEMPLATE:
3254                             // fall through to IN_HEAD
3255                             break;
3256                         default:
3257                             errStrayEndTag(name);
3258                             // fall through to IN_BODY
3259                     }
3260                     // CPPONLY: MOZ_FALLTHROUGH;
3261                 case IN_CAPTION:
3262                     switch (group) {
3263                         case CAPTION:
3264                             eltPos = findLastInTableScope("caption");
3265                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3266                                 break endtagloop;
3267                             }
3268                             generateImpliedEndTags();
3269                             if (errorHandler != null && currentPtr != eltPos) {
3270                                 errUnclosedElements(eltPos, name);
3271                             }
3272                             while (currentPtr >= eltPos) {
3273                                 pop();
3274                             }
3275                             clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3276                             mode = IN_TABLE;
3277                             break endtagloop;
3278                         case TABLE:
3279                             eltPos = findLastInTableScope("caption");
3280 
3281                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3282                                 assert fragment || isTemplateContents();
3283                                 errStrayEndTag(name);
3284                                 break endtagloop;
3285                             }
3286                             generateImpliedEndTags();
3287                             if (errorHandler != null && currentPtr != eltPos) {
3288                                 errUnclosedElements(eltPos, name);
3289                             }
3290                             while (currentPtr >= eltPos) {
3291                                 pop();
3292                             }
3293                             clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3294                             mode = IN_TABLE;
3295                             continue;
3296                         case BODY:
3297                         case COL:
3298                         case COLGROUP:
3299                         case HTML:
3300                         case TBODY_OR_THEAD_OR_TFOOT:
3301                         case TD_OR_TH:
3302                         case TR:
3303                             errStrayEndTag(name);
3304                             break endtagloop;
3305                         default:
3306                             // fall through to IN_BODY
3307                     }
3308                     // CPPONLY: MOZ_FALLTHROUGH;
3309                 case IN_CELL:
3310                     switch (group) {
3311                         case TD_OR_TH:
3312                             eltPos = findLastInTableScope(name);
3313                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3314                                 errStrayEndTag(name);
3315                                 break endtagloop;
3316                             }
3317                             generateImpliedEndTags();
3318                             if (errorHandler != null && !isCurrent(name)) {
3319                                 errUnclosedElements(eltPos, name);
3320                             }
3321                             while (currentPtr >= eltPos) {
3322                                 pop();
3323                             }
3324                             clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3325                             mode = IN_ROW;
3326                             break endtagloop;
3327                         case TABLE:
3328                         case TBODY_OR_THEAD_OR_TFOOT:
3329                         case TR:
3330                             if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
3331                                 assert name == "tbody" || name == "tfoot" || name == "thead" || fragment || isTemplateContents();
3332                                 errStrayEndTag(name);
3333                                 break endtagloop;
3334                             }
3335                             closeTheCell(findLastInTableScopeTdTh());
3336                             continue;
3337                         case BODY:
3338                         case CAPTION:
3339                         case COL:
3340                         case COLGROUP:
3341                         case HTML:
3342                             errStrayEndTag(name);
3343                             break endtagloop;
3344                         default:
3345                             // fall through to IN_BODY
3346                     }
3347                     // CPPONLY: MOZ_FALLTHROUGH;
3348                 case FRAMESET_OK:
3349                 case IN_BODY:
3350                     switch (group) {
3351                         case BODY:
3352                             if (!isSecondOnStackBody()) {
3353                                 assert fragment || isTemplateContents();
3354                                 errStrayEndTag(name);
3355                                 break endtagloop;
3356                             }
3357                             assert currentPtr >= 1;
3358                             if (errorHandler != null) {
3359                                 uncloseloop1: for (int i = 2; i <= currentPtr; i++) {
3360                                     switch (stack[i].getGroup()) {
3361                                         case DD_OR_DT:
3362                                         case LI:
3363                                         case OPTGROUP:
3364                                         case OPTION: // is this possible?
3365                                         case P:
3366                                         case RB_OR_RTC:
3367                                         case RT_OR_RP:
3368                                         case TD_OR_TH:
3369                                         case TBODY_OR_THEAD_OR_TFOOT:
3370                                             break;
3371                                         default:
3372                                             errEndWithUnclosedElements(name);
3373                                             break uncloseloop1;
3374                                     }
3375                                 }
3376                             }
3377                             mode = AFTER_BODY;
3378                             break endtagloop;
3379                         case HTML:
3380                             if (!isSecondOnStackBody()) {
3381                                 assert fragment || isTemplateContents();
3382                                 errStrayEndTag(name);
3383                                 break endtagloop;
3384                             }
3385                             if (errorHandler != null) {
3386                                 uncloseloop2: for (int i = 0; i <= currentPtr; i++) {
3387                                     switch (stack[i].getGroup()) {
3388                                         case DD_OR_DT:
3389                                         case LI:
3390                                         case P:
3391                                         case RB_OR_RTC:
3392                                         case RT_OR_RP:
3393                                         case TBODY_OR_THEAD_OR_TFOOT:
3394                                         case TD_OR_TH:
3395                                         case BODY:
3396                                         case HTML:
3397                                             break;
3398                                         default:
3399                                             errEndWithUnclosedElements(name);
3400                                             break uncloseloop2;
3401                                     }
3402                                 }
3403                             }
3404                             mode = AFTER_BODY;
3405                             continue;
3406                         case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
3407                         case UL_OR_OL_OR_DL:
3408                         case PRE_OR_LISTING:
3409                         case FIELDSET:
3410                         case BUTTON:
3411                         case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY:
3412                             eltPos = findLastInScope(name);
3413                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3414                                 errStrayEndTag(name);
3415                             } else {
3416                                 generateImpliedEndTags();
3417                                 if (errorHandler != null && !isCurrent(name)) {
3418                                     errUnclosedElements(eltPos, name);
3419                                 }
3420                                 while (currentPtr >= eltPos) {
3421                                     pop();
3422                                 }
3423                             }
3424                             break endtagloop;
3425                         case FORM:
3426                             if (!isTemplateContents()) {
3427                                 if (formPointer == null) {
3428                                     errStrayEndTag(name);
3429                                     break endtagloop;
3430                                 }
3431                                 formPointer = null;
3432                                 eltPos = findLastInScope(name);
3433                                 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3434                                     errStrayEndTag(name);
3435                                     break endtagloop;
3436                                 }
3437                                 generateImpliedEndTags();
3438                                 if (errorHandler != null && !isCurrent(name)) {
3439                                     errUnclosedElements(eltPos, name);
3440                                 }
3441                                 removeFromStack(eltPos);
3442                                 break endtagloop;
3443                             } else {
3444                                 eltPos = findLastInScope(name);
3445                                 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3446                                     errStrayEndTag(name);
3447                                     break endtagloop;
3448                                 }
3449                                 generateImpliedEndTags();
3450                                 if (errorHandler != null && !isCurrent(name)) {
3451                                     errUnclosedElements(eltPos, name);
3452                                 }
3453                                 while (currentPtr >= eltPos) {
3454                                     pop();
3455                                 }
3456                                 break endtagloop;
3457                             }
3458                         case P:
3459                             eltPos = findLastInButtonScope("p");
3460                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3461                                 errNoElementToCloseButEndTagSeen("p");
3462                                 // XXX Can the 'in foreign' case happen anymore?
3463                                 if (isInForeign()) {
3464                                     errHtmlStartTagInForeignContext(name);
3465                                     // Check for currentPtr for the fragment
3466                                     // case.
3467                                     while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
3468                                         pop();
3469                                     }
3470                                 }
3471                                 appendVoidElementToCurrentMayFoster(
3472                                         elementName,
3473                                         HtmlAttributes.EMPTY_ATTRIBUTES);
3474                                 break endtagloop;
3475                             }
3476                             generateImpliedEndTagsExceptFor("p");
3477                             assert eltPos != TreeBuilder.NOT_FOUND_ON_STACK;
3478                             if (errorHandler != null && eltPos != currentPtr) {
3479                                 errUnclosedElements(eltPos, name);
3480                             }
3481                             while (currentPtr >= eltPos) {
3482                                 pop();
3483                             }
3484                             break endtagloop;
3485                         case LI:
3486                             eltPos = findLastInListScope(name);
3487                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3488                                 errNoElementToCloseButEndTagSeen(name);
3489                             } else {
3490                                 generateImpliedEndTagsExceptFor(name);
3491                                 if (errorHandler != null
3492                                         && eltPos != currentPtr) {
3493                                     errUnclosedElements(eltPos, name);
3494                                 }
3495                                 while (currentPtr >= eltPos) {
3496                                     pop();
3497                                 }
3498                             }
3499                             break endtagloop;
3500                         case DD_OR_DT:
3501                             eltPos = findLastInScope(name);
3502                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3503                                 errNoElementToCloseButEndTagSeen(name);
3504                             } else {
3505                                 generateImpliedEndTagsExceptFor(name);
3506                                 if (errorHandler != null
3507                                         && eltPos != currentPtr) {
3508                                     errUnclosedElements(eltPos, name);
3509                                 }
3510                                 while (currentPtr >= eltPos) {
3511                                     pop();
3512                                 }
3513                             }
3514                             break endtagloop;
3515                         case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
3516                             eltPos = findLastInScopeHn();
3517                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3518                                 errStrayEndTag(name);
3519                             } else {
3520                                 generateImpliedEndTags();
3521                                 if (errorHandler != null && !isCurrent(name)) {
3522                                     errUnclosedElements(eltPos, name);
3523                                 }
3524                                 while (currentPtr >= eltPos) {
3525                                     pop();
3526                                 }
3527                             }
3528                             break endtagloop;
3529                         case OBJECT:
3530                         case MARQUEE_OR_APPLET:
3531                             eltPos = findLastInScope(name);
3532                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3533                                 errStrayEndTag(name);
3534                             } else {
3535                                 generateImpliedEndTags();
3536                                 if (errorHandler != null && !isCurrent(name)) {
3537                                     errUnclosedElements(eltPos, name);
3538                                 }
3539                                 while (currentPtr >= eltPos) {
3540                                     pop();
3541                                 }
3542                                 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3543                             }
3544                             break endtagloop;
3545                         case BR:
3546                             errEndTagBr();
3547                             if (isInForeign()) {
3548                                 // XXX can this happen anymore?
3549                                 errHtmlStartTagInForeignContext(name);
3550                                 // Check for currentPtr for the fragment
3551                                 // case.
3552                                 while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
3553                                     pop();
3554                                 }
3555                             }
3556                             reconstructTheActiveFormattingElements();
3557                             appendVoidElementToCurrentMayFoster(
3558                                     elementName,
3559                                     HtmlAttributes.EMPTY_ATTRIBUTES);
3560                             break endtagloop;
3561                         case TEMPLATE:
3562                             // fall through to IN_HEAD;
3563                             break;
3564                         case AREA_OR_WBR:
3565                         case KEYGEN: // XXX??
3566                         // CPPONLY: case MENUITEM:
3567                         case PARAM_OR_SOURCE_OR_TRACK:
3568                         case EMBED:
3569                         case IMG:
3570                         case IMAGE:
3571                         case INPUT:
3572                         case HR:
3573                         case IFRAME:
3574                         case NOEMBED: // XXX???
3575                         case NOFRAMES: // XXX??
3576                         case SELECT:
3577                         case TABLE:
3578                         case TEXTAREA: // XXX??
3579                             errStrayEndTag(name);
3580                             break endtagloop;
3581                         case NOSCRIPT:
3582                             if (scriptingEnabled) {
3583                                 errStrayEndTag(name);
3584                                 break endtagloop;
3585                             }
3586                             // CPPONLY: MOZ_FALLTHROUGH;
3587                         case A:
3588                         case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
3589                         case FONT:
3590                         case NOBR:
3591                             if (adoptionAgencyEndTag(name)) {
3592                                 break endtagloop;
3593                             }
3594                             // else handle like any other tag
3595                             // CPPONLY: MOZ_FALLTHROUGH;
3596                         default:
3597                             if (isCurrent(name)) {
3598                                 pop();
3599                                 break endtagloop;
3600                             }
3601 
3602                             eltPos = currentPtr;
3603                             for (;;) {
3604                                 StackNode<T> node = stack[eltPos];
3605                                 if (node.ns == "http://www.w3.org/1999/xhtml" && node.name == name) {
3606                                     generateImpliedEndTags();
3607                                     if (errorHandler != null
3608                                             && !isCurrent(name)) {
3609                                         errUnclosedElements(eltPos, name);
3610                                     }
3611                                     while (currentPtr >= eltPos) {
3612                                         pop();
3613                                     }
3614                                     break endtagloop;
3615                                 } else if (eltPos == 0 || node.isSpecial()) {
3616                                     errStrayEndTag(name);
3617                                     break endtagloop;
3618                                 }
3619                                 eltPos--;
3620                             }
3621                     }
3622                     // CPPONLY: MOZ_FALLTHROUGH;
3623                 case IN_HEAD:
3624                     switch (group) {
3625                         case HEAD:
3626                             pop();
3627                             mode = AFTER_HEAD;
3628                             break endtagloop;
3629                         case BR:
3630                         case HTML:
3631                         case BODY:
3632                             pop();
3633                             mode = AFTER_HEAD;
3634                             continue;
3635                         case TEMPLATE:
3636                             endTagTemplateInHead();
3637                             break endtagloop;
3638                         default:
3639                             errStrayEndTag(name);
3640                             break endtagloop;
3641                     }
3642                 case IN_HEAD_NOSCRIPT:
3643                     switch (group) {
3644                         case NOSCRIPT:
3645                             pop();
3646                             mode = IN_HEAD;
3647                             break endtagloop;
3648                         case BR:
3649                             errStrayEndTag(name);
3650                             pop();
3651                             mode = IN_HEAD;
3652                             continue;
3653                         default:
3654                             errStrayEndTag(name);
3655                             break endtagloop;
3656                     }
3657                 case IN_COLUMN_GROUP:
3658                     switch (group) {
3659                         case COLGROUP:
3660                             if (currentPtr == 0 || stack[currentPtr].getGroup() ==
3661                                     TreeBuilder.TEMPLATE) {
3662                                 assert fragment || isTemplateContents();
3663                                 errGarbageInColgroup();
3664                                 break endtagloop;
3665                             }
3666                             pop();
3667                             mode = IN_TABLE;
3668                             break endtagloop;
3669                         case COL:
3670                             errStrayEndTag(name);
3671                             break endtagloop;
3672                         case TEMPLATE:
3673                             endTagTemplateInHead();
3674                             break endtagloop;
3675                         default:
3676                             if (currentPtr == 0 || stack[currentPtr].getGroup() ==
3677                                     TreeBuilder.TEMPLATE) {
3678                                 assert fragment || isTemplateContents();
3679                                 errGarbageInColgroup();
3680                                 break endtagloop;
3681                             }
3682                             pop();
3683                             mode = IN_TABLE;
3684                             continue;
3685                     }
3686                 case IN_SELECT_IN_TABLE:
3687                     switch (group) {
3688                         case CAPTION:
3689                         case TABLE:
3690                         case TBODY_OR_THEAD_OR_TFOOT:
3691                         case TR:
3692                         case TD_OR_TH:
3693                             errEndTagSeenWithSelectOpen(name);
3694                             if (findLastInTableScope(name) != TreeBuilder.NOT_FOUND_ON_STACK) {
3695                                 eltPos = findLastInTableScope("select");
3696                                 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3697                                     assert fragment;
3698                                     break endtagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375
3699                                 }
3700                                 while (currentPtr >= eltPos) {
3701                                     pop();
3702                                 }
3703                                 resetTheInsertionMode();
3704                                 continue;
3705                             } else {
3706                                 break endtagloop;
3707                             }
3708                         default:
3709                             // fall through to IN_SELECT
3710                     }
3711                     // CPPONLY: MOZ_FALLTHROUGH;
3712                 case IN_SELECT:
3713                     switch (group) {
3714                         case OPTION:
3715                             if (isCurrent("option")) {
3716                                 pop();
3717                                 break endtagloop;
3718                             } else {
3719                                 errStrayEndTag(name);
3720                                 break endtagloop;
3721                             }
3722                         case OPTGROUP:
3723                             if (isCurrent("option")
3724                                     && "optgroup" == stack[currentPtr - 1].name) {
3725                                 pop();
3726                             }
3727                             if (isCurrent("optgroup")) {
3728                                 pop();
3729                             } else {
3730                                 errStrayEndTag(name);
3731                             }
3732                             break endtagloop;
3733                         case SELECT:
3734                             eltPos = findLastInTableScope("select");
3735                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3736                                 assert fragment;
3737                                 errStrayEndTag(name);
3738                                 break endtagloop;
3739                             }
3740                             while (currentPtr >= eltPos) {
3741                                 pop();
3742                             }
3743                             resetTheInsertionMode();
3744                             break endtagloop;
3745                         case TEMPLATE:
3746                             endTagTemplateInHead();
3747                             break endtagloop;
3748                         default:
3749                             errStrayEndTag(name);
3750                             break endtagloop;
3751                     }
3752                 case AFTER_BODY:
3753                     switch (group) {
3754                         case HTML:
3755                             if (fragment) {
3756                                 errStrayEndTag(name);
3757                                 break endtagloop;
3758                             } else {
3759                                 mode = AFTER_AFTER_BODY;
3760                                 break endtagloop;
3761                             }
3762                         default:
3763                             errEndTagAfterBody();
3764                             mode = framesetOk ? FRAMESET_OK : IN_BODY;
3765                             continue;
3766                     }
3767                 case IN_FRAMESET:
3768                     switch (group) {
3769                         case FRAMESET:
3770                             if (currentPtr == 0) {
3771                                 assert fragment;
3772                                 errStrayEndTag(name);
3773                                 break endtagloop;
3774                             }
3775                             pop();
3776                             if ((!fragment) && !isCurrent("frameset")) {
3777                                 mode = AFTER_FRAMESET;
3778                             }
3779                             break endtagloop;
3780                         default:
3781                             errStrayEndTag(name);
3782                             break endtagloop;
3783                     }
3784                 case AFTER_FRAMESET:
3785                     switch (group) {
3786                         case HTML:
3787                             mode = AFTER_AFTER_FRAMESET;
3788                             break endtagloop;
3789                         default:
3790                             errStrayEndTag(name);
3791                             break endtagloop;
3792                     }
3793                 case INITIAL:
3794                     /*
3795                      * Parse error.
3796                      */
3797                     errEndTagSeenWithoutDoctype();
3798                     /*
3799                      *
3800                      * Set the document to quirks mode.
3801                      */
3802                     documentModeInternal(DocumentMode.QUIRKS_MODE, null, null);
3803                     /*
3804                      * Then, switch to the root element mode of the tree
3805                      * construction stage
3806                      */
3807                     mode = BEFORE_HTML;
3808                     /*
3809                      * and reprocess the current token.
3810                      */
3811                     continue;
3812                 case BEFORE_HTML:
3813                     switch (group) {
3814                         case HEAD:
3815                         case BR:
3816                         case HTML:
3817                         case BODY:
3818                             /*
3819                              * Create an HTMLElement node with the tag name
3820                              * html, in the HTML namespace. Append it to the
3821                              * Document object.
3822                              */
3823                             appendHtmlElementToDocumentAndPush();
3824                             /* Switch to the main mode */
3825                             mode = BEFORE_HEAD;
3826                             /*
3827                              * reprocess the current token.
3828                              */
3829                             continue;
3830                         default:
3831                             errStrayEndTag(name);
3832                             break endtagloop;
3833                     }
3834                 case BEFORE_HEAD:
3835                     switch (group) {
3836                         case HEAD:
3837                         case BR:
3838                         case HTML:
3839                         case BODY:
3840                             appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
3841                             mode = IN_HEAD;
3842                             continue;
3843                         default:
3844                             errStrayEndTag(name);
3845                             break endtagloop;
3846                     }
3847                 case AFTER_HEAD:
3848                     switch (group) {
3849                         case TEMPLATE:
3850                             endTagTemplateInHead();
3851                             break endtagloop;
3852                         case HTML:
3853                         case BODY:
3854                         case BR:
3855                             appendToCurrentNodeAndPushBodyElement();
3856                             mode = FRAMESET_OK;
3857                             continue;
3858                         default:
3859                             errStrayEndTag(name);
3860                             break endtagloop;
3861                     }
3862                 case AFTER_AFTER_BODY:
3863                     errStrayEndTag(name);
3864                     mode = framesetOk ? FRAMESET_OK : IN_BODY;
3865                     continue;
3866                 case AFTER_AFTER_FRAMESET:
3867                     errStrayEndTag(name);
3868                     break endtagloop;
3869                 case TEXT:
3870                     // XXX need to manage insertion point here
3871                     pop();
3872                     if (originalMode == AFTER_HEAD) {
3873                         silentPop();
3874                     }
3875                     mode = originalMode;
3876                     break endtagloop;
3877             }
3878         } // endtagloop
3879     }
3880 
endTagTemplateInHead()3881     private void endTagTemplateInHead() throws SAXException {
3882         int eltPos = findLast("template");
3883         if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3884             errStrayEndTag("template");
3885             return;
3886         }
3887         generateImpliedEndTags();
3888         if (errorHandler != null && !isCurrent("template")) {
3889             errUnclosedElements(eltPos, "template");
3890         }
3891         while (currentPtr >= eltPos) {
3892             pop();
3893         }
3894         clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3895         popTemplateMode();
3896         resetTheInsertionMode();
3897     }
3898 
findLastInTableScopeOrRootTemplateTbodyTheadTfoot()3899     private int findLastInTableScopeOrRootTemplateTbodyTheadTfoot() {
3900         for (int i = currentPtr; i > 0; i--) {
3901             if (stack[i].getGroup() == TreeBuilder.TBODY_OR_THEAD_OR_TFOOT ||
3902                     stack[i].getGroup() == TreeBuilder.TEMPLATE) {
3903                 return i;
3904             }
3905         }
3906         return 0;
3907     }
3908 
findLast(@ocal String name)3909     private int findLast(@Local String name) {
3910         for (int i = currentPtr; i > 0; i--) {
3911             if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) {
3912                 return i;
3913             }
3914         }
3915         return TreeBuilder.NOT_FOUND_ON_STACK;
3916     }
3917 
findLastInTableScope(@ocal String name)3918     private int findLastInTableScope(@Local String name) {
3919         for (int i = currentPtr; i > 0; i--) {
3920             if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
3921                 if (stack[i].name == name) {
3922                     return i;
3923                 } else if (stack[i].name == "table" || stack[i].name == "template") {
3924                     return TreeBuilder.NOT_FOUND_ON_STACK;
3925                 }
3926             }
3927         }
3928         return TreeBuilder.NOT_FOUND_ON_STACK;
3929     }
3930 
findLastInButtonScope(@ocal String name)3931     private int findLastInButtonScope(@Local String name) {
3932         for (int i = currentPtr; i > 0; i--) {
3933             if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
3934                 if (stack[i].name == name) {
3935                     return i;
3936                 } else if (stack[i].name == "button") {
3937                     return TreeBuilder.NOT_FOUND_ON_STACK;
3938                 }
3939             }
3940 
3941             if (stack[i].isScoping()) {
3942                 return TreeBuilder.NOT_FOUND_ON_STACK;
3943             }
3944         }
3945         return TreeBuilder.NOT_FOUND_ON_STACK;
3946     }
3947 
findLastInScope(@ocal String name)3948     private int findLastInScope(@Local String name) {
3949         for (int i = currentPtr; i > 0; i--) {
3950             if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) {
3951                 return i;
3952             } else if (stack[i].isScoping()) {
3953                 return TreeBuilder.NOT_FOUND_ON_STACK;
3954             }
3955         }
3956         return TreeBuilder.NOT_FOUND_ON_STACK;
3957     }
3958 
findLastInListScope(@ocal String name)3959     private int findLastInListScope(@Local String name) {
3960         for (int i = currentPtr; i > 0; i--) {
3961             if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
3962                 if (stack[i].name == name) {
3963                     return i;
3964                 } else if (stack[i].name == "ul" || stack[i].name == "ol") {
3965                     return TreeBuilder.NOT_FOUND_ON_STACK;
3966                 }
3967             }
3968 
3969             if (stack[i].isScoping()) {
3970                 return TreeBuilder.NOT_FOUND_ON_STACK;
3971             }
3972         }
3973         return TreeBuilder.NOT_FOUND_ON_STACK;
3974     }
3975 
findLastInScopeHn()3976     private int findLastInScopeHn() {
3977         for (int i = currentPtr; i > 0; i--) {
3978             if (stack[i].getGroup() == TreeBuilder.H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) {
3979                 return i;
3980             } else if (stack[i].isScoping()) {
3981                 return TreeBuilder.NOT_FOUND_ON_STACK;
3982             }
3983         }
3984         return TreeBuilder.NOT_FOUND_ON_STACK;
3985     }
3986 
generateImpliedEndTagsExceptFor(@ocal String name)3987     private void generateImpliedEndTagsExceptFor(@Local String name)
3988             throws SAXException {
3989         for (;;) {
3990             StackNode<T> node = stack[currentPtr];
3991             switch (node.getGroup()) {
3992                 case P:
3993                 case LI:
3994                 case DD_OR_DT:
3995                 case OPTION:
3996                 case OPTGROUP:
3997                 case RB_OR_RTC:
3998                 case RT_OR_RP:
3999                     if (node.ns == "http://www.w3.org/1999/xhtml" && node.name == name) {
4000                         return;
4001                     }
4002                     pop();
4003                     continue;
4004                 default:
4005                     return;
4006             }
4007         }
4008     }
4009 
generateImpliedEndTags()4010     private void generateImpliedEndTags() throws SAXException {
4011         for (;;) {
4012             switch (stack[currentPtr].getGroup()) {
4013                 case P:
4014                 case LI:
4015                 case DD_OR_DT:
4016                 case OPTION:
4017                 case OPTGROUP:
4018                 case RB_OR_RTC:
4019                 case RT_OR_RP:
4020                     pop();
4021                     continue;
4022                 default:
4023                     return;
4024             }
4025         }
4026     }
4027 
isSecondOnStackBody()4028     private boolean isSecondOnStackBody() {
4029         return currentPtr >= 1 && stack[1].getGroup() == TreeBuilder.BODY;
4030     }
4031 
documentModeInternal(DocumentMode m, String publicIdentifier, String systemIdentifier)4032     private void documentModeInternal(DocumentMode m, String publicIdentifier,
4033             String systemIdentifier)
4034             throws SAXException {
4035 
4036         if (isSrcdocDocument) {
4037             // Srcdoc documents are always rendered in standards mode.
4038             quirks = false;
4039             if (documentModeHandler != null) {
4040                 documentModeHandler.documentMode(
4041                         DocumentMode.STANDARDS_MODE
4042                         // [NOCPP[
4043                         , null, null
4044                 // ]NOCPP]
4045                 );
4046             }
4047             return;
4048         }
4049 
4050         quirks = (m == DocumentMode.QUIRKS_MODE);
4051         if (documentModeHandler != null) {
4052             documentModeHandler.documentMode(
4053                     m
4054                     // [NOCPP[
4055                     , publicIdentifier, systemIdentifier
4056             // ]NOCPP]
4057             );
4058         }
4059         // [NOCPP[
4060         documentMode(m, publicIdentifier, systemIdentifier);
4061         // ]NOCPP]
4062     }
4063 
isAlmostStandards(String publicIdentifier, String systemIdentifier)4064     private boolean isAlmostStandards(String publicIdentifier,
4065             String systemIdentifier) {
4066         if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4067                 "-//w3c//dtd xhtml 1.0 transitional//", publicIdentifier)) {
4068             return true;
4069         }
4070         if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4071                 "-//w3c//dtd xhtml 1.0 frameset//", publicIdentifier)) {
4072             return true;
4073         }
4074         if (systemIdentifier != null) {
4075             if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4076                     "-//w3c//dtd html 4.01 transitional//", publicIdentifier)) {
4077                 return true;
4078             }
4079             if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4080                     "-//w3c//dtd html 4.01 frameset//", publicIdentifier)) {
4081                 return true;
4082             }
4083         }
4084         return false;
4085     }
4086 
isQuirky(@ocal String name, String publicIdentifier, String systemIdentifier, boolean forceQuirks)4087     private boolean isQuirky(@Local String name, String publicIdentifier,
4088             String systemIdentifier, boolean forceQuirks) {
4089         if (forceQuirks) {
4090             return true;
4091         }
4092         if (name != HTML_LOCAL) {
4093             return true;
4094         }
4095         if (publicIdentifier != null) {
4096             for (int i = 0; i < TreeBuilder.QUIRKY_PUBLIC_IDS.length; i++) {
4097                 if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4098                         TreeBuilder.QUIRKY_PUBLIC_IDS[i], publicIdentifier)) {
4099                     return true;
4100                 }
4101             }
4102             if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4103                     "-//w3o//dtd w3 html strict 3.0//en//", publicIdentifier)
4104                     || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4105                             "-/w3c/dtd html 4.0 transitional/en",
4106                             publicIdentifier)
4107                     || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4108                             "html", publicIdentifier)) {
4109                 return true;
4110             }
4111         }
4112         if (systemIdentifier == null) {
4113             if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4114                     "-//w3c//dtd html 4.01 transitional//", publicIdentifier)) {
4115                 return true;
4116             } else if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4117                     "-//w3c//dtd html 4.01 frameset//", publicIdentifier)) {
4118                 return true;
4119             }
4120         } else if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4121                 "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd",
4122                 systemIdentifier)) {
4123             return true;
4124         }
4125         return false;
4126     }
4127 
closeTheCell(int eltPos)4128     private void closeTheCell(int eltPos) throws SAXException {
4129         generateImpliedEndTags();
4130         if (errorHandler != null && eltPos != currentPtr) {
4131             errUnclosedElementsCell(eltPos);
4132         }
4133         while (currentPtr >= eltPos) {
4134             pop();
4135         }
4136         clearTheListOfActiveFormattingElementsUpToTheLastMarker();
4137         mode = IN_ROW;
4138         return;
4139     }
4140 
findLastInTableScopeTdTh()4141     private int findLastInTableScopeTdTh() {
4142         for (int i = currentPtr; i > 0; i--) {
4143             @Local String name = stack[i].name;
4144             if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
4145                 if ("td" == name || "th" == name) {
4146                     return i;
4147                 } else if (name == "table" || name == "template") {
4148                     return TreeBuilder.NOT_FOUND_ON_STACK;
4149                 }
4150             }
4151         }
4152         return TreeBuilder.NOT_FOUND_ON_STACK;
4153     }
4154 
clearStackBackTo(int eltPos)4155     private void clearStackBackTo(int eltPos) throws SAXException {
4156         int eltGroup = stack[eltPos].getGroup();
4157         while (currentPtr > eltPos) { // > not >= intentional
4158             if (stack[currentPtr].ns == "http://www.w3.org/1999/xhtml"
4159                     && stack[currentPtr].getGroup() == TEMPLATE
4160                     && (eltGroup == TABLE || eltGroup == TBODY_OR_THEAD_OR_TFOOT|| eltGroup == TR || eltPos == 0)) {
4161                 return;
4162             }
4163             pop();
4164         }
4165     }
4166 
resetTheInsertionMode()4167     private void resetTheInsertionMode() {
4168         StackNode<T> node;
4169         @Local String name;
4170         @NsUri String ns;
4171         for (int i = currentPtr; i >= 0; i--) {
4172             node = stack[i];
4173             name = node.name;
4174             ns = node.ns;
4175             if (i == 0) {
4176                 if (!(contextNamespace == "http://www.w3.org/1999/xhtml" && (contextName == "td" || contextName == "th"))) {
4177                     if (fragment) {
4178                         // Make sure we are parsing a fragment otherwise the context element doesn't make sense.
4179                         name = contextName;
4180                         ns = contextNamespace;
4181                     }
4182                 } else {
4183                     mode = framesetOk ? FRAMESET_OK : IN_BODY; // XXX from Hixie's email
4184                     return;
4185                 }
4186             }
4187             if ("select" == name) {
4188                 int ancestorIndex = i;
4189                 while (ancestorIndex > 0) {
4190                     StackNode<T> ancestor = stack[ancestorIndex--];
4191                     if ("http://www.w3.org/1999/xhtml" == ancestor.ns) {
4192                         if ("template" == ancestor.name) {
4193                             break;
4194                         }
4195                         if ("table" == ancestor.name) {
4196                             mode = IN_SELECT_IN_TABLE;
4197                             return;
4198                         }
4199                     }
4200                 }
4201                 mode = IN_SELECT;
4202                 return;
4203             } else if ("td" == name || "th" == name) {
4204                 mode = IN_CELL;
4205                 return;
4206             } else if ("tr" == name) {
4207                 mode = IN_ROW;
4208                 return;
4209             } else if ("tbody" == name || "thead" == name || "tfoot" == name) {
4210                 mode = IN_TABLE_BODY;
4211                 return;
4212             } else if ("caption" == name) {
4213                 mode = IN_CAPTION;
4214                 return;
4215             } else if ("colgroup" == name) {
4216                 mode = IN_COLUMN_GROUP;
4217                 return;
4218             } else if ("table" == name) {
4219                 mode = IN_TABLE;
4220                 return;
4221             } else if ("http://www.w3.org/1999/xhtml" != ns) {
4222                 mode = framesetOk ? FRAMESET_OK : IN_BODY;
4223                 return;
4224             } else if ("template" == name) {
4225                 assert templateModePtr >= 0;
4226                 mode = templateModeStack[templateModePtr];
4227                 return;
4228             }  else if ("head" == name) {
4229                 if (name == contextName) {
4230                     mode = framesetOk ? FRAMESET_OK : IN_BODY; // really
4231                 } else {
4232                     mode = IN_HEAD;
4233                 }
4234                 return;
4235             } else if ("body" == name) {
4236                 mode = framesetOk ? FRAMESET_OK : IN_BODY;
4237                 return;
4238             } else if ("frameset" == name) {
4239                 // TODO: Fragment case. Add error reporting.
4240                 mode = IN_FRAMESET;
4241                 return;
4242             } else if ("html" == name) {
4243                 if (headPointer == null) {
4244                     // TODO: Fragment case. Add error reporting.
4245                     mode = BEFORE_HEAD;
4246                 } else {
4247                     mode = AFTER_HEAD;
4248                 }
4249                 return;
4250             } else if (i == 0) {
4251                 mode = framesetOk ? FRAMESET_OK : IN_BODY;
4252                 return;
4253             }
4254         }
4255     }
4256 
4257     /**
4258      * @throws SAXException
4259      *
4260      */
implicitlyCloseP()4261     private void implicitlyCloseP() throws SAXException {
4262         int eltPos = findLastInButtonScope("p");
4263         if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
4264             return;
4265         }
4266         generateImpliedEndTagsExceptFor("p");
4267         if (errorHandler != null && eltPos != currentPtr) {
4268             errUnclosedElementsImplied(eltPos, "p");
4269         }
4270         while (currentPtr >= eltPos) {
4271             pop();
4272         }
4273     }
4274 
debugOnlyClearLastStackSlot()4275     private boolean debugOnlyClearLastStackSlot() {
4276         stack[currentPtr] = null;
4277         return true;
4278     }
4279 
debugOnlyClearLastListSlot()4280     private boolean debugOnlyClearLastListSlot() {
4281         listOfActiveFormattingElements[listPtr] = null;
4282         return true;
4283     }
4284 
pushTemplateMode(int mode)4285     private void pushTemplateMode(int mode) {
4286         templateModePtr++;
4287         if (templateModePtr == templateModeStack.length) {
4288             int[] newStack = new int[templateModeStack.length + 64];
4289             System.arraycopy(templateModeStack, 0, newStack, 0, templateModeStack.length);
4290             templateModeStack = newStack;
4291         }
4292         templateModeStack[templateModePtr] = mode;
4293     }
4294 
push(StackNode<T> node)4295     @SuppressWarnings("unchecked") private void push(StackNode<T> node) throws SAXException {
4296         currentPtr++;
4297         if (currentPtr == stack.length) {
4298             StackNode<T>[] newStack = new StackNode[stack.length + 64];
4299             System.arraycopy(stack, 0, newStack, 0, stack.length);
4300             stack = newStack;
4301         }
4302         stack[currentPtr] = node;
4303         elementPushed(node.ns, node.popName, node.node);
4304     }
4305 
silentPush(StackNode<T> node)4306     @SuppressWarnings("unchecked") private void silentPush(StackNode<T> node) throws SAXException {
4307         currentPtr++;
4308         if (currentPtr == stack.length) {
4309             StackNode<T>[] newStack = new StackNode[stack.length + 64];
4310             System.arraycopy(stack, 0, newStack, 0, stack.length);
4311             stack = newStack;
4312         }
4313         stack[currentPtr] = node;
4314     }
4315 
append(StackNode<T> node)4316     @SuppressWarnings("unchecked") private void append(StackNode<T> node) {
4317         listPtr++;
4318         if (listPtr == listOfActiveFormattingElements.length) {
4319             StackNode<T>[] newList = new StackNode[listOfActiveFormattingElements.length + 64];
4320             System.arraycopy(listOfActiveFormattingElements, 0, newList, 0,
4321                     listOfActiveFormattingElements.length);
4322             listOfActiveFormattingElements = newList;
4323         }
4324         listOfActiveFormattingElements[listPtr] = node;
4325     }
4326 
insertMarker()4327     @Inline private void insertMarker() {
4328         append(null);
4329     }
4330 
clearTheListOfActiveFormattingElementsUpToTheLastMarker()4331     private void clearTheListOfActiveFormattingElementsUpToTheLastMarker() {
4332         while (listPtr > -1) {
4333             if (listOfActiveFormattingElements[listPtr] == null) {
4334                 --listPtr;
4335                 return;
4336             }
4337             listOfActiveFormattingElements[listPtr].release(this);
4338             --listPtr;
4339         }
4340     }
4341 
isCurrent(@ocal String name)4342     @Inline private boolean isCurrent(@Local String name) {
4343         return stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" &&
4344                 name == stack[currentPtr].name;
4345     }
4346 
removeFromStack(int pos)4347     private void removeFromStack(int pos) throws SAXException {
4348         if (currentPtr == pos) {
4349             pop();
4350         } else {
4351             fatal();
4352             stack[pos].release(this);
4353             System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
4354             assert debugOnlyClearLastStackSlot();
4355             currentPtr--;
4356         }
4357     }
4358 
removeFromStack(StackNode<T> node)4359     private void removeFromStack(StackNode<T> node) throws SAXException {
4360         if (stack[currentPtr] == node) {
4361             pop();
4362         } else {
4363             int pos = currentPtr - 1;
4364             while (pos >= 0 && stack[pos] != node) {
4365                 pos--;
4366             }
4367             if (pos == -1) {
4368                 // dead code?
4369                 return;
4370             }
4371             fatal();
4372             node.release(this);
4373             System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
4374             currentPtr--;
4375         }
4376     }
4377 
removeFromListOfActiveFormattingElements(int pos)4378     private void removeFromListOfActiveFormattingElements(int pos) {
4379         assert listOfActiveFormattingElements[pos] != null;
4380         listOfActiveFormattingElements[pos].release(this);
4381         if (pos == listPtr) {
4382             assert debugOnlyClearLastListSlot();
4383             listPtr--;
4384             return;
4385         }
4386         assert pos < listPtr;
4387         System.arraycopy(listOfActiveFormattingElements, pos + 1,
4388                 listOfActiveFormattingElements, pos, listPtr - pos);
4389         assert debugOnlyClearLastListSlot();
4390         listPtr--;
4391     }
4392 
4393     /**
4394      * Adoption agency algorithm.
4395      *
4396      * @param name subject as described in the specified algorithm.
4397      * @return Returns true if the algorithm has completed and there is nothing remaining to
4398      * be done. Returns false if the algorithm needs to "act as described in the 'any other
4399      * end tag' entry" as described in the specified algorithm.
4400      * @throws SAXException
4401      */
4402     private boolean adoptionAgencyEndTag(@Local String name) throws SAXException {
4403         // This check intends to ensure that for properly nested tags, closing tags will match
4404         // against the stack instead of the listOfActiveFormattingElements.
4405         if (stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" &&
4406                 stack[currentPtr].name == name &&
4407                 findInListOfActiveFormattingElements(stack[currentPtr]) == -1) {
4408             // If the current element matches the name but isn't on the list of active
4409             // formatting elements, then it is possible that the list was mangled by the Noah's Ark
4410             // clause. In this case, we want to match the end tag against the stack instead of
4411             // proceeding with the AAA algorithm that may match against the list of
4412             // active formatting elements (and possibly mangle the tree in unexpected ways).
4413             pop();
4414             return true;
4415         }
4416 
4417         // If you crash around here, perhaps some stack node variable claimed to
4418         // be a weak ref isn't.
4419         for (int i = 0; i < 8; ++i) {
4420             int formattingEltListPos = listPtr;
4421             while (formattingEltListPos > -1) {
4422                 StackNode<T> listNode = listOfActiveFormattingElements[formattingEltListPos]; // weak ref
4423                 if (listNode == null) {
4424                     formattingEltListPos = -1;
4425                     break;
4426                 } else if (listNode.name == name) {
4427                     break;
4428                 }
4429                 formattingEltListPos--;
4430             }
4431             if (formattingEltListPos == -1) {
4432                 return false;
4433             }
4434             // this *looks* like a weak ref to the list of formatting elements
4435             StackNode<T> formattingElt = listOfActiveFormattingElements[formattingEltListPos];
4436             int formattingEltStackPos = currentPtr;
4437             boolean inScope = true;
4438             while (formattingEltStackPos > -1) {
4439                 StackNode<T> node = stack[formattingEltStackPos]; // weak ref
4440                 if (node == formattingElt) {
4441                     break;
4442                 } else if (node.isScoping()) {
4443                     inScope = false;
4444                 }
4445                 formattingEltStackPos--;
4446             }
4447             if (formattingEltStackPos == -1) {
4448                 errNoElementToCloseButEndTagSeen(name);
4449                 removeFromListOfActiveFormattingElements(formattingEltListPos);
4450                 return true;
4451             }
4452             if (!inScope) {
4453                 errNoElementToCloseButEndTagSeen(name);
4454                 return true;
4455             }
4456             // stackPos now points to the formatting element and it is in scope
4457             if (formattingEltStackPos != currentPtr) {
4458                 errEndTagViolatesNestingRules(name);
4459             }
4460             int furthestBlockPos = formattingEltStackPos + 1;
4461             while (furthestBlockPos <= currentPtr) {
4462                 StackNode<T> node = stack[furthestBlockPos]; // weak ref
4463                 assert furthestBlockPos > 0: "How is formattingEltStackPos + 1 not > 0?";
4464                 if (node.isSpecial()) {
4465                     break;
4466                 }
4467                 furthestBlockPos++;
4468             }
4469             if (furthestBlockPos > currentPtr) {
4470                 // no furthest block
4471                 while (currentPtr >= formattingEltStackPos) {
4472                     pop();
4473                 }
4474                 removeFromListOfActiveFormattingElements(formattingEltListPos);
4475                 return true;
4476             }
4477             // commonAncestor is used for running the algorithm and
4478             // insertionCommonAncestor is used for the actual insertions to
4479             // keep them depth-limited.
4480             StackNode<T> commonAncestor = stack[formattingEltStackPos - 1]; // weak ref
4481             T insertionCommonAncestor = nodeFromStackWithBlinkCompat(formattingEltStackPos - 1); // weak ref
4482             StackNode<T> furthestBlock = stack[furthestBlockPos]; // weak ref
4483             // detachFromParent(furthestBlock.node); XXX AAA CHANGE
4484             int bookmark = formattingEltListPos;
4485             int nodePos = furthestBlockPos;
4486             StackNode<T> lastNode = furthestBlock; // weak ref
4487             int j = 0;
4488             for (;;) {
4489                 ++j;
4490                 nodePos--;
4491                 if (nodePos == formattingEltStackPos) {
4492                     break;
4493                 }
4494                 StackNode<T> node = stack[nodePos]; // weak ref
4495                 int nodeListPos = findInListOfActiveFormattingElements(node);
4496 
4497                 if (j > 3 && nodeListPos != -1) {
4498                     removeFromListOfActiveFormattingElements(nodeListPos);
4499 
4500                     // Adjust the indices into the list to account
4501                     // for the removal of nodeListPos.
4502                     if (nodeListPos <= formattingEltListPos) {
4503                         formattingEltListPos--;
4504                     }
4505                     if (nodeListPos <= bookmark) {
4506                         bookmark--;
4507                     }
4508 
4509                     // Update position to reflect removal from list.
4510                     nodeListPos = -1;
4511                 }
4512 
4513                 if (nodeListPos == -1) {
4514                     assert formattingEltStackPos < nodePos;
4515                     assert bookmark < nodePos;
4516                     assert furthestBlockPos > nodePos;
4517                     removeFromStack(nodePos); // node is now a bad pointer in C++
4518                     furthestBlockPos--;
4519                     continue;
4520                 }
4521                 // now node is both on stack and in the list
4522                 if (nodePos == furthestBlockPos) {
4523                     bookmark = nodeListPos + 1;
4524                 }
4525                 // if (hasChildren(node.node)) { XXX AAA CHANGE
4526                 assert node == listOfActiveFormattingElements[nodeListPos];
4527                 assert node == stack[nodePos];
4528                 T clone = createElement("http://www.w3.org/1999/xhtml",
4529                         node.name, node.attributes.cloneAttributes(), insertionCommonAncestor
4530                         // CPPONLY: , htmlCreator(node.getHtmlCreator())
4531                         );
4532                 StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
4533                         node.name, clone, node.popName, node.attributes
4534                         // CPPONLY: , node.getHtmlCreator()
4535                         // [NOCPP[
4536                         , node.getLocator()
4537                         // ]NOCPP]
4538                 ); // creation ownership goes to stack
4539                 node.dropAttributes(); // adopt ownership to newNode
4540                 stack[nodePos] = newNode;
4541                 newNode.retain(); // retain for list
4542                 listOfActiveFormattingElements[nodeListPos] = newNode;
4543                 node.release(this); // release from stack
4544                 node.release(this); // release from list
4545                 node = newNode;
4546                 // } XXX AAA CHANGE
4547                 detachFromParent(lastNode.node);
4548                 appendElement(lastNode.node, nodeFromStackWithBlinkCompat(nodePos));
4549                 lastNode = node;
4550             }
4551             // If we insert into a foster parent, for simplicity, we insert
4552             // accoding to the spec without Blink's depth limit.
4553             if (commonAncestor.isFosterParenting()) {
4554                 fatal();
4555                 detachFromParent(lastNode.node);
4556                 insertIntoFosterParent(lastNode.node);
4557             } else {
4558                 detachFromParent(lastNode.node);
4559                 appendElement(lastNode.node, insertionCommonAncestor);
4560             }
4561             T clone = createElement("http://www.w3.org/1999/xhtml",
4562                     formattingElt.name,
4563                     formattingElt.attributes.cloneAttributes(), furthestBlock.node
4564                     // CPPONLY: , htmlCreator(formattingElt.getHtmlCreator())
4565                     );
4566             StackNode<T> formattingClone = createStackNode(
4567                     formattingElt.getFlags(), formattingElt.ns,
4568                     formattingElt.name, clone, formattingElt.popName,
4569                     formattingElt.attributes
4570                     // CPPONLY: , formattingElt.getHtmlCreator()
4571                     // [NOCPP[
4572                     , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4573                     // ]NOCPP]
4574             ); // Ownership transfers to stack below
4575             formattingElt.dropAttributes(); // transfer ownership to
4576                                             // formattingClone
4577             appendChildrenToNewParent(furthestBlock.node, clone);
4578             appendElement(clone, furthestBlock.node);
4579             removeFromListOfActiveFormattingElements(formattingEltListPos);
4580             insertIntoListOfActiveFormattingElements(formattingClone, bookmark);
4581             assert formattingEltStackPos < furthestBlockPos;
4582             removeFromStack(formattingEltStackPos);
4583             // furthestBlockPos is now off by one and points to the slot after
4584             // it
4585             insertIntoStack(formattingClone, furthestBlockPos);
4586         }
4587         return true;
4588     }
4589 
4590     private void insertIntoStack(StackNode<T> node, int position)
4591             throws SAXException {
4592         assert currentPtr + 1 < stack.length;
4593         assert position <= currentPtr + 1;
4594         if (position == currentPtr + 1) {
4595             push(node);
4596         } else {
4597             System.arraycopy(stack, position, stack, position + 1,
4598                     (currentPtr - position) + 1);
4599             currentPtr++;
4600             stack[position] = node;
4601         }
4602     }
4603 
4604     private void insertIntoListOfActiveFormattingElements(
4605             StackNode<T> formattingClone, int bookmark) {
4606         formattingClone.retain();
4607         assert listPtr + 1 < listOfActiveFormattingElements.length;
4608         if (bookmark <= listPtr) {
4609             System.arraycopy(listOfActiveFormattingElements, bookmark,
4610                     listOfActiveFormattingElements, bookmark + 1,
4611                     (listPtr - bookmark) + 1);
4612         }
4613         listPtr++;
4614         listOfActiveFormattingElements[bookmark] = formattingClone;
4615     }
4616 
4617     private int findInListOfActiveFormattingElements(StackNode<T> node) {
4618         for (int i = listPtr; i >= 0; i--) {
4619             if (node == listOfActiveFormattingElements[i]) {
4620                 return i;
4621             }
4622         }
4623         return -1;
4624     }
4625 
4626     private int findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker(
4627             @Local String name) {
4628         for (int i = listPtr; i >= 0; i--) {
4629             StackNode<T> node = listOfActiveFormattingElements[i];
4630             if (node == null) {
4631                 return -1;
4632             } else if (node.name == name) {
4633                 return i;
4634             }
4635         }
4636         return -1;
4637     }
4638 
4639 
4640     private void maybeForgetEarlierDuplicateFormattingElement(
4641             @Local String name, HtmlAttributes attributes) throws SAXException {
4642         int candidate = -1;
4643         int count = 0;
4644         for (int i = listPtr; i >= 0; i--) {
4645             StackNode<T> node = listOfActiveFormattingElements[i];
4646             if (node == null) {
4647                 break;
4648             }
4649             if (node.name == name && node.attributes.equalsAnother(attributes)) {
4650                 candidate = i;
4651                 ++count;
4652             }
4653         }
4654         if (count >= 3) {
4655             removeFromListOfActiveFormattingElements(candidate);
4656         }
4657     }
4658 
4659     private int findLastOrRoot(@Local String name) {
4660         for (int i = currentPtr; i > 0; i--) {
4661             if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) {
4662                 return i;
4663             }
4664         }
4665         return 0;
4666     }
4667 
4668     private int findLastOrRoot(int group) {
4669         for (int i = currentPtr; i > 0; i--) {
4670             if (stack[i].getGroup() == group) {
4671                 return i;
4672             }
4673         }
4674         return 0;
4675     }
4676 
4677     /**
4678      * Attempt to add attribute to the body element.
4679      * @param attributes the attributes
4680      * @return <code>true</code> iff the attributes were added
4681      * @throws SAXException
4682      */
addAttributesToBody(HtmlAttributes attributes)4683     private boolean addAttributesToBody(HtmlAttributes attributes)
4684             throws SAXException {
4685         // [NOCPP[
4686         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4687         // ]NOCPP]
4688         if (currentPtr >= 1) {
4689             StackNode<T> body = stack[1];
4690             if (body.getGroup() == TreeBuilder.BODY) {
4691                 addAttributesToElement(body.node, attributes);
4692                 return true;
4693             }
4694         }
4695         return false;
4696     }
4697 
addAttributesToHtml(HtmlAttributes attributes)4698     private void addAttributesToHtml(HtmlAttributes attributes)
4699             throws SAXException {
4700         // [NOCPP[
4701         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4702         // ]NOCPP]
4703         addAttributesToElement(stack[0].node, attributes);
4704     }
4705 
pushHeadPointerOntoStack()4706     private void pushHeadPointerOntoStack() throws SAXException {
4707         assert headPointer != null;
4708         assert mode == AFTER_HEAD;
4709         fatal();
4710         silentPush(createStackNode(ElementName.HEAD, headPointer
4711         // [NOCPP[
4712                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4713         // ]NOCPP]
4714         ));
4715     }
4716 
4717     /**
4718      * @throws SAXException
4719      *
4720      */
reconstructTheActiveFormattingElements()4721     private void reconstructTheActiveFormattingElements() throws SAXException {
4722         if (listPtr == -1) {
4723             return;
4724         }
4725         StackNode<T> mostRecent = listOfActiveFormattingElements[listPtr];
4726         if (mostRecent == null || isInStack(mostRecent)) {
4727             return;
4728         }
4729         int entryPos = listPtr;
4730         for (;;) {
4731             entryPos--;
4732             if (entryPos == -1) {
4733                 break;
4734             }
4735             if (listOfActiveFormattingElements[entryPos] == null) {
4736                 break;
4737             }
4738             if (isInStack(listOfActiveFormattingElements[entryPos])) {
4739                 break;
4740             }
4741         }
4742         while (entryPos < listPtr) {
4743             entryPos++;
4744             StackNode<T> entry = listOfActiveFormattingElements[entryPos];
4745             StackNode<T> current = stack[currentPtr];
4746 
4747             T clone;
4748             if (current.isFosterParenting()) {
4749                 clone = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", entry.name,
4750                         entry.attributes.cloneAttributes()
4751                         // CPPONLY: , htmlCreator(entry.getHtmlCreator())
4752                         );
4753             } else {
4754                 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
4755                 clone = createElement("http://www.w3.org/1999/xhtml", entry.name,
4756                         entry.attributes.cloneAttributes(), currentNode
4757                         // CPPONLY: , htmlCreator(entry.getHtmlCreator())
4758                         );
4759                 appendElement(clone, currentNode);
4760             }
4761 
4762             StackNode<T> entryClone = createStackNode(entry.getFlags(),
4763                     entry.ns, entry.name, clone, entry.popName,
4764                     entry.attributes
4765                     // CPPONLY: , entry.getHtmlCreator()
4766                     // [NOCPP[
4767                     , entry.getLocator()
4768                     // ]NOCPP]
4769             );
4770 
4771             entry.dropAttributes(); // transfer ownership to entryClone
4772 
4773             push(entryClone);
4774             // stack takes ownership of the local variable
4775             listOfActiveFormattingElements[entryPos] = entryClone;
4776             // overwriting the old entry on the list, so release & retain
4777             entry.release(this);
4778             entryClone.retain();
4779         }
4780     }
4781 
notifyUnusedStackNode(int idxInStackNodes)4782     void notifyUnusedStackNode(int idxInStackNodes) {
4783         // stackNodesIdx is the earliest possible index of a stack node that might be unused,
4784         // so update the index if necessary.
4785         if (idxInStackNodes < stackNodesIdx) {
4786             stackNodesIdx = idxInStackNodes;
4787         }
4788     }
4789 
getUnusedStackNode()4790     @SuppressWarnings("unchecked") private StackNode<T> getUnusedStackNode() {
4791         // Search for an unused stack node.
4792         while (stackNodesIdx < numStackNodes) {
4793             if (stackNodes[stackNodesIdx].isUnused()) {
4794                 return stackNodes[stackNodesIdx++];
4795             }
4796             stackNodesIdx++;
4797         }
4798 
4799         if (stackNodesIdx < stackNodes.length) {
4800             // No unused stack nodes, but there is still space in the storage array.
4801             stackNodes[stackNodesIdx] = new StackNode<T>(stackNodesIdx);
4802             numStackNodes++;
4803             return stackNodes[stackNodesIdx++];
4804         }
4805 
4806         // Could not find an unused stack node and storage array is full.
4807         StackNode<T>[] newStack = new StackNode[stackNodes.length + 64];
4808         System.arraycopy(stackNodes, 0, newStack, 0, stackNodes.length);
4809         stackNodes = newStack;
4810 
4811         // Create a new stack node and return it.
4812         stackNodes[stackNodesIdx] = new StackNode<T>(stackNodesIdx);
4813         numStackNodes++;
4814         return stackNodes[stackNodesIdx++];
4815     }
4816 
createStackNode(int flags, @NsUri String ns, @Local String name, T node, @Local String popName, HtmlAttributes attributes , TaintableLocatorImpl locator )4817     private StackNode<T> createStackNode(int flags, @NsUri String ns, @Local String name, T node,
4818             @Local String popName, HtmlAttributes attributes
4819             // CPPONLY: , @HtmlCreator Object htmlCreator
4820             // [NOCPP[
4821             , TaintableLocatorImpl locator
4822             // ]NOCPP]
4823     ) {
4824         StackNode<T> instance = getUnusedStackNode();
4825         instance.setValues(flags, ns, name, node, popName, attributes
4826                 // CPPONLY: , htmlCreator
4827                 // [NOCPP[
4828                 , locator
4829                 // ]NOCPP]
4830         );
4831         return instance;
4832     }
4833 
createStackNode(ElementName elementName, T node , TaintableLocatorImpl locator )4834     private StackNode<T> createStackNode(ElementName elementName, T node
4835             // [NOCPP[
4836             , TaintableLocatorImpl locator
4837             // ]NOCPP]
4838     ) {
4839         StackNode<T> instance = getUnusedStackNode();
4840         instance.setValues(elementName, node
4841                 // [NOCPP[
4842                 , locator
4843                 // ]NOCPP]
4844         );
4845         return instance;
4846     }
4847 
createStackNode(ElementName elementName, T node, HtmlAttributes attributes , TaintableLocatorImpl locator )4848     private StackNode<T> createStackNode(ElementName elementName, T node, HtmlAttributes attributes
4849             // [NOCPP[
4850             , TaintableLocatorImpl locator
4851             // ]NOCPP]
4852     ) {
4853         StackNode<T> instance = getUnusedStackNode();
4854         instance.setValues(elementName, node, attributes
4855                 // [NOCPP[
4856                 , locator
4857                 // ]NOCPP]
4858         );
4859         return instance;
4860     }
4861 
createStackNode(ElementName elementName, T node, @Local String popName , TaintableLocatorImpl locator )4862     private StackNode<T> createStackNode(ElementName elementName, T node, @Local String popName
4863             // [NOCPP[
4864             , TaintableLocatorImpl locator
4865             // ]NOCPP]
4866     ) {
4867         StackNode<T> instance = getUnusedStackNode();
4868         instance.setValues(elementName, node, popName
4869                 // [NOCPP[
4870                 , locator
4871                 // ]NOCPP]
4872         );
4873         return instance;
4874     }
4875 
createStackNode(ElementName elementName, @Local String popName, T node , TaintableLocatorImpl locator )4876     private StackNode<T> createStackNode(ElementName elementName, @Local String popName, T node
4877             // [NOCPP[
4878             , TaintableLocatorImpl locator
4879             // ]NOCPP]
4880     ) {
4881         StackNode<T> instance = getUnusedStackNode();
4882         instance.setValues(elementName, popName, node
4883                 // [NOCPP[
4884                 , locator
4885                 // ]NOCPP]
4886         );
4887         return instance;
4888     }
4889 
createStackNode(ElementName elementName, T node, @Local String popName, boolean markAsIntegrationPoint , TaintableLocatorImpl locator )4890     private StackNode<T> createStackNode(ElementName elementName, T node, @Local String popName,
4891             boolean markAsIntegrationPoint
4892             // [NOCPP[
4893             , TaintableLocatorImpl locator
4894             // ]NOCPP]
4895     ) {
4896         StackNode<T> instance = getUnusedStackNode();
4897         instance.setValues(elementName, node, popName, markAsIntegrationPoint
4898                 // [NOCPP[
4899                 , locator
4900                 // ]NOCPP]
4901         );
4902         return instance;
4903     }
4904 
insertIntoFosterParent(T child)4905     private void insertIntoFosterParent(T child) throws SAXException {
4906         int tablePos = findLastOrRoot(TreeBuilder.TABLE);
4907         int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE);
4908 
4909         if (templatePos >= tablePos) {
4910             appendElement(child, stack[templatePos].node);
4911             return;
4912         }
4913 
4914         StackNode<T> node = stack[tablePos];
4915         insertFosterParentedChild(child, node.node, stack[tablePos - 1].node);
4916     }
4917 
createAndInsertFosterParentedElement(@sUri String ns, @Local String name, HtmlAttributes attributes )4918     private T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
4919             HtmlAttributes attributes
4920             // CPPONLY: , @Creator Object creator
4921             ) throws SAXException {
4922         return createAndInsertFosterParentedElement(ns, name, attributes, null
4923                 // CPPONLY: , creator
4924                 );
4925     }
4926 
createAndInsertFosterParentedElement(@sUri String ns, @Local String name, HtmlAttributes attributes, T form )4927     private T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
4928             HtmlAttributes attributes, T form
4929             // CPPONLY: , @Creator Object creator
4930             ) throws SAXException {
4931         int tablePos = findLastOrRoot(TreeBuilder.TABLE);
4932         int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE);
4933 
4934         if (templatePos >= tablePos) {
4935             T child = createElement(ns, name, attributes, form, stack[templatePos].node
4936                     // CPPONLY: , creator
4937                     );
4938             appendElement(child, stack[templatePos].node);
4939             return child;
4940         }
4941 
4942         StackNode<T> node = stack[tablePos];
4943         return createAndInsertFosterParentedElement(ns, name, attributes, form, node.node, stack[tablePos - 1].node
4944                 // CPPONLY: , creator
4945                 );
4946     }
4947 
isInStack(StackNode<T> node)4948     private boolean isInStack(StackNode<T> node) {
4949         for (int i = currentPtr; i >= 0; i--) {
4950             if (stack[i] == node) {
4951                 return true;
4952             }
4953         }
4954         return false;
4955     }
4956 
popTemplateMode()4957     private void popTemplateMode() {
4958         templateModePtr--;
4959     }
4960 
pop()4961     private void pop() throws SAXException {
4962         StackNode<T> node = stack[currentPtr];
4963         assert debugOnlyClearLastStackSlot();
4964         currentPtr--;
4965         elementPopped(node.ns, node.popName, node.node);
4966         node.release(this);
4967     }
4968 
popForeign(int origPos, int eltPos)4969     private void popForeign(int origPos, int eltPos) throws SAXException {
4970         StackNode<T> node = stack[currentPtr];
4971         if (origPos != currentPtr || eltPos != currentPtr) {
4972             markMalformedIfScript(node.node);
4973         }
4974         assert debugOnlyClearLastStackSlot();
4975         currentPtr--;
4976         elementPopped(node.ns, node.popName, node.node);
4977         node.release(this);
4978     }
4979 
silentPop()4980     private void silentPop() throws SAXException {
4981         StackNode<T> node = stack[currentPtr];
4982         assert debugOnlyClearLastStackSlot();
4983         currentPtr--;
4984         node.release(this);
4985     }
4986 
popOnEof()4987     private void popOnEof() throws SAXException {
4988         StackNode<T> node = stack[currentPtr];
4989         assert debugOnlyClearLastStackSlot();
4990         currentPtr--;
4991         markMalformedIfScript(node.node);
4992         elementPopped(node.ns, node.popName, node.node);
4993         node.release(this);
4994     }
4995 
4996     // [NOCPP[
checkAttributes(HtmlAttributes attributes, @NsUri String ns)4997     private void checkAttributes(HtmlAttributes attributes, @NsUri String ns)
4998             throws SAXException {
4999         if (errorHandler != null) {
5000             int len = attributes.getXmlnsLength();
5001             for (int i = 0; i < len; i++) {
5002                 AttributeName name = attributes.getXmlnsAttributeName(i);
5003                 if (name == AttributeName.XMLNS) {
5004                     String xmlns = attributes.getXmlnsValue(i);
5005                     if (!ns.equals(xmlns)) {
5006                         err("Bad value \u201C"
5007                                 + xmlns
5008                                 + "\u201D for the attribute \u201Cxmlns\u201D (only \u201C"
5009                                 + ns + "\u201D permitted here).");
5010                         switch (namePolicy) {
5011                             case ALTER_INFOSET:
5012                                 // fall through
5013                             case ALLOW:
5014                                 warn("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0.");
5015                                 break;
5016                             case FATAL:
5017                                 fatal("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0.");
5018                                 break;
5019                         }
5020                     }
5021                 } else if (ns != "http://www.w3.org/1999/xhtml"
5022                         && name == AttributeName.XMLNS_XLINK) {
5023                     String xmlns = attributes.getXmlnsValue(i);
5024                     if (!"http://www.w3.org/1999/xlink".equals(xmlns)) {
5025                         err("Bad value \u201C"
5026                                 + xmlns
5027                                 + "\u201D for the attribute \u201Cxmlns:link\u201D (only \u201Chttp://www.w3.org/1999/xlink\u201D permitted here).");
5028                         switch (namePolicy) {
5029                             case ALTER_INFOSET:
5030                                 // fall through
5031                             case ALLOW:
5032                                 warn("Attribute \u201Cxmlns:xlink\u201D with a value other than \u201Chttp://www.w3.org/1999/xlink\u201D is not serializable as XML 1.0 without changing document semantics.");
5033                                 break;
5034                             case FATAL:
5035                                 fatal("Attribute \u201Cxmlns:xlink\u201D with a value other than \u201Chttp://www.w3.org/1999/xlink\u201D is not serializable as XML 1.0 without changing document semantics.");
5036                                 break;
5037                         }
5038                     }
5039                 } else {
5040                     err("Attribute \u201C" + attributes.getXmlnsLocalName(i)
5041                             + "\u201D not allowed here.");
5042                     switch (namePolicy) {
5043                         case ALTER_INFOSET:
5044                             // fall through
5045                         case ALLOW:
5046                             warn("Attribute with the local name \u201C"
5047                                     + attributes.getXmlnsLocalName(i)
5048                                     + "\u201D is not serializable as XML 1.0.");
5049                             break;
5050                         case FATAL:
5051                             fatal("Attribute with the local name \u201C"
5052                                     + attributes.getXmlnsLocalName(i)
5053                                     + "\u201D is not serializable as XML 1.0.");
5054                             break;
5055                     }
5056                 }
5057             }
5058         }
5059         attributes.processNonNcNames(this, namePolicy);
5060     }
5061 
checkPopName(@ocal String name)5062     private String checkPopName(@Local String name) throws SAXException {
5063         if (NCName.isNCName(name)) {
5064             return name;
5065         } else {
5066             switch (namePolicy) {
5067                 case ALLOW:
5068                     warn("Element name \u201C" + name
5069                             + "\u201D cannot be represented as XML 1.0.");
5070                     return name;
5071                 case ALTER_INFOSET:
5072                     warn("Element name \u201C" + name
5073                             + "\u201D cannot be represented as XML 1.0.");
5074                     return NCName.escapeName(name);
5075                 case FATAL:
5076                     fatal("Element name \u201C" + name
5077                             + "\u201D cannot be represented as XML 1.0.");
5078             }
5079         }
5080         return null; // keep compiler happy
5081     }
5082 
5083     // ]NOCPP]
5084 
appendHtmlElementToDocumentAndPush(HtmlAttributes attributes)5085     private void appendHtmlElementToDocumentAndPush(HtmlAttributes attributes)
5086             throws SAXException {
5087         // [NOCPP[
5088         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5089         // ]NOCPP]
5090         T elt = createHtmlElementSetAsRoot(attributes);
5091         StackNode<T> node = createStackNode(ElementName.HTML,
5092                 elt
5093                 // [NOCPP[
5094                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5095         // ]NOCPP]
5096         );
5097         push(node);
5098     }
5099 
appendHtmlElementToDocumentAndPush()5100     private void appendHtmlElementToDocumentAndPush() throws SAXException {
5101         appendHtmlElementToDocumentAndPush(tokenizer.emptyAttributes());
5102     }
5103 
appendToCurrentNodeAndPushHeadElement(HtmlAttributes attributes)5104     private void appendToCurrentNodeAndPushHeadElement(HtmlAttributes attributes)
5105             throws SAXException {
5106         // [NOCPP[
5107         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5108         // ]NOCPP]
5109         T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5110         T elt = createElement("http://www.w3.org/1999/xhtml", "head", attributes, currentNode
5111                 /*
5112                  * head uses NS_NewHTMLSharedElement creator
5113                  */
5114                 // CPPONLY: , htmlCreator(NS_NewHTMLSharedElement)
5115                 );
5116         appendElement(elt, currentNode);
5117         headPointer = elt;
5118         StackNode<T> node = createStackNode(ElementName.HEAD,
5119                 elt
5120                 // [NOCPP[
5121                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5122         // ]NOCPP]
5123         );
5124         push(node);
5125     }
5126 
appendToCurrentNodeAndPushBodyElement(HtmlAttributes attributes)5127     private void appendToCurrentNodeAndPushBodyElement(HtmlAttributes attributes)
5128             throws SAXException {
5129         appendToCurrentNodeAndPushElement(ElementName.BODY,
5130                 attributes);
5131     }
5132 
appendToCurrentNodeAndPushBodyElement()5133     private void appendToCurrentNodeAndPushBodyElement() throws SAXException {
5134         appendToCurrentNodeAndPushBodyElement(tokenizer.emptyAttributes());
5135     }
5136 
appendToCurrentNodeAndPushFormElementMayFoster( HtmlAttributes attributes)5137     private void appendToCurrentNodeAndPushFormElementMayFoster(
5138             HtmlAttributes attributes) throws SAXException {
5139         // [NOCPP[
5140         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5141         // ]NOCPP]
5142 
5143         T elt;
5144         StackNode<T> current = stack[currentPtr];
5145         if (current.isFosterParenting()) {
5146             fatal();
5147             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", "form", attributes
5148                     // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
5149                     );
5150         } else {
5151             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5152             elt = createElement("http://www.w3.org/1999/xhtml", "form", attributes, currentNode
5153                     // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
5154                     );
5155             appendElement(elt, currentNode);
5156         }
5157 
5158         if (!isTemplateContents()) {
5159             formPointer = elt;
5160         }
5161 
5162         StackNode<T> node = createStackNode(ElementName.FORM,
5163                 elt
5164                 // [NOCPP[
5165                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5166                 // ]NOCPP]
5167         );
5168         push(node);
5169     }
5170 
appendToCurrentNodeAndPushFormattingElementMayFoster( ElementName elementName, HtmlAttributes attributes)5171     private void appendToCurrentNodeAndPushFormattingElementMayFoster(
5172             ElementName elementName, HtmlAttributes attributes)
5173             throws SAXException {
5174         // [NOCPP[
5175         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5176         // ]NOCPP]
5177         // This method can't be called for custom elements
5178         HtmlAttributes clone = attributes.cloneAttributes();
5179         // Attributes must not be read after calling createElement, because
5180         // createElement may delete attributes in C++.
5181         T elt;
5182         StackNode<T> current = stack[currentPtr];
5183         if (current.isFosterParenting()) {
5184             fatal();
5185             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes
5186                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5187                     );
5188         } else {
5189             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5190             elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode
5191                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5192                     );
5193             appendElement(elt, currentNode);
5194         }
5195         StackNode<T> node = createStackNode(elementName, elt, clone
5196                 // [NOCPP[
5197                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5198         // ]NOCPP]
5199         );
5200         push(node);
5201         append(node);
5202         node.retain(); // append doesn't retain itself
5203     }
5204 
appendToCurrentNodeAndPushElement(ElementName elementName, HtmlAttributes attributes)5205     private void appendToCurrentNodeAndPushElement(ElementName elementName,
5206             HtmlAttributes attributes)
5207             throws SAXException {
5208         // [NOCPP[
5209         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5210         // ]NOCPP]
5211         // This method can't be called for custom elements
5212         T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5213         T elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode
5214                 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5215                 );
5216         appendElement(elt, currentNode);
5217         if (ElementName.TEMPLATE == elementName) {
5218             elt = getDocumentFragmentForTemplate(elt);
5219         }
5220         StackNode<T> node = createStackNode(elementName, elt
5221                 // [NOCPP[
5222                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5223         // ]NOCPP]
5224         );
5225         push(node);
5226     }
5227 
appendToCurrentNodeAndPushElementMayFoster(ElementName elementName, HtmlAttributes attributes)5228     private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
5229             HtmlAttributes attributes)
5230             throws SAXException {
5231         @Local String popName = elementName.getName();
5232         // [NOCPP[
5233         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5234         if (!elementName.isInterned()) {
5235             popName = checkPopName(popName);
5236         }
5237         // ]NOCPP]
5238         T elt;
5239         StackNode<T> current = stack[currentPtr];
5240         if (current.isFosterParenting()) {
5241             fatal();
5242             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", popName, attributes
5243                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5244                     );
5245         } else {
5246             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5247             elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, currentNode
5248                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5249                     );
5250             appendElement(elt, currentNode);
5251         }
5252         StackNode<T> node = createStackNode(elementName, elt, popName
5253                 // [NOCPP[
5254                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5255         // ]NOCPP]
5256         );
5257         push(node);
5258     }
5259 
appendToCurrentNodeAndPushElementMayFosterMathML( ElementName elementName, HtmlAttributes attributes)5260     private void appendToCurrentNodeAndPushElementMayFosterMathML(
5261             ElementName elementName, HtmlAttributes attributes)
5262             throws SAXException {
5263         @Local String popName = elementName.getName();
5264         // [NOCPP[
5265         checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML");
5266         if (!elementName.isInterned()) {
5267             popName = checkPopName(popName);
5268         }
5269         // ]NOCPP]
5270         boolean markAsHtmlIntegrationPoint = false;
5271         if (ElementName.ANNOTATION_XML == elementName
5272                 && annotationXmlEncodingPermitsHtml(attributes)) {
5273             markAsHtmlIntegrationPoint = true;
5274         }
5275         // Attributes must not be read after calling createElement(), since
5276         // createElement may delete the object in C++.
5277         T elt;
5278         StackNode<T> current = stack[currentPtr];
5279         if (current.isFosterParenting()) {
5280             fatal();
5281             elt = createAndInsertFosterParentedElement("http://www.w3.org/1998/Math/MathML", popName, attributes
5282                     // CPPONLY: , htmlCreator(null)
5283                     );
5284         } else {
5285             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5286             elt  = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, currentNode
5287                     // CPPONLY: , htmlCreator(null)
5288                     );
5289             appendElement(elt, currentNode);
5290         }
5291         StackNode<T> node = createStackNode(elementName, elt, popName,
5292                 markAsHtmlIntegrationPoint
5293                 // [NOCPP[
5294                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5295         // ]NOCPP]
5296         );
5297         push(node);
5298     }
5299 
5300     // [NOCPP[
getDocumentFragmentForTemplate(T template)5301     T getDocumentFragmentForTemplate(T template) {
5302         return template;
5303     }
5304 
getFormPointerForContext(T context)5305     T getFormPointerForContext(T context) {
5306         return null;
5307     }
5308     // ]NOCPP]
5309 
annotationXmlEncodingPermitsHtml(HtmlAttributes attributes)5310     private boolean annotationXmlEncodingPermitsHtml(HtmlAttributes attributes) {
5311         String encoding = attributes.getValue(AttributeName.ENCODING);
5312         if (encoding == null) {
5313             return false;
5314         }
5315         return Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
5316                 "application/xhtml+xml", encoding)
5317                 || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
5318                         "text/html", encoding);
5319     }
5320 
appendToCurrentNodeAndPushElementMayFosterSVG( ElementName elementName, HtmlAttributes attributes)5321     private void appendToCurrentNodeAndPushElementMayFosterSVG(
5322             ElementName elementName, HtmlAttributes attributes)
5323             throws SAXException {
5324         @Local String popName = elementName.getCamelCaseName();
5325         // [NOCPP[
5326         checkAttributes(attributes, "http://www.w3.org/2000/svg");
5327         if (!elementName.isInterned()) {
5328             popName = checkPopName(popName);
5329         }
5330         // ]NOCPP]
5331         T elt;
5332         StackNode<T> current = stack[currentPtr];
5333         if (current.isFosterParenting()) {
5334             fatal();
5335             elt = createAndInsertFosterParentedElement("http://www.w3.org/2000/svg", popName, attributes
5336                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
5337                     );
5338         } else {
5339             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5340             elt = createElement("http://www.w3.org/2000/svg", popName, attributes, currentNode
5341                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
5342                     );
5343             appendElement(elt, currentNode);
5344         }
5345         StackNode<T> node = createStackNode(elementName, popName, elt
5346                 // [NOCPP[
5347                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5348         // ]NOCPP]
5349         );
5350         push(node);
5351     }
5352 
appendToCurrentNodeAndPushElementMayFoster(ElementName elementName, HtmlAttributes attributes, T form)5353     private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
5354             HtmlAttributes attributes, T form)
5355             throws SAXException {
5356         // [NOCPP[
5357         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5358         // ]NOCPP]
5359         // Can't be called for custom elements
5360         T elt;
5361         T formOwner = form == null || fragment || isTemplateContents() ? null : form;
5362         StackNode<T> current = stack[currentPtr];
5363         if (current.isFosterParenting()) {
5364             fatal();
5365             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", elementName.getName(),
5366                     attributes, formOwner
5367                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5368                     );
5369         } else {
5370             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5371             elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(),
5372                     attributes, formOwner, currentNode
5373                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5374                     );
5375             appendElement(elt, currentNode);
5376         }
5377         StackNode<T> node = createStackNode(elementName, elt
5378                 // [NOCPP[
5379                 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5380         // ]NOCPP]
5381         );
5382         push(node);
5383     }
5384 
appendVoidElementToCurrentMayFoster( ElementName elementName, HtmlAttributes attributes, T form)5385     private void appendVoidElementToCurrentMayFoster(
5386             ElementName elementName, HtmlAttributes attributes, T form) throws SAXException {
5387         @Local String name = elementName.getName();
5388         // [NOCPP[
5389         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5390         // ]NOCPP]
5391         // Can't be called for custom elements
5392         T elt;
5393         T formOwner = form == null || fragment || isTemplateContents() ? null : form;
5394         StackNode<T> current = stack[currentPtr];
5395         if (current.isFosterParenting()) {
5396             fatal();
5397             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", name,
5398                     attributes, formOwner
5399                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5400                     );
5401         } else {
5402             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5403             elt = createElement("http://www.w3.org/1999/xhtml", name,
5404                     attributes, formOwner, currentNode
5405                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5406                     );
5407             appendElement(elt, currentNode);
5408         }
5409         elementPushed("http://www.w3.org/1999/xhtml", name, elt);
5410         elementPopped("http://www.w3.org/1999/xhtml", name, elt);
5411     }
5412 
appendVoidElementToCurrentMayFoster( ElementName elementName, HtmlAttributes attributes)5413     private void appendVoidElementToCurrentMayFoster(
5414             ElementName elementName, HtmlAttributes attributes)
5415             throws SAXException {
5416         @Local String popName = elementName.getName();
5417         // [NOCPP[
5418         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5419         if (!elementName.isInterned()) {
5420             popName = checkPopName(popName);
5421         }
5422         // ]NOCPP]
5423         T elt;
5424         StackNode<T> current = stack[currentPtr];
5425         if (current.isFosterParenting()) {
5426             fatal();
5427             elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", popName, attributes
5428                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5429                     );
5430         } else {
5431             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5432             elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, currentNode
5433                     // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5434                     );
5435             appendElement(elt, currentNode);
5436         }
5437         elementPushed("http://www.w3.org/1999/xhtml", popName, elt);
5438         elementPopped("http://www.w3.org/1999/xhtml", popName, elt);
5439     }
5440 
appendVoidElementToCurrentMayFosterSVG( ElementName elementName, HtmlAttributes attributes)5441     private void appendVoidElementToCurrentMayFosterSVG(
5442             ElementName elementName, HtmlAttributes attributes)
5443             throws SAXException {
5444         @Local String popName = elementName.getCamelCaseName();
5445         // [NOCPP[
5446         checkAttributes(attributes, "http://www.w3.org/2000/svg");
5447         if (!elementName.isInterned()) {
5448             popName = checkPopName(popName);
5449         }
5450         // ]NOCPP]
5451         T elt;
5452         StackNode<T> current = stack[currentPtr];
5453         if (current.isFosterParenting()) {
5454             fatal();
5455             elt = createAndInsertFosterParentedElement("http://www.w3.org/2000/svg", popName, attributes
5456                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
5457                     );
5458         } else {
5459             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5460             elt = createElement("http://www.w3.org/2000/svg", popName, attributes, currentNode
5461                     // CPPONLY: , svgCreator(elementName.getSvgCreator())
5462                     );
5463             appendElement(elt, currentNode);
5464         }
5465         elementPushed("http://www.w3.org/2000/svg", popName, elt);
5466         elementPopped("http://www.w3.org/2000/svg", popName, elt);
5467     }
5468 
appendVoidElementToCurrentMayFosterMathML( ElementName elementName, HtmlAttributes attributes)5469     private void appendVoidElementToCurrentMayFosterMathML(
5470             ElementName elementName, HtmlAttributes attributes)
5471             throws SAXException {
5472         @Local String popName = elementName.getName();
5473         // [NOCPP[
5474         checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML");
5475         if (!elementName.isInterned()) {
5476             popName = checkPopName(popName);
5477         }
5478         // ]NOCPP]
5479         T elt;
5480         StackNode<T> current = stack[currentPtr];
5481         if (current.isFosterParenting()) {
5482             fatal();
5483             elt = createAndInsertFosterParentedElement("http://www.w3.org/1998/Math/MathML", popName, attributes
5484                     // CPPONLY: , htmlCreator(null)
5485                     );
5486         } else {
5487             T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5488             elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, currentNode
5489                     // CPPONLY: , htmlCreator(null)
5490                     );
5491             appendElement(elt, currentNode);
5492         }
5493         elementPushed("http://www.w3.org/1998/Math/MathML", popName, elt);
5494         elementPopped("http://www.w3.org/1998/Math/MathML", popName, elt);
5495     }
5496 
appendVoidInputToCurrent(HtmlAttributes attributes, T form)5497     private void appendVoidInputToCurrent(HtmlAttributes attributes, T form) throws SAXException {
5498         // [NOCPP[
5499         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5500         // ]NOCPP]
5501         // Can't be called for custom elements
5502         T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5503         T elt = createElement("http://www.w3.org/1999/xhtml", "input", attributes,
5504                 form == null || fragment || isTemplateContents() ? null : form, currentNode
5505                         // CPPONLY: , htmlCreator(NS_NewHTMLInputElement)
5506                         );
5507         appendElement(elt, currentNode);
5508         elementPushed("http://www.w3.org/1999/xhtml", "input", elt);
5509         elementPopped("http://www.w3.org/1999/xhtml", "input", elt);
5510     }
5511 
appendVoidFormToCurrent(HtmlAttributes attributes)5512     private void appendVoidFormToCurrent(HtmlAttributes attributes) throws SAXException {
5513         // [NOCPP[
5514         checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5515         // ]NOCPP]
5516         T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5517         T elt = createElement("http://www.w3.org/1999/xhtml", "form",
5518                 attributes, currentNode
5519                 // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
5520                 );
5521         formPointer = elt;
5522         // ownership transferred to form pointer
5523         appendElement(elt, currentNode);
5524         elementPushed("http://www.w3.org/1999/xhtml", "form", elt);
5525         elementPopped("http://www.w3.org/1999/xhtml", "form", elt);
5526     }
5527 
5528     // [NOCPP[
5529 
accumulateCharactersForced(@onst @oLength char[] buf, int start, int length)5530     private final void accumulateCharactersForced(@Const @NoLength char[] buf,
5531             int start, int length) throws SAXException {
5532         System.arraycopy(buf, start, charBuffer, charBufferLen, length);
5533         charBufferLen += length;
5534     }
5535 
ensureBufferSpace(int inputLength)5536     @Override public void ensureBufferSpace(int inputLength)
5537             throws SAXException {
5538         // TODO: Unify Tokenizer.strBuf and TreeBuilder.charBuffer so that
5539         // this method becomes unnecessary.
5540         int worstCase = charBufferLen + inputLength;
5541         if (charBuffer == null) {
5542             // Add an arbitrary small value to avoid immediate reallocation
5543             // once there are a few characters in the buffer.
5544             charBuffer = new char[worstCase + 128];
5545         } else if (worstCase > charBuffer.length) {
5546             // HotSpot reportedly allocates memory with 8-byte accuracy, so
5547             // there's no point in trying to do math here to avoid slop.
5548             // Maybe we should add some small constant to worstCase here
5549             // but not doing that without profiling. In C++ with jemalloc,
5550             // the corresponding method should do math to round up here
5551             // to avoid slop.
5552             char[] newBuf = new char[worstCase];
5553             System.arraycopy(charBuffer, 0, newBuf, 0, charBufferLen);
5554             charBuffer = newBuf;
5555         }
5556     }
5557 
5558     // ]NOCPP]
5559 
accumulateCharacters(@onst @oLength char[] buf, int start, int length)5560     protected void accumulateCharacters(@Const @NoLength char[] buf, int start,
5561             int length) throws SAXException {
5562         appendCharacters(stack[currentPtr].node, buf, start, length);
5563     }
5564 
5565     // ------------------------------- //
5566 
requestSuspension()5567     protected final void requestSuspension() {
5568         tokenizer.requestSuspension();
5569     }
5570 
5571     protected abstract T createElement(@NsUri String ns, @Local String name,
5572             HtmlAttributes attributes, T intendedParent
5573             // CPPONLY: , @Creator Object creator
5574             ) throws SAXException;
5575 
createElement(@sUri String ns, @Local String name, HtmlAttributes attributes, T form, T intendedParent )5576     protected T createElement(@NsUri String ns, @Local String name,
5577             HtmlAttributes attributes, T form, T intendedParent
5578             // CPPONLY: , @Creator Object creator
5579             ) throws SAXException {
5580         return createElement("http://www.w3.org/1999/xhtml", name, attributes, intendedParent
5581                 // CPPONLY: , creator
5582                 );
5583     }
5584 
5585     protected abstract T createHtmlElementSetAsRoot(HtmlAttributes attributes)
5586             throws SAXException;
5587 
5588     protected abstract void detachFromParent(T element) throws SAXException;
5589 
5590     protected abstract boolean hasChildren(T element) throws SAXException;
5591 
5592     protected abstract void appendElement(T child, T newParent)
5593             throws SAXException;
5594 
5595     protected abstract void appendChildrenToNewParent(T oldParent, T newParent)
5596             throws SAXException;
5597 
5598     protected abstract void insertFosterParentedChild(T child, T table,
5599             T stackParent) throws SAXException;
5600 
5601     // We don't generate CPP code for this method because it is not used in generated CPP
5602     // code. Instead, the form owner version of this method is called with a null form owner.
5603     // [NOCPP[
5604 
5605     protected abstract T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
5606             HtmlAttributes attributes, T table, T stackParent) throws SAXException;
5607 
5608     // ]NOCPP]
5609 
createAndInsertFosterParentedElement(@sUri String ns, @Local String name, HtmlAttributes attributes, T form, T table, T stackParent )5610     protected T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
5611             HtmlAttributes attributes, T form, T table, T stackParent
5612             // CPPONLY: , @Creator Object creator
5613             ) throws SAXException {
5614         return createAndInsertFosterParentedElement(ns, name, attributes, table, stackParent);
5615     };
5616 
5617     protected abstract void insertFosterParentedCharacters(
5618             @NoLength char[] buf, int start, int length, T table, T stackParent)
5619             throws SAXException;
5620 
5621     protected abstract void appendCharacters(T parent, @NoLength char[] buf,
5622             int start, int length) throws SAXException;
5623 
5624     protected abstract void appendComment(T parent, @NoLength char[] buf,
5625             int start, int length) throws SAXException;
5626 
5627     protected abstract void appendCommentToDocument(@NoLength char[] buf,
5628             int start, int length) throws SAXException;
5629 
5630     protected abstract void addAttributesToElement(T element,
5631             HtmlAttributes attributes) throws SAXException;
5632 
markMalformedIfScript(T elt)5633     protected void markMalformedIfScript(T elt) throws SAXException {
5634 
5635     }
5636 
start(boolean fragmentMode)5637     protected void start(boolean fragmentMode) throws SAXException {
5638 
5639     }
5640 
end()5641     protected void end() throws SAXException {
5642 
5643     }
5644 
appendDoctypeToDocument(@ocal String name, String publicIdentifier, String systemIdentifier)5645     protected void appendDoctypeToDocument(@Local String name,
5646             String publicIdentifier, String systemIdentifier)
5647             throws SAXException {
5648 
5649     }
5650 
elementPushed(@sUri String ns, @Local String name, T node)5651     protected void elementPushed(@NsUri String ns, @Local String name, T node)
5652             throws SAXException {
5653 
5654     }
5655 
elementPopped(@sUri String ns, @Local String name, T node)5656     protected void elementPopped(@NsUri String ns, @Local String name, T node)
5657             throws SAXException {
5658 
5659     }
5660 
5661     // [NOCPP[
5662 
documentMode(DocumentMode m, String publicIdentifier, String systemIdentifier)5663     protected void documentMode(DocumentMode m, String publicIdentifier,
5664             String systemIdentifier)
5665             throws SAXException {
5666 
5667     }
5668 
5669     /**
5670      * @see nu.validator.htmlparser.common.TokenHandler#wantsComments()
5671      */
wantsComments()5672     public boolean wantsComments() {
5673         return wantingComments;
5674     }
5675 
setIgnoringComments(boolean ignoreComments)5676     public void setIgnoringComments(boolean ignoreComments) {
5677         wantingComments = !ignoreComments;
5678     }
5679 
5680     /**
5681      * Sets the errorHandler.
5682      *
5683      * @param errorHandler
5684      *            the errorHandler to set
5685      */
setErrorHandler(ErrorHandler errorHandler)5686     public final void setErrorHandler(ErrorHandler errorHandler) {
5687         this.errorHandler = errorHandler;
5688     }
5689 
5690     /**
5691      * Returns the errorHandler.
5692      *
5693      * @return the errorHandler
5694      */
getErrorHandler()5695     public ErrorHandler getErrorHandler() {
5696         return errorHandler;
5697     }
5698 
5699     /**
5700      * The argument MUST be an interned string or <code>null</code>.
5701      *
5702      * @param context
5703      */
setFragmentContext(@ocal String context)5704     public final void setFragmentContext(@Local String context) {
5705         this.contextName = context;
5706         this.contextNamespace = "http://www.w3.org/1999/xhtml";
5707         this.contextNode = null;
5708         this.fragment = (contextName != null);
5709         this.quirks = false;
5710     }
5711 
5712     // ]NOCPP]
5713 
5714     /**
5715      * @see nu.validator.htmlparser.common.TokenHandler#cdataSectionAllowed()
5716      */
cdataSectionAllowed()5717     @Inline public boolean cdataSectionAllowed() throws SAXException {
5718         return isInForeign();
5719     }
5720 
isInForeign()5721     private boolean isInForeign() {
5722         return currentPtr >= 0
5723                 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml";
5724     }
5725 
isInForeignButNotHtmlOrMathTextIntegrationPoint()5726     private boolean isInForeignButNotHtmlOrMathTextIntegrationPoint() {
5727         if (currentPtr < 0) {
5728             return false;
5729         }
5730         return !isSpecialParentInForeign(stack[currentPtr]);
5731     }
5732 
5733     /**
5734      * The argument MUST be an interned string or <code>null</code>.
5735      *
5736      * @param context
5737      */
setFragmentContext(@ocal String context, @NsUri String ns, T node, boolean quirks)5738     public final void setFragmentContext(@Local String context,
5739             @NsUri String ns, T node, boolean quirks) {
5740         // [NOCPP[
5741         if (!((context == null && ns == null)
5742                 || "http://www.w3.org/1999/xhtml" == ns
5743                 || "http://www.w3.org/2000/svg" == ns || "http://www.w3.org/1998/Math/MathML" == ns)) {
5744             throw new IllegalArgumentException(
5745                     "The namespace must be the HTML, SVG or MathML namespace (or null when the local name is null). Got: "
5746                             + ns);
5747         }
5748         // ]NOCPP]
5749         this.contextName = context;
5750         this.contextNamespace = ns;
5751         this.contextNode = node;
5752         this.fragment = (contextName != null);
5753         this.quirks = quirks;
5754     }
5755 
currentNode()5756     protected final T currentNode() {
5757         return stack[currentPtr].node;
5758     }
5759 
5760     /**
5761      * Returns the scriptingEnabled.
5762      *
5763      * @return the scriptingEnabled
5764      */
isScriptingEnabled()5765     public boolean isScriptingEnabled() {
5766         return scriptingEnabled;
5767     }
5768 
5769     /**
5770      * Sets the scriptingEnabled.
5771      *
5772      * @param scriptingEnabled
5773      *            the scriptingEnabled to set
5774      */
setScriptingEnabled(boolean scriptingEnabled)5775     public void setScriptingEnabled(boolean scriptingEnabled) {
5776         this.scriptingEnabled = scriptingEnabled;
5777     }
5778 
setIsSrcdocDocument(boolean isSrcdocDocument)5779     public void setIsSrcdocDocument(boolean isSrcdocDocument) {
5780         this.isSrcdocDocument = isSrcdocDocument;
5781     }
5782 
5783     // [NOCPP[
5784 
setNamePolicy(XmlViolationPolicy namePolicy)5785     public void setNamePolicy(XmlViolationPolicy namePolicy) {
5786         this.namePolicy = namePolicy;
5787     }
5788 
5789     /**
5790      * Sets the documentModeHandler.
5791      *
5792      * @param documentModeHandler
5793      *            the documentModeHandler to set
5794      */
setDocumentModeHandler(DocumentModeHandler documentModeHandler)5795     public void setDocumentModeHandler(DocumentModeHandler documentModeHandler) {
5796         this.documentModeHandler = documentModeHandler;
5797     }
5798 
5799     /**
5800      * Sets the reportingDoctype.
5801      *
5802      * @param reportingDoctype
5803      *            the reportingDoctype to set
5804      */
setReportingDoctype(boolean reportingDoctype)5805     public void setReportingDoctype(boolean reportingDoctype) {
5806         this.reportingDoctype = reportingDoctype;
5807     }
5808 
5809     // ]NOCPP]
5810 
5811     /**
5812      * Flushes the pending characters. Public for document.write use cases only.
5813      * @throws SAXException
5814      */
flushCharacters()5815     public final void flushCharacters() throws SAXException {
5816         if (charBufferLen > 0) {
5817             if ((mode == IN_TABLE || mode == IN_TABLE_BODY || mode == IN_ROW)
5818                     && charBufferContainsNonWhitespace()) {
5819                 errNonSpaceInTable();
5820                 reconstructTheActiveFormattingElements();
5821                 if (!stack[currentPtr].isFosterParenting()) {
5822                     // reconstructing gave us a new current node
5823                     appendCharacters(currentNode(), charBuffer, 0,
5824                             charBufferLen);
5825                     charBufferLen = 0;
5826                     return;
5827                 }
5828 
5829                 int tablePos = findLastOrRoot(TreeBuilder.TABLE);
5830                 int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE);
5831 
5832                 if (templatePos >= tablePos) {
5833                     appendCharacters(stack[templatePos].node, charBuffer, 0, charBufferLen);
5834                     charBufferLen = 0;
5835                     return;
5836                 }
5837 
5838                 StackNode<T> tableElt = stack[tablePos];
5839                 insertFosterParentedCharacters(charBuffer, 0, charBufferLen,
5840                         tableElt.node, stack[tablePos - 1].node);
5841                 charBufferLen = 0;
5842                 return;
5843             }
5844             appendCharacters(currentNode(), charBuffer, 0, charBufferLen);
5845             charBufferLen = 0;
5846         }
5847     }
5848 
charBufferContainsNonWhitespace()5849     private boolean charBufferContainsNonWhitespace() {
5850         for (int i = 0; i < charBufferLen; i++) {
5851             switch (charBuffer[i]) {
5852                 case ' ':
5853                 case '\t':
5854                 case '\n':
5855                 case '\r':
5856                 case '\u000C':
5857                     continue;
5858                 default:
5859                     return true;
5860             }
5861         }
5862         return false;
5863     }
5864 
5865     /**
5866      * Creates a comparable snapshot of the tree builder state. Snapshot
5867      * creation is only supported immediately after a script end tag has been
5868      * processed. In C++ the caller is responsible for calling
5869      * <code>delete</code> on the returned object.
5870      *
5871      * @return a snapshot.
5872      * @throws SAXException
5873      */
newSnapshot()5874     @SuppressWarnings("unchecked") public TreeBuilderState<T> newSnapshot()
5875             throws SAXException {
5876         StackNode<T>[] listCopy = new StackNode[listPtr + 1];
5877         for (int i = 0; i < listCopy.length; i++) {
5878             StackNode<T> node = listOfActiveFormattingElements[i];
5879             if (node != null) {
5880                 StackNode<T> newNode = new StackNode<T>(-1);
5881                 newNode.setValues(node.getFlags(), node.ns,
5882                         node.name, node.node, node.popName,
5883                         node.attributes.cloneAttributes()
5884                         // CPPONLY: , node.getHtmlCreator()
5885                         // [NOCPP[
5886                         , node.getLocator()
5887                         // ]NOCPP]
5888                 );
5889                 listCopy[i] = newNode;
5890             } else {
5891                 listCopy[i] = null;
5892             }
5893         }
5894         StackNode<T>[] stackCopy = new StackNode[currentPtr + 1];
5895         for (int i = 0; i < stackCopy.length; i++) {
5896             StackNode<T> node = stack[i];
5897             int listIndex = findInListOfActiveFormattingElements(node);
5898             if (listIndex == -1) {
5899                 StackNode<T> newNode = new StackNode<T>(-1);
5900                 newNode.setValues(node.getFlags(), node.ns,
5901                         node.name, node.node, node.popName,
5902                         null
5903                         // CPPONLY: , node.getHtmlCreator()
5904                         // [NOCPP[
5905                         , node.getLocator()
5906                         // ]NOCPP]
5907                 );
5908                 stackCopy[i] = newNode;
5909             } else {
5910                 stackCopy[i] = listCopy[listIndex];
5911                 stackCopy[i].retain();
5912             }
5913         }
5914         int[] templateModeStackCopy = new int[templateModePtr + 1];
5915         System.arraycopy(templateModeStack, 0, templateModeStackCopy, 0,
5916                 templateModeStackCopy.length);
5917         return new StateSnapshot<T>(stackCopy, listCopy, templateModeStackCopy, formPointer,
5918                 headPointer, mode, originalMode, framesetOk,
5919                 needToDropLF, quirks);
5920     }
5921 
snapshotMatches(TreeBuilderState<T> snapshot)5922     public boolean snapshotMatches(TreeBuilderState<T> snapshot) {
5923         StackNode<T>[] stackCopy = snapshot.getStack();
5924         int stackLen = snapshot.getStackLength();
5925         StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
5926         int listLen = snapshot.getListOfActiveFormattingElementsLength();
5927         int[] templateModeStackCopy = snapshot.getTemplateModeStack();
5928         int templateModeStackLen = snapshot.getTemplateModeStackLength();
5929 
5930         if (stackLen != currentPtr + 1
5931                 || listLen != listPtr + 1
5932                 || templateModeStackLen != templateModePtr + 1
5933                 || formPointer != snapshot.getFormPointer()
5934                 || headPointer != snapshot.getHeadPointer()
5935                 || mode != snapshot.getMode()
5936                 || originalMode != snapshot.getOriginalMode()
5937                 || framesetOk != snapshot.isFramesetOk()
5938                 || needToDropLF != snapshot.isNeedToDropLF()
5939                 || quirks != snapshot.isQuirks()) { // maybe just assert quirks
5940             return false;
5941         }
5942         for (int i = listLen - 1; i >= 0; i--) {
5943             if (listCopy[i] == null
5944                     && listOfActiveFormattingElements[i] == null) {
5945                 continue;
5946             } else if (listCopy[i] == null
5947                     || listOfActiveFormattingElements[i] == null) {
5948                 return false;
5949             }
5950             if (listCopy[i].node != listOfActiveFormattingElements[i].node) {
5951                 return false; // it's possible that this condition is overly
5952                               // strict
5953             }
5954         }
5955         for (int i = stackLen - 1; i >= 0; i--) {
5956             if (stackCopy[i].node != stack[i].node) {
5957                 return false;
5958             }
5959         }
5960         for (int i = templateModeStackLen - 1; i >=0; i--) {
5961             if (templateModeStackCopy[i] != templateModeStack[i]) {
5962                 return false;
5963             }
5964         }
5965         return true;
5966     }
5967 
loadState( TreeBuilderState<T> snapshot)5968     @SuppressWarnings("unchecked") public void loadState(
5969             TreeBuilderState<T> snapshot)
5970             throws SAXException {
5971         // CPPONLY: mCurrentHtmlScriptIsAsyncOrDefer = false;
5972         StackNode<T>[] stackCopy = snapshot.getStack();
5973         int stackLen = snapshot.getStackLength();
5974         StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
5975         int listLen = snapshot.getListOfActiveFormattingElementsLength();
5976         int[] templateModeStackCopy = snapshot.getTemplateModeStack();
5977         int templateModeStackLen = snapshot.getTemplateModeStackLength();
5978 
5979         for (int i = 0; i <= listPtr; i++) {
5980             if (listOfActiveFormattingElements[i] != null) {
5981                 listOfActiveFormattingElements[i].release(this);
5982             }
5983         }
5984         if (listOfActiveFormattingElements.length < listLen) {
5985             listOfActiveFormattingElements = new StackNode[listLen];
5986         }
5987         listPtr = listLen - 1;
5988 
5989         for (int i = 0; i <= currentPtr; i++) {
5990             stack[i].release(this);
5991         }
5992         if (stack.length < stackLen) {
5993             stack = new StackNode[stackLen];
5994         }
5995         currentPtr = stackLen - 1;
5996 
5997         if (templateModeStack.length < templateModeStackLen) {
5998             templateModeStack = new int[templateModeStackLen];
5999         }
6000         templateModePtr = templateModeStackLen - 1;
6001 
6002         for (int i = 0; i < listLen; i++) {
6003             StackNode<T> node = listCopy[i];
6004             if (node != null) {
6005                 StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
6006                         node.name, node.node,
6007                         node.popName,
6008                         node.attributes.cloneAttributes()
6009                         // CPPONLY: , node.getHtmlCreator()
6010                         // [NOCPP[
6011                         , node.getLocator()
6012                 // ]NOCPP]
6013                 );
6014                 listOfActiveFormattingElements[i] = newNode;
6015             } else {
6016                 listOfActiveFormattingElements[i] = null;
6017             }
6018         }
6019         for (int i = 0; i < stackLen; i++) {
6020             StackNode<T> node = stackCopy[i];
6021             int listIndex = findInArray(node, listCopy);
6022             if (listIndex == -1) {
6023                 StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
6024                         node.name, node.node,
6025                         node.popName,
6026                         null
6027                         // CPPONLY: , node.getHtmlCreator()
6028                         // [NOCPP[
6029                         , node.getLocator()
6030                 // ]NOCPP]
6031                 );
6032                 stack[i] = newNode;
6033             } else {
6034                 stack[i] = listOfActiveFormattingElements[listIndex];
6035                 stack[i].retain();
6036             }
6037         }
6038         System.arraycopy(templateModeStackCopy, 0, templateModeStack, 0, templateModeStackLen);
6039         formPointer = snapshot.getFormPointer();
6040         headPointer = snapshot.getHeadPointer();
6041         mode = snapshot.getMode();
6042         originalMode = snapshot.getOriginalMode();
6043         framesetOk = snapshot.isFramesetOk();
6044         needToDropLF = snapshot.isNeedToDropLF();
6045         quirks = snapshot.isQuirks();
6046     }
6047 
findInArray(StackNode<T> node, StackNode<T>[] arr)6048     private int findInArray(StackNode<T> node, StackNode<T>[] arr) {
6049         for (int i = listPtr; i >= 0; i--) {
6050             if (node == arr[i]) {
6051                 return i;
6052             }
6053         }
6054         return -1;
6055     }
6056 
6057     /**
6058      * Returns <code>stack[stackPos].node</code> if <code>stackPos</code> is
6059      * smaller than Blink's magic limit or the node at Blink's magic limit
6060      * otherwise.
6061      *
6062      * In order to get Blink-compatible handling of excessive deeply-nested
6063      * markup, this method must be used to obtain the node that is used as the
6064      * parent node of an insertion.
6065      *
6066      * Blink's magic number is 512, but our counting is off by one compared to
6067      * Blink's way of counting, so in order to get the same
6068      * externally-observable outcome, we use 511 as our magic number.
6069      *
6070      * @param stackPos the stack position to attempt to read
6071      * @return node at the position capped to Blink's magic number
6072      * @throws SAXException
6073      */
nodeFromStackWithBlinkCompat(int stackPos)6074     private T nodeFromStackWithBlinkCompat(int stackPos) throws SAXException {
6075         // Magic number if off by one relative to Blink's magic number, but the
6076         // outcome is the same, because the counting is different by one.
6077         if (stackPos > 511) {
6078             errDeepTree();
6079             return stack[511].node;
6080         }
6081         return stack[stackPos].node;
6082     }
6083     /**
6084      * @see nu.validator.htmlparser.impl.TreeBuilderState#getFormPointer()
6085      */
6086     @Override
getFormPointer()6087     public T getFormPointer() {
6088         return formPointer;
6089     }
6090 
6091     /**
6092      * Returns the headPointer.
6093      *
6094      * @return the headPointer
6095      */
6096     @Override
getHeadPointer()6097     public T getHeadPointer() {
6098         return headPointer;
6099     }
6100 
6101     /**
6102      * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElements()
6103      */
6104     @Override
getListOfActiveFormattingElements()6105     public StackNode<T>[] getListOfActiveFormattingElements() {
6106         return listOfActiveFormattingElements;
6107     }
6108 
6109     /**
6110      * @see nu.validator.htmlparser.impl.TreeBuilderState#getStack()
6111      */
6112     @Override
getStack()6113     public StackNode<T>[] getStack() {
6114         return stack;
6115     }
6116 
6117     /**
6118      * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStack()
6119      */
6120     @Override
getTemplateModeStack()6121     public int[] getTemplateModeStack() {
6122         return templateModeStack;
6123     }
6124 
6125     /**
6126      * Returns the mode.
6127      *
6128      * @return the mode
6129      */
6130     @Override
getMode()6131     public int getMode() {
6132         return mode;
6133     }
6134 
6135     /**
6136      * Returns the originalMode.
6137      *
6138      * @return the originalMode
6139      */
6140     @Override
getOriginalMode()6141     public int getOriginalMode() {
6142         return originalMode;
6143     }
6144 
6145     /**
6146      * Returns the framesetOk.
6147      *
6148      * @return the framesetOk
6149      */
6150     @Override
isFramesetOk()6151     public boolean isFramesetOk() {
6152         return framesetOk;
6153     }
6154 
6155     /**
6156      * Returns the needToDropLF.
6157      *
6158      * @return the needToDropLF
6159      */
6160     @Override
isNeedToDropLF()6161     public boolean isNeedToDropLF() {
6162         return needToDropLF;
6163     }
6164 
6165     /**
6166      * Returns the quirks.
6167      *
6168      * @return the quirks
6169      */
6170     @Override
isQuirks()6171     public boolean isQuirks() {
6172         return quirks;
6173     }
6174 
6175     /**
6176      * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElementsLength()
6177      */
6178     @Override
getListOfActiveFormattingElementsLength()6179     public int getListOfActiveFormattingElementsLength() {
6180         return listPtr + 1;
6181     }
6182 
6183     /**
6184      * @see nu.validator.htmlparser.impl.TreeBuilderState#getStackLength()
6185      */
6186     @Override
getStackLength()6187     public int getStackLength() {
6188         return currentPtr + 1;
6189     }
6190 
6191     /**
6192      * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStackLength()
6193      */
6194     @Override
getTemplateModeStackLength()6195     public int getTemplateModeStackLength() {
6196         return templateModePtr + 1;
6197     }
6198 
6199     /**
6200      * Complains about an over-deep tree. Theoretically this should just be
6201      * a warning, but in practice authors should take this as an error.
6202      *
6203      * @throws SAXException
6204      */
errDeepTree()6205     private void errDeepTree() throws SAXException {
6206         err("The document tree is more than 513 elements deep, which causes Firefox and Chrome to flatten the tree.");
6207     }
6208 
6209     /**
6210      * Reports a stray start tag.
6211      * @param name the name of the stray tag
6212      *
6213      * @throws SAXException
6214      */
errStrayStartTag(@ocal String name)6215     private void errStrayStartTag(@Local String name) throws SAXException {
6216         err("Stray start tag \u201C" + name + "\u201D.");
6217     }
6218 
6219     /**
6220      * Reports a stray end tag.
6221      * @param name the name of the stray tag
6222      *
6223      * @throws SAXException
6224      */
errStrayEndTag(@ocal String name)6225     private void errStrayEndTag(@Local String name) throws SAXException {
6226         err("Stray end tag \u201C" + name + "\u201D.");
6227     }
6228 
6229     /**
6230      * Reports a state when elements expected to be closed were not.
6231      *
6232      * @param eltPos the position of the start tag on the stack of the element
6233      * being closed.
6234      * @param name the name of the end tag
6235      *
6236      * @throws SAXException
6237      */
errUnclosedElements(int eltPos, @Local String name)6238     private void errUnclosedElements(int eltPos, @Local String name) throws SAXException {
6239         errNoCheck("End tag \u201C" + name + "\u201D seen, but there were open elements.");
6240         errListUnclosedStartTags(eltPos);
6241     }
6242 
6243     /**
6244      * Reports a state when elements expected to be closed ahead of an implied
6245      * end tag but were not.
6246      *
6247      * @param eltPos the position of the start tag on the stack of the element
6248      * being closed.
6249      * @param name the name of the end tag
6250      *
6251      * @throws SAXException
6252      */
errUnclosedElementsImplied(int eltPos, String name)6253     private void errUnclosedElementsImplied(int eltPos, String name) throws SAXException {
6254         errNoCheck("End tag \u201C" + name + "\u201D implied, but there were open elements.");
6255         errListUnclosedStartTags(eltPos);
6256     }
6257 
6258     /**
6259      * Reports a state when elements expected to be closed ahead of an implied
6260      * table cell close.
6261      *
6262      * @param eltPos the position of the start tag on the stack of the element
6263      * being closed.
6264      * @throws SAXException
6265      */
errUnclosedElementsCell(int eltPos)6266     private void errUnclosedElementsCell(int eltPos) throws SAXException {
6267         errNoCheck("A table cell was implicitly closed, but there were open elements.");
6268         errListUnclosedStartTags(eltPos);
6269     }
6270 
errStrayDoctype()6271     private void errStrayDoctype() throws SAXException {
6272         err("Stray doctype.");
6273     }
6274 
errAlmostStandardsDoctype()6275     private void errAlmostStandardsDoctype() throws SAXException {
6276         if (!isSrcdocDocument) {
6277             err("Almost standards mode doctype. Expected \u201C<!DOCTYPE html>\u201D.");
6278         }
6279     }
6280 
errQuirkyDoctype()6281     private void errQuirkyDoctype() throws SAXException {
6282         if (!isSrcdocDocument) {
6283             err("Quirky doctype. Expected \u201C<!DOCTYPE html>\u201D.");
6284         }
6285     }
6286 
errNonSpaceInTrailer()6287     private void errNonSpaceInTrailer() throws SAXException {
6288         err("Non-space character in page trailer.");
6289     }
6290 
errNonSpaceAfterFrameset()6291     private void errNonSpaceAfterFrameset() throws SAXException {
6292         err("Non-space after \u201Cframeset\u201D.");
6293     }
6294 
errNonSpaceInFrameset()6295     private void errNonSpaceInFrameset() throws SAXException {
6296         err("Non-space in \u201Cframeset\u201D.");
6297     }
6298 
errNonSpaceAfterBody()6299     private void errNonSpaceAfterBody() throws SAXException {
6300         err("Non-space character after body.");
6301     }
6302 
errNonSpaceInColgroupInFragment()6303     private void errNonSpaceInColgroupInFragment() throws SAXException {
6304         err("Non-space in \u201Ccolgroup\u201D when parsing fragment.");
6305     }
6306 
errNonSpaceInNoscriptInHead()6307     private void errNonSpaceInNoscriptInHead() throws SAXException {
6308         err("Non-space character inside \u201Cnoscript\u201D inside \u201Chead\u201D.");
6309     }
6310 
errFooBetweenHeadAndBody(@ocal String name)6311     private void errFooBetweenHeadAndBody(@Local String name) throws SAXException {
6312         if (errorHandler == null) {
6313             return;
6314         }
6315         errNoCheck("\u201C" + name + "\u201D element between \u201Chead\u201D and \u201Cbody\u201D.");
6316     }
6317 
errStartTagWithoutDoctype()6318     private void errStartTagWithoutDoctype() throws SAXException {
6319         if (!isSrcdocDocument) {
6320             err("Start tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
6321         }
6322     }
6323 
errNoSelectInTableScope()6324     private void errNoSelectInTableScope() throws SAXException {
6325         err("No \u201Cselect\u201D in table scope.");
6326     }
6327 
errStartSelectWhereEndSelectExpected()6328     private void errStartSelectWhereEndSelectExpected() throws SAXException {
6329         err("\u201Cselect\u201D start tag where end tag expected.");
6330     }
6331 
errStartTagWithSelectOpen(@ocal String name)6332     private void errStartTagWithSelectOpen(@Local String name)
6333             throws SAXException {
6334         if (errorHandler == null) {
6335             return;
6336         }
6337         errNoCheck("\u201C" + name
6338                 + "\u201D start tag with \u201Cselect\u201D open.");
6339     }
6340 
errBadStartTagInNoscriptInHead(@ocal String name)6341     private void errBadStartTagInNoscriptInHead(@Local String name) throws SAXException {
6342         if (errorHandler == null) {
6343             return;
6344         }
6345         errNoCheck("Bad start tag in \u201C" + name
6346                 + "\u201D in \u201Cnoscript\u201D in \u201Chead\u201D.");
6347     }
6348 
errImage()6349     private void errImage() throws SAXException {
6350         err("Saw a start tag \u201Cimage\u201D.");
6351     }
6352 
errFooSeenWhenFooOpen(@ocal String name)6353     private void errFooSeenWhenFooOpen(@Local String name) throws SAXException {
6354         if (errorHandler == null) {
6355             return;
6356         }
6357         errNoCheck("Start tag \u201C" + name + "\u201D seen but an element of the same type was already open.");
6358     }
6359 
errHeadingWhenHeadingOpen()6360     private void errHeadingWhenHeadingOpen() throws SAXException {
6361         err("Heading cannot be a child of another heading.");
6362     }
6363 
errFramesetStart()6364     private void errFramesetStart() throws SAXException {
6365         err("\u201Cframeset\u201D start tag seen.");
6366     }
6367 
errNoCellToClose()6368     private void errNoCellToClose() throws SAXException {
6369         err("No cell to close.");
6370     }
6371 
errStartTagInTable(@ocal String name)6372     private void errStartTagInTable(@Local String name) throws SAXException {
6373         if (errorHandler == null) {
6374             return;
6375         }
6376         errNoCheck("Start tag \u201C" + name
6377                 + "\u201D seen in \u201Ctable\u201D.");
6378     }
6379 
errFormWhenFormOpen()6380     private void errFormWhenFormOpen() throws SAXException {
6381         err("Saw a \u201Cform\u201D start tag, but there was already an active \u201Cform\u201D element. Nested forms are not allowed. Ignoring the tag.");
6382     }
6383 
errTableSeenWhileTableOpen()6384     private void errTableSeenWhileTableOpen() throws SAXException {
6385         err("Start tag for \u201Ctable\u201D seen but the previous \u201Ctable\u201D is still open.");
6386     }
6387 
errStartTagInTableBody(@ocal String name)6388     private void errStartTagInTableBody(@Local String name) throws SAXException {
6389         if (errorHandler == null) {
6390             return;
6391         }
6392         errNoCheck("\u201C" + name + "\u201D start tag in table body.");
6393     }
6394 
errEndTagSeenWithoutDoctype()6395     private void errEndTagSeenWithoutDoctype() throws SAXException {
6396         if (!isSrcdocDocument) {
6397             err("End tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
6398         }
6399     }
6400 
errEndTagAfterBody()6401     private void errEndTagAfterBody() throws SAXException {
6402         err("Saw an end tag after \u201Cbody\u201D had been closed.");
6403     }
6404 
errEndTagSeenWithSelectOpen(@ocal String name)6405     private void errEndTagSeenWithSelectOpen(@Local String name) throws SAXException {
6406         if (errorHandler == null) {
6407             return;
6408         }
6409         errNoCheck("\u201C" + name
6410                 + "\u201D end tag with \u201Cselect\u201D open.");
6411     }
6412 
errGarbageInColgroup()6413     private void errGarbageInColgroup() throws SAXException {
6414         err("Garbage in \u201Ccolgroup\u201D fragment.");
6415     }
6416 
errEndTagBr()6417     private void errEndTagBr() throws SAXException {
6418         err("End tag \u201Cbr\u201D.");
6419     }
6420 
errNoElementToCloseButEndTagSeen(@ocal String name)6421     private void errNoElementToCloseButEndTagSeen(@Local String name)
6422             throws SAXException {
6423         if (errorHandler == null) {
6424             return;
6425         }
6426         errNoCheck("No \u201C" + name + "\u201D element in scope but a \u201C"
6427                 + name + "\u201D end tag seen.");
6428     }
6429 
errHtmlStartTagInForeignContext(@ocal String name)6430     private void errHtmlStartTagInForeignContext(@Local String name)
6431             throws SAXException {
6432         if (errorHandler == null) {
6433             return;
6434         }
6435         errNoCheck("HTML start tag \u201C" + name
6436                 + "\u201D in a foreign namespace context.");
6437     }
6438 
errNoTableRowToClose()6439     private void errNoTableRowToClose() throws SAXException {
6440         err("No table row to close.");
6441     }
6442 
errNonSpaceInTable()6443     private void errNonSpaceInTable() throws SAXException {
6444         err("Misplaced non-space characters inside a table.");
6445     }
6446 
errUnclosedChildrenInRuby()6447     private void errUnclosedChildrenInRuby() throws SAXException {
6448         if (errorHandler == null) {
6449             return;
6450         }
6451         errNoCheck("Unclosed children in \u201Cruby\u201D.");
6452     }
6453 
errStartTagSeenWithoutRuby(@ocal String name)6454     private void errStartTagSeenWithoutRuby(@Local String name) throws SAXException {
6455         if (errorHandler == null) {
6456             return;
6457         }
6458         errNoCheck("Start tag \u201C"
6459                 + name
6460                 + "\u201D seen without a \u201Cruby\u201D element being open.");
6461     }
6462 
errSelfClosing()6463     private void errSelfClosing() throws SAXException {
6464         if (errorHandler == null) {
6465             return;
6466         }
6467         errNoCheck("Self-closing syntax (\u201C/>\u201D) used on a non-void HTML element. Ignoring the slash and treating as a start tag.");
6468     }
6469 
errNoCheckUnclosedElementsOnStack()6470     private void errNoCheckUnclosedElementsOnStack() throws SAXException {
6471         errNoCheck("Unclosed elements on stack.");
6472     }
6473 
errEndTagDidNotMatchCurrentOpenElement(@ocal String name, @Local String currOpenName)6474     private void errEndTagDidNotMatchCurrentOpenElement(@Local String name,
6475             @Local String currOpenName) throws SAXException {
6476         if (errorHandler == null) {
6477             return;
6478         }
6479         errNoCheck("End tag \u201C"
6480                 + name
6481                 + "\u201D did not match the name of the current open element (\u201C"
6482                 + currOpenName + "\u201D).");
6483     }
6484 
errEndTagViolatesNestingRules(@ocal String name)6485     private void errEndTagViolatesNestingRules(@Local String name) throws SAXException {
6486         if (errorHandler == null) {
6487             return;
6488         }
6489         errNoCheck("End tag \u201C" + name + "\u201D violates nesting rules.");
6490     }
6491 
errEofWithUnclosedElements()6492     private void errEofWithUnclosedElements() throws SAXException {
6493         if (errorHandler == null) {
6494             return;
6495         }
6496         errNoCheck("End of file seen and there were open elements.");
6497         // just report all remaining unclosed elements
6498         errListUnclosedStartTags(0);
6499     }
6500 
6501     /**
6502      * Reports arriving at/near end of document with unclosed elements remaining.
6503      *
6504      * @param message
6505      *            the message
6506      * @throws SAXException
6507      */
errEndWithUnclosedElements(@ocal String name)6508     private void errEndWithUnclosedElements(@Local String name) throws SAXException {
6509         if (errorHandler == null) {
6510             return;
6511         }
6512         errNoCheck("End tag for  \u201C"
6513                 + name
6514                 + "\u201D seen, but there were unclosed elements.");
6515         // just report all remaining unclosed elements
6516         errListUnclosedStartTags(0);
6517     }
6518 }
6519