parser/html/javasrc/TreeBuilder.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial