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