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    }