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