001 // Copyright (c) 2011, Mike Samuel 002 // All rights reserved. 003 // 004 // Redistribution and use in source and binary forms, with or without 005 // modification, are permitted provided that the following conditions 006 // are met: 007 // 008 // Redistributions of source code must retain the above copyright 009 // notice, this list of conditions and the following disclaimer. 010 // Redistributions in binary form must reproduce the above copyright 011 // notice, this list of conditions and the following disclaimer in the 012 // documentation and/or other materials provided with the distribution. 013 // Neither the name of the OWASP nor the names of its contributors may 014 // be used to endorse or promote products derived from this software 015 // without specific prior written permission. 016 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 017 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 018 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 019 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 020 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 021 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 022 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 024 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 025 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 026 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 027 // POSSIBILITY OF SUCH DAMAGE. 028 029 package org.owasp.html; 030 031 import java.util.List; 032 033 import javax.annotation.concurrent.Immutable; 034 035 import com.google.common.collect.ImmutableMap; 036 import com.google.common.collect.Lists; 037 038 /** 039 * Wraps an HTML stream event receiver to fill in missing close tags. 040 * If the balancer is given the HTML {@code <p>1<p>2}, the wrapped receiver will 041 * see events equivalent to {@code <p>1</p><p>2</p>}. 042 * 043 * @author Mike Samuel <mikesamuel@gmail.com> 044 */ 045 @TCB 046 public class TagBalancingHtmlStreamEventReceiver 047 implements HtmlStreamEventReceiver { 048 private final HtmlStreamEventReceiver underlying; 049 private final List<ElementContainmentInfo> openElements 050 = Lists.newArrayList(); 051 052 public TagBalancingHtmlStreamEventReceiver( 053 HtmlStreamEventReceiver underlying) { 054 this.underlying = underlying; 055 } 056 057 public void openDocument() { 058 underlying.openDocument(); 059 } 060 061 public void closeDocument() { 062 while (!openElements.isEmpty()) { 063 underlying.closeTag( 064 openElements.remove(openElements.size() - 1).elementName); 065 } 066 openElements.clear(); 067 underlying.closeDocument(); 068 } 069 070 public void openTag(String elementName, List<String> attrs) { 071 String canonElementName = HtmlLexer.canonicalName(elementName); 072 ElementContainmentInfo elInfo = ELEMENT_CONTAINMENT_RELATIONSHIPS.get( 073 canonElementName); 074 // Treat unrecognized tags as void, but emit closing tags in closeTag(). 075 if (elInfo == null) { 076 underlying.openTag(elementName, attrs); 077 return; 078 } 079 080 // Close all the elements that cannot contain the element to open. 081 List<ElementContainmentInfo> toResumeInReverse = null; 082 for (int i = openElements.size(); --i >= 0;) { 083 ElementContainmentInfo top = openElements.get(i); 084 if ((top.contents & elInfo.types) != 0) { break; } 085 underlying.closeTag(top.elementName); 086 openElements.remove(i); 087 if (top.resumable) { 088 if (toResumeInReverse == null) { 089 toResumeInReverse = Lists.newArrayList(); 090 } 091 toResumeInReverse.add(top); 092 } 093 } 094 095 if (toResumeInReverse != null) { 096 for (ElementContainmentInfo toResume : toResumeInReverse) { 097 openElements.add(toResume); 098 // TODO: If resuming of things other than plain formatting tags like <b> 099 // and <i>, then we need to store the attributes for resumable tags so 100 // that we can resume with the appropriate attributes. 101 underlying.openTag(toResume.elementName, Lists.<String>newArrayList()); 102 } 103 } 104 if (!elInfo.isVoid) { 105 openElements.add(elInfo); 106 } 107 underlying.openTag(elementName, attrs); 108 } 109 110 public void closeTag(String elementName) { 111 String canonElementName = HtmlLexer.canonicalName(elementName); 112 ElementContainmentInfo elInfo = ELEMENT_CONTAINMENT_RELATIONSHIPS.get( 113 canonElementName); 114 if (elInfo == null) { // Allow unrecognized end tags through. 115 underlying.closeTag(elementName); 116 return; 117 } 118 int index = openElements.lastIndexOf(elInfo); 119 if (index < 0) { return; } // Don't close unopened tags. 120 int last = openElements.size(); 121 // Close all the elements that cannot contain the element to open. 122 List<ElementContainmentInfo> toResumeInReverse = null; 123 while (--last > index) { 124 ElementContainmentInfo unclosed = openElements.remove(last); 125 underlying.closeTag(unclosed.elementName); 126 if (unclosed.resumable) { 127 if (toResumeInReverse == null) { 128 toResumeInReverse = Lists.newArrayList(); 129 } 130 toResumeInReverse.add(unclosed); 131 } 132 } 133 openElements.remove(index); 134 underlying.closeTag(elementName); 135 136 if (toResumeInReverse != null) { 137 for (ElementContainmentInfo toResume : toResumeInReverse) { 138 openElements.add(toResume); 139 // TODO: If resuming of things other than plain formatting tags like <b> 140 // and <i>, then we need to store the attributes for resumable tags so 141 // that we can resume with the appropriate attributes. 142 underlying.openTag(toResume.elementName, Lists.<String>newArrayList()); 143 } 144 } 145 } 146 147 public void text(String text) { 148 underlying.text(text); 149 } 150 151 152 @Immutable 153 static final class ElementContainmentInfo { 154 final String elementName; 155 /** 156 * True if the adoption agency algorithm allows an element to be resumed 157 * after a mis-nested end tag closes it. 158 * E.g. in {@code <b>Foo<i>Bar</b>Baz</i>} the {@code <i>} element is 159 * resumed after the {@code <b>} element is closed. 160 */ 161 final boolean resumable; 162 /** A set of bits of element groups into which the element falls. */ 163 final int types; 164 /** The type of elements that an element can contain. */ 165 final int contents; 166 /** True if the element has no content -- not even text content. */ 167 final boolean isVoid; 168 169 ElementContainmentInfo( 170 String elementName, boolean resumable, int types, int contents) { 171 this.elementName = elementName; 172 this.resumable = resumable; 173 this.types = types; 174 this.contents = contents; 175 this.isVoid = contents == 0 176 && HtmlTextEscapingMode.isVoidElement(elementName); 177 } 178 179 @Override public String toString() { 180 return "<" + elementName + ">"; 181 } 182 } 183 184 ImmutableMap<String, ElementContainmentInfo> ELEMENT_CONTAINMENT_RELATIONSHIPS 185 = new ElementContainmentRelationships().toMap(); 186 187 private static class ElementContainmentRelationships { 188 private enum ElementGroup { 189 BLOCK, 190 INLINE, 191 INLINE_MINUS_A, 192 MIXED, 193 TABLE_CONTENT, 194 HEAD_CONTENT, 195 TOP_CONTENT, 196 AREA_ELEMENT, 197 FORM_ELEMENT, 198 LEGEND_ELEMENT, 199 LI_ELEMENT, 200 DL_PART, 201 P_ELEMENT, 202 OPTIONS_ELEMENT, 203 OPTION_ELEMENT, 204 PARAM_ELEMENT, 205 TABLE_ELEMENT, 206 TR_ELEMENT, 207 TD_ELEMENT, 208 COL_ELEMENT, 209 ; 210 } 211 212 private static int elementGroupBits(ElementGroup a) { 213 return 1 << a.ordinal(); 214 } 215 216 private static int elementGroupBits( 217 ElementGroup a, ElementGroup b) { 218 return (1 << a.ordinal()) | (1 << b.ordinal()); 219 } 220 221 private static int elementGroupBits( 222 ElementGroup a, ElementGroup b, ElementGroup c) { 223 return (1 << a.ordinal()) | (1 << b.ordinal()) | (1 << c.ordinal()); 224 } 225 226 private static int elementGroupBits( 227 ElementGroup... bits) { 228 int bitField = 0; 229 for (ElementGroup bit : bits) { 230 bitField |= (1 << bit.ordinal()); 231 } 232 return bitField; 233 } 234 235 private ImmutableMap.Builder<String, ElementContainmentInfo> definitions 236 = ImmutableMap.builder(); 237 238 private void defineElement( 239 String elementName, boolean resumable, int types, int contentTypes) { 240 definitions.put(elementName, new ElementContainmentInfo( 241 elementName, resumable, types, contentTypes)); 242 } 243 244 private ImmutableMap<String, ElementContainmentInfo> toMap() { 245 return definitions.build(); 246 } 247 248 { 249 defineElement( 250 "a", false, elementGroupBits( 251 ElementGroup.INLINE 252 ), elementGroupBits( 253 ElementGroup.INLINE_MINUS_A 254 )); 255 defineElement( 256 "abbr", true, elementGroupBits( 257 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 258 ), elementGroupBits( 259 ElementGroup.INLINE 260 )); 261 defineElement( 262 "acronym", true, elementGroupBits( 263 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 264 ), elementGroupBits( 265 ElementGroup.INLINE 266 )); 267 defineElement( 268 "address", false, elementGroupBits( 269 ElementGroup.BLOCK 270 ), elementGroupBits( 271 ElementGroup.INLINE, ElementGroup.P_ELEMENT 272 )); 273 defineElement( 274 "applet", false, elementGroupBits( 275 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 276 ), elementGroupBits( 277 ElementGroup.BLOCK, ElementGroup.INLINE, 278 ElementGroup.PARAM_ELEMENT 279 )); 280 defineElement( 281 "area", false, elementGroupBits(ElementGroup.AREA_ELEMENT), 0); 282 defineElement( 283 "audio", false, elementGroupBits( 284 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 285 ), 0); 286 defineElement( 287 "b", true, elementGroupBits( 288 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 289 ), elementGroupBits( 290 ElementGroup.INLINE 291 )); 292 defineElement( 293 "base", false, elementGroupBits(ElementGroup.HEAD_CONTENT), 0); 294 defineElement( 295 "basefont", false, elementGroupBits( 296 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 297 ), 0); 298 defineElement( 299 "bdi", true, elementGroupBits( 300 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 301 ), elementGroupBits( 302 ElementGroup.INLINE 303 )); 304 defineElement( 305 "bdo", true, elementGroupBits( 306 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 307 ), elementGroupBits( 308 ElementGroup.INLINE 309 )); 310 defineElement( 311 "big", true, elementGroupBits( 312 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 313 ), elementGroupBits( 314 ElementGroup.INLINE 315 )); 316 defineElement( 317 "blink", true, elementGroupBits( 318 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 319 ), elementGroupBits( 320 ElementGroup.INLINE 321 )); 322 defineElement( 323 "blockquote", false, elementGroupBits( 324 ElementGroup.BLOCK 325 ), elementGroupBits( 326 ElementGroup.BLOCK, ElementGroup.INLINE 327 )); 328 defineElement( 329 "body", false, elementGroupBits( 330 ElementGroup.TOP_CONTENT 331 ), elementGroupBits( 332 ElementGroup.BLOCK, ElementGroup.INLINE 333 )); 334 defineElement( 335 "br", false, elementGroupBits( 336 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 337 ), 0); 338 defineElement( 339 "button", false, elementGroupBits( 340 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 341 ), elementGroupBits( 342 ElementGroup.BLOCK, ElementGroup.INLINE 343 )); 344 defineElement( 345 "canvas", false, elementGroupBits( 346 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 347 ), elementGroupBits( 348 ElementGroup.INLINE 349 )); 350 defineElement( 351 "caption", false, elementGroupBits( 352 ElementGroup.TABLE_CONTENT 353 ), elementGroupBits( 354 ElementGroup.INLINE 355 )); 356 defineElement( 357 "center", false, elementGroupBits( 358 ElementGroup.BLOCK 359 ), elementGroupBits( 360 ElementGroup.BLOCK, ElementGroup.INLINE 361 )); 362 defineElement( 363 "cite", true, elementGroupBits( 364 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 365 ), elementGroupBits( 366 ElementGroup.INLINE 367 )); 368 defineElement( 369 "code", true, elementGroupBits( 370 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 371 ), elementGroupBits( 372 ElementGroup.INLINE 373 )); 374 defineElement( 375 "col", false, elementGroupBits( 376 ElementGroup.TABLE_CONTENT, ElementGroup.COL_ELEMENT 377 ), 0); 378 defineElement( 379 "colgroup", false, elementGroupBits( 380 ElementGroup.TABLE_CONTENT 381 ), elementGroupBits( 382 ElementGroup.COL_ELEMENT 383 )); 384 defineElement( 385 "dd", false, elementGroupBits( 386 ElementGroup.DL_PART 387 ), elementGroupBits( 388 ElementGroup.BLOCK, ElementGroup.INLINE 389 )); 390 defineElement( 391 "del", true, elementGroupBits( 392 ElementGroup.BLOCK, ElementGroup.INLINE, 393 ElementGroup.MIXED 394 ), elementGroupBits( 395 ElementGroup.BLOCK, ElementGroup.INLINE 396 )); 397 defineElement( 398 "dfn", true, elementGroupBits( 399 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 400 ), elementGroupBits( 401 ElementGroup.INLINE 402 )); 403 defineElement( 404 "dir", false, elementGroupBits( 405 ElementGroup.BLOCK 406 ), elementGroupBits( 407 ElementGroup.LI_ELEMENT 408 )); 409 defineElement( 410 "div", false, elementGroupBits( 411 ElementGroup.BLOCK 412 ), elementGroupBits( 413 ElementGroup.BLOCK, ElementGroup.INLINE 414 )); 415 defineElement( 416 "dl", false, elementGroupBits( 417 ElementGroup.BLOCK 418 ), elementGroupBits( 419 ElementGroup.DL_PART 420 )); 421 defineElement( 422 "dt", false, elementGroupBits( 423 ElementGroup.DL_PART 424 ), elementGroupBits( 425 ElementGroup.INLINE 426 )); 427 defineElement( 428 "em", true, elementGroupBits( 429 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 430 ), elementGroupBits( 431 ElementGroup.INLINE 432 )); 433 defineElement( 434 "fieldset", false, elementGroupBits( 435 ElementGroup.BLOCK 436 ), elementGroupBits( 437 ElementGroup.BLOCK, ElementGroup.INLINE, 438 ElementGroup.LEGEND_ELEMENT 439 )); 440 defineElement( 441 "font", false, elementGroupBits( 442 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 443 ), elementGroupBits( 444 ElementGroup.INLINE 445 )); 446 defineElement( 447 "form", false, elementGroupBits( 448 ElementGroup.BLOCK, ElementGroup.FORM_ELEMENT 449 ), elementGroupBits( 450 ElementGroup.BLOCK, ElementGroup.INLINE, 451 ElementGroup.INLINE_MINUS_A, ElementGroup.TR_ELEMENT, 452 ElementGroup.TD_ELEMENT 453 )); 454 defineElement( 455 "h1", false, elementGroupBits( 456 ElementGroup.BLOCK 457 ), elementGroupBits( 458 ElementGroup.INLINE 459 )); 460 defineElement( 461 "h2", false, elementGroupBits( 462 ElementGroup.BLOCK 463 ), elementGroupBits( 464 ElementGroup.INLINE 465 )); 466 defineElement( 467 "h3", false, elementGroupBits( 468 ElementGroup.BLOCK 469 ), elementGroupBits( 470 ElementGroup.INLINE 471 )); 472 defineElement( 473 "h4", false, elementGroupBits( 474 ElementGroup.BLOCK 475 ), elementGroupBits( 476 ElementGroup.INLINE 477 )); 478 defineElement( 479 "h5", false, elementGroupBits( 480 ElementGroup.BLOCK 481 ), elementGroupBits( 482 ElementGroup.INLINE 483 )); 484 defineElement( 485 "h6", false, elementGroupBits( 486 ElementGroup.BLOCK 487 ), elementGroupBits( 488 ElementGroup.INLINE 489 )); 490 defineElement( 491 "head", false, elementGroupBits( 492 ElementGroup.TOP_CONTENT 493 ), elementGroupBits( 494 ElementGroup.HEAD_CONTENT 495 )); 496 defineElement( 497 "hr", false, elementGroupBits(ElementGroup.BLOCK), 0); 498 defineElement( 499 "html", false, 0, elementGroupBits(ElementGroup.TOP_CONTENT)); 500 defineElement( 501 "i", true, elementGroupBits( 502 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 503 ), elementGroupBits( 504 ElementGroup.INLINE 505 )); 506 defineElement( 507 "iframe", false, elementGroupBits( 508 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 509 ), elementGroupBits( 510 ElementGroup.BLOCK, ElementGroup.INLINE 511 )); 512 defineElement( 513 "img", false, elementGroupBits( 514 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 515 ), 0); 516 defineElement( 517 "input", false, elementGroupBits( 518 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 519 ), 0); 520 defineElement( 521 "ins", true, elementGroupBits( 522 ElementGroup.BLOCK, ElementGroup.INLINE 523 ), elementGroupBits( 524 ElementGroup.BLOCK, ElementGroup.INLINE 525 )); 526 defineElement( 527 "isindex", false, elementGroupBits(ElementGroup.INLINE), 0); 528 defineElement( 529 "kbd", true, elementGroupBits( 530 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 531 ), elementGroupBits( 532 ElementGroup.INLINE 533 )); 534 defineElement( 535 "label", false, elementGroupBits( 536 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 537 ), elementGroupBits( 538 ElementGroup.INLINE 539 )); 540 defineElement( 541 "legend", false, elementGroupBits( 542 ElementGroup.LEGEND_ELEMENT 543 ), elementGroupBits( 544 ElementGroup.INLINE 545 )); 546 defineElement( 547 "li", false, elementGroupBits( 548 ElementGroup.LI_ELEMENT 549 ), elementGroupBits( 550 ElementGroup.BLOCK, ElementGroup.INLINE 551 )); 552 defineElement( 553 "link", false, elementGroupBits( 554 ElementGroup.INLINE, ElementGroup.HEAD_CONTENT 555 ), 0); 556 defineElement( 557 "listing", false, elementGroupBits( 558 ElementGroup.BLOCK 559 ), elementGroupBits( 560 ElementGroup.INLINE 561 )); 562 defineElement( 563 "map", false, elementGroupBits( 564 ElementGroup.INLINE 565 ), elementGroupBits( 566 ElementGroup.BLOCK, ElementGroup.AREA_ELEMENT 567 )); 568 defineElement( 569 "meta", false, elementGroupBits(ElementGroup.HEAD_CONTENT), 0); 570 defineElement( 571 "nobr", false, elementGroupBits( 572 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 573 ), elementGroupBits( 574 ElementGroup.INLINE 575 )); 576 defineElement( 577 "noframes", false, elementGroupBits( 578 ElementGroup.BLOCK, ElementGroup.TOP_CONTENT 579 ), elementGroupBits( 580 ElementGroup.BLOCK, ElementGroup.INLINE, 581 ElementGroup.TOP_CONTENT 582 )); 583 defineElement( 584 "noscript", false, elementGroupBits( 585 ElementGroup.BLOCK 586 ), elementGroupBits( 587 ElementGroup.BLOCK, ElementGroup.INLINE 588 )); 589 defineElement( 590 "object", false, elementGroupBits( 591 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A, 592 ElementGroup.HEAD_CONTENT 593 ), elementGroupBits( 594 ElementGroup.BLOCK, ElementGroup.INLINE, 595 ElementGroup.PARAM_ELEMENT 596 )); 597 defineElement( 598 "ol", false, elementGroupBits( 599 ElementGroup.BLOCK 600 ), elementGroupBits( 601 ElementGroup.LI_ELEMENT 602 )); 603 defineElement( 604 "optgroup", false, elementGroupBits( 605 ElementGroup.OPTIONS_ELEMENT 606 ), elementGroupBits( 607 ElementGroup.OPTIONS_ELEMENT 608 )); 609 defineElement( 610 "option", false, elementGroupBits( 611 ElementGroup.OPTIONS_ELEMENT, ElementGroup.OPTION_ELEMENT 612 ), 0); 613 defineElement( 614 "p", false, elementGroupBits( 615 ElementGroup.BLOCK, ElementGroup.P_ELEMENT 616 ), elementGroupBits( 617 ElementGroup.INLINE, ElementGroup.TABLE_ELEMENT 618 )); 619 defineElement( 620 "param", false, elementGroupBits(ElementGroup.PARAM_ELEMENT), 0); 621 defineElement( 622 "pre", false, elementGroupBits( 623 ElementGroup.BLOCK 624 ), elementGroupBits( 625 ElementGroup.INLINE 626 )); 627 defineElement( 628 "q", true, elementGroupBits( 629 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 630 ), elementGroupBits( 631 ElementGroup.INLINE 632 )); 633 defineElement( 634 "s", true, elementGroupBits( 635 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 636 ), elementGroupBits( 637 ElementGroup.INLINE 638 )); 639 defineElement( 640 "samp", true, elementGroupBits( 641 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 642 ), elementGroupBits( 643 ElementGroup.INLINE 644 )); 645 defineElement( 646 "script", false, elementGroupBits( 647 ElementGroup.BLOCK, ElementGroup.INLINE, 648 ElementGroup.INLINE_MINUS_A, ElementGroup.MIXED, 649 ElementGroup.TABLE_CONTENT, ElementGroup.HEAD_CONTENT, 650 ElementGroup.TOP_CONTENT, ElementGroup.AREA_ELEMENT, 651 ElementGroup.FORM_ELEMENT, ElementGroup.LEGEND_ELEMENT, 652 ElementGroup.LI_ELEMENT, ElementGroup.DL_PART, 653 ElementGroup.P_ELEMENT, ElementGroup.OPTIONS_ELEMENT, 654 ElementGroup.OPTION_ELEMENT, ElementGroup.PARAM_ELEMENT, 655 ElementGroup.TABLE_ELEMENT, ElementGroup.TR_ELEMENT, 656 ElementGroup.TD_ELEMENT, ElementGroup.COL_ELEMENT 657 ), 0); 658 defineElement( 659 "select", false, elementGroupBits( 660 ElementGroup.INLINE 661 ), elementGroupBits( 662 ElementGroup.OPTIONS_ELEMENT 663 )); 664 defineElement( 665 "small", true, elementGroupBits( 666 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 667 ), elementGroupBits( 668 ElementGroup.INLINE 669 )); 670 defineElement( 671 "span", false, elementGroupBits( 672 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 673 ), elementGroupBits( 674 ElementGroup.INLINE 675 )); 676 defineElement( 677 "strike", true, elementGroupBits( 678 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 679 ), elementGroupBits( 680 ElementGroup.INLINE 681 )); 682 defineElement( 683 "strong", true, elementGroupBits( 684 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 685 ), elementGroupBits( 686 ElementGroup.INLINE 687 )); 688 defineElement( 689 "style", false, elementGroupBits( 690 ElementGroup.INLINE, ElementGroup.HEAD_CONTENT 691 ), 0); 692 defineElement( 693 "sub", true, elementGroupBits( 694 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 695 ), elementGroupBits( 696 ElementGroup.INLINE 697 )); 698 defineElement( 699 "sup", true, elementGroupBits( 700 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 701 ), elementGroupBits( 702 ElementGroup.INLINE 703 )); 704 defineElement( 705 "table", false, elementGroupBits( 706 ElementGroup.BLOCK, ElementGroup.TABLE_ELEMENT 707 ), elementGroupBits( 708 ElementGroup.TABLE_CONTENT, ElementGroup.FORM_ELEMENT 709 )); 710 defineElement( 711 "tbody", false, elementGroupBits( 712 ElementGroup.TABLE_CONTENT 713 ), elementGroupBits( 714 ElementGroup.TR_ELEMENT 715 )); 716 defineElement( 717 "td", false, elementGroupBits( 718 ElementGroup.TD_ELEMENT 719 ), elementGroupBits( 720 ElementGroup.BLOCK, ElementGroup.INLINE 721 )); 722 defineElement( 723 "textarea", false, 724 // No, a textarea cannot be inside a link. 725 elementGroupBits(ElementGroup.INLINE), 0); 726 defineElement( 727 "tfoot", false, elementGroupBits( 728 ElementGroup.TABLE_CONTENT 729 ), elementGroupBits( 730 ElementGroup.FORM_ELEMENT, ElementGroup.TR_ELEMENT, 731 ElementGroup.TD_ELEMENT 732 )); 733 defineElement( 734 "th", false, elementGroupBits( 735 ElementGroup.TD_ELEMENT 736 ), elementGroupBits( 737 ElementGroup.BLOCK, ElementGroup.INLINE 738 )); 739 defineElement( 740 "thead", false, elementGroupBits( 741 ElementGroup.TABLE_CONTENT 742 ), elementGroupBits( 743 ElementGroup.FORM_ELEMENT, ElementGroup.TR_ELEMENT, 744 ElementGroup.TD_ELEMENT 745 )); 746 defineElement( 747 "title", false, elementGroupBits(ElementGroup.HEAD_CONTENT), 0); 748 defineElement( 749 "tr", false, elementGroupBits( 750 ElementGroup.TABLE_CONTENT, ElementGroup.TR_ELEMENT 751 ), elementGroupBits( 752 ElementGroup.FORM_ELEMENT, ElementGroup.TD_ELEMENT 753 )); 754 defineElement( 755 "tt", true, elementGroupBits( 756 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 757 ), elementGroupBits( 758 ElementGroup.INLINE 759 )); 760 defineElement( 761 "u", true, elementGroupBits( 762 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 763 ), elementGroupBits( 764 ElementGroup.INLINE 765 )); 766 defineElement( 767 "ul", false, elementGroupBits( 768 ElementGroup.BLOCK 769 ), elementGroupBits( 770 ElementGroup.LI_ELEMENT 771 )); 772 defineElement( 773 "var", false, elementGroupBits( 774 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 775 ), elementGroupBits( 776 ElementGroup.INLINE 777 )); 778 defineElement( 779 "video", false, elementGroupBits( 780 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 781 ), 0); 782 defineElement( 783 "wbr", false, elementGroupBits( 784 ElementGroup.INLINE, ElementGroup.INLINE_MINUS_A 785 ), 0); 786 defineElement( 787 "xmp", false, elementGroupBits( 788 ElementGroup.BLOCK 789 ), elementGroupBits( 790 ElementGroup.INLINE 791 )); 792 793 } 794 } 795 }