Backed out 6 changesets (bug 1712140) for causing Linux related wpt failures in nsHtm...
[gecko.git] / parser / html / javasrc / TreeBuilder.java
blobdb5fe94743904ea78d7a274f005558c14276468a
1 /*
2 * Copyright (c) 2007 Henri Sivonen
3 * Copyright (c) 2007-2017 Mozilla Foundation
4 * Portions of comments Copyright 2004-2008 Apple Computer, Inc., Mozilla
5 * Foundation, and Opera Software ASA.
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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.
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."
36 package nu.validator.htmlparser.impl;
38 import java.util.Arrays;
39 import java.util.HashMap;
40 import java.util.Map;
42 import org.xml.sax.ErrorHandler;
43 import org.xml.sax.Locator;
44 import org.xml.sax.SAXException;
45 import org.xml.sax.SAXParseException;
47 import nu.validator.htmlparser.annotation.Auto;
48 import nu.validator.htmlparser.annotation.Const;
49 import nu.validator.htmlparser.annotation.IdType;
50 import nu.validator.htmlparser.annotation.Inline;
51 import nu.validator.htmlparser.annotation.Literal;
52 import nu.validator.htmlparser.annotation.Local;
53 import nu.validator.htmlparser.annotation.NoLength;
54 import nu.validator.htmlparser.annotation.NsUri;
55 import nu.validator.htmlparser.common.DocumentMode;
56 import nu.validator.htmlparser.common.DocumentModeHandler;
57 import nu.validator.htmlparser.common.Interner;
58 import nu.validator.htmlparser.common.TokenHandler;
59 import nu.validator.htmlparser.common.XmlViolationPolicy;
61 public abstract class TreeBuilder<T> implements TokenHandler,
62 TreeBuilderState<T> {
64 /**
65 * Array version of U+FFFD.
67 private static final @NoLength char[] REPLACEMENT_CHARACTER = { '\uFFFD' };
69 // Start dispatch groups
71 final static int OTHER = 0;
73 final static int A = 1;
75 final static int BASE = 2;
77 final static int BODY = 3;
79 final static int BR = 4;
81 final static int BUTTON = 5;
83 final static int CAPTION = 6;
85 final static int COL = 7;
87 final static int COLGROUP = 8;
89 final static int FORM = 9;
91 final static int FRAME = 10;
93 final static int FRAMESET = 11;
95 final static int IMAGE = 12;
97 final static int INPUT = 13;
99 final static int RT_OR_RP = 14;
101 final static int LI = 15;
103 final static int LINK_OR_BASEFONT_OR_BGSOUND = 16;
105 final static int MATH = 17;
107 final static int META = 18;
109 final static int SVG = 19;
111 final static int HEAD = 20;
113 final static int HR = 22;
115 final static int HTML = 23;
117 final static int NOBR = 24;
119 final static int NOFRAMES = 25;
121 final static int NOSCRIPT = 26;
123 final static int OPTGROUP = 27;
125 final static int OPTION = 28;
127 final static int P = 29;
129 final static int PLAINTEXT = 30;
131 final static int SCRIPT = 31;
133 final static int SELECT = 32;
135 final static int STYLE = 33;
137 final static int TABLE = 34;
139 final static int TEXTAREA = 35;
141 final static int TITLE = 36;
143 final static int TR = 37;
145 final static int XMP = 38;
147 final static int TBODY_OR_THEAD_OR_TFOOT = 39;
149 final static int TD_OR_TH = 40;
151 final static int DD_OR_DT = 41;
153 final static int H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6 = 42;
155 final static int MARQUEE_OR_APPLET = 43;
157 final static int PRE_OR_LISTING = 44;
159 final static int B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U = 45;
161 final static int UL_OR_OL_OR_DL = 46;
163 final static int IFRAME = 47;
165 final static int EMBED = 48;
167 final static int AREA_OR_WBR = 49;
169 final static int DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU = 50;
171 final static int ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIALOG_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SEARCH_OR_SECTION_OR_SUMMARY = 51;
173 final static int RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR = 52;
175 final static int RB_OR_RTC = 53;
177 final static int PARAM_OR_SOURCE_OR_TRACK = 55;
179 final static int MGLYPH_OR_MALIGNMARK = 56;
181 final static int MI_MO_MN_MS_MTEXT = 57;
183 final static int ANNOTATION_XML = 58;
185 final static int FOREIGNOBJECT_OR_DESC = 59;
187 final static int NOEMBED = 60;
189 final static int FIELDSET = 61;
191 final static int OUTPUT = 62;
193 final static int OBJECT = 63;
195 final static int FONT = 64;
197 final static int KEYGEN = 65;
199 final static int TEMPLATE = 66;
201 final static int IMG = 67;
203 // start insertion modes
205 private static final int IN_ROW = 0;
207 private static final int IN_TABLE_BODY = 1;
209 private static final int IN_TABLE = 2;
211 private static final int IN_CAPTION = 3;
213 private static final int IN_CELL = 4;
215 private static final int FRAMESET_OK = 5;
217 private static final int IN_BODY = 6;
219 private static final int IN_HEAD = 7;
221 private static final int IN_HEAD_NOSCRIPT = 8;
223 // no fall-through
225 private static final int IN_COLUMN_GROUP = 9;
227 // no fall-through
229 private static final int IN_SELECT_IN_TABLE = 10;
231 private static final int IN_SELECT = 11;
233 // no fall-through
235 private static final int AFTER_BODY = 12;
237 // no fall-through
239 private static final int IN_FRAMESET = 13;
241 private static final int AFTER_FRAMESET = 14;
243 // no fall-through
245 private static final int INITIAL = 15;
247 // could add fall-through
249 private static final int BEFORE_HTML = 16;
251 // could add fall-through
253 private static final int BEFORE_HEAD = 17;
255 // no fall-through
257 private static final int AFTER_HEAD = 18;
259 // no fall-through
261 private static final int AFTER_AFTER_BODY = 19;
263 // no fall-through
265 private static final int AFTER_AFTER_FRAMESET = 20;
267 // no fall-through
269 private static final int TEXT = 21;
271 private static final int IN_TEMPLATE = 22;
273 // start charset states
275 private static final int CHARSET_INITIAL = 0;
277 private static final int CHARSET_C = 1;
279 private static final int CHARSET_H = 2;
281 private static final int CHARSET_A = 3;
283 private static final int CHARSET_R = 4;
285 private static final int CHARSET_S = 5;
287 private static final int CHARSET_E = 6;
289 private static final int CHARSET_T = 7;
291 private static final int CHARSET_EQUALS = 8;
293 private static final int CHARSET_SINGLE_QUOTED = 9;
295 private static final int CHARSET_DOUBLE_QUOTED = 10;
297 private static final int CHARSET_UNQUOTED = 11;
299 // end pseudo enums
301 @Literal private final static String[] QUIRKY_PUBLIC_IDS = {
302 "+//silmaril//dtd html pro v0r11 19970101//",
303 "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
304 "-//as//dtd html 3.0 aswedit + extensions//",
305 "-//ietf//dtd html 2.0 level 1//",
306 "-//ietf//dtd html 2.0 level 2//",
307 "-//ietf//dtd html 2.0 strict level 1//",
308 "-//ietf//dtd html 2.0 strict level 2//",
309 "-//ietf//dtd html 2.0 strict//",
310 "-//ietf//dtd html 2.0//",
311 "-//ietf//dtd html 2.1e//",
312 "-//ietf//dtd html 3.0//",
313 "-//ietf//dtd html 3.2 final//",
314 "-//ietf//dtd html 3.2//",
315 "-//ietf//dtd html 3//",
316 "-//ietf//dtd html level 0//",
317 "-//ietf//dtd html level 1//",
318 "-//ietf//dtd html level 2//",
319 "-//ietf//dtd html level 3//",
320 "-//ietf//dtd html strict level 0//",
321 "-//ietf//dtd html strict level 1//",
322 "-//ietf//dtd html strict level 2//",
323 "-//ietf//dtd html strict level 3//",
324 "-//ietf//dtd html strict//",
325 "-//ietf//dtd html//",
326 "-//metrius//dtd metrius presentational//",
327 "-//microsoft//dtd internet explorer 2.0 html strict//",
328 "-//microsoft//dtd internet explorer 2.0 html//",
329 "-//microsoft//dtd internet explorer 2.0 tables//",
330 "-//microsoft//dtd internet explorer 3.0 html strict//",
331 "-//microsoft//dtd internet explorer 3.0 html//",
332 "-//microsoft//dtd internet explorer 3.0 tables//",
333 "-//netscape comm. corp.//dtd html//",
334 "-//netscape comm. corp.//dtd strict html//",
335 "-//o'reilly and associates//dtd html 2.0//",
336 "-//o'reilly and associates//dtd html extended 1.0//",
337 "-//o'reilly and associates//dtd html extended relaxed 1.0//",
338 "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
339 "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
340 "-//spyglass//dtd html 2.0 extended//",
341 "-//sq//dtd html 2.0 hotmetal + extensions//",
342 "-//sun microsystems corp.//dtd hotjava html//",
343 "-//sun microsystems corp.//dtd hotjava strict html//",
344 "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//",
345 "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//",
346 "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//",
347 "-//w3c//dtd html 4.0 transitional//",
348 "-//w3c//dtd html experimental 19960712//",
349 "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//",
350 "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//",
351 "-//webtechs//dtd mozilla html//" };
353 private static final int NOT_FOUND_ON_STACK = Integer.MAX_VALUE;
355 // [NOCPP[
357 private static final @Local String HTML_LOCAL = "html";
359 // ]NOCPP]
361 private int mode = INITIAL;
363 private int originalMode = INITIAL;
366 * Used only when moving back to IN_BODY.
368 private boolean framesetOk = true;
370 protected Tokenizer tokenizer;
372 // [NOCPP[
374 protected ErrorHandler errorHandler;
376 private DocumentModeHandler documentModeHandler;
378 // ]NOCPP]
380 private boolean scriptingEnabled = false;
382 private boolean needToDropLF;
384 // [NOCPP[
386 private boolean wantingComments;
388 // ]NOCPP]
390 private boolean fragment;
392 private @Local String contextName;
394 private @NsUri String contextNamespace;
396 private T contextNode;
399 * Stack of template insertion modes
401 private @Auto int[] templateModeStack;
404 * Current template mode stack pointer.
406 private int templateModePtr = -1;
408 private @Auto StackNode<T>[] stackNodes;
411 * Index of the earliest possible unused or empty element in stackNodes.
413 private int stackNodesIdx = -1;
415 private int numStackNodes = 0;
417 private @Auto StackNode<T>[] stack;
419 private int currentPtr = -1;
421 private @Auto StackNode<T>[] listOfActiveFormattingElements;
423 private int listPtr = -1;
425 private T formPointer;
427 private T headPointer;
429 protected @Auto char[] charBuffer;
431 protected int charBufferLen = 0;
433 private boolean quirks = false;
435 private boolean forceNoQuirks = false;
437 // [NOCPP[
439 private boolean reportingDoctype = true;
441 private XmlViolationPolicy namePolicy = XmlViolationPolicy.ALTER_INFOSET;
443 private final Map<String, LocatorImpl> idLocations = new HashMap<String, LocatorImpl>();
445 // ]NOCPP]
447 protected TreeBuilder() {
448 fragment = false;
452 * Reports an condition that would make the infoset incompatible with XML
453 * 1.0 as fatal.
455 * @throws SAXException
456 * @throws SAXParseException
458 protected void fatal() throws SAXException {
461 // CPPONLY: @Inline private @Creator Object htmlCreator(@HtmlCreator Object htmlCreator) {
462 // CPPONLY: @Creator Object creator;
463 // CPPONLY: creator.html = htmlCreator;
464 // CPPONLY: return creator;
465 // CPPONLY: }
466 // CPPONLY:
467 // CPPONLY: @Inline private @Creator Object svgCreator(@SvgCreator Object svgCreator) {
468 // CPPONLY: @Creator Object creator;
469 // CPPONLY: creator.svg = svgCreator;
470 // CPPONLY: return creator;
471 // CPPONLY: }
473 // [NOCPP[
475 protected final void fatal(Exception e) throws SAXException {
476 SAXParseException spe = new SAXParseException(e.getMessage(),
477 tokenizer, e);
478 if (errorHandler != null) {
479 errorHandler.fatalError(spe);
481 throw spe;
484 final void fatal(String s) throws SAXException {
485 SAXParseException spe = new SAXParseException(s, tokenizer);
486 if (errorHandler != null) {
487 errorHandler.fatalError(spe);
489 throw spe;
493 * Reports a Parse Error.
495 * @param message
496 * the message
497 * @throws SAXException
499 final void err(String message) throws SAXException {
500 if (errorHandler == null) {
501 return;
503 errNoCheck(message);
507 * Reports a Parse Error without checking if an error handler is present.
509 * @param message
510 * the message
511 * @throws SAXException
513 final void errNoCheck(String message) throws SAXException {
514 SAXParseException spe = new SAXParseException(message, tokenizer);
515 errorHandler.error(spe);
518 private void errListUnclosedStartTags(int eltPos) throws SAXException {
519 if (currentPtr != -1) {
520 for (int i = currentPtr; i > eltPos; i--) {
521 reportUnclosedElementNameAndLocation(i);
527 * Reports the name and location of an unclosed element.
529 * @throws SAXException
531 private final void reportUnclosedElementNameAndLocation(int pos) throws SAXException {
532 StackNode<T> node = stack[pos];
533 if (node.isOptionalEndTag()) {
534 return;
536 TaintableLocatorImpl locator = node.getLocator();
537 if (locator.isTainted()) {
538 return;
540 locator.markTainted();
541 SAXParseException spe = new SAXParseException(
542 "Unclosed element \u201C" + node.popName + "\u201D.", locator);
543 errorHandler.error(spe);
547 * Reports a warning
549 * @param message
550 * the message
551 * @throws SAXException
553 final void warn(String message) throws SAXException {
554 if (errorHandler == null) {
555 return;
557 SAXParseException spe = new SAXParseException(message, tokenizer);
558 errorHandler.warning(spe);
562 * Reports a warning with an explicit locator
564 * @param message
565 * the message
566 * @throws SAXException
568 final void warn(String message, Locator locator) throws SAXException {
569 if (errorHandler == null) {
570 return;
572 SAXParseException spe = new SAXParseException(message, locator);
573 errorHandler.warning(spe);
576 // ]NOCPP]
578 @SuppressWarnings("unchecked") public final void startTokenization(Tokenizer self) throws SAXException {
579 tokenizer = self;
580 stackNodes = new StackNode[64];
581 stack = new StackNode[64];
582 templateModeStack = new int[64];
583 listOfActiveFormattingElements = new StackNode[64];
584 needToDropLF = false;
585 originalMode = INITIAL;
586 templateModePtr = -1;
587 stackNodesIdx = 0;
588 numStackNodes = 0;
589 currentPtr = -1;
590 listPtr = -1;
591 formPointer = null;
592 headPointer = null;
593 // [NOCPP[
594 idLocations.clear();
595 wantingComments = wantsComments();
596 // ]NOCPP]
597 start(fragment);
598 charBufferLen = 0;
599 charBuffer = null;
600 framesetOk = true;
601 if (fragment) {
602 T elt;
603 if (contextNode != null) {
604 elt = contextNode;
605 } else {
606 elt = createHtmlElementSetAsRoot(tokenizer.emptyAttributes());
608 // When the context node is not in the HTML namespace, contrary
609 // to the spec, the first node on the stack is not set to "html"
610 // in the HTML namespace. Instead, it is set to a node that has
611 // the characteristics of the appropriate "adjusted current node".
612 // This way, there is no need to perform "adjusted current node"
613 // checks during tree construction. Instead, it's sufficient to
614 // just look at the current node. However, this also means that it
615 // is not safe to treat "html" in the HTML namespace as a sentinel
616 // that ends stack popping. Instead, stack popping loops that are
617 // meant not to pop the first element on the stack need to check
618 // for currentPos becoming zero.
619 if (contextNamespace == "http://www.w3.org/2000/svg") {
620 ElementName elementName = ElementName.SVG;
621 if ("title" == contextName || "desc" == contextName
622 || "foreignObject" == contextName) {
623 // These elements are all alike and we don't care about
624 // the exact name.
625 elementName = ElementName.FOREIGNOBJECT;
627 // This is the SVG variant of the StackNode constructor.
628 StackNode<T> node = createStackNode(elementName,
629 elementName.getCamelCaseName(), elt
630 // [NOCPP[
631 , errorHandler == null ? null
632 : new TaintableLocatorImpl(tokenizer)
633 // ]NOCPP]
635 currentPtr++;
636 stack[currentPtr] = node;
637 tokenizer.setState(Tokenizer.DATA);
638 // The frameset-ok flag is set even though <frameset> never
639 // ends up being allowed as HTML frameset in the fragment case.
640 mode = FRAMESET_OK;
641 } else if (contextNamespace == "http://www.w3.org/1998/Math/MathML") {
642 ElementName elementName = ElementName.MATH;
643 if ("mi" == contextName || "mo" == contextName
644 || "mn" == contextName || "ms" == contextName
645 || "mtext" == contextName) {
646 // These elements are all alike and we don't care about
647 // the exact name.
648 elementName = ElementName.MTEXT;
649 } else if ("annotation-xml" == contextName) {
650 elementName = ElementName.ANNOTATION_XML;
651 // Blink does not check the encoding attribute of the
652 // annotation-xml element innerHTML is being set on.
653 // Let's do the same at least until
654 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26783
655 // is resolved.
657 // This is the MathML variant of the StackNode constructor.
658 StackNode<T> node = createStackNode(elementName, elt,
659 elementName.getName(), false
660 // [NOCPP[
661 , errorHandler == null ? null
662 : new TaintableLocatorImpl(tokenizer)
663 // ]NOCPP]
665 currentPtr++;
666 stack[currentPtr] = node;
667 tokenizer.setState(Tokenizer.DATA);
668 // The frameset-ok flag is set even though <frameset> never
669 // ends up being allowed as HTML frameset in the fragment case.
670 mode = FRAMESET_OK;
671 } else { // html
672 StackNode<T> node = createStackNode(ElementName.HTML, elt
673 // [NOCPP[
674 , errorHandler == null ? null
675 : new TaintableLocatorImpl(tokenizer)
676 // ]NOCPP]
678 currentPtr++;
679 stack[currentPtr] = node;
680 if ("template" == contextName) {
681 pushTemplateMode(IN_TEMPLATE);
683 resetTheInsertionMode();
684 formPointer = getFormPointerForContext(contextNode);
685 if ("title" == contextName || "textarea" == contextName) {
686 tokenizer.setState(Tokenizer.RCDATA);
687 } else if ("style" == contextName || "xmp" == contextName
688 || "iframe" == contextName || "noembed" == contextName
689 || "noframes" == contextName
690 || (scriptingEnabled && "noscript" == contextName)) {
691 tokenizer.setState(Tokenizer.RAWTEXT);
692 } else if ("plaintext" == contextName) {
693 tokenizer.setState(Tokenizer.PLAINTEXT);
694 } else if ("script" == contextName) {
695 tokenizer.setState(Tokenizer.SCRIPT_DATA);
696 } else {
697 tokenizer.setState(Tokenizer.DATA);
700 } else {
701 mode = INITIAL;
702 // If we are viewing XML source, put a foreign element permanently
703 // on the stack so that cdataSectionAllowed() returns true.
704 // CPPONLY: if (tokenizer.isViewingXmlSource()) {
705 // CPPONLY: T elt = createElement("http://www.w3.org/2000/svg",
706 // CPPONLY: "svg",
707 // CPPONLY: tokenizer.emptyAttributes(), null,
708 // CPPONLY: svgCreator(NS_NewSVGSVGElement));
709 // CPPONLY: StackNode<T> node = createStackNode(ElementName.SVG,
710 // CPPONLY: "svg",
711 // CPPONLY: elt);
712 // CPPONLY: currentPtr++;
713 // CPPONLY: stack[currentPtr] = node;
714 // CPPONLY: }
718 public final void doctype(@Local String name, String publicIdentifier,
719 String systemIdentifier, boolean forceQuirks) throws SAXException {
720 needToDropLF = false;
721 if (!isInForeign() && mode == INITIAL) {
722 // [NOCPP[
723 if (reportingDoctype) {
724 // ]NOCPP]
725 String emptyString = Portability.newEmptyString();
726 appendDoctypeToDocument(name == null ? "" : name,
727 publicIdentifier == null ? emptyString
728 : publicIdentifier,
729 systemIdentifier == null ? emptyString
730 : systemIdentifier);
731 Portability.releaseString(emptyString);
732 // [NOCPP[
734 // ]NOCPP]
735 if (isQuirky(name, publicIdentifier, systemIdentifier,
736 forceQuirks)) {
737 errQuirkyDoctype();
738 documentModeInternal(DocumentMode.QUIRKS_MODE,
739 publicIdentifier, systemIdentifier);
740 } else if (isAlmostStandards(publicIdentifier,
741 systemIdentifier)) {
742 errAlmostStandardsDoctype();
743 documentModeInternal(
744 DocumentMode.ALMOST_STANDARDS_MODE,
745 publicIdentifier, systemIdentifier);
746 } else {
747 // [NOCPP[
748 if ((Portability.literalEqualsString(
749 "-//W3C//DTD HTML 4.0//EN", publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString(
750 "http://www.w3.org/TR/REC-html40/strict.dtd",
751 systemIdentifier)))
752 || (Portability.literalEqualsString(
753 "-//W3C//DTD HTML 4.01//EN",
754 publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString(
755 "http://www.w3.org/TR/html4/strict.dtd",
756 systemIdentifier)))
757 || (Portability.literalEqualsString(
758 "-//W3C//DTD XHTML 1.0 Strict//EN",
759 publicIdentifier) && Portability.literalEqualsString(
760 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd",
761 systemIdentifier))
762 || (Portability.literalEqualsString(
763 "-//W3C//DTD XHTML 1.1//EN",
764 publicIdentifier) && Portability.literalEqualsString(
765 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd",
766 systemIdentifier))
769 err("Obsolete doctype. Expected \u201C<!DOCTYPE html>\u201D.");
770 } else if (!((systemIdentifier == null || Portability.literalEqualsString(
771 "about:legacy-compat", systemIdentifier)) && publicIdentifier == null)) {
772 err("Legacy doctype. Expected \u201C<!DOCTYPE html>\u201D.");
774 // ]NOCPP]
775 documentModeInternal(DocumentMode.STANDARDS_MODE,
776 publicIdentifier, systemIdentifier);
781 * Then, switch to the root element mode of the tree construction
782 * stage.
784 mode = BEFORE_HTML;
785 return;
788 * A DOCTYPE token Parse error.
790 errStrayDoctype();
792 * Ignore the token.
794 return;
797 public final void comment(@NoLength char[] buf, int start, int length)
798 throws SAXException {
799 needToDropLF = false;
800 // [NOCPP[
801 if (!wantingComments) {
802 return;
804 // ]NOCPP]
805 if (!isInForeign()) {
806 switch (mode) {
807 case INITIAL:
808 case BEFORE_HTML:
809 case AFTER_AFTER_BODY:
810 case AFTER_AFTER_FRAMESET:
812 * A comment token Append a Comment node to the Document
813 * object with the data attribute set to the data given in
814 * the comment token.
816 appendCommentToDocument(buf, start, length);
817 return;
818 case AFTER_BODY:
820 * A comment token Append a Comment node to the first
821 * element in the stack of open elements (the html element),
822 * with the data attribute set to the data given in the
823 * comment token.
825 flushCharacters();
826 appendComment(stack[0].node, buf, start, length);
827 return;
828 default:
829 break;
833 * A comment token Append a Comment node to the current node with the
834 * data attribute set to the data given in the comment token.
836 flushCharacters();
837 appendComment(stack[currentPtr].node, buf, start, length);
838 return;
842 * @see nu.validator.htmlparser.common.TokenHandler#characters(char[], int,
843 * int)
845 public final void characters(@Const @NoLength char[] buf, int start, int length)
846 throws SAXException {
847 // Note: Can't attach error messages to EOF in C++ yet
849 // CPPONLY: if (tokenizer.isViewingXmlSource()) {
850 // CPPONLY: return;
851 // CPPONLY: }
852 if (needToDropLF) {
853 needToDropLF = false;
854 if (buf[start] == '\n') {
855 start++;
856 length--;
857 if (length == 0) {
858 return;
863 // optimize the most common case
864 switch (mode) {
865 case IN_BODY:
866 case IN_CELL:
867 case IN_CAPTION:
868 if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
869 reconstructTheActiveFormattingElements();
871 // CPPONLY: MOZ_FALLTHROUGH;
872 case TEXT:
873 accumulateCharacters(buf, start, length);
874 return;
875 case IN_TABLE:
876 case IN_TABLE_BODY:
877 case IN_ROW:
878 accumulateCharactersForced(buf, start, length);
879 return;
880 default:
881 int end = start + length;
882 charactersloop: for (int i = start; i < end; i++) {
883 switch (buf[i]) {
884 case ' ':
885 case '\t':
886 case '\n':
887 case '\r':
888 case '\u000C':
890 * A character token that is one of one of U+0009
891 * CHARACTER TABULATION, U+000A LINE FEED (LF),
892 * U+000C FORM FEED (FF), or U+0020 SPACE
894 switch (mode) {
895 case INITIAL:
896 case BEFORE_HTML:
897 case BEFORE_HEAD:
899 * Ignore the token.
901 start = i + 1;
902 continue;
903 case IN_HEAD:
904 case IN_HEAD_NOSCRIPT:
905 case AFTER_HEAD:
906 case IN_COLUMN_GROUP:
907 case IN_FRAMESET:
908 case AFTER_FRAMESET:
910 * Append the character to the current node.
912 continue;
913 case FRAMESET_OK:
914 case IN_TEMPLATE:
915 case IN_BODY:
916 case IN_CELL:
917 case IN_CAPTION:
918 if (start < i) {
919 accumulateCharacters(buf, start, i
920 - start);
921 start = i;
925 * Reconstruct the active formatting
926 * elements, if any.
928 if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
929 flushCharacters();
930 reconstructTheActiveFormattingElements();
933 * Append the token's character to the
934 * current node.
936 break charactersloop;
937 case IN_SELECT:
938 case IN_SELECT_IN_TABLE:
939 break charactersloop;
940 case IN_TABLE:
941 case IN_TABLE_BODY:
942 case IN_ROW:
943 accumulateCharactersForced(buf, i, 1);
944 start = i + 1;
945 continue;
946 case AFTER_BODY:
947 case AFTER_AFTER_BODY:
948 case AFTER_AFTER_FRAMESET:
949 if (start < i) {
950 accumulateCharacters(buf, start, i
951 - start);
952 start = i;
955 * Reconstruct the active formatting
956 * elements, if any.
958 flushCharacters();
959 reconstructTheActiveFormattingElements();
961 * Append the token's character to the
962 * current node.
964 continue;
966 // CPPONLY: MOZ_FALLTHROUGH_ASSERT();
967 default:
969 * A character token that is not one of one of
970 * U+0009 CHARACTER TABULATION, U+000A LINE FEED
971 * (LF), U+000C FORM FEED (FF), or U+0020 SPACE
973 switch (mode) {
974 case INITIAL:
976 * Parse error.
978 // [NOCPP[
979 // XXX figure out a way to report this in the Gecko View Source case
980 err("Non-space characters found without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
981 // ]NOCPP]
984 * Set the document to quirks mode.
986 documentModeInternal(
987 DocumentMode.QUIRKS_MODE, null,
988 null);
990 * Then, switch to the root element mode of
991 * the tree construction stage
993 mode = BEFORE_HTML;
995 * and reprocess the current token.
997 i--;
998 continue;
999 case BEFORE_HTML:
1001 * Create an HTMLElement node with the tag
1002 * name html, in the HTML namespace. Append
1003 * it to the Document object.
1005 // No need to flush characters here,
1006 // because there's nothing to flush.
1007 appendHtmlElementToDocumentAndPush();
1008 /* Switch to the main mode */
1009 mode = BEFORE_HEAD;
1011 * reprocess the current token.
1013 i--;
1014 continue;
1015 case BEFORE_HEAD:
1016 if (start < i) {
1017 accumulateCharacters(buf, start, i
1018 - start);
1019 start = i;
1022 * /Act as if a start tag token with the tag
1023 * name "head" and no attributes had been
1024 * seen,
1026 flushCharacters();
1027 appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
1028 mode = IN_HEAD;
1030 * then reprocess the current token.
1032 * This will result in an empty head element
1033 * being generated, with the current token
1034 * being reprocessed in the "after head"
1035 * insertion mode.
1037 i--;
1038 continue;
1039 case IN_HEAD:
1040 if (start < i) {
1041 accumulateCharacters(buf, start, i
1042 - start);
1043 start = i;
1046 * Act as if an end tag token with the tag
1047 * name "head" had been seen,
1049 flushCharacters();
1050 pop();
1051 mode = AFTER_HEAD;
1053 * and reprocess the current token.
1055 i--;
1056 continue;
1057 case IN_HEAD_NOSCRIPT:
1058 if (start < i) {
1059 accumulateCharacters(buf, start, i
1060 - start);
1061 start = i;
1064 * Parse error. Act as if an end tag with
1065 * the tag name "noscript" had been seen
1067 errNonSpaceInNoscriptInHead();
1068 flushCharacters();
1069 pop();
1070 mode = IN_HEAD;
1072 * and reprocess the current token.
1074 i--;
1075 continue;
1076 case AFTER_HEAD:
1077 if (start < i) {
1078 accumulateCharacters(buf, start, i
1079 - start);
1080 start = i;
1083 * Act as if a start tag token with the tag
1084 * name "body" and no attributes had been
1085 * seen,
1087 flushCharacters();
1088 appendToCurrentNodeAndPushBodyElement();
1089 mode = FRAMESET_OK;
1091 * and then reprocess the current token.
1093 i--;
1094 continue;
1095 case FRAMESET_OK:
1096 framesetOk = false;
1097 mode = IN_BODY;
1098 i--;
1099 continue;
1100 case IN_TEMPLATE:
1101 case IN_BODY:
1102 case IN_CELL:
1103 case IN_CAPTION:
1104 if (start < i) {
1105 accumulateCharacters(buf, start, i
1106 - start);
1107 start = i;
1110 * Reconstruct the active formatting
1111 * elements, if any.
1113 if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
1114 flushCharacters();
1115 reconstructTheActiveFormattingElements();
1118 * Append the token's character to the
1119 * current node.
1121 break charactersloop;
1122 case IN_TABLE:
1123 case IN_TABLE_BODY:
1124 case IN_ROW:
1125 accumulateCharactersForced(buf, i, 1);
1126 start = i + 1;
1127 continue;
1128 case IN_COLUMN_GROUP:
1129 if (start < i) {
1130 accumulateCharacters(buf, start, i
1131 - start);
1132 start = i;
1135 * Act as if an end tag with the tag name
1136 * "colgroup" had been seen, and then, if
1137 * that token wasn't ignored, reprocess the
1138 * current token.
1140 if (currentPtr == 0 || stack[currentPtr].getGroup() ==
1141 TreeBuilder.TEMPLATE) {
1142 errNonSpaceInColgroupInFragment();
1143 start = i + 1;
1144 continue;
1146 flushCharacters();
1147 pop();
1148 mode = IN_TABLE;
1149 i--;
1150 continue;
1151 case IN_SELECT:
1152 case IN_SELECT_IN_TABLE:
1153 break charactersloop;
1154 case AFTER_BODY:
1155 errNonSpaceAfterBody();
1156 fatal();
1157 mode = framesetOk ? FRAMESET_OK : IN_BODY;
1158 i--;
1159 continue;
1160 case IN_FRAMESET:
1161 if (start < i) {
1162 accumulateCharacters(buf, start, i
1163 - start);
1164 // start index is adjusted below.
1167 * Parse error.
1169 errNonSpaceInFrameset();
1171 * Ignore the token.
1173 start = i + 1;
1174 continue;
1175 case AFTER_FRAMESET:
1176 if (start < i) {
1177 accumulateCharacters(buf, start, i
1178 - start);
1179 // start index is adjusted below.
1182 * Parse error.
1184 errNonSpaceAfterFrameset();
1186 * Ignore the token.
1188 start = i + 1;
1189 continue;
1190 case AFTER_AFTER_BODY:
1192 * Parse error.
1194 errNonSpaceInTrailer();
1196 * Switch back to the main mode and
1197 * reprocess the token.
1199 mode = framesetOk ? FRAMESET_OK : IN_BODY;
1200 i--;
1201 continue;
1202 case AFTER_AFTER_FRAMESET:
1203 if (start < i) {
1204 accumulateCharacters(buf, start, i
1205 - start);
1206 // start index is adjusted below.
1209 * Parse error.
1211 errNonSpaceInTrailer();
1213 * Ignore the token.
1215 start = i + 1;
1216 continue;
1220 if (start < end) {
1221 accumulateCharacters(buf, start, end - start);
1227 * @see nu.validator.htmlparser.common.TokenHandler#zeroOriginatingReplacementCharacter()
1229 public void zeroOriginatingReplacementCharacter() throws SAXException {
1230 if (mode == TEXT) {
1231 accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
1232 return;
1234 if (currentPtr >= 0) {
1235 if (isSpecialParentInForeign(stack[currentPtr])) {
1236 return;
1238 accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
1243 * @see nu.validator.htmlparser.common.TokenHandler#zeroOrReplacementCharacter()
1245 public void zeroOrReplacementCharacter() throws SAXException {
1246 zeroOriginatingReplacementCharacter();
1249 public final void eof() throws SAXException {
1250 flushCharacters();
1251 // Note: Can't attach error messages to EOF in C++ yet
1252 eofloop: for (;;) {
1253 switch (mode) {
1254 case INITIAL:
1256 * Parse error.
1258 // [NOCPP[
1259 err("End of file seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
1260 // ]NOCPP]
1263 * Set the document to quirks mode.
1265 documentModeInternal(DocumentMode.QUIRKS_MODE, null, null);
1267 * Then, switch to the root element mode of the tree
1268 * construction stage
1270 mode = BEFORE_HTML;
1272 * and reprocess the current token.
1274 continue;
1275 case BEFORE_HTML:
1277 * Create an HTMLElement node with the tag name html, in the
1278 * HTML namespace. Append it to the Document object.
1280 appendHtmlElementToDocumentAndPush();
1281 // XXX application cache manifest
1282 /* Switch to the main mode */
1283 mode = BEFORE_HEAD;
1285 * reprocess the current token.
1287 continue;
1288 case BEFORE_HEAD:
1289 appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
1290 mode = IN_HEAD;
1291 continue;
1292 case IN_HEAD:
1293 // [NOCPP[
1294 if (errorHandler != null && currentPtr > 1) {
1295 errEofWithUnclosedElements();
1297 // ]NOCPP]
1298 while (currentPtr > 0) {
1299 popOnEof();
1301 mode = AFTER_HEAD;
1302 continue;
1303 case IN_HEAD_NOSCRIPT:
1304 // [NOCPP[
1305 errEofWithUnclosedElements();
1306 // ]NOCPP]
1307 while (currentPtr > 1) {
1308 popOnEof();
1310 mode = IN_HEAD;
1311 continue;
1312 case AFTER_HEAD:
1313 appendToCurrentNodeAndPushBodyElement();
1314 mode = IN_BODY;
1315 continue;
1316 case IN_TABLE_BODY:
1317 case IN_ROW:
1318 case IN_TABLE:
1319 case IN_SELECT_IN_TABLE:
1320 case IN_SELECT:
1321 case IN_COLUMN_GROUP:
1322 case FRAMESET_OK:
1323 case IN_CAPTION:
1324 case IN_CELL:
1325 case IN_BODY:
1326 // [NOCPP[
1327 // i > 0 to stop in time in the foreign fragment case.
1328 openelementloop: for (int i = currentPtr; i > 0; i--) {
1329 int group = stack[i].getGroup();
1330 switch (group) {
1331 case DD_OR_DT:
1332 case LI:
1333 case P:
1334 case TBODY_OR_THEAD_OR_TFOOT:
1335 case TD_OR_TH:
1336 case BODY:
1337 case HTML:
1338 break;
1339 default:
1340 errEofWithUnclosedElements();
1341 break openelementloop;
1344 // ]NOCPP]
1346 if (isTemplateModeStackEmpty()) {
1347 break eofloop;
1350 // fall through to IN_TEMPLATE
1351 // CPPONLY: MOZ_FALLTHROUGH;
1352 case IN_TEMPLATE:
1353 int eltPos = findLast("template");
1354 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1355 assert fragment;
1356 break eofloop;
1358 if (errorHandler != null) {
1359 errListUnclosedStartTags(0);
1361 while (currentPtr >= eltPos) {
1362 pop();
1364 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
1365 popTemplateMode();
1366 resetTheInsertionMode();
1368 // Reprocess token.
1369 continue;
1370 case TEXT:
1371 // [NOCPP[
1372 if (errorHandler != null) {
1373 errNoCheck("End of file seen when expecting text or an end tag.");
1374 errListUnclosedStartTags(0);
1376 // ]NOCPP]
1377 // XXX mark script as already executed
1378 if (originalMode == AFTER_HEAD) {
1379 popOnEof();
1381 popOnEof();
1382 mode = originalMode;
1383 continue;
1384 case IN_FRAMESET:
1385 // [NOCPP[
1386 if (errorHandler != null && currentPtr > 0) {
1387 errEofWithUnclosedElements();
1389 // ]NOCPP]
1390 break eofloop;
1391 case AFTER_BODY:
1392 case AFTER_FRAMESET:
1393 case AFTER_AFTER_BODY:
1394 case AFTER_AFTER_FRAMESET:
1395 default:
1396 // [NOCPP[
1397 if (currentPtr == 0) { // This silliness is here to poison
1398 // buggy compiler optimizations in
1399 // GWT
1400 System.currentTimeMillis();
1402 // ]NOCPP]
1403 break eofloop;
1406 while (currentPtr > 0) {
1407 popOnEof();
1409 if (!fragment) {
1410 popOnEof();
1412 /* Stop parsing. */
1416 * @see nu.validator.htmlparser.common.TokenHandler#endTokenization()
1418 public final void endTokenization() throws SAXException {
1419 formPointer = null;
1420 headPointer = null;
1421 contextName = null;
1422 contextNode = null;
1423 templateModeStack = null;
1424 if (stack != null) {
1425 while (currentPtr > -1) {
1426 stack[currentPtr].release(this);
1427 currentPtr--;
1429 stack = null;
1431 if (listOfActiveFormattingElements != null) {
1432 while (listPtr > -1) {
1433 if (listOfActiveFormattingElements[listPtr] != null) {
1434 listOfActiveFormattingElements[listPtr].release(this);
1436 listPtr--;
1438 listOfActiveFormattingElements = null;
1440 if (stackNodes != null) {
1441 for (int i = 0; i < numStackNodes; i++) {
1442 assert stackNodes[i].isUnused();
1443 Portability.delete(stackNodes[i]);
1445 numStackNodes = 0;
1446 stackNodesIdx = 0;
1447 stackNodes = null;
1449 // [NOCPP[
1450 idLocations.clear();
1451 // ]NOCPP]
1452 charBuffer = null;
1453 end();
1456 public final void startTag(ElementName elementName,
1457 HtmlAttributes attributes, boolean selfClosing) throws SAXException {
1458 flushCharacters();
1460 // [NOCPP[
1461 boolean wasSelfClosing = selfClosing;
1462 boolean voidElement = false;
1463 if (errorHandler != null) {
1464 // ID uniqueness
1465 @IdType String id = attributes.getId();
1466 if (id != null && !isTemplateContents()) {
1467 LocatorImpl oldLoc = idLocations.get(id);
1468 if (oldLoc != null) {
1469 err("Duplicate ID \u201C" + id + "\u201D.");
1470 errorHandler.warning(new SAXParseException(
1471 "The first occurrence of ID \u201C" + id
1472 + "\u201D was here.", oldLoc));
1473 } else {
1474 idLocations.put(id, new LocatorImpl(tokenizer));
1478 // ]NOCPP]
1480 int eltPos;
1481 needToDropLF = false;
1482 starttagloop: for (;;) {
1483 int group = elementName.getGroup();
1484 @Local String name = elementName.getName();
1485 if (isInForeign()) {
1486 StackNode<T> currentNode = stack[currentPtr];
1487 @NsUri String currNs = currentNode.ns;
1488 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))))) {
1489 switch (group) {
1490 case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
1491 case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
1492 case BODY:
1493 case BR:
1494 case RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR:
1495 case DD_OR_DT:
1496 case UL_OR_OL_OR_DL:
1497 case EMBED:
1498 case IMG:
1499 case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
1500 case HEAD:
1501 case HR:
1502 case LI:
1503 case META:
1504 case NOBR:
1505 case P:
1506 case PRE_OR_LISTING:
1507 case TABLE:
1508 case FONT:
1509 // re-check FONT to deal with the special case
1510 if (!(group == FONT && !(attributes.contains(AttributeName.COLOR)
1511 || attributes.contains(AttributeName.FACE) || attributes.contains(AttributeName.SIZE)))) {
1512 errHtmlStartTagInForeignContext(name);
1513 if (!fragment) {
1514 while (!isSpecialParentInForeign(stack[currentPtr])) {
1515 popForeign(-1, -1);
1517 continue starttagloop;
1518 } // else fall thru
1520 // CPPONLY: MOZ_FALLTHROUGH;
1521 default:
1522 if ("http://www.w3.org/2000/svg" == currNs) {
1523 attributes.adjustForSvg();
1524 if (selfClosing) {
1525 appendVoidElementToCurrentMayFosterSVG(
1526 elementName, attributes);
1527 selfClosing = false;
1528 } else {
1529 appendToCurrentNodeAndPushElementMayFosterSVG(
1530 elementName, attributes);
1532 attributes = null; // CPP
1533 break starttagloop;
1534 } else {
1535 attributes.adjustForMath();
1536 if (selfClosing) {
1537 appendVoidElementToCurrentMayFosterMathML(
1538 elementName, attributes);
1539 selfClosing = false;
1540 } else {
1541 appendToCurrentNodeAndPushElementMayFosterMathML(
1542 elementName, attributes);
1544 attributes = null; // CPP
1545 break starttagloop;
1547 } // switch
1548 } // foreignObject / annotation-xml
1550 switch (mode) {
1551 case IN_TEMPLATE:
1552 switch (group) {
1553 case COL:
1554 popTemplateMode();
1555 pushTemplateMode(IN_COLUMN_GROUP);
1556 mode = IN_COLUMN_GROUP;
1557 // Reprocess token.
1558 continue;
1559 case CAPTION:
1560 case COLGROUP:
1561 case TBODY_OR_THEAD_OR_TFOOT:
1562 popTemplateMode();
1563 pushTemplateMode(IN_TABLE);
1564 mode = IN_TABLE;
1565 // Reprocess token.
1566 continue;
1567 case TR:
1568 popTemplateMode();
1569 pushTemplateMode(IN_TABLE_BODY);
1570 mode = IN_TABLE_BODY;
1571 // Reprocess token.
1572 continue;
1573 case TD_OR_TH:
1574 popTemplateMode();
1575 pushTemplateMode(IN_ROW);
1576 mode = IN_ROW;
1577 // Reprocess token.
1578 continue;
1579 case META:
1580 checkMetaCharset(attributes);
1581 appendVoidElementToCurrentMayFoster(
1582 elementName,
1583 attributes);
1584 selfClosing = false;
1585 // [NOCPP[
1586 voidElement = true;
1587 // ]NOCPP]
1588 attributes = null; // CPP
1589 break starttagloop;
1590 case TITLE:
1591 startTagTitleInHead(elementName, attributes);
1592 attributes = null; // CPP
1593 break starttagloop;
1594 case BASE:
1595 case LINK_OR_BASEFONT_OR_BGSOUND:
1596 appendVoidElementToCurrentMayFoster(
1597 elementName,
1598 attributes);
1599 selfClosing = false;
1600 // [NOCPP[
1601 voidElement = true;
1602 // ]NOCPP]
1603 attributes = null; // CPP
1604 break starttagloop;
1605 case SCRIPT:
1606 startTagScriptInHead(elementName, attributes);
1607 attributes = null; // CPP
1608 break starttagloop;
1609 case NOFRAMES:
1610 case STYLE:
1611 startTagGenericRawText(elementName, attributes);
1612 attributes = null; // CPP
1613 break starttagloop;
1614 case TEMPLATE:
1615 startTagTemplateInHead(elementName, attributes);
1616 attributes = null; // CPP
1617 break starttagloop;
1618 default:
1619 popTemplateMode();
1620 pushTemplateMode(IN_BODY);
1621 mode = IN_BODY;
1622 // Reprocess token.
1623 continue;
1625 case IN_ROW:
1626 switch (group) {
1627 case TD_OR_TH:
1628 clearStackBackTo(findLastOrRoot(TreeBuilder.TR));
1629 appendToCurrentNodeAndPushElement(
1630 elementName,
1631 attributes);
1632 mode = IN_CELL;
1633 insertMarker();
1634 attributes = null; // CPP
1635 break starttagloop;
1636 case CAPTION:
1637 case COL:
1638 case COLGROUP:
1639 case TBODY_OR_THEAD_OR_TFOOT:
1640 case TR:
1641 eltPos = findLastOrRoot(TreeBuilder.TR);
1642 if (eltPos == 0) {
1643 assert fragment || isTemplateContents();
1644 errNoTableRowToClose();
1645 break starttagloop;
1647 clearStackBackTo(eltPos);
1648 pop();
1649 mode = IN_TABLE_BODY;
1650 continue;
1651 default:
1652 // fall through to IN_TABLE
1654 // CPPONLY: MOZ_FALLTHROUGH;
1655 case IN_TABLE_BODY:
1656 switch (group) {
1657 case TR:
1658 clearStackBackTo(findLastInTableScopeOrRootTemplateTbodyTheadTfoot());
1659 appendToCurrentNodeAndPushElement(
1660 elementName,
1661 attributes);
1662 mode = IN_ROW;
1663 attributes = null; // CPP
1664 break starttagloop;
1665 case TD_OR_TH:
1666 errStartTagInTableBody(name);
1667 clearStackBackTo(findLastInTableScopeOrRootTemplateTbodyTheadTfoot());
1668 appendToCurrentNodeAndPushElement(
1669 ElementName.TR,
1670 HtmlAttributes.EMPTY_ATTRIBUTES);
1671 mode = IN_ROW;
1672 continue;
1673 case CAPTION:
1674 case COL:
1675 case COLGROUP:
1676 case TBODY_OR_THEAD_OR_TFOOT:
1677 eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot();
1678 if (eltPos == 0 || stack[eltPos].getGroup() == TEMPLATE) {
1679 assert fragment || isTemplateContents();
1680 errStrayStartTag(name);
1681 break starttagloop;
1682 } else {
1683 clearStackBackTo(eltPos);
1684 pop();
1685 mode = IN_TABLE;
1686 continue;
1688 default:
1689 // fall through to IN_TABLE
1691 // CPPONLY: MOZ_FALLTHROUGH;
1692 case IN_TABLE:
1693 intableloop: for (;;) {
1694 switch (group) {
1695 case CAPTION:
1696 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1697 insertMarker();
1698 appendToCurrentNodeAndPushElement(
1699 elementName,
1700 attributes);
1701 mode = IN_CAPTION;
1702 attributes = null; // CPP
1703 break starttagloop;
1704 case COLGROUP:
1705 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1706 appendToCurrentNodeAndPushElement(
1707 elementName,
1708 attributes);
1709 mode = IN_COLUMN_GROUP;
1710 attributes = null; // CPP
1711 break starttagloop;
1712 case COL:
1713 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1714 appendToCurrentNodeAndPushElement(
1715 ElementName.COLGROUP,
1716 HtmlAttributes.EMPTY_ATTRIBUTES);
1717 mode = IN_COLUMN_GROUP;
1718 continue starttagloop;
1719 case TBODY_OR_THEAD_OR_TFOOT:
1720 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1721 appendToCurrentNodeAndPushElement(
1722 elementName,
1723 attributes);
1724 mode = IN_TABLE_BODY;
1725 attributes = null; // CPP
1726 break starttagloop;
1727 case TR:
1728 case TD_OR_TH:
1729 clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE));
1730 appendToCurrentNodeAndPushElement(
1731 ElementName.TBODY,
1732 HtmlAttributes.EMPTY_ATTRIBUTES);
1733 mode = IN_TABLE_BODY;
1734 continue starttagloop;
1735 case TEMPLATE:
1736 // fall through to IN_HEAD
1737 break intableloop;
1738 case TABLE:
1739 errTableSeenWhileTableOpen();
1740 eltPos = findLastInTableScope(name);
1741 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1742 assert fragment || isTemplateContents();
1743 break starttagloop;
1745 generateImpliedEndTags();
1746 if (errorHandler != null && !isCurrent("table")) {
1747 errNoCheckUnclosedElementsOnStack();
1749 while (currentPtr >= eltPos) {
1750 pop();
1752 resetTheInsertionMode();
1753 continue starttagloop;
1754 case SCRIPT:
1755 // XXX need to manage much more stuff
1756 // here if
1757 // supporting
1758 // document.write()
1759 appendToCurrentNodeAndPushElement(
1760 elementName,
1761 attributes);
1762 originalMode = mode;
1763 mode = TEXT;
1764 tokenizer.setStateAndEndTagExpectation(
1765 Tokenizer.SCRIPT_DATA, elementName);
1766 attributes = null; // CPP
1767 break starttagloop;
1768 case STYLE:
1769 appendToCurrentNodeAndPushElement(
1770 elementName,
1771 attributes);
1772 originalMode = mode;
1773 mode = TEXT;
1774 tokenizer.setStateAndEndTagExpectation(
1775 Tokenizer.RAWTEXT, elementName);
1776 attributes = null; // CPP
1777 break starttagloop;
1778 case INPUT:
1779 errStartTagInTable(name);
1780 if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
1781 "hidden",
1782 attributes.getValue(AttributeName.TYPE))) {
1783 break intableloop;
1785 appendVoidInputToCurrent(
1786 attributes,
1787 formPointer);
1788 selfClosing = false;
1789 // [NOCPP[
1790 voidElement = true;
1791 // ]NOCPP]
1792 attributes = null; // CPP
1793 break starttagloop;
1794 case FORM:
1795 if (formPointer != null || isTemplateContents()) {
1796 errFormWhenFormOpen();
1797 break starttagloop;
1798 } else {
1799 errStartTagInTable(name);
1800 appendVoidFormToCurrent(attributes);
1801 attributes = null; // CPP
1802 break starttagloop;
1804 default:
1805 errStartTagInTable(name);
1806 // fall through to IN_BODY
1807 break intableloop;
1810 // CPPONLY: MOZ_FALLTHROUGH;
1811 case IN_CAPTION:
1812 switch (group) {
1813 case CAPTION:
1814 case COL:
1815 case COLGROUP:
1816 case TBODY_OR_THEAD_OR_TFOOT:
1817 case TR:
1818 case TD_OR_TH:
1819 eltPos = findLastInTableScope("caption");
1820 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1821 assert fragment || isTemplateContents();
1822 errStrayStartTag(name);
1823 break starttagloop;
1825 generateImpliedEndTags();
1826 if (errorHandler != null && currentPtr != eltPos) {
1827 errNoCheckUnclosedElementsOnStack();
1829 while (currentPtr >= eltPos) {
1830 pop();
1832 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
1833 mode = IN_TABLE;
1834 continue;
1835 default:
1836 // fall through to IN_BODY
1838 // CPPONLY: MOZ_FALLTHROUGH;
1839 case IN_CELL:
1840 switch (group) {
1841 case CAPTION:
1842 case COL:
1843 case COLGROUP:
1844 case TBODY_OR_THEAD_OR_TFOOT:
1845 case TR:
1846 case TD_OR_TH:
1847 eltPos = findLastInTableScopeTdTh();
1848 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
1849 errNoCellToClose();
1850 break starttagloop;
1851 } else {
1852 closeTheCell(eltPos);
1853 continue;
1855 default:
1856 // fall through to IN_BODY
1858 // CPPONLY: MOZ_FALLTHROUGH;
1859 case FRAMESET_OK:
1860 switch (group) {
1861 case FRAMESET:
1862 if (mode == FRAMESET_OK) {
1863 if (currentPtr == 0 || stack[1].getGroup() != BODY) {
1864 assert fragment || isTemplateContents();
1865 errStrayStartTag(name);
1866 break starttagloop;
1867 } else {
1868 errFramesetStart();
1869 detachFromParent(stack[1].node);
1870 while (currentPtr > 0) {
1871 pop();
1873 appendToCurrentNodeAndPushElement(
1874 elementName,
1875 attributes);
1876 mode = IN_FRAMESET;
1877 attributes = null; // CPP
1878 break starttagloop;
1880 } else {
1881 errStrayStartTag(name);
1882 break starttagloop;
1884 // NOT falling through!
1885 case PRE_OR_LISTING:
1886 case LI:
1887 case DD_OR_DT:
1888 case BUTTON:
1889 case MARQUEE_OR_APPLET:
1890 case OBJECT:
1891 case TABLE:
1892 case AREA_OR_WBR:
1893 case KEYGEN:
1894 case BR:
1895 case EMBED:
1896 case IMG:
1897 case INPUT:
1898 case HR:
1899 case TEXTAREA:
1900 case XMP:
1901 case IFRAME:
1902 case SELECT:
1903 if (mode == FRAMESET_OK
1904 && !(group == INPUT && Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
1905 "hidden",
1906 attributes.getValue(AttributeName.TYPE)))) {
1907 framesetOk = false;
1908 mode = IN_BODY;
1910 // CPPONLY: MOZ_FALLTHROUGH;
1911 default:
1912 // fall through to IN_BODY
1914 // CPPONLY: MOZ_FALLTHROUGH;
1915 case IN_BODY:
1916 inbodyloop: for (;;) {
1917 switch (group) {
1918 case HTML:
1919 errStrayStartTag(name);
1920 if (!fragment && !isTemplateContents()) {
1921 addAttributesToHtml(attributes);
1922 attributes = null; // CPP
1924 break starttagloop;
1925 case BASE:
1926 case LINK_OR_BASEFONT_OR_BGSOUND:
1927 case META:
1928 case STYLE:
1929 case SCRIPT:
1930 case TITLE:
1931 case TEMPLATE:
1932 // Fall through to IN_HEAD
1933 break inbodyloop;
1934 case BODY:
1935 if (currentPtr == 0 || stack[1].getGroup() != BODY || isTemplateContents()) {
1936 assert fragment || isTemplateContents();
1937 errStrayStartTag(name);
1938 break starttagloop;
1940 errFooSeenWhenFooOpen(name);
1941 framesetOk = false;
1942 if (mode == FRAMESET_OK) {
1943 mode = IN_BODY;
1945 if (addAttributesToBody(attributes)) {
1946 attributes = null; // CPP
1948 break starttagloop;
1949 case P:
1950 case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
1951 case UL_OR_OL_OR_DL:
1952 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_SEARCH_OR_SECTION_OR_SUMMARY:
1953 implicitlyCloseP();
1954 appendToCurrentNodeAndPushElementMayFoster(
1955 elementName,
1956 attributes);
1957 attributes = null; // CPP
1958 break starttagloop;
1959 case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
1960 implicitlyCloseP();
1961 if (stack[currentPtr].getGroup() == H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) {
1962 errHeadingWhenHeadingOpen();
1963 pop();
1965 appendToCurrentNodeAndPushElementMayFoster(
1966 elementName,
1967 attributes);
1968 attributes = null; // CPP
1969 break starttagloop;
1970 case FIELDSET:
1971 implicitlyCloseP();
1972 appendToCurrentNodeAndPushElementMayFoster(
1973 elementName,
1974 attributes, formPointer);
1975 attributes = null; // CPP
1976 break starttagloop;
1977 case PRE_OR_LISTING:
1978 implicitlyCloseP();
1979 appendToCurrentNodeAndPushElementMayFoster(
1980 elementName,
1981 attributes);
1982 needToDropLF = true;
1983 attributes = null; // CPP
1984 break starttagloop;
1985 case FORM:
1986 if (formPointer != null && !isTemplateContents()) {
1987 errFormWhenFormOpen();
1988 break starttagloop;
1989 } else {
1990 implicitlyCloseP();
1991 appendToCurrentNodeAndPushFormElementMayFoster(attributes);
1992 attributes = null; // CPP
1993 break starttagloop;
1995 case LI:
1996 case DD_OR_DT:
1997 eltPos = currentPtr;
1998 for (;;) {
1999 StackNode<T> node = stack[eltPos]; // weak
2000 // ref
2001 if (node.getGroup() == group) { // LI or
2002 // DD_OR_DT
2003 generateImpliedEndTagsExceptFor(node.name);
2004 if (errorHandler != null
2005 && eltPos != currentPtr) {
2006 errUnclosedElementsImplied(eltPos, name);
2008 while (currentPtr >= eltPos) {
2009 pop();
2011 break;
2012 } else if (eltPos == 0 || (node.isSpecial()
2013 && (node.ns != "http://www.w3.org/1999/xhtml"
2014 || (node.name != "p"
2015 && node.name != "address"
2016 && node.name != "div")))) {
2017 break;
2019 eltPos--;
2021 implicitlyCloseP();
2022 appendToCurrentNodeAndPushElementMayFoster(
2023 elementName,
2024 attributes);
2025 attributes = null; // CPP
2026 break starttagloop;
2027 case PLAINTEXT:
2028 implicitlyCloseP();
2029 appendToCurrentNodeAndPushElementMayFoster(
2030 elementName,
2031 attributes);
2032 tokenizer.setStateAndEndTagExpectation(
2033 Tokenizer.PLAINTEXT, elementName);
2034 attributes = null; // CPP
2035 break starttagloop;
2036 case A:
2037 int activeAPos = findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker("a");
2038 if (activeAPos != -1) {
2039 errFooSeenWhenFooOpen(name);
2040 StackNode<T> activeA = listOfActiveFormattingElements[activeAPos];
2041 activeA.retain();
2042 adoptionAgencyEndTag("a");
2043 removeFromStack(activeA);
2044 activeAPos = findInListOfActiveFormattingElements(activeA);
2045 if (activeAPos != -1) {
2046 removeFromListOfActiveFormattingElements(activeAPos);
2048 activeA.release(this);
2050 reconstructTheActiveFormattingElements();
2051 appendToCurrentNodeAndPushFormattingElementMayFoster(
2052 elementName,
2053 attributes);
2054 attributes = null; // CPP
2055 break starttagloop;
2056 case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
2057 case FONT:
2058 reconstructTheActiveFormattingElements();
2059 maybeForgetEarlierDuplicateFormattingElement(elementName.getName(), attributes);
2060 appendToCurrentNodeAndPushFormattingElementMayFoster(
2061 elementName,
2062 attributes);
2063 attributes = null; // CPP
2064 break starttagloop;
2065 case NOBR:
2066 reconstructTheActiveFormattingElements();
2067 if (TreeBuilder.NOT_FOUND_ON_STACK != findLastInScope("nobr")) {
2068 errFooSeenWhenFooOpen(name);
2069 adoptionAgencyEndTag("nobr");
2070 reconstructTheActiveFormattingElements();
2072 appendToCurrentNodeAndPushFormattingElementMayFoster(
2073 elementName,
2074 attributes);
2075 attributes = null; // CPP
2076 break starttagloop;
2077 case BUTTON:
2078 eltPos = findLastInScope(name);
2079 if (eltPos != TreeBuilder.NOT_FOUND_ON_STACK) {
2080 errFooSeenWhenFooOpen(name);
2081 generateImpliedEndTags();
2082 if (errorHandler != null
2083 && !isCurrent(name)) {
2084 errUnclosedElementsImplied(eltPos, name);
2086 while (currentPtr >= eltPos) {
2087 pop();
2089 continue starttagloop;
2090 } else {
2091 reconstructTheActiveFormattingElements();
2092 appendToCurrentNodeAndPushElementMayFoster(
2093 elementName,
2094 attributes, formPointer);
2095 attributes = null; // CPP
2096 break starttagloop;
2098 case OBJECT:
2099 reconstructTheActiveFormattingElements();
2100 appendToCurrentNodeAndPushElementMayFoster(
2101 elementName,
2102 attributes, formPointer);
2103 insertMarker();
2104 attributes = null; // CPP
2105 break starttagloop;
2106 case MARQUEE_OR_APPLET:
2107 reconstructTheActiveFormattingElements();
2108 appendToCurrentNodeAndPushElementMayFoster(
2109 elementName,
2110 attributes);
2111 insertMarker();
2112 attributes = null; // CPP
2113 break starttagloop;
2114 case TABLE:
2115 // The only quirk. Blame Hixie and
2116 // Acid2.
2117 if (!quirks) {
2118 implicitlyCloseP();
2120 appendToCurrentNodeAndPushElementMayFoster(
2121 elementName,
2122 attributes);
2123 mode = IN_TABLE;
2124 attributes = null; // CPP
2125 break starttagloop;
2126 case BR:
2127 case EMBED:
2128 case AREA_OR_WBR:
2129 case KEYGEN:
2130 reconstructTheActiveFormattingElements();
2131 // FALL THROUGH to PARAM_OR_SOURCE_OR_TRACK
2132 // CPPONLY: MOZ_FALLTHROUGH;
2133 case PARAM_OR_SOURCE_OR_TRACK:
2134 appendVoidElementToCurrentMayFoster(
2135 elementName,
2136 attributes);
2137 selfClosing = false;
2138 // [NOCPP[
2139 voidElement = true;
2140 // ]NOCPP]
2141 attributes = null; // CPP
2142 break starttagloop;
2143 case HR:
2144 implicitlyCloseP();
2145 appendVoidElementToCurrentMayFoster(
2146 elementName,
2147 attributes);
2148 selfClosing = false;
2149 // [NOCPP[
2150 voidElement = true;
2151 // ]NOCPP]
2152 attributes = null; // CPP
2153 break starttagloop;
2154 case IMAGE:
2155 errImage();
2156 elementName = ElementName.IMG;
2157 continue starttagloop;
2158 case IMG:
2159 case INPUT:
2160 reconstructTheActiveFormattingElements();
2161 appendVoidElementToCurrentMayFoster(
2162 elementName, attributes,
2163 formPointer);
2164 selfClosing = false;
2165 // [NOCPP[
2166 voidElement = true;
2167 // ]NOCPP]
2168 attributes = null; // CPP
2169 break starttagloop;
2170 case TEXTAREA:
2171 appendToCurrentNodeAndPushElementMayFoster(
2172 elementName,
2173 attributes, formPointer);
2174 tokenizer.setStateAndEndTagExpectation(
2175 Tokenizer.RCDATA, elementName);
2176 originalMode = mode;
2177 mode = TEXT;
2178 needToDropLF = true;
2179 attributes = null; // CPP
2180 break starttagloop;
2181 case XMP:
2182 implicitlyCloseP();
2183 reconstructTheActiveFormattingElements();
2184 appendToCurrentNodeAndPushElementMayFoster(
2185 elementName,
2186 attributes);
2187 originalMode = mode;
2188 mode = TEXT;
2189 tokenizer.setStateAndEndTagExpectation(
2190 Tokenizer.RAWTEXT, elementName);
2191 attributes = null; // CPP
2192 break starttagloop;
2193 case NOSCRIPT:
2194 if (!scriptingEnabled) {
2195 reconstructTheActiveFormattingElements();
2196 appendToCurrentNodeAndPushElementMayFoster(
2197 elementName,
2198 attributes);
2199 attributes = null; // CPP
2200 break starttagloop;
2202 // CPPONLY: MOZ_FALLTHROUGH;
2203 case NOFRAMES:
2204 case IFRAME:
2205 case NOEMBED:
2206 startTagGenericRawText(elementName, attributes);
2207 attributes = null; // CPP
2208 break starttagloop;
2209 case SELECT:
2210 reconstructTheActiveFormattingElements();
2211 appendToCurrentNodeAndPushElementMayFoster(
2212 elementName,
2213 attributes, formPointer);
2214 switch (mode) {
2215 case IN_TABLE:
2216 case IN_CAPTION:
2217 case IN_COLUMN_GROUP:
2218 case IN_TABLE_BODY:
2219 case IN_ROW:
2220 case IN_CELL:
2221 mode = IN_SELECT_IN_TABLE;
2222 break;
2223 default:
2224 mode = IN_SELECT;
2225 break;
2227 attributes = null; // CPP
2228 break starttagloop;
2229 case OPTGROUP:
2230 case OPTION:
2231 if (isCurrent("option")) {
2232 pop();
2234 reconstructTheActiveFormattingElements();
2235 appendToCurrentNodeAndPushElementMayFoster(
2236 elementName,
2237 attributes);
2238 attributes = null; // CPP
2239 break starttagloop;
2240 case RB_OR_RTC:
2241 eltPos = findLastInScope("ruby");
2242 if (eltPos != NOT_FOUND_ON_STACK) {
2243 generateImpliedEndTags();
2245 if (eltPos != currentPtr) {
2246 if (eltPos == NOT_FOUND_ON_STACK) {
2247 errStartTagSeenWithoutRuby(name);
2248 } else {
2249 errUnclosedChildrenInRuby();
2252 appendToCurrentNodeAndPushElementMayFoster(
2253 elementName,
2254 attributes);
2255 attributes = null; // CPP
2256 break starttagloop;
2257 case RT_OR_RP:
2258 eltPos = findLastInScope("ruby");
2259 if (eltPos != NOT_FOUND_ON_STACK) {
2260 generateImpliedEndTagsExceptFor("rtc");
2262 if (eltPos != currentPtr) {
2263 if (!isCurrent("rtc")) {
2264 if (eltPos == NOT_FOUND_ON_STACK) {
2265 errStartTagSeenWithoutRuby(name);
2266 } else {
2267 errUnclosedChildrenInRuby();
2271 appendToCurrentNodeAndPushElementMayFoster(
2272 elementName,
2273 attributes);
2274 attributes = null; // CPP
2275 break starttagloop;
2276 case MATH:
2277 reconstructTheActiveFormattingElements();
2278 attributes.adjustForMath();
2279 if (selfClosing) {
2280 appendVoidElementToCurrentMayFosterMathML(
2281 elementName, attributes);
2282 selfClosing = false;
2283 } else {
2284 appendToCurrentNodeAndPushElementMayFosterMathML(
2285 elementName, attributes);
2287 attributes = null; // CPP
2288 break starttagloop;
2289 case SVG:
2290 reconstructTheActiveFormattingElements();
2291 attributes.adjustForSvg();
2292 if (selfClosing) {
2293 appendVoidElementToCurrentMayFosterSVG(
2294 elementName,
2295 attributes);
2296 selfClosing = false;
2297 } else {
2298 appendToCurrentNodeAndPushElementMayFosterSVG(
2299 elementName, attributes);
2301 attributes = null; // CPP
2302 break starttagloop;
2303 case CAPTION:
2304 case COL:
2305 case COLGROUP:
2306 case TBODY_OR_THEAD_OR_TFOOT:
2307 case TR:
2308 case TD_OR_TH:
2309 case FRAME:
2310 case FRAMESET:
2311 case HEAD:
2312 errStrayStartTag(name);
2313 break starttagloop;
2314 case OUTPUT:
2315 reconstructTheActiveFormattingElements();
2316 appendToCurrentNodeAndPushElementMayFoster(
2317 elementName,
2318 attributes, formPointer);
2319 attributes = null; // CPP
2320 break starttagloop;
2321 default:
2322 reconstructTheActiveFormattingElements();
2323 appendToCurrentNodeAndPushElementMayFoster(
2324 elementName,
2325 attributes);
2326 attributes = null; // CPP
2327 break starttagloop;
2330 // CPPONLY: MOZ_FALLTHROUGH;
2331 case IN_HEAD:
2332 inheadloop: for (;;) {
2333 switch (group) {
2334 case HTML:
2335 errStrayStartTag(name);
2336 if (!fragment && !isTemplateContents()) {
2337 addAttributesToHtml(attributes);
2338 attributes = null; // CPP
2340 break starttagloop;
2341 case BASE:
2342 case LINK_OR_BASEFONT_OR_BGSOUND:
2343 appendVoidElementToCurrentMayFoster(
2344 elementName,
2345 attributes);
2346 selfClosing = false;
2347 // [NOCPP[
2348 voidElement = true;
2349 // ]NOCPP]
2350 attributes = null; // CPP
2351 break starttagloop;
2352 case META:
2353 // Fall through to IN_HEAD_NOSCRIPT
2354 break inheadloop;
2355 case TITLE:
2356 startTagTitleInHead(elementName, attributes);
2357 attributes = null; // CPP
2358 break starttagloop;
2359 case NOSCRIPT:
2360 if (scriptingEnabled) {
2361 appendToCurrentNodeAndPushElement(
2362 elementName,
2363 attributes);
2364 originalMode = mode;
2365 mode = TEXT;
2366 tokenizer.setStateAndEndTagExpectation(
2367 Tokenizer.RAWTEXT, elementName);
2368 } else {
2369 appendToCurrentNodeAndPushElementMayFoster(
2370 elementName,
2371 attributes);
2372 mode = IN_HEAD_NOSCRIPT;
2374 attributes = null; // CPP
2375 break starttagloop;
2376 case SCRIPT:
2377 startTagScriptInHead(elementName, attributes);
2378 attributes = null; // CPP
2379 break starttagloop;
2380 case STYLE:
2381 case NOFRAMES:
2382 startTagGenericRawText(elementName, attributes);
2383 attributes = null; // CPP
2384 break starttagloop;
2385 case HEAD:
2386 /* Parse error. */
2387 errFooSeenWhenFooOpen(name);
2388 /* Ignore the token. */
2389 break starttagloop;
2390 case TEMPLATE:
2391 startTagTemplateInHead(elementName, attributes);
2392 attributes = null; // CPP
2393 break starttagloop;
2394 default:
2395 pop();
2396 mode = AFTER_HEAD;
2397 continue starttagloop;
2400 // CPPONLY: MOZ_FALLTHROUGH;
2401 case IN_HEAD_NOSCRIPT:
2402 switch (group) {
2403 case HTML:
2404 // XXX did Hixie really mean to omit "base"
2405 // here?
2406 errStrayStartTag(name);
2407 if (!fragment && !isTemplateContents()) {
2408 addAttributesToHtml(attributes);
2409 attributes = null; // CPP
2411 break starttagloop;
2412 case LINK_OR_BASEFONT_OR_BGSOUND:
2413 appendVoidElementToCurrentMayFoster(
2414 elementName,
2415 attributes);
2416 selfClosing = false;
2417 // [NOCPP[
2418 voidElement = true;
2419 // ]NOCPP]
2420 attributes = null; // CPP
2421 break starttagloop;
2422 case META:
2423 checkMetaCharset(attributes);
2424 appendVoidElementToCurrentMayFoster(
2425 elementName,
2426 attributes);
2427 selfClosing = false;
2428 // [NOCPP[
2429 voidElement = true;
2430 // ]NOCPP]
2431 attributes = null; // CPP
2432 break starttagloop;
2433 case STYLE:
2434 case NOFRAMES:
2435 appendToCurrentNodeAndPushElement(
2436 elementName,
2437 attributes);
2438 originalMode = mode;
2439 mode = TEXT;
2440 tokenizer.setStateAndEndTagExpectation(
2441 Tokenizer.RAWTEXT, elementName);
2442 attributes = null; // CPP
2443 break starttagloop;
2444 case HEAD:
2445 errFooSeenWhenFooOpen(name);
2446 break starttagloop;
2447 case NOSCRIPT:
2448 errFooSeenWhenFooOpen(name);
2449 break starttagloop;
2450 default:
2451 errBadStartTagInNoscriptInHead(name);
2452 pop();
2453 mode = IN_HEAD;
2454 continue;
2456 case IN_COLUMN_GROUP:
2457 switch (group) {
2458 case HTML:
2459 errStrayStartTag(name);
2460 if (!fragment && !isTemplateContents()) {
2461 addAttributesToHtml(attributes);
2462 attributes = null; // CPP
2464 break starttagloop;
2465 case COL:
2466 appendVoidElementToCurrentMayFoster(
2467 elementName,
2468 attributes);
2469 selfClosing = false;
2470 // [NOCPP[
2471 voidElement = true;
2472 // ]NOCPP]
2473 attributes = null; // CPP
2474 break starttagloop;
2475 case TEMPLATE:
2476 startTagTemplateInHead(elementName, attributes);
2477 attributes = null; // CPP
2478 break starttagloop;
2479 default:
2480 if (currentPtr == 0 || stack[currentPtr].getGroup() == TEMPLATE) {
2481 assert fragment || isTemplateContents();
2482 errGarbageInColgroup();
2483 break starttagloop;
2485 pop();
2486 mode = IN_TABLE;
2487 continue;
2489 case IN_SELECT_IN_TABLE:
2490 switch (group) {
2491 case CAPTION:
2492 case TBODY_OR_THEAD_OR_TFOOT:
2493 case TR:
2494 case TD_OR_TH:
2495 case TABLE:
2496 errStartTagWithSelectOpen(name);
2497 eltPos = findLastInTableScope("select");
2498 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2499 assert fragment;
2500 break starttagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375
2502 while (currentPtr >= eltPos) {
2503 pop();
2505 resetTheInsertionMode();
2506 continue;
2507 default:
2508 // fall through to IN_SELECT
2510 // CPPONLY: MOZ_FALLTHROUGH;
2511 case IN_SELECT:
2512 switch (group) {
2513 case HTML:
2514 errStrayStartTag(name);
2515 if (!fragment) {
2516 addAttributesToHtml(attributes);
2517 attributes = null; // CPP
2519 break starttagloop;
2520 case OPTION:
2521 if (isCurrent("option")) {
2522 pop();
2524 appendToCurrentNodeAndPushElement(
2525 elementName,
2526 attributes);
2527 attributes = null; // CPP
2528 break starttagloop;
2529 case OPTGROUP:
2530 if (isCurrent("option")) {
2531 pop();
2533 if (isCurrent("optgroup")) {
2534 pop();
2536 appendToCurrentNodeAndPushElement(
2537 elementName,
2538 attributes);
2539 attributes = null; // CPP
2540 break starttagloop;
2541 case SELECT:
2542 errStartSelectWhereEndSelectExpected();
2543 eltPos = findLastInTableScope(name);
2544 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2545 assert fragment;
2546 errNoSelectInTableScope();
2547 break starttagloop;
2548 } else {
2549 while (currentPtr >= eltPos) {
2550 pop();
2552 resetTheInsertionMode();
2553 break starttagloop;
2555 case INPUT:
2556 case TEXTAREA:
2557 errStartTagWithSelectOpen(name);
2558 eltPos = findLastInTableScope("select");
2559 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
2560 assert fragment;
2561 break starttagloop;
2563 while (currentPtr >= eltPos) {
2564 pop();
2566 resetTheInsertionMode();
2567 continue;
2568 case SCRIPT:
2569 startTagScriptInHead(elementName, attributes);
2570 attributes = null; // CPP
2571 break starttagloop;
2572 case TEMPLATE:
2573 startTagTemplateInHead(elementName, attributes);
2574 attributes = null; // CPP
2575 break starttagloop;
2576 case HR:
2577 if (isCurrent("option")) {
2578 pop();
2580 if (isCurrent("optgroup")) {
2581 pop();
2583 appendVoidElementToCurrent(elementName, attributes);
2584 selfClosing = false;
2585 // [NOCPP[
2586 voidElement = true;
2587 // ]NOCPP]
2588 attributes = null; // CPP
2589 break starttagloop;
2590 default:
2591 errStrayStartTag(name);
2592 break starttagloop;
2594 case AFTER_BODY:
2595 switch (group) {
2596 case HTML:
2597 errStrayStartTag(name);
2598 if (!fragment && !isTemplateContents()) {
2599 addAttributesToHtml(attributes);
2600 attributes = null; // CPP
2602 break starttagloop;
2603 default:
2604 errStrayStartTag(name);
2605 mode = framesetOk ? FRAMESET_OK : IN_BODY;
2606 continue;
2608 case IN_FRAMESET:
2609 switch (group) {
2610 case FRAMESET:
2611 appendToCurrentNodeAndPushElement(
2612 elementName,
2613 attributes);
2614 attributes = null; // CPP
2615 break starttagloop;
2616 case FRAME:
2617 appendVoidElementToCurrentMayFoster(
2618 elementName,
2619 attributes);
2620 selfClosing = false;
2621 // [NOCPP[
2622 voidElement = true;
2623 // ]NOCPP]
2624 attributes = null; // CPP
2625 break starttagloop;
2626 default:
2627 // fall through to AFTER_FRAMESET
2629 // CPPONLY: MOZ_FALLTHROUGH;
2630 case AFTER_FRAMESET:
2631 switch (group) {
2632 case HTML:
2633 errStrayStartTag(name);
2634 if (!fragment && !isTemplateContents()) {
2635 addAttributesToHtml(attributes);
2636 attributes = null; // CPP
2638 break starttagloop;
2639 case NOFRAMES:
2640 appendToCurrentNodeAndPushElement(
2641 elementName,
2642 attributes);
2643 originalMode = mode;
2644 mode = TEXT;
2645 tokenizer.setStateAndEndTagExpectation(
2646 Tokenizer.RAWTEXT, elementName);
2647 attributes = null; // CPP
2648 break starttagloop;
2649 default:
2650 errStrayStartTag(name);
2651 break starttagloop;
2653 case INITIAL:
2655 * Parse error.
2657 errStartTagWithoutDoctype();
2660 * Set the document to quirks mode.
2662 documentModeInternal(DocumentMode.QUIRKS_MODE, null, null);
2664 * Then, switch to the root element mode of the tree
2665 * construction stage
2667 mode = BEFORE_HTML;
2669 * and reprocess the current token.
2671 continue;
2672 case BEFORE_HTML:
2673 switch (group) {
2674 case HTML:
2675 // optimize error check and streaming SAX by
2676 // hoisting
2677 // "html" handling here.
2678 if (attributes == HtmlAttributes.EMPTY_ATTRIBUTES) {
2679 // This has the right magic side effect
2680 // that
2681 // it
2682 // makes attributes in SAX Tree mutable.
2683 appendHtmlElementToDocumentAndPush();
2684 } else {
2685 appendHtmlElementToDocumentAndPush(attributes);
2687 // XXX application cache should fire here
2688 mode = BEFORE_HEAD;
2689 attributes = null; // CPP
2690 break starttagloop;
2691 default:
2693 * Create an HTMLElement node with the tag name
2694 * html, in the HTML namespace. Append it to the
2695 * Document object.
2697 appendHtmlElementToDocumentAndPush();
2698 /* Switch to the main mode */
2699 mode = BEFORE_HEAD;
2701 * reprocess the current token.
2703 continue;
2705 case BEFORE_HEAD:
2706 switch (group) {
2707 case HTML:
2708 errStrayStartTag(name);
2709 if (!fragment && !isTemplateContents()) {
2710 addAttributesToHtml(attributes);
2711 attributes = null; // CPP
2713 break starttagloop;
2714 case HEAD:
2716 * A start tag whose tag name is "head"
2718 * Create an element for the token.
2720 * Set the head element pointer to this new element
2721 * node.
2723 * Append the new element to the current node and
2724 * push it onto the stack of open elements.
2726 appendToCurrentNodeAndPushHeadElement(attributes);
2728 * Change the insertion mode to "in head".
2730 mode = IN_HEAD;
2731 attributes = null; // CPP
2732 break starttagloop;
2733 default:
2735 * Any other start tag token
2737 * Act as if a start tag token with the tag name
2738 * "head" and no attributes had been seen,
2740 appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
2741 mode = IN_HEAD;
2743 * then reprocess the current token.
2745 * This will result in an empty head element being
2746 * generated, with the current token being
2747 * reprocessed in the "after head" insertion mode.
2749 continue;
2751 case AFTER_HEAD:
2752 switch (group) {
2753 case HTML:
2754 errStrayStartTag(name);
2755 if (!fragment && !isTemplateContents()) {
2756 addAttributesToHtml(attributes);
2757 attributes = null; // CPP
2759 break starttagloop;
2760 case BODY:
2761 if (attributes.getLength() == 0) {
2762 // This has the right magic side effect
2763 // that
2764 // it
2765 // makes attributes in SAX Tree mutable.
2766 appendToCurrentNodeAndPushBodyElement();
2767 } else {
2768 appendToCurrentNodeAndPushBodyElement(attributes);
2770 framesetOk = false;
2771 mode = IN_BODY;
2772 attributes = null; // CPP
2773 break starttagloop;
2774 case FRAMESET:
2775 appendToCurrentNodeAndPushElement(
2776 elementName,
2777 attributes);
2778 mode = IN_FRAMESET;
2779 attributes = null; // CPP
2780 break starttagloop;
2781 case TEMPLATE:
2782 errFooBetweenHeadAndBody(name);
2783 pushHeadPointerOntoStack();
2784 StackNode<T> headOnStack = stack[currentPtr];
2785 startTagTemplateInHead(elementName, attributes);
2786 removeFromStack(headOnStack);
2787 attributes = null; // CPP
2788 break starttagloop;
2789 case BASE:
2790 case LINK_OR_BASEFONT_OR_BGSOUND:
2791 errFooBetweenHeadAndBody(name);
2792 pushHeadPointerOntoStack();
2793 appendVoidElementToCurrentMayFoster(
2794 elementName,
2795 attributes);
2796 selfClosing = false;
2797 // [NOCPP[
2798 voidElement = true;
2799 // ]NOCPP]
2800 pop(); // head
2801 attributes = null; // CPP
2802 break starttagloop;
2803 case META:
2804 errFooBetweenHeadAndBody(name);
2805 checkMetaCharset(attributes);
2806 pushHeadPointerOntoStack();
2807 appendVoidElementToCurrentMayFoster(
2808 elementName,
2809 attributes);
2810 selfClosing = false;
2811 // [NOCPP[
2812 voidElement = true;
2813 // ]NOCPP]
2814 pop(); // head
2815 attributes = null; // CPP
2816 break starttagloop;
2817 case SCRIPT:
2818 errFooBetweenHeadAndBody(name);
2819 pushHeadPointerOntoStack();
2820 appendToCurrentNodeAndPushElement(
2821 elementName,
2822 attributes);
2823 originalMode = mode;
2824 mode = TEXT;
2825 tokenizer.setStateAndEndTagExpectation(
2826 Tokenizer.SCRIPT_DATA, elementName);
2827 attributes = null; // CPP
2828 break starttagloop;
2829 case STYLE:
2830 case NOFRAMES:
2831 errFooBetweenHeadAndBody(name);
2832 pushHeadPointerOntoStack();
2833 appendToCurrentNodeAndPushElement(
2834 elementName,
2835 attributes);
2836 originalMode = mode;
2837 mode = TEXT;
2838 tokenizer.setStateAndEndTagExpectation(
2839 Tokenizer.RAWTEXT, elementName);
2840 attributes = null; // CPP
2841 break starttagloop;
2842 case TITLE:
2843 errFooBetweenHeadAndBody(name);
2844 pushHeadPointerOntoStack();
2845 appendToCurrentNodeAndPushElement(
2846 elementName,
2847 attributes);
2848 originalMode = mode;
2849 mode = TEXT;
2850 tokenizer.setStateAndEndTagExpectation(
2851 Tokenizer.RCDATA, elementName);
2852 attributes = null; // CPP
2853 break starttagloop;
2854 case HEAD:
2855 errStrayStartTag(name);
2856 break starttagloop;
2857 default:
2858 appendToCurrentNodeAndPushBodyElement();
2859 mode = FRAMESET_OK;
2860 continue;
2862 case AFTER_AFTER_BODY:
2863 switch (group) {
2864 case HTML:
2865 errStrayStartTag(name);
2866 if (!fragment && !isTemplateContents()) {
2867 addAttributesToHtml(attributes);
2868 attributes = null; // CPP
2870 break starttagloop;
2871 default:
2872 errStrayStartTag(name);
2873 fatal();
2874 mode = framesetOk ? FRAMESET_OK : IN_BODY;
2875 continue;
2877 case AFTER_AFTER_FRAMESET:
2878 switch (group) {
2879 case HTML:
2880 errStrayStartTag(name);
2881 if (!fragment && !isTemplateContents()) {
2882 addAttributesToHtml(attributes);
2883 attributes = null; // CPP
2885 break starttagloop;
2886 case NOFRAMES:
2887 startTagGenericRawText(elementName, attributes);
2888 attributes = null; // CPP
2889 break starttagloop;
2890 default:
2891 errStrayStartTag(name);
2892 break starttagloop;
2894 case TEXT:
2895 assert false;
2896 break starttagloop; // Avoid infinite loop if the assertion
2897 // fails
2900 if (selfClosing) {
2901 errSelfClosing();
2902 // [NOCPP[
2903 } else if (wasSelfClosing && voidElement
2904 && tokenizer.getErrorProfile() != null
2905 && tokenizer.getErrorProfile().get("html-strict") != null) {
2906 warn("Trailing slash on void elements has no effect and interacts"
2907 + " badly with unquoted attribute values.");
2908 // ]NOCPP]
2910 // CPPONLY: if (mBuilder == null && attributes != HtmlAttributes.EMPTY_ATTRIBUTES) {
2911 // CPPONLY: Portability.delete(attributes);
2912 // CPPONLY: }
2915 private void startTagTitleInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2916 appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
2917 originalMode = mode;
2918 mode = TEXT;
2919 tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA, elementName);
2922 private void startTagGenericRawText(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2923 appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
2924 originalMode = mode;
2925 mode = TEXT;
2926 tokenizer.setStateAndEndTagExpectation(Tokenizer.RAWTEXT, elementName);
2929 private void startTagScriptInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2930 // XXX need to manage much more stuff here if supporting document.write()
2931 appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
2932 originalMode = mode;
2933 mode = TEXT;
2934 tokenizer.setStateAndEndTagExpectation(Tokenizer.SCRIPT_DATA, elementName);
2937 private void startTagTemplateInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException {
2938 appendToCurrentNodeAndPushElement(elementName, attributes);
2939 insertMarker();
2940 framesetOk = false;
2941 originalMode = mode;
2942 mode = IN_TEMPLATE;
2943 pushTemplateMode(IN_TEMPLATE);
2946 private boolean isTemplateContents() {
2947 return TreeBuilder.NOT_FOUND_ON_STACK != findLast("template");
2950 private boolean isTemplateModeStackEmpty() {
2951 return templateModePtr == -1;
2954 private boolean isSpecialParentInForeign(StackNode<T> stackNode) {
2955 @NsUri String ns = stackNode.ns;
2956 return ("http://www.w3.org/1999/xhtml" == ns)
2957 || (stackNode.isHtmlIntegrationPoint())
2958 || (("http://www.w3.org/1998/Math/MathML" == ns) && (stackNode.getGroup() == MI_MO_MN_MS_MTEXT));
2963 * <p>
2964 * C++ memory note: The return value must be released.
2966 * @return
2967 * @throws SAXException
2968 * @throws StopSniffingException
2970 public static String extractCharsetFromContent(String attributeValue
2971 // CPPONLY: , TreeBuilder tb
2973 // This is a bit ugly. Converting the string to char array in order to
2974 // make the portability layer smaller.
2975 int charsetState = CHARSET_INITIAL;
2976 int start = -1;
2977 int end = -1;
2978 @Auto char[] buffer = Portability.newCharArrayFromString(attributeValue);
2980 charsetloop: for (int i = 0; i < buffer.length; i++) {
2981 char c = buffer[i];
2982 switch (charsetState) {
2983 case CHARSET_INITIAL:
2984 switch (c) {
2985 case 'c':
2986 case 'C':
2987 charsetState = CHARSET_C;
2988 continue;
2989 default:
2990 continue;
2992 case CHARSET_C:
2993 switch (c) {
2994 case 'h':
2995 case 'H':
2996 charsetState = CHARSET_H;
2997 continue;
2998 default:
2999 charsetState = CHARSET_INITIAL;
3000 continue;
3002 case CHARSET_H:
3003 switch (c) {
3004 case 'a':
3005 case 'A':
3006 charsetState = CHARSET_A;
3007 continue;
3008 default:
3009 charsetState = CHARSET_INITIAL;
3010 continue;
3012 case CHARSET_A:
3013 switch (c) {
3014 case 'r':
3015 case 'R':
3016 charsetState = CHARSET_R;
3017 continue;
3018 default:
3019 charsetState = CHARSET_INITIAL;
3020 continue;
3022 case CHARSET_R:
3023 switch (c) {
3024 case 's':
3025 case 'S':
3026 charsetState = CHARSET_S;
3027 continue;
3028 default:
3029 charsetState = CHARSET_INITIAL;
3030 continue;
3032 case CHARSET_S:
3033 switch (c) {
3034 case 'e':
3035 case 'E':
3036 charsetState = CHARSET_E;
3037 continue;
3038 default:
3039 charsetState = CHARSET_INITIAL;
3040 continue;
3042 case CHARSET_E:
3043 switch (c) {
3044 case 't':
3045 case 'T':
3046 charsetState = CHARSET_T;
3047 continue;
3048 default:
3049 charsetState = CHARSET_INITIAL;
3050 continue;
3052 case CHARSET_T:
3053 switch (c) {
3054 case '\t':
3055 case '\n':
3056 case '\u000C':
3057 case '\r':
3058 case ' ':
3059 continue;
3060 case '=':
3061 charsetState = CHARSET_EQUALS;
3062 continue;
3063 default:
3064 return null;
3066 case CHARSET_EQUALS:
3067 switch (c) {
3068 case '\t':
3069 case '\n':
3070 case '\u000C':
3071 case '\r':
3072 case ' ':
3073 continue;
3074 case '\'':
3075 start = i + 1;
3076 charsetState = CHARSET_SINGLE_QUOTED;
3077 continue;
3078 case '\"':
3079 start = i + 1;
3080 charsetState = CHARSET_DOUBLE_QUOTED;
3081 continue;
3082 default:
3083 start = i;
3084 charsetState = CHARSET_UNQUOTED;
3085 continue;
3087 case CHARSET_SINGLE_QUOTED:
3088 switch (c) {
3089 case '\'':
3090 end = i;
3091 break charsetloop;
3092 default:
3093 continue;
3095 case CHARSET_DOUBLE_QUOTED:
3096 switch (c) {
3097 case '\"':
3098 end = i;
3099 break charsetloop;
3100 default:
3101 continue;
3103 case CHARSET_UNQUOTED:
3104 switch (c) {
3105 case '\t':
3106 case '\n':
3107 case '\u000C':
3108 case '\r':
3109 case ' ':
3110 case ';':
3111 end = i;
3112 break charsetloop;
3113 default:
3114 continue;
3118 if (start != -1) {
3119 if (end == -1) {
3120 if (charsetState == CHARSET_UNQUOTED) {
3121 end = buffer.length;
3122 } else {
3123 return null;
3126 return Portability.newStringFromBuffer(buffer, start, end
3127 - start
3128 // CPPONLY: , tb, false
3131 return null;
3134 private void checkMetaCharset(HtmlAttributes attributes)
3135 throws SAXException {
3136 String charset = attributes.getValue(AttributeName.CHARSET);
3137 if (charset != null) {
3138 if (tokenizer.internalEncodingDeclaration(charset)) {
3139 requestSuspension();
3140 return;
3142 return;
3144 if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
3145 "content-type",
3146 attributes.getValue(AttributeName.HTTP_EQUIV))) {
3147 return;
3149 String content = attributes.getValue(AttributeName.CONTENT);
3150 if (content != null) {
3151 String extract = TreeBuilder.extractCharsetFromContent(content
3152 // CPPONLY: , this
3154 // remember not to return early without releasing the string
3155 if (extract != null) {
3156 if (tokenizer.internalEncodingDeclaration(extract)) {
3157 requestSuspension();
3160 Portability.releaseString(extract);
3164 public final void endTag(ElementName elementName) throws SAXException {
3165 flushCharacters();
3166 needToDropLF = false;
3167 int eltPos;
3168 int group = elementName.getGroup();
3169 @Local String name = elementName.getName();
3170 endtagloop: for (;;) {
3171 if (isInForeign()) {
3172 if (stack[currentPtr].name != name) {
3173 if (currentPtr == 0) {
3174 errStrayEndTag(name);
3175 } else {
3176 errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr].popName);
3179 eltPos = currentPtr;
3180 int origPos = currentPtr;
3181 for (;;) {
3182 if (eltPos == 0) {
3183 assert fragment: "We can get this close to the root of the stack in foreign content only in the fragment case.";
3184 break endtagloop;
3186 if (stack[eltPos].name == name) {
3187 while (currentPtr >= eltPos) {
3188 popForeign(origPos, eltPos);
3190 break endtagloop;
3192 if (stack[--eltPos].ns == "http://www.w3.org/1999/xhtml") {
3193 break;
3197 switch (mode) {
3198 case IN_TEMPLATE:
3199 switch (group) {
3200 case TEMPLATE:
3201 // fall through to IN_HEAD
3202 break;
3203 default:
3204 errStrayEndTag(name);
3205 break endtagloop;
3207 // CPPONLY: MOZ_FALLTHROUGH;
3208 case IN_ROW:
3209 switch (group) {
3210 case TR:
3211 eltPos = findLastOrRoot(TreeBuilder.TR);
3212 if (eltPos == 0) {
3213 assert fragment || isTemplateContents();
3214 errNoTableRowToClose();
3215 break endtagloop;
3217 clearStackBackTo(eltPos);
3218 pop();
3219 mode = IN_TABLE_BODY;
3220 break endtagloop;
3221 case TABLE:
3222 eltPos = findLastOrRoot(TreeBuilder.TR);
3223 if (eltPos == 0) {
3224 assert fragment || isTemplateContents();
3225 errNoTableRowToClose();
3226 break endtagloop;
3228 clearStackBackTo(eltPos);
3229 pop();
3230 mode = IN_TABLE_BODY;
3231 continue;
3232 case TBODY_OR_THEAD_OR_TFOOT:
3233 if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
3234 errStrayEndTag(name);
3235 break endtagloop;
3237 eltPos = findLastOrRoot(TreeBuilder.TR);
3238 if (eltPos == 0) {
3239 assert fragment || isTemplateContents();
3240 errNoTableRowToClose();
3241 break endtagloop;
3243 clearStackBackTo(eltPos);
3244 pop();
3245 mode = IN_TABLE_BODY;
3246 continue;
3247 case BODY:
3248 case CAPTION:
3249 case COL:
3250 case COLGROUP:
3251 case HTML:
3252 case TD_OR_TH:
3253 errStrayEndTag(name);
3254 break endtagloop;
3255 default:
3256 // fall through to IN_TABLE
3258 // CPPONLY: MOZ_FALLTHROUGH;
3259 case IN_TABLE_BODY:
3260 switch (group) {
3261 case TBODY_OR_THEAD_OR_TFOOT:
3262 eltPos = findLastOrRoot(name);
3263 if (eltPos == 0) {
3264 errStrayEndTag(name);
3265 break endtagloop;
3267 clearStackBackTo(eltPos);
3268 pop();
3269 mode = IN_TABLE;
3270 break endtagloop;
3271 case TABLE:
3272 eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot();
3273 if (eltPos == 0 || stack[eltPos].getGroup() == TEMPLATE) {
3274 assert fragment || isTemplateContents();
3275 errStrayEndTag(name);
3276 break endtagloop;
3278 clearStackBackTo(eltPos);
3279 pop();
3280 mode = IN_TABLE;
3281 continue;
3282 case BODY:
3283 case CAPTION:
3284 case COL:
3285 case COLGROUP:
3286 case HTML:
3287 case TD_OR_TH:
3288 case TR:
3289 errStrayEndTag(name);
3290 break endtagloop;
3291 default:
3292 // fall through to IN_TABLE
3294 // CPPONLY: MOZ_FALLTHROUGH;
3295 case IN_TABLE:
3296 switch (group) {
3297 case TABLE:
3298 eltPos = findLast("table");
3299 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3300 assert fragment || isTemplateContents();
3301 errStrayEndTag(name);
3302 break endtagloop;
3304 while (currentPtr >= eltPos) {
3305 pop();
3307 resetTheInsertionMode();
3308 break endtagloop;
3309 case BODY:
3310 case CAPTION:
3311 case COL:
3312 case COLGROUP:
3313 case HTML:
3314 case TBODY_OR_THEAD_OR_TFOOT:
3315 case TD_OR_TH:
3316 case TR:
3317 errStrayEndTag(name);
3318 break endtagloop;
3319 case TEMPLATE:
3320 // fall through to IN_HEAD
3321 break;
3322 default:
3323 errStrayEndTag(name);
3324 // fall through to IN_BODY
3326 // CPPONLY: MOZ_FALLTHROUGH;
3327 case IN_CAPTION:
3328 switch (group) {
3329 case CAPTION:
3330 eltPos = findLastInTableScope("caption");
3331 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3332 break endtagloop;
3334 generateImpliedEndTags();
3335 if (errorHandler != null && currentPtr != eltPos) {
3336 errUnclosedElements(eltPos, name);
3338 while (currentPtr >= eltPos) {
3339 pop();
3341 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3342 mode = IN_TABLE;
3343 break endtagloop;
3344 case TABLE:
3345 eltPos = findLastInTableScope("caption");
3347 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3348 assert fragment || isTemplateContents();
3349 errStrayEndTag(name);
3350 break endtagloop;
3352 generateImpliedEndTags();
3353 if (errorHandler != null && currentPtr != eltPos) {
3354 errUnclosedElements(eltPos, name);
3356 while (currentPtr >= eltPos) {
3357 pop();
3359 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3360 mode = IN_TABLE;
3361 continue;
3362 case BODY:
3363 case COL:
3364 case COLGROUP:
3365 case HTML:
3366 case TBODY_OR_THEAD_OR_TFOOT:
3367 case TD_OR_TH:
3368 case TR:
3369 errStrayEndTag(name);
3370 break endtagloop;
3371 default:
3372 // fall through to IN_BODY
3374 // CPPONLY: MOZ_FALLTHROUGH;
3375 case IN_CELL:
3376 switch (group) {
3377 case TD_OR_TH:
3378 eltPos = findLastInTableScope(name);
3379 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3380 errStrayEndTag(name);
3381 break endtagloop;
3383 generateImpliedEndTags();
3384 if (errorHandler != null && !isCurrent(name)) {
3385 errUnclosedElements(eltPos, name);
3387 while (currentPtr >= eltPos) {
3388 pop();
3390 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3391 mode = IN_ROW;
3392 break endtagloop;
3393 case TABLE:
3394 case TBODY_OR_THEAD_OR_TFOOT:
3395 case TR:
3396 if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) {
3397 assert name == "tbody" || name == "tfoot" || name == "thead" || fragment || isTemplateContents();
3398 errStrayEndTag(name);
3399 break endtagloop;
3401 closeTheCell(findLastInTableScopeTdTh());
3402 continue;
3403 case BODY:
3404 case CAPTION:
3405 case COL:
3406 case COLGROUP:
3407 case HTML:
3408 errStrayEndTag(name);
3409 break endtagloop;
3410 default:
3411 // fall through to IN_BODY
3413 // CPPONLY: MOZ_FALLTHROUGH;
3414 case FRAMESET_OK:
3415 case IN_BODY:
3416 switch (group) {
3417 case BODY:
3418 if (!isSecondOnStackBody()) {
3419 assert fragment || isTemplateContents();
3420 errStrayEndTag(name);
3421 break endtagloop;
3423 assert currentPtr >= 1;
3424 if (errorHandler != null) {
3425 uncloseloop1: for (int i = 2; i <= currentPtr; i++) {
3426 switch (stack[i].getGroup()) {
3427 case DD_OR_DT:
3428 case LI:
3429 case OPTGROUP:
3430 case OPTION: // is this possible?
3431 case P:
3432 case RB_OR_RTC:
3433 case RT_OR_RP:
3434 case TD_OR_TH:
3435 case TBODY_OR_THEAD_OR_TFOOT:
3436 break;
3437 default:
3438 errEndWithUnclosedElements(name);
3439 break uncloseloop1;
3443 mode = AFTER_BODY;
3444 break endtagloop;
3445 case HTML:
3446 if (!isSecondOnStackBody()) {
3447 assert fragment || isTemplateContents();
3448 errStrayEndTag(name);
3449 break endtagloop;
3451 if (errorHandler != null) {
3452 uncloseloop2: for (int i = 0; i <= currentPtr; i++) {
3453 switch (stack[i].getGroup()) {
3454 case DD_OR_DT:
3455 case LI:
3456 case P:
3457 case RB_OR_RTC:
3458 case RT_OR_RP:
3459 case TBODY_OR_THEAD_OR_TFOOT:
3460 case TD_OR_TH:
3461 case BODY:
3462 case HTML:
3463 break;
3464 default:
3465 errEndWithUnclosedElements(name);
3466 break uncloseloop2;
3470 mode = AFTER_BODY;
3471 continue;
3472 case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU:
3473 case UL_OR_OL_OR_DL:
3474 case PRE_OR_LISTING:
3475 case FIELDSET:
3476 case BUTTON:
3477 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_SEARCH_OR_SECTION_OR_SUMMARY:
3478 eltPos = findLastInScope(name);
3479 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3480 errStrayEndTag(name);
3481 } else {
3482 generateImpliedEndTags();
3483 if (errorHandler != null && !isCurrent(name)) {
3484 errUnclosedElements(eltPos, name);
3486 while (currentPtr >= eltPos) {
3487 pop();
3490 break endtagloop;
3491 case FORM:
3492 if (!isTemplateContents()) {
3493 if (formPointer == null) {
3494 errStrayEndTag(name);
3495 break endtagloop;
3497 formPointer = null;
3498 eltPos = findLastInScope(name);
3499 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3500 errStrayEndTag(name);
3501 break endtagloop;
3503 generateImpliedEndTags();
3504 if (errorHandler != null && !isCurrent(name)) {
3505 errUnclosedElements(eltPos, name);
3507 removeFromStack(eltPos);
3508 break endtagloop;
3509 } else {
3510 eltPos = findLastInScope(name);
3511 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3512 errStrayEndTag(name);
3513 break endtagloop;
3515 generateImpliedEndTags();
3516 if (errorHandler != null && !isCurrent(name)) {
3517 errUnclosedElements(eltPos, name);
3519 while (currentPtr >= eltPos) {
3520 pop();
3522 break endtagloop;
3524 case P:
3525 eltPos = findLastInButtonScope("p");
3526 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3527 errNoElementToCloseButEndTagSeen("p");
3528 // XXX Can the 'in foreign' case happen anymore?
3529 if (isInForeign()) {
3530 errHtmlStartTagInForeignContext(name);
3531 // Check for currentPtr for the fragment
3532 // case.
3533 while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
3534 pop();
3537 appendVoidElementToCurrentMayFoster(
3538 elementName,
3539 HtmlAttributes.EMPTY_ATTRIBUTES);
3540 break endtagloop;
3542 generateImpliedEndTagsExceptFor("p");
3543 assert eltPos != TreeBuilder.NOT_FOUND_ON_STACK;
3544 if (errorHandler != null && eltPos != currentPtr) {
3545 errUnclosedElements(eltPos, name);
3547 while (currentPtr >= eltPos) {
3548 pop();
3550 break endtagloop;
3551 case LI:
3552 eltPos = findLastInListScope(name);
3553 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3554 errNoElementToCloseButEndTagSeen(name);
3555 } else {
3556 generateImpliedEndTagsExceptFor(name);
3557 if (errorHandler != null
3558 && eltPos != currentPtr) {
3559 errUnclosedElements(eltPos, name);
3561 while (currentPtr >= eltPos) {
3562 pop();
3565 break endtagloop;
3566 case DD_OR_DT:
3567 eltPos = findLastInScope(name);
3568 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3569 errNoElementToCloseButEndTagSeen(name);
3570 } else {
3571 generateImpliedEndTagsExceptFor(name);
3572 if (errorHandler != null
3573 && eltPos != currentPtr) {
3574 errUnclosedElements(eltPos, name);
3576 while (currentPtr >= eltPos) {
3577 pop();
3580 break endtagloop;
3581 case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
3582 eltPos = findLastInScopeHn();
3583 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3584 errStrayEndTag(name);
3585 } else {
3586 generateImpliedEndTags();
3587 if (errorHandler != null && !isCurrent(name)) {
3588 errUnclosedElements(eltPos, name);
3590 while (currentPtr >= eltPos) {
3591 pop();
3594 break endtagloop;
3595 case OBJECT:
3596 case MARQUEE_OR_APPLET:
3597 eltPos = findLastInScope(name);
3598 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3599 errStrayEndTag(name);
3600 } else {
3601 generateImpliedEndTags();
3602 if (errorHandler != null && !isCurrent(name)) {
3603 errUnclosedElements(eltPos, name);
3605 while (currentPtr >= eltPos) {
3606 pop();
3608 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3610 break endtagloop;
3611 case BR:
3612 errEndTagBr();
3613 if (isInForeign()) {
3614 // XXX can this happen anymore?
3615 errHtmlStartTagInForeignContext(name);
3616 // Check for currentPtr for the fragment
3617 // case.
3618 while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
3619 pop();
3622 reconstructTheActiveFormattingElements();
3623 appendVoidElementToCurrentMayFoster(
3624 elementName,
3625 HtmlAttributes.EMPTY_ATTRIBUTES);
3626 break endtagloop;
3627 case TEMPLATE:
3628 // fall through to IN_HEAD;
3629 break;
3630 case AREA_OR_WBR:
3631 case KEYGEN: // XXX??
3632 case PARAM_OR_SOURCE_OR_TRACK:
3633 case EMBED:
3634 case IMG:
3635 case IMAGE:
3636 case INPUT:
3637 case HR:
3638 case IFRAME:
3639 case NOEMBED: // XXX???
3640 case NOFRAMES: // XXX??
3641 case SELECT:
3642 case TABLE:
3643 case TEXTAREA: // XXX??
3644 errStrayEndTag(name);
3645 break endtagloop;
3646 case NOSCRIPT:
3647 if (scriptingEnabled) {
3648 errStrayEndTag(name);
3649 break endtagloop;
3651 // CPPONLY: MOZ_FALLTHROUGH;
3652 case A:
3653 case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U:
3654 case FONT:
3655 case NOBR:
3656 if (adoptionAgencyEndTag(name)) {
3657 break endtagloop;
3659 // else handle like any other tag
3660 // CPPONLY: MOZ_FALLTHROUGH;
3661 default:
3662 if (isCurrent(name)) {
3663 pop();
3664 break endtagloop;
3667 eltPos = currentPtr;
3668 for (;;) {
3669 StackNode<T> node = stack[eltPos];
3670 if (node.ns == "http://www.w3.org/1999/xhtml" && node.name == name) {
3671 generateImpliedEndTags();
3672 if (errorHandler != null
3673 && !isCurrent(name)) {
3674 errUnclosedElements(eltPos, name);
3676 while (currentPtr >= eltPos) {
3677 pop();
3679 break endtagloop;
3680 } else if (eltPos == 0 || node.isSpecial()) {
3681 errStrayEndTag(name);
3682 break endtagloop;
3684 eltPos--;
3687 // CPPONLY: MOZ_FALLTHROUGH;
3688 case IN_HEAD:
3689 switch (group) {
3690 case HEAD:
3691 pop();
3692 mode = AFTER_HEAD;
3693 break endtagloop;
3694 case BR:
3695 case HTML:
3696 case BODY:
3697 pop();
3698 mode = AFTER_HEAD;
3699 continue;
3700 case TEMPLATE:
3701 endTagTemplateInHead();
3702 break endtagloop;
3703 default:
3704 errStrayEndTag(name);
3705 break endtagloop;
3707 case IN_HEAD_NOSCRIPT:
3708 switch (group) {
3709 case NOSCRIPT:
3710 pop();
3711 mode = IN_HEAD;
3712 break endtagloop;
3713 case BR:
3714 errStrayEndTag(name);
3715 pop();
3716 mode = IN_HEAD;
3717 continue;
3718 default:
3719 errStrayEndTag(name);
3720 break endtagloop;
3722 case IN_COLUMN_GROUP:
3723 switch (group) {
3724 case COLGROUP:
3725 if (currentPtr == 0 || stack[currentPtr].getGroup() ==
3726 TreeBuilder.TEMPLATE) {
3727 assert fragment || isTemplateContents();
3728 errGarbageInColgroup();
3729 break endtagloop;
3731 pop();
3732 mode = IN_TABLE;
3733 break endtagloop;
3734 case COL:
3735 errStrayEndTag(name);
3736 break endtagloop;
3737 case TEMPLATE:
3738 endTagTemplateInHead();
3739 break endtagloop;
3740 default:
3741 if (currentPtr == 0 || stack[currentPtr].getGroup() ==
3742 TreeBuilder.TEMPLATE) {
3743 assert fragment || isTemplateContents();
3744 errGarbageInColgroup();
3745 break endtagloop;
3747 pop();
3748 mode = IN_TABLE;
3749 continue;
3751 case IN_SELECT_IN_TABLE:
3752 switch (group) {
3753 case CAPTION:
3754 case TABLE:
3755 case TBODY_OR_THEAD_OR_TFOOT:
3756 case TR:
3757 case TD_OR_TH:
3758 errEndTagSeenWithSelectOpen(name);
3759 if (findLastInTableScope(name) != TreeBuilder.NOT_FOUND_ON_STACK) {
3760 eltPos = findLastInTableScope("select");
3761 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3762 assert fragment;
3763 break endtagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375
3765 while (currentPtr >= eltPos) {
3766 pop();
3768 resetTheInsertionMode();
3769 continue;
3770 } else {
3771 break endtagloop;
3773 default:
3774 // fall through to IN_SELECT
3776 // CPPONLY: MOZ_FALLTHROUGH;
3777 case IN_SELECT:
3778 switch (group) {
3779 case OPTION:
3780 if (isCurrent("option")) {
3781 pop();
3782 break endtagloop;
3783 } else {
3784 errStrayEndTag(name);
3785 break endtagloop;
3787 case OPTGROUP:
3788 if (isCurrent("option")
3789 && "optgroup" == stack[currentPtr - 1].name) {
3790 pop();
3792 if (isCurrent("optgroup")) {
3793 pop();
3794 } else {
3795 errStrayEndTag(name);
3797 break endtagloop;
3798 case SELECT:
3799 eltPos = findLastInTableScope("select");
3800 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3801 assert fragment;
3802 errStrayEndTag(name);
3803 break endtagloop;
3805 while (currentPtr >= eltPos) {
3806 pop();
3808 resetTheInsertionMode();
3809 break endtagloop;
3810 case TEMPLATE:
3811 endTagTemplateInHead();
3812 break endtagloop;
3813 default:
3814 errStrayEndTag(name);
3815 break endtagloop;
3817 case AFTER_BODY:
3818 switch (group) {
3819 case HTML:
3820 if (fragment) {
3821 errStrayEndTag(name);
3822 break endtagloop;
3823 } else {
3824 mode = AFTER_AFTER_BODY;
3825 break endtagloop;
3827 default:
3828 errEndTagAfterBody();
3829 mode = framesetOk ? FRAMESET_OK : IN_BODY;
3830 continue;
3832 case IN_FRAMESET:
3833 switch (group) {
3834 case FRAMESET:
3835 if (currentPtr == 0) {
3836 assert fragment;
3837 errStrayEndTag(name);
3838 break endtagloop;
3840 pop();
3841 if ((!fragment) && !isCurrent("frameset")) {
3842 mode = AFTER_FRAMESET;
3844 break endtagloop;
3845 default:
3846 errStrayEndTag(name);
3847 break endtagloop;
3849 case AFTER_FRAMESET:
3850 switch (group) {
3851 case HTML:
3852 mode = AFTER_AFTER_FRAMESET;
3853 break endtagloop;
3854 default:
3855 errStrayEndTag(name);
3856 break endtagloop;
3858 case INITIAL:
3860 * Parse error.
3862 errEndTagSeenWithoutDoctype();
3865 * Set the document to quirks mode.
3867 documentModeInternal(DocumentMode.QUIRKS_MODE, null, null);
3869 * Then, switch to the root element mode of the tree
3870 * construction stage
3872 mode = BEFORE_HTML;
3874 * and reprocess the current token.
3876 continue;
3877 case BEFORE_HTML:
3878 switch (group) {
3879 case HEAD:
3880 case BR:
3881 case HTML:
3882 case BODY:
3884 * Create an HTMLElement node with the tag name
3885 * html, in the HTML namespace. Append it to the
3886 * Document object.
3888 appendHtmlElementToDocumentAndPush();
3889 /* Switch to the main mode */
3890 mode = BEFORE_HEAD;
3892 * reprocess the current token.
3894 continue;
3895 default:
3896 errStrayEndTag(name);
3897 break endtagloop;
3899 case BEFORE_HEAD:
3900 switch (group) {
3901 case HEAD:
3902 case BR:
3903 case HTML:
3904 case BODY:
3905 appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES);
3906 mode = IN_HEAD;
3907 continue;
3908 default:
3909 errStrayEndTag(name);
3910 break endtagloop;
3912 case AFTER_HEAD:
3913 switch (group) {
3914 case TEMPLATE:
3915 endTagTemplateInHead();
3916 break endtagloop;
3917 case HTML:
3918 case BODY:
3919 case BR:
3920 appendToCurrentNodeAndPushBodyElement();
3921 mode = FRAMESET_OK;
3922 continue;
3923 default:
3924 errStrayEndTag(name);
3925 break endtagloop;
3927 case AFTER_AFTER_BODY:
3928 errStrayEndTag(name);
3929 mode = framesetOk ? FRAMESET_OK : IN_BODY;
3930 continue;
3931 case AFTER_AFTER_FRAMESET:
3932 errStrayEndTag(name);
3933 break endtagloop;
3934 case TEXT:
3935 // XXX need to manage insertion point here
3936 pop();
3937 if (originalMode == AFTER_HEAD) {
3938 silentPop();
3940 mode = originalMode;
3941 break endtagloop;
3943 } // endtagloop
3946 private void endTagTemplateInHead() throws SAXException {
3947 int eltPos = findLast("template");
3948 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
3949 errStrayEndTag("template");
3950 return;
3952 generateImpliedEndTagsThoroughly();
3953 if (errorHandler != null && !isCurrent("template")) {
3954 errUnclosedElements(eltPos, "template");
3956 while (currentPtr >= eltPos) {
3957 pop();
3959 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
3960 popTemplateMode();
3961 resetTheInsertionMode();
3964 private int findLastInTableScopeOrRootTemplateTbodyTheadTfoot() {
3965 for (int i = currentPtr; i > 0; i--) {
3966 if (stack[i].ns == "http://www.w3.org/1999/xhtml"
3967 && (stack[i].getGroup() == TreeBuilder.TBODY_OR_THEAD_OR_TFOOT
3968 || stack[i].getGroup() == TreeBuilder.TEMPLATE)) {
3969 return i;
3972 return 0;
3975 private int findLast(@Local String name) {
3976 for (int i = currentPtr; i > 0; i--) {
3977 if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) {
3978 return i;
3981 return TreeBuilder.NOT_FOUND_ON_STACK;
3984 private int findLastInTableScope(@Local String name) {
3985 for (int i = currentPtr; i > 0; i--) {
3986 if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
3987 if (stack[i].name == name) {
3988 return i;
3989 } else if (stack[i].name == "table" || stack[i].name == "template") {
3990 return TreeBuilder.NOT_FOUND_ON_STACK;
3994 return TreeBuilder.NOT_FOUND_ON_STACK;
3997 private int findLastInButtonScope(@Local String name) {
3998 for (int i = currentPtr; i > 0; i--) {
3999 if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
4000 if (stack[i].name == name) {
4001 return i;
4002 } else if (stack[i].name == "button") {
4003 return TreeBuilder.NOT_FOUND_ON_STACK;
4007 if (stack[i].isScoping()) {
4008 return TreeBuilder.NOT_FOUND_ON_STACK;
4011 return TreeBuilder.NOT_FOUND_ON_STACK;
4014 private int findLastInScope(@Local String name) {
4015 for (int i = currentPtr; i > 0; i--) {
4016 if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) {
4017 return i;
4018 } else if (stack[i].isScoping()) {
4019 return TreeBuilder.NOT_FOUND_ON_STACK;
4022 return TreeBuilder.NOT_FOUND_ON_STACK;
4025 private int findLastInListScope(@Local String name) {
4026 for (int i = currentPtr; i > 0; i--) {
4027 if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
4028 if (stack[i].name == name) {
4029 return i;
4030 } else if (stack[i].name == "ul" || stack[i].name == "ol") {
4031 return TreeBuilder.NOT_FOUND_ON_STACK;
4035 if (stack[i].isScoping()) {
4036 return TreeBuilder.NOT_FOUND_ON_STACK;
4039 return TreeBuilder.NOT_FOUND_ON_STACK;
4042 private int findLastInScopeHn() {
4043 for (int i = currentPtr; i > 0; i--) {
4044 if (stack[i].getGroup() == TreeBuilder.H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) {
4045 return i;
4046 } else if (stack[i].isScoping()) {
4047 return TreeBuilder.NOT_FOUND_ON_STACK;
4050 return TreeBuilder.NOT_FOUND_ON_STACK;
4053 private void generateImpliedEndTagsExceptFor(@Local String name)
4054 throws SAXException {
4055 for (;;) {
4056 StackNode<T> node = stack[currentPtr];
4057 switch (node.getGroup()) {
4058 case P:
4059 case LI:
4060 case DD_OR_DT:
4061 case OPTION:
4062 case OPTGROUP:
4063 case RB_OR_RTC:
4064 case RT_OR_RP:
4065 if (node.ns == "http://www.w3.org/1999/xhtml" && node.name == name) {
4066 return;
4068 pop();
4069 continue;
4070 default:
4071 return;
4076 private void generateImpliedEndTags() throws SAXException {
4077 for (;;) {
4078 switch (stack[currentPtr].getGroup()) {
4079 case P:
4080 case LI:
4081 case DD_OR_DT:
4082 case OPTION:
4083 case OPTGROUP:
4084 case RB_OR_RTC:
4085 case RT_OR_RP:
4086 pop();
4087 continue;
4088 default:
4089 return;
4094 private void generateImpliedEndTagsThoroughly() throws SAXException {
4095 for (;;) {
4096 switch (stack[currentPtr].getGroup()) {
4097 case CAPTION:
4098 case COLGROUP:
4099 case DD_OR_DT:
4100 case LI:
4101 case OPTGROUP:
4102 case OPTION:
4103 case P:
4104 case RB_OR_RTC:
4105 case RT_OR_RP:
4106 case TBODY_OR_THEAD_OR_TFOOT:
4107 case TD_OR_TH:
4108 case TR:
4109 pop();
4110 continue;
4111 default:
4112 return;
4117 private boolean isSecondOnStackBody() {
4118 return currentPtr >= 1 && stack[1].getGroup() == TreeBuilder.BODY;
4121 private void documentModeInternal(DocumentMode m, String publicIdentifier,
4122 String systemIdentifier)
4123 throws SAXException {
4125 if (forceNoQuirks) {
4126 // Srcdoc documents are always rendered in standards mode.
4127 quirks = false;
4128 if (documentModeHandler != null) {
4129 documentModeHandler.documentMode(
4130 DocumentMode.STANDARDS_MODE
4131 // [NOCPP[
4132 , null, null
4133 // ]NOCPP]
4136 return;
4139 quirks = (m == DocumentMode.QUIRKS_MODE);
4140 if (documentModeHandler != null) {
4141 documentModeHandler.documentMode(
4143 // [NOCPP[
4144 , publicIdentifier, systemIdentifier
4145 // ]NOCPP]
4148 // [NOCPP[
4149 documentMode(m, publicIdentifier, systemIdentifier);
4150 // ]NOCPP]
4153 private boolean isAlmostStandards(String publicIdentifier,
4154 String systemIdentifier) {
4155 if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4156 "-//w3c//dtd xhtml 1.0 transitional//", publicIdentifier)) {
4157 return true;
4159 if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4160 "-//w3c//dtd xhtml 1.0 frameset//", publicIdentifier)) {
4161 return true;
4163 if (systemIdentifier != null) {
4164 if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4165 "-//w3c//dtd html 4.01 transitional//", publicIdentifier)) {
4166 return true;
4168 if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4169 "-//w3c//dtd html 4.01 frameset//", publicIdentifier)) {
4170 return true;
4173 return false;
4176 private boolean isQuirky(@Local String name, String publicIdentifier,
4177 String systemIdentifier, boolean forceQuirks) {
4178 if (forceQuirks) {
4179 return true;
4181 if (name != HTML_LOCAL) {
4182 return true;
4184 if (publicIdentifier != null) {
4185 for (int i = 0; i < TreeBuilder.QUIRKY_PUBLIC_IDS.length; i++) {
4186 if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4187 TreeBuilder.QUIRKY_PUBLIC_IDS[i], publicIdentifier)) {
4188 return true;
4191 if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4192 "-//w3o//dtd w3 html strict 3.0//en//", publicIdentifier)
4193 || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4194 "-/w3c/dtd html 4.0 transitional/en",
4195 publicIdentifier)
4196 || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4197 "html", publicIdentifier)) {
4198 return true;
4201 if (systemIdentifier == null) {
4202 if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4203 "-//w3c//dtd html 4.01 transitional//", publicIdentifier)) {
4204 return true;
4205 } else if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString(
4206 "-//w3c//dtd html 4.01 frameset//", publicIdentifier)) {
4207 return true;
4209 } else if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
4210 "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd",
4211 systemIdentifier)) {
4212 return true;
4214 return false;
4217 private void closeTheCell(int eltPos) throws SAXException {
4218 generateImpliedEndTags();
4219 if (errorHandler != null && eltPos != currentPtr) {
4220 errUnclosedElementsCell(eltPos);
4222 while (currentPtr >= eltPos) {
4223 pop();
4225 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
4226 mode = IN_ROW;
4227 return;
4230 private int findLastInTableScopeTdTh() {
4231 for (int i = currentPtr; i > 0; i--) {
4232 @Local String name = stack[i].name;
4233 if (stack[i].ns == "http://www.w3.org/1999/xhtml") {
4234 if ("td" == name || "th" == name) {
4235 return i;
4236 } else if (name == "table" || name == "template") {
4237 return TreeBuilder.NOT_FOUND_ON_STACK;
4241 return TreeBuilder.NOT_FOUND_ON_STACK;
4244 private void clearStackBackTo(int eltPos) throws SAXException {
4245 int eltGroup = stack[eltPos].getGroup();
4246 while (currentPtr > eltPos) { // > not >= intentional
4247 if (stack[currentPtr].ns == "http://www.w3.org/1999/xhtml"
4248 && stack[currentPtr].getGroup() == TEMPLATE
4249 && (eltGroup == TABLE || eltGroup == TBODY_OR_THEAD_OR_TFOOT|| eltGroup == TR || eltPos == 0)) {
4250 return;
4252 pop();
4256 private void resetTheInsertionMode() {
4257 StackNode<T> node;
4258 @Local String name;
4259 @NsUri String ns;
4260 for (int i = currentPtr; i >= 0; i--) {
4261 node = stack[i];
4262 name = node.name;
4263 ns = node.ns;
4264 if (i == 0) {
4265 if (!(contextNamespace == "http://www.w3.org/1999/xhtml" && (contextName == "td" || contextName == "th"))) {
4266 if (fragment) {
4267 // Make sure we are parsing a fragment otherwise the context element doesn't make sense.
4268 name = contextName;
4269 ns = contextNamespace;
4271 } else {
4272 mode = framesetOk ? FRAMESET_OK : IN_BODY; // XXX from Hixie's email
4273 return;
4276 if ("select" == name) {
4277 int ancestorIndex = i;
4278 while (ancestorIndex > 0) {
4279 StackNode<T> ancestor = stack[ancestorIndex--];
4280 if ("http://www.w3.org/1999/xhtml" == ancestor.ns) {
4281 if ("template" == ancestor.name) {
4282 break;
4284 if ("table" == ancestor.name) {
4285 mode = IN_SELECT_IN_TABLE;
4286 return;
4290 mode = IN_SELECT;
4291 return;
4292 } else if ("td" == name || "th" == name) {
4293 mode = IN_CELL;
4294 return;
4295 } else if ("tr" == name) {
4296 mode = IN_ROW;
4297 return;
4298 } else if ("tbody" == name || "thead" == name || "tfoot" == name) {
4299 mode = IN_TABLE_BODY;
4300 return;
4301 } else if ("caption" == name) {
4302 mode = IN_CAPTION;
4303 return;
4304 } else if ("colgroup" == name) {
4305 mode = IN_COLUMN_GROUP;
4306 return;
4307 } else if ("table" == name) {
4308 mode = IN_TABLE;
4309 return;
4310 } else if ("http://www.w3.org/1999/xhtml" != ns) {
4311 mode = framesetOk ? FRAMESET_OK : IN_BODY;
4312 return;
4313 } else if ("template" == name) {
4314 assert templateModePtr >= 0;
4315 mode = templateModeStack[templateModePtr];
4316 return;
4317 } else if ("head" == name) {
4318 if (name == contextName) {
4319 mode = framesetOk ? FRAMESET_OK : IN_BODY; // really
4320 } else {
4321 mode = IN_HEAD;
4323 return;
4324 } else if ("body" == name) {
4325 mode = framesetOk ? FRAMESET_OK : IN_BODY;
4326 return;
4327 } else if ("frameset" == name) {
4328 // TODO: Fragment case. Add error reporting.
4329 mode = IN_FRAMESET;
4330 return;
4331 } else if ("html" == name) {
4332 if (headPointer == null) {
4333 // TODO: Fragment case. Add error reporting.
4334 mode = BEFORE_HEAD;
4335 } else {
4336 mode = AFTER_HEAD;
4338 return;
4339 } else if (i == 0) {
4340 mode = framesetOk ? FRAMESET_OK : IN_BODY;
4341 return;
4347 * @throws SAXException
4350 private void implicitlyCloseP() throws SAXException {
4351 int eltPos = findLastInButtonScope("p");
4352 if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
4353 return;
4355 generateImpliedEndTagsExceptFor("p");
4356 if (errorHandler != null && eltPos != currentPtr) {
4357 errUnclosedElementsImplied(eltPos, "p");
4359 while (currentPtr >= eltPos) {
4360 pop();
4364 private boolean debugOnlyClearLastStackSlot() {
4365 stack[currentPtr] = null;
4366 return true;
4369 private boolean debugOnlyClearLastListSlot() {
4370 listOfActiveFormattingElements[listPtr] = null;
4371 return true;
4374 private void pushTemplateMode(int mode) {
4375 templateModePtr++;
4376 if (templateModePtr == templateModeStack.length) {
4377 int[] newStack = new int[templateModeStack.length + 64];
4378 System.arraycopy(templateModeStack, 0, newStack, 0, templateModeStack.length);
4379 templateModeStack = newStack;
4381 templateModeStack[templateModePtr] = mode;
4384 @SuppressWarnings("unchecked") private void push(StackNode<T> node) throws SAXException {
4385 currentPtr++;
4386 if (currentPtr == stack.length) {
4387 StackNode<T>[] newStack = new StackNode[stack.length + 64];
4388 System.arraycopy(stack, 0, newStack, 0, stack.length);
4389 stack = newStack;
4391 stack[currentPtr] = node;
4392 elementPushed(node.ns, node.popName, node.node);
4395 @SuppressWarnings("unchecked") private void silentPush(StackNode<T> node) throws SAXException {
4396 currentPtr++;
4397 if (currentPtr == stack.length) {
4398 StackNode<T>[] newStack = new StackNode[stack.length + 64];
4399 System.arraycopy(stack, 0, newStack, 0, stack.length);
4400 stack = newStack;
4402 stack[currentPtr] = node;
4405 @SuppressWarnings("unchecked") private void append(StackNode<T> node) {
4406 listPtr++;
4407 if (listPtr == listOfActiveFormattingElements.length) {
4408 StackNode<T>[] newList = new StackNode[listOfActiveFormattingElements.length + 64];
4409 System.arraycopy(listOfActiveFormattingElements, 0, newList, 0,
4410 listOfActiveFormattingElements.length);
4411 listOfActiveFormattingElements = newList;
4413 listOfActiveFormattingElements[listPtr] = node;
4416 @Inline private void insertMarker() {
4417 append(null);
4420 private void clearTheListOfActiveFormattingElementsUpToTheLastMarker() {
4421 while (listPtr > -1) {
4422 if (listOfActiveFormattingElements[listPtr] == null) {
4423 --listPtr;
4424 return;
4426 listOfActiveFormattingElements[listPtr].release(this);
4427 --listPtr;
4431 @Inline private boolean isCurrent(@Local String name) {
4432 return stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" &&
4433 name == stack[currentPtr].name;
4436 private void removeFromStack(int pos) throws SAXException {
4437 if (currentPtr == pos) {
4438 pop();
4439 } else {
4440 fatal();
4441 stack[pos].release(this);
4442 System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
4443 assert debugOnlyClearLastStackSlot();
4444 currentPtr--;
4448 private void removeFromStack(StackNode<T> node) throws SAXException {
4449 if (stack[currentPtr] == node) {
4450 pop();
4451 } else {
4452 int pos = currentPtr - 1;
4453 while (pos >= 0 && stack[pos] != node) {
4454 pos--;
4456 if (pos == -1) {
4457 // dead code?
4458 return;
4460 fatal();
4461 node.release(this);
4462 System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos);
4463 currentPtr--;
4467 private void removeFromListOfActiveFormattingElements(int pos) {
4468 assert listOfActiveFormattingElements[pos] != null;
4469 listOfActiveFormattingElements[pos].release(this);
4470 if (pos == listPtr) {
4471 assert debugOnlyClearLastListSlot();
4472 listPtr--;
4473 return;
4475 assert pos < listPtr;
4476 System.arraycopy(listOfActiveFormattingElements, pos + 1,
4477 listOfActiveFormattingElements, pos, listPtr - pos);
4478 assert debugOnlyClearLastListSlot();
4479 listPtr--;
4483 * Adoption agency algorithm.
4485 * @param name subject as described in the specified algorithm.
4486 * @return Returns true if the algorithm has completed and there is nothing remaining to
4487 * be done. Returns false if the algorithm needs to "act as described in the 'any other
4488 * end tag' entry" as described in the specified algorithm.
4489 * @throws SAXException
4491 private boolean adoptionAgencyEndTag(@Local String name) throws SAXException {
4492 // This check intends to ensure that for properly nested tags, closing tags will match
4493 // against the stack instead of the listOfActiveFormattingElements.
4494 if (stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" &&
4495 stack[currentPtr].name == name &&
4496 findInListOfActiveFormattingElements(stack[currentPtr]) == -1) {
4497 // If the current element matches the name but isn't on the list of active
4498 // formatting elements, then it is possible that the list was mangled by the Noah's Ark
4499 // clause. In this case, we want to match the end tag against the stack instead of
4500 // proceeding with the AAA algorithm that may match against the list of
4501 // active formatting elements (and possibly mangle the tree in unexpected ways).
4502 pop();
4503 return true;
4506 // If you crash around here, perhaps some stack node variable claimed to
4507 // be a weak ref isn't.
4508 for (int i = 0; i < 8; ++i) {
4509 int formattingEltListPos = listPtr;
4510 while (formattingEltListPos > -1) {
4511 StackNode<T> listNode = listOfActiveFormattingElements[formattingEltListPos]; // weak ref
4512 if (listNode == null) {
4513 formattingEltListPos = -1;
4514 break;
4515 } else if (listNode.name == name) {
4516 break;
4518 formattingEltListPos--;
4520 if (formattingEltListPos == -1) {
4521 return false;
4523 // this *looks* like a weak ref to the list of formatting elements
4524 StackNode<T> formattingElt = listOfActiveFormattingElements[formattingEltListPos];
4525 int formattingEltStackPos = currentPtr;
4526 boolean inScope = true;
4527 while (formattingEltStackPos > -1) {
4528 StackNode<T> node = stack[formattingEltStackPos]; // weak ref
4529 if (node == formattingElt) {
4530 break;
4531 } else if (node.isScoping()) {
4532 inScope = false;
4534 formattingEltStackPos--;
4536 if (formattingEltStackPos == -1) {
4537 errNoElementToCloseButEndTagSeen(name);
4538 removeFromListOfActiveFormattingElements(formattingEltListPos);
4539 return true;
4541 if (!inScope) {
4542 errNoElementToCloseButEndTagSeen(name);
4543 return true;
4545 // stackPos now points to the formatting element and it is in scope
4546 if (formattingEltStackPos != currentPtr) {
4547 errEndTagViolatesNestingRules(name);
4549 int furthestBlockPos = formattingEltStackPos + 1;
4550 while (furthestBlockPos <= currentPtr) {
4551 StackNode<T> node = stack[furthestBlockPos]; // weak ref
4552 assert furthestBlockPos > 0: "How is formattingEltStackPos + 1 not > 0?";
4553 if (node.isSpecial()) {
4554 break;
4556 furthestBlockPos++;
4558 if (furthestBlockPos > currentPtr) {
4559 // no furthest block
4560 while (currentPtr >= formattingEltStackPos) {
4561 pop();
4563 removeFromListOfActiveFormattingElements(formattingEltListPos);
4564 return true;
4566 // commonAncestor is used for running the algorithm and
4567 // insertionCommonAncestor is used for the actual insertions to
4568 // keep them depth-limited.
4569 StackNode<T> commonAncestor = stack[formattingEltStackPos - 1]; // weak ref
4570 T insertionCommonAncestor = nodeFromStackWithBlinkCompat(formattingEltStackPos - 1); // weak ref
4571 StackNode<T> furthestBlock = stack[furthestBlockPos]; // weak ref
4572 // detachFromParent(furthestBlock.node); XXX AAA CHANGE
4573 int bookmark = formattingEltListPos;
4574 int nodePos = furthestBlockPos;
4575 StackNode<T> lastNode = furthestBlock; // weak ref
4576 int j = 0;
4577 for (;;) {
4578 ++j;
4579 nodePos--;
4580 if (nodePos == formattingEltStackPos) {
4581 break;
4583 StackNode<T> node = stack[nodePos]; // weak ref
4584 int nodeListPos = findInListOfActiveFormattingElements(node);
4586 if (j > 3 && nodeListPos != -1) {
4587 removeFromListOfActiveFormattingElements(nodeListPos);
4589 // Adjust the indices into the list to account
4590 // for the removal of nodeListPos.
4591 if (nodeListPos <= formattingEltListPos) {
4592 formattingEltListPos--;
4594 if (nodeListPos <= bookmark) {
4595 bookmark--;
4598 // Update position to reflect removal from list.
4599 nodeListPos = -1;
4602 if (nodeListPos == -1) {
4603 assert formattingEltStackPos < nodePos;
4604 assert bookmark < nodePos;
4605 assert furthestBlockPos > nodePos;
4606 removeFromStack(nodePos); // node is now a bad pointer in C++
4607 furthestBlockPos--;
4608 continue;
4610 // now node is both on stack and in the list
4611 if (nodePos == furthestBlockPos) {
4612 bookmark = nodeListPos + 1;
4614 // if (hasChildren(node.node)) { XXX AAA CHANGE
4615 assert node == listOfActiveFormattingElements[nodeListPos];
4616 assert node == stack[nodePos];
4617 T clone = createElement("http://www.w3.org/1999/xhtml",
4618 node.name, node.attributes.cloneAttributes(), insertionCommonAncestor
4619 // CPPONLY: , htmlCreator(node.getHtmlCreator())
4621 StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
4622 node.name, clone, node.popName, node.attributes
4623 // CPPONLY: , node.getHtmlCreator()
4624 // [NOCPP[
4625 , node.getLocator()
4626 // ]NOCPP]
4627 ); // creation ownership goes to stack
4628 node.dropAttributes(); // adopt ownership to newNode
4629 stack[nodePos] = newNode;
4630 newNode.retain(); // retain for list
4631 listOfActiveFormattingElements[nodeListPos] = newNode;
4632 node.release(this); // release from stack
4633 node.release(this); // release from list
4634 node = newNode;
4635 // } XXX AAA CHANGE
4636 detachFromParent(lastNode.node);
4637 appendElement(lastNode.node, nodeFromStackWithBlinkCompat(nodePos));
4638 lastNode = node;
4640 // If we insert into a foster parent, for simplicity, we insert
4641 // accoding to the spec without Blink's depth limit.
4642 if (commonAncestor.isFosterParenting()) {
4643 fatal();
4644 detachFromParent(lastNode.node);
4645 insertIntoFosterParent(lastNode.node);
4646 } else {
4647 detachFromParent(lastNode.node);
4648 appendElement(lastNode.node, insertionCommonAncestor);
4650 T clone = createElement("http://www.w3.org/1999/xhtml",
4651 formattingElt.name,
4652 formattingElt.attributes.cloneAttributes(), furthestBlock.node
4653 // CPPONLY: , htmlCreator(formattingElt.getHtmlCreator())
4655 StackNode<T> formattingClone = createStackNode(
4656 formattingElt.getFlags(), formattingElt.ns,
4657 formattingElt.name, clone, formattingElt.popName,
4658 formattingElt.attributes
4659 // CPPONLY: , formattingElt.getHtmlCreator()
4660 // [NOCPP[
4661 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4662 // ]NOCPP]
4663 ); // Ownership transfers to stack below
4664 formattingElt.dropAttributes(); // transfer ownership to
4665 // formattingClone
4666 appendChildrenToNewParent(furthestBlock.node, clone);
4667 appendElement(clone, furthestBlock.node);
4668 removeFromListOfActiveFormattingElements(formattingEltListPos);
4669 insertIntoListOfActiveFormattingElements(formattingClone, bookmark);
4670 assert formattingEltStackPos < furthestBlockPos;
4671 removeFromStack(formattingEltStackPos);
4672 // furthestBlockPos is now off by one and points to the slot after
4673 // it
4674 insertIntoStack(formattingClone, furthestBlockPos);
4676 return true;
4679 private void insertIntoStack(StackNode<T> node, int position)
4680 throws SAXException {
4681 assert currentPtr + 1 < stack.length;
4682 assert position <= currentPtr + 1;
4683 if (position == currentPtr + 1) {
4684 push(node);
4685 } else {
4686 System.arraycopy(stack, position, stack, position + 1,
4687 (currentPtr - position) + 1);
4688 currentPtr++;
4689 stack[position] = node;
4693 private void insertIntoListOfActiveFormattingElements(
4694 StackNode<T> formattingClone, int bookmark) {
4695 formattingClone.retain();
4696 assert listPtr + 1 < listOfActiveFormattingElements.length;
4697 if (bookmark <= listPtr) {
4698 System.arraycopy(listOfActiveFormattingElements, bookmark,
4699 listOfActiveFormattingElements, bookmark + 1,
4700 (listPtr - bookmark) + 1);
4702 listPtr++;
4703 listOfActiveFormattingElements[bookmark] = formattingClone;
4706 private int findInListOfActiveFormattingElements(StackNode<T> node) {
4707 for (int i = listPtr; i >= 0; i--) {
4708 if (node == listOfActiveFormattingElements[i]) {
4709 return i;
4712 return -1;
4715 private int findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker(
4716 @Local String name) {
4717 for (int i = listPtr; i >= 0; i--) {
4718 StackNode<T> node = listOfActiveFormattingElements[i];
4719 if (node == null) {
4720 return -1;
4721 } else if (node.name == name) {
4722 return i;
4725 return -1;
4729 private void maybeForgetEarlierDuplicateFormattingElement(
4730 @Local String name, HtmlAttributes attributes) throws SAXException {
4731 int candidate = -1;
4732 int count = 0;
4733 for (int i = listPtr; i >= 0; i--) {
4734 StackNode<T> node = listOfActiveFormattingElements[i];
4735 if (node == null) {
4736 break;
4738 if (node.name == name && node.attributes.equalsAnother(attributes)) {
4739 candidate = i;
4740 ++count;
4743 if (count >= 3) {
4744 removeFromListOfActiveFormattingElements(candidate);
4748 private int findLastOrRoot(@Local String name) {
4749 for (int i = currentPtr; i > 0; i--) {
4750 if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) {
4751 return i;
4754 return 0;
4757 private int findLastOrRoot(int group) {
4758 for (int i = currentPtr; i > 0; i--) {
4759 if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].getGroup() == group) {
4760 return i;
4763 return 0;
4767 * Attempt to add attribute to the body element.
4768 * @param attributes the attributes
4769 * @return <code>true</code> iff the attributes were added
4770 * @throws SAXException
4772 private boolean addAttributesToBody(HtmlAttributes attributes)
4773 throws SAXException {
4774 // [NOCPP[
4775 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4776 // ]NOCPP]
4777 if (currentPtr >= 1) {
4778 StackNode<T> body = stack[1];
4779 if (body.getGroup() == TreeBuilder.BODY) {
4780 addAttributesToElement(body.node, attributes);
4781 return true;
4784 return false;
4787 private void addAttributesToHtml(HtmlAttributes attributes)
4788 throws SAXException {
4789 // [NOCPP[
4790 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
4791 // ]NOCPP]
4792 addAttributesToElement(stack[0].node, attributes);
4795 private void pushHeadPointerOntoStack() throws SAXException {
4796 assert headPointer != null;
4797 assert mode == AFTER_HEAD;
4798 fatal();
4799 silentPush(createStackNode(ElementName.HEAD, headPointer
4800 // [NOCPP[
4801 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
4802 // ]NOCPP]
4807 * @throws SAXException
4810 private void reconstructTheActiveFormattingElements() throws SAXException {
4811 if (listPtr == -1) {
4812 return;
4814 StackNode<T> mostRecent = listOfActiveFormattingElements[listPtr];
4815 if (mostRecent == null || isInStack(mostRecent)) {
4816 return;
4818 int entryPos = listPtr;
4819 for (;;) {
4820 entryPos--;
4821 if (entryPos == -1) {
4822 break;
4824 if (listOfActiveFormattingElements[entryPos] == null) {
4825 break;
4827 if (isInStack(listOfActiveFormattingElements[entryPos])) {
4828 break;
4831 while (entryPos < listPtr) {
4832 entryPos++;
4833 StackNode<T> entry = listOfActiveFormattingElements[entryPos];
4834 StackNode<T> current = stack[currentPtr];
4836 T clone;
4837 if (current.isFosterParenting()) {
4838 clone = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", entry.name,
4839 entry.attributes.cloneAttributes()
4840 // CPPONLY: , htmlCreator(entry.getHtmlCreator())
4842 } else {
4843 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
4844 clone = createElement("http://www.w3.org/1999/xhtml", entry.name,
4845 entry.attributes.cloneAttributes(), currentNode
4846 // CPPONLY: , htmlCreator(entry.getHtmlCreator())
4848 appendElement(clone, currentNode);
4851 StackNode<T> entryClone = createStackNode(entry.getFlags(),
4852 entry.ns, entry.name, clone, entry.popName,
4853 entry.attributes
4854 // CPPONLY: , entry.getHtmlCreator()
4855 // [NOCPP[
4856 , entry.getLocator()
4857 // ]NOCPP]
4860 entry.dropAttributes(); // transfer ownership to entryClone
4862 push(entryClone);
4863 // stack takes ownership of the local variable
4864 listOfActiveFormattingElements[entryPos] = entryClone;
4865 // overwriting the old entry on the list, so release & retain
4866 entry.release(this);
4867 entryClone.retain();
4871 void notifyUnusedStackNode(int idxInStackNodes) {
4872 // stackNodesIdx is the earliest possible index of a stack node that might be unused,
4873 // so update the index if necessary.
4874 if (idxInStackNodes < stackNodesIdx) {
4875 stackNodesIdx = idxInStackNodes;
4879 @SuppressWarnings("unchecked") private StackNode<T> getUnusedStackNode() {
4880 // Search for an unused stack node.
4881 while (stackNodesIdx < numStackNodes) {
4882 if (stackNodes[stackNodesIdx].isUnused()) {
4883 return stackNodes[stackNodesIdx++];
4885 stackNodesIdx++;
4888 if (stackNodesIdx < stackNodes.length) {
4889 // No unused stack nodes, but there is still space in the storage array.
4890 stackNodes[stackNodesIdx] = new StackNode<T>(stackNodesIdx);
4891 numStackNodes++;
4892 return stackNodes[stackNodesIdx++];
4895 // Could not find an unused stack node and storage array is full.
4896 StackNode<T>[] newStack = new StackNode[stackNodes.length + 64];
4897 System.arraycopy(stackNodes, 0, newStack, 0, stackNodes.length);
4898 stackNodes = newStack;
4900 // Create a new stack node and return it.
4901 stackNodes[stackNodesIdx] = new StackNode<T>(stackNodesIdx);
4902 numStackNodes++;
4903 return stackNodes[stackNodesIdx++];
4906 private StackNode<T> createStackNode(int flags, @NsUri String ns, @Local String name, T node,
4907 @Local String popName, HtmlAttributes attributes
4908 // CPPONLY: , @HtmlCreator Object htmlCreator
4909 // [NOCPP[
4910 , TaintableLocatorImpl locator
4911 // ]NOCPP]
4913 StackNode<T> instance = getUnusedStackNode();
4914 instance.setValues(flags, ns, name, node, popName, attributes
4915 // CPPONLY: , htmlCreator
4916 // [NOCPP[
4917 , locator
4918 // ]NOCPP]
4920 return instance;
4923 private StackNode<T> createStackNode(ElementName elementName, T node
4924 // [NOCPP[
4925 , TaintableLocatorImpl locator
4926 // ]NOCPP]
4928 StackNode<T> instance = getUnusedStackNode();
4929 instance.setValues(elementName, node
4930 // [NOCPP[
4931 , locator
4932 // ]NOCPP]
4934 return instance;
4937 private StackNode<T> createStackNode(ElementName elementName, T node, HtmlAttributes attributes
4938 // [NOCPP[
4939 , TaintableLocatorImpl locator
4940 // ]NOCPP]
4942 StackNode<T> instance = getUnusedStackNode();
4943 instance.setValues(elementName, node, attributes
4944 // [NOCPP[
4945 , locator
4946 // ]NOCPP]
4948 return instance;
4951 private StackNode<T> createStackNode(ElementName elementName, T node, @Local String popName
4952 // [NOCPP[
4953 , TaintableLocatorImpl locator
4954 // ]NOCPP]
4956 StackNode<T> instance = getUnusedStackNode();
4957 instance.setValues(elementName, node, popName
4958 // [NOCPP[
4959 , locator
4960 // ]NOCPP]
4962 return instance;
4965 private StackNode<T> createStackNode(ElementName elementName, @Local String popName, T node
4966 // [NOCPP[
4967 , TaintableLocatorImpl locator
4968 // ]NOCPP]
4970 StackNode<T> instance = getUnusedStackNode();
4971 instance.setValues(elementName, popName, node
4972 // [NOCPP[
4973 , locator
4974 // ]NOCPP]
4976 return instance;
4979 private StackNode<T> createStackNode(ElementName elementName, T node, @Local String popName,
4980 boolean markAsIntegrationPoint
4981 // [NOCPP[
4982 , TaintableLocatorImpl locator
4983 // ]NOCPP]
4985 StackNode<T> instance = getUnusedStackNode();
4986 instance.setValues(elementName, node, popName, markAsIntegrationPoint
4987 // [NOCPP[
4988 , locator
4989 // ]NOCPP]
4991 return instance;
4994 private void insertIntoFosterParent(T child) throws SAXException {
4995 int tablePos = findLastOrRoot(TreeBuilder.TABLE);
4996 int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE);
4998 if (templatePos >= tablePos) {
4999 appendElement(child, stack[templatePos].node);
5000 return;
5003 StackNode<T> node = stack[tablePos];
5004 insertFosterParentedChild(child, node.node, stack[tablePos - 1].node);
5007 private T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
5008 HtmlAttributes attributes
5009 // CPPONLY: , @Creator Object creator
5010 ) throws SAXException {
5011 return createAndInsertFosterParentedElement(ns, name, attributes, null
5012 // CPPONLY: , creator
5016 private T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
5017 HtmlAttributes attributes, T form
5018 // CPPONLY: , @Creator Object creator
5019 ) throws SAXException {
5020 int tablePos = findLastOrRoot(TreeBuilder.TABLE);
5021 int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE);
5023 if (templatePos >= tablePos) {
5024 T child = createElement(ns, name, attributes, form, stack[templatePos].node
5025 // CPPONLY: , creator
5027 appendElement(child, stack[templatePos].node);
5028 return child;
5031 StackNode<T> node = stack[tablePos];
5032 return createAndInsertFosterParentedElement(ns, name, attributes, form, node.node, stack[tablePos - 1].node
5033 // CPPONLY: , creator
5037 private boolean isInStack(StackNode<T> node) {
5038 for (int i = currentPtr; i >= 0; i--) {
5039 if (stack[i] == node) {
5040 return true;
5043 return false;
5046 private void popTemplateMode() {
5047 templateModePtr--;
5050 private void pop() throws SAXException {
5051 StackNode<T> node = stack[currentPtr];
5052 assert debugOnlyClearLastStackSlot();
5053 currentPtr--;
5054 elementPopped(node.ns, node.popName, node.node);
5055 node.release(this);
5058 private void popForeign(int origPos, int eltPos) throws SAXException {
5059 StackNode<T> node = stack[currentPtr];
5060 if (origPos != currentPtr || eltPos != currentPtr) {
5061 markMalformedIfScript(node.node);
5063 assert debugOnlyClearLastStackSlot();
5064 currentPtr--;
5065 elementPopped(node.ns, node.popName, node.node);
5066 node.release(this);
5069 private void silentPop() throws SAXException {
5070 StackNode<T> node = stack[currentPtr];
5071 assert debugOnlyClearLastStackSlot();
5072 currentPtr--;
5073 node.release(this);
5076 private void popOnEof() throws SAXException {
5077 StackNode<T> node = stack[currentPtr];
5078 assert debugOnlyClearLastStackSlot();
5079 currentPtr--;
5080 markMalformedIfScript(node.node);
5081 elementPopped(node.ns, node.popName, node.node);
5082 node.release(this);
5085 // [NOCPP[
5086 private void checkAttributes(HtmlAttributes attributes, @NsUri String ns)
5087 throws SAXException {
5088 if (errorHandler != null) {
5089 int len = attributes.getXmlnsLength();
5090 for (int i = 0; i < len; i++) {
5091 AttributeName name = attributes.getXmlnsAttributeName(i);
5092 if (name == AttributeName.XMLNS) {
5093 String xmlns = attributes.getXmlnsValue(i);
5094 if (!ns.equals(xmlns)) {
5095 err("Bad value \u201C"
5096 + xmlns
5097 + "\u201D for the attribute \u201Cxmlns\u201D (only \u201C"
5098 + ns + "\u201D permitted here).");
5099 switch (namePolicy) {
5100 case ALTER_INFOSET:
5101 // fall through
5102 case ALLOW:
5103 warn("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0.");
5104 break;
5105 case FATAL:
5106 fatal("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0.");
5107 break;
5110 } else if (ns != "http://www.w3.org/1999/xhtml"
5111 && name == AttributeName.XMLNS_XLINK) {
5112 String xmlns = attributes.getXmlnsValue(i);
5113 if (!"http://www.w3.org/1999/xlink".equals(xmlns)) {
5114 err("Bad value \u201C"
5115 + xmlns
5116 + "\u201D for the attribute \u201Cxmlns:link\u201D (only \u201Chttp://www.w3.org/1999/xlink\u201D permitted here).");
5117 switch (namePolicy) {
5118 case ALTER_INFOSET:
5119 // fall through
5120 case ALLOW:
5121 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.");
5122 break;
5123 case FATAL:
5124 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.");
5125 break;
5128 } else {
5129 err("Attribute \u201C" + attributes.getXmlnsLocalName(i)
5130 + "\u201D not allowed here.");
5131 switch (namePolicy) {
5132 case ALTER_INFOSET:
5133 // fall through
5134 case ALLOW:
5135 warn("Attribute with the local name \u201C"
5136 + attributes.getXmlnsLocalName(i)
5137 + "\u201D is not serializable as XML 1.0.");
5138 break;
5139 case FATAL:
5140 fatal("Attribute with the local name \u201C"
5141 + attributes.getXmlnsLocalName(i)
5142 + "\u201D is not serializable as XML 1.0.");
5143 break;
5148 attributes.processNonNcNames(this, namePolicy);
5151 private String checkPopName(@Local String name) throws SAXException {
5152 if (NCName.isNCName(name)) {
5153 return name;
5154 } else {
5155 switch (namePolicy) {
5156 case ALLOW:
5157 warn("Element name \u201C" + name
5158 + "\u201D cannot be represented as XML 1.0.");
5159 return name;
5160 case ALTER_INFOSET:
5161 warn("Element name \u201C" + name
5162 + "\u201D cannot be represented as XML 1.0.");
5163 return NCName.escapeName(name);
5164 case FATAL:
5165 fatal("Element name \u201C" + name
5166 + "\u201D cannot be represented as XML 1.0.");
5169 return null; // keep compiler happy
5172 // ]NOCPP]
5174 private void appendHtmlElementToDocumentAndPush(HtmlAttributes attributes)
5175 throws SAXException {
5176 // [NOCPP[
5177 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5178 // ]NOCPP]
5179 T elt = createHtmlElementSetAsRoot(attributes);
5180 StackNode<T> node = createStackNode(ElementName.HTML,
5182 // [NOCPP[
5183 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5184 // ]NOCPP]
5186 push(node);
5189 private void appendHtmlElementToDocumentAndPush() throws SAXException {
5190 appendHtmlElementToDocumentAndPush(tokenizer.emptyAttributes());
5193 private void appendToCurrentNodeAndPushHeadElement(HtmlAttributes attributes)
5194 throws SAXException {
5195 // [NOCPP[
5196 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5197 // ]NOCPP]
5198 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5199 T elt = createElement("http://www.w3.org/1999/xhtml", "head", attributes, currentNode
5201 * head uses NS_NewHTMLSharedElement creator
5203 // CPPONLY: , htmlCreator(NS_NewHTMLSharedElement)
5205 appendElement(elt, currentNode);
5206 headPointer = elt;
5207 StackNode<T> node = createStackNode(ElementName.HEAD,
5209 // [NOCPP[
5210 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5211 // ]NOCPP]
5213 push(node);
5216 private void appendToCurrentNodeAndPushBodyElement(HtmlAttributes attributes)
5217 throws SAXException {
5218 appendToCurrentNodeAndPushElement(ElementName.BODY,
5219 attributes);
5222 private void appendToCurrentNodeAndPushBodyElement() throws SAXException {
5223 appendToCurrentNodeAndPushBodyElement(tokenizer.emptyAttributes());
5226 private void appendToCurrentNodeAndPushFormElementMayFoster(
5227 HtmlAttributes attributes) throws SAXException {
5228 // [NOCPP[
5229 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5230 // ]NOCPP]
5232 T elt;
5233 StackNode<T> current = stack[currentPtr];
5234 if (current.isFosterParenting()) {
5235 fatal();
5236 elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", "form", attributes
5237 // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
5239 } else {
5240 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5241 elt = createElement("http://www.w3.org/1999/xhtml", "form", attributes, currentNode
5242 // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
5244 appendElement(elt, currentNode);
5247 if (!isTemplateContents()) {
5248 formPointer = elt;
5251 StackNode<T> node = createStackNode(ElementName.FORM,
5253 // [NOCPP[
5254 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5255 // ]NOCPP]
5257 push(node);
5260 private void appendToCurrentNodeAndPushFormattingElementMayFoster(
5261 ElementName elementName, HtmlAttributes attributes)
5262 throws SAXException {
5263 // [NOCPP[
5264 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5265 // ]NOCPP]
5266 // This method can't be called for custom elements
5267 HtmlAttributes clone = attributes.cloneAttributes();
5268 // Attributes must not be read after calling createElement, because
5269 // createElement may delete attributes in C++.
5270 T elt;
5271 StackNode<T> current = stack[currentPtr];
5272 if (current.isFosterParenting()) {
5273 fatal();
5274 elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes
5275 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5277 } else {
5278 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5279 elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode
5280 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5282 appendElement(elt, currentNode);
5284 StackNode<T> node = createStackNode(elementName, elt, clone
5285 // [NOCPP[
5286 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5287 // ]NOCPP]
5289 push(node);
5290 append(node);
5291 node.retain(); // append doesn't retain itself
5294 private void appendToCurrentNodeAndPushElement(ElementName elementName,
5295 HtmlAttributes attributes)
5296 throws SAXException {
5297 // [NOCPP[
5298 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5299 // ]NOCPP]
5300 // This method can't be called for custom elements
5301 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5302 T elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(), attributes, currentNode
5303 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5305 appendElement(elt, currentNode);
5306 if (ElementName.TEMPLATE == elementName) {
5307 elt = getDocumentFragmentForTemplate(elt);
5309 StackNode<T> node = createStackNode(elementName, elt
5310 // [NOCPP[
5311 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5312 // ]NOCPP]
5314 push(node);
5317 private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
5318 HtmlAttributes attributes)
5319 throws SAXException {
5320 @Local String popName = elementName.getName();
5321 // [NOCPP[
5322 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5323 if (!elementName.isInterned()) {
5324 popName = checkPopName(popName);
5326 // ]NOCPP]
5327 T elt;
5328 StackNode<T> current = stack[currentPtr];
5329 if (current.isFosterParenting()) {
5330 fatal();
5331 elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", popName, attributes
5332 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5334 } else {
5335 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5336 elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, currentNode
5337 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5339 appendElement(elt, currentNode);
5341 StackNode<T> node = createStackNode(elementName, elt, popName
5342 // [NOCPP[
5343 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5344 // ]NOCPP]
5346 push(node);
5349 private void appendToCurrentNodeAndPushElementMayFosterMathML(
5350 ElementName elementName, HtmlAttributes attributes)
5351 throws SAXException {
5352 @Local String popName = elementName.getName();
5353 // [NOCPP[
5354 checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML");
5355 if (!elementName.isInterned()) {
5356 popName = checkPopName(popName);
5358 // ]NOCPP]
5359 boolean markAsHtmlIntegrationPoint = false;
5360 if (ElementName.ANNOTATION_XML == elementName
5361 && annotationXmlEncodingPermitsHtml(attributes)) {
5362 markAsHtmlIntegrationPoint = true;
5364 // Attributes must not be read after calling createElement(), since
5365 // createElement may delete the object in C++.
5366 T elt;
5367 StackNode<T> current = stack[currentPtr];
5368 if (current.isFosterParenting()) {
5369 fatal();
5370 elt = createAndInsertFosterParentedElement("http://www.w3.org/1998/Math/MathML", popName, attributes
5371 // CPPONLY: , htmlCreator(null)
5373 } else {
5374 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5375 elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, currentNode
5376 // CPPONLY: , htmlCreator(null)
5378 appendElement(elt, currentNode);
5380 StackNode<T> node = createStackNode(elementName, elt, popName,
5381 markAsHtmlIntegrationPoint
5382 // [NOCPP[
5383 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5384 // ]NOCPP]
5386 push(node);
5389 // [NOCPP[
5390 T getDocumentFragmentForTemplate(T template) {
5391 return template;
5394 T getFormPointerForContext(T context) {
5395 return null;
5397 // ]NOCPP]
5399 private boolean annotationXmlEncodingPermitsHtml(HtmlAttributes attributes) {
5400 String encoding = attributes.getValue(AttributeName.ENCODING);
5401 if (encoding == null) {
5402 return false;
5404 return Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
5405 "application/xhtml+xml", encoding)
5406 || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString(
5407 "text/html", encoding);
5410 private void appendToCurrentNodeAndPushElementMayFosterSVG(
5411 ElementName elementName, HtmlAttributes attributes)
5412 throws SAXException {
5413 @Local String popName = elementName.getCamelCaseName();
5414 // [NOCPP[
5415 checkAttributes(attributes, "http://www.w3.org/2000/svg");
5416 if (!elementName.isInterned()) {
5417 popName = checkPopName(popName);
5419 // ]NOCPP]
5420 T elt;
5421 StackNode<T> current = stack[currentPtr];
5422 if (current.isFosterParenting()) {
5423 fatal();
5424 elt = createAndInsertFosterParentedElement("http://www.w3.org/2000/svg", popName, attributes
5425 // CPPONLY: , svgCreator(elementName.getSvgCreator())
5427 } else {
5428 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5429 elt = createElement("http://www.w3.org/2000/svg", popName, attributes, currentNode
5430 // CPPONLY: , svgCreator(elementName.getSvgCreator())
5432 appendElement(elt, currentNode);
5434 StackNode<T> node = createStackNode(elementName, popName, elt
5435 // [NOCPP[
5436 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5437 // ]NOCPP]
5439 push(node);
5442 private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName,
5443 HtmlAttributes attributes, T form)
5444 throws SAXException {
5445 // [NOCPP[
5446 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5447 // ]NOCPP]
5448 // Can't be called for custom elements
5449 T elt;
5450 T formOwner = form == null || fragment || isTemplateContents() ? null : form;
5451 StackNode<T> current = stack[currentPtr];
5452 if (current.isFosterParenting()) {
5453 fatal();
5454 elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", elementName.getName(),
5455 attributes, formOwner
5456 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5458 } else {
5459 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5460 elt = createElement("http://www.w3.org/1999/xhtml", elementName.getName(),
5461 attributes, formOwner, currentNode
5462 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5464 appendElement(elt, currentNode);
5466 StackNode<T> node = createStackNode(elementName, elt
5467 // [NOCPP[
5468 , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
5469 // ]NOCPP]
5471 push(node);
5474 private void appendVoidElementToCurrent(
5475 ElementName elementName, HtmlAttributes attributes)
5476 throws SAXException {
5477 @Local String popName = elementName.getName();
5478 // [NOCPP[
5479 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5480 if (!elementName.isInterned()) {
5481 popName = checkPopName(popName);
5483 // ]NOCPP]
5484 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5485 T elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, currentNode
5486 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5488 appendElement(elt, currentNode);
5489 elementPushed("http://www.w3.org/1999/xhtml", popName, elt);
5490 elementPopped("http://www.w3.org/1999/xhtml", popName, elt);
5493 private void appendVoidElementToCurrentMayFoster(
5494 ElementName elementName, HtmlAttributes attributes, T form) throws SAXException {
5495 @Local String name = elementName.getName();
5496 // [NOCPP[
5497 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5498 // ]NOCPP]
5499 // Can't be called for custom elements
5500 T elt;
5501 T formOwner = form == null || fragment || isTemplateContents() ? null : form;
5502 StackNode<T> current = stack[currentPtr];
5503 if (current.isFosterParenting()) {
5504 fatal();
5505 elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", name,
5506 attributes, formOwner
5507 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5509 } else {
5510 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5511 elt = createElement("http://www.w3.org/1999/xhtml", name,
5512 attributes, formOwner, currentNode
5513 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5515 appendElement(elt, currentNode);
5517 elementPushed("http://www.w3.org/1999/xhtml", name, elt);
5518 elementPopped("http://www.w3.org/1999/xhtml", name, elt);
5521 private void appendVoidElementToCurrentMayFoster(
5522 ElementName elementName, HtmlAttributes attributes)
5523 throws SAXException {
5524 @Local String popName = elementName.getName();
5525 // [NOCPP[
5526 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5527 if (!elementName.isInterned()) {
5528 popName = checkPopName(popName);
5530 // ]NOCPP]
5531 T elt;
5532 StackNode<T> current = stack[currentPtr];
5533 if (current.isFosterParenting()) {
5534 fatal();
5535 elt = createAndInsertFosterParentedElement("http://www.w3.org/1999/xhtml", popName, attributes
5536 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5538 } else {
5539 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5540 elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes, currentNode
5541 // CPPONLY: , htmlCreator(elementName.getHtmlCreator())
5543 appendElement(elt, currentNode);
5545 elementPushed("http://www.w3.org/1999/xhtml", popName, elt);
5546 elementPopped("http://www.w3.org/1999/xhtml", popName, elt);
5549 private void appendVoidElementToCurrentMayFosterSVG(
5550 ElementName elementName, HtmlAttributes attributes)
5551 throws SAXException {
5552 @Local String popName = elementName.getCamelCaseName();
5553 // [NOCPP[
5554 checkAttributes(attributes, "http://www.w3.org/2000/svg");
5555 if (!elementName.isInterned()) {
5556 popName = checkPopName(popName);
5558 // ]NOCPP]
5559 T elt;
5560 StackNode<T> current = stack[currentPtr];
5561 if (current.isFosterParenting()) {
5562 fatal();
5563 elt = createAndInsertFosterParentedElement("http://www.w3.org/2000/svg", popName, attributes
5564 // CPPONLY: , svgCreator(elementName.getSvgCreator())
5566 } else {
5567 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5568 elt = createElement("http://www.w3.org/2000/svg", popName, attributes, currentNode
5569 // CPPONLY: , svgCreator(elementName.getSvgCreator())
5571 appendElement(elt, currentNode);
5573 elementPushed("http://www.w3.org/2000/svg", popName, elt);
5574 elementPopped("http://www.w3.org/2000/svg", popName, elt);
5577 private void appendVoidElementToCurrentMayFosterMathML(
5578 ElementName elementName, HtmlAttributes attributes)
5579 throws SAXException {
5580 @Local String popName = elementName.getName();
5581 // [NOCPP[
5582 checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML");
5583 if (!elementName.isInterned()) {
5584 popName = checkPopName(popName);
5586 // ]NOCPP]
5587 T elt;
5588 StackNode<T> current = stack[currentPtr];
5589 if (current.isFosterParenting()) {
5590 fatal();
5591 elt = createAndInsertFosterParentedElement("http://www.w3.org/1998/Math/MathML", popName, attributes
5592 // CPPONLY: , htmlCreator(null)
5594 } else {
5595 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5596 elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes, currentNode
5597 // CPPONLY: , htmlCreator(null)
5599 appendElement(elt, currentNode);
5601 elementPushed("http://www.w3.org/1998/Math/MathML", popName, elt);
5602 elementPopped("http://www.w3.org/1998/Math/MathML", popName, elt);
5605 private void appendVoidInputToCurrent(HtmlAttributes attributes, T form) throws SAXException {
5606 // [NOCPP[
5607 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5608 // ]NOCPP]
5609 // Can't be called for custom elements
5610 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5611 T elt = createElement("http://www.w3.org/1999/xhtml", "input", attributes,
5612 form == null || fragment || isTemplateContents() ? null : form, currentNode
5613 // CPPONLY: , htmlCreator(NS_NewHTMLInputElement)
5615 appendElement(elt, currentNode);
5616 elementPushed("http://www.w3.org/1999/xhtml", "input", elt);
5617 elementPopped("http://www.w3.org/1999/xhtml", "input", elt);
5620 private void appendVoidFormToCurrent(HtmlAttributes attributes) throws SAXException {
5621 // [NOCPP[
5622 checkAttributes(attributes, "http://www.w3.org/1999/xhtml");
5623 // ]NOCPP]
5624 T currentNode = nodeFromStackWithBlinkCompat(currentPtr);
5625 T elt = createElement("http://www.w3.org/1999/xhtml", "form",
5626 attributes, currentNode
5627 // CPPONLY: , htmlCreator(NS_NewHTMLFormElement)
5629 formPointer = elt;
5630 // ownership transferred to form pointer
5631 appendElement(elt, currentNode);
5632 elementPushed("http://www.w3.org/1999/xhtml", "form", elt);
5633 elementPopped("http://www.w3.org/1999/xhtml", "form", elt);
5636 // [NOCPP[
5638 private final void accumulateCharactersForced(@Const @NoLength char[] buf,
5639 int start, int length) throws SAXException {
5640 System.arraycopy(buf, start, charBuffer, charBufferLen, length);
5641 charBufferLen += length;
5644 @Override public void ensureBufferSpace(int inputLength)
5645 throws SAXException {
5646 // TODO: Unify Tokenizer.strBuf and TreeBuilder.charBuffer so that
5647 // this method becomes unnecessary.
5648 int worstCase = charBufferLen + inputLength;
5649 if (charBuffer == null) {
5650 // Add an arbitrary small value to avoid immediate reallocation
5651 // once there are a few characters in the buffer.
5652 charBuffer = new char[worstCase + 128];
5653 } else if (worstCase > charBuffer.length) {
5654 // HotSpot reportedly allocates memory with 8-byte accuracy, so
5655 // there's no point in trying to do math here to avoid slop.
5656 // Maybe we should add some small constant to worstCase here
5657 // but not doing that without profiling. In C++ with jemalloc,
5658 // the corresponding method should do math to round up here
5659 // to avoid slop.
5660 char[] newBuf = new char[worstCase];
5661 System.arraycopy(charBuffer, 0, newBuf, 0, charBufferLen);
5662 charBuffer = newBuf;
5666 // ]NOCPP]
5668 protected void accumulateCharacters(@Const @NoLength char[] buf, int start,
5669 int length) throws SAXException {
5670 appendCharacters(stack[currentPtr].node, buf, start, length);
5673 // ------------------------------- //
5675 protected final void requestSuspension() {
5676 tokenizer.requestSuspension();
5679 protected abstract T createElement(@NsUri String ns, @Local String name,
5680 HtmlAttributes attributes, T intendedParent
5681 // CPPONLY: , @Creator Object creator
5682 ) throws SAXException;
5684 protected T createElement(@NsUri String ns, @Local String name,
5685 HtmlAttributes attributes, T form, T intendedParent
5686 // CPPONLY: , @Creator Object creator
5687 ) throws SAXException {
5688 return createElement("http://www.w3.org/1999/xhtml", name, attributes, intendedParent
5689 // CPPONLY: , creator
5693 protected abstract T createHtmlElementSetAsRoot(HtmlAttributes attributes)
5694 throws SAXException;
5696 protected abstract void detachFromParent(T element) throws SAXException;
5698 protected abstract boolean hasChildren(T element) throws SAXException;
5700 protected abstract void appendElement(T child, T newParent)
5701 throws SAXException;
5703 protected abstract void appendChildrenToNewParent(T oldParent, T newParent)
5704 throws SAXException;
5706 protected abstract void insertFosterParentedChild(T child, T table,
5707 T stackParent) throws SAXException;
5709 // We don't generate CPP code for this method because it is not used in generated CPP
5710 // code. Instead, the form owner version of this method is called with a null form owner.
5711 // [NOCPP[
5713 protected abstract T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
5714 HtmlAttributes attributes, T table, T stackParent) throws SAXException;
5716 // ]NOCPP]
5718 protected T createAndInsertFosterParentedElement(@NsUri String ns, @Local String name,
5719 HtmlAttributes attributes, T form, T table, T stackParent
5720 // CPPONLY: , @Creator Object creator
5721 ) throws SAXException {
5722 return createAndInsertFosterParentedElement(ns, name, attributes, table, stackParent);
5725 protected abstract void insertFosterParentedCharacters(
5726 @NoLength char[] buf, int start, int length, T table, T stackParent)
5727 throws SAXException;
5729 protected abstract void appendCharacters(T parent, @NoLength char[] buf,
5730 int start, int length) throws SAXException;
5732 protected abstract void appendComment(T parent, @NoLength char[] buf,
5733 int start, int length) throws SAXException;
5735 protected abstract void appendCommentToDocument(@NoLength char[] buf,
5736 int start, int length) throws SAXException;
5738 protected abstract void addAttributesToElement(T element,
5739 HtmlAttributes attributes) throws SAXException;
5741 protected void markMalformedIfScript(T elt) throws SAXException {
5745 protected void start(boolean fragmentMode) throws SAXException {
5749 protected void end() throws SAXException {
5753 protected void appendDoctypeToDocument(@Local String name,
5754 String publicIdentifier, String systemIdentifier)
5755 throws SAXException {
5759 protected void elementPushed(@NsUri String ns, @Local String name, T node)
5760 throws SAXException {
5764 protected void elementPopped(@NsUri String ns, @Local String name, T node)
5765 throws SAXException {
5769 // [NOCPP[
5771 protected void documentMode(DocumentMode m, String publicIdentifier,
5772 String systemIdentifier)
5773 throws SAXException {
5778 * @see nu.validator.htmlparser.common.TokenHandler#wantsComments()
5780 public boolean wantsComments() {
5781 return wantingComments;
5784 public void setIgnoringComments(boolean ignoreComments) {
5785 wantingComments = !ignoreComments;
5789 * Sets the errorHandler.
5791 * @param errorHandler
5792 * the errorHandler to set
5794 public final void setErrorHandler(ErrorHandler errorHandler) {
5795 this.errorHandler = errorHandler;
5799 * Returns the errorHandler.
5801 * @return the errorHandler
5803 public ErrorHandler getErrorHandler() {
5804 return errorHandler;
5808 * The argument MUST be an interned string or <code>null</code>.
5810 * @param context
5812 public final void setFragmentContext(@Local String context) {
5813 this.contextName = context;
5814 this.contextNamespace = "http://www.w3.org/1999/xhtml";
5815 this.contextNode = null;
5816 this.fragment = (contextName != null);
5817 this.quirks = false;
5820 // ]NOCPP]
5823 * @see nu.validator.htmlparser.common.TokenHandler#cdataSectionAllowed()
5825 @Inline public boolean cdataSectionAllowed() throws SAXException {
5826 return isInForeign();
5829 private boolean isInForeign() {
5830 return currentPtr >= 0
5831 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml";
5834 private boolean isInForeignButNotHtmlOrMathTextIntegrationPoint() {
5835 if (currentPtr < 0) {
5836 return false;
5838 return !isSpecialParentInForeign(stack[currentPtr]);
5842 * The argument MUST be an interned string or <code>null</code>.
5844 * @param context
5846 public final void setFragmentContext(@Local String context,
5847 @NsUri String ns, T node, boolean quirks) {
5848 // [NOCPP[
5849 if (!((context == null && ns == null)
5850 || "http://www.w3.org/1999/xhtml" == ns
5851 || "http://www.w3.org/2000/svg" == ns || "http://www.w3.org/1998/Math/MathML" == ns)) {
5852 throw new IllegalArgumentException(
5853 "The namespace must be the HTML, SVG or MathML namespace (or null when the local name is null). Got: "
5854 + ns);
5856 // ]NOCPP]
5857 this.contextName = context;
5858 this.contextNamespace = ns;
5859 this.contextNode = node;
5860 this.fragment = (contextName != null);
5861 this.quirks = quirks;
5864 protected final T currentNode() {
5865 return stack[currentPtr].node;
5869 * Returns the scriptingEnabled.
5871 * @return the scriptingEnabled
5873 public boolean isScriptingEnabled() {
5874 return scriptingEnabled;
5878 * Sets the scriptingEnabled.
5880 * @param scriptingEnabled
5881 * the scriptingEnabled to set
5883 public void setScriptingEnabled(boolean scriptingEnabled) {
5884 this.scriptingEnabled = scriptingEnabled;
5887 public void setForceNoQuirks(boolean forceNoQuirks) {
5888 this.forceNoQuirks = forceNoQuirks;
5891 // Redundant method retained because previously public.
5892 public void setIsSrcdocDocument(boolean isSrcdocDocument) {
5893 this.setForceNoQuirks(isSrcdocDocument);
5896 // [NOCPP[
5898 public void setNamePolicy(XmlViolationPolicy namePolicy) {
5899 this.namePolicy = namePolicy;
5903 * Sets the documentModeHandler.
5905 * @param documentModeHandler
5906 * the documentModeHandler to set
5908 public void setDocumentModeHandler(DocumentModeHandler documentModeHandler) {
5909 this.documentModeHandler = documentModeHandler;
5913 * Sets the reportingDoctype.
5915 * @param reportingDoctype
5916 * the reportingDoctype to set
5918 public void setReportingDoctype(boolean reportingDoctype) {
5919 this.reportingDoctype = reportingDoctype;
5922 // ]NOCPP]
5925 * Flushes the pending characters. Public for document.write use cases only.
5926 * @throws SAXException
5928 public final void flushCharacters() throws SAXException {
5929 if (charBufferLen > 0) {
5930 if ((mode == IN_TABLE || mode == IN_TABLE_BODY || mode == IN_ROW)
5931 && charBufferContainsNonWhitespace()) {
5932 errNonSpaceInTable();
5933 reconstructTheActiveFormattingElements();
5934 if (!stack[currentPtr].isFosterParenting()) {
5935 // reconstructing gave us a new current node
5936 appendCharacters(currentNode(), charBuffer, 0,
5937 charBufferLen);
5938 charBufferLen = 0;
5939 return;
5942 int tablePos = findLastOrRoot(TreeBuilder.TABLE);
5943 int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE);
5945 if (templatePos >= tablePos) {
5946 appendCharacters(stack[templatePos].node, charBuffer, 0, charBufferLen);
5947 charBufferLen = 0;
5948 return;
5951 StackNode<T> tableElt = stack[tablePos];
5952 insertFosterParentedCharacters(charBuffer, 0, charBufferLen,
5953 tableElt.node, stack[tablePos - 1].node);
5954 charBufferLen = 0;
5955 return;
5957 appendCharacters(currentNode(), charBuffer, 0, charBufferLen);
5958 charBufferLen = 0;
5962 private boolean charBufferContainsNonWhitespace() {
5963 for (int i = 0; i < charBufferLen; i++) {
5964 switch (charBuffer[i]) {
5965 case ' ':
5966 case '\t':
5967 case '\n':
5968 case '\r':
5969 case '\u000C':
5970 continue;
5971 default:
5972 return true;
5975 return false;
5979 * Creates a comparable snapshot of the tree builder state. Snapshot
5980 * creation is only supported immediately after a script end tag has been
5981 * processed. In C++ the caller is responsible for calling
5982 * <code>delete</code> on the returned object.
5984 * @return a snapshot.
5985 * @throws SAXException
5987 @SuppressWarnings("unchecked") public TreeBuilderState<T> newSnapshot()
5988 throws SAXException {
5989 StackNode<T>[] listCopy = new StackNode[listPtr + 1];
5990 for (int i = 0; i < listCopy.length; i++) {
5991 StackNode<T> node = listOfActiveFormattingElements[i];
5992 if (node != null) {
5993 StackNode<T> newNode = new StackNode<T>(-1);
5994 newNode.setValues(node.getFlags(), node.ns,
5995 node.name, node.node, node.popName,
5996 node.attributes.cloneAttributes()
5997 // CPPONLY: , node.getHtmlCreator()
5998 // [NOCPP[
5999 , node.getLocator()
6000 // ]NOCPP]
6002 listCopy[i] = newNode;
6003 } else {
6004 listCopy[i] = null;
6007 StackNode<T>[] stackCopy = new StackNode[currentPtr + 1];
6008 for (int i = 0; i < stackCopy.length; i++) {
6009 StackNode<T> node = stack[i];
6010 int listIndex = findInListOfActiveFormattingElements(node);
6011 if (listIndex == -1) {
6012 StackNode<T> newNode = new StackNode<T>(-1);
6013 newNode.setValues(node.getFlags(), node.ns,
6014 node.name, node.node, node.popName,
6015 null
6016 // CPPONLY: , node.getHtmlCreator()
6017 // [NOCPP[
6018 , node.getLocator()
6019 // ]NOCPP]
6021 stackCopy[i] = newNode;
6022 } else {
6023 stackCopy[i] = listCopy[listIndex];
6024 stackCopy[i].retain();
6027 int[] templateModeStackCopy = new int[templateModePtr + 1];
6028 System.arraycopy(templateModeStack, 0, templateModeStackCopy, 0,
6029 templateModeStackCopy.length);
6030 return new StateSnapshot<T>(stackCopy, listCopy, templateModeStackCopy, formPointer,
6031 headPointer, mode, originalMode, framesetOk,
6032 needToDropLF, quirks);
6035 public boolean snapshotMatches(TreeBuilderState<T> snapshot) {
6036 StackNode<T>[] stackCopy = snapshot.getStack();
6037 int stackLen = snapshot.getStackLength();
6038 StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
6039 int listLen = snapshot.getListOfActiveFormattingElementsLength();
6040 int[] templateModeStackCopy = snapshot.getTemplateModeStack();
6041 int templateModeStackLen = snapshot.getTemplateModeStackLength();
6043 if (stackLen != currentPtr + 1
6044 || listLen != listPtr + 1
6045 || templateModeStackLen != templateModePtr + 1
6046 || formPointer != snapshot.getFormPointer()
6047 || headPointer != snapshot.getHeadPointer()
6048 || mode != snapshot.getMode()
6049 || originalMode != snapshot.getOriginalMode()
6050 || framesetOk != snapshot.isFramesetOk()
6051 || needToDropLF != snapshot.isNeedToDropLF()
6052 || quirks != snapshot.isQuirks()) { // maybe just assert quirks
6053 return false;
6055 for (int i = listLen - 1; i >= 0; i--) {
6056 if (listCopy[i] == null
6057 && listOfActiveFormattingElements[i] == null) {
6058 continue;
6059 } else if (listCopy[i] == null
6060 || listOfActiveFormattingElements[i] == null) {
6061 return false;
6063 if (listCopy[i].node != listOfActiveFormattingElements[i].node) {
6064 return false; // it's possible that this condition is overly
6065 // strict
6068 for (int i = stackLen - 1; i >= 0; i--) {
6069 if (stackCopy[i].node != stack[i].node) {
6070 return false;
6073 for (int i = templateModeStackLen - 1; i >=0; i--) {
6074 if (templateModeStackCopy[i] != templateModeStack[i]) {
6075 return false;
6078 return true;
6081 @SuppressWarnings("unchecked") public void loadState(
6082 TreeBuilderState<T> snapshot)
6083 throws SAXException {
6084 // CPPONLY: mCurrentHtmlScriptCannotDocumentWriteOrBlock = false;
6085 StackNode<T>[] stackCopy = snapshot.getStack();
6086 int stackLen = snapshot.getStackLength();
6087 StackNode<T>[] listCopy = snapshot.getListOfActiveFormattingElements();
6088 int listLen = snapshot.getListOfActiveFormattingElementsLength();
6089 int[] templateModeStackCopy = snapshot.getTemplateModeStack();
6090 int templateModeStackLen = snapshot.getTemplateModeStackLength();
6092 for (int i = 0; i <= listPtr; i++) {
6093 if (listOfActiveFormattingElements[i] != null) {
6094 listOfActiveFormattingElements[i].release(this);
6097 if (listOfActiveFormattingElements.length < listLen) {
6098 listOfActiveFormattingElements = new StackNode[listLen];
6100 listPtr = listLen - 1;
6102 for (int i = 0; i <= currentPtr; i++) {
6103 stack[i].release(this);
6105 if (stack.length < stackLen) {
6106 stack = new StackNode[stackLen];
6108 currentPtr = stackLen - 1;
6110 if (templateModeStack.length < templateModeStackLen) {
6111 templateModeStack = new int[templateModeStackLen];
6113 templateModePtr = templateModeStackLen - 1;
6115 for (int i = 0; i < listLen; i++) {
6116 StackNode<T> node = listCopy[i];
6117 if (node != null) {
6118 StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
6119 node.name, node.node,
6120 node.popName,
6121 node.attributes.cloneAttributes()
6122 // CPPONLY: , node.getHtmlCreator()
6123 // [NOCPP[
6124 , node.getLocator()
6125 // ]NOCPP]
6127 listOfActiveFormattingElements[i] = newNode;
6128 } else {
6129 listOfActiveFormattingElements[i] = null;
6132 for (int i = 0; i < stackLen; i++) {
6133 StackNode<T> node = stackCopy[i];
6134 int listIndex = findInArray(node, listCopy);
6135 if (listIndex == -1) {
6136 StackNode<T> newNode = createStackNode(node.getFlags(), node.ns,
6137 node.name, node.node,
6138 node.popName,
6139 null
6140 // CPPONLY: , node.getHtmlCreator()
6141 // [NOCPP[
6142 , node.getLocator()
6143 // ]NOCPP]
6145 stack[i] = newNode;
6146 } else {
6147 stack[i] = listOfActiveFormattingElements[listIndex];
6148 stack[i].retain();
6151 System.arraycopy(templateModeStackCopy, 0, templateModeStack, 0, templateModeStackLen);
6152 formPointer = snapshot.getFormPointer();
6153 headPointer = snapshot.getHeadPointer();
6154 mode = snapshot.getMode();
6155 originalMode = snapshot.getOriginalMode();
6156 framesetOk = snapshot.isFramesetOk();
6157 needToDropLF = snapshot.isNeedToDropLF();
6158 quirks = snapshot.isQuirks();
6161 private int findInArray(StackNode<T> node, StackNode<T>[] arr) {
6162 for (int i = listPtr; i >= 0; i--) {
6163 if (node == arr[i]) {
6164 return i;
6167 return -1;
6171 * Returns <code>stack[stackPos].node</code> if <code>stackPos</code> is
6172 * smaller than Blink's magic limit or the node at Blink's magic limit
6173 * otherwise.
6175 * In order to get Blink-compatible handling of excessive deeply-nested
6176 * markup, this method must be used to obtain the node that is used as the
6177 * parent node of an insertion.
6179 * Blink's magic number is 512, but our counting is off by one compared to
6180 * Blink's way of counting, so in order to get the same
6181 * externally-observable outcome, we use 511 as our magic number.
6183 * @param stackPos the stack position to attempt to read
6184 * @return node at the position capped to Blink's magic number
6185 * @throws SAXException
6187 private T nodeFromStackWithBlinkCompat(int stackPos) throws SAXException {
6188 // Magic number if off by one relative to Blink's magic number, but the
6189 // outcome is the same, because the counting is different by one.
6190 if (stackPos > 511) {
6191 errDeepTree();
6192 return stack[511].node;
6194 return stack[stackPos].node;
6197 * @see nu.validator.htmlparser.impl.TreeBuilderState#getFormPointer()
6199 @Override
6200 public T getFormPointer() {
6201 return formPointer;
6205 * Returns the headPointer.
6207 * @return the headPointer
6209 @Override
6210 public T getHeadPointer() {
6211 return headPointer;
6215 * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElements()
6217 @Override
6218 public StackNode<T>[] getListOfActiveFormattingElements() {
6219 return listOfActiveFormattingElements;
6223 * @see nu.validator.htmlparser.impl.TreeBuilderState#getStack()
6225 @Override
6226 public StackNode<T>[] getStack() {
6227 return stack;
6231 * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStack()
6233 @Override
6234 public int[] getTemplateModeStack() {
6235 return templateModeStack;
6239 * Returns the mode.
6241 * @return the mode
6243 @Override
6244 public int getMode() {
6245 return mode;
6249 * Returns the originalMode.
6251 * @return the originalMode
6253 @Override
6254 public int getOriginalMode() {
6255 return originalMode;
6259 * Returns the framesetOk.
6261 * @return the framesetOk
6263 @Override
6264 public boolean isFramesetOk() {
6265 return framesetOk;
6269 * Returns the needToDropLF.
6271 * @return the needToDropLF
6273 @Override
6274 public boolean isNeedToDropLF() {
6275 return needToDropLF;
6279 * Returns the quirks.
6281 * @return the quirks
6283 @Override
6284 public boolean isQuirks() {
6285 return quirks;
6289 * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElementsLength()
6291 @Override
6292 public int getListOfActiveFormattingElementsLength() {
6293 return listPtr + 1;
6297 * @see nu.validator.htmlparser.impl.TreeBuilderState#getStackLength()
6299 @Override
6300 public int getStackLength() {
6301 return currentPtr + 1;
6305 * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStackLength()
6307 @Override
6308 public int getTemplateModeStackLength() {
6309 return templateModePtr + 1;
6313 * Complains about an over-deep tree. Theoretically this should just be
6314 * a warning, but in practice authors should take this as an error.
6316 * @throws SAXException
6318 private void errDeepTree() throws SAXException {
6319 err("The document tree is more than 513 elements deep, which causes Firefox and Chrome to flatten the tree.");
6323 * Reports a stray start tag.
6324 * @param name the name of the stray tag
6326 * @throws SAXException
6328 private void errStrayStartTag(@Local String name) throws SAXException {
6329 err("Stray start tag \u201C" + name + "\u201D.");
6333 * Reports a stray end tag.
6334 * @param name the name of the stray tag
6336 * @throws SAXException
6338 private void errStrayEndTag(@Local String name) throws SAXException {
6339 err("Stray end tag \u201C" + name + "\u201D.");
6343 * Reports a state when elements expected to be closed were not.
6345 * @param eltPos the position of the start tag on the stack of the element
6346 * being closed.
6347 * @param name the name of the end tag
6349 * @throws SAXException
6351 private void errUnclosedElements(int eltPos, @Local String name) throws SAXException {
6352 errNoCheck("End tag \u201C" + name + "\u201D seen, but there were open elements.");
6353 errListUnclosedStartTags(eltPos);
6357 * Reports a state when elements expected to be closed ahead of an implied
6358 * end tag but were not.
6360 * @param eltPos the position of the start tag on the stack of the element
6361 * being closed.
6362 * @param name the name of the end tag
6364 * @throws SAXException
6366 private void errUnclosedElementsImplied(int eltPos, String name) throws SAXException {
6367 errNoCheck("End tag \u201C" + name + "\u201D implied, but there were open elements.");
6368 errListUnclosedStartTags(eltPos);
6372 * Reports a state when elements expected to be closed ahead of an implied
6373 * table cell close.
6375 * @param eltPos the position of the start tag on the stack of the element
6376 * being closed.
6377 * @throws SAXException
6379 private void errUnclosedElementsCell(int eltPos) throws SAXException {
6380 errNoCheck("A table cell was implicitly closed, but there were open elements.");
6381 errListUnclosedStartTags(eltPos);
6384 private void errStrayDoctype() throws SAXException {
6385 err("Stray doctype.");
6388 private void errAlmostStandardsDoctype() throws SAXException {
6389 if (!forceNoQuirks) {
6390 err("Almost standards mode doctype. Expected \u201C<!DOCTYPE html>\u201D.");
6394 private void errQuirkyDoctype() throws SAXException {
6395 if (!forceNoQuirks) {
6396 err("Quirky doctype. Expected \u201C<!DOCTYPE html>\u201D.");
6400 private void errNonSpaceInTrailer() throws SAXException {
6401 err("Non-space character in page trailer.");
6404 private void errNonSpaceAfterFrameset() throws SAXException {
6405 err("Non-space after \u201Cframeset\u201D.");
6408 private void errNonSpaceInFrameset() throws SAXException {
6409 err("Non-space in \u201Cframeset\u201D.");
6412 private void errNonSpaceAfterBody() throws SAXException {
6413 err("Non-space character after body.");
6416 private void errNonSpaceInColgroupInFragment() throws SAXException {
6417 err("Non-space in \u201Ccolgroup\u201D when parsing fragment.");
6420 private void errNonSpaceInNoscriptInHead() throws SAXException {
6421 err("Non-space character inside \u201Cnoscript\u201D inside \u201Chead\u201D.");
6424 private void errFooBetweenHeadAndBody(@Local String name) throws SAXException {
6425 if (errorHandler == null) {
6426 return;
6428 errNoCheck("\u201C" + name + "\u201D element between \u201Chead\u201D and \u201Cbody\u201D.");
6431 private void errStartTagWithoutDoctype() throws SAXException {
6432 if (!forceNoQuirks) {
6433 err("Start tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
6437 private void errNoSelectInTableScope() throws SAXException {
6438 err("No \u201Cselect\u201D in table scope.");
6441 private void errStartSelectWhereEndSelectExpected() throws SAXException {
6442 err("\u201Cselect\u201D start tag where end tag expected.");
6445 private void errStartTagWithSelectOpen(@Local String name)
6446 throws SAXException {
6447 if (errorHandler == null) {
6448 return;
6450 errNoCheck("\u201C" + name
6451 + "\u201D start tag with \u201Cselect\u201D open.");
6454 private void errBadStartTagInNoscriptInHead(@Local String name) throws SAXException {
6455 if (errorHandler == null) {
6456 return;
6458 errNoCheck("Bad start tag in \u201C" + name
6459 + "\u201D in \u201Cnoscript\u201D in \u201Chead\u201D.");
6462 private void errImage() throws SAXException {
6463 err("Saw a start tag \u201Cimage\u201D.");
6466 private void errFooSeenWhenFooOpen(@Local String name) throws SAXException {
6467 if (errorHandler == null) {
6468 return;
6470 errNoCheck("Start tag \u201C" + name + "\u201D seen but an element of the same type was already open.");
6473 private void errHeadingWhenHeadingOpen() throws SAXException {
6474 err("Heading cannot be a child of another heading.");
6477 private void errFramesetStart() throws SAXException {
6478 err("\u201Cframeset\u201D start tag seen.");
6481 private void errNoCellToClose() throws SAXException {
6482 err("No cell to close.");
6485 private void errStartTagInTable(@Local String name) throws SAXException {
6486 if (errorHandler == null) {
6487 return;
6489 errNoCheck("Start tag \u201C" + name
6490 + "\u201D seen in \u201Ctable\u201D.");
6493 private void errFormWhenFormOpen() throws SAXException {
6494 err("Saw a \u201Cform\u201D start tag, but there was already an active \u201Cform\u201D element. Nested forms are not allowed. Ignoring the tag.");
6497 private void errTableSeenWhileTableOpen() throws SAXException {
6498 err("Start tag for \u201Ctable\u201D seen but the previous \u201Ctable\u201D is still open.");
6501 private void errStartTagInTableBody(@Local String name) throws SAXException {
6502 if (errorHandler == null) {
6503 return;
6505 errNoCheck("\u201C" + name + "\u201D start tag in table body.");
6508 private void errEndTagSeenWithoutDoctype() throws SAXException {
6509 if (!forceNoQuirks) {
6510 err("End tag seen without seeing a doctype first. Expected \u201C<!DOCTYPE html>\u201D.");
6514 private void errEndTagAfterBody() throws SAXException {
6515 err("Saw an end tag after \u201Cbody\u201D had been closed.");
6518 private void errEndTagSeenWithSelectOpen(@Local String name) throws SAXException {
6519 if (errorHandler == null) {
6520 return;
6522 errNoCheck("\u201C" + name
6523 + "\u201D end tag with \u201Cselect\u201D open.");
6526 private void errGarbageInColgroup() throws SAXException {
6527 err("Garbage in \u201Ccolgroup\u201D fragment.");
6530 private void errEndTagBr() throws SAXException {
6531 err("End tag \u201Cbr\u201D.");
6534 private void errNoElementToCloseButEndTagSeen(@Local String name)
6535 throws SAXException {
6536 if (errorHandler == null) {
6537 return;
6539 errNoCheck("No \u201C" + name + "\u201D element in scope but a \u201C"
6540 + name + "\u201D end tag seen.");
6543 private void errHtmlStartTagInForeignContext(@Local String name)
6544 throws SAXException {
6545 if (errorHandler == null) {
6546 return;
6548 errNoCheck("HTML start tag \u201C" + name
6549 + "\u201D in a foreign namespace context.");
6552 private void errNoTableRowToClose() throws SAXException {
6553 err("No table row to close.");
6556 private void errNonSpaceInTable() throws SAXException {
6557 err("Misplaced non-space characters inside a table.");
6560 private void errUnclosedChildrenInRuby() throws SAXException {
6561 if (errorHandler == null) {
6562 return;
6564 errNoCheck("Unclosed children in \u201Cruby\u201D.");
6567 private void errStartTagSeenWithoutRuby(@Local String name) throws SAXException {
6568 if (errorHandler == null) {
6569 return;
6571 errNoCheck("Start tag \u201C"
6572 + name
6573 + "\u201D seen without a \u201Cruby\u201D element being open.");
6576 private void errSelfClosing() throws SAXException {
6577 if (errorHandler == null) {
6578 return;
6580 errNoCheck("Self-closing syntax (\u201C/>\u201D) used on a non-void HTML element. Ignoring the slash and treating as a start tag.");
6583 private void errNoCheckUnclosedElementsOnStack() throws SAXException {
6584 errNoCheck("Unclosed elements on stack.");
6587 private void errEndTagDidNotMatchCurrentOpenElement(@Local String name,
6588 @Local String currOpenName) throws SAXException {
6589 if (errorHandler == null) {
6590 return;
6592 errNoCheck("End tag \u201C"
6593 + name
6594 + "\u201D did not match the name of the current open element (\u201C"
6595 + currOpenName + "\u201D).");
6598 private void errEndTagViolatesNestingRules(@Local String name) throws SAXException {
6599 if (errorHandler == null) {
6600 return;
6602 errNoCheck("End tag \u201C" + name + "\u201D violates nesting rules.");
6605 private void errEofWithUnclosedElements() throws SAXException {
6606 if (errorHandler == null) {
6607 return;
6609 errNoCheck("End of file seen and there were open elements.");
6610 // just report all remaining unclosed elements
6611 errListUnclosedStartTags(0);
6615 * Reports arriving at/near end of document with unclosed elements remaining.
6617 * @param message
6618 * the message
6619 * @throws SAXException
6621 private void errEndWithUnclosedElements(@Local String name) throws SAXException {
6622 if (errorHandler == null) {
6623 return;
6625 errNoCheck("End tag for \u201C"
6626 + name
6627 + "\u201D seen, but there were unclosed elements.");
6628 // just report all remaining unclosed elements
6629 errListUnclosedStartTags(0);