michael@0: /* michael@0: * Copyright (c) 2007 Henri Sivonen michael@0: * Copyright (c) 2007-2011 Mozilla Foundation michael@0: * Portions of comments Copyright 2004-2008 Apple Computer, Inc., Mozilla michael@0: * Foundation, and Opera Software ASA. michael@0: * michael@0: * Permission is hereby granted, free of charge, to any person obtaining a michael@0: * copy of this software and associated documentation files (the "Software"), michael@0: * to deal in the Software without restriction, including without limitation michael@0: * the rights to use, copy, modify, merge, publish, distribute, sublicense, michael@0: * and/or sell copies of the Software, and to permit persons to whom the michael@0: * Software is furnished to do so, subject to the following conditions: michael@0: * michael@0: * The above copyright notice and this permission notice shall be included in michael@0: * all copies or substantial portions of the Software. michael@0: * michael@0: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR michael@0: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, michael@0: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL michael@0: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER michael@0: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING michael@0: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER michael@0: * DEALINGS IN THE SOFTWARE. michael@0: */ michael@0: michael@0: /* michael@0: * The comments following this one that use the same comment syntax as this michael@0: * comment are quotes from the WHATWG HTML 5 spec as of 27 June 2007 michael@0: * amended as of June 28 2007. michael@0: * That document came with this statement: michael@0: * "© Copyright 2004-2007 Apple Computer, Inc., Mozilla Foundation, and michael@0: * Opera Software ASA. You are granted a license to use, reproduce and michael@0: * create derivative works of this document." michael@0: */ michael@0: michael@0: package nu.validator.htmlparser.impl; michael@0: michael@0: import java.util.Arrays; michael@0: import java.util.HashMap; michael@0: import java.util.Map; michael@0: michael@0: import nu.validator.htmlparser.annotation.Auto; michael@0: import nu.validator.htmlparser.annotation.Const; michael@0: import nu.validator.htmlparser.annotation.IdType; michael@0: import nu.validator.htmlparser.annotation.Inline; michael@0: import nu.validator.htmlparser.annotation.Literal; michael@0: import nu.validator.htmlparser.annotation.Local; michael@0: import nu.validator.htmlparser.annotation.NoLength; michael@0: import nu.validator.htmlparser.annotation.NsUri; michael@0: import nu.validator.htmlparser.common.DoctypeExpectation; michael@0: import nu.validator.htmlparser.common.DocumentMode; michael@0: import nu.validator.htmlparser.common.DocumentModeHandler; michael@0: import nu.validator.htmlparser.common.Interner; michael@0: import nu.validator.htmlparser.common.TokenHandler; michael@0: import nu.validator.htmlparser.common.XmlViolationPolicy; michael@0: michael@0: import org.xml.sax.ErrorHandler; michael@0: import org.xml.sax.Locator; michael@0: import org.xml.sax.SAXException; michael@0: import org.xml.sax.SAXParseException; michael@0: michael@0: public abstract class TreeBuilder implements TokenHandler, michael@0: TreeBuilderState { michael@0: michael@0: /** michael@0: * Array version of U+FFFD. michael@0: */ michael@0: private static final @NoLength char[] REPLACEMENT_CHARACTER = { '\uFFFD' }; michael@0: michael@0: // Start dispatch groups michael@0: michael@0: final static int OTHER = 0; michael@0: michael@0: final static int A = 1; michael@0: michael@0: final static int BASE = 2; michael@0: michael@0: final static int BODY = 3; michael@0: michael@0: final static int BR = 4; michael@0: michael@0: final static int BUTTON = 5; michael@0: michael@0: final static int CAPTION = 6; michael@0: michael@0: final static int COL = 7; michael@0: michael@0: final static int COLGROUP = 8; michael@0: michael@0: final static int FORM = 9; michael@0: michael@0: final static int FRAME = 10; michael@0: michael@0: final static int FRAMESET = 11; michael@0: michael@0: final static int IMAGE = 12; michael@0: michael@0: final static int INPUT = 13; michael@0: michael@0: final static int ISINDEX = 14; michael@0: michael@0: final static int LI = 15; michael@0: michael@0: final static int LINK_OR_BASEFONT_OR_BGSOUND = 16; michael@0: michael@0: final static int MATH = 17; michael@0: michael@0: final static int META = 18; michael@0: michael@0: final static int SVG = 19; michael@0: michael@0: final static int HEAD = 20; michael@0: michael@0: final static int HR = 22; michael@0: michael@0: final static int HTML = 23; michael@0: michael@0: final static int NOBR = 24; michael@0: michael@0: final static int NOFRAMES = 25; michael@0: michael@0: final static int NOSCRIPT = 26; michael@0: michael@0: final static int OPTGROUP = 27; michael@0: michael@0: final static int OPTION = 28; michael@0: michael@0: final static int P = 29; michael@0: michael@0: final static int PLAINTEXT = 30; michael@0: michael@0: final static int SCRIPT = 31; michael@0: michael@0: final static int SELECT = 32; michael@0: michael@0: final static int STYLE = 33; michael@0: michael@0: final static int TABLE = 34; michael@0: michael@0: final static int TEXTAREA = 35; michael@0: michael@0: final static int TITLE = 36; michael@0: michael@0: final static int TR = 37; michael@0: michael@0: final static int XMP = 38; michael@0: michael@0: final static int TBODY_OR_THEAD_OR_TFOOT = 39; michael@0: michael@0: final static int TD_OR_TH = 40; michael@0: michael@0: final static int DD_OR_DT = 41; michael@0: michael@0: final static int H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6 = 42; michael@0: michael@0: final static int MARQUEE_OR_APPLET = 43; michael@0: michael@0: final static int PRE_OR_LISTING = 44; michael@0: michael@0: 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; michael@0: michael@0: final static int UL_OR_OL_OR_DL = 46; michael@0: michael@0: final static int IFRAME = 47; michael@0: michael@0: final static int EMBED = 48; michael@0: michael@0: final static int AREA_OR_WBR = 49; michael@0: michael@0: final static int DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU = 50; michael@0: michael@0: final static int ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY = 51; michael@0: michael@0: final static int RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR = 52; michael@0: michael@0: final static int RT_OR_RP = 53; michael@0: michael@0: final static int PARAM_OR_SOURCE_OR_TRACK = 55; michael@0: michael@0: final static int MGLYPH_OR_MALIGNMARK = 56; michael@0: michael@0: final static int MI_MO_MN_MS_MTEXT = 57; michael@0: michael@0: final static int ANNOTATION_XML = 58; michael@0: michael@0: final static int FOREIGNOBJECT_OR_DESC = 59; michael@0: michael@0: final static int NOEMBED = 60; michael@0: michael@0: final static int FIELDSET = 61; michael@0: michael@0: final static int OUTPUT_OR_LABEL = 62; michael@0: michael@0: final static int OBJECT = 63; michael@0: michael@0: final static int FONT = 64; michael@0: michael@0: final static int KEYGEN = 65; michael@0: michael@0: final static int MENUITEM = 66; michael@0: michael@0: final static int TEMPLATE = 67; michael@0: michael@0: final static int IMG = 68; michael@0: michael@0: // start insertion modes michael@0: michael@0: private static final int IN_ROW = 0; michael@0: michael@0: private static final int IN_TABLE_BODY = 1; michael@0: michael@0: private static final int IN_TABLE = 2; michael@0: michael@0: private static final int IN_CAPTION = 3; michael@0: michael@0: private static final int IN_CELL = 4; michael@0: michael@0: private static final int FRAMESET_OK = 5; michael@0: michael@0: private static final int IN_BODY = 6; michael@0: michael@0: private static final int IN_HEAD = 7; michael@0: michael@0: private static final int IN_HEAD_NOSCRIPT = 8; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int IN_COLUMN_GROUP = 9; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int IN_SELECT_IN_TABLE = 10; michael@0: michael@0: private static final int IN_SELECT = 11; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int AFTER_BODY = 12; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int IN_FRAMESET = 13; michael@0: michael@0: private static final int AFTER_FRAMESET = 14; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int INITIAL = 15; michael@0: michael@0: // could add fall-through michael@0: michael@0: private static final int BEFORE_HTML = 16; michael@0: michael@0: // could add fall-through michael@0: michael@0: private static final int BEFORE_HEAD = 17; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int AFTER_HEAD = 18; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int AFTER_AFTER_BODY = 19; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int AFTER_AFTER_FRAMESET = 20; michael@0: michael@0: // no fall-through michael@0: michael@0: private static final int TEXT = 21; michael@0: michael@0: private static final int IN_TEMPLATE = 22; michael@0: michael@0: // start charset states michael@0: michael@0: private static final int CHARSET_INITIAL = 0; michael@0: michael@0: private static final int CHARSET_C = 1; michael@0: michael@0: private static final int CHARSET_H = 2; michael@0: michael@0: private static final int CHARSET_A = 3; michael@0: michael@0: private static final int CHARSET_R = 4; michael@0: michael@0: private static final int CHARSET_S = 5; michael@0: michael@0: private static final int CHARSET_E = 6; michael@0: michael@0: private static final int CHARSET_T = 7; michael@0: michael@0: private static final int CHARSET_EQUALS = 8; michael@0: michael@0: private static final int CHARSET_SINGLE_QUOTED = 9; michael@0: michael@0: private static final int CHARSET_DOUBLE_QUOTED = 10; michael@0: michael@0: private static final int CHARSET_UNQUOTED = 11; michael@0: michael@0: // end pseudo enums michael@0: michael@0: // [NOCPP[ michael@0: michael@0: private final static String[] HTML4_PUBLIC_IDS = { michael@0: "-//W3C//DTD HTML 4.0 Frameset//EN", michael@0: "-//W3C//DTD HTML 4.0 Transitional//EN", michael@0: "-//W3C//DTD HTML 4.0//EN", "-//W3C//DTD HTML 4.01 Frameset//EN", michael@0: "-//W3C//DTD HTML 4.01 Transitional//EN", michael@0: "-//W3C//DTD HTML 4.01//EN" }; michael@0: michael@0: // ]NOCPP] michael@0: michael@0: @Literal private final static String[] QUIRKY_PUBLIC_IDS = { michael@0: "+//silmaril//dtd html pro v0r11 19970101//", michael@0: "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", michael@0: "-//as//dtd html 3.0 aswedit + extensions//", michael@0: "-//ietf//dtd html 2.0 level 1//", michael@0: "-//ietf//dtd html 2.0 level 2//", michael@0: "-//ietf//dtd html 2.0 strict level 1//", michael@0: "-//ietf//dtd html 2.0 strict level 2//", michael@0: "-//ietf//dtd html 2.0 strict//", michael@0: "-//ietf//dtd html 2.0//", michael@0: "-//ietf//dtd html 2.1e//", michael@0: "-//ietf//dtd html 3.0//", michael@0: "-//ietf//dtd html 3.2 final//", michael@0: "-//ietf//dtd html 3.2//", michael@0: "-//ietf//dtd html 3//", michael@0: "-//ietf//dtd html level 0//", michael@0: "-//ietf//dtd html level 1//", michael@0: "-//ietf//dtd html level 2//", michael@0: "-//ietf//dtd html level 3//", michael@0: "-//ietf//dtd html strict level 0//", michael@0: "-//ietf//dtd html strict level 1//", michael@0: "-//ietf//dtd html strict level 2//", michael@0: "-//ietf//dtd html strict level 3//", michael@0: "-//ietf//dtd html strict//", michael@0: "-//ietf//dtd html//", michael@0: "-//metrius//dtd metrius presentational//", michael@0: "-//microsoft//dtd internet explorer 2.0 html strict//", michael@0: "-//microsoft//dtd internet explorer 2.0 html//", michael@0: "-//microsoft//dtd internet explorer 2.0 tables//", michael@0: "-//microsoft//dtd internet explorer 3.0 html strict//", michael@0: "-//microsoft//dtd internet explorer 3.0 html//", michael@0: "-//microsoft//dtd internet explorer 3.0 tables//", michael@0: "-//netscape comm. corp.//dtd html//", michael@0: "-//netscape comm. corp.//dtd strict html//", michael@0: "-//o'reilly and associates//dtd html 2.0//", michael@0: "-//o'reilly and associates//dtd html extended 1.0//", michael@0: "-//o'reilly and associates//dtd html extended relaxed 1.0//", michael@0: "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", michael@0: "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", michael@0: "-//spyglass//dtd html 2.0 extended//", michael@0: "-//sq//dtd html 2.0 hotmetal + extensions//", michael@0: "-//sun microsystems corp.//dtd hotjava html//", michael@0: "-//sun microsystems corp.//dtd hotjava strict html//", michael@0: "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//", michael@0: "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//", michael@0: "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//", michael@0: "-//w3c//dtd html 4.0 transitional//", michael@0: "-//w3c//dtd html experimental 19960712//", michael@0: "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//", michael@0: "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//", michael@0: "-//webtechs//dtd mozilla html//" }; michael@0: michael@0: private static final int NOT_FOUND_ON_STACK = Integer.MAX_VALUE; michael@0: michael@0: // [NOCPP[ michael@0: michael@0: private static final @Local String HTML_LOCAL = "html"; michael@0: michael@0: // ]NOCPP] michael@0: michael@0: private int mode = INITIAL; michael@0: michael@0: private int originalMode = INITIAL; michael@0: michael@0: /** michael@0: * Used only when moving back to IN_BODY. michael@0: */ michael@0: private boolean framesetOk = true; michael@0: michael@0: protected Tokenizer tokenizer; michael@0: michael@0: // [NOCPP[ michael@0: michael@0: protected ErrorHandler errorHandler; michael@0: michael@0: private DocumentModeHandler documentModeHandler; michael@0: michael@0: private DoctypeExpectation doctypeExpectation = DoctypeExpectation.HTML; michael@0: michael@0: private LocatorImpl firstCommentLocation; michael@0: michael@0: // ]NOCPP] michael@0: michael@0: private boolean scriptingEnabled = false; michael@0: michael@0: private boolean needToDropLF; michael@0: michael@0: // [NOCPP[ michael@0: michael@0: private boolean wantingComments; michael@0: michael@0: // ]NOCPP] michael@0: michael@0: private boolean fragment; michael@0: michael@0: private @Local String contextName; michael@0: michael@0: private @NsUri String contextNamespace; michael@0: michael@0: private T contextNode; michael@0: michael@0: /** michael@0: * Stack of template insertion modes michael@0: */ michael@0: private @Auto int[] templateModeStack; michael@0: michael@0: /** michael@0: * Current template mode stack pointer. michael@0: */ michael@0: private int templateModePtr = -1; michael@0: michael@0: private @Auto StackNode[] stack; michael@0: michael@0: private int currentPtr = -1; michael@0: michael@0: private @Auto StackNode[] listOfActiveFormattingElements; michael@0: michael@0: private int listPtr = -1; michael@0: michael@0: private T formPointer; michael@0: michael@0: private T headPointer; michael@0: michael@0: /** michael@0: * Used to work around Gecko limitations. Not used in Java. michael@0: */ michael@0: private T deepTreeSurrogateParent; michael@0: michael@0: protected @Auto char[] charBuffer; michael@0: michael@0: protected int charBufferLen = 0; michael@0: michael@0: private boolean quirks = false; michael@0: michael@0: private boolean isSrcdocDocument = false; michael@0: michael@0: // [NOCPP[ michael@0: michael@0: private boolean reportingDoctype = true; michael@0: michael@0: private XmlViolationPolicy namePolicy = XmlViolationPolicy.ALTER_INFOSET; michael@0: michael@0: private final Map idLocations = new HashMap(); michael@0: michael@0: private boolean html4; michael@0: michael@0: // ]NOCPP] michael@0: michael@0: protected TreeBuilder() { michael@0: fragment = false; michael@0: } michael@0: michael@0: /** michael@0: * Reports an condition that would make the infoset incompatible with XML michael@0: * 1.0 as fatal. michael@0: * michael@0: * @throws SAXException michael@0: * @throws SAXParseException michael@0: */ michael@0: protected void fatal() throws SAXException { michael@0: } michael@0: michael@0: // [NOCPP[ michael@0: michael@0: protected final void fatal(Exception e) throws SAXException { michael@0: SAXParseException spe = new SAXParseException(e.getMessage(), michael@0: tokenizer, e); michael@0: if (errorHandler != null) { michael@0: errorHandler.fatalError(spe); michael@0: } michael@0: throw spe; michael@0: } michael@0: michael@0: final void fatal(String s) throws SAXException { michael@0: SAXParseException spe = new SAXParseException(s, tokenizer); michael@0: if (errorHandler != null) { michael@0: errorHandler.fatalError(spe); michael@0: } michael@0: throw spe; michael@0: } michael@0: michael@0: /** michael@0: * Reports a Parse Error. michael@0: * michael@0: * @param message michael@0: * the message michael@0: * @throws SAXException michael@0: */ michael@0: final void err(String message) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck(message); michael@0: } michael@0: michael@0: /** michael@0: * Reports a Parse Error without checking if an error handler is present. michael@0: * michael@0: * @param message michael@0: * the message michael@0: * @throws SAXException michael@0: */ michael@0: final void errNoCheck(String message) throws SAXException { michael@0: SAXParseException spe = new SAXParseException(message, tokenizer); michael@0: errorHandler.error(spe); michael@0: } michael@0: michael@0: private void errListUnclosedStartTags(int eltPos) throws SAXException { michael@0: if (currentPtr != -1) { michael@0: for (int i = currentPtr; i > eltPos; i--) { michael@0: reportUnclosedElementNameAndLocation(i); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Reports the name and location of an unclosed element. michael@0: * michael@0: * @throws SAXException michael@0: */ michael@0: private final void reportUnclosedElementNameAndLocation(int pos) throws SAXException { michael@0: StackNode node = stack[pos]; michael@0: if (node.isOptionalEndTag()) { michael@0: return; michael@0: } michael@0: TaintableLocatorImpl locator = node.getLocator(); michael@0: if (locator.isTainted()) { michael@0: return; michael@0: } michael@0: locator.markTainted(); michael@0: SAXParseException spe = new SAXParseException( michael@0: "Unclosed element \u201C" + node.popName + "\u201D.", locator); michael@0: errorHandler.error(spe); michael@0: } michael@0: michael@0: /** michael@0: * Reports a warning michael@0: * michael@0: * @param message michael@0: * the message michael@0: * @throws SAXException michael@0: */ michael@0: final void warn(String message) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: SAXParseException spe = new SAXParseException(message, tokenizer); michael@0: errorHandler.warning(spe); michael@0: } michael@0: michael@0: /** michael@0: * Reports a warning with an explicit locator michael@0: * michael@0: * @param message michael@0: * the message michael@0: * @throws SAXException michael@0: */ michael@0: final void warn(String message, Locator locator) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: SAXParseException spe = new SAXParseException(message, locator); michael@0: errorHandler.warning(spe); michael@0: } michael@0: michael@0: // ]NOCPP] michael@0: michael@0: @SuppressWarnings("unchecked") public final void startTokenization(Tokenizer self) throws SAXException { michael@0: tokenizer = self; michael@0: stack = new StackNode[64]; michael@0: templateModeStack = new int[64]; michael@0: listOfActiveFormattingElements = new StackNode[64]; michael@0: needToDropLF = false; michael@0: originalMode = INITIAL; michael@0: templateModePtr = -1; michael@0: currentPtr = -1; michael@0: listPtr = -1; michael@0: formPointer = null; michael@0: headPointer = null; michael@0: deepTreeSurrogateParent = null; michael@0: // [NOCPP[ michael@0: html4 = false; michael@0: idLocations.clear(); michael@0: wantingComments = wantsComments(); michael@0: firstCommentLocation = null; michael@0: // ]NOCPP] michael@0: start(fragment); michael@0: charBufferLen = 0; michael@0: charBuffer = new char[1024]; michael@0: framesetOk = true; michael@0: if (fragment) { michael@0: T elt; michael@0: if (contextNode != null) { michael@0: elt = contextNode; michael@0: } else { michael@0: elt = createHtmlElementSetAsRoot(tokenizer.emptyAttributes()); michael@0: } michael@0: StackNode node = new StackNode(ElementName.HTML, elt michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: currentPtr++; michael@0: stack[currentPtr] = node; michael@0: if ("template" == contextName) { michael@0: pushTemplateMode(IN_TEMPLATE); michael@0: } michael@0: resetTheInsertionMode(); michael@0: formPointer = getFormPointerForContext(contextNode); michael@0: if ("title" == contextName || "textarea" == contextName) { michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA, contextName); michael@0: } else if ("style" == contextName || "xmp" == contextName michael@0: || "iframe" == contextName || "noembed" == contextName michael@0: || "noframes" == contextName michael@0: || (scriptingEnabled && "noscript" == contextName)) { michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.RAWTEXT, contextName); michael@0: } else if ("plaintext" == contextName) { michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.PLAINTEXT, contextName); michael@0: } else if ("script" == contextName) { michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.SCRIPT_DATA, michael@0: contextName); michael@0: } else { michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.DATA, contextName); michael@0: } michael@0: contextName = null; michael@0: contextNode = null; michael@0: } else { michael@0: mode = INITIAL; michael@0: // If we are viewing XML source, put a foreign element permanently michael@0: // on the stack so that cdataSectionAllowed() returns true. michael@0: // CPPONLY: if (tokenizer.isViewingXmlSource()) { michael@0: // CPPONLY: T elt = createElement("http://www.w3.org/2000/svg", michael@0: // CPPONLY: "svg", michael@0: // CPPONLY: tokenizer.emptyAttributes()); michael@0: // CPPONLY: StackNode node = new StackNode(ElementName.SVG, michael@0: // CPPONLY: "svg", michael@0: // CPPONLY: elt); michael@0: // CPPONLY: currentPtr++; michael@0: // CPPONLY: stack[currentPtr] = node; michael@0: // CPPONLY: } michael@0: } michael@0: } michael@0: michael@0: public final void doctype(@Local String name, String publicIdentifier, michael@0: String systemIdentifier, boolean forceQuirks) throws SAXException { michael@0: needToDropLF = false; michael@0: if (!isInForeign() && mode == INITIAL) { michael@0: // [NOCPP[ michael@0: if (reportingDoctype) { michael@0: // ]NOCPP] michael@0: String emptyString = Portability.newEmptyString(); michael@0: appendDoctypeToDocument(name == null ? "" : name, michael@0: publicIdentifier == null ? emptyString michael@0: : publicIdentifier, michael@0: systemIdentifier == null ? emptyString michael@0: : systemIdentifier); michael@0: Portability.releaseString(emptyString); michael@0: // [NOCPP[ michael@0: } michael@0: switch (doctypeExpectation) { michael@0: case HTML: michael@0: // ]NOCPP] michael@0: if (isQuirky(name, publicIdentifier, systemIdentifier, michael@0: forceQuirks)) { michael@0: errQuirkyDoctype(); michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, michael@0: publicIdentifier, systemIdentifier, false); michael@0: } else if (isAlmostStandards(publicIdentifier, michael@0: systemIdentifier)) { michael@0: // [NOCPP[ michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: // ]NOCPP] michael@0: errAlmostStandardsDoctype(); michael@0: documentModeInternal( michael@0: DocumentMode.ALMOST_STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, false); michael@0: } else { michael@0: // [NOCPP[ michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: if ((Portability.literalEqualsString( michael@0: "-//W3C//DTD HTML 4.0//EN", publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString( michael@0: "http://www.w3.org/TR/REC-html40/strict.dtd", michael@0: systemIdentifier))) michael@0: || (Portability.literalEqualsString( michael@0: "-//W3C//DTD HTML 4.01//EN", michael@0: publicIdentifier) && (systemIdentifier == null || Portability.literalEqualsString( michael@0: "http://www.w3.org/TR/html4/strict.dtd", michael@0: systemIdentifier))) michael@0: || (Portability.literalEqualsString( michael@0: "-//W3C//DTD XHTML 1.0 Strict//EN", michael@0: publicIdentifier) && Portability.literalEqualsString( michael@0: "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", michael@0: systemIdentifier)) michael@0: || (Portability.literalEqualsString( michael@0: "-//W3C//DTD XHTML 1.1//EN", michael@0: publicIdentifier) && Portability.literalEqualsString( michael@0: "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd", michael@0: systemIdentifier)) michael@0: michael@0: ) { michael@0: warn("Obsolete doctype. Expected \u201C\u201D."); michael@0: } else if (!((systemIdentifier == null || Portability.literalEqualsString( michael@0: "about:legacy-compat", systemIdentifier)) && publicIdentifier == null)) { michael@0: err("Legacy doctype. Expected \u201C\u201D."); michael@0: } michael@0: // ]NOCPP] michael@0: documentModeInternal(DocumentMode.STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, false); michael@0: } michael@0: // [NOCPP[ michael@0: break; michael@0: case HTML401_STRICT: michael@0: html4 = true; michael@0: tokenizer.turnOnAdditionalHtml4Errors(); michael@0: if (isQuirky(name, publicIdentifier, systemIdentifier, michael@0: forceQuirks)) { michael@0: err("Quirky doctype. Expected \u201C\u201D."); michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, michael@0: publicIdentifier, systemIdentifier, true); michael@0: } else if (isAlmostStandards(publicIdentifier, michael@0: systemIdentifier)) { michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: err("Almost standards mode doctype. Expected \u201C\u201D."); michael@0: documentModeInternal( michael@0: DocumentMode.ALMOST_STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, true); michael@0: } else { michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: if ("-//W3C//DTD HTML 4.01//EN".equals(publicIdentifier)) { michael@0: if (!"http://www.w3.org/TR/html4/strict.dtd".equals(systemIdentifier)) { michael@0: warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C\u201D."); michael@0: } michael@0: } else { michael@0: err("The doctype was not the HTML 4.01 Strict doctype. Expected \u201C\u201D."); michael@0: } michael@0: documentModeInternal(DocumentMode.STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, true); michael@0: } michael@0: break; michael@0: case HTML401_TRANSITIONAL: michael@0: html4 = true; michael@0: tokenizer.turnOnAdditionalHtml4Errors(); michael@0: if (isQuirky(name, publicIdentifier, systemIdentifier, michael@0: forceQuirks)) { michael@0: err("Quirky doctype. Expected \u201C\u201D."); michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, michael@0: publicIdentifier, systemIdentifier, true); michael@0: } else if (isAlmostStandards(publicIdentifier, michael@0: systemIdentifier)) { michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: if ("-//W3C//DTD HTML 4.01 Transitional//EN".equals(publicIdentifier) michael@0: && systemIdentifier != null) { michael@0: if (!"http://www.w3.org/TR/html4/loose.dtd".equals(systemIdentifier)) { michael@0: warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C\u201D."); michael@0: } michael@0: } else { michael@0: err("The doctype was not a non-quirky HTML 4.01 Transitional doctype. Expected \u201C\u201D."); michael@0: } michael@0: documentModeInternal( michael@0: DocumentMode.ALMOST_STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, true); michael@0: } else { michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: err("The doctype was not the HTML 4.01 Transitional doctype. Expected \u201C\u201D."); michael@0: documentModeInternal(DocumentMode.STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, true); michael@0: } michael@0: break; michael@0: case AUTO: michael@0: html4 = isHtml4Doctype(publicIdentifier); michael@0: if (html4) { michael@0: tokenizer.turnOnAdditionalHtml4Errors(); michael@0: } michael@0: if (isQuirky(name, publicIdentifier, systemIdentifier, michael@0: forceQuirks)) { michael@0: err("Quirky doctype. Expected e.g. \u201C\u201D."); michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, michael@0: publicIdentifier, systemIdentifier, html4); michael@0: } else if (isAlmostStandards(publicIdentifier, michael@0: systemIdentifier)) { michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: if ("-//W3C//DTD HTML 4.01 Transitional//EN".equals(publicIdentifier)) { michael@0: if (!"http://www.w3.org/TR/html4/loose.dtd".equals(systemIdentifier)) { michael@0: warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C\u201D."); michael@0: } michael@0: } else { michael@0: err("Almost standards mode doctype. Expected e.g. \u201C\u201D."); michael@0: } michael@0: documentModeInternal( michael@0: DocumentMode.ALMOST_STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, html4); michael@0: } else { michael@0: if (firstCommentLocation != null) { michael@0: warn("Comments seen before doctype. Internet Explorer will go into the quirks mode.", michael@0: firstCommentLocation); michael@0: } michael@0: if ("-//W3C//DTD HTML 4.01//EN".equals(publicIdentifier)) { michael@0: if (!"http://www.w3.org/TR/html4/strict.dtd".equals(systemIdentifier)) { michael@0: warn("The doctype did not contain the system identifier prescribed by the HTML 4.01 specification. Expected \u201C\u201D."); michael@0: } michael@0: } else if ("-//W3C//DTD XHTML 1.0 Strict//EN".equals(publicIdentifier)) { michael@0: if (!"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".equals(systemIdentifier)) { michael@0: warn("The doctype did not contain the system identifier prescribed by the XHTML 1.0 specification. Expected \u201C\u201D."); michael@0: } michael@0: } else if ("//W3C//DTD XHTML 1.1//EN".equals(publicIdentifier)) { michael@0: if (!"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd".equals(systemIdentifier)) { michael@0: warn("The doctype did not contain the system identifier prescribed by the XHTML 1.1 specification. Expected \u201C\u201D."); michael@0: } michael@0: } else if (!((systemIdentifier == null || Portability.literalEqualsString( michael@0: "about:legacy-compat", systemIdentifier)) && publicIdentifier == null)) { michael@0: err("Unexpected doctype. Expected, e.g., \u201C\u201D."); michael@0: } michael@0: documentModeInternal(DocumentMode.STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, html4); michael@0: } michael@0: break; michael@0: case NO_DOCTYPE_ERRORS: michael@0: if (isQuirky(name, publicIdentifier, systemIdentifier, michael@0: forceQuirks)) { michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, michael@0: publicIdentifier, systemIdentifier, false); michael@0: } else if (isAlmostStandards(publicIdentifier, michael@0: systemIdentifier)) { michael@0: documentModeInternal( michael@0: DocumentMode.ALMOST_STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, false); michael@0: } else { michael@0: documentModeInternal(DocumentMode.STANDARDS_MODE, michael@0: publicIdentifier, systemIdentifier, false); michael@0: } michael@0: break; michael@0: } michael@0: // ]NOCPP] michael@0: michael@0: /* michael@0: * michael@0: * Then, switch to the root element mode of the tree construction michael@0: * stage. michael@0: */ michael@0: mode = BEFORE_HTML; michael@0: return; michael@0: } michael@0: /* michael@0: * A DOCTYPE token Parse error. michael@0: */ michael@0: errStrayDoctype(); michael@0: /* michael@0: * Ignore the token. michael@0: */ michael@0: return; michael@0: } michael@0: michael@0: // [NOCPP[ michael@0: michael@0: private boolean isHtml4Doctype(String publicIdentifier) { michael@0: if (publicIdentifier != null michael@0: && (Arrays.binarySearch(TreeBuilder.HTML4_PUBLIC_IDS, michael@0: publicIdentifier) > -1)) { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // ]NOCPP] michael@0: michael@0: public final void comment(@NoLength char[] buf, int start, int length) michael@0: throws SAXException { michael@0: needToDropLF = false; michael@0: // [NOCPP[ michael@0: if (firstCommentLocation == null) { michael@0: firstCommentLocation = new LocatorImpl(tokenizer); michael@0: } michael@0: if (!wantingComments) { michael@0: return; michael@0: } michael@0: // ]NOCPP] michael@0: if (!isInForeign()) { michael@0: switch (mode) { michael@0: case INITIAL: michael@0: case BEFORE_HTML: michael@0: case AFTER_AFTER_BODY: michael@0: case AFTER_AFTER_FRAMESET: michael@0: /* michael@0: * A comment token Append a Comment node to the Document michael@0: * object with the data attribute set to the data given in michael@0: * the comment token. michael@0: */ michael@0: appendCommentToDocument(buf, start, length); michael@0: return; michael@0: case AFTER_BODY: michael@0: /* michael@0: * A comment token Append a Comment node to the first michael@0: * element in the stack of open elements (the html element), michael@0: * with the data attribute set to the data given in the michael@0: * comment token. michael@0: */ michael@0: flushCharacters(); michael@0: appendComment(stack[0].node, buf, start, length); michael@0: return; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: /* michael@0: * A comment token Append a Comment node to the current node with the michael@0: * data attribute set to the data given in the comment token. michael@0: */ michael@0: flushCharacters(); michael@0: appendComment(stack[currentPtr].node, buf, start, length); michael@0: return; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.common.TokenHandler#characters(char[], int, michael@0: * int) michael@0: */ michael@0: public final void characters(@Const @NoLength char[] buf, int start, int length) michael@0: throws SAXException { michael@0: // Note: Can't attach error messages to EOF in C++ yet michael@0: michael@0: // CPPONLY: if (tokenizer.isViewingXmlSource()) { michael@0: // CPPONLY: return; michael@0: // CPPONLY: } michael@0: if (needToDropLF) { michael@0: needToDropLF = false; michael@0: if (buf[start] == '\n') { michael@0: start++; michael@0: length--; michael@0: if (length == 0) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // optimize the most common case michael@0: switch (mode) { michael@0: case IN_BODY: michael@0: case IN_CELL: michael@0: case IN_CAPTION: michael@0: if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) { michael@0: reconstructTheActiveFormattingElements(); michael@0: } michael@0: // fall through michael@0: case TEXT: michael@0: accumulateCharacters(buf, start, length); michael@0: return; michael@0: case IN_TABLE: michael@0: case IN_TABLE_BODY: michael@0: case IN_ROW: michael@0: accumulateCharactersForced(buf, start, length); michael@0: return; michael@0: default: michael@0: int end = start + length; michael@0: charactersloop: for (int i = start; i < end; i++) { michael@0: switch (buf[i]) { michael@0: case ' ': michael@0: case '\t': michael@0: case '\n': michael@0: case '\r': michael@0: case '\u000C': michael@0: /* michael@0: * A character token that is one of one of U+0009 michael@0: * CHARACTER TABULATION, U+000A LINE FEED (LF), michael@0: * U+000C FORM FEED (FF), or U+0020 SPACE michael@0: */ michael@0: switch (mode) { michael@0: case INITIAL: michael@0: case BEFORE_HTML: michael@0: case BEFORE_HEAD: michael@0: /* michael@0: * Ignore the token. michael@0: */ michael@0: start = i + 1; michael@0: continue; michael@0: case IN_HEAD: michael@0: case IN_HEAD_NOSCRIPT: michael@0: case AFTER_HEAD: michael@0: case IN_COLUMN_GROUP: michael@0: case IN_FRAMESET: michael@0: case AFTER_FRAMESET: michael@0: /* michael@0: * Append the character to the current node. michael@0: */ michael@0: continue; michael@0: case FRAMESET_OK: michael@0: case IN_TEMPLATE: michael@0: case IN_BODY: michael@0: case IN_CELL: michael@0: case IN_CAPTION: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: michael@0: /* michael@0: * Reconstruct the active formatting michael@0: * elements, if any. michael@0: */ michael@0: if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) { michael@0: flushCharacters(); michael@0: reconstructTheActiveFormattingElements(); michael@0: } michael@0: /* michael@0: * Append the token's character to the michael@0: * current node. michael@0: */ michael@0: break charactersloop; michael@0: case IN_SELECT: michael@0: case IN_SELECT_IN_TABLE: michael@0: break charactersloop; michael@0: case IN_TABLE: michael@0: case IN_TABLE_BODY: michael@0: case IN_ROW: michael@0: accumulateCharactersForced(buf, i, 1); michael@0: start = i + 1; michael@0: continue; michael@0: case AFTER_BODY: michael@0: case AFTER_AFTER_BODY: michael@0: case AFTER_AFTER_FRAMESET: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Reconstruct the active formatting michael@0: * elements, if any. michael@0: */ michael@0: flushCharacters(); michael@0: reconstructTheActiveFormattingElements(); michael@0: /* michael@0: * Append the token's character to the michael@0: * current node. michael@0: */ michael@0: continue; michael@0: } michael@0: default: michael@0: /* michael@0: * A character token that is not one of one of michael@0: * U+0009 CHARACTER TABULATION, U+000A LINE FEED michael@0: * (LF), U+000C FORM FEED (FF), or U+0020 SPACE michael@0: */ michael@0: switch (mode) { michael@0: case INITIAL: michael@0: /* michael@0: * Parse error. michael@0: */ michael@0: // [NOCPP[ michael@0: switch (doctypeExpectation) { michael@0: case AUTO: michael@0: err("Non-space characters found without seeing a doctype first. Expected e.g. \u201C\u201D."); michael@0: break; michael@0: case HTML: michael@0: // XXX figure out a way to report this in the Gecko View Source case michael@0: err("Non-space characters found without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case HTML401_STRICT: michael@0: err("Non-space characters found without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case HTML401_TRANSITIONAL: michael@0: err("Non-space characters found without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case NO_DOCTYPE_ERRORS: michael@0: } michael@0: // ]NOCPP] michael@0: /* michael@0: * michael@0: * Set the document to quirks mode. michael@0: */ michael@0: documentModeInternal( michael@0: DocumentMode.QUIRKS_MODE, null, michael@0: null, false); michael@0: /* michael@0: * Then, switch to the root element mode of michael@0: * the tree construction stage michael@0: */ michael@0: mode = BEFORE_HTML; michael@0: /* michael@0: * and reprocess the current token. michael@0: */ michael@0: i--; michael@0: continue; michael@0: case BEFORE_HTML: michael@0: /* michael@0: * Create an HTMLElement node with the tag michael@0: * name html, in the HTML namespace. Append michael@0: * it to the Document object. michael@0: */ michael@0: // No need to flush characters here, michael@0: // because there's nothing to flush. michael@0: appendHtmlElementToDocumentAndPush(); michael@0: /* Switch to the main mode */ michael@0: mode = BEFORE_HEAD; michael@0: /* michael@0: * reprocess the current token. michael@0: */ michael@0: i--; michael@0: continue; michael@0: case BEFORE_HEAD: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * /Act as if a start tag token with the tag michael@0: * name "head" and no attributes had been michael@0: * seen, michael@0: */ michael@0: flushCharacters(); michael@0: appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: mode = IN_HEAD; michael@0: /* michael@0: * then reprocess the current token. michael@0: * michael@0: * This will result in an empty head element michael@0: * being generated, with the current token michael@0: * being reprocessed in the "after head" michael@0: * insertion mode. michael@0: */ michael@0: i--; michael@0: continue; michael@0: case IN_HEAD: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Act as if an end tag token with the tag michael@0: * name "head" had been seen, michael@0: */ michael@0: flushCharacters(); michael@0: pop(); michael@0: mode = AFTER_HEAD; michael@0: /* michael@0: * and reprocess the current token. michael@0: */ michael@0: i--; michael@0: continue; michael@0: case IN_HEAD_NOSCRIPT: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Parse error. Act as if an end tag with michael@0: * the tag name "noscript" had been seen michael@0: */ michael@0: errNonSpaceInNoscriptInHead(); michael@0: flushCharacters(); michael@0: pop(); michael@0: mode = IN_HEAD; michael@0: /* michael@0: * and reprocess the current token. michael@0: */ michael@0: i--; michael@0: continue; michael@0: case AFTER_HEAD: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Act as if a start tag token with the tag michael@0: * name "body" and no attributes had been michael@0: * seen, michael@0: */ michael@0: flushCharacters(); michael@0: appendToCurrentNodeAndPushBodyElement(); michael@0: mode = FRAMESET_OK; michael@0: /* michael@0: * and then reprocess the current token. michael@0: */ michael@0: i--; michael@0: continue; michael@0: case FRAMESET_OK: michael@0: framesetOk = false; michael@0: mode = IN_BODY; michael@0: i--; michael@0: continue; michael@0: case IN_TEMPLATE: michael@0: case IN_BODY: michael@0: case IN_CELL: michael@0: case IN_CAPTION: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Reconstruct the active formatting michael@0: * elements, if any. michael@0: */ michael@0: if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) { michael@0: flushCharacters(); michael@0: reconstructTheActiveFormattingElements(); michael@0: } michael@0: /* michael@0: * Append the token's character to the michael@0: * current node. michael@0: */ michael@0: break charactersloop; michael@0: case IN_TABLE: michael@0: case IN_TABLE_BODY: michael@0: case IN_ROW: michael@0: accumulateCharactersForced(buf, i, 1); michael@0: start = i + 1; michael@0: continue; michael@0: case IN_COLUMN_GROUP: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Act as if an end tag with the tag name michael@0: * "colgroup" had been seen, and then, if michael@0: * that token wasn't ignored, reprocess the michael@0: * current token. michael@0: */ michael@0: if (currentPtr == 0 || stack[currentPtr].getGroup() == michael@0: TreeBuilder.TEMPLATE) { michael@0: errNonSpaceInColgroupInFragment(); michael@0: start = i + 1; michael@0: continue; michael@0: } michael@0: flushCharacters(); michael@0: pop(); michael@0: mode = IN_TABLE; michael@0: i--; michael@0: continue; michael@0: case IN_SELECT: michael@0: case IN_SELECT_IN_TABLE: michael@0: break charactersloop; michael@0: case AFTER_BODY: michael@0: errNonSpaceAfterBody(); michael@0: fatal(); michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: i--; michael@0: continue; michael@0: case IN_FRAMESET: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Parse error. michael@0: */ michael@0: errNonSpaceInFrameset(); michael@0: /* michael@0: * Ignore the token. michael@0: */ michael@0: start = i + 1; michael@0: continue; michael@0: case AFTER_FRAMESET: michael@0: if (start < i) { michael@0: accumulateCharacters(buf, start, i michael@0: - start); michael@0: start = i; michael@0: } michael@0: /* michael@0: * Parse error. michael@0: */ michael@0: errNonSpaceAfterFrameset(); michael@0: /* michael@0: * Ignore the token. michael@0: */ michael@0: start = i + 1; michael@0: continue; michael@0: case AFTER_AFTER_BODY: michael@0: /* michael@0: * Parse error. michael@0: */ michael@0: errNonSpaceInTrailer(); michael@0: /* michael@0: * Switch back to the main mode and michael@0: * reprocess the token. michael@0: */ michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: i--; michael@0: continue; michael@0: case AFTER_AFTER_FRAMESET: michael@0: errNonSpaceInTrailer(); michael@0: /* michael@0: * Switch back to the main mode and michael@0: * reprocess the token. michael@0: */ michael@0: mode = IN_FRAMESET; michael@0: i--; michael@0: continue; michael@0: } michael@0: } michael@0: } michael@0: if (start < end) { michael@0: accumulateCharacters(buf, start, end - start); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.common.TokenHandler#zeroOriginatingReplacementCharacter() michael@0: */ michael@0: public void zeroOriginatingReplacementCharacter() throws SAXException { michael@0: if (mode == TEXT) { michael@0: accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1); michael@0: return; michael@0: } michael@0: if (currentPtr >= 0) { michael@0: if (isSpecialParentInForeign(stack[currentPtr])) { michael@0: return; michael@0: } michael@0: accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1); michael@0: } michael@0: } michael@0: michael@0: public final void eof() throws SAXException { michael@0: flushCharacters(); michael@0: // Note: Can't attach error messages to EOF in C++ yet michael@0: eofloop: for (;;) { michael@0: switch (mode) { michael@0: case INITIAL: michael@0: /* michael@0: * Parse error. michael@0: */ michael@0: // [NOCPP[ michael@0: switch (doctypeExpectation) { michael@0: case AUTO: michael@0: err("End of file seen without seeing a doctype first. Expected e.g. \u201C\u201D."); michael@0: break; michael@0: case HTML: michael@0: err("End of file seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case HTML401_STRICT: michael@0: err("End of file seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case HTML401_TRANSITIONAL: michael@0: err("End of file seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case NO_DOCTYPE_ERRORS: michael@0: } michael@0: // ]NOCPP] michael@0: /* michael@0: * michael@0: * Set the document to quirks mode. michael@0: */ michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, null, null, michael@0: false); michael@0: /* michael@0: * Then, switch to the root element mode of the tree michael@0: * construction stage michael@0: */ michael@0: mode = BEFORE_HTML; michael@0: /* michael@0: * and reprocess the current token. michael@0: */ michael@0: continue; michael@0: case BEFORE_HTML: michael@0: /* michael@0: * Create an HTMLElement node with the tag name html, in the michael@0: * HTML namespace. Append it to the Document object. michael@0: */ michael@0: appendHtmlElementToDocumentAndPush(); michael@0: // XXX application cache manifest michael@0: /* Switch to the main mode */ michael@0: mode = BEFORE_HEAD; michael@0: /* michael@0: * reprocess the current token. michael@0: */ michael@0: continue; michael@0: case BEFORE_HEAD: michael@0: appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: mode = IN_HEAD; michael@0: continue; michael@0: case IN_HEAD: michael@0: // [NOCPP[ michael@0: if (errorHandler != null && currentPtr > 1) { michael@0: errEofWithUnclosedElements(); michael@0: } michael@0: // ]NOCPP] michael@0: while (currentPtr > 0) { michael@0: popOnEof(); michael@0: } michael@0: mode = AFTER_HEAD; michael@0: continue; michael@0: case IN_HEAD_NOSCRIPT: michael@0: // [NOCPP[ michael@0: errEofWithUnclosedElements(); michael@0: // ]NOCPP] michael@0: while (currentPtr > 1) { michael@0: popOnEof(); michael@0: } michael@0: mode = IN_HEAD; michael@0: continue; michael@0: case AFTER_HEAD: michael@0: appendToCurrentNodeAndPushBodyElement(); michael@0: mode = IN_BODY; michael@0: continue; michael@0: case IN_TABLE_BODY: michael@0: case IN_ROW: michael@0: case IN_TABLE: michael@0: case IN_SELECT_IN_TABLE: michael@0: case IN_SELECT: michael@0: case IN_COLUMN_GROUP: michael@0: case FRAMESET_OK: michael@0: case IN_CAPTION: michael@0: case IN_CELL: michael@0: case IN_BODY: michael@0: // [NOCPP[ michael@0: openelementloop: for (int i = currentPtr; i >= 0; i--) { michael@0: int group = stack[i].getGroup(); michael@0: switch (group) { michael@0: case DD_OR_DT: michael@0: case LI: michael@0: case P: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TD_OR_TH: michael@0: case BODY: michael@0: case HTML: michael@0: break; michael@0: default: michael@0: errEofWithUnclosedElements(); michael@0: break openelementloop; michael@0: } michael@0: } michael@0: // ]NOCPP] michael@0: michael@0: if (isTemplateModeStackEmpty()) { michael@0: break eofloop; michael@0: } michael@0: michael@0: // fall through to IN_TEMPLATE michael@0: case IN_TEMPLATE: michael@0: int eltPos = findLast("template"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment; michael@0: break eofloop; michael@0: } michael@0: if (errorHandler != null) { michael@0: errUnclosedElements(eltPos, "template"); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: popTemplateMode(); michael@0: resetTheInsertionMode(); michael@0: michael@0: // Reprocess token. michael@0: continue; michael@0: case TEXT: michael@0: // [NOCPP[ michael@0: if (errorHandler != null) { michael@0: errNoCheck("End of file seen when expecting text or an end tag."); michael@0: errListUnclosedStartTags(0); michael@0: } michael@0: // ]NOCPP] michael@0: // XXX mark script as already executed michael@0: if (originalMode == AFTER_HEAD) { michael@0: popOnEof(); michael@0: } michael@0: popOnEof(); michael@0: mode = originalMode; michael@0: continue; michael@0: case IN_FRAMESET: michael@0: // [NOCPP[ michael@0: if (errorHandler != null && currentPtr > 0) { michael@0: errEofWithUnclosedElements(); michael@0: } michael@0: // ]NOCPP] michael@0: break eofloop; michael@0: case AFTER_BODY: michael@0: case AFTER_FRAMESET: michael@0: case AFTER_AFTER_BODY: michael@0: case AFTER_AFTER_FRAMESET: michael@0: default: michael@0: // [NOCPP[ michael@0: if (currentPtr == 0) { // This silliness is here to poison michael@0: // buggy compiler optimizations in michael@0: // GWT michael@0: System.currentTimeMillis(); michael@0: } michael@0: // ]NOCPP] michael@0: break eofloop; michael@0: } michael@0: } michael@0: while (currentPtr > 0) { michael@0: popOnEof(); michael@0: } michael@0: if (!fragment) { michael@0: popOnEof(); michael@0: } michael@0: /* Stop parsing. */ michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.common.TokenHandler#endTokenization() michael@0: */ michael@0: public final void endTokenization() throws SAXException { michael@0: formPointer = null; michael@0: headPointer = null; michael@0: deepTreeSurrogateParent = null; michael@0: templateModeStack = null; michael@0: if (stack != null) { michael@0: while (currentPtr > -1) { michael@0: stack[currentPtr].release(); michael@0: currentPtr--; michael@0: } michael@0: stack = null; michael@0: } michael@0: if (listOfActiveFormattingElements != null) { michael@0: while (listPtr > -1) { michael@0: if (listOfActiveFormattingElements[listPtr] != null) { michael@0: listOfActiveFormattingElements[listPtr].release(); michael@0: } michael@0: listPtr--; michael@0: } michael@0: listOfActiveFormattingElements = null; michael@0: } michael@0: // [NOCPP[ michael@0: idLocations.clear(); michael@0: // ]NOCPP] michael@0: charBuffer = null; michael@0: end(); michael@0: } michael@0: michael@0: public final void startTag(ElementName elementName, michael@0: HtmlAttributes attributes, boolean selfClosing) throws SAXException { michael@0: flushCharacters(); michael@0: michael@0: // [NOCPP[ michael@0: if (errorHandler != null) { michael@0: // ID uniqueness michael@0: @IdType String id = attributes.getId(); michael@0: if (id != null) { michael@0: LocatorImpl oldLoc = idLocations.get(id); michael@0: if (oldLoc != null) { michael@0: err("Duplicate ID \u201C" + id + "\u201D."); michael@0: errorHandler.warning(new SAXParseException( michael@0: "The first occurrence of ID \u201C" + id michael@0: + "\u201D was here.", oldLoc)); michael@0: } else { michael@0: idLocations.put(id, new LocatorImpl(tokenizer)); michael@0: } michael@0: } michael@0: } michael@0: // ]NOCPP] michael@0: michael@0: int eltPos; michael@0: needToDropLF = false; michael@0: starttagloop: for (;;) { michael@0: int group = elementName.getGroup(); michael@0: @Local String name = elementName.name; michael@0: if (isInForeign()) { michael@0: StackNode currentNode = stack[currentPtr]; michael@0: @NsUri String currNs = currentNode.ns; michael@0: 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))))) { michael@0: switch (group) { michael@0: case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U: michael@0: case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU: michael@0: case BODY: michael@0: case BR: michael@0: case RUBY_OR_SPAN_OR_SUB_OR_SUP_OR_VAR: michael@0: case DD_OR_DT: michael@0: case UL_OR_OL_OR_DL: michael@0: case EMBED: michael@0: case IMG: michael@0: case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6: michael@0: case HEAD: michael@0: case HR: michael@0: case LI: michael@0: case META: michael@0: case NOBR: michael@0: case P: michael@0: case PRE_OR_LISTING: michael@0: case TABLE: michael@0: errHtmlStartTagInForeignContext(name); michael@0: while (!isSpecialParentInForeign(stack[currentPtr])) { michael@0: pop(); michael@0: } michael@0: continue starttagloop; michael@0: case FONT: michael@0: if (attributes.contains(AttributeName.COLOR) michael@0: || attributes.contains(AttributeName.FACE) michael@0: || attributes.contains(AttributeName.SIZE)) { michael@0: errHtmlStartTagInForeignContext(name); michael@0: while (!isSpecialParentInForeign(stack[currentPtr])) { michael@0: pop(); michael@0: } michael@0: continue starttagloop; michael@0: } michael@0: // else fall thru michael@0: default: michael@0: if ("http://www.w3.org/2000/svg" == currNs) { michael@0: attributes.adjustForSvg(); michael@0: if (selfClosing) { michael@0: appendVoidElementToCurrentMayFosterSVG( michael@0: elementName, attributes); michael@0: selfClosing = false; michael@0: } else { michael@0: appendToCurrentNodeAndPushElementMayFosterSVG( michael@0: elementName, attributes); michael@0: } michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } else { michael@0: attributes.adjustForMath(); michael@0: if (selfClosing) { michael@0: appendVoidElementToCurrentMayFosterMathML( michael@0: elementName, attributes); michael@0: selfClosing = false; michael@0: } else { michael@0: appendToCurrentNodeAndPushElementMayFosterMathML( michael@0: elementName, attributes); michael@0: } michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } michael@0: } // switch michael@0: } // foreignObject / annotation-xml michael@0: } michael@0: switch (mode) { michael@0: case IN_TEMPLATE: michael@0: switch (group) { michael@0: case COL: michael@0: popTemplateMode(); michael@0: pushTemplateMode(IN_COLUMN_GROUP); michael@0: mode = IN_COLUMN_GROUP; michael@0: // Reprocess token. michael@0: continue; michael@0: case CAPTION: michael@0: case COLGROUP: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: popTemplateMode(); michael@0: pushTemplateMode(IN_TABLE); michael@0: mode = IN_TABLE; michael@0: // Reprocess token. michael@0: continue; michael@0: case TR: michael@0: popTemplateMode(); michael@0: pushTemplateMode(IN_TABLE_BODY); michael@0: mode = IN_TABLE_BODY; michael@0: // Reprocess token. michael@0: continue; michael@0: case TD_OR_TH: michael@0: popTemplateMode(); michael@0: pushTemplateMode(IN_ROW); michael@0: mode = IN_ROW; michael@0: // Reprocess token. michael@0: continue; michael@0: case META: michael@0: checkMetaCharset(attributes); michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TITLE: michael@0: startTagTitleInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case BASE: michael@0: case LINK_OR_BASEFONT_OR_BGSOUND: michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case SCRIPT: michael@0: startTagScriptInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case NOFRAMES: michael@0: case STYLE: michael@0: startTagGenericRawText(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TEMPLATE: michael@0: startTagTemplateInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: popTemplateMode(); michael@0: pushTemplateMode(IN_BODY); michael@0: mode = IN_BODY; michael@0: // Reprocess token. michael@0: continue; michael@0: } michael@0: case IN_ROW: michael@0: switch (group) { michael@0: case TD_OR_TH: michael@0: clearStackBackTo(findLastOrRoot(TreeBuilder.TR)); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_CELL; michael@0: insertMarker(); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TR: michael@0: eltPos = findLastOrRoot(TreeBuilder.TR); michael@0: if (eltPos == 0) { michael@0: assert fragment || isTemplateContents(); michael@0: errNoTableRowToClose(); michael@0: break starttagloop; michael@0: } michael@0: clearStackBackTo(eltPos); michael@0: pop(); michael@0: mode = IN_TABLE_BODY; michael@0: continue; michael@0: default: michael@0: // fall through to IN_TABLE michael@0: } michael@0: case IN_TABLE_BODY: michael@0: switch (group) { michael@0: case TR: michael@0: clearStackBackTo(findLastInTableScopeOrRootTemplateTbodyTheadTfoot()); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_ROW; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TD_OR_TH: michael@0: errStartTagInTableBody(name); michael@0: clearStackBackTo(findLastInTableScopeOrRootTemplateTbodyTheadTfoot()); michael@0: appendToCurrentNodeAndPushElement( michael@0: ElementName.TR, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: mode = IN_ROW; michael@0: continue; michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot(); michael@0: if (eltPos == 0 || stack[eltPos].getGroup() == TEMPLATE) { michael@0: assert fragment || isTemplateContents(); michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: } else { michael@0: clearStackBackTo(eltPos); michael@0: pop(); michael@0: mode = IN_TABLE; michael@0: continue; michael@0: } michael@0: default: michael@0: // fall through to IN_TABLE michael@0: } michael@0: case IN_TABLE: michael@0: intableloop: for (;;) { michael@0: switch (group) { michael@0: case CAPTION: michael@0: clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE)); michael@0: insertMarker(); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_CAPTION; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case COLGROUP: michael@0: clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE)); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_COLUMN_GROUP; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case COL: michael@0: clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE)); michael@0: appendToCurrentNodeAndPushElement( michael@0: ElementName.COLGROUP, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: mode = IN_COLUMN_GROUP; michael@0: continue starttagloop; michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE)); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_TABLE_BODY; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TR: michael@0: case TD_OR_TH: michael@0: clearStackBackTo(findLastOrRoot(TreeBuilder.TABLE)); michael@0: appendToCurrentNodeAndPushElement( michael@0: ElementName.TBODY, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: mode = IN_TABLE_BODY; michael@0: continue starttagloop; michael@0: case TEMPLATE: michael@0: // fall through to IN_HEAD michael@0: break intableloop; michael@0: case TABLE: michael@0: errTableSeenWhileTableOpen(); michael@0: eltPos = findLastInTableScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment || isTemplateContents(); michael@0: break starttagloop; michael@0: } michael@0: generateImpliedEndTags(); michael@0: // XXX is the next if dead code? michael@0: if (errorHandler != null && !isCurrent("table")) { michael@0: errNoCheckUnclosedElementsOnStack(); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: resetTheInsertionMode(); michael@0: continue starttagloop; michael@0: case SCRIPT: michael@0: // XXX need to manage much more stuff michael@0: // here if michael@0: // supporting michael@0: // document.write() michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.SCRIPT_DATA, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case STYLE: michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RAWTEXT, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case INPUT: michael@0: errStartTagInTable(name); michael@0: if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "hidden", michael@0: attributes.getValue(AttributeName.TYPE))) { michael@0: break intableloop; michael@0: } michael@0: appendVoidElementToCurrent( michael@0: name, attributes, michael@0: formPointer); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case FORM: michael@0: if (formPointer != null || isTemplateContents()) { michael@0: errFormWhenFormOpen(); michael@0: break starttagloop; michael@0: } else { michael@0: errStartTagInTable(name); michael@0: appendVoidFormToCurrent(attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } michael@0: default: michael@0: errStartTagInTable(name); michael@0: // fall through to IN_BODY michael@0: break intableloop; michael@0: } michael@0: } michael@0: case IN_CAPTION: michael@0: switch (group) { michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TR: michael@0: case TD_OR_TH: michael@0: errStrayStartTag(name); michael@0: eltPos = findLastInTableScope("caption"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: break starttagloop; michael@0: } michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && currentPtr != eltPos) { michael@0: errNoCheckUnclosedElementsOnStack(); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: mode = IN_TABLE; michael@0: continue; michael@0: default: michael@0: // fall through to IN_BODY michael@0: } michael@0: case IN_CELL: michael@0: switch (group) { michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TR: michael@0: case TD_OR_TH: michael@0: eltPos = findLastInTableScopeTdTh(); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errNoCellToClose(); michael@0: break starttagloop; michael@0: } else { michael@0: closeTheCell(eltPos); michael@0: continue; michael@0: } michael@0: default: michael@0: // fall through to IN_BODY michael@0: } michael@0: case FRAMESET_OK: michael@0: switch (group) { michael@0: case FRAMESET: michael@0: if (mode == FRAMESET_OK) { michael@0: if (currentPtr == 0 || stack[1].getGroup() != BODY) { michael@0: assert fragment || isTemplateContents(); michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: } else { michael@0: errFramesetStart(); michael@0: detachFromParent(stack[1].node); michael@0: while (currentPtr > 0) { michael@0: pop(); michael@0: } michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_FRAMESET; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } michael@0: } else { michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: } michael@0: // NOT falling through! michael@0: case PRE_OR_LISTING: michael@0: case LI: michael@0: case DD_OR_DT: michael@0: case BUTTON: michael@0: case MARQUEE_OR_APPLET: michael@0: case OBJECT: michael@0: case TABLE: michael@0: case AREA_OR_WBR: michael@0: case BR: michael@0: case EMBED: michael@0: case IMG: michael@0: case INPUT: michael@0: case KEYGEN: michael@0: case HR: michael@0: case TEXTAREA: michael@0: case XMP: michael@0: case IFRAME: michael@0: case SELECT: michael@0: if (mode == FRAMESET_OK michael@0: && !(group == INPUT && Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "hidden", michael@0: attributes.getValue(AttributeName.TYPE)))) { michael@0: framesetOk = false; michael@0: mode = IN_BODY; michael@0: } michael@0: // fall through to IN_BODY michael@0: default: michael@0: // fall through to IN_BODY michael@0: } michael@0: case IN_BODY: michael@0: inbodyloop: for (;;) { michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case BASE: michael@0: case LINK_OR_BASEFONT_OR_BGSOUND: michael@0: case META: michael@0: case STYLE: michael@0: case SCRIPT: michael@0: case TITLE: michael@0: case TEMPLATE: michael@0: // Fall through to IN_HEAD michael@0: break inbodyloop; michael@0: case BODY: michael@0: if (currentPtr == 0 || stack[1].getGroup() != BODY || isTemplateContents()) { michael@0: assert fragment || isTemplateContents(); michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: } michael@0: errFooSeenWhenFooOpen(name); michael@0: framesetOk = false; michael@0: if (mode == FRAMESET_OK) { michael@0: mode = IN_BODY; michael@0: } michael@0: if (addAttributesToBody(attributes)) { michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case P: michael@0: case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU: michael@0: case UL_OR_OL_OR_DL: michael@0: case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY: michael@0: implicitlyCloseP(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6: michael@0: implicitlyCloseP(); michael@0: if (stack[currentPtr].getGroup() == H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) { michael@0: errHeadingWhenHeadingOpen(); michael@0: pop(); michael@0: } michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case FIELDSET: michael@0: implicitlyCloseP(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes, formPointer); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case PRE_OR_LISTING: michael@0: implicitlyCloseP(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: needToDropLF = true; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case FORM: michael@0: if (formPointer != null && !isTemplateContents()) { michael@0: errFormWhenFormOpen(); michael@0: break starttagloop; michael@0: } else { michael@0: implicitlyCloseP(); michael@0: appendToCurrentNodeAndPushFormElementMayFoster(attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } michael@0: case LI: michael@0: case DD_OR_DT: michael@0: eltPos = currentPtr; michael@0: for (;;) { michael@0: StackNode node = stack[eltPos]; // weak michael@0: // ref michael@0: if (node.getGroup() == group) { // LI or michael@0: // DD_OR_DT michael@0: generateImpliedEndTagsExceptFor(node.name); michael@0: if (errorHandler != null michael@0: && eltPos != currentPtr) { michael@0: errUnclosedElementsImplied(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: break; michael@0: } else if (node.isSpecial() michael@0: && (node.ns != "http://www.w3.org/1999/xhtml" michael@0: || (node.name != "p" michael@0: && node.name != "address" michael@0: && node.name != "div"))) { michael@0: break; michael@0: } michael@0: eltPos--; michael@0: } michael@0: implicitlyCloseP(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case PLAINTEXT: michael@0: implicitlyCloseP(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.PLAINTEXT, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case A: michael@0: int activeAPos = findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker("a"); michael@0: if (activeAPos != -1) { michael@0: errFooSeenWhenFooOpen(name); michael@0: StackNode activeA = listOfActiveFormattingElements[activeAPos]; michael@0: activeA.retain(); michael@0: adoptionAgencyEndTag("a"); michael@0: removeFromStack(activeA); michael@0: activeAPos = findInListOfActiveFormattingElements(activeA); michael@0: if (activeAPos != -1) { michael@0: removeFromListOfActiveFormattingElements(activeAPos); michael@0: } michael@0: activeA.release(); michael@0: } michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushFormattingElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U: michael@0: case FONT: michael@0: reconstructTheActiveFormattingElements(); michael@0: maybeForgetEarlierDuplicateFormattingElement(elementName.name, attributes); michael@0: appendToCurrentNodeAndPushFormattingElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case NOBR: michael@0: reconstructTheActiveFormattingElements(); michael@0: if (TreeBuilder.NOT_FOUND_ON_STACK != findLastInScope("nobr")) { michael@0: errFooSeenWhenFooOpen(name); michael@0: adoptionAgencyEndTag("nobr"); michael@0: reconstructTheActiveFormattingElements(); michael@0: } michael@0: appendToCurrentNodeAndPushFormattingElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case BUTTON: michael@0: eltPos = findLastInScope(name); michael@0: if (eltPos != TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errFooSeenWhenFooOpen(name); michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null michael@0: && !isCurrent(name)) { michael@0: errUnclosedElementsImplied(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: continue starttagloop; michael@0: } else { michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes, formPointer); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } michael@0: case OBJECT: michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes, formPointer); michael@0: insertMarker(); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case MARQUEE_OR_APPLET: michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: insertMarker(); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TABLE: michael@0: // The only quirk. Blame Hixie and michael@0: // Acid2. michael@0: if (!quirks) { michael@0: implicitlyCloseP(); michael@0: } michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_TABLE; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case BR: michael@0: case EMBED: michael@0: case AREA_OR_WBR: michael@0: reconstructTheActiveFormattingElements(); michael@0: // FALL THROUGH to PARAM_OR_SOURCE_OR_TRACK michael@0: // CPPONLY: case MENUITEM: michael@0: case PARAM_OR_SOURCE_OR_TRACK: michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case HR: michael@0: implicitlyCloseP(); michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case IMAGE: michael@0: errImage(); michael@0: elementName = ElementName.IMG; michael@0: continue starttagloop; michael@0: case IMG: michael@0: case KEYGEN: michael@0: case INPUT: michael@0: reconstructTheActiveFormattingElements(); michael@0: appendVoidElementToCurrentMayFoster( michael@0: name, attributes, michael@0: formPointer); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case ISINDEX: michael@0: errIsindex(); michael@0: if (formPointer != null && !isTemplateContents()) { michael@0: break starttagloop; michael@0: } michael@0: implicitlyCloseP(); michael@0: HtmlAttributes formAttrs = new HtmlAttributes(0); michael@0: int actionIndex = attributes.getIndex(AttributeName.ACTION); michael@0: if (actionIndex > -1) { michael@0: formAttrs.addAttribute( michael@0: AttributeName.ACTION, michael@0: attributes.getValueNoBoundsCheck(actionIndex) michael@0: // [NOCPP[ michael@0: , XmlViolationPolicy.ALLOW michael@0: // ]NOCPP] michael@0: ); michael@0: } michael@0: appendToCurrentNodeAndPushFormElementMayFoster(formAttrs); michael@0: appendVoidElementToCurrentMayFoster( michael@0: ElementName.HR, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: ElementName.LABEL, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: int promptIndex = attributes.getIndex(AttributeName.PROMPT); michael@0: if (promptIndex > -1) { michael@0: @Auto char[] prompt = Portability.newCharArrayFromString(attributes.getValueNoBoundsCheck(promptIndex)); michael@0: appendCharacters(stack[currentPtr].node, michael@0: prompt, 0, prompt.length); michael@0: } else { michael@0: appendIsindexPrompt(stack[currentPtr].node); michael@0: } michael@0: HtmlAttributes inputAttributes = new HtmlAttributes( michael@0: 0); michael@0: inputAttributes.addAttribute( michael@0: AttributeName.NAME, michael@0: Portability.newStringFromLiteral("isindex") michael@0: // [NOCPP[ michael@0: , XmlViolationPolicy.ALLOW michael@0: // ]NOCPP] michael@0: ); michael@0: for (int i = 0; i < attributes.getLength(); i++) { michael@0: AttributeName attributeQName = attributes.getAttributeNameNoBoundsCheck(i); michael@0: if (AttributeName.NAME == attributeQName michael@0: || AttributeName.PROMPT == attributeQName) { michael@0: attributes.releaseValue(i); michael@0: } else if (AttributeName.ACTION != attributeQName) { michael@0: inputAttributes.addAttribute( michael@0: attributeQName, michael@0: attributes.getValueNoBoundsCheck(i) michael@0: // [NOCPP[ michael@0: , XmlViolationPolicy.ALLOW michael@0: // ]NOCPP] michael@0: michael@0: ); michael@0: } michael@0: } michael@0: attributes.clearWithoutReleasingContents(); michael@0: appendVoidElementToCurrentMayFoster( michael@0: "input", michael@0: inputAttributes, formPointer); michael@0: pop(); // label michael@0: appendVoidElementToCurrentMayFoster( michael@0: ElementName.HR, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: pop(); // form michael@0: michael@0: if (!isTemplateContents()) { michael@0: formPointer = null; michael@0: } michael@0: michael@0: selfClosing = false; michael@0: // Portability.delete(formAttrs); michael@0: // Portability.delete(inputAttributes); michael@0: // Don't delete attributes, they are deleted michael@0: // later michael@0: break starttagloop; michael@0: case TEXTAREA: michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes, formPointer); michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RCDATA, elementName); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: needToDropLF = true; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case XMP: michael@0: implicitlyCloseP(); michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RAWTEXT, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case NOSCRIPT: michael@0: if (!scriptingEnabled) { michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } else { michael@0: // fall through michael@0: } michael@0: case NOFRAMES: michael@0: case IFRAME: michael@0: case NOEMBED: michael@0: startTagGenericRawText(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case SELECT: michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes, formPointer); michael@0: switch (mode) { michael@0: case IN_TABLE: michael@0: case IN_CAPTION: michael@0: case IN_COLUMN_GROUP: michael@0: case IN_TABLE_BODY: michael@0: case IN_ROW: michael@0: case IN_CELL: michael@0: mode = IN_SELECT_IN_TABLE; michael@0: break; michael@0: default: michael@0: mode = IN_SELECT; michael@0: break; michael@0: } michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case OPTGROUP: michael@0: case OPTION: michael@0: if (isCurrent("option")) { michael@0: pop(); michael@0: } michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case RT_OR_RP: michael@0: eltPos = findLastInScope("ruby"); michael@0: if (eltPos != NOT_FOUND_ON_STACK) { michael@0: generateImpliedEndTags(); michael@0: } michael@0: if (eltPos != currentPtr) { michael@0: if (eltPos != NOT_FOUND_ON_STACK) { michael@0: errStartTagSeenWithoutRuby(name); michael@0: } else { michael@0: errUnclosedChildrenInRuby(); michael@0: } michael@0: } michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case MATH: michael@0: reconstructTheActiveFormattingElements(); michael@0: attributes.adjustForMath(); michael@0: if (selfClosing) { michael@0: appendVoidElementToCurrentMayFosterMathML( michael@0: elementName, attributes); michael@0: selfClosing = false; michael@0: } else { michael@0: appendToCurrentNodeAndPushElementMayFosterMathML( michael@0: elementName, attributes); michael@0: } michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case SVG: michael@0: reconstructTheActiveFormattingElements(); michael@0: attributes.adjustForSvg(); michael@0: if (selfClosing) { michael@0: appendVoidElementToCurrentMayFosterSVG( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: } else { michael@0: appendToCurrentNodeAndPushElementMayFosterSVG( michael@0: elementName, attributes); michael@0: } michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TR: michael@0: case TD_OR_TH: michael@0: case FRAME: michael@0: case FRAMESET: michael@0: case HEAD: michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: case OUTPUT_OR_LABEL: michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes, formPointer); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: reconstructTheActiveFormattingElements(); michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: } michael@0: } michael@0: case IN_HEAD: michael@0: inheadloop: for (;;) { michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case BASE: michael@0: case LINK_OR_BASEFONT_OR_BGSOUND: michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case META: michael@0: // Fall through to IN_HEAD_NOSCRIPT michael@0: break inheadloop; michael@0: case TITLE: michael@0: startTagTitleInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case NOSCRIPT: michael@0: if (scriptingEnabled) { michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RAWTEXT, elementName); michael@0: } else { michael@0: appendToCurrentNodeAndPushElementMayFoster( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_HEAD_NOSCRIPT; michael@0: } michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case SCRIPT: michael@0: startTagScriptInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case STYLE: michael@0: case NOFRAMES: michael@0: startTagGenericRawText(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case HEAD: michael@0: /* Parse error. */ michael@0: errFooSeenWhenFooOpen(name); michael@0: /* Ignore the token. */ michael@0: break starttagloop; michael@0: case TEMPLATE: michael@0: startTagTemplateInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: pop(); michael@0: mode = AFTER_HEAD; michael@0: continue starttagloop; michael@0: } michael@0: } michael@0: case IN_HEAD_NOSCRIPT: michael@0: switch (group) { michael@0: case HTML: michael@0: // XXX did Hixie really mean to omit "base" michael@0: // here? michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case LINK_OR_BASEFONT_OR_BGSOUND: michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case META: michael@0: checkMetaCharset(attributes); michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case STYLE: michael@0: case NOFRAMES: michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RAWTEXT, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case HEAD: michael@0: errFooSeenWhenFooOpen(name); michael@0: break starttagloop; michael@0: case NOSCRIPT: michael@0: errFooSeenWhenFooOpen(name); michael@0: break starttagloop; michael@0: default: michael@0: errBadStartTagInHead(name); michael@0: pop(); michael@0: mode = IN_HEAD; michael@0: continue; michael@0: } michael@0: case IN_COLUMN_GROUP: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case COL: michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TEMPLATE: michael@0: startTagTemplateInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: if (currentPtr == 0 || stack[currentPtr].getGroup() == TEMPLATE) { michael@0: assert fragment || isTemplateContents(); michael@0: errGarbageInColgroup(); michael@0: break starttagloop; michael@0: } michael@0: pop(); michael@0: mode = IN_TABLE; michael@0: continue; michael@0: } michael@0: case IN_SELECT_IN_TABLE: michael@0: switch (group) { michael@0: case CAPTION: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TR: michael@0: case TD_OR_TH: michael@0: case TABLE: michael@0: errStartTagWithSelectOpen(name); michael@0: eltPos = findLastInTableScope("select"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment; michael@0: break starttagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375 michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: resetTheInsertionMode(); michael@0: continue; michael@0: default: michael@0: // fall through to IN_SELECT michael@0: } michael@0: case IN_SELECT: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case OPTION: michael@0: if (isCurrent("option")) { michael@0: pop(); michael@0: } michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case OPTGROUP: michael@0: if (isCurrent("option")) { michael@0: pop(); michael@0: } michael@0: if (isCurrent("optgroup")) { michael@0: pop(); michael@0: } michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case SELECT: michael@0: errStartSelectWhereEndSelectExpected(); michael@0: eltPos = findLastInTableScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment; michael@0: errNoSelectInTableScope(); michael@0: break starttagloop; michael@0: } else { michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: resetTheInsertionMode(); michael@0: break starttagloop; michael@0: } michael@0: case INPUT: michael@0: case TEXTAREA: michael@0: case KEYGEN: michael@0: errStartTagWithSelectOpen(name); michael@0: eltPos = findLastInTableScope("select"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment; michael@0: break starttagloop; michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: resetTheInsertionMode(); michael@0: continue; michael@0: case SCRIPT: michael@0: startTagScriptInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TEMPLATE: michael@0: startTagTemplateInHead(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: } michael@0: case AFTER_BODY: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: default: michael@0: errStrayStartTag(name); michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: continue; michael@0: } michael@0: case IN_FRAMESET: michael@0: switch (group) { michael@0: case FRAMESET: michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case FRAME: michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: // fall through to AFTER_FRAMESET michael@0: } michael@0: case AFTER_FRAMESET: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case NOFRAMES: michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RAWTEXT, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: } michael@0: case INITIAL: michael@0: /* michael@0: * Parse error. michael@0: */ michael@0: // [NOCPP[ michael@0: switch (doctypeExpectation) { michael@0: case AUTO: michael@0: err("Start tag seen without seeing a doctype first. Expected e.g. \u201C\u201D."); michael@0: break; michael@0: case HTML: michael@0: // ]NOCPP] michael@0: errStartTagWithoutDoctype(); michael@0: // [NOCPP[ michael@0: break; michael@0: case HTML401_STRICT: michael@0: err("Start tag seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case HTML401_TRANSITIONAL: michael@0: err("Start tag seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case NO_DOCTYPE_ERRORS: michael@0: } michael@0: // ]NOCPP] michael@0: /* michael@0: * michael@0: * Set the document to quirks mode. michael@0: */ michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, null, null, michael@0: false); michael@0: /* michael@0: * Then, switch to the root element mode of the tree michael@0: * construction stage michael@0: */ michael@0: mode = BEFORE_HTML; michael@0: /* michael@0: * and reprocess the current token. michael@0: */ michael@0: continue; michael@0: case BEFORE_HTML: michael@0: switch (group) { michael@0: case HTML: michael@0: // optimize error check and streaming SAX by michael@0: // hoisting michael@0: // "html" handling here. michael@0: if (attributes == HtmlAttributes.EMPTY_ATTRIBUTES) { michael@0: // This has the right magic side effect michael@0: // that michael@0: // it michael@0: // makes attributes in SAX Tree mutable. michael@0: appendHtmlElementToDocumentAndPush(); michael@0: } else { michael@0: appendHtmlElementToDocumentAndPush(attributes); michael@0: } michael@0: // XXX application cache should fire here michael@0: mode = BEFORE_HEAD; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: /* michael@0: * Create an HTMLElement node with the tag name michael@0: * html, in the HTML namespace. Append it to the michael@0: * Document object. michael@0: */ michael@0: appendHtmlElementToDocumentAndPush(); michael@0: /* Switch to the main mode */ michael@0: mode = BEFORE_HEAD; michael@0: /* michael@0: * reprocess the current token. michael@0: */ michael@0: continue; michael@0: } michael@0: case BEFORE_HEAD: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case HEAD: michael@0: /* michael@0: * A start tag whose tag name is "head" michael@0: * michael@0: * Create an element for the token. michael@0: * michael@0: * Set the head element pointer to this new element michael@0: * node. michael@0: * michael@0: * Append the new element to the current node and michael@0: * push it onto the stack of open elements. michael@0: */ michael@0: appendToCurrentNodeAndPushHeadElement(attributes); michael@0: /* michael@0: * Change the insertion mode to "in head". michael@0: */ michael@0: mode = IN_HEAD; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: /* michael@0: * Any other start tag token michael@0: * michael@0: * Act as if a start tag token with the tag name michael@0: * "head" and no attributes had been seen, michael@0: */ michael@0: appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: mode = IN_HEAD; michael@0: /* michael@0: * then reprocess the current token. michael@0: * michael@0: * This will result in an empty head element being michael@0: * generated, with the current token being michael@0: * reprocessed in the "after head" insertion mode. michael@0: */ michael@0: continue; michael@0: } michael@0: case AFTER_HEAD: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case BODY: michael@0: if (attributes.getLength() == 0) { michael@0: // This has the right magic side effect michael@0: // that michael@0: // it michael@0: // makes attributes in SAX Tree mutable. michael@0: appendToCurrentNodeAndPushBodyElement(); michael@0: } else { michael@0: appendToCurrentNodeAndPushBodyElement(attributes); michael@0: } michael@0: framesetOk = false; michael@0: mode = IN_BODY; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case FRAMESET: michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: mode = IN_FRAMESET; michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TEMPLATE: michael@0: errFooBetweenHeadAndBody(name); michael@0: pushHeadPointerOntoStack(); michael@0: StackNode headOnStack = stack[currentPtr]; michael@0: startTagTemplateInHead(elementName, attributes); michael@0: removeFromStack(headOnStack); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case BASE: michael@0: case LINK_OR_BASEFONT_OR_BGSOUND: michael@0: errFooBetweenHeadAndBody(name); michael@0: pushHeadPointerOntoStack(); michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: pop(); // head michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case META: michael@0: errFooBetweenHeadAndBody(name); michael@0: checkMetaCharset(attributes); michael@0: pushHeadPointerOntoStack(); michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: attributes); michael@0: selfClosing = false; michael@0: pop(); // head michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case SCRIPT: michael@0: errFooBetweenHeadAndBody(name); michael@0: pushHeadPointerOntoStack(); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.SCRIPT_DATA, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case STYLE: michael@0: case NOFRAMES: michael@0: errFooBetweenHeadAndBody(name); michael@0: pushHeadPointerOntoStack(); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RAWTEXT, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case TITLE: michael@0: errFooBetweenHeadAndBody(name); michael@0: pushHeadPointerOntoStack(); michael@0: appendToCurrentNodeAndPushElement( michael@0: elementName, michael@0: attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation( michael@0: Tokenizer.RCDATA, elementName); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: case HEAD: michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: default: michael@0: appendToCurrentNodeAndPushBodyElement(); michael@0: mode = FRAMESET_OK; michael@0: continue; michael@0: } michael@0: case AFTER_AFTER_BODY: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: default: michael@0: errStrayStartTag(name); michael@0: fatal(); michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: continue; michael@0: } michael@0: case AFTER_AFTER_FRAMESET: michael@0: switch (group) { michael@0: case HTML: michael@0: errStrayStartTag(name); michael@0: if (!fragment && !isTemplateContents()) { michael@0: addAttributesToHtml(attributes); michael@0: attributes = null; // CPP michael@0: } michael@0: break starttagloop; michael@0: case NOFRAMES: michael@0: startTagGenericRawText(elementName, attributes); michael@0: attributes = null; // CPP michael@0: break starttagloop; michael@0: default: michael@0: errStrayStartTag(name); michael@0: break starttagloop; michael@0: } michael@0: case TEXT: michael@0: assert false; michael@0: break starttagloop; // Avoid infinite loop if the assertion michael@0: // fails michael@0: } michael@0: } michael@0: if (selfClosing) { michael@0: errSelfClosing(); michael@0: } michael@0: // CPPONLY: if (mBuilder == null && attributes != HtmlAttributes.EMPTY_ATTRIBUTES) { michael@0: // CPPONLY: Portability.delete(attributes); michael@0: // CPPONLY: } michael@0: } michael@0: michael@0: private void startTagTitleInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException { michael@0: appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA, elementName); michael@0: } michael@0: michael@0: private void startTagGenericRawText(ElementName elementName, HtmlAttributes attributes) throws SAXException { michael@0: appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.RAWTEXT, elementName); michael@0: } michael@0: michael@0: private void startTagScriptInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException { michael@0: // XXX need to manage much more stuff here if supporting document.write() michael@0: appendToCurrentNodeAndPushElementMayFoster(elementName, attributes); michael@0: originalMode = mode; michael@0: mode = TEXT; michael@0: tokenizer.setStateAndEndTagExpectation(Tokenizer.SCRIPT_DATA, elementName); michael@0: } michael@0: michael@0: private void startTagTemplateInHead(ElementName elementName, HtmlAttributes attributes) throws SAXException { michael@0: appendToCurrentNodeAndPushElement(elementName, attributes); michael@0: insertMarker(); michael@0: framesetOk = false; michael@0: originalMode = mode; michael@0: mode = IN_TEMPLATE; michael@0: pushTemplateMode(IN_TEMPLATE); michael@0: } michael@0: michael@0: private boolean isTemplateContents() { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK != findLast("template"); michael@0: } michael@0: michael@0: private boolean isTemplateModeStackEmpty() { michael@0: return templateModePtr == -1; michael@0: } michael@0: michael@0: private boolean isSpecialParentInForeign(StackNode stackNode) { michael@0: @NsUri String ns = stackNode.ns; michael@0: return ("http://www.w3.org/1999/xhtml" == ns) michael@0: || (stackNode.isHtmlIntegrationPoint()) michael@0: || (("http://www.w3.org/1998/Math/MathML" == ns) && (stackNode.getGroup() == MI_MO_MN_MS_MTEXT)); michael@0: } michael@0: michael@0: /** michael@0: * michael@0: *

michael@0: * C++ memory note: The return value must be released. michael@0: * michael@0: * @return michael@0: * @throws SAXException michael@0: * @throws StopSniffingException michael@0: */ michael@0: public static String extractCharsetFromContent(String attributeValue) { michael@0: // This is a bit ugly. Converting the string to char array in order to michael@0: // make the portability layer smaller. michael@0: int charsetState = CHARSET_INITIAL; michael@0: int start = -1; michael@0: int end = -1; michael@0: @Auto char[] buffer = Portability.newCharArrayFromString(attributeValue); michael@0: michael@0: charsetloop: for (int i = 0; i < buffer.length; i++) { michael@0: char c = buffer[i]; michael@0: switch (charsetState) { michael@0: case CHARSET_INITIAL: michael@0: switch (c) { michael@0: case 'c': michael@0: case 'C': michael@0: charsetState = CHARSET_C; michael@0: continue; michael@0: default: michael@0: continue; michael@0: } michael@0: case CHARSET_C: michael@0: switch (c) { michael@0: case 'h': michael@0: case 'H': michael@0: charsetState = CHARSET_H; michael@0: continue; michael@0: default: michael@0: charsetState = CHARSET_INITIAL; michael@0: continue; michael@0: } michael@0: case CHARSET_H: michael@0: switch (c) { michael@0: case 'a': michael@0: case 'A': michael@0: charsetState = CHARSET_A; michael@0: continue; michael@0: default: michael@0: charsetState = CHARSET_INITIAL; michael@0: continue; michael@0: } michael@0: case CHARSET_A: michael@0: switch (c) { michael@0: case 'r': michael@0: case 'R': michael@0: charsetState = CHARSET_R; michael@0: continue; michael@0: default: michael@0: charsetState = CHARSET_INITIAL; michael@0: continue; michael@0: } michael@0: case CHARSET_R: michael@0: switch (c) { michael@0: case 's': michael@0: case 'S': michael@0: charsetState = CHARSET_S; michael@0: continue; michael@0: default: michael@0: charsetState = CHARSET_INITIAL; michael@0: continue; michael@0: } michael@0: case CHARSET_S: michael@0: switch (c) { michael@0: case 'e': michael@0: case 'E': michael@0: charsetState = CHARSET_E; michael@0: continue; michael@0: default: michael@0: charsetState = CHARSET_INITIAL; michael@0: continue; michael@0: } michael@0: case CHARSET_E: michael@0: switch (c) { michael@0: case 't': michael@0: case 'T': michael@0: charsetState = CHARSET_T; michael@0: continue; michael@0: default: michael@0: charsetState = CHARSET_INITIAL; michael@0: continue; michael@0: } michael@0: case CHARSET_T: michael@0: switch (c) { michael@0: case '\t': michael@0: case '\n': michael@0: case '\u000C': michael@0: case '\r': michael@0: case ' ': michael@0: continue; michael@0: case '=': michael@0: charsetState = CHARSET_EQUALS; michael@0: continue; michael@0: default: michael@0: return null; michael@0: } michael@0: case CHARSET_EQUALS: michael@0: switch (c) { michael@0: case '\t': michael@0: case '\n': michael@0: case '\u000C': michael@0: case '\r': michael@0: case ' ': michael@0: continue; michael@0: case '\'': michael@0: start = i + 1; michael@0: charsetState = CHARSET_SINGLE_QUOTED; michael@0: continue; michael@0: case '\"': michael@0: start = i + 1; michael@0: charsetState = CHARSET_DOUBLE_QUOTED; michael@0: continue; michael@0: default: michael@0: start = i; michael@0: charsetState = CHARSET_UNQUOTED; michael@0: continue; michael@0: } michael@0: case CHARSET_SINGLE_QUOTED: michael@0: switch (c) { michael@0: case '\'': michael@0: end = i; michael@0: break charsetloop; michael@0: default: michael@0: continue; michael@0: } michael@0: case CHARSET_DOUBLE_QUOTED: michael@0: switch (c) { michael@0: case '\"': michael@0: end = i; michael@0: break charsetloop; michael@0: default: michael@0: continue; michael@0: } michael@0: case CHARSET_UNQUOTED: michael@0: switch (c) { michael@0: case '\t': michael@0: case '\n': michael@0: case '\u000C': michael@0: case '\r': michael@0: case ' ': michael@0: case ';': michael@0: end = i; michael@0: break charsetloop; michael@0: default: michael@0: continue; michael@0: } michael@0: } michael@0: } michael@0: String charset = null; michael@0: if (start != -1) { michael@0: if (end == -1) { michael@0: end = buffer.length; michael@0: } michael@0: charset = Portability.newStringFromBuffer(buffer, start, end michael@0: - start); michael@0: } michael@0: return charset; michael@0: } michael@0: michael@0: private void checkMetaCharset(HtmlAttributes attributes) michael@0: throws SAXException { michael@0: String charset = attributes.getValue(AttributeName.CHARSET); michael@0: if (charset != null) { michael@0: if (tokenizer.internalEncodingDeclaration(charset)) { michael@0: requestSuspension(); michael@0: return; michael@0: } michael@0: return; michael@0: } michael@0: if (!Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "content-type", michael@0: attributes.getValue(AttributeName.HTTP_EQUIV))) { michael@0: return; michael@0: } michael@0: String content = attributes.getValue(AttributeName.CONTENT); michael@0: if (content != null) { michael@0: String extract = TreeBuilder.extractCharsetFromContent(content); michael@0: // remember not to return early without releasing the string michael@0: if (extract != null) { michael@0: if (tokenizer.internalEncodingDeclaration(extract)) { michael@0: requestSuspension(); michael@0: } michael@0: } michael@0: Portability.releaseString(extract); michael@0: } michael@0: } michael@0: michael@0: public final void endTag(ElementName elementName) throws SAXException { michael@0: flushCharacters(); michael@0: needToDropLF = false; michael@0: int eltPos; michael@0: int group = elementName.getGroup(); michael@0: @Local String name = elementName.name; michael@0: endtagloop: for (;;) { michael@0: if (isInForeign()) { michael@0: if (stack[currentPtr].name != name) { michael@0: errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr].popName); michael@0: } michael@0: eltPos = currentPtr; michael@0: for (;;) { michael@0: if (stack[eltPos].name == name) { michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: break endtagloop; michael@0: } michael@0: if (stack[--eltPos].ns == "http://www.w3.org/1999/xhtml") { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: switch (mode) { michael@0: case IN_TEMPLATE: michael@0: switch (group) { michael@0: case TEMPLATE: michael@0: // fall through to IN_HEAD michael@0: break; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case IN_ROW: michael@0: switch (group) { michael@0: case TR: michael@0: eltPos = findLastOrRoot(TreeBuilder.TR); michael@0: if (eltPos == 0) { michael@0: assert fragment || isTemplateContents(); michael@0: errNoTableRowToClose(); michael@0: break endtagloop; michael@0: } michael@0: clearStackBackTo(eltPos); michael@0: pop(); michael@0: mode = IN_TABLE_BODY; michael@0: break endtagloop; michael@0: case TABLE: michael@0: eltPos = findLastOrRoot(TreeBuilder.TR); michael@0: if (eltPos == 0) { michael@0: assert fragment || isTemplateContents(); michael@0: errNoTableRowToClose(); michael@0: break endtagloop; michael@0: } michael@0: clearStackBackTo(eltPos); michael@0: pop(); michael@0: mode = IN_TABLE_BODY; michael@0: continue; michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: eltPos = findLastOrRoot(TreeBuilder.TR); michael@0: if (eltPos == 0) { michael@0: assert fragment || isTemplateContents(); michael@0: errNoTableRowToClose(); michael@0: break endtagloop; michael@0: } michael@0: clearStackBackTo(eltPos); michael@0: pop(); michael@0: mode = IN_TABLE_BODY; michael@0: continue; michael@0: case BODY: michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case HTML: michael@0: case TD_OR_TH: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: default: michael@0: // fall through to IN_TABLE michael@0: } michael@0: case IN_TABLE_BODY: michael@0: switch (group) { michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: eltPos = findLastOrRoot(name); michael@0: if (eltPos == 0) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: clearStackBackTo(eltPos); michael@0: pop(); michael@0: mode = IN_TABLE; michael@0: break endtagloop; michael@0: case TABLE: michael@0: eltPos = findLastInTableScopeOrRootTemplateTbodyTheadTfoot(); michael@0: if (eltPos == 0 || stack[eltPos].getGroup() == TEMPLATE) { michael@0: assert fragment || isTemplateContents(); michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: clearStackBackTo(eltPos); michael@0: pop(); michael@0: mode = IN_TABLE; michael@0: continue; michael@0: case BODY: michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case HTML: michael@0: case TD_OR_TH: michael@0: case TR: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: default: michael@0: // fall through to IN_TABLE michael@0: } michael@0: case IN_TABLE: michael@0: switch (group) { michael@0: case TABLE: michael@0: eltPos = findLast("table"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment || isTemplateContents(); michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: resetTheInsertionMode(); michael@0: break endtagloop; michael@0: case BODY: michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case HTML: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TD_OR_TH: michael@0: case TR: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: case TEMPLATE: michael@0: // fall through to IN_HEAD michael@0: break; michael@0: default: michael@0: errStrayEndTag(name); michael@0: // fall through to IN_BODY michael@0: } michael@0: case IN_CAPTION: michael@0: switch (group) { michael@0: case CAPTION: michael@0: eltPos = findLastInTableScope("caption"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: break endtagloop; michael@0: } michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && currentPtr != eltPos) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: mode = IN_TABLE; michael@0: break endtagloop; michael@0: case TABLE: michael@0: errTableClosedWhileCaptionOpen(); michael@0: eltPos = findLastInTableScope("caption"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: break endtagloop; michael@0: } michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && currentPtr != eltPos) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: mode = IN_TABLE; michael@0: continue; michael@0: case BODY: michael@0: case COL: michael@0: case COLGROUP: michael@0: case HTML: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TD_OR_TH: michael@0: case TR: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: default: michael@0: // fall through to IN_BODY michael@0: } michael@0: case IN_CELL: michael@0: switch (group) { michael@0: case TD_OR_TH: michael@0: eltPos = findLastInTableScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && !isCurrent(name)) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: mode = IN_ROW; michael@0: break endtagloop; michael@0: case TABLE: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TR: michael@0: if (findLastInTableScope(name) == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert name == "tbody" || name == "tfoot" || name == "thead" || fragment || isTemplateContents(); michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: closeTheCell(findLastInTableScopeTdTh()); michael@0: continue; michael@0: case BODY: michael@0: case CAPTION: michael@0: case COL: michael@0: case COLGROUP: michael@0: case HTML: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: default: michael@0: // fall through to IN_BODY michael@0: } michael@0: case FRAMESET_OK: michael@0: case IN_BODY: michael@0: switch (group) { michael@0: case BODY: michael@0: if (!isSecondOnStackBody()) { michael@0: assert fragment || isTemplateContents(); michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: assert currentPtr >= 1; michael@0: if (errorHandler != null) { michael@0: uncloseloop1: for (int i = 2; i <= currentPtr; i++) { michael@0: switch (stack[i].getGroup()) { michael@0: case DD_OR_DT: michael@0: case LI: michael@0: case OPTGROUP: michael@0: case OPTION: // is this possible? michael@0: case P: michael@0: case RT_OR_RP: michael@0: case TD_OR_TH: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: break; michael@0: default: michael@0: errEndWithUnclosedElements(name); michael@0: break uncloseloop1; michael@0: } michael@0: } michael@0: } michael@0: mode = AFTER_BODY; michael@0: break endtagloop; michael@0: case HTML: michael@0: if (!isSecondOnStackBody()) { michael@0: assert fragment || isTemplateContents(); michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: if (errorHandler != null) { michael@0: uncloseloop2: for (int i = 0; i <= currentPtr; i++) { michael@0: switch (stack[i].getGroup()) { michael@0: case DD_OR_DT: michael@0: case LI: michael@0: case P: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TD_OR_TH: michael@0: case BODY: michael@0: case HTML: michael@0: break; michael@0: default: michael@0: errEndWithUnclosedElements(name); michael@0: break uncloseloop2; michael@0: } michael@0: } michael@0: } michael@0: mode = AFTER_BODY; michael@0: continue; michael@0: case DIV_OR_BLOCKQUOTE_OR_CENTER_OR_MENU: michael@0: case UL_OR_OL_OR_DL: michael@0: case PRE_OR_LISTING: michael@0: case FIELDSET: michael@0: case BUTTON: michael@0: case ADDRESS_OR_ARTICLE_OR_ASIDE_OR_DETAILS_OR_DIR_OR_FIGCAPTION_OR_FIGURE_OR_FOOTER_OR_HEADER_OR_HGROUP_OR_MAIN_OR_NAV_OR_SECTION_OR_SUMMARY: michael@0: eltPos = findLastInScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag(name); michael@0: } else { michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && !isCurrent(name)) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: } michael@0: break endtagloop; michael@0: case FORM: michael@0: if (!isTemplateContents()) { michael@0: if (formPointer == null) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: formPointer = null; michael@0: eltPos = findLastInScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && !isCurrent(name)) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: removeFromStack(eltPos); michael@0: break endtagloop; michael@0: } else { michael@0: eltPos = findLastInScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && !isCurrent(name)) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: break endtagloop; michael@0: } michael@0: case P: michael@0: eltPos = findLastInButtonScope("p"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errNoElementToCloseButEndTagSeen("p"); michael@0: // XXX Can the 'in foreign' case happen anymore? michael@0: if (isInForeign()) { michael@0: errHtmlStartTagInForeignContext(name); michael@0: while (stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") { michael@0: pop(); michael@0: } michael@0: } michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: break endtagloop; michael@0: } michael@0: generateImpliedEndTagsExceptFor("p"); michael@0: assert eltPos != TreeBuilder.NOT_FOUND_ON_STACK; michael@0: if (errorHandler != null && eltPos != currentPtr) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: break endtagloop; michael@0: case LI: michael@0: eltPos = findLastInListScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errNoElementToCloseButEndTagSeen(name); michael@0: } else { michael@0: generateImpliedEndTagsExceptFor(name); michael@0: if (errorHandler != null michael@0: && eltPos != currentPtr) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: } michael@0: break endtagloop; michael@0: case DD_OR_DT: michael@0: eltPos = findLastInScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errNoElementToCloseButEndTagSeen(name); michael@0: } else { michael@0: generateImpliedEndTagsExceptFor(name); michael@0: if (errorHandler != null michael@0: && eltPos != currentPtr) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: } michael@0: break endtagloop; michael@0: case H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6: michael@0: eltPos = findLastInScopeHn(); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag(name); michael@0: } else { michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && !isCurrent(name)) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: } michael@0: break endtagloop; michael@0: case OBJECT: michael@0: case MARQUEE_OR_APPLET: michael@0: eltPos = findLastInScope(name); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag(name); michael@0: } else { michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && !isCurrent(name)) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: } michael@0: break endtagloop; michael@0: case BR: michael@0: errEndTagBr(); michael@0: if (isInForeign()) { michael@0: errHtmlStartTagInForeignContext(name); michael@0: while (stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") { michael@0: pop(); michael@0: } michael@0: } michael@0: reconstructTheActiveFormattingElements(); michael@0: appendVoidElementToCurrentMayFoster( michael@0: elementName, michael@0: HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: break endtagloop; michael@0: case TEMPLATE: michael@0: // fall through to IN_HEAD; michael@0: break; michael@0: case AREA_OR_WBR: michael@0: // CPPONLY: case MENUITEM: michael@0: case PARAM_OR_SOURCE_OR_TRACK: michael@0: case EMBED: michael@0: case IMG: michael@0: case IMAGE: michael@0: case INPUT: michael@0: case KEYGEN: // XXX?? michael@0: case HR: michael@0: case ISINDEX: michael@0: case IFRAME: michael@0: case NOEMBED: // XXX??? michael@0: case NOFRAMES: // XXX?? michael@0: case SELECT: michael@0: case TABLE: michael@0: case TEXTAREA: // XXX?? michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: case NOSCRIPT: michael@0: if (scriptingEnabled) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } else { michael@0: // fall through michael@0: } michael@0: case A: michael@0: case B_OR_BIG_OR_CODE_OR_EM_OR_I_OR_S_OR_SMALL_OR_STRIKE_OR_STRONG_OR_TT_OR_U: michael@0: case FONT: michael@0: case NOBR: michael@0: if (adoptionAgencyEndTag(name)) { michael@0: break endtagloop; michael@0: } michael@0: // else handle like any other tag michael@0: default: michael@0: if (isCurrent(name)) { michael@0: pop(); michael@0: break endtagloop; michael@0: } michael@0: michael@0: eltPos = currentPtr; michael@0: for (;;) { michael@0: StackNode node = stack[eltPos]; michael@0: if (node.ns == "http://www.w3.org/1999/xhtml" && node.name == name) { michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null michael@0: && !isCurrent(name)) { michael@0: errUnclosedElements(eltPos, name); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: break endtagloop; michael@0: } else if (node.isSpecial()) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: eltPos--; michael@0: } michael@0: } michael@0: case IN_HEAD: michael@0: switch (group) { michael@0: case HEAD: michael@0: pop(); michael@0: mode = AFTER_HEAD; michael@0: break endtagloop; michael@0: case BR: michael@0: case HTML: michael@0: case BODY: michael@0: pop(); michael@0: mode = AFTER_HEAD; michael@0: continue; michael@0: case TEMPLATE: michael@0: endTagTemplateInHead(); michael@0: break endtagloop; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case IN_HEAD_NOSCRIPT: michael@0: switch (group) { michael@0: case NOSCRIPT: michael@0: pop(); michael@0: mode = IN_HEAD; michael@0: break endtagloop; michael@0: case BR: michael@0: errStrayEndTag(name); michael@0: pop(); michael@0: mode = IN_HEAD; michael@0: continue; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case IN_COLUMN_GROUP: michael@0: switch (group) { michael@0: case COLGROUP: michael@0: if (currentPtr == 0 || stack[currentPtr].getGroup() == michael@0: TreeBuilder.TEMPLATE) { michael@0: assert fragment || isTemplateContents(); michael@0: errGarbageInColgroup(); michael@0: break endtagloop; michael@0: } michael@0: pop(); michael@0: mode = IN_TABLE; michael@0: break endtagloop; michael@0: case COL: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: case TEMPLATE: michael@0: endTagTemplateInHead(); michael@0: break endtagloop; michael@0: default: michael@0: if (currentPtr == 0 || stack[currentPtr].getGroup() == michael@0: TreeBuilder.TEMPLATE) { michael@0: assert fragment || isTemplateContents(); michael@0: errGarbageInColgroup(); michael@0: break endtagloop; michael@0: } michael@0: pop(); michael@0: mode = IN_TABLE; michael@0: continue; michael@0: } michael@0: case IN_SELECT_IN_TABLE: michael@0: switch (group) { michael@0: case CAPTION: michael@0: case TABLE: michael@0: case TBODY_OR_THEAD_OR_TFOOT: michael@0: case TR: michael@0: case TD_OR_TH: michael@0: errEndTagSeenWithSelectOpen(name); michael@0: if (findLastInTableScope(name) != TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: eltPos = findLastInTableScope("select"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment; michael@0: break endtagloop; // http://www.w3.org/Bugs/Public/show_bug.cgi?id=8375 michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: resetTheInsertionMode(); michael@0: continue; michael@0: } else { michael@0: break endtagloop; michael@0: } michael@0: default: michael@0: // fall through to IN_SELECT michael@0: } michael@0: case IN_SELECT: michael@0: switch (group) { michael@0: case OPTION: michael@0: if (isCurrent("option")) { michael@0: pop(); michael@0: break endtagloop; michael@0: } else { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case OPTGROUP: michael@0: if (isCurrent("option") michael@0: && "optgroup" == stack[currentPtr - 1].name) { michael@0: pop(); michael@0: } michael@0: if (isCurrent("optgroup")) { michael@0: pop(); michael@0: } else { michael@0: errStrayEndTag(name); michael@0: } michael@0: break endtagloop; michael@0: case SELECT: michael@0: eltPos = findLastInTableScope("select"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: assert fragment; michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: resetTheInsertionMode(); michael@0: break endtagloop; michael@0: case TEMPLATE: michael@0: endTagTemplateInHead(); michael@0: break endtagloop; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case AFTER_BODY: michael@0: switch (group) { michael@0: case HTML: michael@0: if (fragment) { michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } else { michael@0: mode = AFTER_AFTER_BODY; michael@0: break endtagloop; michael@0: } michael@0: default: michael@0: errEndTagAfterBody(); michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: continue; michael@0: } michael@0: case IN_FRAMESET: michael@0: switch (group) { michael@0: case FRAMESET: michael@0: if (currentPtr == 0) { michael@0: assert fragment; michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: pop(); michael@0: if ((!fragment) && !isCurrent("frameset")) { michael@0: mode = AFTER_FRAMESET; michael@0: } michael@0: break endtagloop; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case AFTER_FRAMESET: michael@0: switch (group) { michael@0: case HTML: michael@0: mode = AFTER_AFTER_FRAMESET; michael@0: break endtagloop; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case INITIAL: michael@0: /* michael@0: * Parse error. michael@0: */ michael@0: // [NOCPP[ michael@0: switch (doctypeExpectation) { michael@0: case AUTO: michael@0: err("End tag seen without seeing a doctype first. Expected e.g. \u201C\u201D."); michael@0: break; michael@0: case HTML: michael@0: // ]NOCPP] michael@0: errEndTagSeenWithoutDoctype(); michael@0: // [NOCPP[ michael@0: break; michael@0: case HTML401_STRICT: michael@0: err("End tag seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case HTML401_TRANSITIONAL: michael@0: err("End tag seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: break; michael@0: case NO_DOCTYPE_ERRORS: michael@0: } michael@0: // ]NOCPP] michael@0: /* michael@0: * michael@0: * Set the document to quirks mode. michael@0: */ michael@0: documentModeInternal(DocumentMode.QUIRKS_MODE, null, null, michael@0: false); michael@0: /* michael@0: * Then, switch to the root element mode of the tree michael@0: * construction stage michael@0: */ michael@0: mode = BEFORE_HTML; michael@0: /* michael@0: * and reprocess the current token. michael@0: */ michael@0: continue; michael@0: case BEFORE_HTML: michael@0: switch (group) { michael@0: case HEAD: michael@0: case BR: michael@0: case HTML: michael@0: case BODY: michael@0: /* michael@0: * Create an HTMLElement node with the tag name michael@0: * html, in the HTML namespace. Append it to the michael@0: * Document object. michael@0: */ michael@0: appendHtmlElementToDocumentAndPush(); michael@0: /* Switch to the main mode */ michael@0: mode = BEFORE_HEAD; michael@0: /* michael@0: * reprocess the current token. michael@0: */ michael@0: continue; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case BEFORE_HEAD: michael@0: switch (group) { michael@0: case HEAD: michael@0: case BR: michael@0: case HTML: michael@0: case BODY: michael@0: appendToCurrentNodeAndPushHeadElement(HtmlAttributes.EMPTY_ATTRIBUTES); michael@0: mode = IN_HEAD; michael@0: continue; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case AFTER_HEAD: michael@0: switch (group) { michael@0: case TEMPLATE: michael@0: endTagTemplateInHead(); michael@0: break endtagloop; michael@0: case HTML: michael@0: case BODY: michael@0: case BR: michael@0: appendToCurrentNodeAndPushBodyElement(); michael@0: mode = FRAMESET_OK; michael@0: continue; michael@0: default: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: } michael@0: case AFTER_AFTER_BODY: michael@0: errStrayEndTag(name); michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: continue; michael@0: case AFTER_AFTER_FRAMESET: michael@0: errStrayEndTag(name); michael@0: break endtagloop; michael@0: case TEXT: michael@0: // XXX need to manage insertion point here michael@0: pop(); michael@0: if (originalMode == AFTER_HEAD) { michael@0: silentPop(); michael@0: } michael@0: mode = originalMode; michael@0: break endtagloop; michael@0: } michael@0: } // endtagloop michael@0: } michael@0: michael@0: private void endTagTemplateInHead() throws SAXException { michael@0: int eltPos = findLast("template"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: errStrayEndTag("template"); michael@0: return; michael@0: } michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && !isCurrent("template")) { michael@0: errUnclosedElements(eltPos, "template"); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: popTemplateMode(); michael@0: resetTheInsertionMode(); michael@0: } michael@0: michael@0: private int findLastInTableScopeOrRootTemplateTbodyTheadTfoot() { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].getGroup() == TreeBuilder.TBODY_OR_THEAD_OR_TFOOT || michael@0: stack[i].getGroup() == TreeBuilder.TEMPLATE) { michael@0: return i; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: private int findLast(@Local String name) { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) { michael@0: return i; michael@0: } michael@0: } michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: michael@0: private int findLastInTableScope(@Local String name) { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].ns == "http://www.w3.org/1999/xhtml") { michael@0: if (stack[i].name == name) { michael@0: return i; michael@0: } else if (stack[i].name == "table" || stack[i].name == "template") { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: } michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: michael@0: private int findLastInButtonScope(@Local String name) { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].ns == "http://www.w3.org/1999/xhtml") { michael@0: if (stack[i].name == name) { michael@0: return i; michael@0: } else if (stack[i].name == "button") { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: michael@0: if (stack[i].isScoping()) { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: michael@0: private int findLastInScope(@Local String name) { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) { michael@0: return i; michael@0: } else if (stack[i].isScoping()) { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: michael@0: private int findLastInListScope(@Local String name) { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].ns == "http://www.w3.org/1999/xhtml") { michael@0: if (stack[i].name == name) { michael@0: return i; michael@0: } else if (stack[i].name == "ul" || stack[i].name == "ol") { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: michael@0: if (stack[i].isScoping()) { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: michael@0: private int findLastInScopeHn() { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].getGroup() == TreeBuilder.H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6) { michael@0: return i; michael@0: } else if (stack[i].isScoping()) { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: michael@0: private void generateImpliedEndTagsExceptFor(@Local String name) michael@0: throws SAXException { michael@0: for (;;) { michael@0: StackNode node = stack[currentPtr]; michael@0: switch (node.getGroup()) { michael@0: case P: michael@0: case LI: michael@0: case DD_OR_DT: michael@0: case OPTION: michael@0: case OPTGROUP: michael@0: case RT_OR_RP: michael@0: if (node.ns == "http://www.w3.org/1999/xhtml" && node.name == name) { michael@0: return; michael@0: } michael@0: pop(); michael@0: continue; michael@0: default: michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: private void generateImpliedEndTags() throws SAXException { michael@0: for (;;) { michael@0: switch (stack[currentPtr].getGroup()) { michael@0: case P: michael@0: case LI: michael@0: case DD_OR_DT: michael@0: case OPTION: michael@0: case OPTGROUP: michael@0: case RT_OR_RP: michael@0: pop(); michael@0: continue; michael@0: default: michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: private boolean isSecondOnStackBody() { michael@0: return currentPtr >= 1 && stack[1].getGroup() == TreeBuilder.BODY; michael@0: } michael@0: michael@0: private void documentModeInternal(DocumentMode m, String publicIdentifier, michael@0: String systemIdentifier, boolean html4SpecificAdditionalErrorChecks) michael@0: throws SAXException { michael@0: michael@0: if (isSrcdocDocument) { michael@0: // Srcdoc documents are always rendered in standards mode. michael@0: quirks = false; michael@0: if (documentModeHandler != null) { michael@0: documentModeHandler.documentMode( michael@0: DocumentMode.STANDARDS_MODE michael@0: // [NOCPP[ michael@0: , null, null, false michael@0: // ]NOCPP] michael@0: ); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: quirks = (m == DocumentMode.QUIRKS_MODE); michael@0: if (documentModeHandler != null) { michael@0: documentModeHandler.documentMode( michael@0: m michael@0: // [NOCPP[ michael@0: , publicIdentifier, systemIdentifier, michael@0: html4SpecificAdditionalErrorChecks michael@0: // ]NOCPP] michael@0: ); michael@0: } michael@0: // [NOCPP[ michael@0: documentMode(m, publicIdentifier, systemIdentifier, michael@0: html4SpecificAdditionalErrorChecks); michael@0: // ]NOCPP] michael@0: } michael@0: michael@0: private boolean isAlmostStandards(String publicIdentifier, michael@0: String systemIdentifier) { michael@0: if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-//w3c//dtd xhtml 1.0 transitional//en", publicIdentifier)) { michael@0: return true; michael@0: } michael@0: if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-//w3c//dtd xhtml 1.0 frameset//en", publicIdentifier)) { michael@0: return true; michael@0: } michael@0: if (systemIdentifier != null) { michael@0: if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-//w3c//dtd html 4.01 transitional//en", publicIdentifier)) { michael@0: return true; michael@0: } michael@0: if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-//w3c//dtd html 4.01 frameset//en", publicIdentifier)) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: private boolean isQuirky(@Local String name, String publicIdentifier, michael@0: String systemIdentifier, boolean forceQuirks) { michael@0: if (forceQuirks) { michael@0: return true; michael@0: } michael@0: if (name != HTML_LOCAL) { michael@0: return true; michael@0: } michael@0: if (publicIdentifier != null) { michael@0: for (int i = 0; i < TreeBuilder.QUIRKY_PUBLIC_IDS.length; i++) { michael@0: if (Portability.lowerCaseLiteralIsPrefixOfIgnoreAsciiCaseString( michael@0: TreeBuilder.QUIRKY_PUBLIC_IDS[i], publicIdentifier)) { michael@0: return true; michael@0: } michael@0: } michael@0: if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-//w3o//dtd w3 html strict 3.0//en//", publicIdentifier) michael@0: || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-/w3c/dtd html 4.0 transitional/en", michael@0: publicIdentifier) michael@0: || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "html", publicIdentifier)) { michael@0: return true; michael@0: } michael@0: } michael@0: if (systemIdentifier == null) { michael@0: if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-//w3c//dtd html 4.01 transitional//en", publicIdentifier)) { michael@0: return true; michael@0: } else if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "-//w3c//dtd html 4.01 frameset//en", publicIdentifier)) { michael@0: return true; michael@0: } michael@0: } else if (Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd", michael@0: systemIdentifier)) { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: private void closeTheCell(int eltPos) throws SAXException { michael@0: generateImpliedEndTags(); michael@0: if (errorHandler != null && eltPos != currentPtr) { michael@0: errUnclosedElementsCell(eltPos); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: clearTheListOfActiveFormattingElementsUpToTheLastMarker(); michael@0: mode = IN_ROW; michael@0: return; michael@0: } michael@0: michael@0: private int findLastInTableScopeTdTh() { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: @Local String name = stack[i].name; michael@0: if (stack[i].ns == "http://www.w3.org/1999/xhtml") { michael@0: if ("td" == name || "th" == name) { michael@0: return i; michael@0: } else if (name == "table" || name == "template") { michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: } michael@0: } michael@0: return TreeBuilder.NOT_FOUND_ON_STACK; michael@0: } michael@0: michael@0: private void clearStackBackTo(int eltPos) throws SAXException { michael@0: int eltGroup = stack[eltPos].getGroup(); michael@0: while (currentPtr > eltPos) { // > not >= intentional michael@0: if (stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" michael@0: && stack[currentPtr].getGroup() == TEMPLATE michael@0: && (eltGroup == TABLE || eltGroup == TBODY_OR_THEAD_OR_TFOOT|| eltGroup == TR || eltGroup == HTML)) { michael@0: return; michael@0: } michael@0: pop(); michael@0: } michael@0: } michael@0: michael@0: private void resetTheInsertionMode() { michael@0: StackNode node; michael@0: @Local String name; michael@0: @NsUri String ns; michael@0: for (int i = currentPtr; i >= 0; i--) { michael@0: node = stack[i]; michael@0: name = node.name; michael@0: ns = node.ns; michael@0: if (i == 0) { michael@0: if (!(contextNamespace == "http://www.w3.org/1999/xhtml" && (contextName == "td" || contextName == "th"))) { michael@0: if (fragment) { michael@0: // Make sure we are parsing a fragment otherwise the context element doesn't make sense. michael@0: name = contextName; michael@0: ns = contextNamespace; michael@0: } michael@0: } else { michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; // XXX from Hixie's email michael@0: return; michael@0: } michael@0: } michael@0: if ("select" == name) { michael@0: int ancestorIndex = i; michael@0: while (ancestorIndex > 0) { michael@0: StackNode ancestor = stack[ancestorIndex--]; michael@0: if ("http://www.w3.org/1999/xhtml" == ancestor.ns) { michael@0: if ("template" == ancestor.name) { michael@0: break; michael@0: } michael@0: if ("table" == ancestor.name) { michael@0: mode = IN_SELECT_IN_TABLE; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: mode = IN_SELECT; michael@0: return; michael@0: } else if ("td" == name || "th" == name) { michael@0: mode = IN_CELL; michael@0: return; michael@0: } else if ("tr" == name) { michael@0: mode = IN_ROW; michael@0: return; michael@0: } else if ("tbody" == name || "thead" == name || "tfoot" == name) { michael@0: mode = IN_TABLE_BODY; michael@0: return; michael@0: } else if ("caption" == name) { michael@0: mode = IN_CAPTION; michael@0: return; michael@0: } else if ("colgroup" == name) { michael@0: mode = IN_COLUMN_GROUP; michael@0: return; michael@0: } else if ("table" == name) { michael@0: mode = IN_TABLE; michael@0: return; michael@0: } else if ("http://www.w3.org/1999/xhtml" != ns) { michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: return; michael@0: } else if ("template" == name) { michael@0: assert templateModePtr >= 0; michael@0: mode = templateModeStack[templateModePtr]; michael@0: return; michael@0: } else if ("head" == name) { michael@0: if (name == contextName) { michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; // really michael@0: } else { michael@0: mode = IN_HEAD; michael@0: } michael@0: return; michael@0: } else if ("body" == name) { michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: return; michael@0: } else if ("frameset" == name) { michael@0: // TODO: Fragment case. Add error reporting. michael@0: mode = IN_FRAMESET; michael@0: return; michael@0: } else if ("html" == name) { michael@0: if (headPointer == null) { michael@0: // TODO: Fragment case. Add error reporting. michael@0: mode = BEFORE_HEAD; michael@0: } else { michael@0: mode = AFTER_HEAD; michael@0: } michael@0: return; michael@0: } else if (i == 0) { michael@0: mode = framesetOk ? FRAMESET_OK : IN_BODY; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * @throws SAXException michael@0: * michael@0: */ michael@0: private void implicitlyCloseP() throws SAXException { michael@0: int eltPos = findLastInButtonScope("p"); michael@0: if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) { michael@0: return; michael@0: } michael@0: generateImpliedEndTagsExceptFor("p"); michael@0: if (errorHandler != null && eltPos != currentPtr) { michael@0: errUnclosedElementsImplied(eltPos, "p"); michael@0: } michael@0: while (currentPtr >= eltPos) { michael@0: pop(); michael@0: } michael@0: } michael@0: michael@0: private boolean debugOnlyClearLastStackSlot() { michael@0: stack[currentPtr] = null; michael@0: return true; michael@0: } michael@0: michael@0: private boolean debugOnlyClearLastListSlot() { michael@0: listOfActiveFormattingElements[listPtr] = null; michael@0: return true; michael@0: } michael@0: michael@0: private void pushTemplateMode(int mode) { michael@0: templateModePtr++; michael@0: if (templateModePtr == templateModeStack.length) { michael@0: int[] newStack = new int[templateModeStack.length + 64]; michael@0: System.arraycopy(templateModeStack, 0, newStack, 0, templateModeStack.length); michael@0: templateModeStack = newStack; michael@0: } michael@0: templateModeStack[templateModePtr] = mode; michael@0: } michael@0: michael@0: @SuppressWarnings("unchecked") private void push(StackNode node) throws SAXException { michael@0: currentPtr++; michael@0: if (currentPtr == stack.length) { michael@0: StackNode[] newStack = new StackNode[stack.length + 64]; michael@0: System.arraycopy(stack, 0, newStack, 0, stack.length); michael@0: stack = newStack; michael@0: } michael@0: stack[currentPtr] = node; michael@0: elementPushed(node.ns, node.popName, node.node); michael@0: } michael@0: michael@0: @SuppressWarnings("unchecked") private void silentPush(StackNode node) throws SAXException { michael@0: currentPtr++; michael@0: if (currentPtr == stack.length) { michael@0: StackNode[] newStack = new StackNode[stack.length + 64]; michael@0: System.arraycopy(stack, 0, newStack, 0, stack.length); michael@0: stack = newStack; michael@0: } michael@0: stack[currentPtr] = node; michael@0: } michael@0: michael@0: @SuppressWarnings("unchecked") private void append(StackNode node) { michael@0: listPtr++; michael@0: if (listPtr == listOfActiveFormattingElements.length) { michael@0: StackNode[] newList = new StackNode[listOfActiveFormattingElements.length + 64]; michael@0: System.arraycopy(listOfActiveFormattingElements, 0, newList, 0, michael@0: listOfActiveFormattingElements.length); michael@0: listOfActiveFormattingElements = newList; michael@0: } michael@0: listOfActiveFormattingElements[listPtr] = node; michael@0: } michael@0: michael@0: @Inline private void insertMarker() { michael@0: append(null); michael@0: } michael@0: michael@0: private void clearTheListOfActiveFormattingElementsUpToTheLastMarker() { michael@0: while (listPtr > -1) { michael@0: if (listOfActiveFormattingElements[listPtr] == null) { michael@0: --listPtr; michael@0: return; michael@0: } michael@0: listOfActiveFormattingElements[listPtr].release(); michael@0: --listPtr; michael@0: } michael@0: } michael@0: michael@0: @Inline private boolean isCurrent(@Local String name) { michael@0: return stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" && michael@0: name == stack[currentPtr].name; michael@0: } michael@0: michael@0: private void removeFromStack(int pos) throws SAXException { michael@0: if (currentPtr == pos) { michael@0: pop(); michael@0: } else { michael@0: fatal(); michael@0: stack[pos].release(); michael@0: System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos); michael@0: assert debugOnlyClearLastStackSlot(); michael@0: currentPtr--; michael@0: } michael@0: } michael@0: michael@0: private void removeFromStack(StackNode node) throws SAXException { michael@0: if (stack[currentPtr] == node) { michael@0: pop(); michael@0: } else { michael@0: int pos = currentPtr - 1; michael@0: while (pos >= 0 && stack[pos] != node) { michael@0: pos--; michael@0: } michael@0: if (pos == -1) { michael@0: // dead code? michael@0: return; michael@0: } michael@0: fatal(); michael@0: node.release(); michael@0: System.arraycopy(stack, pos + 1, stack, pos, currentPtr - pos); michael@0: currentPtr--; michael@0: } michael@0: } michael@0: michael@0: private void removeFromListOfActiveFormattingElements(int pos) { michael@0: assert listOfActiveFormattingElements[pos] != null; michael@0: listOfActiveFormattingElements[pos].release(); michael@0: if (pos == listPtr) { michael@0: assert debugOnlyClearLastListSlot(); michael@0: listPtr--; michael@0: return; michael@0: } michael@0: assert pos < listPtr; michael@0: System.arraycopy(listOfActiveFormattingElements, pos + 1, michael@0: listOfActiveFormattingElements, pos, listPtr - pos); michael@0: assert debugOnlyClearLastListSlot(); michael@0: listPtr--; michael@0: } michael@0: michael@0: /** michael@0: * Adoption agency algorithm. michael@0: * michael@0: * @param name subject as described in the specified algorithm. michael@0: * @return Returns true if the algorithm has completed and there is nothing remaining to michael@0: * be done. Returns false if the algorithm needs to "act as described in the 'any other michael@0: * end tag' entry" as described in the specified algorithm. michael@0: * @throws SAXException michael@0: */ michael@0: private boolean adoptionAgencyEndTag(@Local String name) throws SAXException { michael@0: // This check intends to ensure that for properly nested tags, closing tags will match michael@0: // against the stack instead of the listOfActiveFormattingElements. michael@0: if (stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" && michael@0: stack[currentPtr].name == name && michael@0: findInListOfActiveFormattingElements(stack[currentPtr]) == -1) { michael@0: // If the current element matches the name but isn't on the list of active michael@0: // formatting elements, then it is possible that the list was mangled by the Noah's Ark michael@0: // clause. In this case, we want to match the end tag against the stack instead of michael@0: // proceeding with the AAA algorithm that may match against the list of michael@0: // active formatting elements (and possibly mangle the tree in unexpected ways). michael@0: pop(); michael@0: return true; michael@0: } michael@0: michael@0: // If you crash around here, perhaps some stack node variable claimed to michael@0: // be a weak ref isn't. michael@0: for (int i = 0; i < 8; ++i) { michael@0: int formattingEltListPos = listPtr; michael@0: while (formattingEltListPos > -1) { michael@0: StackNode listNode = listOfActiveFormattingElements[formattingEltListPos]; // weak ref michael@0: if (listNode == null) { michael@0: formattingEltListPos = -1; michael@0: break; michael@0: } else if (listNode.name == name) { michael@0: break; michael@0: } michael@0: formattingEltListPos--; michael@0: } michael@0: if (formattingEltListPos == -1) { michael@0: return false; michael@0: } michael@0: // this *looks* like a weak ref to the list of formatting elements michael@0: StackNode formattingElt = listOfActiveFormattingElements[formattingEltListPos]; michael@0: int formattingEltStackPos = currentPtr; michael@0: boolean inScope = true; michael@0: while (formattingEltStackPos > -1) { michael@0: StackNode node = stack[formattingEltStackPos]; // weak ref michael@0: if (node == formattingElt) { michael@0: break; michael@0: } else if (node.isScoping()) { michael@0: inScope = false; michael@0: } michael@0: formattingEltStackPos--; michael@0: } michael@0: if (formattingEltStackPos == -1) { michael@0: errNoElementToCloseButEndTagSeen(name); michael@0: removeFromListOfActiveFormattingElements(formattingEltListPos); michael@0: return true; michael@0: } michael@0: if (!inScope) { michael@0: errNoElementToCloseButEndTagSeen(name); michael@0: return true; michael@0: } michael@0: // stackPos now points to the formatting element and it is in scope michael@0: if (formattingEltStackPos != currentPtr) { michael@0: errEndTagViolatesNestingRules(name); michael@0: } michael@0: int furthestBlockPos = formattingEltStackPos + 1; michael@0: while (furthestBlockPos <= currentPtr) { michael@0: StackNode node = stack[furthestBlockPos]; // weak ref michael@0: if (node.isSpecial()) { michael@0: break; michael@0: } michael@0: furthestBlockPos++; michael@0: } michael@0: if (furthestBlockPos > currentPtr) { michael@0: // no furthest block michael@0: while (currentPtr >= formattingEltStackPos) { michael@0: pop(); michael@0: } michael@0: removeFromListOfActiveFormattingElements(formattingEltListPos); michael@0: return true; michael@0: } michael@0: StackNode commonAncestor = stack[formattingEltStackPos - 1]; // weak ref michael@0: StackNode furthestBlock = stack[furthestBlockPos]; // weak ref michael@0: // detachFromParent(furthestBlock.node); XXX AAA CHANGE michael@0: int bookmark = formattingEltListPos; michael@0: int nodePos = furthestBlockPos; michael@0: StackNode lastNode = furthestBlock; // weak ref michael@0: int j = 0; michael@0: for (;;) { michael@0: ++j; michael@0: nodePos--; michael@0: if (nodePos == formattingEltStackPos) { michael@0: break; michael@0: } michael@0: StackNode node = stack[nodePos]; // weak ref michael@0: int nodeListPos = findInListOfActiveFormattingElements(node); michael@0: michael@0: if (j > 3 && nodeListPos != -1) { michael@0: removeFromListOfActiveFormattingElements(nodeListPos); michael@0: michael@0: // Adjust the indices into the list to account michael@0: // for the removal of nodeListPos. michael@0: if (nodeListPos <= formattingEltListPos) { michael@0: formattingEltListPos--; michael@0: } michael@0: if (nodeListPos <= bookmark) { michael@0: bookmark--; michael@0: } michael@0: michael@0: // Update position to reflect removal from list. michael@0: nodeListPos = -1; michael@0: } michael@0: michael@0: if (nodeListPos == -1) { michael@0: assert formattingEltStackPos < nodePos; michael@0: assert bookmark < nodePos; michael@0: assert furthestBlockPos > nodePos; michael@0: removeFromStack(nodePos); // node is now a bad pointer in C++ michael@0: furthestBlockPos--; michael@0: continue; michael@0: } michael@0: // now node is both on stack and in the list michael@0: if (nodePos == furthestBlockPos) { michael@0: bookmark = nodeListPos + 1; michael@0: } michael@0: // if (hasChildren(node.node)) { XXX AAA CHANGE michael@0: assert node == listOfActiveFormattingElements[nodeListPos]; michael@0: assert node == stack[nodePos]; michael@0: T clone = createElement("http://www.w3.org/1999/xhtml", michael@0: node.name, node.attributes.cloneAttributes(null)); michael@0: StackNode newNode = new StackNode(node.getFlags(), node.ns, michael@0: node.name, clone, node.popName, node.attributes michael@0: // [NOCPP[ michael@0: , node.getLocator() michael@0: // ]NOCPP] michael@0: ); // creation ownership goes to stack michael@0: node.dropAttributes(); // adopt ownership to newNode michael@0: stack[nodePos] = newNode; michael@0: newNode.retain(); // retain for list michael@0: listOfActiveFormattingElements[nodeListPos] = newNode; michael@0: node.release(); // release from stack michael@0: node.release(); // release from list michael@0: node = newNode; michael@0: // } XXX AAA CHANGE michael@0: detachFromParent(lastNode.node); michael@0: appendElement(lastNode.node, node.node); michael@0: lastNode = node; michael@0: } michael@0: if (commonAncestor.isFosterParenting()) { michael@0: fatal(); michael@0: detachFromParent(lastNode.node); michael@0: insertIntoFosterParent(lastNode.node); michael@0: } else { michael@0: detachFromParent(lastNode.node); michael@0: appendElement(lastNode.node, commonAncestor.node); michael@0: } michael@0: T clone = createElement("http://www.w3.org/1999/xhtml", michael@0: formattingElt.name, michael@0: formattingElt.attributes.cloneAttributes(null)); michael@0: StackNode formattingClone = new StackNode( michael@0: formattingElt.getFlags(), formattingElt.ns, michael@0: formattingElt.name, clone, formattingElt.popName, michael@0: formattingElt.attributes michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); // Ownership transfers to stack below michael@0: formattingElt.dropAttributes(); // transfer ownership to michael@0: // formattingClone michael@0: appendChildrenToNewParent(furthestBlock.node, clone); michael@0: appendElement(clone, furthestBlock.node); michael@0: removeFromListOfActiveFormattingElements(formattingEltListPos); michael@0: insertIntoListOfActiveFormattingElements(formattingClone, bookmark); michael@0: assert formattingEltStackPos < furthestBlockPos; michael@0: removeFromStack(formattingEltStackPos); michael@0: // furthestBlockPos is now off by one and points to the slot after michael@0: // it michael@0: insertIntoStack(formattingClone, furthestBlockPos); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: private void insertIntoStack(StackNode node, int position) michael@0: throws SAXException { michael@0: assert currentPtr + 1 < stack.length; michael@0: assert position <= currentPtr + 1; michael@0: if (position == currentPtr + 1) { michael@0: push(node); michael@0: } else { michael@0: System.arraycopy(stack, position, stack, position + 1, michael@0: (currentPtr - position) + 1); michael@0: currentPtr++; michael@0: stack[position] = node; michael@0: } michael@0: } michael@0: michael@0: private void insertIntoListOfActiveFormattingElements( michael@0: StackNode formattingClone, int bookmark) { michael@0: formattingClone.retain(); michael@0: assert listPtr + 1 < listOfActiveFormattingElements.length; michael@0: if (bookmark <= listPtr) { michael@0: System.arraycopy(listOfActiveFormattingElements, bookmark, michael@0: listOfActiveFormattingElements, bookmark + 1, michael@0: (listPtr - bookmark) + 1); michael@0: } michael@0: listPtr++; michael@0: listOfActiveFormattingElements[bookmark] = formattingClone; michael@0: } michael@0: michael@0: private int findInListOfActiveFormattingElements(StackNode node) { michael@0: for (int i = listPtr; i >= 0; i--) { michael@0: if (node == listOfActiveFormattingElements[i]) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: private int findInListOfActiveFormattingElementsContainsBetweenEndAndLastMarker( michael@0: @Local String name) { michael@0: for (int i = listPtr; i >= 0; i--) { michael@0: StackNode node = listOfActiveFormattingElements[i]; michael@0: if (node == null) { michael@0: return -1; michael@0: } else if (node.name == name) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: michael@0: private void maybeForgetEarlierDuplicateFormattingElement( michael@0: @Local String name, HtmlAttributes attributes) throws SAXException { michael@0: int candidate = -1; michael@0: int count = 0; michael@0: for (int i = listPtr; i >= 0; i--) { michael@0: StackNode node = listOfActiveFormattingElements[i]; michael@0: if (node == null) { michael@0: break; michael@0: } michael@0: if (node.name == name && node.attributes.equalsAnother(attributes)) { michael@0: candidate = i; michael@0: ++count; michael@0: } michael@0: } michael@0: if (count >= 3) { michael@0: removeFromListOfActiveFormattingElements(candidate); michael@0: } michael@0: } michael@0: michael@0: private int findLastOrRoot(@Local String name) { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].ns == "http://www.w3.org/1999/xhtml" && stack[i].name == name) { michael@0: return i; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: private int findLastOrRoot(int group) { michael@0: for (int i = currentPtr; i > 0; i--) { michael@0: if (stack[i].getGroup() == group) { michael@0: return i; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Attempt to add attribute to the body element. michael@0: * @param attributes the attributes michael@0: * @return true iff the attributes were added michael@0: * @throws SAXException michael@0: */ michael@0: private boolean addAttributesToBody(HtmlAttributes attributes) michael@0: throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: if (currentPtr >= 1) { michael@0: StackNode body = stack[1]; michael@0: if (body.getGroup() == TreeBuilder.BODY) { michael@0: addAttributesToElement(body.node, attributes); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: private void addAttributesToHtml(HtmlAttributes attributes) michael@0: throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: addAttributesToElement(stack[0].node, attributes); michael@0: } michael@0: michael@0: private void pushHeadPointerOntoStack() throws SAXException { michael@0: assert headPointer != null; michael@0: assert mode == AFTER_HEAD; michael@0: fatal(); michael@0: silentPush(new StackNode(ElementName.HEAD, headPointer michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: )); michael@0: } michael@0: michael@0: /** michael@0: * @throws SAXException michael@0: * michael@0: */ michael@0: private void reconstructTheActiveFormattingElements() throws SAXException { michael@0: if (listPtr == -1) { michael@0: return; michael@0: } michael@0: StackNode mostRecent = listOfActiveFormattingElements[listPtr]; michael@0: if (mostRecent == null || isInStack(mostRecent)) { michael@0: return; michael@0: } michael@0: int entryPos = listPtr; michael@0: for (;;) { michael@0: entryPos--; michael@0: if (entryPos == -1) { michael@0: break; michael@0: } michael@0: if (listOfActiveFormattingElements[entryPos] == null) { michael@0: break; michael@0: } michael@0: if (isInStack(listOfActiveFormattingElements[entryPos])) { michael@0: break; michael@0: } michael@0: } michael@0: while (entryPos < listPtr) { michael@0: entryPos++; michael@0: StackNode entry = listOfActiveFormattingElements[entryPos]; michael@0: T clone = createElement("http://www.w3.org/1999/xhtml", entry.name, michael@0: entry.attributes.cloneAttributes(null)); michael@0: StackNode entryClone = new StackNode(entry.getFlags(), michael@0: entry.ns, entry.name, clone, entry.popName, michael@0: entry.attributes michael@0: // [NOCPP[ michael@0: , entry.getLocator() michael@0: // ]NOCPP] michael@0: ); michael@0: entry.dropAttributes(); // transfer ownership to entryClone michael@0: StackNode currentNode = stack[currentPtr]; michael@0: if (currentNode.isFosterParenting()) { michael@0: insertIntoFosterParent(clone); michael@0: } else { michael@0: appendElement(clone, currentNode.node); michael@0: } michael@0: push(entryClone); michael@0: // stack takes ownership of the local variable michael@0: listOfActiveFormattingElements[entryPos] = entryClone; michael@0: // overwriting the old entry on the list, so release & retain michael@0: entry.release(); michael@0: entryClone.retain(); michael@0: } michael@0: } michael@0: michael@0: private void insertIntoFosterParent(T child) throws SAXException { michael@0: int tablePos = findLastOrRoot(TreeBuilder.TABLE); michael@0: int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE); michael@0: michael@0: if (templatePos >= tablePos) { michael@0: appendElement(child, stack[templatePos].node); michael@0: return; michael@0: } michael@0: michael@0: StackNode node = stack[tablePos]; michael@0: insertFosterParentedChild(child, node.node, stack[tablePos - 1].node); michael@0: } michael@0: michael@0: private boolean isInStack(StackNode node) { michael@0: for (int i = currentPtr; i >= 0; i--) { michael@0: if (stack[i] == node) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: private void popTemplateMode() { michael@0: templateModePtr--; michael@0: } michael@0: michael@0: private void pop() throws SAXException { michael@0: StackNode node = stack[currentPtr]; michael@0: assert debugOnlyClearLastStackSlot(); michael@0: currentPtr--; michael@0: elementPopped(node.ns, node.popName, node.node); michael@0: node.release(); michael@0: } michael@0: michael@0: private void silentPop() throws SAXException { michael@0: StackNode node = stack[currentPtr]; michael@0: assert debugOnlyClearLastStackSlot(); michael@0: currentPtr--; michael@0: node.release(); michael@0: } michael@0: michael@0: private void popOnEof() throws SAXException { michael@0: StackNode node = stack[currentPtr]; michael@0: assert debugOnlyClearLastStackSlot(); michael@0: currentPtr--; michael@0: markMalformedIfScript(node.node); michael@0: elementPopped(node.ns, node.popName, node.node); michael@0: node.release(); michael@0: } michael@0: michael@0: // [NOCPP[ michael@0: private void checkAttributes(HtmlAttributes attributes, @NsUri String ns) michael@0: throws SAXException { michael@0: if (errorHandler != null) { michael@0: int len = attributes.getXmlnsLength(); michael@0: for (int i = 0; i < len; i++) { michael@0: AttributeName name = attributes.getXmlnsAttributeName(i); michael@0: if (name == AttributeName.XMLNS) { michael@0: if (html4) { michael@0: err("Attribute \u201Cxmlns\u201D not allowed here. (HTML4-only error.)"); michael@0: } else { michael@0: String xmlns = attributes.getXmlnsValue(i); michael@0: if (!ns.equals(xmlns)) { michael@0: err("Bad value \u201C" michael@0: + xmlns michael@0: + "\u201D for the attribute \u201Cxmlns\u201D (only \u201C" michael@0: + ns + "\u201D permitted here)."); michael@0: switch (namePolicy) { michael@0: case ALTER_INFOSET: michael@0: // fall through michael@0: case ALLOW: michael@0: warn("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0."); michael@0: break; michael@0: case FATAL: michael@0: fatal("Attribute \u201Cxmlns\u201D is not serializable as XML 1.0."); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } else if (ns != "http://www.w3.org/1999/xhtml" michael@0: && name == AttributeName.XMLNS_XLINK) { michael@0: String xmlns = attributes.getXmlnsValue(i); michael@0: if (!"http://www.w3.org/1999/xlink".equals(xmlns)) { michael@0: err("Bad value \u201C" michael@0: + xmlns michael@0: + "\u201D for the attribute \u201Cxmlns:link\u201D (only \u201Chttp://www.w3.org/1999/xlink\u201D permitted here)."); michael@0: switch (namePolicy) { michael@0: case ALTER_INFOSET: michael@0: // fall through michael@0: case ALLOW: michael@0: 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."); michael@0: break; michael@0: case FATAL: michael@0: 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."); michael@0: break; michael@0: } michael@0: } michael@0: } else { michael@0: err("Attribute \u201C" + attributes.getXmlnsLocalName(i) michael@0: + "\u201D not allowed here."); michael@0: switch (namePolicy) { michael@0: case ALTER_INFOSET: michael@0: // fall through michael@0: case ALLOW: michael@0: warn("Attribute with the local name \u201C" michael@0: + attributes.getXmlnsLocalName(i) michael@0: + "\u201D is not serializable as XML 1.0."); michael@0: break; michael@0: case FATAL: michael@0: fatal("Attribute with the local name \u201C" michael@0: + attributes.getXmlnsLocalName(i) michael@0: + "\u201D is not serializable as XML 1.0."); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: attributes.processNonNcNames(this, namePolicy); michael@0: } michael@0: michael@0: private String checkPopName(@Local String name) throws SAXException { michael@0: if (NCName.isNCName(name)) { michael@0: return name; michael@0: } else { michael@0: switch (namePolicy) { michael@0: case ALLOW: michael@0: warn("Element name \u201C" + name michael@0: + "\u201D cannot be represented as XML 1.0."); michael@0: return name; michael@0: case ALTER_INFOSET: michael@0: warn("Element name \u201C" + name michael@0: + "\u201D cannot be represented as XML 1.0."); michael@0: return NCName.escapeName(name); michael@0: case FATAL: michael@0: fatal("Element name \u201C" + name michael@0: + "\u201D cannot be represented as XML 1.0."); michael@0: } michael@0: } michael@0: return null; // keep compiler happy michael@0: } michael@0: michael@0: // ]NOCPP] michael@0: michael@0: private void appendHtmlElementToDocumentAndPush(HtmlAttributes attributes) michael@0: throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: T elt = createHtmlElementSetAsRoot(attributes); michael@0: StackNode node = new StackNode(ElementName.HTML, michael@0: elt michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: private void appendHtmlElementToDocumentAndPush() throws SAXException { michael@0: appendHtmlElementToDocumentAndPush(tokenizer.emptyAttributes()); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushHeadElement(HtmlAttributes attributes) michael@0: throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", "head", michael@0: attributes); michael@0: appendElement(elt, stack[currentPtr].node); michael@0: headPointer = elt; michael@0: StackNode node = new StackNode(ElementName.HEAD, michael@0: elt michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushBodyElement(HtmlAttributes attributes) michael@0: throws SAXException { michael@0: appendToCurrentNodeAndPushElement(ElementName.BODY, michael@0: attributes); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushBodyElement() throws SAXException { michael@0: appendToCurrentNodeAndPushBodyElement(tokenizer.emptyAttributes()); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushFormElementMayFoster( michael@0: HtmlAttributes attributes) throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", "form", michael@0: attributes); michael@0: michael@0: if (!isTemplateContents()) { michael@0: formPointer = elt; michael@0: } michael@0: michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: StackNode node = new StackNode(ElementName.FORM, michael@0: elt michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushFormattingElementMayFoster( michael@0: ElementName elementName, HtmlAttributes attributes) michael@0: throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: // This method can't be called for custom elements michael@0: HtmlAttributes clone = attributes.cloneAttributes(null); michael@0: // Attributes must not be read after calling createElement, because michael@0: // createElement may delete attributes in C++. michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: StackNode node = new StackNode(elementName, elt, clone michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: append(node); michael@0: node.retain(); // append doesn't retain itself michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushElement(ElementName elementName, michael@0: HtmlAttributes attributes) michael@0: throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: // This method can't be called for custom elements michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes); michael@0: appendElement(elt, stack[currentPtr].node); michael@0: if (ElementName.TEMPLATE == elementName) { michael@0: elt = getDocumentFragmentForTemplate(elt); michael@0: } michael@0: StackNode node = new StackNode(elementName, elt michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName, michael@0: HtmlAttributes attributes) michael@0: throws SAXException { michael@0: @Local String popName = elementName.name; michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: if (elementName.isCustom()) { michael@0: popName = checkPopName(popName); michael@0: } michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: StackNode node = new StackNode(elementName, elt, popName michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushElementMayFosterMathML( michael@0: ElementName elementName, HtmlAttributes attributes) michael@0: throws SAXException { michael@0: @Local String popName = elementName.name; michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML"); michael@0: if (elementName.isCustom()) { michael@0: popName = checkPopName(popName); michael@0: } michael@0: // ]NOCPP] michael@0: boolean markAsHtmlIntegrationPoint = false; michael@0: if (ElementName.ANNOTATION_XML == elementName michael@0: && annotationXmlEncodingPermitsHtml(attributes)) { michael@0: markAsHtmlIntegrationPoint = true; michael@0: } michael@0: // Attributes must not be read after calling createElement(), since michael@0: // createElement may delete the object in C++. michael@0: T elt = createElement("http://www.w3.org/1998/Math/MathML", popName, michael@0: attributes); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: StackNode node = new StackNode(elementName, elt, popName, michael@0: markAsHtmlIntegrationPoint michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: // [NOCPP[ michael@0: T getDocumentFragmentForTemplate(T template) { michael@0: return template; michael@0: } michael@0: michael@0: T getFormPointerForContext(T context) { michael@0: return null; michael@0: } michael@0: // ]NOCPP] michael@0: michael@0: private boolean annotationXmlEncodingPermitsHtml(HtmlAttributes attributes) { michael@0: String encoding = attributes.getValue(AttributeName.ENCODING); michael@0: if (encoding == null) { michael@0: return false; michael@0: } michael@0: return Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "application/xhtml+xml", encoding) michael@0: || Portability.lowerCaseLiteralEqualsIgnoreAsciiCaseString( michael@0: "text/html", encoding); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushElementMayFosterSVG( michael@0: ElementName elementName, HtmlAttributes attributes) michael@0: throws SAXException { michael@0: @Local String popName = elementName.camelCaseName; michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/2000/svg"); michael@0: if (elementName.isCustom()) { michael@0: popName = checkPopName(popName); michael@0: } michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/2000/svg", popName, attributes); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: StackNode node = new StackNode(elementName, popName, elt michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: private void appendToCurrentNodeAndPushElementMayFoster(ElementName elementName, michael@0: HtmlAttributes attributes, T form) michael@0: throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: // Can't be called for custom elements michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", elementName.name, attributes, michael@0: form == null || fragment || isTemplateContents() ? null : form); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: StackNode node = new StackNode(elementName, elt michael@0: // [NOCPP[ michael@0: , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer) michael@0: // ]NOCPP] michael@0: ); michael@0: push(node); michael@0: } michael@0: michael@0: private void appendVoidElementToCurrentMayFoster( michael@0: @Local String name, HtmlAttributes attributes, T form) throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: // Can't be called for custom elements michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", name, attributes, michael@0: form == null || fragment || isTemplateContents() ? null : form); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: elementPushed("http://www.w3.org/1999/xhtml", name, elt); michael@0: elementPopped("http://www.w3.org/1999/xhtml", name, elt); michael@0: } michael@0: michael@0: private void appendVoidElementToCurrentMayFoster( michael@0: ElementName elementName, HtmlAttributes attributes) michael@0: throws SAXException { michael@0: @Local String popName = elementName.name; michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: if (elementName.isCustom()) { michael@0: popName = checkPopName(popName); michael@0: } michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", popName, attributes); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: elementPushed("http://www.w3.org/1999/xhtml", popName, elt); michael@0: elementPopped("http://www.w3.org/1999/xhtml", popName, elt); michael@0: } michael@0: michael@0: private void appendVoidElementToCurrentMayFosterSVG( michael@0: ElementName elementName, HtmlAttributes attributes) michael@0: throws SAXException { michael@0: @Local String popName = elementName.camelCaseName; michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/2000/svg"); michael@0: if (elementName.isCustom()) { michael@0: popName = checkPopName(popName); michael@0: } michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/2000/svg", popName, attributes); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: elementPushed("http://www.w3.org/2000/svg", popName, elt); michael@0: elementPopped("http://www.w3.org/2000/svg", popName, elt); michael@0: } michael@0: michael@0: private void appendVoidElementToCurrentMayFosterMathML( michael@0: ElementName elementName, HtmlAttributes attributes) michael@0: throws SAXException { michael@0: @Local String popName = elementName.name; michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1998/Math/MathML"); michael@0: if (elementName.isCustom()) { michael@0: popName = checkPopName(popName); michael@0: } michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/1998/Math/MathML", popName, attributes); michael@0: StackNode current = stack[currentPtr]; michael@0: if (current.isFosterParenting()) { michael@0: fatal(); michael@0: insertIntoFosterParent(elt); michael@0: } else { michael@0: appendElement(elt, current.node); michael@0: } michael@0: elementPushed("http://www.w3.org/1998/Math/MathML", popName, elt); michael@0: elementPopped("http://www.w3.org/1998/Math/MathML", popName, elt); michael@0: } michael@0: michael@0: private void appendVoidElementToCurrent( michael@0: @Local String name, HtmlAttributes attributes, T form) throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: // Can't be called for custom elements michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", name, attributes, michael@0: form == null || fragment || isTemplateContents() ? null : form); michael@0: StackNode current = stack[currentPtr]; michael@0: appendElement(elt, current.node); michael@0: elementPushed("http://www.w3.org/1999/xhtml", name, elt); michael@0: elementPopped("http://www.w3.org/1999/xhtml", name, elt); michael@0: } michael@0: michael@0: private void appendVoidFormToCurrent(HtmlAttributes attributes) throws SAXException { michael@0: // [NOCPP[ michael@0: checkAttributes(attributes, "http://www.w3.org/1999/xhtml"); michael@0: // ]NOCPP] michael@0: T elt = createElement("http://www.w3.org/1999/xhtml", "form", michael@0: attributes); michael@0: formPointer = elt; michael@0: // ownership transferred to form pointer michael@0: StackNode current = stack[currentPtr]; michael@0: appendElement(elt, current.node); michael@0: elementPushed("http://www.w3.org/1999/xhtml", "form", elt); michael@0: elementPopped("http://www.w3.org/1999/xhtml", "form", elt); michael@0: } michael@0: michael@0: // [NOCPP[ michael@0: michael@0: private final void accumulateCharactersForced(@Const @NoLength char[] buf, michael@0: int start, int length) throws SAXException { michael@0: int newLen = charBufferLen + length; michael@0: if (newLen > charBuffer.length) { michael@0: char[] newBuf = new char[newLen]; michael@0: System.arraycopy(charBuffer, 0, newBuf, 0, charBufferLen); michael@0: charBuffer = newBuf; michael@0: } michael@0: System.arraycopy(buf, start, charBuffer, charBufferLen, length); michael@0: charBufferLen = newLen; michael@0: } michael@0: michael@0: // ]NOCPP] michael@0: michael@0: protected void accumulateCharacters(@Const @NoLength char[] buf, int start, michael@0: int length) throws SAXException { michael@0: appendCharacters(stack[currentPtr].node, buf, start, length); michael@0: } michael@0: michael@0: // ------------------------------- // michael@0: michael@0: protected final void requestSuspension() { michael@0: tokenizer.requestSuspension(); michael@0: } michael@0: michael@0: protected abstract T createElement(@NsUri String ns, @Local String name, michael@0: HtmlAttributes attributes) throws SAXException; michael@0: michael@0: protected T createElement(@NsUri String ns, @Local String name, michael@0: HtmlAttributes attributes, T form) throws SAXException { michael@0: return createElement("http://www.w3.org/1999/xhtml", name, attributes); michael@0: } michael@0: michael@0: protected abstract T createHtmlElementSetAsRoot(HtmlAttributes attributes) michael@0: throws SAXException; michael@0: michael@0: protected abstract void detachFromParent(T element) throws SAXException; michael@0: michael@0: protected abstract boolean hasChildren(T element) throws SAXException; michael@0: michael@0: protected abstract void appendElement(T child, T newParent) michael@0: throws SAXException; michael@0: michael@0: protected abstract void appendChildrenToNewParent(T oldParent, T newParent) michael@0: throws SAXException; michael@0: michael@0: protected abstract void insertFosterParentedChild(T child, T table, michael@0: T stackParent) throws SAXException; michael@0: michael@0: protected abstract void insertFosterParentedCharacters( michael@0: @NoLength char[] buf, int start, int length, T table, T stackParent) michael@0: throws SAXException; michael@0: michael@0: protected abstract void appendCharacters(T parent, @NoLength char[] buf, michael@0: int start, int length) throws SAXException; michael@0: michael@0: protected abstract void appendIsindexPrompt(T parent) throws SAXException; michael@0: michael@0: protected abstract void appendComment(T parent, @NoLength char[] buf, michael@0: int start, int length) throws SAXException; michael@0: michael@0: protected abstract void appendCommentToDocument(@NoLength char[] buf, michael@0: int start, int length) throws SAXException; michael@0: michael@0: protected abstract void addAttributesToElement(T element, michael@0: HtmlAttributes attributes) throws SAXException; michael@0: michael@0: protected void markMalformedIfScript(T elt) throws SAXException { michael@0: michael@0: } michael@0: michael@0: protected void start(boolean fragmentMode) throws SAXException { michael@0: michael@0: } michael@0: michael@0: protected void end() throws SAXException { michael@0: michael@0: } michael@0: michael@0: protected void appendDoctypeToDocument(@Local String name, michael@0: String publicIdentifier, String systemIdentifier) michael@0: throws SAXException { michael@0: michael@0: } michael@0: michael@0: protected void elementPushed(@NsUri String ns, @Local String name, T node) michael@0: throws SAXException { michael@0: michael@0: } michael@0: michael@0: protected void elementPopped(@NsUri String ns, @Local String name, T node) michael@0: throws SAXException { michael@0: michael@0: } michael@0: michael@0: // [NOCPP[ michael@0: michael@0: protected void documentMode(DocumentMode m, String publicIdentifier, michael@0: String systemIdentifier, boolean html4SpecificAdditionalErrorChecks) michael@0: throws SAXException { michael@0: michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.common.TokenHandler#wantsComments() michael@0: */ michael@0: public boolean wantsComments() { michael@0: return wantingComments; michael@0: } michael@0: michael@0: public void setIgnoringComments(boolean ignoreComments) { michael@0: wantingComments = !ignoreComments; michael@0: } michael@0: michael@0: /** michael@0: * Sets the errorHandler. michael@0: * michael@0: * @param errorHandler michael@0: * the errorHandler to set michael@0: */ michael@0: public final void setErrorHandler(ErrorHandler errorHandler) { michael@0: this.errorHandler = errorHandler; michael@0: } michael@0: michael@0: /** michael@0: * Returns the errorHandler. michael@0: * michael@0: * @return the errorHandler michael@0: */ michael@0: public ErrorHandler getErrorHandler() { michael@0: return errorHandler; michael@0: } michael@0: michael@0: /** michael@0: * The argument MUST be an interned string or null. michael@0: * michael@0: * @param context michael@0: */ michael@0: public final void setFragmentContext(@Local String context) { michael@0: this.contextName = context; michael@0: this.contextNamespace = "http://www.w3.org/1999/xhtml"; michael@0: this.contextNode = null; michael@0: this.fragment = (contextName != null); michael@0: this.quirks = false; michael@0: } michael@0: michael@0: // ]NOCPP] michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.common.TokenHandler#cdataSectionAllowed() michael@0: */ michael@0: @Inline public boolean cdataSectionAllowed() throws SAXException { michael@0: return isInForeign(); michael@0: } michael@0: michael@0: private boolean isInForeign() { michael@0: return currentPtr >= 0 michael@0: && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml"; michael@0: } michael@0: michael@0: private boolean isInForeignButNotHtmlOrMathTextIntegrationPoint() { michael@0: if (currentPtr < 0) { michael@0: return false; michael@0: } michael@0: return !isSpecialParentInForeign(stack[currentPtr]); michael@0: } michael@0: michael@0: /** michael@0: * The argument MUST be an interned string or null. michael@0: * michael@0: * @param context michael@0: */ michael@0: public final void setFragmentContext(@Local String context, michael@0: @NsUri String ns, T node, boolean quirks) { michael@0: this.contextName = context; michael@0: this.contextNamespace = ns; michael@0: this.contextNode = node; michael@0: this.fragment = (contextName != null); michael@0: this.quirks = quirks; michael@0: } michael@0: michael@0: protected final T currentNode() { michael@0: return stack[currentPtr].node; michael@0: } michael@0: michael@0: /** michael@0: * Returns the scriptingEnabled. michael@0: * michael@0: * @return the scriptingEnabled michael@0: */ michael@0: public boolean isScriptingEnabled() { michael@0: return scriptingEnabled; michael@0: } michael@0: michael@0: /** michael@0: * Sets the scriptingEnabled. michael@0: * michael@0: * @param scriptingEnabled michael@0: * the scriptingEnabled to set michael@0: */ michael@0: public void setScriptingEnabled(boolean scriptingEnabled) { michael@0: this.scriptingEnabled = scriptingEnabled; michael@0: } michael@0: michael@0: public void setIsSrcdocDocument(boolean isSrcdocDocument) { michael@0: this.isSrcdocDocument = isSrcdocDocument; michael@0: } michael@0: michael@0: // [NOCPP[ michael@0: michael@0: /** michael@0: * Sets the doctypeExpectation. michael@0: * michael@0: * @param doctypeExpectation michael@0: * the doctypeExpectation to set michael@0: */ michael@0: public void setDoctypeExpectation(DoctypeExpectation doctypeExpectation) { michael@0: this.doctypeExpectation = doctypeExpectation; michael@0: } michael@0: michael@0: public void setNamePolicy(XmlViolationPolicy namePolicy) { michael@0: this.namePolicy = namePolicy; michael@0: } michael@0: michael@0: /** michael@0: * Sets the documentModeHandler. michael@0: * michael@0: * @param documentModeHandler michael@0: * the documentModeHandler to set michael@0: */ michael@0: public void setDocumentModeHandler(DocumentModeHandler documentModeHandler) { michael@0: this.documentModeHandler = documentModeHandler; michael@0: } michael@0: michael@0: /** michael@0: * Sets the reportingDoctype. michael@0: * michael@0: * @param reportingDoctype michael@0: * the reportingDoctype to set michael@0: */ michael@0: public void setReportingDoctype(boolean reportingDoctype) { michael@0: this.reportingDoctype = reportingDoctype; michael@0: } michael@0: michael@0: // ]NOCPP] michael@0: michael@0: /** michael@0: * Flushes the pending characters. Public for document.write use cases only. michael@0: * @throws SAXException michael@0: */ michael@0: public final void flushCharacters() throws SAXException { michael@0: if (charBufferLen > 0) { michael@0: if ((mode == IN_TABLE || mode == IN_TABLE_BODY || mode == IN_ROW) michael@0: && charBufferContainsNonWhitespace()) { michael@0: errNonSpaceInTable(); michael@0: reconstructTheActiveFormattingElements(); michael@0: if (!stack[currentPtr].isFosterParenting()) { michael@0: // reconstructing gave us a new current node michael@0: appendCharacters(currentNode(), charBuffer, 0, michael@0: charBufferLen); michael@0: charBufferLen = 0; michael@0: return; michael@0: } michael@0: michael@0: int tablePos = findLastOrRoot(TreeBuilder.TABLE); michael@0: int templatePos = findLastOrRoot(TreeBuilder.TEMPLATE); michael@0: michael@0: if (templatePos >= tablePos) { michael@0: appendCharacters(stack[templatePos].node, charBuffer, 0, charBufferLen); michael@0: charBufferLen = 0; michael@0: return; michael@0: } michael@0: michael@0: StackNode tableElt = stack[tablePos]; michael@0: insertFosterParentedCharacters(charBuffer, 0, charBufferLen, michael@0: tableElt.node, stack[tablePos - 1].node); michael@0: charBufferLen = 0; michael@0: return; michael@0: } michael@0: appendCharacters(currentNode(), charBuffer, 0, charBufferLen); michael@0: charBufferLen = 0; michael@0: } michael@0: } michael@0: michael@0: private boolean charBufferContainsNonWhitespace() { michael@0: for (int i = 0; i < charBufferLen; i++) { michael@0: switch (charBuffer[i]) { michael@0: case ' ': michael@0: case '\t': michael@0: case '\n': michael@0: case '\r': michael@0: case '\u000C': michael@0: continue; michael@0: default: michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Creates a comparable snapshot of the tree builder state. Snapshot michael@0: * creation is only supported immediately after a script end tag has been michael@0: * processed. In C++ the caller is responsible for calling michael@0: * delete on the returned object. michael@0: * michael@0: * @return a snapshot. michael@0: * @throws SAXException michael@0: */ michael@0: @SuppressWarnings("unchecked") public TreeBuilderState newSnapshot() michael@0: throws SAXException { michael@0: StackNode[] listCopy = new StackNode[listPtr + 1]; michael@0: for (int i = 0; i < listCopy.length; i++) { michael@0: StackNode node = listOfActiveFormattingElements[i]; michael@0: if (node != null) { michael@0: StackNode newNode = new StackNode(node.getFlags(), node.ns, michael@0: node.name, node.node, node.popName, michael@0: node.attributes.cloneAttributes(null) michael@0: // [NOCPP[ michael@0: , node.getLocator() michael@0: // ]NOCPP] michael@0: ); michael@0: listCopy[i] = newNode; michael@0: } else { michael@0: listCopy[i] = null; michael@0: } michael@0: } michael@0: StackNode[] stackCopy = new StackNode[currentPtr + 1]; michael@0: for (int i = 0; i < stackCopy.length; i++) { michael@0: StackNode node = stack[i]; michael@0: int listIndex = findInListOfActiveFormattingElements(node); michael@0: if (listIndex == -1) { michael@0: StackNode newNode = new StackNode(node.getFlags(), node.ns, michael@0: node.name, node.node, node.popName, michael@0: null michael@0: // [NOCPP[ michael@0: , node.getLocator() michael@0: // ]NOCPP] michael@0: ); michael@0: stackCopy[i] = newNode; michael@0: } else { michael@0: stackCopy[i] = listCopy[listIndex]; michael@0: stackCopy[i].retain(); michael@0: } michael@0: } michael@0: int[] templateModeStackCopy = new int[templateModePtr + 1]; michael@0: System.arraycopy(templateModeStack, 0, templateModeStackCopy, 0, michael@0: templateModeStackCopy.length); michael@0: return new StateSnapshot(stackCopy, listCopy, templateModeStackCopy, formPointer, michael@0: headPointer, deepTreeSurrogateParent, mode, originalMode, framesetOk, michael@0: needToDropLF, quirks); michael@0: } michael@0: michael@0: public boolean snapshotMatches(TreeBuilderState snapshot) { michael@0: StackNode[] stackCopy = snapshot.getStack(); michael@0: int stackLen = snapshot.getStackLength(); michael@0: StackNode[] listCopy = snapshot.getListOfActiveFormattingElements(); michael@0: int listLen = snapshot.getListOfActiveFormattingElementsLength(); michael@0: int[] templateModeStackCopy = snapshot.getTemplateModeStack(); michael@0: int templateModeStackLen = snapshot.getTemplateModeStackLength(); michael@0: michael@0: if (stackLen != currentPtr + 1 michael@0: || listLen != listPtr + 1 michael@0: || templateModeStackLen != templateModePtr + 1 michael@0: || formPointer != snapshot.getFormPointer() michael@0: || headPointer != snapshot.getHeadPointer() michael@0: || deepTreeSurrogateParent != snapshot.getDeepTreeSurrogateParent() michael@0: || mode != snapshot.getMode() michael@0: || originalMode != snapshot.getOriginalMode() michael@0: || framesetOk != snapshot.isFramesetOk() michael@0: || needToDropLF != snapshot.isNeedToDropLF() michael@0: || quirks != snapshot.isQuirks()) { // maybe just assert quirks michael@0: return false; michael@0: } michael@0: for (int i = listLen - 1; i >= 0; i--) { michael@0: if (listCopy[i] == null michael@0: && listOfActiveFormattingElements[i] == null) { michael@0: continue; michael@0: } else if (listCopy[i] == null michael@0: || listOfActiveFormattingElements[i] == null) { michael@0: return false; michael@0: } michael@0: if (listCopy[i].node != listOfActiveFormattingElements[i].node) { michael@0: return false; // it's possible that this condition is overly michael@0: // strict michael@0: } michael@0: } michael@0: for (int i = stackLen - 1; i >= 0; i--) { michael@0: if (stackCopy[i].node != stack[i].node) { michael@0: return false; michael@0: } michael@0: } michael@0: for (int i = templateModeStackLen - 1; i >=0; i--) { michael@0: if (templateModeStackCopy[i] != templateModeStack[i]) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: @SuppressWarnings("unchecked") public void loadState( michael@0: TreeBuilderState snapshot, Interner interner) michael@0: throws SAXException { michael@0: StackNode[] stackCopy = snapshot.getStack(); michael@0: int stackLen = snapshot.getStackLength(); michael@0: StackNode[] listCopy = snapshot.getListOfActiveFormattingElements(); michael@0: int listLen = snapshot.getListOfActiveFormattingElementsLength(); michael@0: int[] templateModeStackCopy = snapshot.getTemplateModeStack(); michael@0: int templateModeStackLen = snapshot.getTemplateModeStackLength(); michael@0: michael@0: for (int i = 0; i <= listPtr; i++) { michael@0: if (listOfActiveFormattingElements[i] != null) { michael@0: listOfActiveFormattingElements[i].release(); michael@0: } michael@0: } michael@0: if (listOfActiveFormattingElements.length < listLen) { michael@0: listOfActiveFormattingElements = new StackNode[listLen]; michael@0: } michael@0: listPtr = listLen - 1; michael@0: michael@0: for (int i = 0; i <= currentPtr; i++) { michael@0: stack[i].release(); michael@0: } michael@0: if (stack.length < stackLen) { michael@0: stack = new StackNode[stackLen]; michael@0: } michael@0: currentPtr = stackLen - 1; michael@0: michael@0: if (templateModeStack.length < templateModeStackLen) { michael@0: templateModeStack = new int[templateModeStackLen]; michael@0: } michael@0: templateModePtr = templateModeStackLen - 1; michael@0: michael@0: for (int i = 0; i < listLen; i++) { michael@0: StackNode node = listCopy[i]; michael@0: if (node != null) { michael@0: StackNode newNode = new StackNode(node.getFlags(), node.ns, michael@0: Portability.newLocalFromLocal(node.name, interner), node.node, michael@0: Portability.newLocalFromLocal(node.popName, interner), michael@0: node.attributes.cloneAttributes(null) michael@0: // [NOCPP[ michael@0: , node.getLocator() michael@0: // ]NOCPP] michael@0: ); michael@0: listOfActiveFormattingElements[i] = newNode; michael@0: } else { michael@0: listOfActiveFormattingElements[i] = null; michael@0: } michael@0: } michael@0: for (int i = 0; i < stackLen; i++) { michael@0: StackNode node = stackCopy[i]; michael@0: int listIndex = findInArray(node, listCopy); michael@0: if (listIndex == -1) { michael@0: StackNode newNode = new StackNode(node.getFlags(), node.ns, michael@0: Portability.newLocalFromLocal(node.name, interner), node.node, michael@0: Portability.newLocalFromLocal(node.popName, interner), michael@0: null michael@0: // [NOCPP[ michael@0: , node.getLocator() michael@0: // ]NOCPP] michael@0: ); michael@0: stack[i] = newNode; michael@0: } else { michael@0: stack[i] = listOfActiveFormattingElements[listIndex]; michael@0: stack[i].retain(); michael@0: } michael@0: } michael@0: System.arraycopy(templateModeStackCopy, 0, templateModeStack, 0, templateModeStackLen); michael@0: formPointer = snapshot.getFormPointer(); michael@0: headPointer = snapshot.getHeadPointer(); michael@0: deepTreeSurrogateParent = snapshot.getDeepTreeSurrogateParent(); michael@0: mode = snapshot.getMode(); michael@0: originalMode = snapshot.getOriginalMode(); michael@0: framesetOk = snapshot.isFramesetOk(); michael@0: needToDropLF = snapshot.isNeedToDropLF(); michael@0: quirks = snapshot.isQuirks(); michael@0: } michael@0: michael@0: private int findInArray(StackNode node, StackNode[] arr) { michael@0: for (int i = listPtr; i >= 0; i--) { michael@0: if (node == arr[i]) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.impl.TreeBuilderState#getFormPointer() michael@0: */ michael@0: public T getFormPointer() { michael@0: return formPointer; michael@0: } michael@0: michael@0: /** michael@0: * Returns the headPointer. michael@0: * michael@0: * @return the headPointer michael@0: */ michael@0: public T getHeadPointer() { michael@0: return headPointer; michael@0: } michael@0: michael@0: /** michael@0: * Returns the deepTreeSurrogateParent. michael@0: * michael@0: * @return the deepTreeSurrogateParent michael@0: */ michael@0: public T getDeepTreeSurrogateParent() { michael@0: return deepTreeSurrogateParent; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElements() michael@0: */ michael@0: public StackNode[] getListOfActiveFormattingElements() { michael@0: return listOfActiveFormattingElements; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.impl.TreeBuilderState#getStack() michael@0: */ michael@0: public StackNode[] getStack() { michael@0: return stack; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStack() michael@0: */ michael@0: public int[] getTemplateModeStack() { michael@0: return templateModeStack; michael@0: } michael@0: michael@0: /** michael@0: * Returns the mode. michael@0: * michael@0: * @return the mode michael@0: */ michael@0: public int getMode() { michael@0: return mode; michael@0: } michael@0: michael@0: /** michael@0: * Returns the originalMode. michael@0: * michael@0: * @return the originalMode michael@0: */ michael@0: public int getOriginalMode() { michael@0: return originalMode; michael@0: } michael@0: michael@0: /** michael@0: * Returns the framesetOk. michael@0: * michael@0: * @return the framesetOk michael@0: */ michael@0: public boolean isFramesetOk() { michael@0: return framesetOk; michael@0: } michael@0: michael@0: /** michael@0: * Returns the needToDropLF. michael@0: * michael@0: * @return the needToDropLF michael@0: */ michael@0: public boolean isNeedToDropLF() { michael@0: return needToDropLF; michael@0: } michael@0: michael@0: /** michael@0: * Returns the quirks. michael@0: * michael@0: * @return the quirks michael@0: */ michael@0: public boolean isQuirks() { michael@0: return quirks; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.impl.TreeBuilderState#getListOfActiveFormattingElementsLength() michael@0: */ michael@0: public int getListOfActiveFormattingElementsLength() { michael@0: return listPtr + 1; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.impl.TreeBuilderState#getStackLength() michael@0: */ michael@0: public int getStackLength() { michael@0: return currentPtr + 1; michael@0: } michael@0: michael@0: /** michael@0: * @see nu.validator.htmlparser.impl.TreeBuilderState#getTemplateModeStackLength() michael@0: */ michael@0: public int getTemplateModeStackLength() { michael@0: return templateModePtr + 1; michael@0: } michael@0: michael@0: /** michael@0: * Reports a stray start tag. michael@0: * @param name the name of the stray tag michael@0: * michael@0: * @throws SAXException michael@0: */ michael@0: private void errStrayStartTag(@Local String name) throws SAXException { michael@0: err("Stray start tag \u201C" + name + "\u201D."); michael@0: } michael@0: michael@0: /** michael@0: * Reports a stray end tag. michael@0: * @param name the name of the stray tag michael@0: * michael@0: * @throws SAXException michael@0: */ michael@0: private void errStrayEndTag(@Local String name) throws SAXException { michael@0: err("Stray end tag \u201C" + name + "\u201D."); michael@0: } michael@0: michael@0: /** michael@0: * Reports a state when elements expected to be closed were not. michael@0: * michael@0: * @param eltPos the position of the start tag on the stack of the element michael@0: * being closed. michael@0: * @param name the name of the end tag michael@0: * michael@0: * @throws SAXException michael@0: */ michael@0: private void errUnclosedElements(int eltPos, @Local String name) throws SAXException { michael@0: errNoCheck("End tag \u201C" + name + "\u201D seen, but there were open elements."); michael@0: errListUnclosedStartTags(eltPos); michael@0: } michael@0: michael@0: /** michael@0: * Reports a state when elements expected to be closed ahead of an implied michael@0: * end tag but were not. michael@0: * michael@0: * @param eltPos the position of the start tag on the stack of the element michael@0: * being closed. michael@0: * @param name the name of the end tag michael@0: * michael@0: * @throws SAXException michael@0: */ michael@0: private void errUnclosedElementsImplied(int eltPos, String name) throws SAXException { michael@0: errNoCheck("End tag \u201C" + name + "\u201D implied, but there were open elements."); michael@0: errListUnclosedStartTags(eltPos); michael@0: } michael@0: michael@0: /** michael@0: * Reports a state when elements expected to be closed ahead of an implied michael@0: * table cell close. michael@0: * michael@0: * @param eltPos the position of the start tag on the stack of the element michael@0: * being closed. michael@0: * @throws SAXException michael@0: */ michael@0: private void errUnclosedElementsCell(int eltPos) throws SAXException { michael@0: errNoCheck("A table cell was implicitly closed, but there were open elements."); michael@0: errListUnclosedStartTags(eltPos); michael@0: } michael@0: michael@0: private void errStrayDoctype() throws SAXException { michael@0: err("Stray doctype."); michael@0: } michael@0: michael@0: private void errAlmostStandardsDoctype() throws SAXException { michael@0: if (!isSrcdocDocument) { michael@0: err("Almost standards mode doctype. Expected \u201C\u201D."); michael@0: } michael@0: } michael@0: michael@0: private void errQuirkyDoctype() throws SAXException { michael@0: if (!isSrcdocDocument) { michael@0: err("Quirky doctype. Expected \u201C\u201D."); michael@0: } michael@0: } michael@0: michael@0: private void errNonSpaceInTrailer() throws SAXException { michael@0: err("Non-space character in page trailer."); michael@0: } michael@0: michael@0: private void errNonSpaceAfterFrameset() throws SAXException { michael@0: err("Non-space after \u201Cframeset\u201D."); michael@0: } michael@0: michael@0: private void errNonSpaceInFrameset() throws SAXException { michael@0: err("Non-space in \u201Cframeset\u201D."); michael@0: } michael@0: michael@0: private void errNonSpaceAfterBody() throws SAXException { michael@0: err("Non-space character after body."); michael@0: } michael@0: michael@0: private void errNonSpaceInColgroupInFragment() throws SAXException { michael@0: err("Non-space in \u201Ccolgroup\u201D when parsing fragment."); michael@0: } michael@0: michael@0: private void errNonSpaceInNoscriptInHead() throws SAXException { michael@0: err("Non-space character inside \u201Cnoscript\u201D inside \u201Chead\u201D."); michael@0: } michael@0: michael@0: private void errFooBetweenHeadAndBody(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("\u201C" + name + "\u201D element between \u201Chead\u201D and \u201Cbody\u201D."); michael@0: } michael@0: michael@0: private void errStartTagWithoutDoctype() throws SAXException { michael@0: if (!isSrcdocDocument) { michael@0: err("Start tag seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: } michael@0: } michael@0: michael@0: private void errNoSelectInTableScope() throws SAXException { michael@0: err("No \u201Cselect\u201D in table scope."); michael@0: } michael@0: michael@0: private void errStartSelectWhereEndSelectExpected() throws SAXException { michael@0: err("\u201Cselect\u201D start tag where end tag expected."); michael@0: } michael@0: michael@0: private void errStartTagWithSelectOpen(@Local String name) michael@0: throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("\u201C" + name michael@0: + "\u201D start tag with \u201Cselect\u201D open."); michael@0: } michael@0: michael@0: private void errBadStartTagInHead(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("Bad start tag in \u201C" + name michael@0: + "\u201D in \u201Chead\u201D."); michael@0: } michael@0: michael@0: private void errImage() throws SAXException { michael@0: err("Saw a start tag \u201Cimage\u201D."); michael@0: } michael@0: michael@0: private void errIsindex() throws SAXException { michael@0: err("\u201Cisindex\u201D seen."); michael@0: } michael@0: michael@0: private void errFooSeenWhenFooOpen(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("An \u201C" + name + "\u201D start tag seen but an element of the same type was already open."); michael@0: } michael@0: michael@0: private void errHeadingWhenHeadingOpen() throws SAXException { michael@0: err("Heading cannot be a child of another heading."); michael@0: } michael@0: michael@0: private void errFramesetStart() throws SAXException { michael@0: err("\u201Cframeset\u201D start tag seen."); michael@0: } michael@0: michael@0: private void errNoCellToClose() throws SAXException { michael@0: err("No cell to close."); michael@0: } michael@0: michael@0: private void errStartTagInTable(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("Start tag \u201C" + name michael@0: + "\u201D seen in \u201Ctable\u201D."); michael@0: } michael@0: michael@0: private void errFormWhenFormOpen() throws SAXException { michael@0: err("Saw a \u201Cform\u201D start tag, but there was already an active \u201Cform\u201D element. Nested forms are not allowed. Ignoring the tag."); michael@0: } michael@0: michael@0: private void errTableSeenWhileTableOpen() throws SAXException { michael@0: err("Start tag for \u201Ctable\u201D seen but the previous \u201Ctable\u201D is still open."); michael@0: } michael@0: michael@0: private void errStartTagInTableBody(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("\u201C" + name + "\u201D start tag in table body."); michael@0: } michael@0: michael@0: private void errEndTagSeenWithoutDoctype() throws SAXException { michael@0: if (!isSrcdocDocument) { michael@0: err("End tag seen without seeing a doctype first. Expected \u201C\u201D."); michael@0: } michael@0: } michael@0: michael@0: private void errEndTagAfterBody() throws SAXException { michael@0: err("Saw an end tag after \u201Cbody\u201D had been closed."); michael@0: } michael@0: michael@0: private void errEndTagSeenWithSelectOpen(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("\u201C" + name michael@0: + "\u201D end tag with \u201Cselect\u201D open."); michael@0: } michael@0: michael@0: private void errGarbageInColgroup() throws SAXException { michael@0: err("Garbage in \u201Ccolgroup\u201D fragment."); michael@0: } michael@0: michael@0: private void errEndTagBr() throws SAXException { michael@0: err("End tag \u201Cbr\u201D."); michael@0: } michael@0: michael@0: private void errNoElementToCloseButEndTagSeen(@Local String name) michael@0: throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("No \u201C" + name + "\u201D element in scope but a \u201C" michael@0: + name + "\u201D end tag seen."); michael@0: } michael@0: michael@0: private void errHtmlStartTagInForeignContext(@Local String name) michael@0: throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("HTML start tag \u201C" + name michael@0: + "\u201D in a foreign namespace context."); michael@0: } michael@0: michael@0: private void errTableClosedWhileCaptionOpen() throws SAXException { michael@0: err("\u201Ctable\u201D closed but \u201Ccaption\u201D was still open."); michael@0: } michael@0: michael@0: private void errNoTableRowToClose() throws SAXException { michael@0: err("No table row to close."); michael@0: } michael@0: michael@0: private void errNonSpaceInTable() throws SAXException { michael@0: err("Misplaced non-space characters insided a table."); michael@0: } michael@0: michael@0: private void errUnclosedChildrenInRuby() throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("Unclosed children in \u201Cruby\u201D."); michael@0: } michael@0: michael@0: private void errStartTagSeenWithoutRuby(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("Start tag \u201C" michael@0: + name michael@0: + "\u201D seen without a \u201Cruby\u201D element being open."); michael@0: } michael@0: michael@0: private void errSelfClosing() throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("Self-closing syntax (\u201C/>\u201D) used on a non-void HTML element. Ignoring the slash and treating as a start tag."); michael@0: } michael@0: michael@0: private void errNoCheckUnclosedElementsOnStack() throws SAXException { michael@0: errNoCheck("Unclosed elements on stack."); michael@0: } michael@0: michael@0: private void errEndTagDidNotMatchCurrentOpenElement(@Local String name, michael@0: @Local String currOpenName) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("End tag \u201C" michael@0: + name michael@0: + "\u201D did not match the name of the current open element (\u201C" michael@0: + currOpenName + "\u201D)."); michael@0: } michael@0: michael@0: private void errEndTagViolatesNestingRules(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("End tag \u201C" + name + "\u201D violates nesting rules."); michael@0: } michael@0: michael@0: private void errEofWithUnclosedElements() throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("End of file seen and there were open elements."); michael@0: // just report all remaining unclosed elements michael@0: errListUnclosedStartTags(0); michael@0: } michael@0: michael@0: /** michael@0: * Reports arriving at/near end of document with unclosed elements remaining. michael@0: * michael@0: * @param message michael@0: * the message michael@0: * @throws SAXException michael@0: */ michael@0: private void errEndWithUnclosedElements(@Local String name) throws SAXException { michael@0: if (errorHandler == null) { michael@0: return; michael@0: } michael@0: errNoCheck("End tag for \u201C" michael@0: + name michael@0: + "\u201D seen, but there were unclosed elements."); michael@0: // just report all remaining unclosed elements michael@0: errListUnclosedStartTags(0); michael@0: } michael@0: }